From nobody Sun Feb 8 20:12:41 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1565280206; cv=none; d=zoho.com; s=zohoarc; b=JcZqEMTxzCwuqmZdDTpq/JdXFGa3lOSHrzSHVuuC8/fAZ+geLFyms6X9QsKpH2oSdR4IjZ+0PTDDdXJ3XXltY6XOEEuJXNnhfPIQRav8EWL2tIFV95VMZohFyhCdQYJIL/NOIJcsRqrl+M9A+4mGYaJ0K/1FEqq+ri+M7G2rgBs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1565280206; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=UYTqszyeXPEudkVMKxxilQ664Gs8sQJk8GUacgML9u0=; b=ft/b5ne8/pyhIDF2MqoQd2lJeogzQ56U3vAS6SnKtZ6r+lcMuGFdHZbfGYpRtRSlXkA1JbM/0PZiM6ECkooN3eYVMvJHRIvfDd2LhOkATYpRpq9ED6BVd4ghu66U0m2kIiFjVH7r6EqyRUfgcthpX+EiA5ICqPvZfAo1R9iwLEs= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1565280206197943.382815198477; Thu, 8 Aug 2019 09:03:26 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 04FD630A922D; Thu, 8 Aug 2019 16:03:25 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D16695D772; Thu, 8 Aug 2019 16:03:24 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 7FF6918005C7; Thu, 8 Aug 2019 16:03:24 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x78G0sXH005442 for ; Thu, 8 Aug 2019 12:00:54 -0400 Received: by smtp.corp.redhat.com (Postfix) id 93CBB165D9; Thu, 8 Aug 2019 16:00:54 +0000 (UTC) Received: from angien.brq.redhat.com (unknown [10.43.2.229]) by smtp.corp.redhat.com (Postfix) with ESMTP id ECF3216BE2 for ; Thu, 8 Aug 2019 16:00:53 +0000 (UTC) From: Peter Krempa To: libvir-list@redhat.com Date: Thu, 8 Aug 2019 18:00:40 +0200 Message-Id: <43fa65cd8fbc232f4076bb2d770af96147630452.1565279921.git.pkrempa@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH 10/12] qemu: Introduce code for blockdev-create X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.44]); Thu, 08 Aug 2019 16:03:25 +0000 (UTC) Content-Type: text/plain; charset="utf-8" QEMU finally exposes an interface which allows us to instruct it to format or create arbitrary images. This is required for blockdev integration of block copy and snapshots as we need to pre-format images prior to use with blockdev-add. This path introduces job handling and also helpers for formatting and attaching a whole image described by a virStorageSource. Signed-off-by: Peter Krempa Reviewed-by: J=C3=A1n Tomko --- src/qemu/qemu_block.c | 250 ++++++++++++++++++ src/qemu/qemu_block.h | 14 + src/qemu/qemu_blockjob.c | 88 +++++- src/qemu/qemu_blockjob.h | 17 ++ src/qemu/qemu_domain.c | 34 ++- src/qemu/qemu_driver.c | 1 + .../blockjob-blockdev-in.xml | 45 ++++ 7 files changed, 446 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 47661fb8bd..cb9a085e5d 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -2379,3 +2379,253 @@ qemuBlockStorageSourceCreateGetStorageProps(virStor= ageSourcePtr src, return 0; } + + +static int +qemuBlockStorageSourceCreateGeneric(virDomainObjPtr vm, + virJSONValuePtr createProps, + virStorageSourcePtr src, + virStorageSourcePtr chain, + bool storageCreate, + qemuDomainAsyncJob asyncJob) +{ + VIR_AUTOPTR(virJSONValue) props =3D createProps; + qemuDomainObjPrivatePtr priv =3D vm->privateData; + qemuBlockJobDataPtr job =3D NULL; + int ret =3D -1; + int rc; + + if (!(job =3D qemuBlockJobNewCreate(vm, src, chain, storageCreate))) + return -1; + + qemuBlockJobSyncBegin(job); + + if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) + goto cleanup; + + rc =3D qemuMonitorBlockdevCreate(priv->mon, job->name, props); + props =3D NULL; + + if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0) + goto cleanup; + + qemuBlockJobStarted(job, vm); + + qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE); + while (qemuBlockJobIsRunning(job)) { + if (virDomainObjWait(vm) < 0) + goto cleanup; + qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE); + } + + if (job->state =3D=3D QEMU_BLOCKJOB_STATE_FAILED || + job->state =3D=3D QEMU_BLOCKJOB_STATE_CANCELLED) { + if (job->state =3D=3D QEMU_BLOCKJOB_STATE_CANCELLED && !job->errms= g) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("blockdev-create job was cancelled")); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("failed to format image: '%s'"), NULLSTR(job-= >errmsg)); + } + goto cleanup; + } + + ret =3D 0; + + cleanup: + qemuBlockJobStartupFinalize(vm, job); + return ret; +} + + +static int +qemuBlockStorageSourceCreateStorage(virDomainObjPtr vm, + virStorageSourcePtr src, + virStorageSourcePtr chain, + qemuDomainAsyncJob asyncJob) +{ + int actualType =3D virStorageSourceGetActualType(src); + VIR_AUTOPTR(virJSONValue) createstorageprops =3D NULL; + int ret; + + /* we need to do stuff only for remote storage and local raw files */ + if (actualType !=3D VIR_STORAGE_TYPE_NETWORK && + !(actualType =3D=3D VIR_STORAGE_TYPE_FILE && src->format =3D=3D VI= R_STORAGE_FILE_RAW)) + return 0; + + if (qemuBlockStorageSourceCreateGetStorageProps(src, &createstoragepro= ps) < 0) + return -1; + + if (!createstorageprops) { + /* we can always try opening it to see whether it was existing */ + return 0; + } + + ret =3D qemuBlockStorageSourceCreateGeneric(vm, createstorageprops, sr= c, chain, + true, asyncJob); + createstorageprops =3D NULL; + + return ret; +} + + +static int +qemuBlockStorageSourceCreateFormat(virDomainObjPtr vm, + virStorageSourcePtr src, + virStorageSourcePtr backingStore, + virStorageSourcePtr chain, + qemuDomainAsyncJob asyncJob) +{ + VIR_AUTOPTR(virJSONValue) createformatprops =3D NULL; + int ret; + + if (src->format =3D=3D VIR_STORAGE_FILE_RAW) + return 0; + + if (qemuBlockStorageSourceCreateGetFormatProps(src, backingStore, + &createformatprops) < 0) + return -1; + + if (!createformatprops) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("can't create storage format '%s'"), + virStorageFileFormatTypeToString(src->format)); + return -1; + } + + ret =3D qemuBlockStorageSourceCreateGeneric(vm, createformatprops, src= , chain, + false, asyncJob); + createformatprops =3D NULL; + + return ret; +} + + +/** + * qemuBlockStorageSourceCreate: + * @vm: domain object + * @src: storage source definition to create + * @backingStore: backingStore of the new image (used only in image metada= ta) + * @chain: backing chain to unplug in case of a long-running job failure + * @data: qemuBlockStorageSourceAttachData for @src so that it can be atta= ched + * @asyncJob: qemu asynchronous job type + * + * Creates and formats a storage volume according to @src and attaches it = to @vm. + * @data must provide attachment data as if @src was existing. @src is att= ached + * after successful return of this function. If libvirtd is restarted duri= ng + * the create job @chain is unplugged, otherwise it's left for the caller. + * If @backingStore is provided, the new image will refer to it as it's ba= cking + * store. + */ +int +qemuBlockStorageSourceCreate(virDomainObjPtr vm, + virStorageSourcePtr src, + virStorageSourcePtr backingStore, + virStorageSourcePtr chain, + qemuBlockStorageSourceAttachDataPtr data, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv =3D vm->privateData; + int ret =3D -1; + int rc; + + if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) + goto cleanup; + + rc =3D qemuBlockStorageSourceAttachApplyStorageDeps(priv->mon, data); + + if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0) + goto cleanup; + + if (qemuBlockStorageSourceCreateStorage(vm, src, chain, asyncJob) < 0) + goto cleanup; + + if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) + goto cleanup; + + rc =3D qemuBlockStorageSourceAttachApplyStorage(priv->mon, data); + + if (rc =3D=3D 0) + rc =3D qemuBlockStorageSourceAttachApplyFormatDeps(priv->mon, data= ); + + if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0) + goto cleanup; + + if (qemuBlockStorageSourceCreateFormat(vm, src, backingStore, chain, + asyncJob) < 0) + goto cleanup; + + if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) + goto cleanup; + + rc =3D qemuBlockStorageSourceAttachApplyFormat(priv->mon, data); + + if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0) + goto cleanup; + + ret =3D 0; + + cleanup: + if (ret < 0 && + virDomainObjIsActive(vm) && + qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) =3D=3D = 0) { + + qemuBlockStorageSourceAttachRollback(priv->mon, data); + ignore_value(qemuDomainObjExitMonitor(priv->driver, vm)); + } + + return ret; +} + + +/** + * qemuBlockStorageSourceCreateDetectSize: + * @vm: domain object + * @src: storage source to update size/capacity on + * @templ: storage source template + * @asyncJob: qemu asynchronous job type + * + * When creating a storage source via blockdev-create we need to know the = size + * and capacity of the original volume (e.g. when creating a snapshot or c= opy). + * This function updates @src's 'capacity' and 'physical' attributes accor= ding + * to the detected sizes from @templ. + */ +int +qemuBlockStorageSourceCreateDetectSize(virDomainObjPtr vm, + virStorageSourcePtr src, + virStorageSourcePtr templ, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv =3D vm->privateData; + VIR_AUTOPTR(virHashTable) stats =3D NULL; + qemuBlockStatsPtr entry; + int rc; + + if (!(stats =3D virHashCreate(10, virHashValueFree))) + return -1; + + if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) + return -1; + + rc =3D qemuMonitorBlockStatsUpdateCapacityBlockdev(priv->mon, stats); + + if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0) + return -1; + + if (!(entry =3D virHashLookup(stats, templ->nodeformat))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to update capacity data for block node '%= s'"), + templ->nodeformat); + return -1; + } + + if (src->format =3D=3D VIR_STORAGE_FILE_RAW) { + src->physical =3D entry->capacity; + } else { + src->physical =3D entry->physical; + } + + src->capacity =3D entry->capacity; + + return 0; +} diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h index ab6b9dc791..a5e970fa1e 100644 --- a/src/qemu/qemu_block.h +++ b/src/qemu/qemu_block.h @@ -182,3 +182,17 @@ int qemuBlockStorageSourceCreateGetStorageProps(virStorageSourcePtr src, virJSONValuePtr *props) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; + +int +qemuBlockStorageSourceCreate(virDomainObjPtr vm, + virStorageSourcePtr src, + virStorageSourcePtr backingStore, + virStorageSourcePtr chain, + qemuBlockStorageSourceAttachDataPtr data, + qemuDomainAsyncJob asyncJob); + +int +qemuBlockStorageSourceCreateDetectSize(virDomainObjPtr vm, + virStorageSourcePtr src, + virStorageSourcePtr templ, + qemuDomainAsyncJob asyncJob); diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 6ac60e86d7..70550d17e7 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -65,7 +65,8 @@ VIR_ENUM_IMPL(qemuBlockjob, "copy", "commit", "active-commit", - ""); + "", + "create"); static virClassPtr qemuBlockJobDataClass; @@ -78,6 +79,9 @@ qemuBlockJobDataDispose(void *obj) virObjectUnref(job->chain); virObjectUnref(job->mirrorChain); + if (job->type =3D=3D QEMU_BLOCKJOB_TYPE_CREATE) + virObjectUnref(job->data.create.src); + VIR_FREE(job->name); VIR_FREE(job->errmsg); } @@ -274,6 +278,37 @@ qemuBlockJobDiskNewCommit(virDomainObjPtr vm, } +qemuBlockJobDataPtr +qemuBlockJobNewCreate(virDomainObjPtr vm, + virStorageSourcePtr src, + virStorageSourcePtr chain, + bool storage) +{ + VIR_AUTOUNREF(qemuBlockJobDataPtr) job =3D NULL; + VIR_AUTOFREE(char *) jobname =3D NULL; + const char *nodename =3D src->nodeformat; + + if (storage) + nodename =3D src->nodestorage; + + if (virAsprintf(&jobname, "create-%s", nodename) < 0) + return NULL; + + if (!(job =3D qemuBlockJobDataNew(QEMU_BLOCKJOB_TYPE_CREATE, jobname))) + return NULL; + + if (virStorageSourceIsBacking(chain)) + job->chain =3D virObjectRef(chain); + + job->data.create.src =3D virObjectRef(src); + + if (qemuBlockJobRegister(job, vm, NULL, true) < 0) + return NULL; + + VIR_RETURN_PTR(job); +} + + /** * qemuBlockJobDiskGetJob: * @disk: disk definition @@ -1007,6 +1042,49 @@ qemuBlockJobProcessEventCompletedActiveCommit(virQEM= UDriverPtr driver, job->disk->mirror =3D NULL; } + +static void +qemuBlockJobProcessEventConcludedCreate(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuBlockJobDataPtr job, + qemuDomainAsyncJob asyncJob) +{ + VIR_AUTOPTR(qemuBlockStorageSourceAttachData) backend =3D NULL; + + /* if there is a synchronous client waiting for this job that means th= at + * it will handle further hotplug of the created volume and also that + * the 'chain' which was registered is under their control */ + if (job->synchronous) { + virObjectUnref(job->chain); + job->chain =3D NULL; + return; + } + + if (!job->data.create.src) + return; + + if (!(backend =3D qemuBlockStorageSourceDetachPrepare(job->data.create= .src, NULL))) + return; + + /* the format node part was not attached yet, so we don't need to deta= ch it */ + backend->formatAttached =3D false; + if (job->data.create.storage) { + backend->storageAttached =3D false; + VIR_FREE(backend->encryptsecretAlias); + } + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return; + + qemuBlockStorageSourceAttachRollback(qemuDomainGetMonitor(vm), backend= ); + + if (qemuDomainObjExitMonitor(driver, vm) < 0) + return; + + qemuDomainStorageSourceAccessRevoke(driver, vm, job->data.create.src); +} + + static void qemuBlockJobEventProcessConcludedTransition(qemuBlockJobDataPtr job, virQEMUDriverPtr driver, @@ -1028,6 +1106,10 @@ qemuBlockJobEventProcessConcludedTransition(qemuBloc= kJobDataPtr job, qemuBlockJobProcessEventCompletedActiveCommit(driver, vm, job,= asyncJob); break; + case QEMU_BLOCKJOB_TYPE_CREATE: + qemuBlockJobProcessEventConcludedCreate(driver, vm, job, async= Job); + break; + case QEMU_BLOCKJOB_TYPE_COPY: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: @@ -1051,6 +1133,10 @@ qemuBlockJobEventProcessConcludedTransition(qemuBloc= kJobDataPtr job, } break; + case QEMU_BLOCKJOB_TYPE_CREATE: + qemuBlockJobProcessEventConcludedCreate(driver, vm, job, async= Job); + break; + case QEMU_BLOCKJOB_TYPE_COPY: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 5b740db5a8..ff3c4a9eb7 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -62,6 +62,7 @@ typedef enum { QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT =3D VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_= COMMIT, /* Additional enum values local to qemu */ QEMU_BLOCKJOB_TYPE_INTERNAL, + QEMU_BLOCKJOB_TYPE_CREATE, QEMU_BLOCKJOB_TYPE_LAST } qemuBlockJobType; verify((int)QEMU_BLOCKJOB_TYPE_INTERNAL =3D=3D VIR_DOMAIN_BLOCK_JOB_TYPE_L= AST); @@ -87,6 +88,15 @@ struct _qemuBlockJobCommitData { }; +typedef struct _qemuBlockJobCreateData qemuBlockJobCreateData; +typedef qemuBlockJobCreateData *qemuBlockJobDataCreatePtr; + +struct _qemuBlockJobCreateData { + bool storage; + virStorageSourcePtr src; +}; + + typedef struct _qemuBlockJobData qemuBlockJobData; typedef qemuBlockJobData *qemuBlockJobDataPtr; @@ -102,6 +112,7 @@ struct _qemuBlockJobData { union { qemuBlockJobPullData pull; qemuBlockJobCommitData commit; + qemuBlockJobCreateData create; } data; int type; /* qemuBlockJobType */ @@ -146,6 +157,12 @@ qemuBlockJobDiskNewCommit(virDomainObjPtr vm, virStorageSourcePtr top, virStorageSourcePtr base); +qemuBlockJobDataPtr +qemuBlockJobNewCreate(virDomainObjPtr vm, + virStorageSourcePtr src, + virStorageSourcePtr chain, + bool storage); + qemuBlockJobDataPtr qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk) ATTRIBUTE_NONNULL(1); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 2e722f4ee4..8f32f8a035 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2412,6 +2412,19 @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *= payload, virBufferAsprintf(&childBuf, "\n",= job->data.commit.topparent->nodeformat); break; + case QEMU_BLOCKJOB_TYPE_CREATE: + if (job->data.create.storage) + virBufferAddLit(&childBuf, "\n"); + + if (job->data.create.src && + qemuDomainObjPrivateXMLFormatBlockjobFormatSource(&childBu= f, + "src", + job->dat= a.create.src, + data->xm= lopt, + false) <= 0) + return -1; + break; + case QEMU_BLOCKJOB_TYPE_COPY: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: @@ -2856,8 +2869,12 @@ qemuDomainObjPrivateXMLParseBlockjobNodename(qemuBlo= ckJobDataPtr job, static void qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobDataPtr job, - xmlXPathContextPtr ctxt) + xmlXPathContextPtr ctxt, + virDomainXMLOptionPtr xml= opt) { + VIR_AUTOFREE(char *) createmode =3D NULL; + xmlNodePtr tmp; + switch ((qemuBlockJobType) job->type) { case QEMU_BLOCKJOB_TYPE_PULL: qemuDomainObjPrivateXMLParseBlockjobNodename(job, @@ -2891,6 +2908,19 @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qem= uBlockJobDataPtr job, goto broken; break; + case QEMU_BLOCKJOB_TYPE_CREATE: + if (!(tmp =3D virXPathNode("./src", ctxt)) || + !(job->data.create.src =3D qemuDomainObjPrivateXMLParseBlo= ckjobChain(tmp, ctxt, xmlopt))) + goto broken; + + if ((createmode =3D virXPathString("string(./create/@mode)", c= txt))) { + if (STRNEQ(createmode, "storage")) + goto broken; + + job->data.create.storage =3D true; + } + break; + case QEMU_BLOCKJOB_TYPE_COPY: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: @@ -2980,7 +3010,7 @@ qemuDomainObjPrivateXMLParseBlockjobData(virDomainObj= Ptr vm, if (mirror) job->mirrorChain =3D virObjectRef(job->disk->mirror); - qemuDomainObjPrivateXMLParseBlockjobDataSpecific(job, ctxt); + qemuDomainObjPrivateXMLParseBlockjobDataSpecific(job, ctxt, xmlopt); if (qemuBlockJobRegister(job, vm, disk, false) < 0) return -1; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2c03cc5dff..e358c6a1c4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17730,6 +17730,7 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, case QEMU_BLOCKJOB_TYPE_PULL: case QEMU_BLOCKJOB_TYPE_COMMIT: case QEMU_BLOCKJOB_TYPE_INTERNAL: + case QEMU_BLOCKJOB_TYPE_CREATE: virReportError(VIR_ERR_OPERATION_INVALID, _("job type '%s' does not support pivot"), qemuBlockjobTypeToString(job->type)); diff --git a/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml b/tests/q= emustatusxml2xmldata/blockjob-blockdev-in.xml index d06bbb7059..408e9269db 100644 --- a/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml +++ b/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml @@ -243,6 +243,23 @@ + + + + + + + + + + + + + + + + + @@ -252,6 +269,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + --=20 2.21.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list