I've recently worked with rather large number of virtual machines
and needed to rename all domains. I couldn't find better way how
to rename domain other than:
virsh dumpxml domain > domain.xml
(change domain name in domain.xml)
virsh undefine domain
virsh define domain.xml
This is rather pain to do every time I want to rename domain.
I think there should be simple way to change domain name.
So, I decided to implement new command which will basically perform
all actions listed above. When running:
virsh rename foo bar
domain foo will be renamed to bar.
Command rename is implemented using cmdUndefine options with addition
of option name-name.
I included opts_undefine into the opts_rename because I couldn't find
any other way how to call cmdUndefine directly from the cmdRename and
not getting vshCommandOpt assertion fails.
In order to hide undefine options, I added VSH_OFLAG_HIDDEN flag to
the Command Option Flags, so options flagged as hidden wont show up
in help and autocompletion results.
But it is still possible to run rename command with undefine flags:
virsh rename foo bar --managed-save
I would like to call cmdUndefine from cmdRename and not having to
use undefine options as a part of rename options but I just don't
know how to do it at this point.
---
tools/virsh-domain.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++
tools/virsh.c | 10 ++++
tools/virsh.h | 2 +
tools/virsh.pod | 12 +++++
4 files changed, 157 insertions(+)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index c75cd73..cd8c663 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -5103,6 +5103,133 @@ cmdReboot(vshControl *ctl, const vshCmd *cmd)
}
/*
+ * "rename" command
+ */
+static const vshCmdInfo info_rename[] = {
+ {.name = "help",
+ .data = N_("rename a domain")
+ },
+ {.name = "desc",
+ .data = N_("Change domain name.")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_rename[] = {
+ {.name = "domain",
+ .type = VSH_OT_DATA,
+ .flags = VSH_OFLAG_REQ,
+ .help = N_("domain name or uuid")
+ },
+ {.name = "new-name",
+ .type = VSH_OT_DATA,
+ .flags = VSH_OFLAG_REQ,
+ .help = N_("new domain name")
+ },
+ {.name = "managed-save",
+ .type = VSH_OT_BOOL,
+ .flags = VSH_OFLAG_HIDDEN,
+ },
+ {.name = "storage",
+ .type = VSH_OT_DATA,
+ .flags = VSH_OFLAG_HIDDEN,
+ },
+ {.name = "remove-all-storage",
+ .type = VSH_OT_BOOL,
+ .flags = VSH_OFLAG_HIDDEN,
+ },
+ {.name = "wipe-storage",
+ .type = VSH_OT_BOOL,
+ .flags = VSH_OFLAG_HIDDEN,
+ },
+ {.name = "snapshots-metadata",
+ .type = VSH_OT_BOOL,
+ .flags = VSH_OFLAG_HIDDEN,
+ },
+ {.name = NULL}
+};
+
+static bool
+cmdRename(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ virDomainPtr new_dom = NULL;
+ bool ret = false;
+ int dom_state;
+ int xml_size;
+ char *dom_xml = NULL;
+ const char *new_name = NULL;
+ xmlDocPtr doc = NULL;
+ xmlChar *new_dom_xml = NULL;
+ xmlNodePtr name_node = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (virDomainGetState(dom, &dom_state, NULL, 0) < 0) {
+ vshError(ctl, "%s", _("Failed to get domain state"));
+ goto cleanup;
+ }
+
+ if (dom_state != VIR_DOMAIN_SHUTOFF) {
+ vshError(ctl, _("Please shutdown domain before renaming"));
+ goto cleanup;
+ }
+
+ if (vshCommandOptString(cmd, "new-name", &new_name) <= 0) {
+ vshError(ctl, _("Failed to parse --new-name parameter"));
+ goto cleanup;
+ }
+
+ if (!(dom_xml = virDomainGetXMLDesc(dom, 0)))
+ goto cleanup;
+ if (!(doc = virXMLParseStringCtxt(dom_xml, NULL, &ctxt)))
+ goto cleanup;
+
+ obj = xmlXPathEval(BAD_CAST "/domain/name", ctxt);
+ if (obj == NULL || obj->type != XPATH_NODESET || obj->nodesetval == NULL ||
+ obj->nodesetval->nodeNr == 0 || obj->nodesetval->nodeTab == NULL) {
+ vshError(ctl, _("Failed to extract domain name"));
+ goto cleanup;
+ }
+
+ if (!(name_node = obj->nodesetval->nodeTab[0]->children))
+ goto cleanup;
+
+ xmlNodeSetContent(name_node, BAD_CAST new_name);
+ xmlDocDumpMemory(doc, &new_dom_xml, &xml_size);
+ if (new_dom_xml == NULL || xml_size <= 0) {
+ vshError(ctl, _("Failed to format new XML for domain %s"), new_name);
+ goto cleanup;
+ }
+
+ if (!cmdUndefine(ctl, cmd))
+ goto cleanup;
+
+ if (!(new_dom = virDomainDefineXML(ctl->conn, (char *)new_dom_xml))) {
+ vshError(ctl, _("Failed to define domain %s"), new_name);
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("Domain %s has been renamed to %s\n"),
+ virDomainGetName(dom), virDomainGetName(new_dom));
+ ret = true;
+
+ cleanup:
+ virDomainFree(dom);
+ if (new_dom)
+ virDomainFree(new_dom);
+ VIR_FREE(dom_xml);
+ VIR_FREE(new_dom_xml);
+ xmlFreeDoc(doc);
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ return ret;
+}
+
+/*
* "reset" command
*/
static const vshCmdInfo info_reset[] = {
@@ -12057,6 +12184,12 @@ const vshCmdDef domManagementCmds[] = {
.info = info_reboot,
.flags = 0
},
+ {.name = "rename",
+ .handler = cmdRename,
+ .opts = opts_rename,
+ .info = info_rename,
+ .flags = 0
+ },
{.name = "reset",
.handler = cmdReset,
.opts = opts_reset,
diff --git a/tools/virsh.c b/tools/virsh.c
index 713c9a5..ef0d2df 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -1299,6 +1299,10 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname)
const vshCmdOptDef *opt;
for (opt = def->opts; opt->name; opt++) {
const char *fmt = "%s";
+
+ if (opt->flags & VSH_OFLAG_HIDDEN)
+ continue;
+
switch (opt->type) {
case VSH_OT_BOOL:
fmt = "[--%s]";
@@ -1352,6 +1356,9 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname)
const vshCmdOptDef *opt;
fputs(_("\n OPTIONS\n"), stdout);
for (opt = def->opts; opt->name; opt++) {
+ if (opt->flags & VSH_OFLAG_HIDDEN)
+ continue;
+
switch (opt->type) {
case VSH_OT_BOOL:
snprintf(buf, sizeof(buf), "--%s", opt->name);
@@ -3001,6 +3008,9 @@ vshReadlineOptionsGenerator(const char *text, int state)
/* ignore non --option */
continue;
+ if (opt->flags & VSH_OFLAG_HIDDEN)
+ continue;
+
if (len > 2) {
if (STRNEQLEN(name, text + 2, len - 2))
continue;
diff --git a/tools/virsh.h b/tools/virsh.h
index b4df24b..5fa22d0 100644
--- a/tools/virsh.h
+++ b/tools/virsh.h
@@ -136,6 +136,8 @@ enum {
VSH_OFLAG_REQ = (1 << 0), /* option required */
VSH_OFLAG_EMPTY_OK = (1 << 1), /* empty string option allowed */
VSH_OFLAG_REQ_OPT = (1 << 2), /* --optionname required */
+ VSH_OFLAG_HIDDEN = (1 << 4), /* option doesn't show up in help
+ * messages and autocompletion */
};
/* forward declarations */
diff --git a/tools/virsh.pod b/tools/virsh.pod
index ea9267e..3c56695 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1512,6 +1512,18 @@ try each mode is undefined, and not related to the order specified
to virsh.
For strict control over ordering, use a single mode at a time and
repeat the command.
+=item B<rename> I<domain> I<new-name>
+
+Rename a domain. This command changes current domain name to the new name
+specified by the user. This is equivalent to:
+
+ virsh dumpxml domain > domain.xml
+ (edit domain.xml, change <name>domain</name> to
<name>new-name</name>)
+ virsh undefine domain
+ virsh define domain.xml
+
+B<Note>: Domain must be shut down before renaming.
+
=item B<reset> I<domain>
Reset a domain immediately without any guest shutdown. B<reset>
--
1.9.3