merge: activate slaves when master is activated (bgo #735052) (rh #1158529)

When a master connection is activated, check all its slaves and decide whether
they should be activated as well. This is done according to the
connection.autoconnect-slaves property.

https://bugzilla.gnome.org/show_bug.cgi?id=735052
https://bugzilla.redhat.com/show_bug.cgi?id=1158529

(cherry-picked from b18f328967)
This commit is contained in:
Jiří Klimeš 2015-06-19 12:19:18 +02:00
commit b7b47941c0
9 changed files with 276 additions and 5 deletions

View file

@ -51,8 +51,9 @@ NmcOutputField nmc_fields_setting_connection[] = {
SETTING_FIELD (NM_SETTING_CONNECTION_ZONE, 10), /* 10 */
SETTING_FIELD (NM_SETTING_CONNECTION_MASTER, 20), /* 11 */
SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE, 20), /* 12 */
SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 13 */
SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 14 */
SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, 13), /* 13 */
SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 14 */
SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 15 */
{NULL, NULL, 0, NULL, FALSE, FALSE, 0}
};
#define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\
@ -68,6 +69,7 @@ NmcOutputField nmc_fields_setting_connection[] = {
NM_SETTING_CONNECTION_ZONE","\
NM_SETTING_CONNECTION_MASTER","\
NM_SETTING_CONNECTION_SLAVE_TYPE","\
NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES","\
NM_SETTING_CONNECTION_SECONDARIES","\
NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT
#define NMC_FIELDS_SETTING_CONNECTION_COMMON NMC_FIELDS_SETTING_CONNECTION_ALL
@ -757,6 +759,20 @@ ip6_privacy_to_string (NMSettingIP6ConfigPrivacy ip6_privacy)
}
}
static char *
autoconnect_slaves_to_string (NMSettingConnectionAutoconnectSlaves autoconnect_slaves)
{
switch (autoconnect_slaves) {
case NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO:
return g_strdup_printf (_("%d (no)"), autoconnect_slaves);
case NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES:
return g_strdup_printf (_("%d (yes)"), autoconnect_slaves);
case NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT:
default:
return g_strdup_printf (_("%d (default)"), autoconnect_slaves);
}
}
static char *
secret_flags_to_string (guint32 flags)
{
@ -1046,6 +1062,14 @@ nmc_property_connection_get_permissions (NMSetting *setting)
DEFINE_GETTER (nmc_property_connection_get_zone, NM_SETTING_CONNECTION_ZONE)
DEFINE_GETTER (nmc_property_connection_get_master, NM_SETTING_CONNECTION_MASTER)
DEFINE_GETTER (nmc_property_connection_get_slave_type, NM_SETTING_CONNECTION_SLAVE_TYPE)
static char *
nmc_property_connection_get_autoconnect_slaves (NMSetting *setting)
{
NMSettingConnection *s_con = NM_SETTING_CONNECTION (setting);
return autoconnect_slaves_to_string (nm_setting_connection_get_autoconnect_slaves (s_con));
}
DEFINE_GETTER (nmc_property_connection_get_secondaries, NM_SETTING_CONNECTION_SECONDARIES)
DEFINE_GETTER (nmc_property_connection_get_gateway_ping_timeout, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT)
@ -2239,6 +2263,22 @@ nmc_property_set_bool (NMSetting *setting, const char *prop, const char *val, GE
return TRUE;
}
static gboolean
nmc_property_set_trilean (NMSetting *setting, const char *prop, const char *val, GError **error)
{
long int val_int;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (!nmc_string_to_int (val, TRUE, -1, 1, &val_int)) {
g_set_error (error, 1, 0, _("'%s' is not a valid value; use -1, 0 or 1"), val);
return FALSE;
}
g_object_set (setting, prop, val_int, NULL);
return TRUE;
}
static gboolean
nmc_property_set_ssid (NMSetting *setting, const char *prop, const char *val, GError **error)
{
@ -5233,6 +5273,13 @@ nmc_properties_init (void)
NULL,
nmc_property_con_allowed_slave_type,
NULL);
nmc_add_prop_funcs (GLUE (CONNECTION, AUTOCONNECT_SLAVES),
nmc_property_connection_get_autoconnect_slaves,
nmc_property_set_trilean,
NULL,
NULL,
NULL,
NULL);
nmc_add_prop_funcs (GLUE (CONNECTION, SECONDARIES),
nmc_property_connection_get_secondaries,
nmc_property_connection_set_secondaries,
@ -6611,8 +6658,9 @@ setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_pro
set_val_str (arr, 10, nmc_property_connection_get_zone (setting));
set_val_str (arr, 11, nmc_property_connection_get_master (setting));
set_val_str (arr, 12, nmc_property_connection_get_slave_type (setting));
set_val_str (arr, 13, nmc_property_connection_get_secondaries (setting));
set_val_str (arr, 14, nmc_property_connection_get_gateway_ping_timeout (setting));
set_val_str (arr, 13, nmc_property_connection_get_autoconnect_slaves (setting));
set_val_str (arr, 14, nmc_property_connection_get_secondaries (setting));
set_val_str (arr, 15, nmc_property_connection_get_gateway_ping_timeout (setting));
g_ptr_array_add (nmc->output_data, arr);
print_data (nmc); /* Print all data */

View file

@ -27,6 +27,7 @@
#include "nm-utils.h"
#include "nm-utils-private.h"
#include "nm-core-enum-types.h"
#include "nm-setting-connection.h"
#include "nm-connection-private.h"
#include "nm-setting-bond.h"
@ -66,6 +67,7 @@ typedef struct {
char *type;
char *master;
char *slave_type;
NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
GSList *permissions; /* list of Permission structs */
gboolean autoconnect;
gint autoconnect_priority;
@ -90,6 +92,7 @@ enum {
PROP_ZONE,
PROP_MASTER,
PROP_SLAVE_TYPE,
PROP_AUTOCONNECT_SLAVES,
PROP_SECONDARIES,
PROP_GATEWAY_PING_TIMEOUT,
@ -602,6 +605,23 @@ nm_setting_connection_is_slave_type (NMSettingConnection *setting,
return !g_strcmp0 (NM_SETTING_CONNECTION_GET_PRIVATE (setting)->slave_type, type);
}
/**
* nm_setting_connection_get_autoconnect_slaves:
* @setting: the #NMSettingConnection
*
* Returns the #NMSettingConnection:autoconnect-slaves property of the connection.
*
* Returns: whether slaves of the connection should be activated together
* with the connection.
**/
NMSettingConnectionAutoconnectSlaves
nm_setting_connection_get_autoconnect_slaves (NMSettingConnection *setting)
{
g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT);
return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->autoconnect_slaves;
}
/**
* nm_setting_connection_get_num_secondaries:
* @setting: the #NMSettingConnection
@ -1122,6 +1142,9 @@ set_property (GObject *object, guint prop_id,
g_free (priv->slave_type);
priv->slave_type = g_value_dup_string (value);
break;
case PROP_AUTOCONNECT_SLAVES:
priv->autoconnect_slaves = g_value_get_enum (value);
break;
case PROP_SECONDARIES:
g_slist_free_full (priv->secondaries, g_free);
priv->secondaries = _nm_utils_strv_to_slist (g_value_get_boxed (value));
@ -1193,6 +1216,9 @@ get_property (GObject *object, guint prop_id,
case PROP_SLAVE_TYPE:
g_value_set_string (value, nm_setting_connection_get_slave_type (setting));
break;
case PROP_AUTOCONNECT_SLAVES:
g_value_set_enum (value, nm_setting_connection_get_autoconnect_slaves (setting));
break;
case PROP_SECONDARIES:
g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->secondaries));
break;
@ -1523,6 +1549,35 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS));
/**
* NMSettingConnection:autoconnect-slaves:
*
* Whether or not slaves of this connection should be automatically brought up
* when NetworkManager activates this connection. This only has a real effect
* for master connections.
* The permitted values are: 0: leave slave connections untouched,
* 1: activate all the slave connections with this connection, -1: default.
* If -1 (default) is set, global connection.autoconnect-slaves is read to
* determine the real value. If it is default as well, this fallbacks to 0.
**/
/* ---ifcfg-rh---
* property: autoconnect-slaves
* variable: AUTOCONNECT-SLAVES(+)
* default: missing variable means global default
* description: Whether slaves of this connection should be auto-connected
* when this connection is activated.
* ---end---
*/
g_object_class_install_property
(object_class, PROP_AUTOCONNECT_SLAVES,
g_param_spec_enum (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, "", "",
NM_TYPE_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
NM_SETTING_PARAM_FUZZY_IGNORE |
G_PARAM_STATIC_STRINGS));
/**
* NMSettingConnection:secondaries:
*

View file

@ -56,9 +56,29 @@ G_BEGIN_DECLS
#define NM_SETTING_CONNECTION_ZONE "zone"
#define NM_SETTING_CONNECTION_MASTER "master"
#define NM_SETTING_CONNECTION_SLAVE_TYPE "slave-type"
#define NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES "autoconnect-slaves"
#define NM_SETTING_CONNECTION_SECONDARIES "secondaries"
#define NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT "gateway-ping-timeout"
/* Types for property values */
/**
* NMSettingConnectionAutoconnectSlaves:
* @NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT: default value
* @NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO: slaves are not brought up when
* master is activated
* @NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES: slaves are brought up when
* master is activated
*
* #NMSettingConnectionAutoconnectSlaves values indicate whether slave connections
* should be activated when master is activated.
*/
typedef enum {
NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT = -1,
NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO = 0,
NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES = 1,
} NMSettingConnectionAutoconnectSlaves;
/**
* NMSettingConnection:
*
@ -111,6 +131,7 @@ const char *nm_setting_connection_get_master (NMSettingConnection *set
gboolean nm_setting_connection_is_slave_type (NMSettingConnection *setting,
const char *type);
const char *nm_setting_connection_get_slave_type (NMSettingConnection *setting);
NMSettingConnectionAutoconnectSlaves nm_setting_connection_get_autoconnect_slaves (NMSettingConnection *setting);
guint32 nm_setting_connection_get_num_secondaries (NMSettingConnection *setting);
const char *nm_setting_connection_get_secondary (NMSettingConnection *setting, guint32 idx);

View file

@ -1963,6 +1963,7 @@ test_connection_diff_a_only (void)
{ NM_SETTING_CONNECTION_ZONE, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_CONNECTION_MASTER, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_CONNECTION_SECONDARIES, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A },
{ NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }

View file

@ -466,8 +466,10 @@ global:
nm_setting_compare_flags_get_type;
nm_setting_connection_add_permission;
nm_setting_connection_add_secondary;
nm_setting_connection_autoconnect_slaves_get_type;
nm_setting_connection_get_autoconnect;
nm_setting_connection_get_autoconnect_priority;
nm_setting_connection_get_autoconnect_slaves;
nm_setting_connection_get_connection_type;
nm_setting_connection_get_gateway_ping_timeout;
nm_setting_connection_get_id;

View file

@ -451,6 +451,7 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth
<programlisting>
[connection]
ipv6.ip6-privacy=0
connection.autoconnect-slaves=1
[connection-wifi-wlan0]
match-device=interface-name:wlan0
@ -517,6 +518,9 @@ ipv6.ip6-privacy=1
<varlistentry>
<term><varname>ipv6.route-metric</varname></term>
</varlistentry>
<varlistentry>
<term><varname>connection.autoconnect-slaves</varname></term>
</varlistentry>
</variablelist>
</para>
</refsect1>

View file

@ -2392,6 +2392,127 @@ ensure_master_active_connection (NMManager *self,
return NULL;
}
/**
* find_slaves:
* @manager: #NMManager object
* @connection: the master #NMConnection to find slave connections for
* @device: the master #NMDevice for the @connection
*
* Given an #NMConnection, attempts to find its slaves. If @connection is not
* master, or has not any slaves, this will return %NULL.
*
* Returns: list of slave connections for given master @connection, or %NULL
**/
static GSList *
find_slaves (NMManager *manager,
NMConnection *connection,
NMDevice *device)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
GSList *all_connections, *iter;
GSList *slaves = NULL;
NMSettingConnection *s_con;
const char *master;
s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
master = nm_setting_connection_get_master (s_con);
if (master != NULL)
return NULL; /* connection is not master */
/* Search through all connections, not only inactive ones, because
* even if a slave was already active, it might be deactivated during
* master reactivation.
*/
all_connections = nm_settings_get_connections (priv->settings);
for (iter = all_connections; iter; iter = iter->next) {
NMConnection *master_connection = NULL;
NMDevice *master_device = NULL;
NMConnection *candidate = iter->data;
find_master (manager, candidate, NULL, &master_connection, &master_device, NULL, NULL);
if ( (master_connection && master_connection == connection)
|| (master_device && master_device == device)) {
slaves = g_slist_prepend (slaves, candidate);
}
}
g_slist_free (all_connections);
return g_slist_reverse (slaves);
}
static gboolean
should_connect_slaves (NMConnection *connection, NMDevice *device)
{
NMSettingConnection *s_con;
NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
gs_free char *value = NULL;
s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
/* Check autoconnect-slaves property */
autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con);
if (autoconnect_slaves != NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT)
goto out;
/* Check configuration default for autoconnect-slaves property */
value = nm_config_data_get_connection_default (nm_config_get_data (nm_config_get ()),
"connection.autoconnect-slaves", device);
if (value)
autoconnect_slaves = _nm_utils_ascii_str_to_int64 (value, 10, 0, 1, -1);
out:
if (autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO)
return FALSE;
if (autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES)
return TRUE;
return FALSE;
}
static gboolean
autoconnect_slaves (NMManager *manager,
NMConnection *master_connection,
NMDevice *master_device,
NMAuthSubject *subject)
{
GError *local_err = NULL;
gboolean ret = FALSE;
if (should_connect_slaves (master_connection, master_device)) {
GSList *slaves, *iter;
iter = slaves = find_slaves (manager, master_connection, master_device);
ret = slaves != NULL;
while (iter) {
NMConnection *slave_connection = iter->data;
iter = iter->next;
nm_log_dbg (LOGD_CORE, "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)",
nm_connection_get_id (slave_connection),
nm_connection_get_uuid (slave_connection),
nm_connection_get_id (master_connection),
nm_connection_get_uuid (master_connection));
/* Schedule slave activation */
nm_manager_activate_connection (manager,
slave_connection,
NULL,
nm_manager_get_best_device_for_connection (manager, slave_connection),
subject,
&local_err);
if (local_err) {
nm_log_warn (LOGD_CORE, "Slave connection activation failed: %s", local_err->message);
g_error_free (local_err);
}
}
g_slist_free (slaves);
}
return ret;
}
static gboolean
_internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **error)
{
@ -2565,6 +2686,9 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
nm_active_connection_get_path (master_ac));
}
/* Check slaves for master connection and possibly activate them */
autoconnect_slaves (self, connection, device, nm_active_connection_get_subject (active));
/* Disconnect the connection if connected or queued on another device */
existing = nm_manager_get_connection_device (self, connection);
if (existing)

View file

@ -178,6 +178,8 @@ make_connection_setting (const char *file,
NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN,
NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX,
NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT),
NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
svTrueValue (ifcfg, "AUTOCONNECT_SLAVES", NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT),
NULL);
value = svGetValue (ifcfg, "USERS", FALSE);

View file

@ -1665,7 +1665,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
{
guint32 n, i;
GString *str;
const char *master;
const char *master, *type;
char *tmp;
gint i_int;
@ -1682,6 +1682,20 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
svSetValue (ifcfg, "AUTOCONNECT_PRIORITY", tmp, FALSE);
g_free (tmp);
/* Only save the value for master connections */
svSetValue (ifcfg, "AUTOCONNECT_SLAVES", NULL, FALSE);
type = nm_setting_connection_get_connection_type (s_con);
if ( !g_strcmp0 (type, NM_SETTING_BOND_SETTING_NAME)
|| !g_strcmp0 (type, NM_SETTING_TEAM_SETTING_NAME)
|| !g_strcmp0 (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con);
svSetValue (ifcfg, "AUTOCONNECT_SLAVES",
autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" :
autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" : NULL,
FALSE);
}
/* Permissions */
svSetValue (ifcfg, "USERS", NULL, FALSE);
n = nm_setting_connection_get_num_permissions (s_con);