[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH RFC v2] virsh: Add option to undefine storage with domains



Add an option for virsh undefine command, to remove associated storage
volumes while undefining a domain. This patch alows the user to remove
associated (libvirt managed ) storage volumes while undefining a domain.

The new option --storage for the undefine command takes a string
argument that consists of comma separated list of target volumes to be
undefined. Each of the volumes is removed from the domain definition and
afterwards removed from storage pools.

If a volume is not part of a storage pool, the user is warned to remove
the volume in question himself.

Option --wipe-storage may be specified along with this, that ensures
the image is wiped before removing.
---
 tools/virsh.c   |  167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/virsh.pod |   12 ++++
 2 files changed, 179 insertions(+), 0 deletions(-)

diff --git a/tools/virsh.c b/tools/virsh.c
index 3b060bf..67125c9 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -1858,6 +1858,9 @@ static const vshCmdInfo info_undefine[] = {
 static const vshCmdOptDef opts_undefine[] = {
     {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
     {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
+    {"storage", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("remove associated storage volumes (comma separated list of targets) (see domblklist)")},
+    {"wipe-storage", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("wipe data on the removed volumes")},
     {"snapshots-metadata", VSH_OT_BOOL, 0,
      N_("remove all domain snapshot metadata, if inactive")},
     {NULL, 0, 0, NULL}
@@ -1874,6 +1877,8 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
     /* User-requested actions.  */
     bool managed_save = vshCommandOptBool(cmd, "managed-save");
     bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
+    bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
+    bool remove_volumes = false;
     /* Positive if these items exist.  */
     int has_managed_save = 0;
     int has_snapshots_metadata = 0;
@@ -1883,6 +1888,25 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
     bool snapshots_safe = false;
     int rc = -1;
     int running;
+    /* list of volumes to remove along with this domain */
+    const char *volumes_arg = NULL;
+    char *volumes = NULL;
+    const char *volume;
+    char *saveptr = NULL;
+    char *def = NULL;
+    char *xpath_str = NULL;
+    char *volume_path = NULL;
+    char *device_desc = NULL;
+    xmlNodePtr node = NULL;
+    xmlXPathContextPtr ctxt_dom = NULL;
+    xmlXPathContextPtr ctxt_dev = NULL;
+    xmlBufferPtr xml_buf = NULL;
+    xmlDocPtr xml_dom = NULL;
+    xmlDocPtr xml_dev = NULL;
+    virStorageVolPtr vol = NULL;
+    bool wipe_failed;
+
+    virBuffer xpath = VIR_BUFFER_INITIALIZER;

     if (managed_save) {
         flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
@@ -1893,12 +1917,19 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
         snapshots_safe = true;
     }

+
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;

     if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
         return false;

+    /* check if a string that should contain list of volumes to remove is present */
+    if (vshCommandOptString(cmd, "storage", &volumes_arg) > 0) {
+        remove_volumes = true;
+        volumes = vshStrdup(ctl, volumes_arg);
+    }
+
     /* Do some flag manipulation.  The goal here is to disable bits
      * from flags to reduce the likelihood of a server rejecting
      * unknown flag bits, as well as to track conditions which are
@@ -1961,6 +1992,138 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
         snapshots_safe = true;
     }

+    /* try to undefine storage volumes associated with this domain, if it's requested */
+    if (remove_volumes) {
+        if (running) {
+            vshError(ctl, _("Refusing to remove storage for running domain"));
+            goto cleanup;
+        }
+
+        /* check if managed save is being removed, as the domain removal
+         * might fail and leave it without storage */
+        if (has_managed_save && !managed_save) {
+            vshError(ctl, "%s",
+                     _("Refusing to undefine with storage volumes while domain managed save "
+                       "image exists"));
+            goto cleanup;
+        }
+
+        /* get domain description */
+        if (!(def = virDomainGetXMLDesc(dom, 0)))
+            goto cleanup;
+
+        xml_dom = virXMLParseStringCtxt(def, _("(domain definition)"), &ctxt_dom);
+        VIR_FREE(def);
+
+        if (!xml_dom)
+            goto cleanup;
+
+        xml_buf = xmlBufferCreate();
+        if (!xml_buf)
+            goto cleanup;
+
+        /* tokenize the string of devices to remove */
+        volume = strtok_r(volumes, ",", &saveptr);
+
+        while (volume) {
+
+            /* find and try to remove each volume selected by the user */
+            virBufferAsprintf(&xpath, "/domain/devices/disk[target[ dev='%s']]", volume);
+            xpath_str = virBufferContentAndReset(&xpath);
+
+            node = virXPathNode(xpath_str, ctxt_dom);
+            VIR_FREE(xpath_str);
+            if (!node) {
+                vshPrint(ctl, _("Volume %s not found. Skipping\n"), volume);
+                goto next_volume;
+            }
+
+            xmlBufferEmpty(xml_buf);
+
+            /* dump and detatch the device from persistent conf. */
+
+            if (xmlNodeDump(xml_buf, xml_dom, node, 0, 0) < 0) {
+                vshError(ctl, _("Failed to create device description"));
+                goto cleanup;
+            }
+
+            device_desc = (char *) xmlBufferContent(xml_buf);
+
+            if (virDomainDetachDeviceFlags(dom, device_desc, VIR_DOMAIN_AFFECT_CURRENT) < 0) {
+                vshError(ctl,
+                         _("Failed to remove storage device '%s' from definition. "
+                           "If domain undefinition fails, this domain may remain inconsistent"),
+                         volume);
+            }
+
+            xml_dev = virXMLParseStringCtxt(device_desc,
+                                            _("(storage volume definition)"),
+                                            &ctxt_dev);
+
+            /* find the target and lookup the image in storage pools */
+            if (!xml_dev)
+                goto cleanup;
+
+            volume_path = virXPathString("string(//disk/source/@file"
+                                         "| //disk/source/@dev"
+                                         "| //disk/source/@dir"
+                                         "| //disk/source/@name)", ctxt_dev);
+
+            if (!volume_path) {
+                vshPrint(ctl,
+                         _("Virtual storage device '%s' has no associated volume. Skipping."),
+                         volume);
+                goto next_volume;
+            }
+
+            vol = virStorageVolLookupByPath(ctl->conn, volume_path);
+
+            if (!vol) {
+                vshPrint(ctl,
+                         _("Storage volume '%s' is not managed by libvirt. Remove it manualy.\n"),
+                         volume_path);
+                goto next_volume;
+            }
+
+            wipe_failed = false;
+
+            /* wipe the volume */
+            if (wipe_storage) {
+                vshPrint(ctl, _("Wiping volume '%s' ... "), volume_path);
+                if (virStorageVolWipe(vol, 0) < 0) {
+                    vshPrint(ctl, _("Failed!\n"));
+                    wipe_failed = true;
+                } else {
+                    vshPrint(ctl, _("Done.\n"));
+                }
+            }
+
+
+            /* delete the volume */
+            if (wipe_failed || virStorageVolDelete(vol, 0) < 0) {
+                vshError(ctl,
+                         _("Failed to remove storage volume '%s'. Remove it manualy."),
+                         volume_path);
+            } else {
+                vshPrint(ctl, _("Volume '%s' deleted.\n"), volume_path);
+            }
+
+
+next_volume:
+            VIR_FREE(volume_path);
+            if (vol)
+                virStorageVolFree(vol);
+            vol = NULL;
+            xmlXPathFreeContext(ctxt_dev);
+            ctxt_dev = NULL;
+            xmlFreeDoc(xml_dev);
+            xml_dev = NULL;
+
+            volume = strtok_r(NULL, ",", &saveptr);
+        }
+        /* domain's storage removed */
+    }
+
     /* Generally we want to try the new API first.  However, while
      * virDomainUndefineFlags was introduced at the same time as
      * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
@@ -2013,6 +2176,10 @@ out:
     }

 cleanup:
+    xmlFreeDoc(xml_dom);
+    xmlBufferFree(xml_buf);
+    xmlXPathFreeContext(ctxt_dom);
+    VIR_FREE(volumes);
     virDomainFree(dom);
     return ret;
 }
diff --git a/tools/virsh.pod b/tools/virsh.pod
index e82567d..87a1e7d 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1067,6 +1067,7 @@ Output the device used for the TTY console of the domain. If the information
 is not available the processes will provide an exit code of 1.

 =item B<undefine> I<domain-id> [I<--managed-save>] [I<--snapshots-metadata]
+[I<--storage volumes>] [I<--wipe-storage>]

 Undefine a domain. If the domain is running, this converts it to a
 transient domain, without stopping it. If the domain is inactive,
@@ -1082,6 +1083,17 @@ domain.  Without the flag, attempts to undefine an inactive domain with
 snapshot metadata will fail.  If the domain is active, this flag is
 ignored.

+The I<--storage> flag takes a parameter volumes, that is a comma separated
+list of volume target names of storage volumes to be removed along with
+the undefined domain. This operation is valid only for a inactive domain.
+If one or more of the operations while removing disk storage fail, the
+domain may still be undefined and some volumes may remain undeleted.
+(See B<domblklist> for list of target names associated to a domain).
+Example: --storage vda,vdb,vdc
+
+The Flag I<--wipe-storage> specifies, that the storage volumes should be
+wiped before removal.
+
 NOTE: For an inactive domain, the domain name or UUID must be used as the
 I<domain-id>.

-- 
1.7.3.4


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]