checkpoint: merge branch 'bg/checkpoint-improvements-bgo770315'

https://bugzilla.gnome.org/show_bug.cgi?id=770315
This commit is contained in:
Beniamino Galvani 2016-09-26 15:10:44 +02:00
commit d27ae4a8fe
11 changed files with 186 additions and 99 deletions

View file

@ -20,36 +20,48 @@
import dbus, sys
# This example takes a device interface name as a parameter and tells
# NetworkManager to disconnect that device, closing down any network
# connection it may have
if len(sys.argv) != 2:
raise Exception("Usage: %s <interface>" % sys.argv[0])
bus = dbus.SystemBus()
# This example takes a list of device interface names as a parameter
# and tells NetworkManager to create a checkpoint on those devices. It
# is then possible to restore or destroy the checkpoint.
# Get a proxy for the base NetworkManager object
bus = dbus.SystemBus()
proxy = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
manager = dbus.Interface(proxy, "org.freedesktop.NetworkManager")
allDevs = manager.GetDevices()
dpath = None
def Usage():
print "Usage: %s <ROLLBACK-INTERVAL> [INTERFACE]..." % sys.argv[0]
sys.exit(1)
# Find the device
devices = manager.GetDevices()
for d in devices:
dev_proxy = bus.get_object("org.freedesktop.NetworkManager", d)
prop_iface = dbus.Interface(dev_proxy, "org.freedesktop.DBus.Properties")
iface = prop_iface.Get("org.freedesktop.NetworkManager.Device", "Interface")
if iface == sys.argv[1]:
dpath = d
break
def GetDevicePath(ifname):
for dev in allDevs:
dev_proxy = bus.get_object("org.freedesktop.NetworkManager", dev)
prop_iface = dbus.Interface(dev_proxy, "org.freedesktop.DBus.Properties")
interface = prop_iface.Get("org.freedesktop.NetworkManager.Device", "Interface")
if interface == ifname:
return dev
return
if not dpath or not len(dpath):
raise Exception("NetworkManager knows nothing about %s" % sys.argv[1])
if len(sys.argv) < 2:
Usage()
checkpoint = manager.CheckpointCreate([ dpath ],
0, # no rollback
try:
interval = int(sys.argv[1])
except ValueError:
Usage()
devList = []
for arg in sys.argv[2:]:
path = GetDevicePath(arg)
if path == None:
raise Exception("NetworkManager knows nothing about %s" % arg)
else:
devList.append(path)
checkpoint = manager.CheckpointCreate(devList,
interval,
1); # DESTROY_ALL
choice = raw_input('Do you want to rollback [y/n]? ').lower()

View file

@ -209,7 +209,7 @@
<!--
CheckpointCreate:
@devices: a list of device paths for which a checkpoint should be created. An empty list means all managed devices.
@devices: a list of device paths for which a checkpoint should be created. An empty list means all devices.
@rollback_timeout: the time in seconds until NetworkManager will automatically rollback to the checkpoint. Set to zero for infinite.
@flags: optional flags that influence the creation.
@checkpoint: on success, returns the path of the checkpoint.

View file

@ -462,17 +462,20 @@ master_failed (NMActiveConnection *self)
* nm_act_request_new:
*
* @settings_connection: (allow-none): the connection to activate @device with
* @applied_connection: (allow-none): the applied connection
* @specific_object: the object path of the specific object (ie, WiFi access point,
* etc) that will be used to activate @connection and @device
* @subject: the #NMAuthSubject representing the requestor of the activation
* @device: the device/interface to configure according to @connection
*
* Creates a new device-based activation request.
* Creates a new device-based activation request. If an applied connection is
* supplied, it shall not be modified by the caller afterwards.
*
* Returns: the new activation request on success, %NULL on error.
*/
NMActRequest *
nm_act_request_new (NMSettingsConnection *settings_connection,
NMConnection *applied_connection,
const char *specific_object,
NMAuthSubject *subject,
NMDevice *device)
@ -482,6 +485,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection,
g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
return (NMActRequest *) g_object_new (NM_TYPE_ACT_REQUEST,
NM_ACTIVE_CONNECTION_INT_APPLIED_CONNECTION, applied_connection,
NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION, settings_connection,
NM_ACTIVE_CONNECTION_INT_DEVICE, device,
NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object,

View file

@ -46,6 +46,7 @@ typedef struct {
GType nm_act_request_get_type (void);
NMActRequest *nm_act_request_new (NMSettingsConnection *settings_connection,
NMConnection *applied_connection,
const char *specific_object,
NMAuthSubject *subject,
NMDevice *device);

View file

@ -90,6 +90,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection,
PROP_MASTER,
PROP_INT_SETTINGS_CONNECTION,
PROP_INT_APPLIED_CONNECTION,
PROP_INT_DEVICE,
PROP_INT_SUBJECT,
PROP_INT_MASTER,
@ -951,6 +952,14 @@ constructed (GObject *object)
G_OBJECT_CLASS (nm_active_connection_parent_class)->constructed (object);
if (!priv->applied_connection && priv->settings_connection) {
priv->applied_connection =
nm_simple_connection_new_clone ((NMConnection *) priv->settings_connection);
}
if (priv->applied_connection)
nm_connection_clear_secrets (priv->applied_connection);
_LOGD ("constructed (%s, version-id %llu)", G_OBJECT_TYPE_NAME (self), (long long unsigned) priv->version_id);
g_return_if_fail (priv->subject);
@ -964,16 +973,20 @@ set_property (GObject *object, guint prop_id,
NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
const char *tmp;
NMSettingsConnection *con;
NMConnection *acon;
switch (prop_id) {
case PROP_INT_SETTINGS_CONNECTION:
/* construct-only */
con = g_value_get_object (value);
if (con) {
if (con)
_set_settings_connection (self, con);
priv->applied_connection = nm_simple_connection_new_clone ((NMConnection *) priv->settings_connection);
nm_connection_clear_secrets (priv->applied_connection);
}
break;
case PROP_INT_APPLIED_CONNECTION:
/* construct-only */
acon = g_value_get_object (value);
if (acon)
priv->applied_connection = g_object_ref (acon);
break;
case PROP_INT_DEVICE:
/* construct-only */
@ -1259,6 +1272,12 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_INT_APPLIED_CONNECTION] =
g_param_spec_object (NM_ACTIVE_CONNECTION_INT_APPLIED_CONNECTION, "", "",
NM_TYPE_CONNECTION,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_INT_DEVICE] =
g_param_spec_object (NM_ACTIVE_CONNECTION_INT_DEVICE, "", "",
NM_TYPE_DEVICE,

View file

@ -50,6 +50,7 @@
/* Internal non-exported properties */
#define NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION "int-settings-connection"
#define NM_ACTIVE_CONNECTION_INT_APPLIED_CONNECTION "int-applied-connection"
#define NM_ACTIVE_CONNECTION_INT_DEVICE "int-device"
#define NM_ACTIVE_CONNECTION_INT_SUBJECT "int-subject"
#define NM_ACTIVE_CONNECTION_INT_MASTER "int-master"

View file

@ -145,19 +145,27 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
NMCheckpointCreateFlags flags,
GError **error)
{
NMManager *manager;
NMCheckpoint *checkpoint;
const char * const *path;
gs_unref_ptrarray GPtrArray *devices = NULL;
NMDevice *device;
const char *checkpoint_path;
gs_free const char **device_paths_free = NULL;
guint i;
g_return_val_if_fail (self, FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
manager = GET_MANAGER (self);
if (!device_paths || !device_paths[0]) {
device_paths_free = nm_manager_get_device_paths (manager);
device_paths = (const char *const *) device_paths_free;
}
devices = g_ptr_array_new ();
for (path = device_paths; *path; path++) {
device = nm_manager_get_device_by_path (GET_MANAGER (self), *path);
device = nm_manager_get_device_by_path (manager, *path);
if (!device) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
"device %s does not exist", *path);
@ -178,8 +186,7 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
}
}
checkpoint = nm_checkpoint_new (GET_MANAGER (self), devices,
rollback_timeout, error);
checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, error);
if (!checkpoint)
return NULL;

View file

@ -55,7 +55,11 @@
typedef struct {
char *original_dev_path;
NMDevice *device;
NMConnection *connection;
NMConnection *applied_connection;
NMConnection *settings_connection;
NMDeviceState state;
bool realized:1;
bool unmanaged_explicit:1;
} DeviceCheckpoint;
typedef struct {
@ -122,47 +126,75 @@ nm_checkpoint_rollback (NMCheckpoint *self)
while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) {
gs_unref_object NMAuthSubject *subject = NULL;
guint32 result = NM_ROLLBACK_RESULT_OK;
const char *con_path;
const char *con_uuid;
_LOGD ("rollback: restoring state of device %s", nm_device_get_iface (device));
_LOGD ("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)",
nm_device_get_iface (device),
(int) dev_checkpoint->state,
dev_checkpoint->realized,
dev_checkpoint->unmanaged_explicit);
if (!nm_device_is_real (device)) {
result = NM_ROLLBACK_RESULT_ERR_NO_DEVICE;
_LOGD ("rollback: device is not realized");
if (nm_device_is_real (device)) {
if (!dev_checkpoint->realized) {
_LOGD ("rollback: device was not realized, unmanage it");
nm_device_set_unmanaged_by_flags_queue (device,
NM_UNMANAGED_USER_EXPLICIT,
TRUE,
NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
goto next_dev;
}
} else {
if (dev_checkpoint->realized) {
if (nm_device_is_software (device)) {
/* try to recreate software device */
_LOGD ("rollback: software device not realized, will re-activate");
goto activate;
} else {
_LOGD ("rollback: device is not realized");
result = NM_ROLLBACK_RESULT_ERR_FAILED;
}
}
goto next_dev;
}
if (nm_device_get_state (device) <= NM_DEVICE_STATE_UNMANAGED) {
result = NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED;
_LOGD ("rollback: device is unmanaged");
activate:
if (dev_checkpoint->state == NM_DEVICE_STATE_UNMANAGED) {
if ( nm_device_get_state (device) != NM_DEVICE_STATE_UNMANAGED
|| dev_checkpoint->unmanaged_explicit) {
_LOGD ("rollback: explicitly unmanage device");
nm_device_set_unmanaged_by_flags_queue (device,
NM_UNMANAGED_USER_EXPLICIT,
TRUE,
NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
}
goto next_dev;
}
if (dev_checkpoint->connection) {
if (dev_checkpoint->applied_connection) {
/* The device had an active connection, check if the
* connection still exists
* */
con_path = nm_connection_get_path (dev_checkpoint->connection);
connection = nm_settings_get_connection_by_path (nm_settings_get(), con_path);
con_uuid = nm_connection_get_uuid (dev_checkpoint->settings_connection);
connection = nm_settings_get_connection_by_uuid (nm_settings_get (), con_uuid);
if (connection) {
/* If the connection is still there, restore its content
* and save it
* */
_LOGD ("rollback: connection %s still exists", con_path);
_LOGD ("rollback: connection %s still exists", con_uuid);
nm_connection_replace_settings_from_connection (NM_CONNECTION (connection),
dev_checkpoint->connection);
dev_checkpoint->settings_connection);
nm_settings_connection_commit_changes (connection,
NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
NULL,
NULL);
} else {
/* The connection was deleted, recreate it */
_LOGD ("rollback: adding connection %s again", con_path);
_LOGD ("rollback: adding connection %s again", con_uuid);
connection = nm_settings_add_connection (nm_settings_get (),
dev_checkpoint->connection,
dev_checkpoint->settings_connection,
TRUE,
&local_error);
if (!connection) {
@ -177,6 +209,7 @@ nm_checkpoint_rollback (NMCheckpoint *self)
subject = nm_auth_subject_new_internal ();
if (!nm_manager_activate_connection (priv->manager,
connection,
dev_checkpoint->applied_connection,
NULL,
device,
subject,
@ -213,36 +246,32 @@ device_checkpoint_create (NMDevice *device,
GError **error)
{
DeviceCheckpoint *dev_checkpoint;
NMConnection *connection;
NMConnection *applied_connection;
NMSettingsConnection *settings_connection;
const char *path;
if (!nm_device_is_real (device)) {
g_set_error (error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"device '%s' is not realized",
nm_device_get_iface (device));
return NULL;
}
if (nm_device_get_state (device) <= NM_DEVICE_STATE_UNMANAGED) {
g_set_error (error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"device '%s' is unmanaged",
nm_device_get_iface (device));
return NULL;
}
gboolean unmanaged_explicit;
path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
unmanaged_explicit = !!nm_device_get_unmanaged_flags (device,
NM_UNMANAGED_USER_EXPLICIT);
dev_checkpoint = g_slice_new0 (DeviceCheckpoint);
dev_checkpoint->device = g_object_ref (device);
dev_checkpoint->original_dev_path = g_strdup (path);
dev_checkpoint->state = nm_device_get_state (device);
dev_checkpoint->realized = nm_device_is_real (device);
dev_checkpoint->unmanaged_explicit = unmanaged_explicit;
connection = nm_device_get_applied_connection (device);
if (connection)
dev_checkpoint->connection = nm_simple_connection_new_clone (connection);
applied_connection = nm_device_get_applied_connection (device);
if (applied_connection) {
dev_checkpoint->applied_connection =
nm_simple_connection_new_clone (applied_connection);
settings_connection = nm_device_get_settings_connection (device);
g_return_val_if_fail (settings_connection, NULL);
dev_checkpoint->settings_connection =
nm_simple_connection_new_clone (NM_CONNECTION (settings_connection));
}
return dev_checkpoint;
}
@ -252,7 +281,8 @@ device_checkpoint_destroy (gpointer data)
{
DeviceCheckpoint *dev_checkpoint = data;
g_clear_object (&dev_checkpoint->connection);
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);
@ -268,29 +298,6 @@ nm_checkpoint_init (NMCheckpoint *self)
NULL, device_checkpoint_destroy);
}
static void
get_all_devices (NMManager *manager, GPtrArray *devices)
{
const GSList *list, *iter;
NMDevice *dev;
list = nm_manager_get_devices (manager);
for (iter = list; iter; iter = g_slist_next (iter)) {
dev = iter->data;
if (!nm_device_is_real (dev))
continue;
if (nm_device_get_state (dev) <= NM_DEVICE_STATE_UNMANAGED)
continue;
/* We never touch assumed connections, unless told explicitly */
if (nm_device_uses_assumed_connection (dev))
continue;
g_ptr_array_add (devices, dev);
}
}
NMCheckpoint *
nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
GError **error)
@ -305,9 +312,6 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
g_return_val_if_fail (devices, NULL);
g_return_val_if_fail (!error || !*error, NULL);
if (!devices->len)
get_all_devices (manager, devices);
if (!devices->len) {
g_set_error_literal (error,
NM_MANAGER_ERROR,

View file

@ -64,6 +64,7 @@ static gboolean add_device (NMManager *self, NMDevice *device, GError **error);
static NMActiveConnection *_new_active_connection (NMManager *self,
NMConnection *connection,
NMConnection *applied,
const char *specific_object,
NMDevice *device,
NMAuthSubject *subject,
@ -1745,7 +1746,7 @@ assume_connection (NMManager *self, NMDevice *device, NMSettingsConnection *conn
g_return_val_if_fail (nm_device_get_state (device) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
subject = nm_auth_subject_new_internal ();
active = _new_active_connection (self, NM_CONNECTION (connection), NULL, device, subject, &error);
active = _new_active_connection (self, NM_CONNECTION (connection), NULL, NULL, device, subject, &error);
g_object_unref (subject);
if (!active) {
@ -2288,6 +2289,28 @@ nm_manager_get_devices (NMManager *manager)
return NM_MANAGER_GET_PRIVATE (manager)->devices;
}
const char **
nm_manager_get_device_paths (NMManager *self)
{
const GSList *devices, *iter;
GPtrArray *paths;
const char *path;
g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
devices = NM_MANAGER_GET_PRIVATE (self)->devices;
paths = g_ptr_array_new ();
for (iter = devices; iter; iter = g_slist_next (iter)) {
path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data));
if (path)
g_ptr_array_add (paths, (gpointer) path);
}
g_ptr_array_add (paths, NULL);
return (const char **) g_ptr_array_free (paths, FALSE);
}
static NMDevice *
nm_manager_get_connection_device (NMManager *self,
NMConnection *connection)
@ -2612,6 +2635,7 @@ ensure_master_active_connection (NMManager *self,
master_ac = nm_manager_activate_connection (self,
candidate,
NULL,
NULL,
master_device,
subject,
error);
@ -2658,6 +2682,7 @@ ensure_master_active_connection (NMManager *self,
master_ac = nm_manager_activate_connection (self,
master_connection,
NULL,
NULL,
candidate,
subject,
error);
@ -2782,6 +2807,7 @@ autoconnect_slaves (NMManager *self,
nm_manager_activate_connection (self,
slave_connection,
NULL,
NULL,
nm_manager_get_best_device_for_connection (self, NM_CONNECTION (slave_connection), FALSE),
subject,
&local_err);
@ -2941,7 +2967,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
return FALSE;
}
parent_ac = nm_manager_activate_connection (self, parent_con, NULL, parent, subject, error);
parent_ac = nm_manager_activate_connection (self, parent_con, NULL, NULL, parent, subject, error);
if (!parent_ac) {
g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device));
return FALSE;
@ -3123,6 +3149,7 @@ _new_vpn_active_connection (NMManager *self,
static NMActiveConnection *
_new_active_connection (NMManager *self,
NMConnection *connection,
NMConnection *applied,
const char *specific_object,
NMDevice *device,
NMAuthSubject *subject,
@ -3162,6 +3189,7 @@ _new_active_connection (NMManager *self,
}
return (NMActiveConnection *) nm_act_request_new (settings_connection,
applied,
specific_object,
subject,
device);
@ -3212,6 +3240,7 @@ _internal_activation_auth_done (NMActiveConnection *active,
* nm_manager_activate_connection():
* @self: the #NMManager
* @connection: the #NMSettingsConnection to activate on @device
* @applied: (allow-none): the applied connection to activate on @device
* @specific_object: the specific object path, if any, for the activation
* @device: the #NMDevice to activate @connection on
* @subject: the subject which requested activation
@ -3221,7 +3250,8 @@ _internal_activation_auth_done (NMActiveConnection *active,
* @subject should be the subject of the activation that triggered this
* one, or if this is an autoconnect request, a new internal subject.
* The returned #NMActiveConnection is owned by the Manager and should be
* referenced by the caller if the caller continues to use it.
* referenced by the caller if the caller continues to use it. If @applied
* is supplied, it shall not be modified by the caller afterwards.
*
* Returns: (transfer none): the new #NMActiveConnection that tracks
* activation of @connection on @device
@ -3229,6 +3259,7 @@ _internal_activation_auth_done (NMActiveConnection *active,
NMActiveConnection *
nm_manager_activate_connection (NMManager *self,
NMSettingsConnection *connection,
NMConnection *applied,
const char *specific_object,
NMDevice *device,
NMAuthSubject *subject,
@ -3274,6 +3305,7 @@ nm_manager_activate_connection (NMManager *self,
active = _new_active_connection (self,
NM_CONNECTION (connection),
applied,
specific_object,
device,
subject,
@ -3528,6 +3560,7 @@ impl_manager_activate_connection (NMManager *self,
active = _new_active_connection (self,
NM_CONNECTION (connection),
NULL,
specific_object_path,
device,
subject,
@ -3735,6 +3768,7 @@ impl_manager_add_and_activate_connection (NMManager *self,
active = _new_active_connection (self,
connection,
NULL,
specific_object_path,
device,
subject,

View file

@ -91,6 +91,7 @@ void nm_manager_write_device_state (NMManager *manager);
/* Device handling */
const GSList * nm_manager_get_devices (NMManager *manager);
const char ** nm_manager_get_device_paths (NMManager *self);
NMDevice * nm_manager_get_device_by_ifindex (NMManager *manager,
int ifindex);
@ -104,6 +105,7 @@ char * nm_manager_get_connection_iface (NMManager *self,
NMActiveConnection *nm_manager_activate_connection (NMManager *manager,
NMSettingsConnection *connection,
NMConnection *applied_connection,
const char *specific_object,
NMDevice *device,
NMAuthSubject *subject,

View file

@ -724,6 +724,7 @@ auto_activate_device (gpointer user_data)
subject = nm_auth_subject_new_internal ();
if (!nm_manager_activate_connection (priv->manager,
best_connection,
NULL,
specific_object,
data->device,
subject,
@ -1117,6 +1118,7 @@ activate_secondary_connections (NMPolicy *self,
nm_connection_get_id (connection), nm_connection_get_uuid (connection));
ac = nm_manager_activate_connection (priv->manager,
settings_con,
NULL,
nm_exported_object_get_path (NM_EXPORTED_OBJECT (req)),
device,
nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)),
@ -1520,6 +1522,7 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *self)
connection,
NULL,
NULL,
NULL,
nm_active_connection_get_subject (ac),
&error)) {
_LOGW (LOGD_DEVICE, "VPN '%s' reconnect failed: %s",