[libvirt] [PATCH 2/6] snapshot: add new snapshot delete flags

Eric Blake eblake at redhat.com
Fri Mar 23 04:17:35 UTC 2012


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                |   13 +++++++++++++
 tools/virsh.pod              |   21 ++++++++++++++-------
 4 files changed, 68 insertions(+), 9 deletions(-)

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 4566580..dbb6358 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -3369,6 +3369,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 7df3667..800d1ee 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -2702,6 +2702,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().
  *
@@ -17087,12 +17090,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
@@ -17698,6 +17703,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
@@ -17764,6 +17772,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.
  */
@@ -17797,6 +17824,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 2548b73..9c0358e 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -15790,6 +15790,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);
@@ -15813,6 +15814,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;
     }
@@ -15826,6 +15829,8 @@ vshParseSnapshotDiskspec(vshControl *ctl, virBufferPtr buf, const char *str)
             virBufferAsprintf(buf, "      <driver type='%s'/>\n", driver);
         if (file)
             virBufferEscapeString(buf, "      <source file='%s'/>\n", file);
+        if (mirror)
+            virBufferEscapeString(buf, "      <mirror file='%s'/>\n", mirror);
         virBufferAddLit(buf, "    </disk>\n");
     } else {
         virBufferAddLit(buf, "/>\n");
@@ -16861,6 +16866,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}
 };

@@ -16890,6 +16899,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 8517fe5..5712c83 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -2355,12 +2355,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>
@@ -2503,7 +2503,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
@@ -2518,6 +2518,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




More information about the libvir-list mailing list