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 or source path
of volumes to be undefined. Volumes are removed after the domain has
been successfuly undefined,

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.

Option --remove-all-storage enables the user to remove all storage. The
name is chosen long as the users shoudl be aware what they're about to
do.
---
Changes to v2 based on reveiw by Eric:
( http://www.redhat.com/archives/libvir-list/2011-September/msg00481.html )
        - First undefine the domain, then undefine volumes
        - Add option to remove all volumes
        - Fix typos and strange wordings
        - Change retur value to failure if some operations on volumes fail.

 tools/virsh.c   |  143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/virsh.pod |   18 +++++++
 2 files changed, 161 insertions(+), 0 deletions(-)

diff --git a/tools/virsh.c b/tools/virsh.c
index d58b827..281ffa9 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -1924,6 +1924,13 @@ 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 "
+        "or source paths) (see domblklist)")},
+    {"remove-all-storage", VSH_OT_BOOL, 0,
+     N_("remove all associated storage volumes (use with caution)")},
+    {"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}
@@ -1940,6 +1947,9 @@ 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_storage = false;
+    bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
     /* Positive if these items exist.  */
     int has_managed_save = 0;
     int has_snapshots_metadata = 0;
@@ -1949,6 +1959,22 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
     bool snapshots_safe = false;
     int rc = -1;
     int running;
+    /* list of volumes to remove along with this domain */
+    char *volumes = NULL;
+    const char *volume;
+    char *saveptr = NULL;
+    char *def = NULL;
+    char *source = NULL;
+    char *target = NULL;
+    int i;
+
+    xmlNodePtr *vol_nodes = NULL;
+    int nvolumes = 0;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlDocPtr doc = NULL;
+    virStorageVolPtr vol = NULL;
+    bool vol_del_failed = false;
+

     if (managed_save) {
         flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
@@ -1965,6 +1991,18 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
     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", (const char **)&volumes) > 0) {
+        /* caution volumes has to be re-assigned as it would be doulbe freed */
+        volumes = vshStrdup(ctl, volumes);
+
+        if (remove_all_storage) {
+            vshError(ctl, _("Specified both --storage and 
--remove-all-storage"));
+            goto cleanup;
+        }
+        remove_storage = true;
+   }
+
     /* 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
@@ -2027,6 +2065,20 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
         snapshots_safe = true;
     }

+    /* Stash domain description for later use */
+    if (remove_storage || remove_all_storage) {
+        if (running) {
+            vshError(ctl, _("Storage volume deletion is supported only on 
stopped domains"));
+            goto cleanup;
+        }
+
+        if (!(def = virDomainGetXMLDesc(dom, 0))) {
+            vshError(ctl, _("Could not retrieve domain XML description"));
+            goto cleanup;
+        }
+    }
+
+
     /* 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
@@ -2076,9 +2128,100 @@ out:
         ret = true;
     } else {
         vshError(ctl, _("Failed to undefine domain %s"), name);
+        goto cleanup;
+    }
+
+    /* try to undefine storage volumes associated with this domain, if it's 
requested */
+    if (remove_storage || remove_all_storage) {
+        ret = false;
+        doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
+        if (!doc)
+            goto cleanup;
+
+        nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);
+
+        if (nvolumes < 0)
+            goto cleanup;
+
+        for (i = 0; i < nvolumes; i++) {
+            ctxt->node = vol_nodes[i];
+            VIR_FREE(target);
+            VIR_FREE(source);
+            if (vol) {
+                virStorageVolFree(vol);
+                vol = NULL;
+            }
+
+            /* get volume source and target paths */
+            if (!(target = virXPathString("string(./target/@dev)", ctxt))) {
+                vshError(ctl, _("Failed to enumerate devices"));
+                goto cleanup;
+            }
+
+            if (!(source = virXPathString("string("
+                                          "./source/@file|"
+                                          "./source/@dir|"
+                                          "./source/@name|"
+                                          "./source/@dev)", ctxt)) &&
+                virGetLastError())
+                goto cleanup;
+
+            if (volumes) {
+                volume = strtok_r(volumes, ",", &saveptr);
+                while (volume) {
+                    if (STREQ_NULLABLE(volume, target) ||
+                        STREQ_NULLABLE(volume, source))
+                        break;
+                    volume = strtok_r(NULL, ",", &saveptr);
+                }
+                if (!volume)
+                    continue;
+            }
+
+            if (!source)
+                continue;
+
+            if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
+                vshPrint(ctl,
+                         _("Storage volume '%s' is not managed by libvirt. "
+                           "Remove it manually.\n"), source);
+                virResetLastError();
+                continue;
+            }
+
+            if (wipe_storage) {
+                vshPrint(ctl, _("Wiping volume '%s' ... "), source);
+                fflush(stdout);
+                if (virStorageVolWipe(vol, 0) < 0) {
+                    vshError(ctl, _("Failed! Volume not removed."));
+                    vol_del_failed = true;
+                    continue;
+                } else {
+                    vshPrint(ctl, _("Done.\n"));
+                }
+            }
+
+            /* delete the volume */
+            if (virStorageVolDelete(vol, 0) < 0) {
+                vshError(ctl, _("Failed to remove storage volume '%s'"),
+                         source);
+                vol_del_failed = true;
+            }
+            vshPrint(ctl, _("Volume '%s' removed.\n"), source);
+        }
+
+        if (!vol_del_failed)
+            ret = true;
     }

 cleanup:
+    VIR_FREE(source);
+    VIR_FREE(target);
+    VIR_FREE(volumes);
+    VIR_FREE(def);
+    VIR_FREE(vol_nodes);
+    xmlFreeDoc(doc);
+    xmlXPathFreeContext(ctxt);
     virDomainFree(dom);
     return ret;
 }
diff --git a/tools/virsh.pod b/tools/virsh.pod
index fe92714..c0b55bd 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1179,6 +1179,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> B<volumes> I<--wipe-storage> I<--remove-all-storage>]

 Undefine a domain. If the domain is running, this converts it to a
 transient domain, without stopping it. If the domain is inactive,
@@ -1194,6 +1195,23 @@ 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 B<volumes>, that is a comma separated
+list of volume target names or source paths of storage volumes to be removed 
+along with the undefined domain. Volumes can be undefined and thus removed only
+on inactive domains. Volume deletion is only attempted after the domain is
+undefined; if not all of the requested volumes could be deleted, the the
+error message indicates what still remains behind. If a volume path is not
+found in the domain definition, it's treated as if the volume was successfuly
+deleteted.
+(See B<domblklist> for list of target names associated to a domain).
+Example: --storage vda,vdb,vdc
+
+The I<--remove-all-storage> flag specifies, that all domain's storage volumes
+should be deleted.
+
+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

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to