From nobody Tue May 13 17:43:10 2025 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; dkim=fail header.i=povilas@radix.lt; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1540141541543412.43289067482874; Sun, 21 Oct 2018 10:05:41 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 192D030B9303; Sun, 21 Oct 2018 17:05:39 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CFC4017A88; Sun, 21 Oct 2018 17:05:38 +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 091B04CA95; Sun, 21 Oct 2018 17:05:38 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id w9LGdDRG005893 for ; Sun, 21 Oct 2018 12:39:13 -0400 Received: by smtp.corp.redhat.com (Postfix) id 4E92760BF1; Sun, 21 Oct 2018 16:39:13 +0000 (UTC) Received: from mx1.redhat.com (ext-mx05.extmail.prod.ext.phx2.redhat.com [10.5.110.29]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 456A860BE0 for ; Sun, 21 Oct 2018 16:39:11 +0000 (UTC) Received: from sender-op-o11.zoho.com (sender-op-o11.zoho.com [135.84.80.196]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id A9CF836A5E6 for ; Sun, 21 Oct 2018 16:39:08 +0000 (UTC) Received: from localhost.localdomain (94.244.84.107 [94.244.84.107]) by mx.zohomail.com with SMTPS id 1540139943868704.4752513275795; Sun, 21 Oct 2018 09:39:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1540139944; cv=none; d=zoho.com; s=zohoarc; b=IvhVFGXhxj/L0/sQp287gjYtpYdr31VWQfM/oHaLuvJtUVG6yujHwQ4IJTTiirERSsMkyTq9v4Abq0saJyA3Sf9D9PaSqfGf+l1seI2m71Vj3I1C9+AlFzOlnzosxRL6vf+FvHTGSiwANXgvNwAerUaA5hBOU3Jqg8a2fVch3TQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1540139944; h=Cc:Date:From:In-Reply-To:Message-ID:References:Subject:To:ARC-Authentication-Results; bh=NWcJ19BAcmJSViznsmhZuPmR2RdEZtaTItMyk0tKviI=; b=difkruTTFLUB80IoVSDC1rLV3b6c5MxFopgDmE6QgeHnYcTTukNLwcqaBP7VrRxKWEB+1si2Ee/H4XK68lxjIfpjEHBHHIuFWq2868AgmGBOIrdvleWV538PCtutclkbCMyqPcig2FfRv0eYmC2/OMJp53mDxVbvsWUSmVhDw1Q= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=pass header.i=radix.lt; spf=pass smtp.mailfrom=povilas@radix.lt; dmarc=pass header.from= header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1540139944; s=zoho; d=radix.lt; i=povilas@radix.lt; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References; l=11118; bh=NWcJ19BAcmJSViznsmhZuPmR2RdEZtaTItMyk0tKviI=; b=f2Slrn58ppHT8oH6xVFg2Yr+LXirKEvOfiS8LIxYFIbnU0Bhcpnz5U4cVgnbo7rj zatfhgP6uI4Vs2IVpGplxK9Xw+uxNhuc1zCXyx7sXwfvbuHqN0pBbkqEj2yz5gzPI6n 1j9J10pSjcDtxKiDEYegrVPsF1HOhy8Afo4D8FJk= From: Povilas Kanapickas To: libvir-list@redhat.com Date: Sun, 21 Oct 2018 19:38:48 +0300 Message-Id: <20181021163852.15114-2-povilas@radix.lt> In-Reply-To: <20181021163852.15114-1-povilas@radix.lt> References: <20181021163852.15114-1-povilas@radix.lt> X-ZohoMailClient: External X-Greylist: Sender passed SPF test, Sender IP whitelisted by DNSRBL, ACL 216 matched, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Sun, 21 Oct 2018 16:39:09 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Sun, 21 Oct 2018 16:39:09 +0000 (UTC) for IP:'135.84.80.196' DOMAIN:'sender-op-o11.zoho.com' HELO:'sender-op-o11.zoho.com' FROM:'povilas@radix.lt' RCPT:'' X-RedHat-Spam-Score: -0.111 (DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_NONE, SPF_PASS) 135.84.80.196 sender-op-o11.zoho.com 135.84.80.196 sender-op-o11.zoho.com X-Scanned-By: MIMEDefang 2.78 on 10.5.110.29 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Cc: Povilas Kanapickas Subject: [libvirt] [PATCH 1/5] snapshot: Implement reverting for external disk snapshots 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: , MIME-Version: 1.0 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.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.49]); Sun, 21 Oct 2018 17:05:39 +0000 (UTC) X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Povilas Kanapickas --- src/qemu/qemu_driver.c | 246 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 224 insertions(+), 22 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a52e2495d5..279e5d93aa 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -15952,19 +15952,194 @@ qemuDomainSnapshotHasMetadata(virDomainSnapshotP= tr snapshot, return ret; } =20 +static int +qemuDomainSnapshotRevertExternalSingleDisk(virQEMUDriverPtr driver, + virDomainDiskDefPtr revert_disk, + virDomainDiskDefPtr backing_dis= k) +{ + virCommandPtr cmd =3D NULL; + const char *qemuImgPath; + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + int ret =3D -1; + + if (!(qemuImgPath =3D qemuFindQemuImgBinary(driver))) + goto cleanup; + + if (unlink(revert_disk->src->path) < 0) + VIR_WARN("Failed to overwrite current image for snapshot '%s'", + revert_disk->src->path); + + /* TODO: maybe figure out the format from the backing_disk? */ + revert_disk->src->format =3D VIR_STORAGE_FILE_QCOW2; + /* FIXME: what about revert_disk->src->backingStore ? */ + + /* creates cmd line args: qemu-img create -f qcow2 -o */ + if (!(cmd =3D virCommandNewArgList(qemuImgPath, + "create", + "-f", + virStorageFileFormatTypeToString(reve= rt_disk->src->format), + "-o", + NULL))) + goto cleanup; + + /* adds cmd line arg: backing_file=3D/path/to/backing/file,backing_fmt= =3Dformat */ + virCommandAddArgFormat(cmd, "backing_file=3D%s,backing_fmt=3D%s", + backing_disk->src->path, + virStorageFileFormatTypeToString(backing_disk->= src->format)); + + /* adds cmd line args: /path/to/target/file */ + virCommandAddArg(cmd, revert_disk->src->path); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + virCommandFree(cmd); + cmd =3D NULL; + ret =3D 0; + + cleanup: + virCommandFree(cmd); + virObjectUnref(cfg); + + return ret; +} + +static void +qemuComputeSnapshotDiskToDomainDiskMapping(virDomainSnapshotDefPtr snap_de= f, + virDomainDefPtr dom_def, + int* out_mapping) +{ + size_t i, j; + int found_idx; + virDomainSnapshotDiskDefPtr snap_disk; + + for (i =3D 0; i < snap_def->ndisks; ++i) { + snap_disk =3D &(snap_def->disks[i]); + if (snap_disk->snapshot !=3D VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) + continue; + + found_idx =3D -1; + for (j =3D 0; j < dom_def->ndisks; ++j) { + if (STREQ(dom_def->disks[j]->dst, snap_disk->name)) { + found_idx =3D j; + break; + } + } + out_mapping[i] =3D found_idx; + } +} + +/* This function reverts an external snapshot disk state. With external di= sks + we can't just revert to the disks listed in the domain stored within the + snapshot, because it's read-only and might be used by other VMs in diff= erent + backing chains. Since the contents of the current disks will be discard= ed + in favor of data in the snapshot, we reuse them by resetting them and + pointing the backing image link to the disks listed within the snapshot. + + The domain is expected to be inactive. + + new_def is the new domain definition that will be stored to vm sometime= in + the future. + */ +static int +qemuDomainSnapshotRevertInactiveExternal(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainSnapshotObjPtr snap, + virDomainDefPtr new_def) +{ + /* We have the following disks recorded in the snapshot and the current + domain definition: + + - the current disk state before the revert (vm->def->disks). We'll + discard and reuse them. + - the lists of disks that were snapshotted (snap->def->disks). We'= ll + use this information to figure out which disks to actually rever= t. + - the original disk state stored in the snapshot + (snap->def->dom->disks). We'll point reverted disks to use these + disks as backing images. + + FIXME: what to do with disks that weren't included in all snapshots + within the hierrachy? + */ + size_t i; + int* snap_disk_to_vm_def_disk_idx_map =3D NULL; + int* snap_disk_to_new_def_disk_idx_map =3D NULL; + int ret =3D -1; + virDomainSnapshotDiskDefPtr snap_disk; + virDomainDiskDefPtr backing_disk; + virDomainDiskDefPtr revert_disk; + virDomainDiskDefPtr new_disk; + + if (VIR_ALLOC_N(snap_disk_to_vm_def_disk_idx_map, snap->def->ndisks) <= 0) + goto cleanup; + if (VIR_ALLOC_N(snap_disk_to_new_def_disk_idx_map, snap->def->ndisks) = < 0) + goto cleanup; + + /* Figure out the matching disks from the current VM state. */ + qemuComputeSnapshotDiskToDomainDiskMapping(snap->def, vm->def, + snap_disk_to_vm_def_disk_id= x_map); + qemuComputeSnapshotDiskToDomainDiskMapping(snap->def, new_def, + snap_disk_to_new_def_disk_i= dx_map); + + for (i =3D 0; i < snap->def->ndisks; ++i) { + snap_disk =3D &(snap->def->disks[i]); + if (snap_disk->snapshot !=3D VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) + continue; + if (snap_disk_to_vm_def_disk_idx_map[i] < 0 || + snap_disk_to_new_def_disk_idx_map[i] < 0) { + // FIXME: we could create additional disks, but for now it's n= ot + // supported. + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("all disks in the snapshots must have " + "equivalents in the current VM state.")); + goto cleanup; + } + } + + /* Discard old disk contents and point them to the backing disks */ + for (i =3D 0; i < snap->def->ndisks; ++i) { + snap_disk =3D &(snap->def->disks[i]); + + // Note that at the moment we don't support mixing internal and + // external snapshot modes for different disks, but skip non-exter= nal + // disks just in case. + if (snap_disk->snapshot !=3D VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) + continue; + + backing_disk =3D snap->def->dom->disks[snap_disk->idx]; + revert_disk =3D vm->def->disks[snap_disk_to_vm_def_disk_idx_map[i]= ]; + if (qemuDomainSnapshotRevertExternalSingleDisk(driver, revert_disk, + backing_disk) < 0) { + goto cleanup; + } + + /* FIXME: figure out which data exactly needs copying. + */ + new_disk =3D new_def->disks[snap_disk_to_new_def_disk_idx_map[i]]; + new_disk->src->format =3D revert_disk->src->format; + if (VIR_STRDUP(new_disk->src->path, revert_disk->src->path) < 0) { + goto cleanup; + } + } + ret =3D 0; + +cleanup: + VIR_FREE(snap_disk_to_vm_def_disk_idx_map); + VIR_FREE(snap_disk_to_new_def_disk_idx_map); + return ret; +} =20 /* The domain is expected to be locked and inactive. */ static int -qemuDomainSnapshotRevertInactive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainSnapshotObjPtr snap) +qemuDomainSnapshotRevertInactiveInternal(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainSnapshotObjPtr snap) { /* Try all disks, but report failure if we skipped any. */ int ret =3D qemuDomainSnapshotForEachQcow2(driver, vm, snap, "-a", tru= e); return ret > 0 ? -1 : ret; } =20 - static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -15981,6 +16156,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr sna= pshot, virDomainDefPtr config =3D NULL; virQEMUDriverConfigPtr cfg =3D NULL; virCapsPtr caps =3D NULL; + bool is_external =3D false; bool was_stopped =3D false; qemuDomainSaveCookiePtr cookie; virCPUDefPtr origCPU =3D NULL; @@ -16043,11 +16219,13 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr s= napshot, goto endjob; } =20 - if (virDomainSnapshotIsExternal(snap)) { + if (snap->def->memory =3D=3D VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("revert to external snapshot not supported yet")); + _("revert to external snapshot with memory state is= " + "not supported yet")); goto endjob; } + is_external =3D virDomainSnapshotIsExternal(snap); =20 if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_FORCE)) { if (!snap->def->dom) { @@ -16090,6 +16268,11 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr sn= apshot, driver->xmlopt, NULL, true); if (!config) goto endjob; + } else if (is_external) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("revert to a an external snapshot without the VM " + "definition recorded is not supported.")); + goto endjob; } =20 cookie =3D (qemuDomainSaveCookiePtr) snap->def->cookie; @@ -16110,8 +16293,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr sna= pshot, /* Transitions 5, 6, 8, 9 */ /* Check for ABI compatibility. We need to do this check again= st * the migratable XML or it will always fail otherwise */ + bool compatible =3D true; if (config) { - bool compatible; =20 /* Replace the CPU in config and put the original one in p= riv * once we're done. When we have the updated CPU def in the @@ -16152,22 +16335,27 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr s= napshot, goto endjob; } virResetError(err); - qemuProcessStop(driver, vm, - VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT, - QEMU_ASYNC_JOB_START, 0); - virDomainAuditStop(vm, "from-snapshot"); - detail =3D VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT; - event =3D virDomainEventLifecycleNewFromObj(vm, - VIR_DOMAIN_EVENT_STOP= PED, - detail); - virObjectEventStateQueue(driver->domainEventState, eve= nt); - /* Start after stop won't be an async start job, so - * reset to none */ - jobType =3D QEMU_ASYNC_JOB_NONE; - goto load; } } =20 + if (!compatible || is_external) { + // If the snapshot is external we completely stop QEMU, ad= just + // the disk state and restart it. + qemuProcessStop(driver, vm, + VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT, + QEMU_ASYNC_JOB_START, 0); + virDomainAuditStop(vm, "from-snapshot"); + detail =3D VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT; + event =3D virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + detail); + virObjectEventStateQueue(driver->domainEventState, event); + /* Start after stop won't be an async start job, so + * reset to none */ + jobType =3D QEMU_ASYNC_JOB_NONE; + goto load; + } + priv =3D vm->privateData; if (virDomainObjGetState(vm, NULL) =3D=3D VIR_DOMAIN_RUNNING) { /* Transitions 5, 6 */ @@ -16208,7 +16396,13 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr sn= apshot, } else { /* Transitions 2, 3 */ load: + was_stopped =3D true; + + if (is_external && + qemuDomainSnapshotRevertInactiveExternal(driver, vm, snap,= config) < 0) + goto cleanup; + if (config) virDomainObjAssignDef(vm, config, false, NULL); =20 @@ -16221,7 +16415,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr sna= pshot, =20 rc =3D qemuProcessStart(snapshot->domain->conn, driver, vm, cookie ? cookie->cpu : NULL, - jobType, NULL, -1, NULL, snap, + jobType, NULL, -1, NULL, + is_external ? NULL : snap, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags); virDomainAuditStart(vm, "from-snapshot", rc >=3D 0); @@ -16291,11 +16486,18 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr s= napshot, detail); } =20 - if (qemuDomainSnapshotRevertInactive(driver, vm, snap) < 0) { + if (is_external) { + rc =3D qemuDomainSnapshotRevertInactiveExternal(driver, vm, sn= ap, config); + } else { + rc =3D qemuDomainSnapshotRevertInactiveInternal(driver, vm, sn= ap); + } + + if (rc < 0) { qemuDomainRemoveInactive(driver, vm); qemuProcessEndJob(driver, vm); goto cleanup; } + if (config) virDomainObjAssignDef(vm, config, false, NULL); =20 --=20 2.17.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list