mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-03 11:35:19 +00:00
qemu: blockjob: Track orphaned backing chains in blockjob status XML
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>
This commit is contained in:
parent
7a264536a6
commit
b3f8ad07dd
@ -72,6 +72,9 @@ qemuBlockJobDataDispose(void *obj)
|
|||||||
{
|
{
|
||||||
qemuBlockJobDataPtr job = obj;
|
qemuBlockJobDataPtr job = obj;
|
||||||
|
|
||||||
|
virObjectUnref(job->chain);
|
||||||
|
virObjectUnref(job->mirrorChain);
|
||||||
|
|
||||||
VIR_FREE(job->name);
|
VIR_FREE(job->name);
|
||||||
VIR_FREE(job->errmsg);
|
VIR_FREE(job->errmsg);
|
||||||
}
|
}
|
||||||
@ -127,6 +130,8 @@ qemuBlockJobRegister(qemuBlockJobDataPtr job,
|
|||||||
|
|
||||||
if (disk) {
|
if (disk) {
|
||||||
job->disk = disk;
|
job->disk = disk;
|
||||||
|
job->chain = virObjectRef(disk->src);
|
||||||
|
job->mirrorChain = virObjectRef(disk->mirror);
|
||||||
QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = virObjectRef(job);
|
QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = virObjectRef(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,8 @@ struct _qemuBlockJobData {
|
|||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
virDomainDiskDefPtr disk; /* may be NULL, if blockjob does not correspond to any disk */
|
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 type; /* qemuBlockJobType */
|
||||||
int state; /* qemuBlockjobState */
|
int state; /* qemuBlockjobState */
|
||||||
|
@ -2306,22 +2306,59 @@ qemuDomainObjPrivateXMLFormatAutomaticPlacement(virBufferPtr buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct qemuDomainPrivateBlockJobFormatData {
|
||||||
|
virDomainXMLOptionPtr xmlopt;
|
||||||
|
virBufferPtr buf;
|
||||||
|
} qemuDomainPrivateBlockJobFormatData;
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload,
|
qemuDomainObjPrivateXMLFormatBlockjobFormatChain(virBufferPtr buf,
|
||||||
const void *name ATTRIBUTE_UNUSED,
|
const char *chainname,
|
||||||
void *data)
|
virStorageSourcePtr src,
|
||||||
|
virDomainXMLOptionPtr xmlopt)
|
||||||
{
|
{
|
||||||
VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
||||||
VIR_AUTOCLEAN(virBuffer) childBuf = 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 *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;
|
qemuBlockJobDataPtr job = payload;
|
||||||
virBufferPtr buf = data;
|
|
||||||
const char *state = qemuBlockjobStateTypeToString(job->state);
|
const char *state = qemuBlockjobStateTypeToString(job->state);
|
||||||
const char *newstate = NULL;
|
const char *newstate = NULL;
|
||||||
|
struct qemuDomainPrivateBlockJobFormatData *data = opaque;
|
||||||
|
|
||||||
if (job->newstate != -1)
|
if (job->newstate != -1)
|
||||||
newstate = qemuBlockjobStateTypeToString(job->newstate);
|
newstate = qemuBlockjobStateTypeToString(job->newstate);
|
||||||
|
|
||||||
virBufferSetChildIndent(&childBuf, buf);
|
virBufferSetChildIndent(&childBuf, data->buf);
|
||||||
|
virBufferSetChildIndent(&chainsBuf, &childBuf);
|
||||||
|
|
||||||
virBufferEscapeString(&attrBuf, " name='%s'", job->name);
|
virBufferEscapeString(&attrBuf, " name='%s'", job->name);
|
||||||
virBufferEscapeString(&attrBuf, " type='%s'", qemuBlockjobTypeToString(job->type));
|
virBufferEscapeString(&attrBuf, " type='%s'", qemuBlockjobTypeToString(job->type));
|
||||||
@ -2329,10 +2366,28 @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload,
|
|||||||
virBufferEscapeString(&attrBuf, " newstate='%s'", newstate);
|
virBufferEscapeString(&attrBuf, " newstate='%s'", newstate);
|
||||||
virBufferEscapeString(&childBuf, "<errmsg>%s</errmsg>", job->errmsg);
|
virBufferEscapeString(&childBuf, "<errmsg>%s</errmsg>", job->errmsg);
|
||||||
|
|
||||||
if (job->disk)
|
if (job->disk) {
|
||||||
virBufferEscapeString(&childBuf, "<disk dst='%s'/>\n", job->disk->dst);
|
virBufferEscapeString(&childBuf, "<disk dst='%s'/>\n", job->disk->dst);
|
||||||
|
} else {
|
||||||
|
if (job->chain &&
|
||||||
|
qemuDomainObjPrivateXMLFormatBlockjobFormatChain(&chainsBuf,
|
||||||
|
"disk",
|
||||||
|
job->chain,
|
||||||
|
data->xmlopt) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
return virXMLFormatElement(buf, "blockjob", &attrBuf, &childBuf);
|
if (job->mirrorChain &&
|
||||||
|
qemuDomainObjPrivateXMLFormatBlockjobFormatChain(&chainsBuf,
|
||||||
|
"mirror",
|
||||||
|
job->mirrorChain,
|
||||||
|
data->xmlopt) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (virXMLFormatElement(&childBuf, "chains", NULL, &chainsBuf) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return virXMLFormatElement(data->buf, "blockjob", &attrBuf, &childBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2344,6 +2399,8 @@ qemuDomainObjPrivateXMLFormatBlockjobs(virBufferPtr buf,
|
|||||||
VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
||||||
VIR_AUTOCLEAN(virBuffer) childBuf = VIR_BUFFER_INITIALIZER;
|
VIR_AUTOCLEAN(virBuffer) childBuf = VIR_BUFFER_INITIALIZER;
|
||||||
bool bj = qemuDomainHasBlockjob(vm, false);
|
bool bj = qemuDomainHasBlockjob(vm, false);
|
||||||
|
struct qemuDomainPrivateBlockJobFormatData iterdata = { priv->driver->xmlopt,
|
||||||
|
&childBuf };
|
||||||
|
|
||||||
virBufferAsprintf(&attrBuf, " active='%s'",
|
virBufferAsprintf(&attrBuf, " active='%s'",
|
||||||
virTristateBoolTypeToString(virTristateBoolFromBool(bj)));
|
virTristateBoolTypeToString(virTristateBoolFromBool(bj)));
|
||||||
@ -2353,7 +2410,7 @@ qemuDomainObjPrivateXMLFormatBlockjobs(virBufferPtr buf,
|
|||||||
if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV) &&
|
if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV) &&
|
||||||
virHashForEach(priv->blockjobs,
|
virHashForEach(priv->blockjobs,
|
||||||
qemuDomainObjPrivateXMLFormatBlockjobIterator,
|
qemuDomainObjPrivateXMLFormatBlockjobIterator,
|
||||||
&childBuf) < 0)
|
&iterdata) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return virXMLFormatElement(buf, "blockjobs", &attrBuf, &childBuf);
|
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
|
static int
|
||||||
qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm,
|
qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm,
|
||||||
xmlNodePtr node,
|
xmlNodePtr node,
|
||||||
xmlXPathContextPtr ctxt)
|
xmlXPathContextPtr ctxt,
|
||||||
|
virDomainXMLOptionPtr xmlopt)
|
||||||
{
|
{
|
||||||
VIR_XPATH_NODE_AUTORESTORE(ctxt);
|
VIR_XPATH_NODE_AUTORESTORE(ctxt);
|
||||||
virDomainDiskDefPtr disk = NULL;
|
virDomainDiskDefPtr disk = NULL;
|
||||||
@ -2712,6 +2808,7 @@ qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm,
|
|||||||
VIR_AUTOFREE(char *) newstatestr = NULL;
|
VIR_AUTOFREE(char *) newstatestr = NULL;
|
||||||
int newstate = -1;
|
int newstate = -1;
|
||||||
bool invalidData = false;
|
bool invalidData = false;
|
||||||
|
xmlNodePtr tmp;
|
||||||
|
|
||||||
ctxt->node = node;
|
ctxt->node = node;
|
||||||
|
|
||||||
@ -2743,6 +2840,16 @@ qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm,
|
|||||||
!(disk = virDomainDiskByName(vm->def, diskdst, false)))
|
!(disk = virDomainDiskByName(vm->def, diskdst, false)))
|
||||||
invalidData = true;
|
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->state = state;
|
||||||
job->newstate = newstate;
|
job->newstate = newstate;
|
||||||
job->errmsg = virXPathString("string(./errmsg)", ctxt);
|
job->errmsg = virXPathString("string(./errmsg)", ctxt);
|
||||||
@ -2775,7 +2882,8 @@ qemuDomainObjPrivateXMLParseBlockjobs(virDomainObjPtr vm,
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (i = 0; i < nnodes; i++) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,6 +237,43 @@
|
|||||||
<blockjob name='drive-virtio-disk0' type='copy' state='ready'>
|
<blockjob name='drive-virtio-disk0' type='copy' state='ready'>
|
||||||
<disk dst='vda'/>
|
<disk dst='vda'/>
|
||||||
</blockjob>
|
</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>
|
</blockjobs>
|
||||||
<domain type='kvm' id='4'>
|
<domain type='kvm' id='4'>
|
||||||
<name>copy</name>
|
<name>copy</name>
|
||||||
|
Loading…
Reference in New Issue
Block a user