mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-07 07:50:17 +01:00
cli: add 'nmcli connection clone' for cloning connections (bgo #757627)
Synopsis: nmcli connection clone [--temporary] [id|uuid|path] <ID> <new name> It copies the <ID> connection as <new name>. The command is very useful if there is a connection, but another one is needed for a related configuration. One can copy the existing profile and modify it for the new situation. For example: $ nmcli con clone main-eth second-eth $ nmcli con modify second-eth connection.interface-name em4 https://bugzilla.gnome.org/show_bug.cgi?id=757627
This commit is contained in:
parent
f902444325
commit
0c65b28960
3 changed files with 203 additions and 1 deletions
|
|
@ -251,6 +251,7 @@ usage (void)
|
|||
" down [id | uuid | path | apath] <ID> ...\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"
|
||||
" clone [--temporary] [id | uuid | path ] <ID> <new name>\n\n"
|
||||
" edit [id | uuid | path] <ID>\n"
|
||||
" edit [type <new_con_type>] [con-name <new_con_name>]\n\n"
|
||||
" delete [id | uuid | path] <ID>\n\n"
|
||||
|
|
@ -427,6 +428,18 @@ usage_connection_modify (void)
|
|||
"nmcli con mod bond0 -bond.options downdelay\n\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
usage_connection_clone (void)
|
||||
{
|
||||
g_printerr (_("Usage: nmcli connection clone { ARGUMENTS | help }\n"
|
||||
"\n"
|
||||
"ARGUMENTS := [--temporary] [id | uuid | path] <ID> <new name>\n"
|
||||
"\n"
|
||||
"Clone an existing connection profile. The newly created connection will be\n"
|
||||
"the exact copy of the <ID>, except the uuid property (will be generated) and\n"
|
||||
"id (provided as <new name> argument).\n\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
usage_connection_edit (void)
|
||||
{
|
||||
|
|
@ -488,6 +501,8 @@ usage_connection_second_level (const char *cmd)
|
|||
usage_connection_add ();
|
||||
else if (matches (cmd, "modify") == 0)
|
||||
usage_connection_modify ();
|
||||
else if (matches (cmd, "clone") == 0)
|
||||
usage_connection_clone ();
|
||||
else if (matches (cmd, "edit") == 0)
|
||||
usage_connection_edit ();
|
||||
else if (matches (cmd, "delete") == 0)
|
||||
|
|
@ -9145,6 +9160,142 @@ finish:
|
|||
return nmc->return_value;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
NmCli *nmc;
|
||||
char *orig_id;
|
||||
char *orig_uuid;
|
||||
char *con_id;
|
||||
} CloneConnectionInfo;
|
||||
|
||||
static void
|
||||
clone_connection_cb (GObject *client,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
CloneConnectionInfo *info = (CloneConnectionInfo *) user_data;
|
||||
NmCli *nmc = info->nmc;
|
||||
NMRemoteConnection *connection;
|
||||
GError *error = NULL;
|
||||
|
||||
connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
|
||||
if (error) {
|
||||
g_string_printf (nmc->return_text,
|
||||
_("Error: Failed to add '%s' connection: %s"),
|
||||
info->con_id, error->message);
|
||||
g_error_free (error);
|
||||
nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
|
||||
} else {
|
||||
g_print (_("%s (%s) cloned as %s (%s).\n"),
|
||||
info->orig_id,
|
||||
info->orig_uuid,
|
||||
nm_connection_get_id (NM_CONNECTION (connection)),
|
||||
nm_connection_get_uuid (NM_CONNECTION (connection)));
|
||||
g_object_unref (connection);
|
||||
}
|
||||
|
||||
g_free (info->con_id);
|
||||
g_free (info->orig_id);
|
||||
g_free (info->orig_uuid);
|
||||
g_slice_free (CloneConnectionInfo, info);
|
||||
quit ();
|
||||
}
|
||||
|
||||
static NMCResultCode
|
||||
do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv)
|
||||
{
|
||||
NMConnection *connection = NULL;
|
||||
NMConnection *new_connection = NULL;
|
||||
NMSettingConnection *s_con;
|
||||
CloneConnectionInfo *info;
|
||||
const char *name;
|
||||
const char *new_name;
|
||||
char *name_ask = NULL;
|
||||
char *new_name_ask = NULL;
|
||||
const char *selector = NULL;
|
||||
char *uuid;
|
||||
|
||||
if (argc == 0) {
|
||||
if (nmc->ask) {
|
||||
name = name_ask = nmc_readline (PROMPT_CONNECTION);
|
||||
new_name = new_name_ask = nmc_readline ("New connection name: ");
|
||||
} else {
|
||||
g_string_printf (nmc->return_text, _("Error: No arguments provided."));
|
||||
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
|
||||
goto finish;
|
||||
}
|
||||
} else {
|
||||
if ( strcmp (*argv, "id") == 0
|
||||
|| strcmp (*argv, "uuid") == 0
|
||||
|| strcmp (*argv, "path") == 0) {
|
||||
|
||||
selector = *argv;
|
||||
if (next_arg (&argc, &argv) != 0) {
|
||||
g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
|
||||
selector);
|
||||
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
|
||||
goto finish;
|
||||
}
|
||||
name = *argv;
|
||||
}
|
||||
name = *argv;
|
||||
if (!name) {
|
||||
g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
|
||||
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
|
||||
goto finish;
|
||||
}
|
||||
if (next_arg (&argc, &argv) != 0) {
|
||||
g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
|
||||
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
|
||||
goto finish;
|
||||
}
|
||||
new_name = *argv;
|
||||
}
|
||||
|
||||
connection = nmc_find_connection (nmc->connections, selector, name, NULL);
|
||||
if (!connection) {
|
||||
g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
|
||||
nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Copy the connection */
|
||||
new_connection = nm_simple_connection_new_clone (connection);
|
||||
|
||||
s_con = nm_connection_get_setting_connection (new_connection);
|
||||
g_assert (s_con);
|
||||
uuid = nm_utils_uuid_generate ();
|
||||
g_object_set (s_con,
|
||||
NM_SETTING_CONNECTION_ID, new_name,
|
||||
NM_SETTING_CONNECTION_UUID, uuid,
|
||||
NULL);
|
||||
g_free (uuid);
|
||||
|
||||
/* Merge secrets into the new connection */
|
||||
update_secrets_in_connection (NM_REMOTE_CONNECTION (connection), new_connection);
|
||||
|
||||
info = g_slice_new0 (CloneConnectionInfo);
|
||||
info->nmc = nmc;
|
||||
info->orig_id = g_strdup (nm_connection_get_id (connection));
|
||||
info->orig_uuid = g_strdup (nm_connection_get_uuid (connection));
|
||||
info->con_id = g_strdup (nm_connection_get_id (new_connection));
|
||||
|
||||
/* Add the new cloned connection to NetworkManager */
|
||||
add_new_connection (!temporary,
|
||||
nmc->client,
|
||||
new_connection,
|
||||
clone_connection_cb,
|
||||
info);
|
||||
|
||||
nmc->should_wait = TRUE;
|
||||
finish:
|
||||
if (new_connection)
|
||||
g_object_unref (new_connection);
|
||||
g_free (name_ask);
|
||||
g_free (new_name_ask);
|
||||
|
||||
return nmc->return_value;
|
||||
}
|
||||
|
||||
static void
|
||||
delete_cb (GObject *con, GAsyncResult *result, gpointer user_data)
|
||||
{
|
||||
|
|
@ -9604,6 +9755,15 @@ do_connections (NmCli *nmc, int argc, char **argv)
|
|||
next_arg (&argc, &argv);
|
||||
}
|
||||
nmc->return_value = do_connection_modify (nmc, temporary, argc, argv);
|
||||
} else if (matches (*argv, "clone") == 0) {
|
||||
gboolean temporary = FALSE;
|
||||
|
||||
next_arg (&argc, &argv);
|
||||
if (nmc_arg_is_option (*argv, "temporary")) {
|
||||
temporary = TRUE;
|
||||
next_arg (&argc, &argv);
|
||||
}
|
||||
nmc->return_value = do_connection_clone (nmc, temporary, argc, argv);
|
||||
} else {
|
||||
usage ();
|
||||
g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
|
||||
|
|
|
|||
|
|
@ -870,7 +870,7 @@ _nmcli()
|
|||
;;
|
||||
c|co|con|conn|conne|connec|connect|connecti|connectio|connection)
|
||||
if [[ ${#words[@]} -eq 2 ]]; then
|
||||
_nmcli_compl_COMMAND "$command" show up down add modify edit delete reload load
|
||||
_nmcli_compl_COMMAND "$command" show up down add modify clone edit delete reload load
|
||||
elif [[ ${#words[@]} -gt 2 ]]; then
|
||||
case "$command" in
|
||||
s|sh|sho|show)
|
||||
|
|
@ -1252,6 +1252,34 @@ _nmcli()
|
|||
return 0
|
||||
fi
|
||||
;;
|
||||
c|cl|clo|clon|clone)
|
||||
if [[ ${#words[@]} -eq 3 ]]; then
|
||||
_nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" temporary
|
||||
elif [[ ${#words[@]} -gt 3 ]]; then
|
||||
_nmcli_array_delete_at words 0 1
|
||||
|
||||
LONG_OPTIONS=(help temporary)
|
||||
HELP_ONLY_AS_FIRST=1
|
||||
_nmcli_compl_OPTIONS
|
||||
case $? in
|
||||
0)
|
||||
return 0
|
||||
;;
|
||||
1)
|
||||
if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then
|
||||
_nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}"
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
OPTIONS=(id uuid path)
|
||||
_nmcli_compl_ARGS_CONNECTION && return 0
|
||||
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
|
||||
de|del|dele|delet|delete)
|
||||
if [[ ${#words[@]} -eq 3 ]]; then
|
||||
_nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")"
|
||||
|
|
|
|||
|
|
@ -739,6 +739,20 @@ The changes to the connection profile will be saved persistently by
|
|||
NetworkManager, unless \fI--temporary\fP option is provided, in which case the
|
||||
changes won't persist over NetworkManager restart.
|
||||
.TP
|
||||
.B clone [--temporary] [ id | uuid | path ] <ID> <new name>
|
||||
.br
|
||||
Clone a connection. The connection to be cloned is identified by its
|
||||
name, UUID or D-Bus path. If <ID> is ambiguous, a keyword \fIid\fP,
|
||||
\fIuuid\fP or \fIpath\fP can be used. See \fBconnection show\fP above for
|
||||
the description of the <ID>-specifying keywords. \fI<new name>\fP is the name
|
||||
of the new cloned connection. The new connection will be the exact copy except
|
||||
the connection.id (\fI<new name>\fP) and connection.uuid (generated)
|
||||
properties.
|
||||
.br
|
||||
The new connection profile will be saved as persistent unless \fI--temporary\fP
|
||||
option is specified, in which case the new profile won't outlive NetworkManager
|
||||
restart.
|
||||
.TP
|
||||
.B delete [ id | uuid | path ] <ID> ...
|
||||
.br
|
||||
Delete a configured connection. The connection to be deleted is identified by
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue