diff --git a/cli/src/connections.c b/cli/src/connections.c index 011a4031b3..d31bff8f69 100644 --- a/cli/src/connections.c +++ b/cli/src/connections.c @@ -386,9 +386,9 @@ usage_connection_add (void) " [updelay ]\n" " [arp-interval ]\n" " [arp-ip-target ]\n\n" - " bond-slave: master \n\n" + " bond-slave: master \n\n" " team: [config |]\n\n" - " team-slave: master \n" + " team-slave: master \n" " [config |]\n\n" " bridge: [stp yes|no]\n" " [priority ]\n" @@ -396,7 +396,7 @@ usage_connection_add (void) " [hello-time <1-10>]\n" " [max-age <6-40>]\n" " [ageing-time <0-1000000>]\n\n" - " bridge-slave: master \n" + " bridge-slave: master \n" " [priority <0-63>]\n" " [path-cost <1-65535>]\n" " [hairpin yes|no]\n\n" @@ -2743,6 +2743,92 @@ unique_master_iface_ifname (GSList *list, return new_name; } +static const char * +_strip_master_prefix (const char *master, const char *(**func)(NMConnection *)) +{ + if (!master) + return NULL; + + if (g_str_has_prefix (master, "ifname/")) { + master = master + strlen ("ifname/"); + if (func) + *func = nm_connection_get_virtual_iface_name; + } else if (g_str_has_prefix (master, "uuid/")) { + master = master + strlen ("uuid/"); + if (func) + *func = nm_connection_get_uuid; + } else if (g_str_has_prefix (master, "id/")) { + master = master + strlen ("id/"); + if (func) + *func = nm_connection_get_id; + } + return master; +} + +/* verify_master_for_slave: + * @connections: list af all connections + * @master: UUID, ifname or ID of the master connection + * @type: virtual connection type (bond, team, bridge, ...) + * + * Check whether master is a valid interface name, UUID or ID of some @type connection. + * First UUID and ifname are checked. If they don't match, ID is checked + * and replaced by UUID on a match. + * + * Returns: identifier of master connection if found, %NULL otherwise + */ +static const char * +verify_master_for_slave (GSList *connections, + const char *master, + const char *type) +{ + NMConnection *connection; + NMSettingConnection *s_con; + const char *con_type, *id, *uuid, *ifname; + GSList *iterator = connections; + const char *found_by_id = NULL; + const char *out_master = NULL; + const char *(*func) (NMConnection *) = NULL; + + if (!master) + return NULL; + + master = _strip_master_prefix (master, &func); + while (iterator) { + connection = NM_CONNECTION (iterator->data); + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + if (g_strcmp0 (con_type, type) != 0) { + iterator = g_slist_next (iterator); + continue; + } + if (func) { + /* There was a prefix; only compare to that type. */ + if (g_strcmp0 (master, func (connection)) == 0) { + if (func == nm_connection_get_id) + out_master = nm_connection_get_uuid (connection); + else + out_master = master; + break; + } + } else { + id = nm_connection_get_id (connection); + uuid = nm_connection_get_uuid (connection); + ifname = nm_connection_get_virtual_iface_name (connection); + if ( g_strcmp0 (master, uuid) == 0 + || g_strcmp0 (master, ifname) == 0) { + out_master = master; + break; + } + if (!found_by_id && g_strcmp0 (master, id) == 0) + found_by_id = uuid; + } + + iterator = g_slist_next (iterator); + } + return out_master ? out_master : found_by_id; +} + static gboolean bridge_prop_string_to_uint (const char *str, const char *nmc_arg, @@ -4343,6 +4429,7 @@ cleanup_bond: /* Build up the settings required for 'bond-slave' */ const char *master = NULL; char *master_ask = NULL; + const char *checked_master = NULL; const char *type = NULL; nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, {"type", TRUE, &type, FALSE}, @@ -4358,6 +4445,10 @@ cleanup_bond: _("Error: 'master' is required.")); return FALSE; } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_BOND_SETTING_NAME); + if (!checked_master) + printf (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); if (type) printf (_("Warning: 'type' is currently ignored. " @@ -4366,7 +4457,7 @@ cleanup_bond: /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_MASTER, master, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, NULL); @@ -4431,6 +4522,7 @@ cleanup_team: gboolean success = FALSE; const char *master = NULL; char *master_ask = NULL; + const char *checked_master = NULL; const char *type = NULL; const char *config_c = NULL; char *config = NULL; @@ -4450,6 +4542,10 @@ cleanup_team: _("Error: 'master' is required.")); return FALSE; } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_TEAM_SETTING_NAME); + if (!checked_master) + printf (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); /* Also ask for all optional arguments if '--ask' is specified. */ config = g_strdup (config_c); @@ -4475,7 +4571,7 @@ cleanup_team: /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_MASTER, master, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, NULL); @@ -4611,6 +4707,7 @@ cleanup_bridge: gboolean success = FALSE; const char *master = NULL; char *master_ask = NULL; + const char *checked_master = NULL; const char *type = NULL; const char *priority_c = NULL; char *priority = NULL; @@ -4637,12 +4734,10 @@ cleanup_bridge: _("Error: 'master' is required.")); return FALSE; } - if (!nm_utils_is_uuid (master) && !nm_utils_iface_valid_name (master)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'master': '%s' is not valid UUID nor interface."), - master); - goto cleanup_bridge_slave; - } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_BRIDGE_SETTING_NAME); + if (!checked_master) + printf (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); if (type) printf (_("Warning: 'type' is currently ignored. " @@ -4681,7 +4776,7 @@ cleanup_bridge: /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_MASTER, master, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, NULL); diff --git a/man/nmcli.1.in b/man/nmcli.1.in index 0292fa5257..6eb6f1fac0 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -521,8 +521,9 @@ Note: use quotes around \fB*\fP to suppress shell expansion. .RS .TP .B bond-slave: -.IP "\fImaster \fP" 42 -\(en name of bond master interface +.IP "\fImaster \fP" 42 +\(en master bond interface name, or connection UUID or ID of bond master connection profile. +The value can be prefixed with \fBifname/\fP, \fBuuid/\fP or \fBid/\fP to disambiguate it. .RE .RS .TP @@ -533,8 +534,9 @@ Note: use quotes around \fB*\fP to suppress shell expansion. .RS .TP .B team-slave: -.IP "\fImaster \fP" 42 -\(en name of team master interface +.IP "\fImaster \fP" 42 +\(en master team interface name, or connection UUID or ID of team master connection profile. +The value can be prefixed with \fBifname/\fP, \fBuuid/\fP or \fBid/\fP to disambiguate it. .IP "\fI[config |]\fP" 42 \(en JSON configuration for team .RE @@ -557,8 +559,9 @@ Note: use quotes around \fB*\fP to suppress shell expansion. .RS .TP .B bridge-slave: -.IP "\fImaster \fP" 42 -\(en name of bridge master interface +.IP "\fImaster \fP" 42 +\(en master bridge interface name, or connection UUID or ID of bridge master connection profile. +The value can be prefixed with \fBifname/\fP, \fBuuid/\fP or \fBid/\fP to disambiguate it. .IP "\fI[priority <0-63>]\fP" 42 \(en STP priority of this slave (default: 32) .IP "\fI[path-cost <1-65535>]\fP" 42