This completes the public API for using mirrored snapshots as a means of performing live storage migration. Of course, it will take additional patches to actually provide the implementation. The idea here is that oVirt can start with a domain with 'vda' open on /path1/to/old.qcow2 with a base file of /path1/to/common, then do the following steps for a live migration to /path2 (here using virsh commands, although the underlying API would be available to oVirt through other language bindings as well). First, pre-create two files; it is important that the mirror file have a relative backing file name (if we let qemu create the entire snapshot, the backing file name would be absolute to /path1, and pivoting to the mirror would still be using the original source): $ qemu-img create -f qcow2 -o backing_file=old.qcow2 \ -o backing_fmt=qcow2 /path1/to/old.migrate $ qemu-img create -f qcow2 -o backing_file=old.qcow2 \ -o backing_fmt=qcow2 /path2/to/new.qcow2 Next, create a mirrored snapshot, while telling qemu to respect the pre-existing qcow2 header in both files: $ virsh snapshot-create-as $dom migrate --reuse-external \ --diskspec vda,file=/path1/to/old.migrate,mirror=/path2/to/new.qcow2 which means 'vda' is now open read-write on /path1/to/old.migrate and mirrored (write only) on /path2/to/new.qcow2, where qemu is using /path1/to/old.qcow2 as the logial backing for both files, but where the relative name is still intact in /path2/to/new.qcow2. Next, the backing files can be copied between locations: $ cp /path1/to/common /path1/to/old.qcow2 /path2/to When that is complete, the mirrored snapshot is no longer necessary, and requesting a pivot while deleting things will force qemu to reread the header of /path2/to/new.qcow2, notice a relative backing file name, and open /path2/to/old.qcow2 as the logical backing file: $ virsh snapshot-delete $dom migrate --mirror-pivot Now /path1 is no longer in use, but the backing chain on /path2 is longer than original. This can be cleaned up with: $ virsh blockpull $dom vda --base /path2/to/common to go back to having /path2/to/new.qcow2 directly backed by /path2/to/common. That smells like a hack, right? Well, there is a proposal for a better, more powerful API named virDomainBlockCopy: https://www.redhat.com/archives/libvir-list/2012-March/msg00585.html which would more appropriately expose the various knobs of the new qemu commands; but being a new API, it lacks the ability to be backported without causing a .so bump. So this is the compromise. * include/libvirt/libvirt.h.in (VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_ABORT) (VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_PIVOT): New flags. * src/libvirt.c (virDomainSnapshotDelete): Document them. (virDomainSnapshotCreateXML, virDomainRevertToSnapshot) (virDomainSaveFlags): Mention effects of mirrored snapshots. * tools/virsh.c (vshParseSnapshotDiskspec): Add <mirror> support. (cmdSnapshotDelete): Add --mirror-abort, --mirror-pivot flags. * tools/virsh.pod (snapshot-delete, snapshot-create-as): Document new options. --- include/libvirt/libvirt.h.in | 6 ++++++ src/libvirt.c | 37 +++++++++++++++++++++++++++++++++++-- tools/virsh.c | 17 ++++++++++++++--- tools/virsh.pod | 21 ++++++++++++++------- 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index aaee218..7a4d416 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3430,6 +3430,12 @@ typedef enum { VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN = (1 << 0), /* Also delete children */ VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY = (1 << 1), /* Delete just metadata */ VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY = (1 << 2), /* Delete just children */ + VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_ABORT = (1 << 3), /* Stop a mirrored + snapshot, reopening + to the source. */ + VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_PIVOT = (1 << 4), /* Stop a mirrored + snapshot, reopening + to the mirror. */ } virDomainSnapshotDeleteFlags; int virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, diff --git a/src/libvirt.c b/src/libvirt.c index ad50815..cd7469a 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -2693,6 +2693,9 @@ error: * @flags will override what state gets saved into the file. These * two flags are mutually exclusive. * + * Some hypervisors may prevent this call if there is a current + * mirrored snapshot. + * * A save file can be inspected or modified slightly with * virDomainSaveImageGetXMLDesc() and virDomainSaveImageDefineXML(). * @@ -17089,12 +17092,14 @@ virDomainSnapshotGetConnect(virDomainSnapshotPtr snapshot) * not exist, the hypervisor may validate that reverting to the * snapshot appears to be possible (for example, disk images have * snapshot contents by the requested name). Not all hypervisors - * support these flags. + * support these flags; this might also be forbidden if there is a + * current mirrored snapshot. * * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, then the * domain's disk images are modified according to @xmlDesc, but then * the just-created snapshot has its metadata deleted. This flag is - * incompatible with VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE. + * incompatible with VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE, and may + * also prevent any @xmlDesc that tries to create a mirrored snapshot. * * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_HALT, then the domain * will be inactive after the snapshot completes, regardless of whether @@ -17704,6 +17709,9 @@ error: * inactive snapshots with a @flags request to start the domain after * the revert. * + * Some hypervisors may prevent reverting to snapshots if there is an + * active mirrored snapshot. + * * Returns 0 if the creation is successful, -1 on error. */ int @@ -17770,6 +17778,25 @@ error: * libvirt metadata to track snapshots, then this flag is silently * ignored. * + * If the domain has a current mirrored snapshot (that is, if the XML + * given to virDomainSnapshotCreateXML() included <mirror> elements for + * a disk), then the caller must specify how to end the mirroring before + * the snapshot can be deleted. In this situation, @snap must be the + * current mirrored snapshot, and @flags must contain either + * VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_ABORT to abandon the mirror while + * keeping the primary external file, or contain + * VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_PIVOT to reopen the storage device + * using just the mirror, and abandon the primary external file. Note + * that while virDomainSnapshotCreateXML can create mirrors atomically, + * the deletion process might not be atomic; in this case, the operation + * will fail, but only after updating the snapshot object to record + * which mirrors were successfully reopened. If atomicity is important + * when using mirrored snapshots to perform live storage migration, it + * is recommended to migrate only one disk per snapshot. After using + * a mirrored snapshot to perform a storage migration, the caller may + * also want to use virDomainBlockRebase() to reduce the length of the + * backing chain that was lengthened by the snapshot process. + * * Returns 0 if the selected snapshot(s) were successfully deleted, * -1 on error. */ @@ -17803,6 +17830,12 @@ virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, "mutually exclusive")); goto error; } + if ((flags & VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_ABORT) && + (flags & VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_PIVOT)) { + virLibDomainError(VIR_ERR_INVALID_ARG, + _("delete mirror flags are mutually exclusive")); + goto error; + } if (conn->driver->domainSnapshotDelete) { int ret = conn->driver->domainSnapshotDelete(snapshot, flags); diff --git a/tools/virsh.c b/tools/virsh.c index e521933..d0cdba5 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -16110,6 +16110,7 @@ vshParseSnapshotDiskspec(vshControl *ctl, virBufferPtr buf, const char *str) char *snapshot = NULL; char *driver = NULL; char *file = NULL; + char *mirror = NULL; char *spec = vshStrdup(ctl, str); char *tmp = spec; size_t len = strlen(str); @@ -16133,6 +16134,8 @@ vshParseSnapshotDiskspec(vshControl *ctl, virBufferPtr buf, const char *str) driver = tmp + strlen("driver="); else if (!file && STRPREFIX(tmp, "file=")) file = tmp + strlen("file="); + else if (!mirror && STRPREFIX(tmp, "mirror=")) + mirror = tmp + strlen("mirror="); else goto cleanup; } @@ -16140,12 +16143,12 @@ vshParseSnapshotDiskspec(vshControl *ctl, virBufferPtr buf, const char *str) virBufferEscapeString(buf, " <disk name='%s'", name); if (snapshot) virBufferAsprintf(buf, " snapshot='%s'", snapshot); - if (driver || file) { + if (driver || file || mirror) { virBufferAddLit(buf, ">\n"); if (driver) virBufferAsprintf(buf, " <driver type='%s'/>\n", driver); - if (file) - virBufferEscapeString(buf, " <source file='%s'/>\n", file); + virBufferEscapeString(buf, " <source file='%s'/>\n", file); + virBufferEscapeString(buf, " <mirror file='%s'/>\n", mirror); virBufferAddLit(buf, " </disk>\n"); } else { virBufferAddLit(buf, "/>\n"); @@ -17182,6 +17185,10 @@ static const vshCmdOptDef opts_snapshot_delete[] = { {"children-only", VSH_OT_BOOL, 0, N_("delete children but not snapshot")}, {"metadata", VSH_OT_BOOL, 0, N_("delete only libvirt metadata, leaving snapshot contents behind")}, + {"mirror-abort", VSH_OT_BOOL, 0, + N_("abort a mirror while removing snapshot")}, + {"mirror-pivot", VSH_OT_BOOL, 0, + N_("pivot to the mirror before removing snapshot")}, {NULL, 0, 0, NULL} }; @@ -17211,6 +17218,10 @@ cmdSnapshotDelete(vshControl *ctl, const vshCmd *cmd) flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY; if (vshCommandOptBool(cmd, "metadata")) flags |= VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY; + if (vshCommandOptBool(cmd, "mirror-abort")) + flags |= VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_ABORT; + if (vshCommandOptBool(cmd, "mirror-pivot")) + flags |= VIR_DOMAIN_SNAPSHOT_DELETE_MIRROR_PIVOT; /* XXX If we wanted, we could emulate DELETE_CHILDREN_ONLY even on * older servers that reject the flag, by manually computing the diff --git a/tools/virsh.pod b/tools/virsh.pod index 0e7617b..cb121a5 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -2430,12 +2430,12 @@ is specified, the snapshot will not include vm state. The I<--disk-only> flag is used to request a disk-only snapshot. When this flag is in use, the command can also take additional I<diskspec> arguments to add <disk> elements to the xml. Each <diskspec> is in the -form B<disk[,snapshot=type][,driver=type][,file=name]>. To include a -literal comma in B<disk> or in B<file=name>, escape it with a second -comma. A literal I<--diskspec> must preceed each B<diskspec> unless -all three of I<domain>, I<name>, and I<description> are also present. -For example, a diskspec of "vda,snapshot=external,file=/path/to,,new" -results in the following XML: +form B<disk[,snapshot=type][,driver=type][,file=name][,mirror=name]>. +To include a literal comma in B<disk>, B<file=name>, or B<mirror=name>, +escape it with a second comma. A literal I<--diskspec> must preceed +each B<diskspec> unless all three of I<domain>, I<name>, and +I<description> are also present. For example, a diskspec of +"vda,snapshot=external,file=/path/to,,new" results in the following XML: <disk name='vda' snapshot='external'> <source file='/path/to,new'/> </disk> @@ -2578,7 +2578,7 @@ with an inactive snapshot that is combined with the I<--start> or I<--pause> flag. =item B<snapshot-delete> I<domain> {I<snapshot> | I<--current>} [I<--metadata>] -[{I<--children> | I<--children-only>}] +[{I<--children> | I<--children-only>}] [{I<--mirror-abort> | I<--mirror-pivot>}] Delete the snapshot for the domain named I<snapshot>, or the current snapshot with I<--current>. If this snapshot @@ -2593,6 +2593,13 @@ maintained by libvirt, while leaving the snapshot contents intact for access by external tools; otherwise deleting a snapshot also removes the data contents from that point in time. +If the snapshot is mirrored (that is, the <domainsnapshot> XML used to +create the snapshot included an external <mirror> designation for use +in live migration of storage), then this command will fail unless the +current snapshot is being deleted, and either the I<--mirror-abort> or +I<--mirror-pivot> flag is present to control which half of the mirror +is kept active. + =back =head1 NWFILTER COMMMANDS -- 1.7.7.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list