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

[libvirt] [PATCH 8/8] virsh: Resurrect vshCompleteXMLFromDomain



Signed-off-by: Michal Privoznik <mprivozn redhat com>
---
 tools/virsh-domain.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 151 insertions(+), 4 deletions(-)

diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 5075d0b..6a60a40 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -9357,7 +9357,6 @@ error:
  * @n2 second node
  * returns true in case n1 covers n2, false otherwise.
  */
-ATTRIBUTE_UNUSED
 static bool
 vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2)
 {
@@ -9465,6 +9464,139 @@ cleanup:
     return ret;
 }
 
+/**
+ * vshCompleteXMLFromDomain:
+ * @ctl vshControl for error messages printing
+ * @dom domain
+ * @oldXML device XML before
+ * @newXML and after completion
+ *
+ * For given domain and (probably incomplete) device XML specification try to
+ * find such device in domain and complete missing parts. This is however
+ * possible only when given device XML is sufficiently precise so it addresses
+ * only one device.
+ *
+ * Returns -2 when no such device exists in domain, -3 when given XML selects many
+ *          (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
+ *          is touched only in case of success.
+ */
+static int
+vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom,
+                         const char *oldXML, char **newXML)
+{
+    int funcRet = -1;
+    char *domXML = NULL, *normalizedXML = NULL;
+    xmlDocPtr domDoc = NULL, devDoc = NULL;
+    xmlNodePtr node = NULL;
+    xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
+    xmlNodePtr *devices = NULL;
+    xmlSaveCtxtPtr sctxt = NULL;
+    int devices_size;
+    char *xpath = NULL;
+    xmlBufferPtr buf = NULL;
+    size_t i = 0;
+    ssize_t indx = -1;
+
+    if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
+        vshError(ctl, _("couldn't get XML description of domain %s"),
+                 virDomainGetName(dom));
+        goto cleanup;
+    }
+
+    domDoc = virXMLParseStringCtxt(domXML, _("(domain_definition)"), &domCtxt);
+    if (!domDoc) {
+        vshError(ctl, _("Failed to parse domain definition xml"));
+        goto cleanup;
+    }
+
+    /* Prior parsing oldXML try to normalize it */
+    if (virDomainNormalizeXML(dom, oldXML, &normalizedXML, 0) >= 0) {
+        /* Yay, success! Deliberately not freeing oldXML here, as it's caller
+         * who is supposed to do so. */
+        oldXML = normalizedXML;
+    }
+
+    devDoc = virXMLParseStringCtxt(oldXML, _("(device_definition)"), &devCtxt);
+    if (!devDoc) {
+        vshError(ctl, _("Failed to parse device definition xml"));
+        goto cleanup;
+    }
+
+    node = xmlDocGetRootElement(devDoc);
+
+    buf = xmlBufferCreate();
+    if (!buf) {
+        vshError(ctl, "%s", _("out of memory"));
+        goto cleanup;
+    }
+
+    /* Get all possible devices */
+    if (virAsprintf(&xpath, "/domain/devices/%s", node->name) < 0)
+        goto cleanup;
+
+    devices_size = virXPathNodeSet(xpath, domCtxt, &devices);
+
+    if (devices_size < 0) {
+        /* error */
+        vshError(ctl, "%s", _("error when selecting nodes"));
+        goto cleanup;
+    } else if (devices_size == 0) {
+        /* no such device */
+        funcRet = -2;
+        goto cleanup;
+    }
+
+    /* and refine */
+    for (i = 0; i < devices_size; i++) {
+        if (vshNodeIsSuperset(devices[i], node)) {
+            if (indx >= 0) {
+                funcRet = -3; /* ambiguous */
+                goto cleanup;
+            }
+            indx = i;
+        }
+    }
+
+    if (indx < 0) {
+        funcRet = -2; /* no such device */
+        goto cleanup;
+    }
+
+    vshDebug(ctl, VSH_ERR_DEBUG, "Found device at pos %zd\n", indx);
+
+    if (newXML) {
+        sctxt = xmlSaveToBuffer(buf, NULL, 0);
+        if (!sctxt) {
+            vshError(ctl, "%s", _("failed to create document saving context"));
+            goto cleanup;
+        }
+
+        xmlSaveTree(sctxt, devices[indx]);
+        xmlSaveClose(sctxt);
+        *newXML = (char *) xmlBufferContent(buf);
+        if (!*newXML) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        buf->content = NULL;
+    }
+
+    vshDebug(ctl, VSH_ERR_DEBUG, "Old xml:\n%s\nNew xml:\n%s\n", oldXML,
+             newXML ? NULLSTR(*newXML) : "(null)");
+
+    funcRet = 0;
+
+cleanup:
+    xmlBufferFree(buf);
+    VIR_FREE(devices);
+    xmlXPathFreeContext(devCtxt);
+    xmlXPathFreeContext(domCtxt);
+    xmlFreeDoc(devDoc);
+    xmlFreeDoc(domDoc);
+    VIR_FREE(domXML);
+    VIR_FREE(xpath);
+    return funcRet;
+}
 
 /*
  * "normalize" command
@@ -9602,7 +9734,7 @@ cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
 {
     virDomainPtr dom = NULL;
     const char *from = NULL;
-    char *buffer = NULL;
+    char *buffer = NULL, *new_buffer = NULL;
     int ret;
     bool funcRet = false;
     bool current = vshCommandOptBool(cmd, "current");
@@ -9636,10 +9768,24 @@ cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
         goto cleanup;
     }
 
+    ret = vshCompleteXMLFromDomain(ctl, dom, buffer, &new_buffer);
+    if (ret < 0) {
+        if (ret == -2) {
+            vshError(ctl, _("no such device in %s"), virDomainGetName(dom));
+        } else if (ret == -3) {
+            vshError(ctl, "%s", _("given XML selects too many devices. "
+                                  "Please, be more specific"));
+        } else {
+            /* vshCompleteXMLFromDomain() already printed error message,
+             * so nothing to do here. */
+        }
+        goto cleanup;
+    }
+
     if (flags != 0)
-        ret = virDomainDetachDeviceFlags(dom, buffer, flags);
+        ret = virDomainDetachDeviceFlags(dom, new_buffer, flags);
     else
-        ret = virDomainDetachDevice(dom, buffer);
+        ret = virDomainDetachDevice(dom, new_buffer);
 
     if (ret < 0) {
         vshError(ctl, _("Failed to detach device from %s"), from);
@@ -9651,6 +9797,7 @@ cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
 
 cleanup:
     VIR_FREE(buffer);
+    VIR_FREE(new_buffer);
     virDomainFree(dom);
     return funcRet;
 }
-- 
1.8.1.5


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