From nobody Mon Sep 16 20:08:28 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.129.124 as permitted sender) client-ip=170.10.129.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.129.124 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=1678722247; cv=none; d=zohomail.com; s=zohoarc; b=AWZMXZMcgxnNlNboPjv1H8YShsD+NPiDtQWX58ujb1sllw0/vFeGYLmMGMYvSnHtx9oNcC+FU6QWzz/U57g5g/IEvhlK29w4917A23O1TJExI9TVYX62Z7iXL1+fjRQgeuHhVgAcanZ3NdutcvhS0LZJbCeFiA820UIKosqTd0Y= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1678722247; 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; bh=1iiJNhqI4+fV2YLYIfhOv2F6WMnDmJ8YAUt4IB1X9LM=; b=PN2g0oVyKnynQOnh9HVlrAxpIVdlWcgNJY72c/xNpIMQuBDyPwcbYi6DyVcqWBV88gC5b/dvUo8rxI+IQakPXMvZ0rg1rsPhwN7HNKmiG3jwlN6g/H9Qy0ASEkS1Y1Tgiw7LvJG9X+VVDJiUtTu95mdCXA00q7lfi2fm8JaLCQw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.129.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by mx.zohomail.com with SMTPS id 1678722247397564.3253812066521; Mon, 13 Mar 2023 08:44:07 -0700 (PDT) Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-41-7I-KjDnzPxqoqSxq0ojAUA-1; Mon, 13 Mar 2023 11:43:02 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 4B692280AA35; Mon, 13 Mar 2023 15:42:35 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (unknown [10.30.29.100]) by smtp.corp.redhat.com (Postfix) with ESMTP id 35C803542A; Mon, 13 Mar 2023 15:42:35 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (localhost [IPv6:::1]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 00F3519465BA; Mon, 13 Mar 2023 15:42:35 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 3EC5D194658F for ; Mon, 13 Mar 2023 15:42:33 +0000 (UTC) Received: by smtp.corp.redhat.com (Postfix) id 330982166B26; Mon, 13 Mar 2023 15:42:33 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.226.242]) by smtp.corp.redhat.com (Postfix) with ESMTP id CA7832166B2A for ; Mon, 13 Mar 2023 15:42:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1678722246; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=1iiJNhqI4+fV2YLYIfhOv2F6WMnDmJ8YAUt4IB1X9LM=; b=Q7gDw0Wdyr4wFHsw2zWuiUyRw3KiT5wTOfYvIw7RRZ8qNIQeWChs5uGigGLi0wgWdqnQnq OqQ79zt6UlfJBomBSzTAjQP7EjcHRnTFBUSay07YXVt+bEqSV6B/j2ZkDzMhIuumKfQ2RT BwwsQJQKvYHOWUStE0V903PacR1VeJY= X-MC-Unique: 7I-KjDnzPxqoqSxq0ojAUA-1 X-Original-To: libvir-list@listman.corp.redhat.com From: Pavel Hrdina To: libvir-list@redhat.com Subject: [libvirt PATCH 09/20] qemu_snapshot: introduce external snapshot revert support Date: Mon, 13 Mar 2023 16:42:10 +0100 Message-Id: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.6 X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libvir-list-bounces@redhat.com Sender: "libvir-list" X-Scanned-By: MIMEDefang 3.1 on 10.11.54.5 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1678722249356100002 Content-Type: text/plain; charset="utf-8"; x-default="true" When reverting to external snapshot we need to create new overlay qcow2 files from the disk files the VM had when the snapshot was taken. There are some specifics and limitations when reverting to a snapshot: 1) When reverting to last snapshot we need to first create new overlay files before we can safely delete the old overlay files in case the creation fails so we have still recovery option when we error out. These new files will not have the suffix as when the snapshot was created as renaming the original files in order to use the same file names as when the snapshot was created would add unnecessary complexity to the code. 2) When reverting to any snapshot we will always create overlay files for every disk the VM had when the snapshot was done. Otherwise we would have to figure out if there is any other qcow2 image already using any of the VM disks as backing store and that itself might be extremely complex and in some cases impossible. 3) When reverting from any state the current overlay files will be always removed as that VM state is not meant to be saved. It's the same as with internal snapshots. If user want's to keep the current state before reverting they need to create a new snapshot. For now this will only work if the current snapshot is the last. Signed-off-by: Pavel Hrdina --- src/qemu/qemu_snapshot.c | 143 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 9e4b978b1b..af4e2ea6aa 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -18,6 +18,8 @@ =20 #include =20 +#include + #include "qemu_snapshot.h" =20 #include "qemu_monitor.h" @@ -1968,6 +1970,119 @@ qemuSnapshotRevertWriteMetadata(virDomainObj *vm, } =20 =20 +static int +qemuSnapshotRevertExternal(virDomainObj *vm, + virDomainMomentObj *snap, + virDomainDef *config, + virDomainDef *inactiveConfig, + int *memsnapFD, + char **memsnapPath) +{ + size_t i; + virDomainDef *domdef =3D NULL; + virDomainSnapshotLocation location =3D VIR_DOMAIN_SNAPSHOT_LOCATION_EX= TERNAL; + virDomainMomentObj *curSnap =3D virDomainSnapshotGetCurrent(vm->snapsh= ots); + virDomainSnapshotDef *snapdef =3D virDomainSnapshotObjGetDef(snap); + virDomainSnapshotDef *curdef =3D virDomainSnapshotObjGetDef(curSnap); + g_autoptr(virDomainSnapshotDef) tmpsnapdef =3D NULL; + g_autoptr(virBitmap) created =3D NULL; + int ret =3D -1; + + if (config) { + domdef =3D config; + } else { + domdef =3D inactiveConfig; + } + + if (!(tmpsnapdef =3D virDomainSnapshotDefNew())) + return -1; + + if (virDomainMomentDefPostParse(&tmpsnapdef->parent) < 0) + return -1; + + if (virDomainSnapshotAlignDisks(tmpsnapdef, domdef, location, false) <= 0) + return -1; + + created =3D virBitmapNew(tmpsnapdef->ndisks); + + if (qemuSnapshotCreateQcow2Files(vm, domdef, tmpsnapdef, created, fals= e) < 0) + return -1; + + if (memsnapFD && memsnapPath && snapdef->memorysnapshotfile) { + virQEMUDriver *driver =3D ((qemuDomainObjPrivate *) vm->privateDat= a)->driver; + g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driv= er); + + *memsnapPath =3D snapdef->memorysnapshotfile; + *memsnapFD =3D qemuDomainOpenFile(cfg, NULL, *memsnapPath, O_RDONL= Y, NULL); + } + + if (config) { + if (qemuSnapshotDomainDefUpdateDisk(config, tmpsnapdef, false) < 0) + goto cleanup; + } + + if (qemuSnapshotDomainDefUpdateDisk(inactiveConfig, tmpsnapdef, false)= < 0) + goto cleanup; + + if (curdef->revertdisks) { + for (i =3D 0; i < curdef->nrevertdisks; i++) { + virDomainSnapshotDiskDef *snapdisk =3D &(curdef->revertdisks[i= ]); + + if (unlink(snapdisk->src->path) < 0) { + VIR_WARN("Failed to remove snapshot image '%s'", + snapdisk->src->path); + } + + virDomainSnapshotDiskDefClear(snapdisk); + } + + g_clear_pointer(&curdef->revertdisks, g_free); + curdef->nrevertdisks =3D 0; + } else { + for (i =3D 0; i < curdef->ndisks; i++) { + virDomainSnapshotDiskDef *snapdisk =3D &(curdef->disks[i]); + + if (unlink(snapdisk->src->path) < 0) { + VIR_WARN("Failed to remove snapshot image '%s'", + snapdisk->src->path); + } + } + } + + if (snap->nchildren !=3D 0) { + snapdef->revertdisks =3D g_steal_pointer(&tmpsnapdef->disks); + snapdef->nrevertdisks =3D tmpsnapdef->ndisks; + tmpsnapdef->ndisks =3D 0; + } else { + for (i =3D 0; i < snapdef->ndisks; i++) { + virDomainSnapshotDiskDefClear(&snapdef->disks[i]); + } + g_free(snapdef->disks); + snapdef->disks =3D g_steal_pointer(&tmpsnapdef->disks); + snapdef->ndisks =3D tmpsnapdef->ndisks; + tmpsnapdef->ndisks =3D 0; + } + + ret =3D 0; + + cleanup: + if (ret !=3D 0 && created) { + ssize_t bit =3D -1; + + while ((bit =3D virBitmapNextSetBit(created, bit)) >=3D 0) { + virDomainSnapshotDiskDef *snapdisk =3D &(tmpsnapdef->disks[bit= ]); + + if (unlink(snapdisk->src->path) < 0) { + VIR_WARN("Failed to remove snapshot image '%s'", + snapdisk->src->path); + } + } + } + + return ret; +} + + static int qemuSnapshotRevertActive(virDomainObj *vm, virDomainSnapshotPtr snapshot, @@ -1982,6 +2097,9 @@ qemuSnapshotRevertActive(virDomainObj *vm, { virObjectEvent *event =3D NULL; virObjectEvent *event2 =3D NULL; + virDomainMomentObj *loadSnap =3D NULL; + VIR_AUTOCLOSE memsnapFD =3D -1; + char *memsnapPath =3D NULL; int detail; bool defined =3D false; qemuDomainSaveCookie *cookie =3D (qemuDomainSaveCookie *) snapdef->coo= kie; @@ -2003,6 +2121,15 @@ qemuSnapshotRevertActive(virDomainObj *vm, virObjectEventStateQueue(driver->domainEventState, event); } =20 + if (virDomainSnapshotIsExternal(snap)) { + if (qemuSnapshotRevertExternal(vm, snap, *config, *inactiveConfig, + &memsnapFD, &memsnapPath) < 0) { + return -1; + } + } else { + loadSnap =3D snap; + } + if (*inactiveConfig) { virDomainObjAssignDef(vm, inactiveConfig, false, NULL); defined =3D true; @@ -2019,7 +2146,8 @@ qemuSnapshotRevertActive(virDomainObj *vm, =20 rc =3D qemuProcessStart(snapshot->domain->conn, driver, vm, cookie ? cookie->cpu : NULL, - VIR_ASYNC_JOB_START, NULL, -1, NULL, snap, + VIR_ASYNC_JOB_START, NULL, memsnapFD, + memsnapPath, loadSnap, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags); virDomainAuditStart(vm, "from-snapshot", rc >=3D 0); @@ -2124,9 +2252,16 @@ qemuSnapshotRevertInactive(virDomainObj *vm, virObjectEventStateQueue(driver->domainEventState, event); } =20 - if (qemuSnapshotInternalRevertInactive(driver, vm, snap) < 0) { - qemuDomainRemoveInactive(driver, vm, 0, false); - return -1; + if (virDomainSnapshotIsExternal(snap)) { + if (qemuSnapshotRevertExternal(vm, snap, NULL, *inactiveConfig, + NULL, NULL) < 0) { + return -1; + } + } else { + if (qemuSnapshotInternalRevertInactive(driver, vm, snap) < 0) { + qemuDomainRemoveInactive(driver, vm, 0, false); + return -1; + } } =20 if (*inactiveConfig) { --=20 2.39.2