checkpoint: reactivate/update connections only if necessary on rollback

On rollback, before updating the settings-connection check if it
actually changed. Also, only reactivate the connection if it was
deactivated or if the settings/applied connection changed.

https://bugzilla.redhat.com/show_bug.cgi?id=1427187
This commit is contained in:
Beniamino Galvani
2017-03-05 01:00:52 +01:00
parent 6b7419c780
commit b9d11ffaa2

View File

@@ -24,6 +24,7 @@
#include <string.h> #include <string.h>
#include "nm-active-connection.h"
#include "nm-auth-subject.h" #include "nm-auth-subject.h"
#include "nm-core-utils.h" #include "nm-core-utils.h"
#include "nm-dbus-interface.h" #include "nm-dbus-interface.h"
@@ -42,6 +43,7 @@ typedef struct {
NMDevice *device; NMDevice *device;
NMConnection *applied_connection; NMConnection *applied_connection;
NMConnection *settings_connection; NMConnection *settings_connection;
guint64 ac_version_id;
NMDeviceState state; NMDeviceState state;
bool realized:1; bool realized:1;
bool unmanaged_explicit:1; bool unmanaged_explicit:1;
@@ -116,6 +118,62 @@ nm_checkpoint_includes_device (NMCheckpoint *self, NMDevice *device)
return g_hash_table_contains (priv->devices, device); return g_hash_table_contains (priv->devices, device);
} }
static NMSettingsConnection *
find_settings_connection (NMCheckpoint *self,
DeviceCheckpoint *dev_checkpoint,
gboolean *need_update,
gboolean *need_activation)
{
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
const GSList *active_connections, *iter;
NMActiveConnection *active = NULL;
NMSettingsConnection *connection;
const char *uuid, *ac_uuid;
*need_activation = FALSE;
*need_update = FALSE;
uuid = nm_connection_get_uuid (dev_checkpoint->settings_connection);
connection = nm_settings_get_connection_by_uuid (nm_settings_get (), uuid);
if (!connection)
return NULL;
/* Now check if the connection changed, ... */
if (!nm_connection_compare (dev_checkpoint->settings_connection,
NM_CONNECTION (connection),
NM_SETTING_COMPARE_FLAG_EXACT)) {
_LOGT ("rollback: settings connection %s changed", uuid);
*need_update = TRUE;
*need_activation = TRUE;
}
/* ... is active, ... */
active_connections = nm_manager_get_active_connections (priv->manager);
for (iter = active_connections; iter; iter = g_slist_next (iter)) {
active = iter->data;
ac_uuid = nm_settings_connection_get_uuid (nm_active_connection_get_settings_connection (active));
if (nm_streq (uuid, ac_uuid)) {
_LOGT ("rollback: connection %s is active", uuid);
break;
}
}
if (!iter) {
_LOGT ("rollback: connection %s is not active", uuid);
*need_activation = TRUE;
return connection;
}
/* ... or if the connection was reactivated/reapplied */
if (nm_active_connection_version_id_get (active) != dev_checkpoint->ac_version_id) {
_LOGT ("rollback: active connection version id of %s changed", uuid);
*need_activation = TRUE;
}
return connection;
}
GVariant * GVariant *
nm_checkpoint_rollback (NMCheckpoint *self) nm_checkpoint_rollback (NMCheckpoint *self)
{ {
@@ -135,7 +193,6 @@ nm_checkpoint_rollback (NMCheckpoint *self)
while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) { while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) {
gs_unref_object NMAuthSubject *subject = NULL; gs_unref_object NMAuthSubject *subject = NULL;
guint32 result = NM_ROLLBACK_RESULT_OK; guint32 result = NM_ROLLBACK_RESULT_OK;
const char *con_uuid;
_LOGD ("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)", _LOGD ("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)",
nm_device_get_iface (device), nm_device_get_iface (device),
@@ -180,27 +237,26 @@ activate:
} }
if (dev_checkpoint->applied_connection) { if (dev_checkpoint->applied_connection) {
/* The device had an active connection, check if the gboolean need_update, need_activation;
* connection still exists
* */
con_uuid = nm_connection_get_uuid (dev_checkpoint->settings_connection);
connection = nm_settings_get_connection_by_uuid (nm_settings_get (), con_uuid);
/* The device had an active connection: check if the
* connection still exists, is active and was changed */
connection = find_settings_connection (self, dev_checkpoint, &need_update, &need_activation);
if (connection) { if (connection) {
/* If the connection is still there, restore its content if (need_update) {
* and save it _LOGD ("rollback: updating connection %s",
* */ nm_settings_connection_get_uuid (connection));
_LOGD ("rollback: connection %s still exists", con_uuid); nm_connection_replace_settings_from_connection (NM_CONNECTION (connection),
dev_checkpoint->settings_connection);
nm_connection_replace_settings_from_connection (NM_CONNECTION (connection), nm_settings_connection_commit_changes (connection,
dev_checkpoint->settings_connection); NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
nm_settings_connection_commit_changes (connection, NULL,
NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, NULL);
NULL, }
NULL);
} else { } else {
/* The connection was deleted, recreate it */ /* The connection was deleted, recreate it */
_LOGD ("rollback: adding connection %s again", con_uuid); _LOGD ("rollback: adding connection %s again",
nm_connection_get_uuid (dev_checkpoint->settings_connection));
connection = nm_settings_add_connection (nm_settings_get (), connection = nm_settings_add_connection (nm_settings_get (),
dev_checkpoint->settings_connection, dev_checkpoint->settings_connection,
@@ -212,24 +268,28 @@ activate:
result = NM_ROLLBACK_RESULT_ERR_FAILED; result = NM_ROLLBACK_RESULT_ERR_FAILED;
goto next_dev; goto next_dev;
} }
need_activation = TRUE;
} }
/* Now re-activate the connection */ if (need_activation) {
subject = nm_auth_subject_new_internal (); _LOGD ("rollback: reactivating connection %s",
if (!nm_manager_activate_connection (priv->manager, nm_settings_connection_get_uuid (connection));
connection, subject = nm_auth_subject_new_internal ();
dev_checkpoint->applied_connection, if (!nm_manager_activate_connection (priv->manager,
NULL, connection,
device, dev_checkpoint->applied_connection,
subject, NULL,
&local_error)) { device,
_LOGW ("rollback: reactivation of connection %s/%s failed: %s", subject,
nm_connection_get_id ((NMConnection *) connection), &local_error)) {
nm_connection_get_uuid ((NMConnection * ) connection), _LOGW ("rollback: reactivation of connection %s/%s failed: %s",
local_error->message); nm_connection_get_id ((NMConnection *) connection),
g_clear_error (&local_error); nm_connection_get_uuid ((NMConnection * ) connection),
result = NM_ROLLBACK_RESULT_ERR_FAILED; local_error->message);
goto next_dev; g_clear_error (&local_error);
result = NM_ROLLBACK_RESULT_ERR_FAILED;
goto next_dev;
}
} }
} else { } else {
/* The device was initially disconnected, deactivate any existing connection */ /* The device was initially disconnected, deactivate any existing connection */
@@ -259,9 +319,8 @@ next_dev:
con = list[i]; con = list[i];
if (!g_hash_table_contains (priv->connection_uuids, if (!g_hash_table_contains (priv->connection_uuids,
nm_settings_connection_get_uuid (con))) { nm_settings_connection_get_uuid (con))) {
_LOGD ("rollback: deleting new connection %s (%s)", _LOGD ("rollback: deleting new connection %s",
nm_settings_connection_get_uuid (con), nm_settings_connection_get_uuid (con));
nm_settings_connection_get_id (con));
nm_settings_connection_delete (con, NULL, NULL); nm_settings_connection_delete (con, NULL, NULL);
} }
} }
@@ -300,6 +359,7 @@ device_checkpoint_create (NMDevice *device,
NMSettingsConnection *settings_connection; NMSettingsConnection *settings_connection;
const char *path; const char *path;
gboolean unmanaged_explicit; gboolean unmanaged_explicit;
NMActRequest *act_request;
path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device)); path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
unmanaged_explicit = !!nm_device_get_unmanaged_flags (device, unmanaged_explicit = !!nm_device_get_unmanaged_flags (device,
@@ -321,6 +381,11 @@ device_checkpoint_create (NMDevice *device,
g_return_val_if_fail (settings_connection, NULL); g_return_val_if_fail (settings_connection, NULL);
dev_checkpoint->settings_connection = dev_checkpoint->settings_connection =
nm_simple_connection_new_clone (NM_CONNECTION (settings_connection)); nm_simple_connection_new_clone (NM_CONNECTION (settings_connection));
act_request = nm_device_get_act_request (device);
g_return_val_if_fail (act_request, NULL);
dev_checkpoint->ac_version_id =
nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request));
} }
return dev_checkpoint; return dev_checkpoint;