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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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"
|
||||
@@ -2766,6 +2766,53 @@ do_questionnaire_bond (char **mode, char **primary, char **miimon,
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
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, 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' */
|
||||
|
@@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user