merge: branch 'fg/checkpoint-devices-rh1578335-pt2'

https://bugzilla.redhat.com/show_bug.cgi?id=1578335

https://github.com/NetworkManager/NetworkManager/pull/316
This commit is contained in:
Francesco Giudici 2019-04-10 10:44:18 +02:00
commit 3f62c49295

View file

@ -39,6 +39,7 @@
typedef struct {
char *original_dev_path;
char *original_dev_name;
NMDeviceType dev_type;
NMDevice *device;
NMConnection *applied_connection;
NMConnection *settings_connection;
@ -61,6 +62,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMCheckpoint,
struct _NMCheckpointPrivate {
/* properties */
GHashTable *devices;
GPtrArray *removed_devices;
gint64 created_at_ms;
guint32 rollback_timeout_s;
guint timeout_id;
@ -68,6 +70,7 @@ struct _NMCheckpointPrivate {
NMManager *manager;
NMCheckpointCreateFlags flags;
GHashTable *connection_uuids;
gulong dev_removed_id;
NMCheckpointTimeoutCallback timeout_cb;
gpointer timeout_data;
@ -206,24 +209,136 @@ find_settings_connection (NMCheckpoint *self,
return sett_conn;
}
static gboolean
restore_and_activate_connection (NMCheckpoint *self,
DeviceCheckpoint *dev_checkpoint)
{
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
NMSettingsConnection *connection;
gs_unref_object NMAuthSubject *subject = NULL;
GError *local_error = NULL;
gboolean need_update, need_activation;
connection = find_settings_connection (self,
dev_checkpoint,
&need_update,
&need_activation);
if (connection) {
if (need_update) {
_LOGD ("rollback: updating connection %s",
nm_settings_connection_get_uuid (connection));
nm_settings_connection_update (connection,
dev_checkpoint->settings_connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
"checkpoint-rollback",
NULL);
}
} else {
/* The connection was deleted, recreate it */
_LOGD ("rollback: adding connection %s again",
nm_connection_get_uuid (dev_checkpoint->settings_connection));
connection = nm_settings_add_connection (nm_settings_get (),
dev_checkpoint->settings_connection,
TRUE,
&local_error);
if (!connection) {
_LOGD ("rollback: connection add failure: %s", local_error->message);
g_clear_error (&local_error);
return FALSE;
}
/* If the device is software, a brand new NMDevice may have been created */
if ( dev_checkpoint->is_software
&& !dev_checkpoint->device) {
dev_checkpoint->device = nm_manager_get_device (priv->manager,
dev_checkpoint->original_dev_name,
dev_checkpoint->dev_type);
nm_g_object_ref (dev_checkpoint->device);
}
need_activation = TRUE;
}
if (!dev_checkpoint->device) {
_LOGD ("rollback: device cannot be restored");
return FALSE;
}
if (need_activation) {
_LOGD ("rollback: reactivating connection %s",
nm_settings_connection_get_uuid (connection));
subject = nm_auth_subject_new_internal ();
/* Disconnect the device if needed. This necessary because now
* the manager prevents the reactivation of the same connection by
* an internal subject. */
if ( nm_device_get_state (dev_checkpoint->device) > NM_DEVICE_STATE_DISCONNECTED
&& nm_device_get_state (dev_checkpoint->device) < NM_DEVICE_STATE_DEACTIVATING) {
nm_device_state_changed (dev_checkpoint->device,
NM_DEVICE_STATE_DEACTIVATING,
NM_DEVICE_STATE_REASON_NEW_ACTIVATION);
}
if (!nm_manager_activate_connection (priv->manager,
connection,
dev_checkpoint->applied_connection,
NULL,
dev_checkpoint->device,
subject,
NM_ACTIVATION_TYPE_MANAGED,
dev_checkpoint->activation_reason,
dev_checkpoint->activation_lifetime_bound_to_profile_visiblity
? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY
: NM_ACTIVATION_STATE_FLAG_NONE,
&local_error)) {
_LOGW ("rollback: reactivation of connection %s/%s failed: %s",
nm_settings_connection_get_id (connection),
nm_settings_connection_get_uuid (connection),
local_error->message);
g_clear_error (&local_error);
return FALSE;
}
}
return TRUE;
}
GVariant *
nm_checkpoint_rollback (NMCheckpoint *self)
{
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
DeviceCheckpoint *dev_checkpoint;
GHashTableIter iter;
NMSettingsConnection *connection;
NMDevice *device;
GError *local_error = NULL;
GVariantBuilder builder;
uint i;
_LOGI ("rollback of %s", nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{su}"));
/* Start creating removed devices (if any and if possible) */
if (priv->removed_devices) {
for (i = 0; i < priv->removed_devices->len; i++) {
guint32 result = NM_ROLLBACK_RESULT_OK;
dev_checkpoint = priv->removed_devices->pdata[i];
_LOGD ("rollback: restoring removed device %s (state %d, realized %d, explicitly unmanaged %d)",
dev_checkpoint->original_dev_name,
(int) dev_checkpoint->state,
dev_checkpoint->realized,
dev_checkpoint->unmanaged_explicit);
if (dev_checkpoint->applied_connection) {
if (!restore_and_activate_connection (self, dev_checkpoint))
result = NM_ROLLBACK_RESULT_ERR_FAILED;
}
g_variant_builder_add (&builder, "{su}", dev_checkpoint->original_dev_path, result);
}
}
/* Start rolling-back each device */
g_hash_table_iter_init (&iter, priv->devices);
while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) {
gs_unref_object NMAuthSubject *subject = NULL;
guint32 result = NM_ROLLBACK_RESULT_OK;
_LOGD ("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)",
@ -279,75 +394,9 @@ nm_checkpoint_rollback (NMCheckpoint *self)
activate:
if (dev_checkpoint->applied_connection) {
gboolean need_update, need_activation;
/* 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 (need_update) {
_LOGD ("rollback: updating connection %s",
nm_settings_connection_get_uuid (connection));
nm_settings_connection_update (connection,
dev_checkpoint->settings_connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
"checkpoint-rollback",
NULL);
}
} else {
/* The connection was deleted, recreate it */
_LOGD ("rollback: adding connection %s again",
nm_connection_get_uuid (dev_checkpoint->settings_connection));
connection = nm_settings_add_connection (nm_settings_get (),
dev_checkpoint->settings_connection,
TRUE,
&local_error);
if (!connection) {
_LOGD ("rollback: connection add failure: %s", local_error->message);
g_clear_error (&local_error);
result = NM_ROLLBACK_RESULT_ERR_FAILED;
goto next_dev;
}
need_activation = TRUE;
}
if (need_activation) {
_LOGD ("rollback: reactivating connection %s",
nm_settings_connection_get_uuid (connection));
subject = nm_auth_subject_new_internal ();
/* Disconnect the device if needed. This necessary because now
* the manager prevents the reactivation of the same connection by
* an internal subject. */
if ( nm_device_get_state (device) > NM_DEVICE_STATE_DISCONNECTED
&& nm_device_get_state (device) < NM_DEVICE_STATE_DEACTIVATING) {
nm_device_state_changed (device,
NM_DEVICE_STATE_DEACTIVATING,
NM_DEVICE_STATE_REASON_NEW_ACTIVATION);
}
if (!nm_manager_activate_connection (priv->manager,
connection,
dev_checkpoint->applied_connection,
NULL,
device,
subject,
NM_ACTIVATION_TYPE_MANAGED,
dev_checkpoint->activation_reason,
dev_checkpoint->activation_lifetime_bound_to_profile_visiblity
? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY
: NM_ACTIVATION_STATE_FLAG_NONE,
&local_error)) {
_LOGW ("rollback: reactivation of connection %s/%s failed: %s",
nm_settings_connection_get_id (connection),
nm_settings_connection_get_uuid (connection),
local_error->message);
g_clear_error (&local_error);
result = NM_ROLLBACK_RESULT_ERR_FAILED;
goto next_dev;
}
if (!restore_and_activate_connection (self, dev_checkpoint)) {
result = NM_ROLLBACK_RESULT_ERR_FAILED;
goto next_dev;
}
} else {
/* The device was initially disconnected, deactivate any existing connection */
@ -368,7 +417,6 @@ next_dev:
if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
NMSettingsConnection *con;
gs_free NMSettingsConnection **list = NULL;
guint i;
g_return_val_if_fail (priv->connection_uuids, NULL);
list = nm_settings_get_connections_clone (nm_settings_get (), NULL,
@ -408,11 +456,52 @@ next_dev:
return g_variant_new ("(a{su})", &builder);
}
static void
device_checkpoint_destroy (gpointer data)
{
DeviceCheckpoint *dev_checkpoint = data;
nm_clear_g_signal_handler (dev_checkpoint->device, &dev_checkpoint->dev_exported_change_id);
g_clear_object (&dev_checkpoint->applied_connection);
g_clear_object (&dev_checkpoint->settings_connection);
g_clear_object (&dev_checkpoint->device);
g_free (dev_checkpoint->original_dev_path);
g_free (dev_checkpoint->original_dev_name);
g_slice_free (DeviceCheckpoint, dev_checkpoint);
}
static void
_move_dev_to_removed_devices (NMDevice *device,
NMCheckpoint *checkpoint)
{
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (checkpoint);
DeviceCheckpoint *dev_checkpoint;
g_return_if_fail (device);
dev_checkpoint = g_hash_table_lookup (priv->devices, device);
if (!dev_checkpoint)
return;
g_hash_table_steal (priv->devices, dev_checkpoint->device);
nm_clear_g_signal_handler (dev_checkpoint->device,
&dev_checkpoint->dev_exported_change_id);
g_clear_object (&dev_checkpoint->device);
if (!priv->removed_devices)
priv->removed_devices = g_ptr_array_new_with_free_func ((GDestroyNotify) device_checkpoint_destroy);
g_ptr_array_add (priv->removed_devices, dev_checkpoint);
_notify (checkpoint, PROP_DEVICES);
}
static void
_dev_exported_changed (NMDBusObject *obj,
NMCheckpoint *checkpoint)
{
_notify (checkpoint, PROP_DEVICES);
_move_dev_to_removed_devices (NM_DEVICE (obj), checkpoint);
}
static DeviceCheckpoint *
@ -433,6 +522,7 @@ device_checkpoint_create (NMCheckpoint *checkpoint, NMDevice *device)
dev_checkpoint->device = g_object_ref (device);
dev_checkpoint->original_dev_path = g_strdup (path);
dev_checkpoint->original_dev_name = g_strdup (nm_device_get_iface (device));
dev_checkpoint->dev_type = nm_device_get_device_type (device);
dev_checkpoint->state = nm_device_get_state (device);
dev_checkpoint->is_software = nm_device_is_software (device);
dev_checkpoint->realized = nm_device_is_real (device);
@ -463,21 +553,6 @@ device_checkpoint_create (NMCheckpoint *checkpoint, NMDevice *device)
return dev_checkpoint;
}
static void
device_checkpoint_destroy (gpointer data)
{
DeviceCheckpoint *dev_checkpoint = data;
nm_clear_g_signal_handler (dev_checkpoint->device, &dev_checkpoint->dev_exported_change_id);
g_clear_object (&dev_checkpoint->applied_connection);
g_clear_object (&dev_checkpoint->settings_connection);
g_clear_object (&dev_checkpoint->device);
g_free (dev_checkpoint->original_dev_path);
g_free (dev_checkpoint->original_dev_name);
g_slice_free (DeviceCheckpoint, dev_checkpoint);
}
static gboolean
_timeout_cb (gpointer user_data)
{
@ -575,6 +650,12 @@ nm_checkpoint_init (NMCheckpoint *self)
NULL, device_checkpoint_destroy);
}
static void
_device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
{
_move_dev_to_removed_devices (device, NM_CHECKPOINT (user_data));
}
NMCheckpoint *
nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout_s,
NMCheckpointCreateFlags flags)
@ -592,7 +673,7 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
self = g_object_new (NM_TYPE_CHECKPOINT, NULL);
priv = NM_CHECKPOINT_GET_PRIVATE (self);
priv->manager = manager;
priv->manager = g_object_ref (manager);
priv->rollback_timeout_s = rollback_timeout_s;
priv->created_at_ms = nm_utils_get_monotonic_timestamp_ms ();
priv->flags = flags;
@ -623,6 +704,10 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
device_checkpoint_create (self, device));
}
priv->dev_removed_id = g_signal_connect (priv->manager,
NM_MANAGER_DEVICE_REMOVED,
G_CALLBACK (_device_removed),
self);
return self;
}
@ -636,6 +721,10 @@ dispose (GObject *object)
g_clear_pointer (&priv->devices, g_hash_table_unref);
g_clear_pointer (&priv->connection_uuids, g_hash_table_unref);
nm_clear_pointer (&priv->removed_devices, g_ptr_array_unref);
nm_clear_g_signal_handler (priv->manager, &priv->dev_removed_id);
g_clear_object (&priv->manager);
nm_clear_g_source (&priv->timeout_id);