since the snapshot in a single file) and external (the
snapshot is one file, and the changes since the snapshot are
in another file).</dd>
- <dt>VM state</dt>
+ <dt>memory state (or VM state)</dt>
<dd>Tracks only the state of RAM and all other resources in use
by the VM. If the disks are unmodified between the time a VM
state snapshot is taken and restored, then the guest will
corruption.</dd>
<dt>system checkpoint</dt>
<dd>A combination of disk snapshots for all disks as well as VM
- state, which can be used to resume the guest from where it
+ memory state, which can be used to resume the guest from where it
left off with symptoms similar to hibernation (that is, TCP
connections in the guest may have timed out, but no files or
processes are lost).</dd>
<p>
Libvirt can manage all three types of snapshots. For now, VM
- state snapshots are created only by
+ state (memory) snapshots are created only by
the <code>virDomainSave()</code>, <code>virDomainSaveFlags</code>,
and <code>virDomainManagedSave()</code> functions, and restored
via the <code>virDomainRestore()</code>,
description is omitted when initially creating the snapshot,
then this field will be empty.
</dd>
+ <dt><code>memory</code></dt>
+ <dd>On input, this is an optional request for how to handle VM
+ memory state. For an offline domain or a disk-only snapshot,
+ attribute <code>snapshot</code> must be <code>no</code>, since
+ there is no VM state saved; otherwise, the attribute can
+ be <code>internal</code> if the memory state is piggy-backed with
+ other internal disk state, or <code>external</code> along with
+ a second attribute <code>file</code> giving the absolute path
+ of the file holding the VM memory state. <span class="since">Since
+ 1.0.1</span>
+ </dd>
<dt><code>disks</code></dt>
<dd>On input, this is an optional listing of specific
instructions for disk snapshots; it is needed when making a
to <code>virDomainRevertToSnapshot()</code>. Additionally,
this field can be the value "disk-snapshot"
(<span class="since">since 0.9.5</span>) when it represents
- only a disk snapshot (no VM state), and reverting to this
+ only a disk snapshot (no VM memory state), and reverting to this
snapshot will default to an inactive guest. Readonly.
</dd>
<dt><code>parent</code></dt>
<parent>
<name>bare-os-install</name>
</parent>
+ <memory snapshot='no'/>
<disks>
<disk name='vda' snapshot='external'>
<driver type='qcow2'/>
<text/>
</element>
</optional>
+ <optional>
+ <element name='memory'>
+ <choice>
+ <attribute name='snapshot'>
+ <choice>
+ <value>no</value>
+ <value>internal</value>
+ </choice>
+ </attribute>
+ <group>
+ <optional>
+ <attribute name='snapshot'>
+ <value>external</value>
+ </attribute>
+ </optional>
+ <attribute name='file'>
+ <ref name='absFilePath'/>
+ </attribute>
+ </group>
+ </choice>
+ <empty/>
+ </element>
+ </optional>
<optional>
<element name='disks'>
<zeroOrMore>
VIR_FREE(def->name);
VIR_FREE(def->description);
VIR_FREE(def->parent);
+ VIR_FREE(def->file);
for (i = 0; i < def->ndisks; i++)
virDomainSnapshotDiskDefClear(&def->disks[i]);
VIR_FREE(def->disks);
int active;
char *tmp;
int keepBlanksDefault = xmlKeepBlanksDefault(0);
+ char *memorySnapshot = NULL;
+ char *memoryFile = NULL;
+ bool offline = !!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE);
xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt);
if (!xml) {
state);
goto cleanup;
}
+ offline = (def->state == VIR_DOMAIN_SHUTOFF ||
+ def->state == VIR_DOMAIN_DISK_SNAPSHOT);
/* Older snapshots were created with just <domain>/<uuid>, and
* lack domain/@type. In that case, leave dom NULL, and
def->creationTime = tv.tv_sec;
}
+ memorySnapshot = virXPathString("string(./memory/@snapshot)", ctxt);
+ memoryFile = virXPathString("string(./memory/@file)", ctxt);
+ if (memorySnapshot) {
+ def->memory = virDomainSnapshotLocationTypeFromString(memorySnapshot);
+ if (def->memory <= 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unknown memory snapshot setting '%s'"),
+ memorySnapshot);
+ goto cleanup;
+ }
+ if (memoryFile &&
+ def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("memory filename '%s' requires external snapshot"),
+ memoryFile);
+ goto cleanup;
+ }
+ } else if (memoryFile) {
+ def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
+ } else if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
+ def->memory = (offline ?
+ VIR_DOMAIN_SNAPSHOT_LOCATION_NONE :
+ VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL);
+ }
+ if (offline && def->memory &&
+ def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_NONE) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("memory state cannot be saved with offline snapshot"));
+ goto cleanup;
+ }
+ def->file = memoryFile;
+ memoryFile = NULL;
+
if ((i = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
goto cleanup;
- if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS ||
- (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE &&
- def->state == VIR_DOMAIN_DISK_SNAPSHOT)) {
+ if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS) {
def->ndisks = i;
if (def->ndisks && VIR_ALLOC_N(def->disks, def->ndisks) < 0) {
virReportOOMError();
VIR_FREE(creation);
VIR_FREE(state);
VIR_FREE(nodes);
+ VIR_FREE(memorySnapshot);
+ VIR_FREE(memoryFile);
xmlXPathFreeContext(ctxt);
if (ret == NULL)
virDomainSnapshotDefFree(def);
}
virBufferAsprintf(&buf, " <creationTime>%lld</creationTime>\n",
def->creationTime);
- /* For now, only output <disks> on disk-snapshot */
- if (def->state == VIR_DOMAIN_DISK_SNAPSHOT) {
+ if (def->memory) {
+ virBufferAsprintf(&buf, " <memory snapshot='%s'",
+ virDomainSnapshotLocationTypeToString(def->memory));
+ virBufferEscapeString(&buf, " file='%s'", def->file);
+ virBufferAddLit(&buf, "/>\n");
+ }
+ if (def->ndisks) {
virBufferAddLit(&buf, " <disks>\n");
for (i = 0; i < def->ndisks; i++) {
virDomainSnapshotDiskDefPtr disk = &def->disks[i];
long long creationTime; /* in seconds */
int state; /* enum virDomainSnapshotState */
+ int memory; /* enum virDomainMemorySnapshot */
+ char *file; /* memory state file when snapshot is external */
+
size_t ndisks; /* should not exceed dom->ndisks */
virDomainSnapshotDiskDef *disks;
VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE = 1 << 0,
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS = 1 << 1,
VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL = 1 << 2,
+ VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE = 1 << 3,
} virDomainSnapshotParseFlags;
virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
--- /dev/null
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <memory snapshot='external' file='/dev/HostVG/GuestMemory'/>
+ <parent>
+ <name>earlier_snap</name>
+ </parent>
+ <creationTime>1272917631</creationTime>
+</domainsnapshot>
--- /dev/null
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+ <domain>
+ <uuid>9d37b878-a7cc-9f9a-b78f-49b3abad25a8</uuid>
+ </domain>
+</domainsnapshot>
<name>earlier_snap</name>
</parent>
<creationTime>1272917631</creationTime>
+ <memory snapshot='internal'/>
<domain>
<uuid>9d37b878-a7cc-9f9a-b78f-49b3abad25a8</uuid>
</domain>
<name>earlier_snap</name>
</parent>
<creationTime>1272917631</creationTime>
+ <memory snapshot='no'/>
<disks>
<disk name='hda' snapshot='no'/>
<disk name='hdb' snapshot='no'/>
--- /dev/null
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <parent>
+ <name>earlier_snap</name>
+ </parent>
+ <creationTime>1272917631</creationTime>
+ <memory snapshot='external' file='/dev/HostVG/GuestMemory'/>
+ <disks>
+ <disk name='hda' snapshot='no'/>
+ </disks>
+ <domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <metadata>
+ <app1:foo xmlns:app1="http://foo.org/">fooish</app1:foo>
+ <app2:bar xmlns:app2="http://bar.com/" maman="baz">barish</app2:bar>
+ </metadata>
+ <memory unit='KiB'>219100</memory>
+ <currentMemory unit='KiB'>219100</currentMemory>
+ <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu</emulator>
+ <disk type='block' device='disk' snapshot='no'>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <memballoon model='virtio'/>
+ </devices>
+ </domain>
+</domainsnapshot>
<name>earlier_snap</name>
</parent>
<creationTime>1272917631</creationTime>
+ <memory snapshot='internal'/>
<domain type='qemu'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<name>earlier_snap</name>
</parent>
<creationTime>1272917631</creationTime>
+ <memory snapshot='internal'/>
<domain type='qemu'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<description>!@#$%^</description>
<state>running</state>
<creationTime>1272917631</creationTime>
+ <memory snapshot='internal'/>
<domain>
<uuid>9d37b878-a7cc-9f9a-b78f-49b3abad25a8</uuid>
</domain>
<description>!@#$%^</description>
<state>running</state>
<creationTime>1272917631</creationTime>
+ <memory snapshot='internal'/>
<active>1</active>
</domainsnapshot>
<description>!@#$%^</description>
<state>running</state>
<creationTime>1272917631</creationTime>
+ <memory snapshot='no'/>
</domainsnapshot>
DO_TEST("noparent_nodescription", NULL, 1);
DO_TEST("noparent", "9d37b878-a7cc-9f9a-b78f-49b3abad25a8", 0);
DO_TEST("metadata", "c7a5fdbd-edaf-9455-926a-d65c16db1809", 0);
+ DO_TEST("external_vm", "c7a5fdbd-edaf-9455-926a-d65c16db1809", 0);
virCapabilitiesFree(driver.caps);