]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
qemu: blockjob: Track orphaned backing chains in blockjob status XML
authorPeter Krempa <pkrempa@redhat.com>
Tue, 19 Mar 2019 06:54:12 +0000 (07:54 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Thu, 18 Jul 2019 15:59:34 +0000 (17:59 +0200)
When the guest unplugs the disk frontend libvirt is responsible for
deleting the backend. Since a blockjob may still have a reference to the
backing chain when it is running we'll have to store the metadata for
the unplugged disk for future reference.

This patch adds 'chain' and 'mirrorChain' fields to 'qemuBlockJobData'
to keep them around with the job along with status XML machinery and
tests. Later patches will then add code to change the ownership of the
chain when unplugging the disk backend.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
src/qemu/qemu_blockjob.c
src/qemu/qemu_blockjob.h
src/qemu/qemu_domain.c
tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml

index 722074bb8d6f8747e24e03985f2ad90b75d28c39..d55eb4860573b144cd370b6d0f7a8ab2b7d3d0ac 100644 (file)
@@ -72,6 +72,9 @@ qemuBlockJobDataDispose(void *obj)
 {
     qemuBlockJobDataPtr job = obj;
 
+    virObjectUnref(job->chain);
+    virObjectUnref(job->mirrorChain);
+
     VIR_FREE(job->name);
     VIR_FREE(job->errmsg);
 }
@@ -127,6 +130,8 @@ qemuBlockJobRegister(qemuBlockJobDataPtr job,
 
     if (disk) {
         job->disk = disk;
+        job->chain = virObjectRef(disk->src);
+        job->mirrorChain = virObjectRef(disk->mirror);
         QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = virObjectRef(job);
     }
 
index 2d8ecdd4c3a0feb5a98545320e0abe8861efb627..d07ab75c8bd54513ed1eab6f365d78b09d0ae02a 100644 (file)
@@ -75,6 +75,8 @@ struct _qemuBlockJobData {
     char *name;
 
     virDomainDiskDefPtr disk; /* may be NULL, if blockjob does not correspond to any disk */
+    virStorageSourcePtr chain; /* Reference to the chain the job operates on. */
+    virStorageSourcePtr mirrorChain; /* reference to 'mirror' part of the job */
 
     int type; /* qemuBlockJobType */
     int state; /* qemuBlockjobState */
index 1a0e6aac6ee0eff99fd54cf61b683d1e65eea311..fc8deb70842befdfc4a20109ededff91aff4c162 100644 (file)
@@ -2306,22 +2306,59 @@ qemuDomainObjPrivateXMLFormatAutomaticPlacement(virBufferPtr buf,
 }
 
 
+typedef struct qemuDomainPrivateBlockJobFormatData {
+    virDomainXMLOptionPtr xmlopt;
+    virBufferPtr buf;
+} qemuDomainPrivateBlockJobFormatData;
+
+
+static int
+qemuDomainObjPrivateXMLFormatBlockjobFormatChain(virBufferPtr buf,
+                                                 const char *chainname,
+                                                 virStorageSourcePtr src,
+                                                 virDomainXMLOptionPtr xmlopt)
+{
+    VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
+    VIR_AUTOCLEAN(virBuffer) childBuf = VIR_BUFFER_INITIALIZER;
+    unsigned int xmlflags = VIR_DOMAIN_DEF_FORMAT_STATUS;
+
+    virBufferSetChildIndent(&childBuf, buf);
+
+    virBufferAsprintf(&attrBuf, " type='%s' format='%s'",
+                      virStorageTypeToString(src->type),
+                      virStorageFileFormatTypeToString(src->format));
+
+    if (virDomainDiskSourceFormat(&childBuf, src, "source", 0, true, xmlflags, xmlopt) < 0)
+        return -1;
+
+    if (virDomainDiskBackingStoreFormat(&childBuf, src, xmlopt, xmlflags) < 0)
+        return -1;
+
+    if (virXMLFormatElement(buf, chainname, &attrBuf, &childBuf) < 0)
+        return -1;
+
+    return 0;
+}
+
+
 static int
 qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload,
                                               const void *name ATTRIBUTE_UNUSED,
-                                              void *data)
+                                              void *opaque)
 {
     VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
     VIR_AUTOCLEAN(virBuffer) childBuf = VIR_BUFFER_INITIALIZER;
+    VIR_AUTOCLEAN(virBuffer) chainsBuf = VIR_BUFFER_INITIALIZER;
     qemuBlockJobDataPtr job = payload;
-    virBufferPtr buf = data;
     const char *state = qemuBlockjobStateTypeToString(job->state);
     const char *newstate = NULL;
+    struct qemuDomainPrivateBlockJobFormatData *data = opaque;
 
     if (job->newstate != -1)
         newstate = qemuBlockjobStateTypeToString(job->newstate);
 
-    virBufferSetChildIndent(&childBuf, buf);
+    virBufferSetChildIndent(&childBuf, data->buf);
+    virBufferSetChildIndent(&chainsBuf, &childBuf);
 
     virBufferEscapeString(&attrBuf, " name='%s'", job->name);
     virBufferEscapeString(&attrBuf, " type='%s'", qemuBlockjobTypeToString(job->type));
@@ -2329,10 +2366,28 @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload,
     virBufferEscapeString(&attrBuf, " newstate='%s'", newstate);
     virBufferEscapeString(&childBuf, "<errmsg>%s</errmsg>", job->errmsg);
 
-    if (job->disk)
+    if (job->disk) {
         virBufferEscapeString(&childBuf, "<disk dst='%s'/>\n", job->disk->dst);
+    } else {
+        if (job->chain &&
+            qemuDomainObjPrivateXMLFormatBlockjobFormatChain(&chainsBuf,
+                                                             "disk",
+                                                             job->chain,
+                                                             data->xmlopt) < 0)
+            return -1;
+
+        if (job->mirrorChain &&
+            qemuDomainObjPrivateXMLFormatBlockjobFormatChain(&chainsBuf,
+                                                             "mirror",
+                                                             job->mirrorChain,
+                                                             data->xmlopt) < 0)
+            return -1;
+
+        if (virXMLFormatElement(&childBuf, "chains", NULL, &chainsBuf) < 0)
+            return -1;
+    }
 
-    return virXMLFormatElement(buf, "blockjob", &attrBuf, &childBuf);
+    return virXMLFormatElement(data->buf, "blockjob", &attrBuf, &childBuf);
 }
 
 
@@ -2344,6 +2399,8 @@ qemuDomainObjPrivateXMLFormatBlockjobs(virBufferPtr buf,
     VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
     VIR_AUTOCLEAN(virBuffer) childBuf = VIR_BUFFER_INITIALIZER;
     bool bj = qemuDomainHasBlockjob(vm, false);
+    struct qemuDomainPrivateBlockJobFormatData iterdata = { priv->driver->xmlopt,
+                                                            &childBuf };
 
     virBufferAsprintf(&attrBuf, " active='%s'",
                       virTristateBoolTypeToString(virTristateBoolFromBool(bj)));
@@ -2353,7 +2410,7 @@ qemuDomainObjPrivateXMLFormatBlockjobs(virBufferPtr buf,
     if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV) &&
         virHashForEach(priv->blockjobs,
                        qemuDomainObjPrivateXMLFormatBlockjobIterator,
-                       &childBuf) < 0)
+                       &iterdata) < 0)
         return -1;
 
     return virXMLFormatElement(buf, "blockjobs", &attrBuf, &childBuf);
@@ -2695,10 +2752,49 @@ qemuDomainObjPrivateXMLParseAutomaticPlacement(xmlXPathContextPtr ctxt,
 }
 
 
+static virStorageSourcePtr
+qemuDomainObjPrivateXMLParseBlockjobChain(xmlNodePtr node,
+                                          xmlXPathContextPtr ctxt,
+                                          virDomainXMLOptionPtr xmlopt)
+
+{
+    VIR_XPATH_NODE_AUTORESTORE(ctxt);
+    VIR_AUTOFREE(char *) format = NULL;
+    VIR_AUTOFREE(char *) type = NULL;
+    VIR_AUTOFREE(char *) index = NULL;
+    VIR_AUTOUNREF(virStorageSourcePtr) src = NULL;
+    xmlNodePtr sourceNode;
+    unsigned int xmlflags = VIR_DOMAIN_DEF_PARSE_STATUS;
+
+    ctxt->node = node;
+
+    if (!(type = virXMLPropString(ctxt->node, "type")) ||
+        !(format = virXMLPropString(ctxt->node, "format")) ||
+        !(index = virXPathString("string(./source/@index)", ctxt)) ||
+        !(sourceNode = virXPathNode("./source", ctxt))) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("missing job chain data"));
+        return NULL;
+    }
+
+    if (!(src = virDomainStorageSourceParseBase(type, format, index)))
+        return NULL;
+
+    if (virDomainStorageSourceParse(sourceNode, ctxt, src, xmlflags, xmlopt) < 0)
+        return NULL;
+
+    if (virDomainDiskBackingStoreParse(ctxt, src, xmlflags, xmlopt) < 0)
+        return NULL;
+
+    VIR_RETURN_PTR(src);
+}
+
+
 static int
 qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm,
                                          xmlNodePtr node,
-                                         xmlXPathContextPtr ctxt)
+                                         xmlXPathContextPtr ctxt,
+                                         virDomainXMLOptionPtr xmlopt)
 {
     VIR_XPATH_NODE_AUTORESTORE(ctxt);
     virDomainDiskDefPtr disk = NULL;
@@ -2712,6 +2808,7 @@ qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm,
     VIR_AUTOFREE(char *) newstatestr = NULL;
     int newstate = -1;
     bool invalidData = false;
+    xmlNodePtr tmp;
 
     ctxt->node = node;
 
@@ -2743,6 +2840,16 @@ qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm,
         !(disk = virDomainDiskByName(vm->def, diskdst, false)))
         invalidData = true;
 
+    if (!disk && !invalidData) {
+        if ((tmp = virXPathNode("./chains/disk", ctxt)) &&
+            !(job->chain = qemuDomainObjPrivateXMLParseBlockjobChain(tmp, ctxt, xmlopt)))
+            invalidData = true;
+
+        if ((tmp = virXPathNode("./chains/mirror", ctxt)) &&
+            !(job->mirrorChain = qemuDomainObjPrivateXMLParseBlockjobChain(tmp, ctxt, xmlopt)))
+            invalidData = true;
+    }
+
     job->state = state;
     job->newstate = newstate;
     job->errmsg = virXPathString("string(./errmsg)", ctxt);
@@ -2775,7 +2882,8 @@ qemuDomainObjPrivateXMLParseBlockjobs(virDomainObjPtr vm,
             return -1;
 
         for (i = 0; i < nnodes; i++) {
-            if (qemuDomainObjPrivateXMLParseBlockjobData(vm, nodes[i], ctxt) < 0)
+            if (qemuDomainObjPrivateXMLParseBlockjobData(vm, nodes[i], ctxt,
+                                                         priv->driver->xmlopt) < 0)
                 return -1;
         }
     }
index dce9f4446622043618745786a2c97ab70dd9e574..43fe02137b11e36c5376a9b7e527bb277b26634a 100644 (file)
     <blockjob name='drive-virtio-disk0' type='copy' state='ready'>
       <disk dst='vda'/>
     </blockjob>
+    <blockjob name='test-orphan-job0' type='copy' state='ready'>
+      <chains>
+        <disk type='file' format='qcow2'>
+          <source file='/orphan/source/top.qcow2' index='123'>
+            <privateData>
+              <nodenames>
+                <nodename type='storage' name='orphan-source-top-storage'/>
+                <nodename type='format' name='orphan-source-top-format'/>
+              </nodenames>
+            </privateData>
+          </source>
+          <backingStore type='file' index='321'>
+            <format type='qcow2'/>
+            <source file='/orphan/source/base.qcow2'>
+              <privateData>
+                <nodenames>
+                  <nodename type='storage' name='orphan-source-base-storage'/>
+                  <nodename type='format' name='orphan-source-base-format'/>
+                </nodenames>
+              </privateData>
+            </source>
+            <backingStore/>
+          </backingStore>
+        </disk>
+        <mirror type='file' format='raw'>
+          <source file='/orphan/mirror/raw' index='42'>
+            <privateData>
+              <nodenames>
+                <nodename type='storage' name='orphan-mirror-raw-storage'/>
+                <nodename type='format' name='orphan-mirror-raw-format'/>
+              </nodenames>
+            </privateData>
+          </source>
+          <backingStore/>
+        </mirror>
+      </chains>
+    </blockjob>
   </blockjobs>
   <domain type='kvm' id='4'>
     <name>copy</name>