summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiří Klimeš <jklimes@redhat.com>2013-11-11 17:48:25 +0100
committerJiří Klimeš <jklimes@redhat.com>2013-11-22 14:09:10 +0100
commita3c06afc12e0cab6e09ec32139fa5cb0d4d98e2d (patch)
tree4adc79fbb0bb0531e4eeca03c7cb96e7211dcc2c
parent799477872394a5e473cb868e8c3ae2ba5fff7f50 (diff)
downloadNetworkManager-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.c33
-rw-r--r--cli/src/common.h3
-rw-r--r--cli/src/connections.c108
-rw-r--r--cli/src/settings.c36
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);