mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-07 09:50:28 +01:00
checkpoint: allow overlapping checkpoints
Introduce a new flag NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING that allows the creation of overlapping checkpoints. Before, and by default, checkpoints that reference a same device conflict, and creating such a checkpoint failed. Now, allow this. But during rollback automatically destroy all overlapping checkpoints that were created after the checkpoint that is about to rollback. With this, you can create a series of checkpoints, and rollback them individually. With the restriction, that if you once rolled back to an older checkpoint, you no longer can roll"forward" to a younger one. What this implies and what is new here, is that the checkpoint might be automatically destroyed by NetworkManager before the timeout expires. When the user later would try to manually destroy/rollback such a checkpoint, it would fail because the checkpoint no longer exists.
This commit is contained in:
parent
e5fc9a307d
commit
5c283356a1
4 changed files with 78 additions and 30 deletions
|
|
@ -842,6 +842,18 @@ typedef enum {
|
|||
* delete any new connection added after the checkpoint (Since: 1.6)
|
||||
* @NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES: upon rollback,
|
||||
* disconnect any new device appeared after the checkpoint (Since: 1.6)
|
||||
* @NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING: by default, creating
|
||||
* a checkpoint fails if there are already existing checkoints that
|
||||
* reference the same devices. With this flag, creation of such
|
||||
* checkpoints is allowed, however, if an older checkpoint
|
||||
* that references overlapping devices gets rolled back, it will
|
||||
* automatically destroy this checkpoint during rollback. This
|
||||
* allows to create several overlapping checkpoints in parallel,
|
||||
* and rollback to them at will. With the special case that
|
||||
* rolling back to an older checkpoint will invalidate all
|
||||
* overlapping younger checkpoints. This opts-in that the
|
||||
* checkpoint can be automatically destroyed by the rollback
|
||||
* of an older checkpoint. (Since: 1.12)
|
||||
*
|
||||
* The flags for CheckpointCreate call
|
||||
*
|
||||
|
|
@ -852,6 +864,7 @@ typedef enum { /*< skip >*/
|
|||
NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 0x01,
|
||||
NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS = 0x02,
|
||||
NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES = 0x04,
|
||||
NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING = 0x08,
|
||||
} NMCheckpointCreateFlags;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ notify_checkpoints (NMCheckpointManager *self) {
|
|||
}
|
||||
|
||||
static void
|
||||
destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint)
|
||||
destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint, gboolean log_destroy)
|
||||
{
|
||||
nm_assert (NM_IS_CHECKPOINT (checkpoint));
|
||||
nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (checkpoint)));
|
||||
|
|
@ -73,6 +73,9 @@ destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint)
|
|||
|
||||
c_list_unlink (&checkpoint->checkpoints_lst);
|
||||
|
||||
if (log_destroy)
|
||||
nm_checkpoint_log_destroy (checkpoint);
|
||||
|
||||
notify_checkpoints (self);
|
||||
|
||||
nm_dbus_object_unexport (NM_DBUS_OBJECT (checkpoint));
|
||||
|
|
@ -83,9 +86,26 @@ static GVariant *
|
|||
rollback_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint)
|
||||
{
|
||||
GVariant *result;
|
||||
const CList *iter;
|
||||
|
||||
nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
|
||||
|
||||
/* we destroy first all overlapping checkpoints that are younger/newer. */
|
||||
for (iter = checkpoint->checkpoints_lst.next;
|
||||
iter != &self->checkpoints_lst_head;
|
||||
) {
|
||||
NMCheckpoint *cp = c_list_entry (iter, NMCheckpoint, checkpoints_lst);
|
||||
|
||||
iter = iter->next;
|
||||
if (nm_checkpoint_includes_devices_of (cp, checkpoint)) {
|
||||
/* the younger checkpoint has overlapping devices and gets obsoleted.
|
||||
* Destroy it. */
|
||||
destroy_checkpoint (self, cp, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
result = nm_checkpoint_rollback (checkpoint);
|
||||
destroy_checkpoint (self, checkpoint);
|
||||
destroy_checkpoint (self, checkpoint, FALSE);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -99,19 +119,6 @@ rollback_timeout_cb (NMCheckpoint *checkpoint,
|
|||
result = rollback_checkpoint (self, checkpoint);
|
||||
}
|
||||
|
||||
static NMCheckpoint *
|
||||
find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device)
|
||||
{
|
||||
NMCheckpoint *checkpoint;
|
||||
|
||||
c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) {
|
||||
if (nm_checkpoint_includes_device (checkpoint, device))
|
||||
return checkpoint;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NMCheckpoint *
|
||||
nm_checkpoint_manager_create (NMCheckpointManager *self,
|
||||
const char *const *device_paths,
|
||||
|
|
@ -123,7 +130,6 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
|
|||
NMCheckpoint *checkpoint;
|
||||
gs_unref_ptrarray GPtrArray *devices = NULL;
|
||||
NMDevice *device;
|
||||
guint i;
|
||||
|
||||
g_return_val_if_fail (self, FALSE);
|
||||
g_return_val_if_fail (!error || !*error, FALSE);
|
||||
|
|
@ -169,11 +175,12 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) {
|
||||
for (i = 0; i < devices->len; i++) {
|
||||
device = devices->pdata[i];
|
||||
checkpoint = find_checkpoint_for_device (self, device);
|
||||
if (checkpoint) {
|
||||
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
|
||||
nm_checkpoint_manager_destroy_all (self);
|
||||
else if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING)) {
|
||||
c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) {
|
||||
device = nm_checkpoint_includes_devices (checkpoint, (NMDevice *const*) devices->pdata, devices->len);
|
||||
if (device) {
|
||||
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
|
||||
"device '%s' is already included in checkpoint %s",
|
||||
nm_device_get_iface (device),
|
||||
|
|
@ -185,9 +192,6 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
|
|||
|
||||
checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags);
|
||||
|
||||
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
|
||||
nm_checkpoint_manager_destroy_all (self);
|
||||
|
||||
nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint));
|
||||
|
||||
nm_checkpoint_set_timeout_callback (checkpoint, rollback_timeout_cb, self);
|
||||
|
|
@ -204,7 +208,7 @@ nm_checkpoint_manager_destroy_all (NMCheckpointManager *self)
|
|||
g_return_if_fail (self);
|
||||
|
||||
while ((checkpoint = c_list_first_entry (&self->checkpoints_lst_head, NMCheckpoint, checkpoints_lst)))
|
||||
destroy_checkpoint (self, checkpoint);
|
||||
destroy_checkpoint (self, checkpoint, TRUE);
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
|
@ -232,7 +236,7 @@ nm_checkpoint_manager_destroy (NMCheckpointManager *self,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
destroy_checkpoint (self, checkpoint);
|
||||
destroy_checkpoint (self, checkpoint, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,12 @@ G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_DBUS_OBJECT)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
void
|
||||
nm_checkpoint_log_destroy (NMCheckpoint *self)
|
||||
{
|
||||
_LOGI ("destroy %s", nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
|
||||
}
|
||||
|
||||
void
|
||||
nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
|
||||
NMCheckpointTimeoutCallback callback,
|
||||
|
|
@ -114,12 +120,33 @@ nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
|
|||
priv->timeout_data = user_data;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_checkpoint_includes_device (NMCheckpoint *self, NMDevice *device)
|
||||
NMDevice *
|
||||
nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices)
|
||||
{
|
||||
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
|
||||
guint i;
|
||||
|
||||
return g_hash_table_contains (priv->devices, device);
|
||||
for (i = 0; i < n_devices; i++) {
|
||||
if (g_hash_table_contains (priv->devices, devices[i]))
|
||||
return devices[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NMDevice *
|
||||
nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices)
|
||||
{
|
||||
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
|
||||
NMCheckpointPrivate *priv2 = NM_CHECKPOINT_GET_PRIVATE (cp_for_devices);
|
||||
GHashTableIter iter;
|
||||
NMDevice *device;
|
||||
|
||||
g_hash_table_iter_init (&iter, priv2->devices);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL)) {
|
||||
if (g_hash_table_contains (priv->devices, device))
|
||||
return device;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static NMSettingsConnection *
|
||||
|
|
|
|||
|
|
@ -53,11 +53,15 @@ NMCheckpoint *nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32
|
|||
typedef void (*NMCheckpointTimeoutCallback) (NMCheckpoint *self,
|
||||
gpointer user_data);
|
||||
|
||||
void nm_checkpoint_log_destroy (NMCheckpoint *self);
|
||||
|
||||
void nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
|
||||
NMCheckpointTimeoutCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean nm_checkpoint_includes_device (NMCheckpoint *checkpoint, NMDevice *device);
|
||||
GVariant *nm_checkpoint_rollback (NMCheckpoint *self);
|
||||
|
||||
NMDevice *nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices);
|
||||
NMDevice *nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices);
|
||||
|
||||
#endif /* __NETWORKMANAGER_CHECKPOINT_H__ */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue