diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index af0f856b78..faa68e86fd 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -32,6 +32,7 @@ #include "qemu_migration_params.h" #include "qemu_security.h" #include "qemu_extdevice.h" +#include "qemu_blockjob.h" #include "viralloc.h" #include "virlog.h" #include "virerror.h" @@ -2305,17 +2306,57 @@ qemuDomainObjPrivateXMLFormatAutomaticPlacement(virBufferPtr buf, } +static int +qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *data) +{ + VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + VIR_AUTOCLEAN(virBuffer) childBuf = VIR_BUFFER_INITIALIZER; + qemuBlockJobDataPtr job = payload; + virBufferPtr buf = data; + const char *state = qemuBlockjobStateTypeToString(job->state); + const char *newstate = NULL; + + if (job->newstate != -1) + newstate = qemuBlockjobStateTypeToString(job->newstate); + + virBufferSetChildIndent(&childBuf, buf); + + virBufferEscapeString(&attrBuf, " name='%s'", job->name); + virBufferEscapeString(&attrBuf, " type='%s'", qemuBlockjobTypeToString(job->type)); + virBufferEscapeString(&attrBuf, " state='%s'", state); + virBufferEscapeString(&attrBuf, " newstate='%s'", newstate); + virBufferEscapeString(&childBuf, "%s", job->errmsg); + + if (job->disk) + virBufferEscapeString(&childBuf, "\n", job->disk->dst); + + return virXMLFormatElement(buf, "blockjob", &attrBuf, &childBuf); +} + + static int qemuDomainObjPrivateXMLFormatBlockjobs(virBufferPtr buf, virDomainObjPtr vm) { - virBuffer attrBuf = VIR_BUFFER_INITIALIZER; + qemuDomainObjPrivatePtr priv = vm->privateData; + VIR_AUTOCLEAN(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + VIR_AUTOCLEAN(virBuffer) childBuf = VIR_BUFFER_INITIALIZER; bool bj = qemuDomainHasBlockjob(vm, false); virBufferAsprintf(&attrBuf, " active='%s'", virTristateBoolTypeToString(virTristateBoolFromBool(bj))); - return virXMLFormatElement(buf, "blockjobs", &attrBuf, NULL); + virBufferSetChildIndent(&childBuf, buf); + + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV) && + virHashForEach(priv->blockjobs, + qemuDomainObjPrivateXMLFormatBlockjobIterator, + &childBuf) < 0) + return -1; + + return virXMLFormatElement(buf, "blockjobs", &attrBuf, &childBuf); } @@ -2655,16 +2696,90 @@ qemuDomainObjPrivateXMLParseAutomaticPlacement(xmlXPathContextPtr ctxt, static int -qemuDomainObjPrivateXMLParseBlockjobs(qemuDomainObjPrivatePtr priv, +qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm, + xmlNodePtr node, + xmlXPathContextPtr ctxt) +{ + VIR_XPATH_NODE_AUTORESTORE(ctxt); + virDomainDiskDefPtr disk = NULL; + VIR_AUTOUNREF(qemuBlockJobDataPtr) job = NULL; + VIR_AUTOFREE(char *) name = NULL; + VIR_AUTOFREE(char *) typestr = NULL; + int type; + VIR_AUTOFREE(char *) statestr = NULL; + int state = QEMU_BLOCKJOB_STATE_FAILED; + VIR_AUTOFREE(char *) diskdst = NULL; + VIR_AUTOFREE(char *) newstatestr = NULL; + int newstate = -1; + bool invalidData = false; + + ctxt->node = node; + + if (!(name = virXPathString("string(./@name)", ctxt))) { + VIR_WARN("malformed block job data for vm '%s'", vm->def->name); + return 0; + } + + /* if the job name is known we need to register such a job so that we can + * clean it up */ + if (!(typestr = virXPathString("string(./@type)", ctxt)) || + (type = qemuBlockjobTypeFromString(typestr)) < 0) { + type = QEMU_BLOCKJOB_TYPE_NONE; + invalidData = true; + } + + if (!(job = qemuBlockJobDataNew(type, name))) + return -1; + + if (!(statestr = virXPathString("string(./@state)", ctxt)) || + (state = qemuBlockjobStateTypeFromString(statestr)) < 0) + invalidData = true; + + if ((newstatestr = virXPathString("string(./@newstate)", ctxt)) && + (newstate = qemuBlockjobStateTypeFromString(newstatestr)) < 0) + invalidData = true; + + if ((diskdst = virXPathString("string(./disk/@dst)", ctxt)) && + !(disk = virDomainDiskByName(vm->def, diskdst, false))) + invalidData = true; + + job->state = state; + job->newstate = newstate; + job->errmsg = virXPathString("string(./errmsg)", ctxt); + job->invalidData = invalidData; + + if (qemuBlockJobRegister(job, vm, disk) < 0) + return -1; + + return 0; +} + + +static int +qemuDomainObjPrivateXMLParseBlockjobs(virDomainObjPtr vm, + qemuDomainObjPrivatePtr priv, xmlXPathContextPtr ctxt) { + VIR_AUTOFREE(xmlNodePtr *) nodes = NULL; + ssize_t nnodes = 0; VIR_AUTOFREE(char *) active = NULL; int tmp; + size_t i; if ((active = virXPathString("string(./blockjobs/@active)", ctxt)) && (tmp = virTristateBoolTypeFromString(active)) > 0) priv->reconnectBlockjobs = tmp; + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) { + if ((nnodes = virXPathNodeSet("./blockjobs/blockjob", ctxt, &nodes)) < 0) + return -1; + + for (i = 0; i < nnodes; i++) { + if (qemuDomainObjPrivateXMLParseBlockjobData(vm, nodes[i], ctxt) < 0) + return -1; + } + } + return 0; } @@ -3022,7 +3137,7 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, qemuDomainObjPrivateXMLParsePR(ctxt, &priv->prDaemonRunning); - if (qemuDomainObjPrivateXMLParseBlockjobs(priv, ctxt) < 0) + if (qemuDomainObjPrivateXMLParseBlockjobs(vm, priv, ctxt) < 0) goto error; qemuDomainStorageIdReset(priv);