[libvirt] [PATCH] virsh edit command

Richard W.M. Jones rjones at redhat.com
Tue Jul 29 12:27:42 UTC 2008


One thing which is very apparent is that sys admins using libvirt /
virsh have a great deal of difficulty understanding "where the
configuration files have gone" and how to edit them.

This patch adds a "virsh edit <domain>" command which is basically the
equivalent of:

  virsh dumpxml dom > /tmp/dom.xml
  $EDITOR /tmp/dom.xml && virsh define /tmp/dom.xml

but with much more sanity checking.  The editor is $EDITOR or vi, and
it does the right thing if the user doesn't modify the file, or if
another user edits the configuration at the same time.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
Index: docs/virsh.pod
===================================================================
RCS file: /data/cvs/libvirt/docs/virsh.pod,v
retrieving revision 1.16
diff -u -r1.16 virsh.pod
--- docs/virsh.pod	15 May 2008 06:12:32 -0000	1.16
+++ docs/virsh.pod	29 Jul 2008 12:21:38 -0000
@@ -277,6 +277,19 @@
 
 Output the domain information as an XML dump to stdout, this format can be used by the B<create> command.
 
+=item B<edit> I<domain-id>
+
+Edit the XML configuration file for a domain.
+
+This is equivalent to:
+ virsh dumpxml domain > domain.xml
+ edit domain.xml
+ virsh define domain.xml
+except that it does some error checking.
+
+The editor used can be supplied by the C<$EDITOR> environment
+variable, or if that is not defined defaults to C<vi>.
+
 =item B<migrate> optional I<--live> I<domain-id> I<desturi> I<migrateuri>
 
 Migrate domain to another host.  Add --live for live migration. The I<desturi>
Index: src/virsh.c
===================================================================
RCS file: /data/cvs/libvirt/src/virsh.c,v
retrieving revision 1.157
diff -u -r1.157 virsh.c
--- src/virsh.c	22 Jul 2008 16:12:01 -0000	1.157
+++ src/virsh.c	29 Jul 2008 12:21:42 -0000
@@ -5070,6 +5070,179 @@
 }
 
 /*
+ * "edit" command
+ */
+static vshCmdInfo info_edit[] = {
+    {"syntax", "edit <domain>"},
+    {"help", gettext_noop("edit XML configuration for a domain")},
+    {"desc", gettext_noop("Edit the XML configuration for a domain.")},
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_edit[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdEdit (vshControl *ctl, vshCmd *cmd)
+{
+    int ret = FALSE;
+    virDomainPtr dom = NULL;
+    char *doc = NULL;
+    char *tmp = NULL;
+    int fd = -1;
+    const char *editor;
+    char command[100];
+    int command_ret;
+    char *doc_edited = NULL;
+    struct stat statbuf;
+    char *doc_reread = NULL;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        goto cleanup;
+
+    dom = vshCommandOptDomain (ctl, cmd, "domain", NULL);
+    if (dom == NULL)
+        goto cleanup;
+
+    /* Get the XML configuration of the domain. */
+    doc = virDomainGetXMLDesc (dom, 0);
+    if (!doc)
+        goto cleanup;
+
+    /* Create and open the temporary file. */
+    tmp = tempnam (NULL, "virsh");
+    if (!tmp) {
+        vshError(ctl, FALSE,
+                 _("tempnam: failed to create temporary file: %s"),
+                 strerror (errno));
+        goto cleanup;
+    }
+    fd = open (tmp, O_EXCL|O_CREAT|O_WRONLY, 0600);
+    if (fd == -1) {
+        vshError(ctl, FALSE,
+                 _("open: %s: failed to create temporary file: %s"),
+                 tmp, strerror (errno));
+        goto cleanup;
+    }
+
+    if (safewrite (fd, doc, strlen (doc)) == -1) {
+        vshError(ctl, FALSE,
+                 _("write: %s: failed to create temporary file: %s"),
+                 tmp, strerror (errno));
+        goto cleanup;
+    }
+    if (close (fd) == -1) {
+        vshError(ctl, FALSE,
+                 _("close: %s: failed to create temporary file: %s"),
+                 tmp, strerror (errno));
+        goto cleanup;
+    }
+    fd = -1;
+
+    /* Start the editor. */
+    editor = getenv ("EDITOR");
+    if (!editor) editor = "vi"; /* could be cruel & default to ed(1) here */
+
+    snprintf (command, sizeof command, "%s %s", editor, tmp);
+    command_ret = system (command);
+
+    if (command_ret == -1) {
+        vshError(ctl, FALSE,
+                 "%s: %s",
+                 command, strerror (errno));
+        goto cleanup;
+    }
+    if (command_ret != WEXITSTATUS (0)) {
+        vshError(ctl, FALSE,
+                 _("%s: command exited with non-zero status"), command);
+        goto cleanup;
+    }
+
+    /* Read back the edited XML file. */
+    fd = open (tmp, O_RDONLY);
+    if (fd == -1) {
+        vshError(ctl, FALSE,
+                 _("open: %s: failed to read temporary file: %s"),
+                 tmp, strerror (errno));
+        goto cleanup;
+    }
+    if (fstat (fd, &statbuf) == -1) {
+        vshError(ctl, FALSE,
+                 _("stat: %s: failed to read temporary file: %s"),
+                 tmp, strerror (errno));
+        goto cleanup;
+    }
+    doc_edited = malloc (statbuf.st_size + 1);
+    if (!doc_edited) {
+        vshError(ctl, FALSE,
+                 _("malloc: failed to allocate memory for file: %s"),
+                 strerror (errno));
+        goto cleanup;
+    }
+    doc_edited[statbuf.st_size+1] = '\0';
+    if (saferead (fd, doc_edited, statbuf.st_size) == -1) {
+        vshError(ctl, FALSE,
+                 _("read: %s: failed to read temporary file: %s"),
+                 tmp, strerror (errno));
+        goto cleanup;
+    }
+    close (fd);
+    fd = -1;
+
+    unlink (tmp);
+    tmp = NULL;
+
+    /* Compare original XML with edited.  Has it changed at all? */
+    if (STREQ (doc, doc_edited)) {
+        vshPrint(ctl, _("Domain %s XML configuration not changed.\n"),
+                 virDomainGetName (dom));
+        ret = TRUE;
+        goto cleanup;
+    }
+
+    /* Now re-read the domain XML.  Did someone else change it while
+     * it was being edited?  This also catches problems such as us
+     * losing a connection or the domain going away.
+     */
+    doc_reread = virDomainGetXMLDesc (dom, 0);
+    if (!doc_reread)
+        goto cleanup;
+
+    if (STRNEQ (doc, doc_reread)) {
+        vshError (ctl, FALSE,
+                  _("ERROR: the XML configuration was changed by another user"));
+        goto cleanup;
+    }
+
+    /* Everything checks out, so redefine the domain. */
+    virDomainFree (dom);
+    dom = virDomainDefineXML(ctl->conn, doc_edited);
+    if (!dom)
+        goto cleanup;
+
+    vshPrint(ctl, _("Domain %s XML configuration edited.\n"),
+             virDomainGetName(dom));
+
+    ret = TRUE;
+
+ cleanup:
+    if (fd >= 0)
+        close (fd);
+    if (dom)
+        virDomainFree(dom);
+    free (doc);
+    free (doc_edited);
+    if (tmp) {
+        unlink (tmp);
+        free (tmp);
+    }
+
+    return ret;
+}
+
+/*
  * "quit" command
  */
 static vshCmdInfo info_quit[] = {
@@ -5112,6 +5285,7 @@
     {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat},
     {"domifstat", cmdDomIfstat, opts_domifstat, info_domifstat},
     {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml},
+    {"edit", cmdEdit, opts_edit, info_edit},
     {"freecell", cmdFreecell, opts_freecell, info_freecell},
     {"hostname", cmdHostname, NULL, info_hostname},
     {"list", cmdList, opts_list, info_list},


More information about the libvir-list mailing list