diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 796a8947ca..cfb6d81bc4 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -47,9 +47,7 @@ /* define some other prompts */ #define PROMPT_CON_TYPE _("Connection type: ") #define PROMPT_VPN_TYPE _("VPN type: ") -#define PROMPT_BOND_MASTER _("Bond master: ") -#define PROMPT_TEAM_MASTER _("Team master: ") -#define PROMPT_BRIDGE_MASTER _("Bridge master: ") +#define PROMPT_MASTER _("Master: ") #define PROMPT_CONNECTION _("Connection (name, UUID, or path): ") #define PROMPT_CONNECTIONS _("Connection(s) (name, UUID, or path): ") #define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath): ") @@ -253,7 +251,7 @@ usage (void) " show [--active] [--show-secrets] [id | uuid | path | apath] ...\n\n" " up [[id | uuid | path] ] [ifname ] [ap ] [passwd-file ]\n\n" " down [id | uuid | path | apath] ...\n\n" - " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS [-- ([+|-]. )+]\n\n" + " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]. )+]\n\n" " modify [--temporary] [id | uuid | path] ([+|-]. )+\n\n" " edit [id | uuid | path] \n" " edit [type ] [con-name ]\n\n" @@ -321,13 +319,16 @@ usage_connection_add (void) { g_printerr (_("Usage: nmcli connection add { ARGUMENTS | help }\n" "\n" - "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS [-- ([+|-]. )+]\n\n" + "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]. )+]\n\n" " COMMON_OPTIONS:\n" " type \n" " ifname | \"*\"\n" " [con-name ]\n" " [autoconnect yes|no]\n\n" " [save yes|no]\n\n" + " [master ]\n" + " [slave-type ]\n\n" + " [save yes|no]\n\n" " TYPE_SPECIFIC_OPTIONS:\n" " ethernet: [mac ]\n" " [cloned-mac ]\n" @@ -392,6 +393,11 @@ usage_connection_add (void) " olpc-mesh: ssid \n" " [channel <1-13>]\n" " [dhcp-anycast ]\n\n" + " SLAVE_OPTIONS:\n" + " bridge: [priority <0-63>]\n" + " [path-cost <1-65535>]\n" + " [hairpin yes|no]\n\n" + " team: [config |]\n\n" " IP_OPTIONS:\n" " [ip4 ] [gw4 ]\n" " [ip6 ] [gw6 ]\n\n")); @@ -3154,27 +3160,31 @@ _strip_master_prefix (const char *master, const char *(**func)(NMConnection *)) return master; } -/* verify_master_for_slave: +/* normalized_master_for_slave: * @connections: list af all connections * @master: UUID, ifname or ID of the master connection - * @type: virtual connection type (bond, team, bridge, ...) + * @type: virtual connection type (bond, team, bridge, ...) or %NULL + * @out_type: type of the connection that matched * - * Check whether master is a valid interface name, UUID or ID of some @type connection. + * Check whether master is a valid interface name, UUID or ID of some connection, + * possibly of a specified @type. * 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 (const GPtrArray *connections, - const char *master, - const char *type) +normalized_master_for_slave (const GPtrArray *connections, + const char *master, + const char *type, + const char **out_type) { NMConnection *connection; NMSettingConnection *s_con; - const char *con_type, *id, *uuid, *ifname; + const char *con_type = NULL, *id, *uuid, *ifname; int i; const char *found_by_id = NULL; + const char *out_type_by_id = NULL; const char *out_master = NULL; const char *(*func) (NMConnection *) = NULL; @@ -3187,11 +3197,12 @@ verify_master_for_slave (const GPtrArray *connections, 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) + if (type && g_strcmp0 (con_type, type) != 0) continue; if (func) { /* There was a prefix; only compare to that type. */ if (g_strcmp0 (master, func (connection)) == 0) { + *out_type = con_type; if (func == nm_connection_get_id) out_master = nm_connection_get_uuid (connection); else @@ -3205,13 +3216,30 @@ verify_master_for_slave (const GPtrArray *connections, if ( g_strcmp0 (master, uuid) == 0 || g_strcmp0 (master, ifname) == 0) { out_master = master; + *out_type = con_type; break; } - if (!found_by_id && g_strcmp0 (master, id) == 0) + if (!found_by_id && g_strcmp0 (master, id) == 0) { + out_type_by_id = con_type; found_by_id = uuid; + } } } - return out_master ? out_master : found_by_id; + + if (!out_master) { + out_master = found_by_id; + if (out_type) + *out_type = out_type_by_id; + } + + if (!out_master) { + g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); + out_master = master; + if (out_type) + *out_type = type; + } + + return out_master; } static gboolean @@ -4284,6 +4312,53 @@ finish: return success; } +static gboolean +complete_slave (NMSettingConnection *s_con, + const GPtrArray *all_connections, + const char *slave_type, + const char *master, + const char *type, + gboolean ask, + GError **error) +{ + char *master_ask = NULL; + const char *checked_master = NULL; + + if (type) + g_print (_("Warning: 'type' is ignored. " + "Use 'nmcli connection add \"%s\" ...' instead."), + type); + + if (nm_setting_connection_get_master (s_con)) { + /* Master already set. */ + if (master) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: redundant 'master' option.")); + return FALSE; + } + return TRUE; + } + + if (!master && ask) + master = master_ask = nmc_readline (PROMPT_MASTER); + if (!master) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'master' is required.")); + return FALSE; + } + /* Verify master argument */ + checked_master = normalized_master_for_slave (all_connections, master, slave_type, NULL); + + /* Change properties in 'connection' setting */ + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, checked_master, + NULL); + + g_free (master_ask); + + return TRUE; +} + static gboolean complete_connection_by_type (NMConnection *connection, const char *con_type, @@ -4311,6 +4386,7 @@ complete_connection_by_type (NMConnection *connection, NMSettingBridgePort *s_bridge_port; NMSettingVpn *s_vpn; NMSettingOlpcMesh *s_olpc_mesh; + const char *slave_type; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); @@ -5015,41 +5091,10 @@ cleanup_bond: return FALSE; } else if (!strcmp (con_type, "bond-slave")) { - /* 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}, - {NULL} }; - - /* Set global variables for use in TAB completion */ - nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME; - - if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) - return FALSE; - - if (!master && ask) - master = master_ask = nmc_readline (PROMPT_BOND_MASTER); - if (!master) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("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) - g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); - - if (type) - g_print (_("Warning: 'type' is currently ignored. " - "We only support ethernet slaves for now.\n")); /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, NULL); @@ -5057,8 +5102,6 @@ cleanup_bond: s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); - g_free (master_ask); - } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) { /* Build up the settings required for 'team' */ gboolean success = FALSE; @@ -5108,63 +5151,10 @@ cleanup_team: return FALSE; } else if (!strcmp (con_type, "team-slave")) { - /* Build up the settings required for 'team-slave' */ - 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; - char *json = NULL; - nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, - {"type", TRUE, &type, FALSE}, - {"config", TRUE, &config_c, FALSE}, - {NULL} }; - - /* Set global variables for use in TAB completion */ - nmc_tab_completion.con_type = NM_SETTING_TEAM_SETTING_NAME; - - if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) - return FALSE; - - if (!master && ask) - master = master_ask = nmc_readline (PROMPT_TEAM_MASTER); - if (!master) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("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) - g_print (_("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); - if (ask) - do_questionnaire_team_slave (&config); - - if (type) - g_print (_("Warning: 'type' is currently ignored. " - "We only support ethernet slaves for now.\n")); - - /* Add 'team-port' setting */ - s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); - nm_connection_add_setting (connection, NM_SETTING (s_team_port)); - - if (!nmc_team_check_config (config, &json, error)) { - g_prefix_error (error, _("Error: ")); - goto cleanup_team_slave; - } - - /* Set team-port options */ - g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL); /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, NULL); @@ -5172,14 +5162,6 @@ cleanup_team: s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); - success = TRUE; -cleanup_team_slave: - g_free (master_ask); - g_free (config); - g_free (json); - if (!success) - return FALSE; - } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) { /* Build up the settings required for 'bridge' */ gboolean success = FALSE; @@ -5319,83 +5301,10 @@ cleanup_bridge: return FALSE; } else if (!strcmp (con_type, "bridge-slave")) { - /* Build up the settings required for 'bridge-slave' */ - 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; - const char *path_cost_c = NULL; - char *path_cost = NULL; - const char *hairpin_c = NULL; - char *hairpin = NULL; - unsigned long prio_int, path_cost_int; - gboolean hairpin_bool; - nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, - {"type", TRUE, &type, FALSE}, - {"priority", TRUE, &priority_c, FALSE}, - {"path-cost", TRUE, &path_cost_c, FALSE}, - {"hairpin", TRUE, &hairpin_c, FALSE}, - {NULL} }; - - /* Set global variables for use in TAB completion */ - nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME; - - if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) - return FALSE; - - if (!master && ask) - master = master_ask = nmc_readline (PROMPT_BRIDGE_MASTER); - if (!master) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'master' is required.")); - return FALSE; - } - /* Verify master argument */ - checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_BRIDGE_SETTING_NAME); - if (!checked_master) - g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); - - if (type) - g_print (_("Warning: 'type' is currently ignored. " - "We only support ethernet slaves for now.\n")); - - /* Add 'bridge-port' setting */ - /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ - s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); - nm_connection_add_setting (connection, NM_SETTING (s_bridge_port)); - - /* Also ask for all optional arguments if '--ask' is specified. */ - priority = g_strdup (priority_c); - path_cost = g_strdup (path_cost_c); - hairpin = g_strdup (hairpin_c); - if (ask) - do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin); - - if (priority) - if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, - NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error)) - goto cleanup_bridge_slave; - if (path_cost) - if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, - NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error)) - goto cleanup_bridge_slave; - if (hairpin) { - GError *tmp_err = NULL; - if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'hairpin': %s."), tmp_err->message); - g_clear_error (&tmp_err); - goto cleanup_bridge_slave; - } - } /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, NULL); @@ -5403,22 +5312,6 @@ cleanup_bridge: s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); - if (priority) - g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL); - if (path_cost) - g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL); - if (hairpin) - g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL); - - success = TRUE; -cleanup_bridge_slave: - g_free (master_ask); - g_free (priority); - g_free (path_cost); - g_free (hairpin); - if (!success) - return FALSE; - } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) { /* Build up the settings required for 'vpn' */ gboolean success = FALSE; @@ -5548,11 +5441,146 @@ cleanup_olpc: return FALSE; } - /* Read and add IP configuration */ - if ( strcmp (con_type, "bond-slave") != 0 - && strcmp (con_type, "team-slave") != 0 - && strcmp (con_type, "bridge-slave") != 0) { + slave_type = nm_setting_connection_get_slave_type (s_con); + if (slave_type) { + /* Set global variables for use in TAB completion */ + nmc_tab_completion.con_type = (char *)slave_type; + + if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME)) { + /* Build up the settings required for 'team-slave' */ + gboolean success = FALSE; + const char *master = NULL; + char *master_ask = NULL; + const char *type = NULL; + const char *config_c = NULL; + char *config = NULL; + char *json = NULL; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE}, + {"type", TRUE, &type, FALSE}, + {"config", TRUE, &config_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + config = g_strdup (config_c); + if (ask) + do_questionnaire_team_slave (&config); + + /* Add 'team-port' setting */ + s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); + nm_connection_add_setting (connection, NM_SETTING (s_team_port)); + + if (!nmc_team_check_config (config, &json, error)) { + g_prefix_error (error, _("Error: ")); + goto cleanup_team_slave; + } + + /* Set team-port options */ + g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL); + + success = TRUE; +cleanup_team_slave: + g_free (master_ask); + g_free (config); + g_free (json); + if (!success) + return FALSE; + + } else if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) { + /* Build up the settings required for 'bridge-slave' */ + gboolean success = FALSE; + const char *master = NULL; + char *master_ask = NULL; + const char *type = NULL; + const char *priority_c = NULL; + char *priority = NULL; + const char *path_cost_c = NULL; + char *path_cost = NULL; + const char *hairpin_c = NULL; + char *hairpin = NULL; + unsigned long prio_int, path_cost_int; + gboolean hairpin_bool; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE}, + {"type", TRUE, &type, FALSE}, + {"priority", TRUE, &priority_c, FALSE}, + {"path-cost", TRUE, &path_cost_c, FALSE}, + {"hairpin", TRUE, &hairpin_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error)) + return FALSE; + + /* Add 'bridge-port' setting */ + /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ + s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bridge_port)); + + /* Also ask for all optional arguments if '--ask' is specified. */ + priority = g_strdup (priority_c); + path_cost = g_strdup (path_cost_c); + hairpin = g_strdup (hairpin_c); + if (ask) + do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin); + + if (priority) + if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error)) + goto cleanup_bridge_slave; + if (path_cost) + if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error)) + goto cleanup_bridge_slave; + if (hairpin) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'hairpin': %s."), tmp_err->message); + g_clear_error (&tmp_err); + goto cleanup_bridge_slave; + } + } + + if (priority) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL); + if (path_cost) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL); + if (hairpin) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL); + + success = TRUE; +cleanup_bridge_slave: + g_free (master_ask); + g_free (priority); + g_free (path_cost); + g_free (hairpin); + if (!success) + return FALSE; + } else { + /* Slave types without any specific settings ('bond-slave') */ + const char *master = NULL; + const char *type = NULL; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE}, + {"type", TRUE, &type, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error)) + return FALSE; + } + + } else { + /* Read and add IP configuration */ NMIPAddress *ip4addr = NULL, *ip6addr = NULL; const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL; nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE}, @@ -5854,9 +5882,7 @@ nmcli_con_add_tab_completion (const char *text, int start, int end) generator_func = gen_connection_types; else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0) generator_func = gen_func_vpn_types; - else if ( g_strcmp0 (rl_prompt, PROMPT_BOND_MASTER) == 0 - || g_strcmp0 (rl_prompt, PROMPT_TEAM_MASTER) == 0 - || g_strcmp0 (rl_prompt, PROMPT_BRIDGE_MASTER) == 0) + else if (g_strcmp0 (rl_prompt, PROMPT_MASTER) == 0) generator_func = gen_func_master_ifnames; else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL)) || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":")) @@ -5897,6 +5923,9 @@ do_connection_add (NmCli *nmc, int argc, char **argv) gboolean ifname_mandatory = TRUE; const char *save = NULL; gboolean save_bool = TRUE; + const char *master = NULL; + const char *checked_master = NULL; + const char *slave_type = NULL; AddConnectionInfo *info = NULL; const char *setting_name; GError *error = NULL; @@ -5905,6 +5934,8 @@ do_connection_add (NmCli *nmc, int argc, char **argv) {"autoconnect", TRUE, &autoconnect, FALSE}, {"ifname", TRUE, &ifname, FALSE}, {"save", TRUE, &save, FALSE}, + {"master", TRUE, &master, FALSE}, + {"slave-type", TRUE, &slave_type, FALSE}, {NULL} }; rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion; @@ -6005,12 +6036,19 @@ do_connection_add (NmCli *nmc, int argc, char **argv) default_name = unique_connection_name (nmc->connections, try_name); g_free (try_name); } + + if (master) + /* Verify master argument */ + checked_master = normalized_master_for_slave (nmc->connections, master, slave_type, &slave_type); + g_object_set (s_con, NM_SETTING_CONNECTION_ID, default_name, NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_TYPE, setting_name, NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool, NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, + NM_SETTING_CONNECTION_MASTER, checked_master, + NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, NULL); g_free (uuid); g_free (default_name); diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index 08c78ba062..825008cbb5 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -387,7 +387,7 @@ _nmcli_compl_ARGS() # user friendly. Only complete them, if the current word already starts with an "8". _nmcli_list "802-3-ethernet 802-11-wireless 802-11-olpc-mesh" else - _nmcli_list "ethernet wifi wimax gsm cdma infiniband bluetooth vpn olpc-mesh vlan bond bond-slave bridge bridge-slave team team-slave pppoe" + _nmcli_list "ethernet wifi wimax gsm cdma infiniband bluetooth vpn olpc-mesh vlan bond bridge team pppoe" fi return 0 fi @@ -934,7 +934,7 @@ _nmcli() ;; a|ad|add) if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect + _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect master elif [[ ${#words[@]} -gt 3 ]]; then _nmcli_array_delete_at words 0 1 @@ -947,14 +947,14 @@ _nmcli() ;; 1) if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect + _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect master fi return 0 ;; esac OPTIONS_TYPE= - OPTIONS=(type ifname con-name autoconnect save) + OPTIONS=(type ifname con-name autoconnect save master) OPTIONS_MANDATORY=(type ifname) COMMAND_ARGS_WAIT_OPTIONS=1 OPTIONS_MANDATORY_IFNAME=1 @@ -1019,32 +1019,14 @@ _nmcli() OPTIONS_TYPE=bond OPTIONS_TYPED=(mode miimon downdelay updelay arp-interval arp-ip-target primary lacp-rate) ;; - bond-|bond-s|bond-sl|bond-sla|bond-slav|bond-slave) - OPTIONS_TYPE=bond-slave - OPTIONS_TYPED=(master) - OPTIONS_MANDATORY=(master) - OPTIONS_IP=() - ;; team) OPTIONS_TYPE=team OPTIONS_TYPED=(config) ;; - team-|team-s|team-sl|team-sla|team-slav|team-slave) - OPTIONS_TYPE=team-slave - OPTIONS_TYPED=(master config) - OPTIONS_MANDATORY=(master) - OPTIONS_IP=() - ;; bridge) OPTIONS_TYPE=bridge OPTIONS_TYPED=(stp priority forward-delay hello-time max-age ageing-time mac) ;; - bridge-|bridge-s|bridge-sl|bridge-sla|bridge-slav|bridge-slave) - OPTIONS_TYPE=bridge-slave - OPTIONS_TYPED=(master priority path-cost hairpin) - OPTIONS_MANDATORY=(master) - OPTIONS_IP=() - ;; vp|vpn) OPTIONS_TYPE=vpn OPTIONS_TYPED=(vpn-type user) diff --git a/man/nmcli-examples.xml b/man/nmcli-examples.xml index 23ef231864..a0da09cc2d 100644 --- a/man/nmcli-examples.xml +++ b/man/nmcli-examples.xml @@ -178,8 +178,8 @@ $ nmcli g log level INFO domains DEFAULT Adding a bonding master and two slave connection profiles $ nmcli con add type bond ifname mybond0 mode active-backup -$ nmcli con add type bond-slave ifname eth1 master mybond0 -$ nmcli con add type bond-slave ifname eth2 master mybond0 +$ nmcli con add type ethernet ifname eth1 master mybond0 +$ nmcli con add type ethernet ifname eth2 master mybond0 @@ -194,8 +194,8 @@ $ nmcli con add type bond-slave ifname eth2 master mybond0 Adding a team master and two slave connection profiles $ nmcli con add type team con-name Team1 ifname Team1 config team1-master-json.conf -$ nmcli con add type team-slave con-name Team1-slave1 ifname em1 master Team1 -$ nmcli con add type team-slave con-name Team1-slave2 ifname em2 master Team1 +$ nmcli con add type ethernet con-name Team1-slave1 ifname em1 master Team1 +$ nmcli con add type ethernet con-name Team1-slave2 ifname em2 master Team1 @@ -222,8 +222,8 @@ $ nmcli con up Team1-slave2 Adding a bridge and two slave profiles $ nmcli con add type bridge con-name TowerBridge ifname TowerBridge -$ nmcli con add type bridge-slave con-name br-slave-1 ifname ens3 master TowerBridge -$ nmcli con add type bridge-slave con-name br-slave-2 ifname ens4 master TowerBridge +$ nmcli con add type ethernet con-name br-slave-1 ifname ens3 master TowerBridge +$ nmcli con add type ethernet con-name br-slave-2 ifname ens4 master TowerBridge $ nmcli con modify TowerBridge bridge.stp no diff --git a/man/nmcli.1.in b/man/nmcli.1.in index 0c9434c232..7ab3ef8cda 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -418,7 +418,7 @@ See \fBconnection show\fP above for the description of the -specifying keywo .br If '--wait' option is not specified, the default timeout will be 10 seconds. .TP -.B add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS [-- [+|-]. ...] +.B add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- [+|-]. ...] .br Add a connection for NetworkManager. Arguments differ according to connection types, see below. .RS @@ -426,6 +426,9 @@ Add a connection for NetworkManager. Arguments differ according to connection ty .B COMMON_OPTIONS: .IP "\fItype \fP" 42 \(en connection type; see below \fBTYPE_SPECIFIC_OPTIONS\fP for allowed values; (mandatory) +Note that types \fIbond-slave\fP, \fIteam-slave\fP and \fIbridge-slave\fP create \fIethernet\fP +connection profiles. Their use is discouraged in favor of using a specific type with \fImaster\fP +option. .IP "\fIifname | \(dq\&*\(dq\&\fP" 42 \(en interface to bind the connection to. The connection will only be applicable to this interface name. A special value of "\fB*\fP" can be used for interface-independent connections. @@ -437,6 +440,13 @@ Note: use quotes around \fB*\fP to suppress shell expansion. \(en whether the connection profile can be automatically activated (default: yes) .IP "\fI[save yes|no]\fP" 42 \(en whether the connection should be persistent, i.e. NetworkManager should store it on disk (default: yes) +.IP "\fI[master ]\fP" 42 +\(en master interface name, or connection UUID or ID of master connection profile. +The value can be prefixed with \fBifname/\fP, \fBuuid/\fP or \fBid/\fP to disambiguate it. +See below \fBSLAVE_OPTIONS\fP for additional options for slave connection to masters of various types. +.IP "\fI[slave-type ]\fP" 42 +\(en type of master connection. Only required when it can not be inferred (i.e. the master connection does +not exist yet). .RE .RS .TP @@ -580,8 +590,6 @@ The value can be prefixed with \fBifname/\fP, \fBuuid/\fP or \fBid/\fP to disamb .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 .RS .TP @@ -610,13 +618,6 @@ originally introduced in 3.15 upstream kernel) .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 -\(en STP port cost for destinations via this slave (default: 100) -.IP "\fI[hairpin yes|no]\fP" 42 -\(en 'hairpin mode' for the slave, which allows frames -to be sent back out through the slave the frame was received on (default: yes) .RE .RS .TP @@ -638,6 +639,27 @@ to be sent back out through the slave the frame was received on (default: yes) .RE .RS .TP +.B SLAVE_OPTIONS: +.RE +.RS +.TP +.B bridge: +.IP "\fI[priority <0-63>]\fP" 42 +\(en STP priority of this slave (default: 32) +.IP "\fI[path-cost <1-65535>]\fP" 42 +\(en STP port cost for destinations via this slave (default: 100) +.IP "\fI[hairpin yes|no]\fP" 42 +\(en 'hairpin mode' for the slave, which allows frames +to be sent back out through the slave the frame was received on (default: yes) +.RE +.RS +.TP +.B team: +.IP "\fI[config |]\fP" 42 +\(en JSON configuration for team +.RE +.RS +.TP .B IP_OPTIONS: .IP "\fI[ip4 ] [gw4 ]\fP" 42 \(en IPv4 addresses