checkpoint: introduce new flags to better restore previous state

When a global checkpoint is created (one with empty device list) we
save the status of all devices to restore it later. After the
checkpoint new interfaces and connections may appear and they can
significantly influence the overall networking status, but we don't
consider them at the moment.

Introduce a new flag DELETE_NEW_CONNECTIONS to delete any connection
added after the checkpoint and similarly a DISCONNECT_NEW_DEVICES to
ensure that the connection active on newly appeared devices doesn't
disrupt network connectivity.

https://bugzilla.redhat.com/show_bug.cgi?id=1378393
This commit is contained in:
Beniamino Galvani 2016-09-23 09:37:42 +02:00
parent a5e3016fc9
commit ddeef464af
4 changed files with 69 additions and 5 deletions

View file

@ -712,14 +712,20 @@ typedef enum {
* @NM_CHECKPOINT_CREATE_FLAG_NONE: no flags
* @NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL: when creating
* a new checkpoint, destroy all existing ones.
* @NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS: upon rollback,
* delete any new connection added after the checkpoint.
* @NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES: upon rollback,
* disconnect any new device appeared after the checkpoint.
*
* The flags for CheckpointCreate call
*
* Since: 1.4
*/
typedef enum { /*< skip >*/
NM_CHECKPOINT_CREATE_FLAG_NONE = 0,
NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 0x01,
NM_CHECKPOINT_CREATE_FLAG_NONE = 0,
NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 0x01,
NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS = 0x02,
NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES = 0x04,
} NMCheckpointCreateFlags;
/**

View file

@ -155,6 +155,10 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
if (!device_paths || !device_paths[0]) {
device_paths_free = nm_manager_get_device_paths (manager);
device_paths = (const char *const *) device_paths_free;
} else if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) {
g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"the DISCONNECT_NEW_DEVICES flag can only be used with an empty device list");
return NULL;
}
devices = g_ptr_array_new ();
@ -180,7 +184,7 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
}
}
checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, error);
checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags, error);
if (!checkpoint)
return NULL;

View file

@ -61,6 +61,8 @@ typedef struct {
/* private members */
NMManager *manager;
gint64 rollback_ts;
NMCheckpointCreateFlags flags;
GHashTable *connection_uuids;
} NMCheckpointPrivate;
struct _NMCheckpoint {
@ -245,6 +247,47 @@ next_dev:
g_variant_builder_add (&builder, "{su}", dev_checkpoint->original_dev_path, result);
}
if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
NMSettingsConnection *con;
gs_free_slist GSList *list = NULL;
GSList *item;
g_return_val_if_fail (priv->connection_uuids, NULL);
list = nm_settings_get_connections_sorted (nm_settings_get ());
for (item = list; item; item = g_slist_next (item)) {
con = item->data;
if (!g_hash_table_contains (priv->connection_uuids,
nm_settings_connection_get_uuid (con))) {
_LOGD ("rollback: deleting new connection %s (%s)",
nm_settings_connection_get_uuid (con),
nm_settings_connection_get_id (con));
nm_settings_connection_delete (con, NULL, NULL);
}
}
}
if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) {
const GSList *list = nm_manager_get_devices (priv->manager);
NMDeviceState state;
NMDevice *dev;
for (list = nm_manager_get_devices (priv->manager); list ; list = g_slist_next (list)) {
dev = list->data;
if (!g_hash_table_contains (priv->devices, dev)) {
state = nm_device_get_state (dev);
if ( state > NM_DEVICE_STATE_DISCONNECTED
&& state < NM_DEVICE_STATE_DEACTIVATING) {
_LOGD ("rollback: disconnecting new device %s", nm_device_get_iface (dev));
nm_device_state_changed (dev,
NM_DEVICE_STATE_DEACTIVATING,
NM_DEVICE_STATE_REASON_USER_REQUESTED);
}
}
}
}
return g_variant_new ("(a{su})", &builder);
}
@ -340,10 +383,11 @@ nm_checkpoint_init (NMCheckpoint *self)
NMCheckpoint *
nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
GError **error)
NMCheckpointCreateFlags flags, GError **error)
{
NMCheckpoint *self;
NMCheckpointPrivate *priv;
NMSettingsConnection *const *con;
DeviceCheckpoint *dev_checkpoint;
NMDevice *device;
guint i;
@ -370,6 +414,15 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
priv->rollback_ts = rollback_timeout ?
(nm_utils_get_monotonic_timestamp_ms () + ((gint64) rollback_timeout * 1000)) :
0;
priv->flags = flags;
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
priv->connection_uuids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) {
g_hash_table_add (priv->connection_uuids,
g_strdup (nm_settings_connection_get_uuid (*con)));
}
}
for (i = 0; i < devices->len; i++) {
device = (NMDevice *) devices->pdata[i];
@ -391,6 +444,7 @@ dispose (GObject *object)
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
g_clear_pointer (&priv->devices, g_hash_table_unref);
g_clear_pointer (&priv->connection_uuids, g_hash_table_unref);
G_OBJECT_CLASS (nm_checkpoint_parent_class)->dispose (object);
}

View file

@ -41,7 +41,7 @@ typedef struct _NMCheckpointClass NMCheckpointClass;
GType nm_checkpoint_get_type (void);
NMCheckpoint *nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
GError **error);
NMCheckpointCreateFlags flags, GError **error);
guint64 nm_checkpoint_get_rollback_ts (NMCheckpoint *checkpoint);
gboolean nm_checkpoint_includes_device (NMCheckpoint *checkpoint, NMDevice *device);