diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2015-07-12 15:53:52 +0200 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2015-07-12 15:53:52 +0200 |
commit | 9d5f114bfd1b4e2ba82e708e78d204435f74b362 (patch) | |
tree | 9cbe3403eb228a26d038525ea87d9dd1b1ea1bd0 | |
parent | 63413e1cc6b13095b8f799579b229cac9c91c8c1 (diff) | |
parent | 1ff98fca40168d01fcd9da33058ff6e1d012b426 (diff) | |
download | NetworkManager-9d5f114bfd1b4e2ba82e708e78d204435f74b362.tar.gz |
merge: branch 'lr/cli-add-master'
Make it possible to specify master connection/device for any connection profile
https://bugzilla.gnome.org/show_bug.cgi?id=748302
-rw-r--r-- | clients/cli/connections.c | 448 | ||||
-rw-r--r-- | clients/cli/nmcli-completion | 26 | ||||
-rw-r--r-- | man/nmcli-examples.xml | 12 | ||||
-rw-r--r-- | man/nmcli.1.in | 42 |
4 files changed, 285 insertions, 243 deletions
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] <ID> ...\n\n" " up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [passwd-file <file with passwords>]\n\n" " down [id | uuid | path | apath] <ID> ...\n\n" - " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n" + " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n" " modify [--temporary] [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n\n" " edit [id | uuid | path] <ID>\n" " edit [type <new_con_type>] [con-name <new_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 [-- ([+|-]<setting>.<property> <value>)+]\n\n" + "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n" " COMMON_OPTIONS:\n" " type <type>\n" " ifname <interface name> | \"*\"\n" " [con-name <connection name>]\n" " [autoconnect yes|no]\n\n" " [save yes|no]\n\n" + " [master <master (ifname, or connection UUID or name)>]\n" + " [slave-type <master connection type>]\n\n" + " [save yes|no]\n\n" " TYPE_SPECIFIC_OPTIONS:\n" " ethernet: [mac <MAC address>]\n" " [cloned-mac <cloned MAC address>]\n" @@ -392,6 +393,11 @@ usage_connection_add (void) " olpc-mesh: ssid <SSID>\n" " [channel <1-13>]\n" " [dhcp-anycast <MAC address>]\n\n" + " SLAVE_OPTIONS:\n" + " bridge: [priority <0-63>]\n" + " [path-cost <1-65535>]\n" + " [hairpin yes|no]\n\n" + " team: [config <file>|<raw JSON data>]\n\n" " IP_OPTIONS:\n" " [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n" " [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\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 @@ -4285,6 +4313,53 @@ finish: } 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, const GPtrArray *all_connections, @@ -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 <example><title>Adding a bonding master and two slave connection profiles</title> <programlisting><emphasis role="bold"> $ 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 </emphasis></programlisting> </example> <para> @@ -194,8 +194,8 @@ $ nmcli con add type bond-slave ifname eth2 master mybond0 <example><title>Adding a team master and two slave connection profiles</title> <programlisting><emphasis role="bold"> $ 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 </emphasis></programlisting> </example> <para> @@ -222,8 +222,8 @@ $ nmcli con up Team1-slave2 <example><title>Adding a bridge and two slave profiles</title> <programlisting><emphasis role="bold"> $ 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 </emphasis></programlisting> </example> 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 <ID>-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 [-- [+|-]<setting>.<property> <value> ...] +.B add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- [+|-]<setting>.<property> <value> ...] .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 <type>\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 <ifname> | \(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 <master (ifname, or connection UUID or name)>]\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 <master connection 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 <master (ifname, or connection UUID or name)>\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 <file>|<raw JSON data>]\fP" 42 -\(en JSON configuration for team .RE .RS .TP @@ -610,13 +618,6 @@ originally introduced in 3.15 upstream kernel) .IP "\fImaster <master (ifname, or connection UUID or name)>\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 <file>|<raw JSON data>]\fP" 42 +\(en JSON configuration for team +.RE +.RS +.TP .B IP_OPTIONS: .IP "\fI[ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\fP" 42 \(en IPv4 addresses |