2012-08-13 18:09:12 -06:00
|
|
|
/*
|
|
|
|
* snapshot_conf.c: domain snapshot XML processing
|
|
|
|
*
|
2019-02-15 14:43:43 -06:00
|
|
|
* Copyright (C) 2006-2019 Red Hat, Inc.
|
2012-08-13 18:09:12 -06:00
|
|
|
* Copyright (C) 2006-2008 Daniel P. Berrange
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 16:30:55 -06:00
|
|
|
* License along with this library. If not, see
|
2012-08-13 18:09:12 -06:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2019-07-05 22:02:03 -05:00
|
|
|
#include "configmake.h"
|
2012-08-13 18:09:12 -06:00
|
|
|
#include "internal.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2012-08-13 18:09:12 -06:00
|
|
|
#include "domain_conf.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-08-13 18:09:12 -06:00
|
|
|
#include "snapshot_conf.h"
|
2021-01-21 15:44:53 +01:00
|
|
|
#include "storage_source_conf.h"
|
2012-12-13 18:01:25 +00:00
|
|
|
#include "viruuid.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-13 18:13:21 +00:00
|
|
|
#include "virxml.h"
|
2019-03-14 21:19:18 -05:00
|
|
|
#include "virdomainsnapshotobjlist.h"
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2018-09-04 15:42:42 +02:00
|
|
|
#define LIBVIRT_SNAPSHOT_CONF_PRIV_H_ALLOW
|
|
|
|
#include "snapshot_conf_priv.h"
|
|
|
|
|
2012-08-13 18:09:12 -06:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("conf.snapshot_conf");
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static virClass *virDomainSnapshotDefClass;
|
2019-05-09 09:59:06 -05:00
|
|
|
static void virDomainSnapshotDefDispose(void *obj);
|
|
|
|
|
|
|
|
static int
|
|
|
|
virDomainSnapshotOnceInit(void)
|
|
|
|
{
|
|
|
|
if (!VIR_CLASS_NEW(virDomainSnapshotDef, virClassForDomainMomentDef()))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virDomainSnapshot);
|
|
|
|
|
2012-08-13 18:09:12 -06:00
|
|
|
/* virDomainSnapshotState is really virDomainState plus one extra state */
|
2019-03-16 14:20:32 -04:00
|
|
|
VIR_ENUM_IMPL(virDomainSnapshotState,
|
|
|
|
VIR_DOMAIN_SNAPSHOT_LAST,
|
2012-08-13 18:09:12 -06:00
|
|
|
"nostate",
|
|
|
|
"running",
|
|
|
|
"blocked",
|
|
|
|
"paused",
|
|
|
|
"shutdown",
|
|
|
|
"shutoff",
|
|
|
|
"crashed",
|
|
|
|
"pmsuspended",
|
2019-01-20 11:30:15 -05:00
|
|
|
"disk-snapshot",
|
|
|
|
);
|
2012-08-13 18:09:12 -06:00
|
|
|
|
|
|
|
/* Snapshot Def functions */
|
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDef *disk)
|
2012-08-13 18:09:12 -06:00
|
|
|
{
|
|
|
|
VIR_FREE(disk->name);
|
2022-01-28 18:42:45 +01:00
|
|
|
g_clear_pointer(&disk->src, virObjectUnref);
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2018-09-04 16:02:43 +02:00
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDiskDefFree(virDomainSnapshotDiskDef *disk)
|
2018-09-04 16:02:43 +02:00
|
|
|
{
|
|
|
|
if (!disk)
|
|
|
|
return;
|
|
|
|
|
|
|
|
virDomainSnapshotDiskDefClear(disk);
|
2021-01-30 14:05:50 -05:00
|
|
|
g_free(disk);
|
2018-09-04 16:02:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-09 09:59:06 -05:00
|
|
|
/* Allocate a new virDomainSnapshotDef; free with virObjectUnref() */
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDef *
|
2019-05-08 13:30:38 -05:00
|
|
|
virDomainSnapshotDefNew(void)
|
|
|
|
{
|
2019-05-09 09:59:06 -05:00
|
|
|
if (virDomainSnapshotInitialize() < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2019-10-17 10:10:10 +02:00
|
|
|
return virObjectNew(virDomainSnapshotDefClass);
|
2019-05-08 13:30:38 -05:00
|
|
|
}
|
|
|
|
|
2019-05-09 09:59:06 -05:00
|
|
|
static void
|
|
|
|
virDomainSnapshotDefDispose(void *obj)
|
2012-08-13 18:09:12 -06:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDef *def = obj;
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2021-06-16 16:54:18 +02:00
|
|
|
g_free(def->memorysnapshotfile);
|
2012-08-13 18:09:12 -06:00
|
|
|
for (i = 0; i < def->ndisks; i++)
|
|
|
|
virDomainSnapshotDiskDefClear(&def->disks[i]);
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(def->disks);
|
2017-06-02 00:44:46 +02:00
|
|
|
virObjectUnref(def->cookie);
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2018-09-04 15:42:42 +02:00
|
|
|
int
|
2012-08-13 18:09:12 -06:00
|
|
|
virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
|
2014-11-11 11:35:25 +01:00
|
|
|
xmlXPathContextPtr ctxt,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDiskDef *def,
|
2017-12-12 17:55:03 +01:00
|
|
|
unsigned int flags,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainXMLOption *xmlopt)
|
2012-08-13 18:09:12 -06:00
|
|
|
{
|
2022-03-08 09:11:15 +01:00
|
|
|
g_autofree char *driver = NULL;
|
|
|
|
g_autofree char *name = NULL;
|
|
|
|
g_autoptr(virStorageSource) src = virStorageSourceNew();
|
2012-08-13 18:09:12 -06:00
|
|
|
xmlNodePtr cur;
|
2020-07-28 21:47:48 +02:00
|
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt)
|
2015-04-10 11:55:43 +02:00
|
|
|
|
|
|
|
ctxt->node = node;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2022-03-08 09:11:15 +01:00
|
|
|
if (!(name = virXMLPropString(node, "name"))) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing name from disk snapshot element"));
|
2022-03-08 09:11:15 +01:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2022-03-09 15:52:45 +01:00
|
|
|
if (virXMLPropEnumDefault(node, "snapshot",
|
|
|
|
virDomainSnapshotLocationTypeFromString,
|
|
|
|
VIR_XML_PROP_NONZERO,
|
|
|
|
&def->snapshot,
|
|
|
|
VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT) < 0)
|
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2022-03-08 15:02:29 +01:00
|
|
|
if (virXMLPropEnumDefault(node, "type",
|
|
|
|
virStorageTypeFromString,
|
|
|
|
VIR_XML_PROP_NONZERO,
|
|
|
|
&src->type,
|
|
|
|
VIR_STORAGE_TYPE_FILE) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (src->type == VIR_STORAGE_TYPE_VOLUME ||
|
|
|
|
src->type == VIR_STORAGE_TYPE_DIR) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("unsupported disk snapshot type '%s'"),
|
|
|
|
virStorageTypeToString(src->type));
|
|
|
|
return -1;
|
2013-11-12 14:15:51 +01:00
|
|
|
}
|
2013-11-12 11:37:04 +01:00
|
|
|
|
2022-12-05 13:03:32 +01:00
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
|
|
|
def->snapshotDeleteInProgress = !!virXPathNode("./snapshotDeleteInProgress",
|
|
|
|
ctxt);
|
|
|
|
}
|
|
|
|
|
2015-04-10 11:55:43 +02:00
|
|
|
if ((cur = virXPathNode("./source", ctxt)) &&
|
2022-03-08 09:11:15 +01:00
|
|
|
virDomainStorageSourceParse(cur, ctxt, src, flags, xmlopt) < 0)
|
|
|
|
return -1;
|
2015-04-10 11:55:43 +02:00
|
|
|
|
2018-09-04 15:48:48 +02:00
|
|
|
if ((driver = virXPathString("string(./driver/@type)", ctxt)) &&
|
2022-03-08 09:11:15 +01:00
|
|
|
(src->format = virStorageFileFormatTypeFromString(driver)) <= 0) {
|
2015-04-10 11:55:43 +02:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
2018-09-04 15:48:48 +02:00
|
|
|
_("unknown disk snapshot driver '%s'"), driver);
|
2022-03-08 09:11:15 +01:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2021-01-07 15:30:21 +01:00
|
|
|
if (virParseScaledValue("./driver/metadata_cache/max_size", NULL,
|
|
|
|
ctxt,
|
2022-03-08 09:11:15 +01:00
|
|
|
&src->metadataCacheMaxSize,
|
2021-01-07 15:30:21 +01:00
|
|
|
1, ULLONG_MAX, false) < 0)
|
2022-03-08 09:11:15 +01:00
|
|
|
return -1;
|
2021-01-07 15:30:21 +01:00
|
|
|
|
2014-08-06 15:17:00 +02:00
|
|
|
/* validate that the passed path is absolute */
|
2022-03-08 09:11:15 +01:00
|
|
|
if (virStorageSourceIsRelative(src)) {
|
2014-08-06 15:17:00 +02:00
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("disk snapshot image path '%s' must be absolute"),
|
2022-03-08 09:11:15 +01:00
|
|
|
src->path);
|
|
|
|
return -1;
|
2014-08-06 15:17:00 +02:00
|
|
|
}
|
|
|
|
|
2022-03-09 15:52:45 +01:00
|
|
|
if (def->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT &&
|
|
|
|
(src->path || src->format))
|
2012-08-13 16:59:57 -06:00
|
|
|
def->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2022-03-08 09:11:15 +01:00
|
|
|
def->name = g_steal_pointer(&name);
|
|
|
|
def->src = g_steal_pointer(&src);
|
2015-04-10 11:55:43 +02:00
|
|
|
|
2022-03-08 09:11:15 +01:00
|
|
|
return 0;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* flags is bitwise-or of virDomainSnapshotParseFlags.
|
2019-11-27 13:10:21 +00:00
|
|
|
* If flags does not include
|
snapshot: Drop virDomainSnapshotDef.current
The only use for the 'current' member of virDomainSnapshotDef was with
the PARSE/FORMAT_INTERNAL flag for controlling an internal-use
<active> element marking whether a particular snapshot definition was
current, and even then, only by the qemu driver on output, and by qemu
and test driver on input. But this duplicates vm->snapshot_current,
and gets in the way of potential simplifications to have qemu store a
single file for all snapshots rather than one file per snapshot. Get
rid of the member by adding a bool* parameter during parse (ignored if
the PARSE_INTERNAL flag is not set), and by adding a new flag during
format (if FORMAT_INTERNAL is set, the value printed in <active>
depends on the new FORMAT_CURRENT).
Then update the qemu driver accordingly, which involves hoisting
assignments to vm->current_snapshot to occur prior to any point where
a snapshot XML file is written (although qemu kept
vm->current_snapshot and snapshot->def_current in sync by the end of
the function, they were not always identical in the middle of
functions, so the shuffling gets a bit interesting). Later patches
will clean up some of that confusing churn to vm->current_snapshot.
Note: even if later patches refactor qemu to no longer use
FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we
will always need PARSE_INTERNAL for input (because on upgrade, a new
libvirt still has to parse XML left from a previous libvirt).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-18 22:56:19 -05:00
|
|
|
* VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL, then current is ignored.
|
2012-08-13 18:09:12 -06:00
|
|
|
*/
|
2022-09-22 17:33:56 +02:00
|
|
|
virDomainSnapshotDef *
|
2013-08-07 10:34:40 -04:00
|
|
|
virDomainSnapshotDefParse(xmlXPathContextPtr ctxt,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainXMLOption *xmlopt,
|
2019-08-06 14:19:35 +02:00
|
|
|
void *parseOpaque,
|
snapshot: Drop virDomainSnapshotDef.current
The only use for the 'current' member of virDomainSnapshotDef was with
the PARSE/FORMAT_INTERNAL flag for controlling an internal-use
<active> element marking whether a particular snapshot definition was
current, and even then, only by the qemu driver on output, and by qemu
and test driver on input. But this duplicates vm->snapshot_current,
and gets in the way of potential simplifications to have qemu store a
single file for all snapshots rather than one file per snapshot. Get
rid of the member by adding a bool* parameter during parse (ignored if
the PARSE_INTERNAL flag is not set), and by adding a new flag during
format (if FORMAT_INTERNAL is set, the value printed in <active>
depends on the new FORMAT_CURRENT).
Then update the qemu driver accordingly, which involves hoisting
assignments to vm->current_snapshot to occur prior to any point where
a snapshot XML file is written (although qemu kept
vm->current_snapshot and snapshot->def_current in sync by the end of
the function, they were not always identical in the middle of
functions, so the shuffling gets a bit interesting). Later patches
will clean up some of that confusing churn to vm->current_snapshot.
Note: even if later patches refactor qemu to no longer use
FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we
will always need PARSE_INTERNAL for input (because on upgrade, a new
libvirt still has to parse XML left from a previous libvirt).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-18 22:56:19 -05:00
|
|
|
bool *current,
|
2013-08-07 10:34:40 -04:00
|
|
|
unsigned int flags)
|
2012-08-13 18:09:12 -06:00
|
|
|
{
|
2022-03-08 18:32:36 +01:00
|
|
|
g_autoptr(virDomainSnapshotDef) def = NULL;
|
|
|
|
g_autofree xmlNodePtr *diskNodes = NULL;
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
|
|
|
int n;
|
2022-03-09 15:52:45 +01:00
|
|
|
xmlNodePtr memoryNode = NULL;
|
snapshot: new XML for external system checkpoint
Each <domainsnapshot> can now contain an optional <memory>
element that describes how the VM state was handled, similar
to disk snapshots. The new element will always appear in
output; for back-compat, an input that lacks the element will
assume 'no' or 'internal' according to the domain state.
Along with this change, it is now possible to pass <disks> in
the XML for an offline snapshot; this also needs to be wired up
in a future patch, to make it possible to choose internal vs.
external on a per-disk basis for each disk in an offline domain.
At that point, using the --disk-only flag for an offline domain
will be able to work.
For some examples below, remember that qemu supports the
following snapshot actions:
qemu-img: offline external and internal disk
savevm: online internal VM and disk
migrate: online external VM
transaction: online external disk
=====
<domainsnapshot>
<memory snapshot='no'/>
...
</domainsnapshot>
implies that there is no VM state saved (mandatory for
offline and disk-only snapshots, not possible otherwise);
using qemu-img for offline domains and transaction for online.
=====
<domainsnapshot>
<memory snapshot='internal'/>
...
</domainsnapshot>
state is saved inside one of the disks (as in qemu's 'savevm'
system checkpoint implementation). If needed in the future,
we can also add an attribute pointing out _which_ disk saved
the internal state; maybe disk='vda'.
=====
<domainsnapshot>
<memory snapshot='external' file='/path/to/state'/>
...
</domainsnapshot>
This is not wired up yet, but future patches will allow this to
control a combination of 'virsh save /path/to/state' plus disk
snapshots from the same point in time.
=====
So for 1.0.1 (and later, as needed), I plan to implement this table
of combinations, with '*' designating new code and '+' designating
existing code reached through new combinations of xml and/or the
existing DISK_ONLY flag:
domain memory disk disk-only | result
-----------------------------------------
offline omit omit any | memory=no disk=int, via qemu-img
offline no omit any |+memory=no disk=int, via qemu-img
offline omit/no no any | invalid combination (nothing to snapshot)
offline omit/no int any |+memory=no disk=int, via qemu-img
offline omit/no ext any |*memory=no disk=ext, via qemu-img
offline int/ext any any | invalid combination (no memory to save)
online omit omit off | memory=int disk=int, via savevm
online omit omit on | memory=no disk=default, via transaction
online omit no/ext off | unsupported for now
online omit no on | invalid combination (nothing to snapshot)
online omit ext on | memory=no disk=ext, via transaction
online omit int off |+memory=int disk=int, via savevm
online omit int on | unsupported for now
online no omit any |+memory=no disk=default, via transaction
online no no any | invalid combination (nothing to snapshot)
online no int any | unsupported for now
online no ext any |+memory=no disk=ext, via transaction
online int/ext any on | invalid combination (disk-only vs. memory)
online int omit off |+memory=int disk=int, via savevm
online int no/ext off | unsupported for now
online int int off |+memory=int disk=int, via savevm
online ext omit off |*memory=ext disk=default, via migrate+trans
online ext no off |+memory=ext disk=no, via migrate
online ext int off | unsupported for now
online ext ext off |*memory=ext disk=ext, via migrate+transaction
* docs/schemas/domainsnapshot.rng (memory): New RNG element.
* docs/formatsnapshot.html.in: Document it.
* src/conf/snapshot_conf.h (virDomainSnapshotDef): New fields.
* src/conf/domain_conf.c (virDomainSnapshotDefFree)
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Manage new fields.
* tests/domainsnapshotxml2xmltest.c: New test.
* tests/domainsnapshotxml2xmlin/*.xml: Update existing tests.
* tests/domainsnapshotxml2xmlout/*.xml: Likewise.
2012-10-23 17:12:23 +02:00
|
|
|
bool offline = !!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE);
|
2021-03-11 08:16:13 +01:00
|
|
|
virSaveCookieCallbacks *saveCookie = virDomainXMLOptionGetSaveCookie(xmlopt);
|
2019-08-29 17:55:43 -03:00
|
|
|
int domainflags = VIR_DOMAIN_DEF_PARSE_INACTIVE |
|
|
|
|
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2019-05-08 13:30:38 -05:00
|
|
|
if (!(def = virDomainSnapshotDefNew()))
|
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.name = virXPathString("string(./name)", ctxt);
|
|
|
|
if (def->parent.name == NULL) {
|
2012-08-13 18:09:12 -06:00
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
|
|
_("a redefined snapshot must have a name"));
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.description = virXPathString("string(./description)", ctxt);
|
2012-08-13 18:09:12 -06:00
|
|
|
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
2022-03-08 18:32:36 +01:00
|
|
|
g_autofree char *state = NULL;
|
|
|
|
g_autofree char *domtype = NULL;
|
|
|
|
xmlNodePtr inactiveDomNode = NULL;
|
|
|
|
|
2012-08-13 18:09:12 -06:00
|
|
|
if (virXPathLongLong("string(./creationTime)", ctxt,
|
2019-05-08 17:10:58 -05:00
|
|
|
&def->parent.creationTime) < 0) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing creationTime from existing snapshot"));
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.parent_name = virXPathString("string(./parent/name)", ctxt);
|
2012-08-13 18:09:12 -06:00
|
|
|
|
|
|
|
state = virXPathString("string(./state)", ctxt);
|
|
|
|
if (state == NULL) {
|
|
|
|
/* there was no state in an existing snapshot; this
|
|
|
|
* should never happen
|
|
|
|
*/
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
|
|
_("missing state from existing snapshot"));
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
def->state = virDomainSnapshotStateTypeFromString(state);
|
snapshot: Don't expose testsuite-only state in snapshot XML
None of the existing drivers actually use the 0-valued 'nostate'
snapshot state; rather, it was a fluke of implementation. In fact,
some drivers, like qemu, actively reject 'nostate' as invalid during a
snapshot redefine. Normally, a driver computes the state post-parse
from the current domain, and thus virDomainSnapshotGetXMLDesc() will
never expose the state. However, since the testsuite lacks any
associated domain to copy state from, and lacks post-parse processing
that normal drivers have, the testsuite output had several spots with
the state, coupled with a regex filter to ignore the oddity.
It is better to follow the lead of other XML defaults, by not
outputting anything during format if post-parse defaults have not been
applied, and rejecting the default value during parsing. The testsuite
needs a bit of an update, by adding another flag for when to simulate
a post-parse action of setting a snapshot state, but none of the
drivers are impacted other than rejecting XML that was previously
already suspicious in nature.
Similarly, don't expose creation time 0 (for now, only possible if a
user redefined a snapshot to claim creation at the Epoch, but also
happens once setting the creation time is deferred to a post-parse
handler).
This is also a step towards cleaning up snapshot_conf.c to separate
its existing post-parse work (namely, setting the creationTime and
default snapshot name) from the pure parsing work, so that we can get
rid of the testsuite hack of regex filtering of the XML and instead
have more accurate testing of our parser/formatter code.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Cole Robinson <crobinso@redhat.com>
2019-04-16 17:44:38 -05:00
|
|
|
if (def->state <= 0) {
|
2014-01-10 17:41:33 +01:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
2012-08-13 18:09:12 -06:00
|
|
|
_("Invalid state '%s' in domain snapshot XML"),
|
|
|
|
state);
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
2019-02-26 14:14:36 -06:00
|
|
|
offline = (def->state == VIR_DOMAIN_SNAPSHOT_SHUTOFF ||
|
|
|
|
def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT);
|
2012-08-13 18:09:12 -06:00
|
|
|
|
|
|
|
/* Older snapshots were created with just <domain>/<uuid>, and
|
|
|
|
* lack domain/@type. In that case, leave dom NULL, and
|
|
|
|
* clients will have to decide between best effort
|
|
|
|
* initialization or outright failure. */
|
2022-03-08 18:32:36 +01:00
|
|
|
if ((domtype = virXPathString("string(./domain/@type)", ctxt))) {
|
2022-09-23 14:38:39 +02:00
|
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt)
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2022-09-23 14:38:39 +02:00
|
|
|
if (!(ctxt->node = virXPathNode("./domain", ctxt))) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing domain in snapshot"));
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
2022-09-23 14:38:39 +02:00
|
|
|
|
|
|
|
def->parent.dom = virDomainDefParseNode(ctxt, xmlopt, parseOpaque,
|
2019-08-06 14:19:35 +02:00
|
|
|
domainflags);
|
2019-05-08 17:10:58 -05:00
|
|
|
if (!def->parent.dom)
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
} else {
|
|
|
|
VIR_WARN("parsing older snapshot that lacks domain");
|
|
|
|
}
|
2019-08-29 17:55:43 -03:00
|
|
|
|
|
|
|
/* /inactiveDomain entry saves the config XML present in a running
|
|
|
|
* VM. In case of absent, leave parent.inactiveDom NULL and use
|
|
|
|
* parent.dom for config and live XML. */
|
|
|
|
if ((inactiveDomNode = virXPathNode("./inactiveDomain", ctxt))) {
|
2022-09-23 14:38:39 +02:00
|
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt)
|
|
|
|
|
|
|
|
ctxt->node = inactiveDomNode;
|
|
|
|
|
|
|
|
def->parent.inactiveDom = virDomainDefParseNode(ctxt, xmlopt, NULL,
|
|
|
|
domainflags);
|
2019-08-29 17:55:43 -03:00
|
|
|
if (!def->parent.inactiveDom)
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2019-08-29 17:55:43 -03:00
|
|
|
}
|
2019-05-08 17:10:58 -05:00
|
|
|
} else if (virDomainXMLOptionRunMomentPostParse(xmlopt, &def->parent) < 0) {
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2022-03-09 15:52:45 +01:00
|
|
|
if ((memoryNode = virXPathNode("./memory", ctxt))) {
|
|
|
|
def->memorysnapshotfile = virXMLPropString(memoryNode, "file");
|
|
|
|
|
|
|
|
if (virXMLPropEnumDefault(memoryNode, "snapshot",
|
|
|
|
virDomainSnapshotLocationTypeFromString,
|
|
|
|
VIR_XML_PROP_NONZERO,
|
|
|
|
&def->memory,
|
|
|
|
VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT) < 0)
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2022-03-04 15:50:19 +01:00
|
|
|
|
|
|
|
if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_MANUAL) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
|
|
_("'manual' memory snapshot mode not supported"));
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-03-09 15:43:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT) {
|
|
|
|
if (def->memorysnapshotfile) {
|
|
|
|
def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
|
|
|
|
} else if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
|
|
|
if (offline) {
|
|
|
|
def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_NO;
|
|
|
|
} else {
|
|
|
|
def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL;
|
|
|
|
}
|
2012-11-15 16:38:13 -07:00
|
|
|
}
|
snapshot: new XML for external system checkpoint
Each <domainsnapshot> can now contain an optional <memory>
element that describes how the VM state was handled, similar
to disk snapshots. The new element will always appear in
output; for back-compat, an input that lacks the element will
assume 'no' or 'internal' according to the domain state.
Along with this change, it is now possible to pass <disks> in
the XML for an offline snapshot; this also needs to be wired up
in a future patch, to make it possible to choose internal vs.
external on a per-disk basis for each disk in an offline domain.
At that point, using the --disk-only flag for an offline domain
will be able to work.
For some examples below, remember that qemu supports the
following snapshot actions:
qemu-img: offline external and internal disk
savevm: online internal VM and disk
migrate: online external VM
transaction: online external disk
=====
<domainsnapshot>
<memory snapshot='no'/>
...
</domainsnapshot>
implies that there is no VM state saved (mandatory for
offline and disk-only snapshots, not possible otherwise);
using qemu-img for offline domains and transaction for online.
=====
<domainsnapshot>
<memory snapshot='internal'/>
...
</domainsnapshot>
state is saved inside one of the disks (as in qemu's 'savevm'
system checkpoint implementation). If needed in the future,
we can also add an attribute pointing out _which_ disk saved
the internal state; maybe disk='vda'.
=====
<domainsnapshot>
<memory snapshot='external' file='/path/to/state'/>
...
</domainsnapshot>
This is not wired up yet, but future patches will allow this to
control a combination of 'virsh save /path/to/state' plus disk
snapshots from the same point in time.
=====
So for 1.0.1 (and later, as needed), I plan to implement this table
of combinations, with '*' designating new code and '+' designating
existing code reached through new combinations of xml and/or the
existing DISK_ONLY flag:
domain memory disk disk-only | result
-----------------------------------------
offline omit omit any | memory=no disk=int, via qemu-img
offline no omit any |+memory=no disk=int, via qemu-img
offline omit/no no any | invalid combination (nothing to snapshot)
offline omit/no int any |+memory=no disk=int, via qemu-img
offline omit/no ext any |*memory=no disk=ext, via qemu-img
offline int/ext any any | invalid combination (no memory to save)
online omit omit off | memory=int disk=int, via savevm
online omit omit on | memory=no disk=default, via transaction
online omit no/ext off | unsupported for now
online omit no on | invalid combination (nothing to snapshot)
online omit ext on | memory=no disk=ext, via transaction
online omit int off |+memory=int disk=int, via savevm
online omit int on | unsupported for now
online no omit any |+memory=no disk=default, via transaction
online no no any | invalid combination (nothing to snapshot)
online no int any | unsupported for now
online no ext any |+memory=no disk=ext, via transaction
online int/ext any on | invalid combination (disk-only vs. memory)
online int omit off |+memory=int disk=int, via savevm
online int no/ext off | unsupported for now
online int int off |+memory=int disk=int, via savevm
online ext omit off |*memory=ext disk=default, via migrate+trans
online ext no off |+memory=ext disk=no, via migrate
online ext int off | unsupported for now
online ext ext off |*memory=ext disk=ext, via migrate+transaction
* docs/schemas/domainsnapshot.rng (memory): New RNG element.
* docs/formatsnapshot.html.in: Document it.
* src/conf/snapshot_conf.h (virDomainSnapshotDef): New fields.
* src/conf/domain_conf.c (virDomainSnapshotDefFree)
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Manage new fields.
* tests/domainsnapshotxml2xmltest.c: New test.
* tests/domainsnapshotxml2xmlin/*.xml: Update existing tests.
* tests/domainsnapshotxml2xmlout/*.xml: Likewise.
2012-10-23 17:12:23 +02:00
|
|
|
}
|
2022-03-09 15:43:56 +01:00
|
|
|
|
|
|
|
if (def->memorysnapshotfile &&
|
|
|
|
def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("memory filename '%s' requires external snapshot"),
|
|
|
|
def->memorysnapshotfile);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!def->memorysnapshotfile &&
|
|
|
|
def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
|
|
_("external memory snapshots require a filename"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offline &&
|
|
|
|
def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT &&
|
2022-03-08 15:42:38 +01:00
|
|
|
def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_NO) {
|
snapshot: new XML for external system checkpoint
Each <domainsnapshot> can now contain an optional <memory>
element that describes how the VM state was handled, similar
to disk snapshots. The new element will always appear in
output; for back-compat, an input that lacks the element will
assume 'no' or 'internal' according to the domain state.
Along with this change, it is now possible to pass <disks> in
the XML for an offline snapshot; this also needs to be wired up
in a future patch, to make it possible to choose internal vs.
external on a per-disk basis for each disk in an offline domain.
At that point, using the --disk-only flag for an offline domain
will be able to work.
For some examples below, remember that qemu supports the
following snapshot actions:
qemu-img: offline external and internal disk
savevm: online internal VM and disk
migrate: online external VM
transaction: online external disk
=====
<domainsnapshot>
<memory snapshot='no'/>
...
</domainsnapshot>
implies that there is no VM state saved (mandatory for
offline and disk-only snapshots, not possible otherwise);
using qemu-img for offline domains and transaction for online.
=====
<domainsnapshot>
<memory snapshot='internal'/>
...
</domainsnapshot>
state is saved inside one of the disks (as in qemu's 'savevm'
system checkpoint implementation). If needed in the future,
we can also add an attribute pointing out _which_ disk saved
the internal state; maybe disk='vda'.
=====
<domainsnapshot>
<memory snapshot='external' file='/path/to/state'/>
...
</domainsnapshot>
This is not wired up yet, but future patches will allow this to
control a combination of 'virsh save /path/to/state' plus disk
snapshots from the same point in time.
=====
So for 1.0.1 (and later, as needed), I plan to implement this table
of combinations, with '*' designating new code and '+' designating
existing code reached through new combinations of xml and/or the
existing DISK_ONLY flag:
domain memory disk disk-only | result
-----------------------------------------
offline omit omit any | memory=no disk=int, via qemu-img
offline no omit any |+memory=no disk=int, via qemu-img
offline omit/no no any | invalid combination (nothing to snapshot)
offline omit/no int any |+memory=no disk=int, via qemu-img
offline omit/no ext any |*memory=no disk=ext, via qemu-img
offline int/ext any any | invalid combination (no memory to save)
online omit omit off | memory=int disk=int, via savevm
online omit omit on | memory=no disk=default, via transaction
online omit no/ext off | unsupported for now
online omit no on | invalid combination (nothing to snapshot)
online omit ext on | memory=no disk=ext, via transaction
online omit int off |+memory=int disk=int, via savevm
online omit int on | unsupported for now
online no omit any |+memory=no disk=default, via transaction
online no no any | invalid combination (nothing to snapshot)
online no int any | unsupported for now
online no ext any |+memory=no disk=ext, via transaction
online int/ext any on | invalid combination (disk-only vs. memory)
online int omit off |+memory=int disk=int, via savevm
online int no/ext off | unsupported for now
online int int off |+memory=int disk=int, via savevm
online ext omit off |*memory=ext disk=default, via migrate+trans
online ext no off |+memory=ext disk=no, via migrate
online ext int off | unsupported for now
online ext ext off |*memory=ext disk=ext, via migrate+transaction
* docs/schemas/domainsnapshot.rng (memory): New RNG element.
* docs/formatsnapshot.html.in: Document it.
* src/conf/snapshot_conf.h (virDomainSnapshotDef): New fields.
* src/conf/domain_conf.c (virDomainSnapshotDefFree)
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Manage new fields.
* tests/domainsnapshotxml2xmltest.c: New test.
* tests/domainsnapshotxml2xmlin/*.xml: Update existing tests.
* tests/domainsnapshotxml2xmlout/*.xml: Likewise.
2012-10-23 17:12:23 +02:00
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
2013-01-25 11:50:43 +01:00
|
|
|
_("memory state cannot be saved with offline or "
|
|
|
|
"disk-only snapshot"));
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
snapshot: new XML for external system checkpoint
Each <domainsnapshot> can now contain an optional <memory>
element that describes how the VM state was handled, similar
to disk snapshots. The new element will always appear in
output; for back-compat, an input that lacks the element will
assume 'no' or 'internal' according to the domain state.
Along with this change, it is now possible to pass <disks> in
the XML for an offline snapshot; this also needs to be wired up
in a future patch, to make it possible to choose internal vs.
external on a per-disk basis for each disk in an offline domain.
At that point, using the --disk-only flag for an offline domain
will be able to work.
For some examples below, remember that qemu supports the
following snapshot actions:
qemu-img: offline external and internal disk
savevm: online internal VM and disk
migrate: online external VM
transaction: online external disk
=====
<domainsnapshot>
<memory snapshot='no'/>
...
</domainsnapshot>
implies that there is no VM state saved (mandatory for
offline and disk-only snapshots, not possible otherwise);
using qemu-img for offline domains and transaction for online.
=====
<domainsnapshot>
<memory snapshot='internal'/>
...
</domainsnapshot>
state is saved inside one of the disks (as in qemu's 'savevm'
system checkpoint implementation). If needed in the future,
we can also add an attribute pointing out _which_ disk saved
the internal state; maybe disk='vda'.
=====
<domainsnapshot>
<memory snapshot='external' file='/path/to/state'/>
...
</domainsnapshot>
This is not wired up yet, but future patches will allow this to
control a combination of 'virsh save /path/to/state' plus disk
snapshots from the same point in time.
=====
So for 1.0.1 (and later, as needed), I plan to implement this table
of combinations, with '*' designating new code and '+' designating
existing code reached through new combinations of xml and/or the
existing DISK_ONLY flag:
domain memory disk disk-only | result
-----------------------------------------
offline omit omit any | memory=no disk=int, via qemu-img
offline no omit any |+memory=no disk=int, via qemu-img
offline omit/no no any | invalid combination (nothing to snapshot)
offline omit/no int any |+memory=no disk=int, via qemu-img
offline omit/no ext any |*memory=no disk=ext, via qemu-img
offline int/ext any any | invalid combination (no memory to save)
online omit omit off | memory=int disk=int, via savevm
online omit omit on | memory=no disk=default, via transaction
online omit no/ext off | unsupported for now
online omit no on | invalid combination (nothing to snapshot)
online omit ext on | memory=no disk=ext, via transaction
online omit int off |+memory=int disk=int, via savevm
online omit int on | unsupported for now
online no omit any |+memory=no disk=default, via transaction
online no no any | invalid combination (nothing to snapshot)
online no int any | unsupported for now
online no ext any |+memory=no disk=ext, via transaction
online int/ext any on | invalid combination (disk-only vs. memory)
online int omit off |+memory=int disk=int, via savevm
online int no/ext off | unsupported for now
online int int off |+memory=int disk=int, via savevm
online ext omit off |*memory=ext disk=default, via migrate+trans
online ext no off |+memory=ext disk=no, via migrate
online ext int off | unsupported for now
online ext ext off |*memory=ext disk=ext, via migrate+transaction
* docs/schemas/domainsnapshot.rng (memory): New RNG element.
* docs/formatsnapshot.html.in: Document it.
* src/conf/snapshot_conf.h (virDomainSnapshotDef): New fields.
* src/conf/domain_conf.c (virDomainSnapshotDefFree)
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Manage new fields.
* tests/domainsnapshotxml2xmltest.c: New test.
* tests/domainsnapshotxml2xmlin/*.xml: Update existing tests.
* tests/domainsnapshotxml2xmlout/*.xml: Likewise.
2012-10-23 17:12:23 +02:00
|
|
|
}
|
|
|
|
|
2014-08-06 15:17:00 +02:00
|
|
|
/* verify that memory path is absolute */
|
2021-06-16 16:54:18 +02:00
|
|
|
if (def->memorysnapshotfile && !g_path_is_absolute(def->memorysnapshotfile)) {
|
2014-08-06 15:17:00 +02:00
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("memory snapshot file path (%s) must be absolute"),
|
2021-06-16 16:54:18 +02:00
|
|
|
def->memorysnapshotfile);
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2014-08-06 15:17:00 +02:00
|
|
|
}
|
|
|
|
|
2022-03-08 18:32:36 +01:00
|
|
|
if ((n = virXPathNodeSet("./disks/*", ctxt, &diskNodes)) < 0)
|
|
|
|
return NULL;
|
2022-03-08 18:20:46 +01:00
|
|
|
if (n)
|
|
|
|
def->disks = g_new0(virDomainSnapshotDiskDef, n);
|
|
|
|
def->ndisks = n;
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
2022-03-08 18:32:36 +01:00
|
|
|
if (virDomainSnapshotDiskDefParseXML(diskNodes[i], ctxt, &def->disks[i],
|
2022-03-08 18:20:46 +01:00
|
|
|
flags, xmlopt) < 0)
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL) {
|
2022-03-08 18:32:36 +01:00
|
|
|
int active;
|
|
|
|
|
snapshot: Drop virDomainSnapshotDef.current
The only use for the 'current' member of virDomainSnapshotDef was with
the PARSE/FORMAT_INTERNAL flag for controlling an internal-use
<active> element marking whether a particular snapshot definition was
current, and even then, only by the qemu driver on output, and by qemu
and test driver on input. But this duplicates vm->snapshot_current,
and gets in the way of potential simplifications to have qemu store a
single file for all snapshots rather than one file per snapshot. Get
rid of the member by adding a bool* parameter during parse (ignored if
the PARSE_INTERNAL flag is not set), and by adding a new flag during
format (if FORMAT_INTERNAL is set, the value printed in <active>
depends on the new FORMAT_CURRENT).
Then update the qemu driver accordingly, which involves hoisting
assignments to vm->current_snapshot to occur prior to any point where
a snapshot XML file is written (although qemu kept
vm->current_snapshot and snapshot->def_current in sync by the end of
the function, they were not always identical in the middle of
functions, so the shuffling gets a bit interesting). Later patches
will clean up some of that confusing churn to vm->current_snapshot.
Note: even if later patches refactor qemu to no longer use
FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we
will always need PARSE_INTERNAL for input (because on upgrade, a new
libvirt still has to parse XML left from a previous libvirt).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-18 22:56:19 -05:00
|
|
|
if (!current) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("internal parse requested with NULL current"));
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
snapshot: Drop virDomainSnapshotDef.current
The only use for the 'current' member of virDomainSnapshotDef was with
the PARSE/FORMAT_INTERNAL flag for controlling an internal-use
<active> element marking whether a particular snapshot definition was
current, and even then, only by the qemu driver on output, and by qemu
and test driver on input. But this duplicates vm->snapshot_current,
and gets in the way of potential simplifications to have qemu store a
single file for all snapshots rather than one file per snapshot. Get
rid of the member by adding a bool* parameter during parse (ignored if
the PARSE_INTERNAL flag is not set), and by adding a new flag during
format (if FORMAT_INTERNAL is set, the value printed in <active>
depends on the new FORMAT_CURRENT).
Then update the qemu driver accordingly, which involves hoisting
assignments to vm->current_snapshot to occur prior to any point where
a snapshot XML file is written (although qemu kept
vm->current_snapshot and snapshot->def_current in sync by the end of
the function, they were not always identical in the middle of
functions, so the shuffling gets a bit interesting). Later patches
will clean up some of that confusing churn to vm->current_snapshot.
Note: even if later patches refactor qemu to no longer use
FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we
will always need PARSE_INTERNAL for input (because on upgrade, a new
libvirt still has to parse XML left from a previous libvirt).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-18 22:56:19 -05:00
|
|
|
}
|
2012-08-13 18:09:12 -06:00
|
|
|
if (virXPathInt("string(./active)", ctxt, &active) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Could not find 'active' element"));
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
snapshot: Drop virDomainSnapshotDef.current
The only use for the 'current' member of virDomainSnapshotDef was with
the PARSE/FORMAT_INTERNAL flag for controlling an internal-use
<active> element marking whether a particular snapshot definition was
current, and even then, only by the qemu driver on output, and by qemu
and test driver on input. But this duplicates vm->snapshot_current,
and gets in the way of potential simplifications to have qemu store a
single file for all snapshots rather than one file per snapshot. Get
rid of the member by adding a bool* parameter during parse (ignored if
the PARSE_INTERNAL flag is not set), and by adding a new flag during
format (if FORMAT_INTERNAL is set, the value printed in <active>
depends on the new FORMAT_CURRENT).
Then update the qemu driver accordingly, which involves hoisting
assignments to vm->current_snapshot to occur prior to any point where
a snapshot XML file is written (although qemu kept
vm->current_snapshot and snapshot->def_current in sync by the end of
the function, they were not always identical in the middle of
functions, so the shuffling gets a bit interesting). Later patches
will clean up some of that confusing churn to vm->current_snapshot.
Note: even if later patches refactor qemu to no longer use
FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we
will always need PARSE_INTERNAL for input (because on upgrade, a new
libvirt still has to parse XML left from a previous libvirt).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-18 22:56:19 -05:00
|
|
|
*current = active != 0;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2017-06-02 00:44:46 +02:00
|
|
|
if (!offline && virSaveCookieParse(ctxt, &def->cookie, saveCookie) < 0)
|
2022-03-08 18:32:36 +01:00
|
|
|
return NULL;
|
2013-08-07 10:34:40 -04:00
|
|
|
|
2022-03-08 18:32:36 +01:00
|
|
|
return g_steal_pointer(&def);
|
2013-08-07 10:34:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDef *
|
2013-08-07 10:34:40 -04:00
|
|
|
virDomainSnapshotDefParseString(const char *xmlStr,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainXMLOption *xmlopt,
|
2019-08-06 14:19:35 +02:00
|
|
|
void *parseOpaque,
|
snapshot: Drop virDomainSnapshotDef.current
The only use for the 'current' member of virDomainSnapshotDef was with
the PARSE/FORMAT_INTERNAL flag for controlling an internal-use
<active> element marking whether a particular snapshot definition was
current, and even then, only by the qemu driver on output, and by qemu
and test driver on input. But this duplicates vm->snapshot_current,
and gets in the way of potential simplifications to have qemu store a
single file for all snapshots rather than one file per snapshot. Get
rid of the member by adding a bool* parameter during parse (ignored if
the PARSE_INTERNAL flag is not set), and by adding a new flag during
format (if FORMAT_INTERNAL is set, the value printed in <active>
depends on the new FORMAT_CURRENT).
Then update the qemu driver accordingly, which involves hoisting
assignments to vm->current_snapshot to occur prior to any point where
a snapshot XML file is written (although qemu kept
vm->current_snapshot and snapshot->def_current in sync by the end of
the function, they were not always identical in the middle of
functions, so the shuffling gets a bit interesting). Later patches
will clean up some of that confusing churn to vm->current_snapshot.
Note: even if later patches refactor qemu to no longer use
FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we
will always need PARSE_INTERNAL for input (because on upgrade, a new
libvirt still has to parse XML left from a previous libvirt).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-18 22:56:19 -05:00
|
|
|
bool *current,
|
2013-08-07 10:34:40 -04:00
|
|
|
unsigned int flags)
|
|
|
|
{
|
2022-09-22 17:33:56 +02:00
|
|
|
g_autoptr(xmlXPathContext) ctxt = NULL;
|
2021-08-11 13:57:18 +02:00
|
|
|
g_autoptr(xmlDoc) xml = NULL;
|
2013-08-07 10:34:40 -04:00
|
|
|
int keepBlanksDefault = xmlKeepBlanksDefault(0);
|
2022-09-22 14:30:53 +02:00
|
|
|
bool validate = flags & VIR_DOMAIN_SNAPSHOT_PARSE_VALIDATE;
|
2013-08-07 10:34:40 -04:00
|
|
|
|
2022-09-22 17:33:56 +02:00
|
|
|
xml = virXMLParse(NULL, xmlStr, _("(domain_snapshot)"),
|
|
|
|
"domainsnapshot", &ctxt, "domainsnapshot.rng", validate);
|
|
|
|
|
2013-08-07 10:34:40 -04:00
|
|
|
xmlKeepBlanksDefault(keepBlanksDefault);
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2022-09-22 17:33:56 +02:00
|
|
|
if (!xml)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return virDomainSnapshotDefParse(ctxt, xmlopt, parseOpaque, current, flags);
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2016-02-11 09:07:05 +01:00
|
|
|
|
2022-01-12 15:08:25 +01:00
|
|
|
/* Perform sanity checking on a redefined snapshot definition. */
|
2022-01-12 14:28:21 +01:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotRedefineValidate(virDomainSnapshotDef *def,
|
2019-03-07 16:12:24 -06:00
|
|
|
const unsigned char *domain_uuid,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainMomentObj *other,
|
|
|
|
virDomainXMLOption *xmlopt,
|
2019-03-07 16:12:24 -06:00
|
|
|
unsigned int flags)
|
|
|
|
{
|
2022-01-12 11:02:49 +01:00
|
|
|
if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) &&
|
|
|
|
def->state != VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT) {
|
2019-03-07 16:12:24 -06:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("disk-only flag for snapshot %s requires "
|
|
|
|
"disk-snapshot state"),
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.name);
|
2019-03-07 16:12:24 -06:00
|
|
|
return -1;
|
|
|
|
}
|
2019-05-08 17:10:58 -05:00
|
|
|
if (def->parent.dom && memcmp(def->parent.dom->uuid, domain_uuid,
|
2019-03-21 21:02:19 -05:00
|
|
|
VIR_UUID_BUFLEN)) {
|
2019-03-07 16:12:24 -06:00
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
|
|
|
|
virUUIDFormat(domain_uuid, uuidstr);
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("definition for snapshot %s must use uuid %s"),
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.name, uuidstr);
|
2019-03-07 16:12:24 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (other) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDef *otherdef = virDomainSnapshotObjGetDef(other);
|
2019-03-18 16:13:50 -05:00
|
|
|
|
|
|
|
if ((otherdef->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
|
|
|
|
otherdef->state == VIR_DOMAIN_SNAPSHOT_PAUSED) !=
|
2019-03-07 16:12:24 -06:00
|
|
|
(def->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
|
|
|
|
def->state == VIR_DOMAIN_SNAPSHOT_PAUSED)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("cannot change between online and offline "
|
|
|
|
"snapshot state in snapshot %s"),
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.name);
|
2019-03-07 16:12:24 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-03-18 16:13:50 -05:00
|
|
|
if ((otherdef->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT) !=
|
2019-03-07 16:12:24 -06:00
|
|
|
(def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("cannot change between disk only and "
|
|
|
|
"full system in snapshot %s"),
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.name);
|
2019-03-07 16:12:24 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
if (otherdef->parent.dom) {
|
|
|
|
if (def->parent.dom) {
|
|
|
|
if (!virDomainDefCheckABIStability(otherdef->parent.dom,
|
|
|
|
def->parent.dom, xmlopt))
|
2019-03-07 16:12:24 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-11 09:07:05 +01:00
|
|
|
/**
|
|
|
|
* virDomainSnapshotDefAssignExternalNames:
|
|
|
|
* @def: snapshot def object
|
|
|
|
*
|
|
|
|
* Generate default external file names for snapshot targets. Returns 0 on
|
|
|
|
* success, -1 on error.
|
|
|
|
*/
|
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDefAssignExternalNames(virDomainSnapshotDef *def)
|
2016-02-11 09:07:05 +01:00
|
|
|
{
|
2016-02-11 09:42:36 +01:00
|
|
|
const char *origpath;
|
|
|
|
char *tmppath;
|
|
|
|
char *tmp;
|
|
|
|
struct stat sb;
|
2016-02-11 09:07:05 +01:00
|
|
|
size_t i;
|
2016-02-11 09:57:45 +01:00
|
|
|
size_t j;
|
2016-02-11 09:07:05 +01:00
|
|
|
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDiskDef *disk = &def->disks[i];
|
2016-02-11 09:07:05 +01:00
|
|
|
|
2016-02-11 09:42:36 +01:00
|
|
|
if (disk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL ||
|
|
|
|
disk->src->path)
|
|
|
|
continue;
|
2016-02-11 09:07:05 +01:00
|
|
|
|
2016-02-11 09:42:36 +01:00
|
|
|
if (disk->src->type != VIR_STORAGE_TYPE_FILE) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("cannot generate external snapshot name "
|
|
|
|
"for disk '%s' on a '%s' device"),
|
|
|
|
disk->name, virStorageTypeToString(disk->src->type));
|
|
|
|
return -1;
|
|
|
|
}
|
2016-02-11 09:07:05 +01:00
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
if (!(origpath = virDomainDiskGetSource(def->parent.dom->disks[i]))) {
|
2016-02-11 09:42:36 +01:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("cannot generate external snapshot name "
|
|
|
|
"for disk '%s' without source"),
|
|
|
|
disk->name);
|
|
|
|
return -1;
|
2016-02-11 09:07:05 +01:00
|
|
|
}
|
|
|
|
|
2016-02-11 09:42:36 +01:00
|
|
|
if (stat(origpath, &sb) < 0 || !S_ISREG(sb.st_mode)) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("source for disk '%s' is not a regular "
|
|
|
|
"file; refusing to generate external "
|
|
|
|
"snapshot name"),
|
|
|
|
disk->name);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-02-11 09:07:05 +01:00
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
tmppath = g_strdup(origpath);
|
2016-02-11 09:42:36 +01:00
|
|
|
|
|
|
|
/* drop suffix of the file name */
|
|
|
|
if ((tmp = strrchr(tmppath, '.')) && !strchr(tmp, '/'))
|
|
|
|
*tmp = '\0';
|
|
|
|
|
2019-10-22 15:26:14 +02:00
|
|
|
disk->src->path = g_strdup_printf("%s.%s", tmppath, def->parent.name);
|
2016-02-11 09:42:36 +01:00
|
|
|
|
|
|
|
VIR_FREE(tmppath);
|
2016-02-11 09:57:45 +01:00
|
|
|
|
|
|
|
/* verify that we didn't generate a duplicate name */
|
|
|
|
for (j = 0; j < i; j++) {
|
|
|
|
if (STREQ_NULLABLE(disk->src->path, def->disks[j].src->path)) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("cannot generate external snapshot name for "
|
|
|
|
"disk '%s': collision with disk '%s'"),
|
|
|
|
disk->name, def->disks[j].name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2016-02-11 09:42:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2016-02-11 09:07:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-01-12 12:46:19 +01:00
|
|
|
/**
|
|
|
|
* virDomainSnapshotAlignDisks:
|
|
|
|
* @snapdef: Snapshot definition to align
|
2022-01-12 14:20:04 +01:00
|
|
|
* @existingDomainDef: definition of the domain belonging to a redefined snapshot
|
2022-01-12 12:46:19 +01:00
|
|
|
* @default_snapshot: snapshot location to assign to disks which don't have any
|
2022-01-12 13:49:44 +01:00
|
|
|
* @uniform_internal_snapshot: Require that for an internal snapshot all disks
|
|
|
|
* take part in the internal snapshot
|
2022-01-12 12:46:19 +01:00
|
|
|
*
|
2022-01-12 14:20:04 +01:00
|
|
|
* Align snapdef->disks to domain definition, filling in any missing disks or
|
2022-01-12 12:46:19 +01:00
|
|
|
* snapshot state defaults given by the domain, with a fallback to
|
|
|
|
* @default_snapshot. Ensure that there are no duplicate snapshot disk
|
|
|
|
* definitions in @snapdef and there are no disks described in @snapdef but
|
|
|
|
* missing from the domain definition.
|
|
|
|
*
|
2022-01-12 14:20:04 +01:00
|
|
|
* By default the domain definition from @snapdef->parent.dom is used, but when
|
|
|
|
* redefining an existing snapshot the domain definition may be omitted in
|
|
|
|
* @snapdef. In such case callers must pass in the definition from the snapsot
|
|
|
|
* being redefined as @existingDomainDef. In all other cases callers pass NULL.
|
|
|
|
*
|
2022-01-12 13:49:44 +01:00
|
|
|
* When @uniform_internal_snapshot is true and @default_snapshot is
|
|
|
|
* VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL, all disks in @snapdef must take part
|
|
|
|
* in the internal snapshot. This is for hypervisors where granularity of an
|
|
|
|
* internal snapshot can't be controlled.
|
|
|
|
*
|
2022-01-12 12:46:19 +01:00
|
|
|
* Convert paths to disk targets for uniformity.
|
|
|
|
*
|
|
|
|
* On error -1 is returned and a libvirt error is reported.
|
|
|
|
*/
|
2012-08-13 18:09:12 -06:00
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotAlignDisks(virDomainSnapshotDef *snapdef,
|
2022-01-12 14:20:04 +01:00
|
|
|
virDomainDef *existingDomainDef,
|
2022-01-12 12:52:16 +01:00
|
|
|
virDomainSnapshotLocation default_snapshot,
|
2022-01-12 13:49:44 +01:00
|
|
|
bool uniform_internal_snapshot)
|
2012-08-13 18:09:12 -06:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainDef *domdef = snapdef->parent.dom;
|
2020-10-22 19:04:18 +02:00
|
|
|
g_autoptr(GHashTable) map = virHashNew(NULL);
|
2021-03-11 08:16:13 +01:00
|
|
|
g_autofree virDomainSnapshotDiskDef *olddisks = NULL;
|
2022-01-12 13:49:44 +01:00
|
|
|
bool require_match = false;
|
2021-08-23 14:14:55 +02:00
|
|
|
size_t oldndisks;
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2022-01-12 14:20:04 +01:00
|
|
|
if (!domdef)
|
|
|
|
domdef = existingDomainDef;
|
|
|
|
|
2020-09-21 18:27:37 +02:00
|
|
|
if (!domdef) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing domain in snapshot"));
|
2020-09-21 17:06:31 +02:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2020-09-21 18:27:37 +02:00
|
|
|
if (snapdef->ndisks > domdef->ndisks) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("too many disk snapshot requests for domain"));
|
2020-09-21 17:06:31 +02:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2022-01-12 13:49:44 +01:00
|
|
|
if (uniform_internal_snapshot &&
|
|
|
|
default_snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL)
|
|
|
|
require_match = true;
|
|
|
|
|
2012-08-13 18:09:12 -06:00
|
|
|
/* Unlikely to have a guest without disks but technically possible. */
|
2020-09-21 18:27:37 +02:00
|
|
|
if (!domdef->ndisks)
|
2020-09-21 17:06:31 +02:00
|
|
|
return 0;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2021-08-23 14:14:55 +02:00
|
|
|
olddisks = g_steal_pointer(&snapdef->disks);
|
|
|
|
oldndisks = snapdef->ndisks;
|
|
|
|
snapdef->disks = g_new0(virDomainSnapshotDiskDef, domdef->ndisks);
|
|
|
|
snapdef->ndisks = domdef->ndisks;
|
|
|
|
|
2012-08-13 18:09:12 -06:00
|
|
|
/* Double check requested disks. */
|
2021-08-23 14:14:55 +02:00
|
|
|
for (i = 0; i < oldndisks; i++) {
|
|
|
|
virDomainSnapshotDiskDef *snapdisk = &olddisks[i];
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainDiskDef *domdisk = virDomainDiskByName(domdef, snapdisk->name, false);
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2020-12-01 17:55:54 +01:00
|
|
|
if (!domdisk) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
2020-09-21 18:26:03 +02:00
|
|
|
_("no disk named '%s'"), snapdisk->name);
|
2020-09-21 17:06:31 +02:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2020-09-21 19:36:17 +02:00
|
|
|
if (virHashHasEntry(map, domdisk->dst)) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("disk '%s' specified twice"),
|
2020-09-21 18:26:03 +02:00
|
|
|
snapdisk->name);
|
2020-09-21 17:06:31 +02:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
2020-09-21 19:36:17 +02:00
|
|
|
|
|
|
|
if (virHashAddEntry(map, domdisk->dst, snapdisk) < 0)
|
|
|
|
return -1;
|
2012-10-23 17:12:24 +02:00
|
|
|
|
2020-09-21 18:59:37 +02:00
|
|
|
if (snapdisk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT) {
|
|
|
|
if (domdisk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT &&
|
2012-10-23 17:12:24 +02:00
|
|
|
(!require_match ||
|
2022-03-08 15:42:38 +01:00
|
|
|
domdisk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NO)) {
|
2020-09-21 18:59:37 +02:00
|
|
|
snapdisk->snapshot = domdisk->snapshot;
|
|
|
|
} else {
|
2020-09-21 18:26:03 +02:00
|
|
|
snapdisk->snapshot = default_snapshot;
|
2020-09-21 18:59:37 +02:00
|
|
|
}
|
2012-10-23 17:12:24 +02:00
|
|
|
} else if (require_match &&
|
2020-09-21 18:26:03 +02:00
|
|
|
snapdisk->snapshot != default_snapshot &&
|
2022-03-08 15:42:38 +01:00
|
|
|
!(snapdisk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NO &&
|
|
|
|
domdisk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NO)) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("disk '%s' must use snapshot mode '%s'"),
|
2020-09-21 18:58:37 +02:00
|
|
|
snapdisk->name,
|
|
|
|
virDomainSnapshotLocationTypeToString(default_snapshot));
|
2020-09-21 17:06:31 +02:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
2020-09-21 18:59:37 +02:00
|
|
|
|
2020-09-21 18:26:03 +02:00
|
|
|
if (snapdisk->src->path &&
|
|
|
|
snapdisk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
|
2012-08-13 18:09:12 -06:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("file '%s' for disk '%s' requires "
|
|
|
|
"use of external snapshot mode"),
|
2020-09-21 18:26:03 +02:00
|
|
|
snapdisk->src->path, snapdisk->name);
|
2020-09-21 17:06:31 +02:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
2020-09-21 18:51:56 +02:00
|
|
|
if (STRNEQ(snapdisk->name, domdisk->dst)) {
|
2020-09-21 18:26:03 +02:00
|
|
|
VIR_FREE(snapdisk->name);
|
2020-09-21 18:51:56 +02:00
|
|
|
snapdisk->name = g_strdup(domdisk->dst);
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-21 18:27:37 +02:00
|
|
|
for (i = 0; i < domdef->ndisks; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainDiskDef *domdisk = domdef->disks[i];
|
|
|
|
virDomainSnapshotDiskDef *snapdisk = snapdef->disks + i;
|
|
|
|
virDomainSnapshotDiskDef *existing;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2020-09-21 19:36:17 +02:00
|
|
|
/* copy existing disks */
|
|
|
|
if ((existing = virHashLookup(map, domdisk->dst))) {
|
|
|
|
memcpy(snapdisk, existing, sizeof(*snapdisk));
|
2012-08-13 18:09:12 -06:00
|
|
|
continue;
|
2020-09-21 19:36:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Provide defaults for all remaining disks. */
|
2020-09-21 18:26:03 +02:00
|
|
|
snapdisk->src = virStorageSourceNew();
|
2020-09-21 18:27:37 +02:00
|
|
|
snapdisk->name = g_strdup(domdef->disks[i]->dst);
|
2014-09-11 17:45:06 +02:00
|
|
|
|
|
|
|
/* Don't snapshot empty drives */
|
2020-09-21 18:27:37 +02:00
|
|
|
if (virStorageSourceIsEmpty(domdef->disks[i]->src))
|
2022-03-08 15:42:38 +01:00
|
|
|
snapdisk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NO;
|
2014-09-11 17:45:06 +02:00
|
|
|
else
|
2020-09-21 18:27:37 +02:00
|
|
|
snapdisk->snapshot = domdef->disks[i]->snapshot;
|
2014-09-11 17:45:06 +02:00
|
|
|
|
2020-09-21 18:26:03 +02:00
|
|
|
snapdisk->src->type = VIR_STORAGE_TYPE_FILE;
|
|
|
|
if (!snapdisk->snapshot)
|
|
|
|
snapdisk->snapshot = default_snapshot;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2016-02-11 09:07:05 +01:00
|
|
|
/* Generate default external file names for external snapshot locations */
|
2020-09-21 17:23:10 +02:00
|
|
|
if (virDomainSnapshotDefAssignExternalNames(snapdef) < 0)
|
2020-09-21 17:06:31 +02:00
|
|
|
return -1;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2020-09-21 17:06:31 +02:00
|
|
|
return 0;
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2019-02-15 14:43:43 -06:00
|
|
|
|
|
|
|
/* Converts public VIR_DOMAIN_SNAPSHOT_XML_* into
|
|
|
|
* VIR_DOMAIN_SNAPSHOT_FORMAT_* flags, and silently ignores any other
|
|
|
|
* flags. */
|
|
|
|
unsigned int
|
|
|
|
virDomainSnapshotFormatConvertXMLFlags(unsigned int flags)
|
|
|
|
{
|
|
|
|
unsigned int formatFlags = 0;
|
|
|
|
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_XML_SECURE)
|
|
|
|
formatFlags |= VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE;
|
|
|
|
|
|
|
|
return formatFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-08 07:13:20 -05:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDiskDefFormat(virBuffer *buf,
|
|
|
|
virDomainSnapshotDiskDef *disk,
|
|
|
|
virDomainXMLOption *xmlopt)
|
2013-05-16 08:24:56 +02:00
|
|
|
{
|
2021-01-07 15:03:57 +01:00
|
|
|
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
|
|
|
g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
|
2013-11-12 11:37:04 +01:00
|
|
|
|
2013-05-16 08:24:56 +02:00
|
|
|
if (!disk->name)
|
2017-11-08 07:13:20 -05:00
|
|
|
return 0;
|
2013-05-16 08:24:56 +02:00
|
|
|
|
2021-01-07 15:03:57 +01:00
|
|
|
virBufferEscapeString(&attrBuf, " name='%s'", disk->name);
|
2013-05-16 08:24:56 +02:00
|
|
|
if (disk->snapshot > 0)
|
2021-01-07 15:03:57 +01:00
|
|
|
virBufferAsprintf(&attrBuf, " snapshot='%s'",
|
2013-05-16 08:24:56 +02:00
|
|
|
virDomainSnapshotLocationTypeToString(disk->snapshot));
|
2013-11-12 11:37:04 +01:00
|
|
|
|
2022-12-05 13:03:32 +01:00
|
|
|
if (disk->snapshotDeleteInProgress)
|
|
|
|
virBufferAddLit(&childBuf, "<snapshotDeleteInProgress/>\n");
|
|
|
|
|
2021-01-07 15:03:57 +01:00
|
|
|
if (disk->src->path || disk->src->format != 0) {
|
2021-01-07 15:30:21 +01:00
|
|
|
g_auto(virBuffer) driverAttrBuf = VIR_BUFFER_INITIALIZER;
|
|
|
|
g_auto(virBuffer) driverChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf);
|
|
|
|
|
2021-01-07 15:03:57 +01:00
|
|
|
virBufferAsprintf(&attrBuf, " type='%s'", virStorageTypeToString(disk->src->type));
|
2013-05-16 08:24:56 +02:00
|
|
|
|
2021-01-07 15:03:57 +01:00
|
|
|
if (disk->src->format > 0)
|
2021-01-07 15:30:21 +01:00
|
|
|
virBufferEscapeString(&driverAttrBuf, " type='%s'",
|
2021-01-07 15:03:57 +01:00
|
|
|
virStorageFileFormatTypeToString(disk->src->format));
|
2013-05-16 08:24:56 +02:00
|
|
|
|
2021-01-07 15:30:21 +01:00
|
|
|
if (disk->src->metadataCacheMaxSize > 0) {
|
|
|
|
g_auto(virBuffer) metadataCacheChildBuf = VIR_BUFFER_INIT_CHILD(&driverChildBuf);
|
|
|
|
|
|
|
|
virBufferAsprintf(&metadataCacheChildBuf,
|
|
|
|
"<max_size unit='bytes'>%llu</max_size>\n",
|
|
|
|
disk->src->metadataCacheMaxSize);
|
|
|
|
|
|
|
|
virXMLFormatElement(&driverChildBuf, "metadata_cache", NULL, &metadataCacheChildBuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
virXMLFormatElement(&childBuf, "driver", &driverAttrBuf, &driverChildBuf);
|
|
|
|
|
2021-01-07 15:03:57 +01:00
|
|
|
if (virDomainDiskSourceFormat(&childBuf, disk->src, "source", 0, false, 0,
|
|
|
|
false, false, xmlopt) < 0)
|
2017-11-08 07:13:20 -05:00
|
|
|
return -1;
|
2021-01-07 15:03:57 +01:00
|
|
|
}
|
2013-11-12 11:37:04 +01:00
|
|
|
|
2021-01-07 15:03:57 +01:00
|
|
|
virXMLFormatElement(buf, "disk", &attrBuf, &childBuf);
|
2017-11-08 07:13:20 -05:00
|
|
|
return 0;
|
2013-05-16 08:24:56 +02:00
|
|
|
}
|
|
|
|
|
2017-06-01 15:57:55 +02:00
|
|
|
|
2019-02-16 19:58:58 -06:00
|
|
|
/* Append XML describing def into buf. Return 0 on success, or -1 on
|
|
|
|
* failure with buf cleared. */
|
2019-03-25 14:46:18 -05:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDefFormatInternal(virBuffer *buf,
|
2019-02-16 19:58:58 -06:00
|
|
|
const char *uuidstr,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDef *def,
|
|
|
|
virDomainXMLOption *xmlopt,
|
2019-02-16 19:58:58 -06:00
|
|
|
unsigned int flags)
|
2012-08-13 18:09:12 -06:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2019-02-15 14:43:43 -06:00
|
|
|
int domainflags = VIR_DOMAIN_DEF_FORMAT_INACTIVE;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2019-02-15 14:43:43 -06:00
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE)
|
|
|
|
domainflags |= VIR_DOMAIN_DEF_FORMAT_SECURE;
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAddLit(buf, "<domainsnapshot>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2017-06-01 15:57:55 +02:00
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
virBufferEscapeString(buf, "<name>%s</name>\n", def->parent.name);
|
|
|
|
if (def->parent.description)
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferEscapeString(buf, "<description>%s</description>\n",
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.description);
|
snapshot: Don't expose testsuite-only state in snapshot XML
None of the existing drivers actually use the 0-valued 'nostate'
snapshot state; rather, it was a fluke of implementation. In fact,
some drivers, like qemu, actively reject 'nostate' as invalid during a
snapshot redefine. Normally, a driver computes the state post-parse
from the current domain, and thus virDomainSnapshotGetXMLDesc() will
never expose the state. However, since the testsuite lacks any
associated domain to copy state from, and lacks post-parse processing
that normal drivers have, the testsuite output had several spots with
the state, coupled with a regex filter to ignore the oddity.
It is better to follow the lead of other XML defaults, by not
outputting anything during format if post-parse defaults have not been
applied, and rejecting the default value during parsing. The testsuite
needs a bit of an update, by adding another flag for when to simulate
a post-parse action of setting a snapshot state, but none of the
drivers are impacted other than rejecting XML that was previously
already suspicious in nature.
Similarly, don't expose creation time 0 (for now, only possible if a
user redefined a snapshot to claim creation at the Epoch, but also
happens once setting the creation time is deferred to a post-parse
handler).
This is also a step towards cleaning up snapshot_conf.c to separate
its existing post-parse work (namely, setting the creationTime and
default snapshot name) from the pure parsing work, so that we can get
rid of the testsuite hack of regex filtering of the XML and instead
have more accurate testing of our parser/formatter code.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Cole Robinson <crobinso@redhat.com>
2019-04-16 17:44:38 -05:00
|
|
|
if (def->state)
|
|
|
|
virBufferAsprintf(buf, "<state>%s</state>\n",
|
|
|
|
virDomainSnapshotStateTypeToString(def->state));
|
2017-06-01 15:57:55 +02:00
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
if (def->parent.parent_name) {
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAddLit(buf, "<parent>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2019-05-08 11:39:13 -05:00
|
|
|
virBufferEscapeString(buf, "<name>%s</name>\n",
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.parent_name);
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</parent>\n");
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
2017-06-01 15:57:55 +02:00
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
if (def->parent.creationTime)
|
snapshot: Don't expose testsuite-only state in snapshot XML
None of the existing drivers actually use the 0-valued 'nostate'
snapshot state; rather, it was a fluke of implementation. In fact,
some drivers, like qemu, actively reject 'nostate' as invalid during a
snapshot redefine. Normally, a driver computes the state post-parse
from the current domain, and thus virDomainSnapshotGetXMLDesc() will
never expose the state. However, since the testsuite lacks any
associated domain to copy state from, and lacks post-parse processing
that normal drivers have, the testsuite output had several spots with
the state, coupled with a regex filter to ignore the oddity.
It is better to follow the lead of other XML defaults, by not
outputting anything during format if post-parse defaults have not been
applied, and rejecting the default value during parsing. The testsuite
needs a bit of an update, by adding another flag for when to simulate
a post-parse action of setting a snapshot state, but none of the
drivers are impacted other than rejecting XML that was previously
already suspicious in nature.
Similarly, don't expose creation time 0 (for now, only possible if a
user redefined a snapshot to claim creation at the Epoch, but also
happens once setting the creation time is deferred to a post-parse
handler).
This is also a step towards cleaning up snapshot_conf.c to separate
its existing post-parse work (namely, setting the creationTime and
default snapshot name) from the pure parsing work, so that we can get
rid of the testsuite hack of regex filtering of the XML and instead
have more accurate testing of our parser/formatter code.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Cole Robinson <crobinso@redhat.com>
2019-04-16 17:44:38 -05:00
|
|
|
virBufferAsprintf(buf, "<creationTime>%lld</creationTime>\n",
|
2019-05-08 17:10:58 -05:00
|
|
|
def->parent.creationTime);
|
2017-06-01 15:57:55 +02:00
|
|
|
|
snapshot: new XML for external system checkpoint
Each <domainsnapshot> can now contain an optional <memory>
element that describes how the VM state was handled, similar
to disk snapshots. The new element will always appear in
output; for back-compat, an input that lacks the element will
assume 'no' or 'internal' according to the domain state.
Along with this change, it is now possible to pass <disks> in
the XML for an offline snapshot; this also needs to be wired up
in a future patch, to make it possible to choose internal vs.
external on a per-disk basis for each disk in an offline domain.
At that point, using the --disk-only flag for an offline domain
will be able to work.
For some examples below, remember that qemu supports the
following snapshot actions:
qemu-img: offline external and internal disk
savevm: online internal VM and disk
migrate: online external VM
transaction: online external disk
=====
<domainsnapshot>
<memory snapshot='no'/>
...
</domainsnapshot>
implies that there is no VM state saved (mandatory for
offline and disk-only snapshots, not possible otherwise);
using qemu-img for offline domains and transaction for online.
=====
<domainsnapshot>
<memory snapshot='internal'/>
...
</domainsnapshot>
state is saved inside one of the disks (as in qemu's 'savevm'
system checkpoint implementation). If needed in the future,
we can also add an attribute pointing out _which_ disk saved
the internal state; maybe disk='vda'.
=====
<domainsnapshot>
<memory snapshot='external' file='/path/to/state'/>
...
</domainsnapshot>
This is not wired up yet, but future patches will allow this to
control a combination of 'virsh save /path/to/state' plus disk
snapshots from the same point in time.
=====
So for 1.0.1 (and later, as needed), I plan to implement this table
of combinations, with '*' designating new code and '+' designating
existing code reached through new combinations of xml and/or the
existing DISK_ONLY flag:
domain memory disk disk-only | result
-----------------------------------------
offline omit omit any | memory=no disk=int, via qemu-img
offline no omit any |+memory=no disk=int, via qemu-img
offline omit/no no any | invalid combination (nothing to snapshot)
offline omit/no int any |+memory=no disk=int, via qemu-img
offline omit/no ext any |*memory=no disk=ext, via qemu-img
offline int/ext any any | invalid combination (no memory to save)
online omit omit off | memory=int disk=int, via savevm
online omit omit on | memory=no disk=default, via transaction
online omit no/ext off | unsupported for now
online omit no on | invalid combination (nothing to snapshot)
online omit ext on | memory=no disk=ext, via transaction
online omit int off |+memory=int disk=int, via savevm
online omit int on | unsupported for now
online no omit any |+memory=no disk=default, via transaction
online no no any | invalid combination (nothing to snapshot)
online no int any | unsupported for now
online no ext any |+memory=no disk=ext, via transaction
online int/ext any on | invalid combination (disk-only vs. memory)
online int omit off |+memory=int disk=int, via savevm
online int no/ext off | unsupported for now
online int int off |+memory=int disk=int, via savevm
online ext omit off |*memory=ext disk=default, via migrate+trans
online ext no off |+memory=ext disk=no, via migrate
online ext int off | unsupported for now
online ext ext off |*memory=ext disk=ext, via migrate+transaction
* docs/schemas/domainsnapshot.rng (memory): New RNG element.
* docs/formatsnapshot.html.in: Document it.
* src/conf/snapshot_conf.h (virDomainSnapshotDef): New fields.
* src/conf/domain_conf.c (virDomainSnapshotDefFree)
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Manage new fields.
* tests/domainsnapshotxml2xmltest.c: New test.
* tests/domainsnapshotxml2xmlin/*.xml: Update existing tests.
* tests/domainsnapshotxml2xmlout/*.xml: Likewise.
2012-10-23 17:12:23 +02:00
|
|
|
if (def->memory) {
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAsprintf(buf, "<memory snapshot='%s'",
|
snapshot: new XML for external system checkpoint
Each <domainsnapshot> can now contain an optional <memory>
element that describes how the VM state was handled, similar
to disk snapshots. The new element will always appear in
output; for back-compat, an input that lacks the element will
assume 'no' or 'internal' according to the domain state.
Along with this change, it is now possible to pass <disks> in
the XML for an offline snapshot; this also needs to be wired up
in a future patch, to make it possible to choose internal vs.
external on a per-disk basis for each disk in an offline domain.
At that point, using the --disk-only flag for an offline domain
will be able to work.
For some examples below, remember that qemu supports the
following snapshot actions:
qemu-img: offline external and internal disk
savevm: online internal VM and disk
migrate: online external VM
transaction: online external disk
=====
<domainsnapshot>
<memory snapshot='no'/>
...
</domainsnapshot>
implies that there is no VM state saved (mandatory for
offline and disk-only snapshots, not possible otherwise);
using qemu-img for offline domains and transaction for online.
=====
<domainsnapshot>
<memory snapshot='internal'/>
...
</domainsnapshot>
state is saved inside one of the disks (as in qemu's 'savevm'
system checkpoint implementation). If needed in the future,
we can also add an attribute pointing out _which_ disk saved
the internal state; maybe disk='vda'.
=====
<domainsnapshot>
<memory snapshot='external' file='/path/to/state'/>
...
</domainsnapshot>
This is not wired up yet, but future patches will allow this to
control a combination of 'virsh save /path/to/state' plus disk
snapshots from the same point in time.
=====
So for 1.0.1 (and later, as needed), I plan to implement this table
of combinations, with '*' designating new code and '+' designating
existing code reached through new combinations of xml and/or the
existing DISK_ONLY flag:
domain memory disk disk-only | result
-----------------------------------------
offline omit omit any | memory=no disk=int, via qemu-img
offline no omit any |+memory=no disk=int, via qemu-img
offline omit/no no any | invalid combination (nothing to snapshot)
offline omit/no int any |+memory=no disk=int, via qemu-img
offline omit/no ext any |*memory=no disk=ext, via qemu-img
offline int/ext any any | invalid combination (no memory to save)
online omit omit off | memory=int disk=int, via savevm
online omit omit on | memory=no disk=default, via transaction
online omit no/ext off | unsupported for now
online omit no on | invalid combination (nothing to snapshot)
online omit ext on | memory=no disk=ext, via transaction
online omit int off |+memory=int disk=int, via savevm
online omit int on | unsupported for now
online no omit any |+memory=no disk=default, via transaction
online no no any | invalid combination (nothing to snapshot)
online no int any | unsupported for now
online no ext any |+memory=no disk=ext, via transaction
online int/ext any on | invalid combination (disk-only vs. memory)
online int omit off |+memory=int disk=int, via savevm
online int no/ext off | unsupported for now
online int int off |+memory=int disk=int, via savevm
online ext omit off |*memory=ext disk=default, via migrate+trans
online ext no off |+memory=ext disk=no, via migrate
online ext int off | unsupported for now
online ext ext off |*memory=ext disk=ext, via migrate+transaction
* docs/schemas/domainsnapshot.rng (memory): New RNG element.
* docs/formatsnapshot.html.in: Document it.
* src/conf/snapshot_conf.h (virDomainSnapshotDef): New fields.
* src/conf/domain_conf.c (virDomainSnapshotDefFree)
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Manage new fields.
* tests/domainsnapshotxml2xmltest.c: New test.
* tests/domainsnapshotxml2xmlin/*.xml: Update existing tests.
* tests/domainsnapshotxml2xmlout/*.xml: Likewise.
2012-10-23 17:12:23 +02:00
|
|
|
virDomainSnapshotLocationTypeToString(def->memory));
|
2021-06-16 16:54:18 +02:00
|
|
|
virBufferEscapeString(buf, " file='%s'", def->memorysnapshotfile);
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAddLit(buf, "/>\n");
|
snapshot: new XML for external system checkpoint
Each <domainsnapshot> can now contain an optional <memory>
element that describes how the VM state was handled, similar
to disk snapshots. The new element will always appear in
output; for back-compat, an input that lacks the element will
assume 'no' or 'internal' according to the domain state.
Along with this change, it is now possible to pass <disks> in
the XML for an offline snapshot; this also needs to be wired up
in a future patch, to make it possible to choose internal vs.
external on a per-disk basis for each disk in an offline domain.
At that point, using the --disk-only flag for an offline domain
will be able to work.
For some examples below, remember that qemu supports the
following snapshot actions:
qemu-img: offline external and internal disk
savevm: online internal VM and disk
migrate: online external VM
transaction: online external disk
=====
<domainsnapshot>
<memory snapshot='no'/>
...
</domainsnapshot>
implies that there is no VM state saved (mandatory for
offline and disk-only snapshots, not possible otherwise);
using qemu-img for offline domains and transaction for online.
=====
<domainsnapshot>
<memory snapshot='internal'/>
...
</domainsnapshot>
state is saved inside one of the disks (as in qemu's 'savevm'
system checkpoint implementation). If needed in the future,
we can also add an attribute pointing out _which_ disk saved
the internal state; maybe disk='vda'.
=====
<domainsnapshot>
<memory snapshot='external' file='/path/to/state'/>
...
</domainsnapshot>
This is not wired up yet, but future patches will allow this to
control a combination of 'virsh save /path/to/state' plus disk
snapshots from the same point in time.
=====
So for 1.0.1 (and later, as needed), I plan to implement this table
of combinations, with '*' designating new code and '+' designating
existing code reached through new combinations of xml and/or the
existing DISK_ONLY flag:
domain memory disk disk-only | result
-----------------------------------------
offline omit omit any | memory=no disk=int, via qemu-img
offline no omit any |+memory=no disk=int, via qemu-img
offline omit/no no any | invalid combination (nothing to snapshot)
offline omit/no int any |+memory=no disk=int, via qemu-img
offline omit/no ext any |*memory=no disk=ext, via qemu-img
offline int/ext any any | invalid combination (no memory to save)
online omit omit off | memory=int disk=int, via savevm
online omit omit on | memory=no disk=default, via transaction
online omit no/ext off | unsupported for now
online omit no on | invalid combination (nothing to snapshot)
online omit ext on | memory=no disk=ext, via transaction
online omit int off |+memory=int disk=int, via savevm
online omit int on | unsupported for now
online no omit any |+memory=no disk=default, via transaction
online no no any | invalid combination (nothing to snapshot)
online no int any | unsupported for now
online no ext any |+memory=no disk=ext, via transaction
online int/ext any on | invalid combination (disk-only vs. memory)
online int omit off |+memory=int disk=int, via savevm
online int no/ext off | unsupported for now
online int int off |+memory=int disk=int, via savevm
online ext omit off |*memory=ext disk=default, via migrate+trans
online ext no off |+memory=ext disk=no, via migrate
online ext int off | unsupported for now
online ext ext off |*memory=ext disk=ext, via migrate+transaction
* docs/schemas/domainsnapshot.rng (memory): New RNG element.
* docs/formatsnapshot.html.in: Document it.
* src/conf/snapshot_conf.h (virDomainSnapshotDef): New fields.
* src/conf/domain_conf.c (virDomainSnapshotDefFree)
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Manage new fields.
* tests/domainsnapshotxml2xmltest.c: New test.
* tests/domainsnapshotxml2xmlin/*.xml: Update existing tests.
* tests/domainsnapshotxml2xmlout/*.xml: Likewise.
2012-10-23 17:12:23 +02:00
|
|
|
}
|
2017-06-01 15:57:55 +02:00
|
|
|
|
snapshot: new XML for external system checkpoint
Each <domainsnapshot> can now contain an optional <memory>
element that describes how the VM state was handled, similar
to disk snapshots. The new element will always appear in
output; for back-compat, an input that lacks the element will
assume 'no' or 'internal' according to the domain state.
Along with this change, it is now possible to pass <disks> in
the XML for an offline snapshot; this also needs to be wired up
in a future patch, to make it possible to choose internal vs.
external on a per-disk basis for each disk in an offline domain.
At that point, using the --disk-only flag for an offline domain
will be able to work.
For some examples below, remember that qemu supports the
following snapshot actions:
qemu-img: offline external and internal disk
savevm: online internal VM and disk
migrate: online external VM
transaction: online external disk
=====
<domainsnapshot>
<memory snapshot='no'/>
...
</domainsnapshot>
implies that there is no VM state saved (mandatory for
offline and disk-only snapshots, not possible otherwise);
using qemu-img for offline domains and transaction for online.
=====
<domainsnapshot>
<memory snapshot='internal'/>
...
</domainsnapshot>
state is saved inside one of the disks (as in qemu's 'savevm'
system checkpoint implementation). If needed in the future,
we can also add an attribute pointing out _which_ disk saved
the internal state; maybe disk='vda'.
=====
<domainsnapshot>
<memory snapshot='external' file='/path/to/state'/>
...
</domainsnapshot>
This is not wired up yet, but future patches will allow this to
control a combination of 'virsh save /path/to/state' plus disk
snapshots from the same point in time.
=====
So for 1.0.1 (and later, as needed), I plan to implement this table
of combinations, with '*' designating new code and '+' designating
existing code reached through new combinations of xml and/or the
existing DISK_ONLY flag:
domain memory disk disk-only | result
-----------------------------------------
offline omit omit any | memory=no disk=int, via qemu-img
offline no omit any |+memory=no disk=int, via qemu-img
offline omit/no no any | invalid combination (nothing to snapshot)
offline omit/no int any |+memory=no disk=int, via qemu-img
offline omit/no ext any |*memory=no disk=ext, via qemu-img
offline int/ext any any | invalid combination (no memory to save)
online omit omit off | memory=int disk=int, via savevm
online omit omit on | memory=no disk=default, via transaction
online omit no/ext off | unsupported for now
online omit no on | invalid combination (nothing to snapshot)
online omit ext on | memory=no disk=ext, via transaction
online omit int off |+memory=int disk=int, via savevm
online omit int on | unsupported for now
online no omit any |+memory=no disk=default, via transaction
online no no any | invalid combination (nothing to snapshot)
online no int any | unsupported for now
online no ext any |+memory=no disk=ext, via transaction
online int/ext any on | invalid combination (disk-only vs. memory)
online int omit off |+memory=int disk=int, via savevm
online int no/ext off | unsupported for now
online int int off |+memory=int disk=int, via savevm
online ext omit off |*memory=ext disk=default, via migrate+trans
online ext no off |+memory=ext disk=no, via migrate
online ext int off | unsupported for now
online ext ext off |*memory=ext disk=ext, via migrate+transaction
* docs/schemas/domainsnapshot.rng (memory): New RNG element.
* docs/formatsnapshot.html.in: Document it.
* src/conf/snapshot_conf.h (virDomainSnapshotDef): New fields.
* src/conf/domain_conf.c (virDomainSnapshotDefFree)
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Manage new fields.
* tests/domainsnapshotxml2xmltest.c: New test.
* tests/domainsnapshotxml2xmlin/*.xml: Update existing tests.
* tests/domainsnapshotxml2xmlout/*.xml: Likewise.
2012-10-23 17:12:23 +02:00
|
|
|
if (def->ndisks) {
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAddLit(buf, "<disks>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2017-11-08 07:13:20 -05:00
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
2019-02-16 19:58:58 -06:00
|
|
|
if (virDomainSnapshotDiskDefFormat(buf, &def->disks[i], xmlopt) < 0)
|
2020-07-02 23:19:26 -04:00
|
|
|
return -1;
|
2017-11-08 07:13:20 -05:00
|
|
|
}
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</disks>\n");
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
2017-06-01 15:57:55 +02:00
|
|
|
|
2019-05-08 17:10:58 -05:00
|
|
|
if (def->parent.dom) {
|
2019-11-26 19:40:46 +00:00
|
|
|
if (virDomainDefFormatInternal(def->parent.dom, xmlopt,
|
2019-11-27 11:57:34 +00:00
|
|
|
buf, domainflags) < 0)
|
2020-07-02 23:19:26 -04:00
|
|
|
return -1;
|
2019-02-21 09:43:49 -06:00
|
|
|
} else if (uuidstr) {
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAddLit(buf, "<domain>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</domain>\n");
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
2017-06-01 15:57:55 +02:00
|
|
|
|
2019-08-29 17:55:43 -03:00
|
|
|
if (def->parent.inactiveDom) {
|
2019-11-26 19:40:46 +00:00
|
|
|
if (virDomainDefFormatInternalSetRootName(def->parent.inactiveDom, xmlopt,
|
2019-11-27 11:57:34 +00:00
|
|
|
buf, "inactiveDomain",
|
2019-11-26 19:40:46 +00:00
|
|
|
domainflags) < 0)
|
2020-07-02 23:19:26 -04:00
|
|
|
return -1;
|
2019-08-29 17:55:43 -03:00
|
|
|
}
|
|
|
|
|
2019-02-16 19:58:58 -06:00
|
|
|
if (virSaveCookieFormatBuf(buf, def->cookie,
|
2017-06-02 00:44:46 +02:00
|
|
|
virDomainXMLOptionGetSaveCookie(xmlopt)) < 0)
|
2020-07-02 23:19:26 -04:00
|
|
|
return -1;
|
2017-06-02 00:44:46 +02:00
|
|
|
|
2019-02-15 14:43:43 -06:00
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL)
|
snapshot: Drop virDomainSnapshotDef.current
The only use for the 'current' member of virDomainSnapshotDef was with
the PARSE/FORMAT_INTERNAL flag for controlling an internal-use
<active> element marking whether a particular snapshot definition was
current, and even then, only by the qemu driver on output, and by qemu
and test driver on input. But this duplicates vm->snapshot_current,
and gets in the way of potential simplifications to have qemu store a
single file for all snapshots rather than one file per snapshot. Get
rid of the member by adding a bool* parameter during parse (ignored if
the PARSE_INTERNAL flag is not set), and by adding a new flag during
format (if FORMAT_INTERNAL is set, the value printed in <active>
depends on the new FORMAT_CURRENT).
Then update the qemu driver accordingly, which involves hoisting
assignments to vm->current_snapshot to occur prior to any point where
a snapshot XML file is written (although qemu kept
vm->current_snapshot and snapshot->def_current in sync by the end of
the function, they were not always identical in the middle of
functions, so the shuffling gets a bit interesting). Later patches
will clean up some of that confusing churn to vm->current_snapshot.
Note: even if later patches refactor qemu to no longer use
FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we
will always need PARSE_INTERNAL for input (because on upgrade, a new
libvirt still has to parse XML left from a previous libvirt).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-18 22:56:19 -05:00
|
|
|
virBufferAsprintf(buf, "<active>%d</active>\n",
|
|
|
|
!!(flags & VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT));
|
2017-06-01 15:57:55 +02:00
|
|
|
|
2019-02-16 19:58:58 -06:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</domainsnapshot>\n");
|
2012-08-13 18:09:12 -06:00
|
|
|
|
2019-02-16 19:58:58 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
virDomainSnapshotDefFormat(const char *uuidstr,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDef *def,
|
|
|
|
virDomainXMLOption *xmlopt,
|
2019-02-16 19:58:58 -06:00
|
|
|
unsigned int flags)
|
|
|
|
{
|
2020-07-02 22:19:01 -04:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2019-02-16 19:58:58 -06:00
|
|
|
|
|
|
|
virCheckFlags(VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE |
|
snapshot: Drop virDomainSnapshotDef.current
The only use for the 'current' member of virDomainSnapshotDef was with
the PARSE/FORMAT_INTERNAL flag for controlling an internal-use
<active> element marking whether a particular snapshot definition was
current, and even then, only by the qemu driver on output, and by qemu
and test driver on input. But this duplicates vm->snapshot_current,
and gets in the way of potential simplifications to have qemu store a
single file for all snapshots rather than one file per snapshot. Get
rid of the member by adding a bool* parameter during parse (ignored if
the PARSE_INTERNAL flag is not set), and by adding a new flag during
format (if FORMAT_INTERNAL is set, the value printed in <active>
depends on the new FORMAT_CURRENT).
Then update the qemu driver accordingly, which involves hoisting
assignments to vm->current_snapshot to occur prior to any point where
a snapshot XML file is written (although qemu kept
vm->current_snapshot and snapshot->def_current in sync by the end of
the function, they were not always identical in the middle of
functions, so the shuffling gets a bit interesting). Later patches
will clean up some of that confusing churn to vm->current_snapshot.
Note: even if later patches refactor qemu to no longer use
FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we
will always need PARSE_INTERNAL for input (because on upgrade, a new
libvirt still has to parse XML left from a previous libvirt).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-18 22:56:19 -05:00
|
|
|
VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL |
|
|
|
|
VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT, NULL);
|
2019-11-27 13:10:21 +00:00
|
|
|
if (virDomainSnapshotDefFormatInternal(&buf, uuidstr, def,
|
2019-02-16 19:58:58 -06:00
|
|
|
xmlopt, flags) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return virBufferContentAndReset(&buf);
|
2012-08-13 18:09:12 -06:00
|
|
|
}
|
|
|
|
|
2019-02-16 20:13:44 -06:00
|
|
|
|
2012-10-19 11:55:36 +02:00
|
|
|
bool
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDefIsExternal(virDomainSnapshotDef *def)
|
2012-10-19 11:55:36 +02:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2012-10-19 11:55:36 +02:00
|
|
|
|
2013-01-03 14:10:39 +01:00
|
|
|
if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
|
2012-10-19 11:55:36 +02:00
|
|
|
return true;
|
|
|
|
|
2013-01-03 14:10:39 +01:00
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
|
|
if (def->disks[i].snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
|
2012-10-19 11:55:36 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2013-01-03 14:10:39 +01:00
|
|
|
|
|
|
|
bool
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotIsExternal(virDomainMomentObj *snap)
|
2013-01-03 14:10:39 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotDef *def = virDomainSnapshotObjGetDef(snap);
|
2019-03-18 16:13:50 -05:00
|
|
|
|
|
|
|
return virDomainSnapshotDefIsExternal(def);
|
2013-01-03 14:10:39 +01:00
|
|
|
}
|
2013-08-21 16:39:02 -04:00
|
|
|
|
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainSnapshotRedefinePrep(virDomainObj *vm,
|
2022-01-12 17:00:22 +01:00
|
|
|
virDomainSnapshotDef *snapdef,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainMomentObj **snap,
|
|
|
|
virDomainXMLOption *xmlopt,
|
2013-08-21 16:39:02 -04:00
|
|
|
unsigned int flags)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainMomentObj *other;
|
2022-01-12 15:08:25 +01:00
|
|
|
virDomainSnapshotDef *otherSnapDef = NULL;
|
|
|
|
virDomainDef *otherDomDef = NULL;
|
|
|
|
virDomainSnapshotLocation align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL;
|
2013-08-21 16:39:02 -04:00
|
|
|
|
2022-01-12 14:56:04 +01:00
|
|
|
if (virDomainSnapshotCheckCycles(vm->snapshots, snapdef, vm->def->name) < 0)
|
2019-07-05 21:07:43 -05:00
|
|
|
return -1;
|
2013-08-21 16:39:02 -04:00
|
|
|
|
2022-01-12 15:08:25 +01:00
|
|
|
if ((other = virDomainSnapshotFindByName(vm->snapshots, snapdef->parent.name))) {
|
|
|
|
otherSnapDef = virDomainSnapshotObjGetDef(other);
|
|
|
|
otherDomDef = otherSnapDef->parent.dom;
|
2019-03-07 16:12:24 -06:00
|
|
|
}
|
2022-01-12 15:08:25 +01:00
|
|
|
|
2022-01-12 17:00:22 +01:00
|
|
|
*snap = other;
|
|
|
|
|
2022-01-12 15:08:25 +01:00
|
|
|
if (virDomainSnapshotRedefineValidate(snapdef, vm->def->uuid, other, xmlopt, flags) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (snapdef->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT ||
|
|
|
|
virDomainSnapshotDefIsExternal(snapdef))
|
|
|
|
align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
|
|
|
|
|
|
|
|
if (virDomainSnapshotAlignDisks(snapdef, otherDomDef, align_location, true) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-03-07 16:12:24 -06:00
|
|
|
return 0;
|
2013-08-21 16:39:02 -04:00
|
|
|
}
|