diff options
author | Jiří Klimeš <jklimes@redhat.com> | 2013-11-11 17:48:25 +0100 |
---|---|---|
committer | Jiří Klimeš <jklimes@redhat.com> | 2013-11-22 14:09:10 +0100 |
commit | a3c06afc12e0cab6e09ec32139fa5cb0d4d98e2d (patch) | |
tree | 4adc79fbb0bb0531e4eeca03c7cb96e7211dcc2c | |
parent | 799477872394a5e473cb868e8c3ae2ba5fff7f50 (diff) | |
download | NetworkManager-a3c06afc12e0cab6e09ec32139fa5cb0d4d98e2d.tar.gz |
cli: support file names for 'config' argument when creating team connections
nmcli con add type team config /home/cimrman/team-config.json
libteam (and in turn NetworkManager) configures team devices via plain config
data in JSON format. However, it is useful and more user-friendly for nmcli to
accept also a file name that contains the config data, and read it. Thus the
user is not forced to type whole (possibly long) config on the command line.
-rw-r--r-- | cli/src/common.c | 33 | ||||
-rw-r--r-- | cli/src/common.h | 3 | ||||
-rw-r--r-- | cli/src/connections.c | 108 | ||||
-rw-r--r-- | cli/src/settings.c | 36 |
4 files changed, 163 insertions, 17 deletions
diff --git a/cli/src/common.c b/cli/src/common.c index a405118ed3..33c42b5f4a 100644 --- a/cli/src/common.c +++ b/cli/src/common.c @@ -891,3 +891,36 @@ nmc_bond_validate_mode (const char *mode, GError **error) return nmc_string_is_valid (mode, valid_modes, error); } +gboolean +nmc_team_check_config (const char *config, char **out_config, GError **error) +{ + char *contents = NULL; + size_t c_len = 0; + + *out_config = NULL; + + if (!config || strlen (config) == strspn (config, " \t")) + return TRUE; + + /* 'config' can be either a file name or raw JSON config data */ + if (g_file_test (config, G_FILE_TEST_EXISTS)) + g_file_get_contents (config, &contents, NULL, NULL); + else + contents = g_strdup (config); + + if (contents) { + g_strstrip (contents); + c_len = strlen (contents); + } + + /* Do a simple validity check */ + if (!contents || !contents[0] || c_len > 100000 || contents[0] != '{' || contents[c_len-1] != '}') { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("'%s' is not a valid team configuration or file name."), config); + g_free (contents); + return FALSE; + } + *out_config = contents; + return TRUE; +} + diff --git a/cli/src/common.h b/cli/src/common.h index 69a81d5878..6683502805 100644 --- a/cli/src/common.h +++ b/cli/src/common.h @@ -16,7 +16,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2012 Red Hat, Inc. + * (C) Copyright 2012 - 2013 Red Hat, Inc. */ #ifndef NMC_COMMON_H @@ -52,5 +52,6 @@ nmc_vlan_parse_priority_maps (const char *priority_map, GError **error); const char *nmc_bond_validate_mode (const char *mode, GError **error); +gboolean nmc_team_check_config (const char *config, char **out_config, GError **error); #endif /* NMC_COMMON_H */ diff --git a/cli/src/connections.c b/cli/src/connections.c index 49083528e4..05b3d3435b 100644 --- a/cli/src/connections.c +++ b/cli/src/connections.c @@ -280,9 +280,9 @@ usage_connection_add (void) " [arp-interval <num>]\n" " [arp-ip-target <num>]\n\n" " bond-slave: master <master (ifname or connection UUID)>\n\n" - " team: [config <json config>]\n\n" + " team: [config <file>|<raw JSON data>]\n\n" " team-slave: master <master (ifname or connection UUID)>\n" - " [config <json config>]\n\n" + " [config <file>|<raw JSON data>]\n\n" " bridge: [stp yes|no>]\n" " [priority <num>]\n" " [forward-delay <2-30>]\n" @@ -2767,6 +2767,53 @@ do_questionnaire_bond (char **mode, char **primary, char **miimon, } static void +do_questionnaire_team_common (const char *type_name, char **config) +{ + char *answer; + gboolean answer_bool; + gboolean once_more; + char *json = NULL; + GError *error = NULL; + + /* Ask for optional 'team' arguments. */ + printf (_("There is 1 optional argument for '%s' connection type.\n"), type_name); + answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] ")); + if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { + g_free (answer); + return; + } + + if (!*config) { + do { + *config = nmc_get_user_input (_("Team JSON configuration [none]: ")); + once_more = !nmc_team_check_config (*config, &json, &error); + if (once_more) { + printf ("Error: %s\n", error->message); + g_clear_error (&error); + g_free (*config); + } + } while (once_more); + } + + *config = json; + g_free (answer); + return; +} + +/* Both team and team-slave curently have just ithe same one optional argument */ +static void +do_questionnaire_team (char **config) +{ + do_questionnaire_team_common (_("team"), config); +} + +static void +do_questionnaire_team_slave (char **config) +{ + do_questionnaire_team_common (_("team-slave"), config); +} + +static void do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time, char **max_age, char **ageing_time) { @@ -3792,15 +3839,23 @@ cleanup_bond: } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) { /* Build up the settings required for 'team' */ + gboolean success = FALSE; char *team_ifname = NULL; const char *ifname = NULL; - const char *config = NULL; - nmc_arg_t exp_args[] = { {"config", TRUE, &config, FALSE}, + const char *config_c = NULL; + char *config = NULL; + char *json = NULL; + nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; + /* Also ask for all optional arguments if '--ask' is specified. */ + config = g_strdup (config_c); + if (ask) + do_questionnaire_team (&config); + /* Use connection's ifname as 'team' ifname if exists, else generate one */ ifname = nm_setting_connection_get_interface_name (s_con); if (!ifname) @@ -3815,22 +3870,35 @@ cleanup_bond: s_team = (NMSettingTeam *) nm_setting_team_new (); nm_connection_add_setting (connection, NM_SETTING (s_team)); + if (!nmc_team_check_config (config, &json, error)) { + g_prefix_error (error, _("Error: ")); + goto cleanup_team; + } + /* Set team options */ g_object_set (s_team, NM_SETTING_TEAM_INTERFACE_NAME, team_ifname, NULL); - if (config) - g_object_set (s_team, NM_SETTING_TEAM_CONFIG, config, NULL); + g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL); + success = TRUE; +cleanup_team: g_free (team_ifname); + g_free (config); + g_free (json); + if (!success) + 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 *type = NULL; - const char *config = NULL; - nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, - {"type", TRUE, &type, FALSE}, - {"config", TRUE, &config, FALSE}, + 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} }; if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) @@ -3844,6 +3912,11 @@ cleanup_bond: return FALSE; } + /* Also ask for all optional arguments if '--ask' is specified. */ + config = g_strdup (config_c); + if (ask) + do_questionnaire_team_slave (&config); + if (type) printf (_("Warning: 'type' is currently ignored. " "We only support ethernet slaves for now.\n")); @@ -3852,8 +3925,13 @@ cleanup_bond: s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); nm_connection_add_setting (connection, NM_SETTING (s_team_port)); - if (config) - g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, config, NULL); + 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, @@ -3866,7 +3944,13 @@ cleanup_bond: 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' */ diff --git a/cli/src/settings.c b/cli/src/settings.c index 4e4903facc..712622bf68 100644 --- a/cli/src/settings.c +++ b/cli/src/settings.c @@ -3100,6 +3100,34 @@ nmc_property_serial_set_parity (NMSetting *setting, const char *prop, const char return TRUE; } +/* --- NM_SETTING_TEAM_SETTING_NAME property functions --- */ +/* --- NM_SETTING_TEAM_PORT_SETTING_NAME property functions --- */ +static gboolean +nmc_property_team_set_config (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char *json = NULL; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_team_check_config (val, &json, error)) { + return FALSE; + } + g_object_set (setting, prop, json, NULL); + g_free (json); + return TRUE; +} + +static const char * +nmc_property_team_describe_config (NMSetting *setting, const char *prop) +{ + return _("nmcli can accepts both direct JSON configuration data and a file name containing " + "the configuration. In the latter case the file is read and the contents is put " + "into this property.\n\n" + "Examples: set team.config " + "{ \"device\": \"team0\", \"runner\": {\"name\": \"roundrobin\"}, \"ports\": {\"eth1\": {}, \"eth2\": {}} }\n" + " set team.config /etc/my-team.conf\n"); +} + /* --- NM_SETTING_VLAN_SETTING_NAME property setter functions --- */ static gboolean nmc_property_vlan_set_prio_map (NMSetting *setting, @@ -4590,18 +4618,18 @@ nmc_properties_init (void) NULL); nmc_add_prop_funcs (GLUE (TEAM, CONFIG), nmc_property_team_get_config, - nmc_property_set_string, - NULL, + nmc_property_team_set_config, NULL, + nmc_property_team_describe_config, NULL, NULL); /* Add editable properties for NM_SETTING_TEAM_PORT_SETTING_NAME */ nmc_add_prop_funcs (GLUE (TEAM_PORT, CONFIG), nmc_property_team_port_get_config, - nmc_property_set_string, - NULL, + nmc_property_team_set_config, NULL, + nmc_property_team_describe_config, NULL, NULL); |