Adds an option to virsh undefine command to undefine managed
storage volumes along with (inactive) domains. Storage volumes
are enumerated and the user may interactivly choose volumes
to delete.

Unmanaged volumes are listed and the user shall delete them
manualy.
---
I marked this as a RFC because I am concerned about my "naming scheme" of  the 
added parameters.
I couldn't decide which of the following "volumes/storage/disks/..." to use. 
I'd appreciate your
comments on this. 

This is my second approach to this problem after I got some really good 
critique from Eric,
Daniel and Dave. The user has the choice to activate an interactive mode, that 
allows to select on a 
per-device basis which volumes/disks to remove along with the domain.

To avoid possible problems, I only allowed to remove storage for inactive 
domains and unmanaged
images (which sidetracked me a lot on my previous attempt) are left to a action 
of the user.
(the user is notified about any unmanaged image for the domain).

My next concern is about interactive of the user. I tried to implement a 
boolean query function, 
but I'd like to know if I took the right path, as I couldn't find any example 
in virsh from which I
could learn.

Thanks for your comments (and time) :)

    Peter Krempa

 tools/virsh.c |  265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 259 insertions(+), 6 deletions(-)

diff --git a/tools/virsh.c b/tools/virsh.c
index 61f69f0..3795d2b 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -295,6 +295,9 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const 
char *name,
 static bool vshCommandOptBool(const vshCmd *cmd, const char *name);
 static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd,
                                           const vshCmdOpt *opt);
+static int vshInteractiveBoolPrompt(vshControl *ctl,
+                                    const char *prompt,
+                                     bool *confirm);

 #define VSH_BYID     (1 << 1)
 #define VSH_BYUUID   (1 << 2)
@@ -1422,6 +1425,8 @@ 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")},
+    {"disks", VSH_OT_BOOL, 0, N_("remove associated disk images managed in 
storage pools (interactive)")},
+    {"disks-all", VSH_OT_BOOL, 0, N_("remove all associated disk images 
managed in storage pools")},
     {NULL, 0, 0, NULL}
 };

@@ -1434,9 +1439,25 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
     int id;
     int flags = 0;
     int managed_save = vshCommandOptBool(cmd, "managed-save");
+    int remove_disks = vshCommandOptBool(cmd, "disks");
+    int remove_all_disks = vshCommandOptBool(cmd, "disks-all");
     int has_managed_save = 0;
     int rc = -1;

+    char *domxml;
+    xmlDocPtr xml = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlXPathObjectPtr obj = NULL;
+    xmlNodePtr cur = NULL;
+    int i = 0;
+    char *source = NULL;
+    char *target = NULL;
+    char *type = NULL;
+    xmlBufferPtr xml_buf = NULL;
+    virStorageVolPtr volume = NULL;
+    int state;
+    bool confirm = false;
+
     if (managed_save)
         flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;

@@ -1475,15 +1496,172 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
         }
     }

-    if (flags == -1) {
-        if (has_managed_save == 1) {
+
+    if (flags == -1 && has_managed_save == 1) {
+        vshError(ctl,
+                 _("Refusing to undefine while domain managed save "
+                   "image exists"));
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (remove_disks || remove_all_disks) {
+        if ((state = vshDomainState(ctl, dom, NULL)) < 0) {
+            vshError(ctl, _("Failed to get domain state"));
+            goto disk_error;
+        }
+
+        /* removal of storage is possible only for inactive domains */
+        if (!((state == VIR_DOMAIN_SHUTOFF) ||
+              (state == VIR_DOMAIN_CRASHED))) {
             vshError(ctl,
-                     _("Refusing to undefine while domain managed save "
-                       "image exists"));
-            virDomainFree(dom);
-            return false;
+                     _("Domain needs to be inactive to delete it with 
associated storage"));
+            goto disk_error;
+        }
+
+        if (remove_disks && !ctl->imode) {
+            vshError(ctl, "%s\n", _("Option --disks is available only in 
interactive mode"));
+            goto disk_error;
+        }
+
+        domxml = virDomainGetXMLDesc(dom, 0);
+        if (!domxml) {
+            vshError(ctl, "%s", _("Failed to get disk information"));
+            goto disk_error;
+        }
+
+        xml = xmlReadDoc((const xmlChar *) domxml, "domain.xml", NULL,
+                         XML_PARSE_NOENT |
+                         XML_PARSE_NONET |
+                         XML_PARSE_NOWARNING);
+        VIR_FREE(domxml);
+
+        if (!xml) {
+            vshError(ctl, "%s", _("Failed to get disk information"));
+            goto disk_error;
+        }
+
+        ctxt = xmlXPathNewContext(xml);
+        if (!ctxt) {
+            vshError(ctl, "%s", _("Failed to get disk information"));
+            goto disk_error;
+        }
+
+        obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
+        if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
+            (obj->nodesetval == NULL)) {
+            vshError(ctl, "%s", _("Failed to get disk information"));
+            goto disk_error;
+        }
+
+        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+            cur = obj->nodesetval->nodeTab[i]->children;
+
+            type = virXMLPropString(cur, "device");
+
+            while (cur != NULL) {
+                if (cur->type == XML_ELEMENT_NODE) {
+                    if (xmlStrEqual(cur->name, BAD_CAST "target"))
+                        target = virXMLPropString(cur, "dev");
+                    else if (xmlStrEqual(cur->name, BAD_CAST "source"))
+                        source = virXMLPropString(cur, "file");
+                }
+                cur = cur->next;
+            }
+
+            if (!source) {
+                VIR_FREE(target);
+                VIR_FREE(type);
+            }
+
+            volume = virStorageVolLookupByPath(ctl->conn,  (const char *) 
source);
+            if (!volume) {
+                vshPrint(ctl, "%s %s %s %s\n",
+                         _("Volume: Source:"), (const char *)source,
+                         _("Target:"), (const char *) target);
+                vshPrint(ctl, _("This volume isn't managed by any storage 
pool, "
+                                "please delete it manualy\n\n"));
+                /* remove error indication */
+                virFreeError(last_error);
+                last_error = NULL;
+            } else {
+                vshPrint(ctl, "%s %s %s %s\n",
+                         _("Volume: Source:"), (const char *)source,
+                         _("Target:"), (const char *) target);
+
+                if (remove_all_disks) {
+                    confirm = true;
+                } else {
+                    if (vshInteractiveBoolPrompt(ctl,
+                                                 _("Do you want to undefine 
this volume?"),
+                                                 &confirm) < 0) {
+                        vshError(ctl, _("\nError while geting response from 
user"));
+                        virStorageVolFree(volume);
+                        goto disk_error;
+                    }
+                }
+
+                /* removal of volume */
+                if (confirm) {
+                    if (virStorageVolDelete(volume, 0) == 0) {
+                        virStorageVolFree(volume);
+
+                        vshPrint(ctl, _("Volume deleted\n\n"));
+
+                        /* remove definition of volume from xml */
+                        xml_buf = xmlBufferCreate();
+                        if (!xml_buf) {
+                            vshPrint(ctl, _("Failed to create XML buffer. "
+                                            "If domain undefinition fails, 
domain will be left in inconsistent state.\n\n"));
+                            goto disk_next;
+                        }
+
+                        if (xmlNodeDump(xml_buf, xml, 
obj->nodesetval->nodeTab[i], 0, 0) < 0) {
+                            vshPrint(ctl, _("Failed to extract XML volume 
description. "
+                                            "If domain undefinition fails, 
domain will be left in inconsistent state.\n\n"));
+
+                            xmlBufferFree(xml_buf);
+                            xml_buf = NULL;
+                            goto disk_next;
+                        }
+
+                        if (virDomainDetachDeviceFlags(dom,
+                                                       (char *) 
xmlBufferContent(xml_buf),
+                                                       
VIR_DOMAIN_AFFECT_CONFIG) < 0) {
+                            vshPrint(ctl,
+                                     _("Failed to remove volume \"%s\" from 
configuration. "
+                                       "If domain undefinition fails, domain 
will be left in inconsistent state.\n\n"),
+                                     source);
+
+                            xmlBufferFree(xml_buf);
+                            xml_buf = NULL;
+                            goto disk_next;
+                        }
+
+                        xmlBufferFree(xml_buf);
+                        xml_buf = NULL;
+
+                    } else {
+                        virStorageVolFree(volume);
+
+                        vshError(ctl, _("Failed to delete volume."));
+                        goto disk_error;
+                    }
+                }
+            }
+
+disk_next:
+            VIR_FREE(source);
+            VIR_FREE(target);
+            VIR_FREE(type);
         }

+        xmlXPathFreeObject(obj);
+        xmlXPathFreeContext(ctxt);
+        xmlFreeDoc(xml);
+    } /* end of disk undefine stuff */
+
+    if (flags == -1) {
         rc = virDomainUndefine(dom);
     } else {
         rc = virDomainUndefineFlags(dom, flags);
@@ -1520,6 +1698,21 @@ end:

     virDomainFree(dom);
     return ret;
+
+disk_error:
+    VIR_FREE(source);
+    VIR_FREE(target);
+    VIR_FREE(type);
+
+    xmlXPathFreeObject(obj);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    xmlBufferFree(xml_buf);
+
+    virDomainFree(dom);
+
+    vshError(ctl, _("\nFailed to undefine domain %s"), name);
+    return false;
 }


@@ -14736,6 +14929,66 @@ vshReadline (vshControl *ctl, const char *prompt)

 #endif /* !USE_READLINE */

+
+/*
+ * Propmpt for boolean question (yes/no)
+ *
+ * Returns "true" on positive answer, "false" on negative
+ * -1 on error.
+ */
+static int
+vshInteractiveBoolPrompt(vshControl *ctl,
+                         const char *prompt,
+                         bool *confirm) {
+    const char *positive = _("yes");
+    const char *negative = _("no");
+    char *r;
+    char buff[100];
+    int ret = -1;
+    int len;
+    int c;
+
+    if (confirm == NULL)
+        return ret;
+
+    if (!ctl->imode)
+        return ret;
+
+    while (ret == -1) {
+        vshPrint(ctl, "%s (%s/%s)? ", prompt, positive, negative);
+
+        r = fgets(buff, sizeof(buff), stdin);
+        if (r == NULL)
+            break;
+        len = strlen(buff);
+        if (len > 0 && buff[len-1] == '\n')
+            buff[len-1] = '\0';
+        else
+            /* eat rest of line */
+            while ((c = fgetc(stdin) != EOF))
+                if (c == '\n')
+                    break;
+
+        /* compare */
+        if (STRCASEEQLEN(buff, positive, strlen(positive))) {
+            ret = 0;
+            *confirm = true;
+            break;
+        }
+
+        if (STRCASEEQLEN(buff, negative, strlen(negative))) {
+            ret = 0;
+            *confirm = false;
+            break;
+        }
+
+        /* errorneus response - warn and ask again*/
+        vshPrint(ctl, "\"%s\" %s\n", buff, _("Response not understood"));
+
+    }
+    return ret;
+}
+
 /*
  * Deinitialize virsh
  */
-- 
1.7.6

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

Reply via email to