From af65695af0472f95e912e532702f462214ba5992 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 30 Aug 2011 19:02:46 -0600 Subject: [PATCH] snapshot: allow recreation of metadata The first two flags are essential for being able to replicate snapshot hierarchies across multiple hosts, which will come in handy for supervised migrations. It also allows a management app to take a snapshot of a transient domain, save the metadata, stop the domain, recreate a new transient domain by the same name, redefine the snapshot, then revert to it. This is not quite as convenient as leaving the metadata behind after a domain is no longer around, but doing that has a few problems: 1. the libvirt API can only delete snapshot metadata if there is a valid domain handle to use to get to that snapshot object - if stale data is left behind without a domain, there is no way to request that the data be cleaned up. 2. creating a new domain with the same name but different uuid than the older domain where a snapshot existed cannot use the older snapshot data; this risks confusing libvirt, and forbidding the stale data is similar to the recent patch to forbid stale managed save. The first two flags might be useful on hypervisors with no metadata, but only for modifying the notion of the current snapshot; however, I don't know how to do that for ESX or VBox. The third flag is a convenience option, to combine a creation with a delete metadata into one step. It is trivial for hypervisors with no metadata. The qemu changes will be involved enough to warrant a separate patch. * include/libvirt/libvirt.h.in (VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) (VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT) (VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA): New flags. * src/libvirt.c (virDomainSnapshotCreateXML): Document them, and enforce mutual exclusion. * src/esx/esx_driver.c (esxDomainSnapshotCreateXML): Trivial implementation. * src/vbox/vbox_tmpl.c (vboxDomainSnapshotCreateXML): Likewise. * docs/formatsnapshot.html.in: Document re-creation. --- docs/formatsnapshot.html.in | 69 ++++++++++++++++++++++-------------- include/libvirt/libvirt.h.in | 9 +++++ src/esx/esx_driver.c | 3 +- src/libvirt.c | 50 +++++++++++++++++++++++++- src/vbox/vbox_tmpl.c | 3 +- 5 files changed, 105 insertions(+), 29 deletions(-) diff --git a/docs/formatsnapshot.html.in b/docs/formatsnapshot.html.in index 79ed1d234a..dc5873a822 100644 --- a/docs/formatsnapshot.html.in +++ b/docs/formatsnapshot.html.in @@ -9,10 +9,26 @@

Attributes of libvirt snapshots are stored as child elements of the domainsnapshot element. At snapshot creation - time, only the name and description - elements are settable; the rest of the fields are informational - (and readonly) and will be filled in by libvirt when the - snapshot is created. + time, normally only the name + and description elements are settable; the rest of + the fields are ignored on creation, and will be filled in by + libvirt in for informational purposes + by virDomainSnapshotGetXMLDesc(). However, when + redefining a snapshot (since 0.9.5), + with the VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE flag + of virDomainSnapshotCreateXML(), all of the XML + described here is relevant. +

+

+ Snapshots are maintained in a hierarchy. A domain can have a + current snapshot, which is the most recent snapshot compared to + the current state of the domain (although a domain might have + snapshots without a current snapshot, if snapshots have been + deleted in the meantime). Creating or reverting to a snapshot + sets that snapshot as current, and the prior current snapshot is + the parent of the new snapshot. Branches in the hierarchy can + be formed by reverting to a snapshot with a child, then creating + another snapshot.

The top-level domainsnapshot element may contain @@ -21,9 +37,10 @@

name
The name for this snapshot. If the name is specified when - initially creating the snapshot, then the snapshot will have - that particular name. If the name is omitted when initially - creating the snapshot, then libvirt will make up a name for the snapshot. + initially creating the snapshot, then the snapshot will have + that particular name. If the name is omitted when initially + creating the snapshot, then libvirt will make up a name for + the snapshot, based on the time when it was created.
description
A human-readable description of the snapshot. If the @@ -32,18 +49,18 @@
creationTime
The time this snapshot was created. The time is specified - in seconds since the Epoch, UTC (i.e. Unix time). Readonly. + in seconds since the Epoch, UTC (i.e. Unix time). Readonly.
state
-
The state of the domain at the time this snapshot was - taken. When the domain is reverted to this snapshot, the domain's state - will be set to whatever is in this field. Readonly. +
The state of the domain at the time this snapshot was taken. + When the domain is reverted to this snapshot, the domain's + state will default to whatever is in this field. Readonly.
parent
The parent of this snapshot. This element contains exactly - one child element, name. This specifies the name of the parent - snapshot of this snapshot, and is used to represent trees of - snapshots. Readonly. + one child element, name. This specifies the name of the parent + snapshot of this snapshot, and is used to represent trees of + snapshots, as described above. Readonly.
domain
The domain that this snapshot was taken against. This @@ -56,17 +73,17 @@

Example

-      <domainsnapshot>
-         <name>os-updates</name>
-         <description>Snapshot of OS install and updates</description>
-         <state>running</state>
-         <creationTime>1270477159</creationTime>
-         <parent>
-            <name>bare-os-install</name>
-         </parent>
-         <domain>
-            <uuid>93a5c045-6457-2c09-e56c-927cdf34e178</uuid>
-         </domain>
-      </domainsnapshot>
+<domainsnapshot> + <name>os-updates</name> + <description>Snapshot of OS install and updates</description> + <state>running</state> + <creationTime>1270477159</creationTime> + <parent> + <name>bare-os-install</name> + </parent> + <domain> + <uuid>93a5c045-6457-2c09-e56c-927cdf34e178</uuid> + </domain> +</domainsnapshot> diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 257d9dec15..59c9e63dac 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2554,6 +2554,15 @@ typedef struct _virDomainSnapshot virDomainSnapshot; */ typedef virDomainSnapshot *virDomainSnapshotPtr; +typedef enum { + VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE = (1 << 0), /* Restore or alter + metadata */ + VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT = (1 << 1), /* With redefine, make + snapshot current */ + VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA = (1 << 2), /* Make snapshot without + remembering it */ +} virDomainSnapshotCreateFlags; + /* Take a snapshot of the current VM state */ virDomainSnapshotPtr virDomainSnapshotCreateXML(virDomainPtr domain, const char *xmlDesc, diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 7cad031360..695fb03674 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -4210,7 +4210,8 @@ esxDomainSnapshotCreateXML(virDomainPtr domain, const char *xmlDesc, char *taskInfoErrorMessage = NULL; virDomainSnapshotPtr snapshot = NULL; - virCheckFlags(0, NULL); + /* ESX has no snapshot metadata, so this flag is trivial. */ + virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL); if (esxVI_EnsureSession(priv->primary) < 0) { return NULL; diff --git a/src/libvirt.c b/src/libvirt.c index 8cfac89ca9..c1b154e590 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -15576,11 +15576,46 @@ error: * virDomainSnapshotCreateXML: * @domain: a domain object * @xmlDesc: string containing an XML description of the domain - * @flags: unused flag parameters; callers should pass 0 + * @flags: bitwise-OR of virDomainSnapshotCreateFlags * * Creates a new snapshot of a domain based on the snapshot xml * contained in xmlDesc. * + * If @flags is 0, the domain can be active, in which case the + * snapshot will be a system checkpoint (both disk state and runtime + * VM state such as RAM contents), where reverting to the snapshot is + * the same as resuming from hibernation (TCP connections may have + * timed out, but everything else picks up where it left off); or + * the domain can be inactive, in which case the snapshot includes + * just the disk state prior to booting. The newly created snapshot + * becomes current (see virDomainSnapshotCurrent()), and is a child + * of any previous current snapshot. + * + * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE, then this + * is a request to reinstate snapshot metadata that was previously + * discarded, rather than creating a new snapshot. This can be used + * to recreate a snapshot hierarchy on a destination, then remove it + * on the source, in order to allow migration (since migration + * normally fails if snapshot metadata still remains on the source + * machine). When redefining snapshot metadata, the current snapshot + * will not be altered unless the VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT + * flag is also present. It is an error to request the + * VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT flag without + * VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE. On some hypervisors, + * redefining an existing snapshot can be used to alter host-specific + * portions of the domain XML to be used during revert (such as + * backing filenames associated with disk devices), but must not alter + * guest-visible layout. When redefining a snapshot name that does + * 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. + * + * 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. + * * Returns an (opaque) virDomainSnapshotPtr on success, NULL on failure. */ virDomainSnapshotPtr @@ -15612,6 +15647,19 @@ virDomainSnapshotCreateXML(virDomainPtr domain, goto error; } + if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT) && + !(flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE)) { + virLibDomainError(VIR_ERR_INVALID_ARG, + _("use of current flag requires redefine flag")); + goto error; + } + if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) && + (flags & VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA)) { + virLibDomainError(VIR_ERR_INVALID_ARG, + _("redefine and no metadata flags are mutually exclusive")); + goto error; + } + if (conn->driver->domainSnapshotCreateXML) { virDomainSnapshotPtr ret; ret = conn->driver->domainSnapshotCreateXML(domain, xmlDesc, flags); diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index fc9739e302..afe951ccde 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -5652,7 +5652,8 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, PRInt32 result; #endif - virCheckFlags(0, NULL); + /* VBox has no snapshot metadata, so this flag is trivial. */ + virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL); if (!(def = virDomainSnapshotDefParseString(xmlDesc, 1))) goto cleanup;