2012-08-14 00:09:12 +00:00
|
|
|
/*
|
|
|
|
* snapshot_conf.c: domain snapshot XML processing
|
|
|
|
*
|
2019-02-15 20:43:43 +00:00
|
|
|
* Copyright (C) 2006-2019 Red Hat, Inc.
|
2012-08-14 00:09:12 +00: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 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-08-14 00:09:12 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
2012-12-04 11:56:32 +00:00
|
|
|
#include "virbitmap.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2012-08-14 00:09:12 +00:00
|
|
|
#include "count-one-bits.h"
|
|
|
|
#include "datatypes.h"
|
|
|
|
#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-14 00:09:12 +00:00
|
|
|
#include "netdev_bandwidth_conf.h"
|
|
|
|
#include "netdev_vport_profile_conf.h"
|
|
|
|
#include "nwfilter_conf.h"
|
|
|
|
#include "secret_conf.h"
|
|
|
|
#include "snapshot_conf.h"
|
2012-12-13 15:25:48 +00:00
|
|
|
#include "virstoragefile.h"
|
2012-12-13 18:01:25 +00:00
|
|
|
#include "viruuid.h"
|
2012-08-14 00:09:12 +00:00
|
|
|
#include "virfile.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-13 18:13:21 +00:00
|
|
|
#include "virxml.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("conf.snapshot_conf");
|
|
|
|
|
2012-08-13 22:59:57 +00:00
|
|
|
VIR_ENUM_IMPL(virDomainSnapshotLocation, VIR_DOMAIN_SNAPSHOT_LOCATION_LAST,
|
2012-08-14 00:09:12 +00:00
|
|
|
"default",
|
|
|
|
"no",
|
|
|
|
"internal",
|
2019-01-20 16:30:15 +00:00
|
|
|
"external",
|
|
|
|
);
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
/* virDomainSnapshotState is really virDomainState plus one extra state */
|
2019-02-26 20:14:36 +00:00
|
|
|
VIR_ENUM_IMPL(virDomainSnapshotState, VIR_DOMAIN_SNAPSHOT_LAST,
|
2012-08-14 00:09:12 +00:00
|
|
|
"nostate",
|
|
|
|
"running",
|
|
|
|
"blocked",
|
|
|
|
"paused",
|
|
|
|
"shutdown",
|
|
|
|
"shutoff",
|
|
|
|
"crashed",
|
|
|
|
"pmsuspended",
|
2019-01-20 16:30:15 +00:00
|
|
|
"disk-snapshot",
|
|
|
|
);
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
struct _virDomainSnapshotObjList {
|
|
|
|
/* name string -> virDomainSnapshotObj mapping
|
|
|
|
* for O(1), lockless lookup-by-name */
|
|
|
|
virHashTable *objs;
|
|
|
|
|
|
|
|
virDomainSnapshotObj metaroot; /* Special parent of all root snapshots */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Snapshot Def functions */
|
|
|
|
static void
|
|
|
|
virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk)
|
|
|
|
{
|
|
|
|
VIR_FREE(disk->name);
|
2019-02-15 12:03:58 +00:00
|
|
|
virObjectUnref(disk->src);
|
2014-05-21 21:21:02 +00:00
|
|
|
disk->src = NULL;
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
|
|
|
|
{
|
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 14:09:33 +00:00
|
|
|
size_t i;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
if (!def)
|
|
|
|
return;
|
|
|
|
|
|
|
|
VIR_FREE(def->name);
|
|
|
|
VIR_FREE(def->description);
|
|
|
|
VIR_FREE(def->parent);
|
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 15:12:23 +00:00
|
|
|
VIR_FREE(def->file);
|
2012-08-14 00:09:12 +00:00
|
|
|
for (i = 0; i < def->ndisks; i++)
|
|
|
|
virDomainSnapshotDiskDefClear(&def->disks[i]);
|
|
|
|
VIR_FREE(def->disks);
|
|
|
|
virDomainDefFree(def->dom);
|
2017-06-01 22:44:46 +00:00
|
|
|
virObjectUnref(def->cookie);
|
2012-08-14 00:09:12 +00:00
|
|
|
VIR_FREE(def);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
|
2014-11-11 10:35:25 +00:00
|
|
|
xmlXPathContextPtr ctxt,
|
2017-08-30 15:35:34 +00:00
|
|
|
virDomainSnapshotDiskDefPtr def,
|
2017-12-12 16:55:03 +00:00
|
|
|
unsigned int flags,
|
|
|
|
virDomainXMLOptionPtr xmlopt)
|
2012-08-14 00:09:12 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
char *snapshot = NULL;
|
2013-11-12 13:15:51 +00:00
|
|
|
char *type = NULL;
|
2015-04-10 09:55:43 +00:00
|
|
|
char *driver = NULL;
|
2012-08-14 00:09:12 +00:00
|
|
|
xmlNodePtr cur;
|
2015-04-10 09:55:43 +00:00
|
|
|
xmlNodePtr saved = ctxt->node;
|
|
|
|
|
|
|
|
ctxt->node = node;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2019-02-14 15:20:25 +00:00
|
|
|
if (!(def->src = virStorageSourceNew()))
|
2014-05-21 21:21:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-08-14 00:09:12 +00:00
|
|
|
def->name = virXMLPropString(node, "name");
|
|
|
|
if (!def->name) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing name from disk snapshot element"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
snapshot = virXMLPropString(node, "snapshot");
|
|
|
|
if (snapshot) {
|
2012-08-13 22:59:57 +00:00
|
|
|
def->snapshot = virDomainSnapshotLocationTypeFromString(snapshot);
|
2012-08-14 00:09:12 +00:00
|
|
|
if (def->snapshot <= 0) {
|
2014-01-10 16:41:33 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
2012-08-14 00:09:12 +00:00
|
|
|
_("unknown disk snapshot setting '%s'"),
|
|
|
|
snapshot);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-12 13:15:51 +00:00
|
|
|
if ((type = virXMLPropString(node, "type"))) {
|
2014-05-21 21:21:02 +00:00
|
|
|
if ((def->src->type = virStorageTypeFromString(type)) <= 0 ||
|
|
|
|
def->src->type == VIR_STORAGE_TYPE_VOLUME ||
|
|
|
|
def->src->type == VIR_STORAGE_TYPE_DIR) {
|
2013-11-12 13:15:51 +00:00
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("unknown disk snapshot type '%s'"), type);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
2014-05-21 21:21:02 +00:00
|
|
|
def->src->type = VIR_STORAGE_TYPE_FILE;
|
2013-11-12 13:15:51 +00:00
|
|
|
}
|
2013-11-12 10:37:04 +00:00
|
|
|
|
2015-04-10 09:55:43 +00:00
|
|
|
if ((cur = virXPathNode("./source", ctxt)) &&
|
2017-12-12 16:55:03 +00:00
|
|
|
virDomainDiskSourceParse(cur, ctxt, def->src, flags, xmlopt) < 0)
|
2015-04-10 09:55:43 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2018-09-04 13:48:48 +00:00
|
|
|
if ((driver = virXPathString("string(./driver/@type)", ctxt)) &&
|
|
|
|
(def->src->format = virStorageFileFormatTypeFromString(driver)) <= 0) {
|
2015-04-10 09:55:43 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
2018-09-04 13:48:48 +00:00
|
|
|
_("unknown disk snapshot driver '%s'"), driver);
|
2015-04-10 09:55:43 +00:00
|
|
|
goto cleanup;
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
|
2014-08-06 13:17:00 +00:00
|
|
|
/* validate that the passed path is absolute */
|
2016-12-16 17:30:39 +00:00
|
|
|
if (virStorageSourceIsRelative(def->src)) {
|
2014-08-06 13:17:00 +00:00
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("disk snapshot image path '%s' must be absolute"),
|
|
|
|
def->src->path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-05-21 21:21:02 +00:00
|
|
|
if (!def->snapshot && (def->src->path || def->src->format))
|
2012-08-13 22:59:57 +00:00
|
|
|
def->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 06:48:31 +00:00
|
|
|
cleanup:
|
2015-04-10 09:55:43 +00:00
|
|
|
ctxt->node = saved;
|
|
|
|
|
|
|
|
VIR_FREE(driver);
|
2012-08-14 00:09:12 +00:00
|
|
|
VIR_FREE(snapshot);
|
2013-11-12 13:15:51 +00:00
|
|
|
VIR_FREE(type);
|
2012-08-14 00:09:12 +00:00
|
|
|
if (ret < 0)
|
|
|
|
virDomainSnapshotDiskDefClear(def);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* flags is bitwise-or of virDomainSnapshotParseFlags.
|
|
|
|
* If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
|
2015-04-18 01:22:48 +00:00
|
|
|
* caps are ignored.
|
2012-08-14 00:09:12 +00:00
|
|
|
*/
|
2013-08-07 14:34:40 +00:00
|
|
|
static virDomainSnapshotDefPtr
|
|
|
|
virDomainSnapshotDefParse(xmlXPathContextPtr ctxt,
|
|
|
|
virCapsPtr caps,
|
|
|
|
virDomainXMLOptionPtr xmlopt,
|
|
|
|
unsigned int flags)
|
2012-08-14 00:09:12 +00:00
|
|
|
{
|
|
|
|
virDomainSnapshotDefPtr def = NULL;
|
|
|
|
virDomainSnapshotDefPtr ret = NULL;
|
|
|
|
xmlNodePtr *nodes = 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 14:09:33 +00:00
|
|
|
size_t i;
|
|
|
|
int n;
|
2012-08-14 00:09:12 +00:00
|
|
|
char *creation = NULL, *state = NULL;
|
|
|
|
struct timeval tv;
|
|
|
|
int active;
|
|
|
|
char *tmp;
|
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 15:12:23 +00:00
|
|
|
char *memorySnapshot = NULL;
|
|
|
|
char *memoryFile = NULL;
|
|
|
|
bool offline = !!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE);
|
2017-06-01 22:44:46 +00:00
|
|
|
virSaveCookieCallbacksPtr saveCookie = virDomainXMLOptionGetSaveCookie(xmlopt);
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2013-07-04 10:02:00 +00:00
|
|
|
if (VIR_ALLOC(def) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
|
|
def->name = virXPathString("string(./name)", ctxt);
|
|
|
|
if (def->name == NULL) {
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
|
|
_("a redefined snapshot must have a name"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-07-04 10:02:00 +00:00
|
|
|
if (virAsprintf(&def->name, "%lld", (long long)tv.tv_sec) < 0)
|
2012-10-25 22:26:41 +00:00
|
|
|
goto cleanup;
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
def->description = virXPathString("string(./description)", ctxt);
|
|
|
|
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
|
|
|
if (virXPathLongLong("string(./creationTime)", ctxt,
|
|
|
|
&def->creationTime) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing creationTime from existing snapshot"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->parent = virXPathString("string(./parent/name)", ctxt);
|
|
|
|
|
|
|
|
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"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
def->state = virDomainSnapshotStateTypeFromString(state);
|
|
|
|
if (def->state < 0) {
|
2014-01-10 16:41:33 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
2012-08-14 00:09:12 +00:00
|
|
|
_("Invalid state '%s' in domain snapshot XML"),
|
|
|
|
state);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2019-02-26 20:14:36 +00:00
|
|
|
offline = (def->state == VIR_DOMAIN_SNAPSHOT_SHUTOFF ||
|
|
|
|
def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT);
|
2012-08-14 00:09:12 +00: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. */
|
|
|
|
if ((tmp = virXPathString("string(./domain/@type)", ctxt))) {
|
2016-05-26 13:58:53 +00:00
|
|
|
int domainflags = VIR_DOMAIN_DEF_PARSE_INACTIVE |
|
|
|
|
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE;
|
2012-08-14 00:09:12 +00:00
|
|
|
xmlNodePtr domainNode = virXPathNode("./domain", ctxt);
|
|
|
|
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
if (!domainNode) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing domain in snapshot"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-08-07 14:34:40 +00:00
|
|
|
def->dom = virDomainDefParseNode(ctxt->node->doc, domainNode,
|
2016-09-22 14:56:26 +00:00
|
|
|
caps, xmlopt, NULL, domainflags);
|
2012-08-14 00:09:12 +00:00
|
|
|
if (!def->dom)
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
VIR_WARN("parsing older snapshot that lacks domain");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
def->creationTime = tv.tv_sec;
|
|
|
|
}
|
|
|
|
|
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 15:12:23 +00:00
|
|
|
memorySnapshot = virXPathString("string(./memory/@snapshot)", ctxt);
|
|
|
|
memoryFile = virXPathString("string(./memory/@file)", ctxt);
|
|
|
|
if (memorySnapshot) {
|
|
|
|
def->memory = virDomainSnapshotLocationTypeFromString(memorySnapshot);
|
|
|
|
if (def->memory <= 0) {
|
2014-01-10 16:41:33 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
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 15:12:23 +00:00
|
|
|
_("unknown memory snapshot setting '%s'"),
|
|
|
|
memorySnapshot);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (memoryFile &&
|
|
|
|
def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("memory filename '%s' requires external snapshot"),
|
|
|
|
memoryFile);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-11-15 23:38:13 +00:00
|
|
|
if (!memoryFile &&
|
|
|
|
def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
|
|
_("external memory snapshots require a filename"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
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 15:12:23 +00:00
|
|
|
} else if (memoryFile) {
|
|
|
|
def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
|
|
|
|
} else if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
|
|
|
def->memory = (offline ?
|
|
|
|
VIR_DOMAIN_SNAPSHOT_LOCATION_NONE :
|
|
|
|
VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL);
|
|
|
|
}
|
|
|
|
if (offline && def->memory &&
|
|
|
|
def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_NONE) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
2013-01-25 10:50:43 +00:00
|
|
|
_("memory state cannot be saved with offline or "
|
|
|
|
"disk-only snapshot"));
|
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 15:12:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2018-06-21 10:11:29 +00:00
|
|
|
VIR_STEAL_PTR(def->file, memoryFile);
|
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 15:12:23 +00:00
|
|
|
|
2014-08-06 13:17:00 +00:00
|
|
|
/* verify that memory path is absolute */
|
|
|
|
if (def->file && def->file[0] != '/') {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("memory snapshot file path (%s) must be absolute"),
|
|
|
|
def->file);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
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 14:09:33 +00:00
|
|
|
if ((n = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
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 15:12:23 +00:00
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS) {
|
2013-09-25 08:34:25 +00:00
|
|
|
if (n && VIR_ALLOC_N(def->disks, n) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
2013-09-25 08:34:25 +00:00
|
|
|
def->ndisks = n;
|
2012-08-14 00:09:12 +00:00
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
2017-12-12 16:55:03 +00:00
|
|
|
if (virDomainSnapshotDiskDefParseXML(nodes[i], ctxt, &def->disks[i],
|
|
|
|
flags, xmlopt) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
VIR_FREE(nodes);
|
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 14:09:33 +00:00
|
|
|
} else if (n) {
|
2012-08-14 00:09:12 +00:00
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("unable to handle disk requests in snapshot"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL) {
|
|
|
|
if (virXPathInt("string(./active)", ctxt, &active) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Could not find 'active' element"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
def->current = active != 0;
|
|
|
|
}
|
|
|
|
|
2017-06-01 22:44:46 +00:00
|
|
|
if (!offline && virSaveCookieParse(ctxt, &def->cookie, saveCookie) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2018-06-21 10:11:29 +00:00
|
|
|
VIR_STEAL_PTR(ret, def);
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2014-03-25 06:48:31 +00:00
|
|
|
cleanup:
|
2012-08-14 00:09:12 +00:00
|
|
|
VIR_FREE(creation);
|
|
|
|
VIR_FREE(state);
|
|
|
|
VIR_FREE(nodes);
|
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 15:12:23 +00:00
|
|
|
VIR_FREE(memorySnapshot);
|
|
|
|
VIR_FREE(memoryFile);
|
2018-06-21 10:11:29 +00:00
|
|
|
virDomainSnapshotDefFree(def);
|
2013-08-07 14:34:40 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
virDomainSnapshotDefPtr
|
|
|
|
virDomainSnapshotDefParseNode(xmlDocPtr xml,
|
|
|
|
xmlNodePtr root,
|
|
|
|
virCapsPtr caps,
|
|
|
|
virDomainXMLOptionPtr xmlopt,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
|
|
virDomainSnapshotDefPtr def = NULL;
|
|
|
|
|
2017-08-14 12:31:52 +00:00
|
|
|
if (!virXMLNodeNameEqual(root, "domainsnapshot")) {
|
2013-08-07 14:34:40 +00:00
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctxt = xmlXPathNewContext(xml);
|
|
|
|
if (ctxt == NULL) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctxt->node = root;
|
2015-04-18 01:22:48 +00:00
|
|
|
def = virDomainSnapshotDefParse(ctxt, caps, xmlopt, flags);
|
2014-03-25 06:48:31 +00:00
|
|
|
cleanup:
|
2013-08-07 14:34:40 +00:00
|
|
|
xmlXPathFreeContext(ctxt);
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
|
|
|
|
virDomainSnapshotDefPtr
|
|
|
|
virDomainSnapshotDefParseString(const char *xmlStr,
|
|
|
|
virCapsPtr caps,
|
|
|
|
virDomainXMLOptionPtr xmlopt,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
virDomainSnapshotDefPtr ret = NULL;
|
|
|
|
xmlDocPtr xml;
|
|
|
|
int keepBlanksDefault = xmlKeepBlanksDefault(0);
|
|
|
|
|
|
|
|
if ((xml = virXMLParse(NULL, xmlStr, _("(domain_snapshot)")))) {
|
|
|
|
xmlKeepBlanksDefault(keepBlanksDefault);
|
|
|
|
ret = virDomainSnapshotDefParseNode(xml, xmlDocGetRootElement(xml),
|
2015-04-18 01:22:48 +00:00
|
|
|
caps, xmlopt, flags);
|
2013-08-07 14:34:40 +00:00
|
|
|
xmlFreeDoc(xml);
|
|
|
|
}
|
|
|
|
xmlKeepBlanksDefault(keepBlanksDefault);
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-02-11 08:07:05 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virDomainSnapshotDefAssignExternalNames:
|
|
|
|
* @def: snapshot def object
|
|
|
|
*
|
|
|
|
* Generate default external file names for snapshot targets. Returns 0 on
|
|
|
|
* success, -1 on error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virDomainSnapshotDefAssignExternalNames(virDomainSnapshotDefPtr def)
|
|
|
|
{
|
2016-02-11 08:42:36 +00:00
|
|
|
const char *origpath;
|
|
|
|
char *tmppath;
|
|
|
|
char *tmp;
|
|
|
|
struct stat sb;
|
2016-02-11 08:07:05 +00:00
|
|
|
size_t i;
|
2016-02-11 08:57:45 +00:00
|
|
|
size_t j;
|
2016-02-11 08:07:05 +00:00
|
|
|
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
|
|
virDomainSnapshotDiskDefPtr disk = &def->disks[i];
|
|
|
|
|
2016-02-11 08:42:36 +00:00
|
|
|
if (disk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL ||
|
|
|
|
disk->src->path)
|
|
|
|
continue;
|
2016-02-11 08:07:05 +00:00
|
|
|
|
2016-02-11 08:42:36 +00: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 08:07:05 +00:00
|
|
|
|
2016-02-11 08:42:36 +00:00
|
|
|
if (!(origpath = virDomainDiskGetSource(def->dom->disks[i]))) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("cannot generate external snapshot name "
|
|
|
|
"for disk '%s' without source"),
|
|
|
|
disk->name);
|
|
|
|
return -1;
|
2016-02-11 08:07:05 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 08:42:36 +00: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 08:07:05 +00:00
|
|
|
|
2016-02-11 08:42:36 +00:00
|
|
|
if (VIR_STRDUP(tmppath, origpath) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* drop suffix of the file name */
|
|
|
|
if ((tmp = strrchr(tmppath, '.')) && !strchr(tmp, '/'))
|
|
|
|
*tmp = '\0';
|
|
|
|
|
|
|
|
if (virAsprintf(&disk->src->path, "%s.%s", tmppath, def->name) < 0) {
|
|
|
|
VIR_FREE(tmppath);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(tmppath);
|
2016-02-11 08:57:45 +00: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 08:42:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2016-02-11 08:07:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-14 00:09:12 +00:00
|
|
|
static int
|
2016-02-11 07:42:00 +00:00
|
|
|
virDomainSnapshotCompareDiskIndex(const void *a, const void *b)
|
2012-08-14 00:09:12 +00:00
|
|
|
{
|
|
|
|
const virDomainSnapshotDiskDef *diska = a;
|
|
|
|
const virDomainSnapshotDiskDef *diskb = b;
|
|
|
|
|
|
|
|
/* Integer overflow shouldn't be a problem here. */
|
2015-04-14 11:35:29 +00:00
|
|
|
return diska->idx - diskb->idx;
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Align def->disks to def->domain. Sort the list of def->disks,
|
|
|
|
* filling in any missing disks or snapshot state defaults given by
|
|
|
|
* the domain, with a fallback to a passed in default. Convert paths
|
|
|
|
* to disk targets for uniformity. Issue an error and return -1 if
|
|
|
|
* any def->disks[n]->name appears more than once or does not map to
|
2012-10-23 15:12:24 +00:00
|
|
|
* dom->disks. If require_match, also ensure that there is no
|
|
|
|
* conflicting requests for both internal and external snapshots. */
|
2012-08-14 00:09:12 +00:00
|
|
|
int
|
|
|
|
virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def,
|
|
|
|
int default_snapshot,
|
|
|
|
bool require_match)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virBitmapPtr map = 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 14:09:33 +00:00
|
|
|
size_t i;
|
2012-08-14 00:09:12 +00:00
|
|
|
int ndisks;
|
|
|
|
|
|
|
|
if (!def->dom) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing domain in snapshot"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->ndisks > def->dom->ndisks) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("too many disk snapshot requests for domain"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlikely to have a guest without disks but technically possible. */
|
|
|
|
if (!def->dom->ndisks) {
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-07-04 10:02:00 +00:00
|
|
|
if (!(map = virBitmapNew(def->dom->ndisks)))
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Double check requested disks. */
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
|
|
virDomainSnapshotDiskDefPtr disk = &def->disks[i];
|
|
|
|
int idx = virDomainDiskIndexByName(def->dom, disk->name, false);
|
|
|
|
int disk_snapshot;
|
|
|
|
|
|
|
|
if (idx < 0) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("no disk named '%s'"), disk->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2015-03-11 15:41:57 +00:00
|
|
|
if (virBitmapIsBitSet(map, idx)) {
|
2012-08-14 00:09:12 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("disk '%s' specified twice"),
|
|
|
|
disk->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ignore_value(virBitmapSetBit(map, idx));
|
2015-04-14 11:35:29 +00:00
|
|
|
disk->idx = idx;
|
2012-10-23 15:12:24 +00:00
|
|
|
|
|
|
|
disk_snapshot = def->dom->disks[idx]->snapshot;
|
2012-08-14 00:09:12 +00:00
|
|
|
if (!disk->snapshot) {
|
2012-10-23 15:12:24 +00:00
|
|
|
if (disk_snapshot &&
|
|
|
|
(!require_match ||
|
|
|
|
disk_snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE))
|
|
|
|
disk->snapshot = disk_snapshot;
|
|
|
|
else
|
|
|
|
disk->snapshot = default_snapshot;
|
|
|
|
} else if (require_match &&
|
|
|
|
disk->snapshot != default_snapshot &&
|
|
|
|
!(disk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE &&
|
|
|
|
disk_snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE)) {
|
2012-08-13 22:59:57 +00:00
|
|
|
const char *tmp;
|
|
|
|
|
2012-10-23 15:12:24 +00:00
|
|
|
tmp = virDomainSnapshotLocationTypeToString(default_snapshot);
|
2012-08-14 00:09:12 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("disk '%s' must use snapshot mode '%s'"),
|
|
|
|
disk->name, tmp);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-05-21 21:21:02 +00:00
|
|
|
if (disk->src->path &&
|
2012-08-13 22:59:57 +00:00
|
|
|
disk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
|
2012-08-14 00:09:12 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("file '%s' for disk '%s' requires "
|
|
|
|
"use of external snapshot mode"),
|
2014-05-21 21:21:02 +00:00
|
|
|
disk->src->path, disk->name);
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (STRNEQ(disk->name, def->dom->disks[idx]->dst)) {
|
|
|
|
VIR_FREE(disk->name);
|
2013-05-03 12:40:46 +00:00
|
|
|
if (VIR_STRDUP(disk->name, def->dom->disks[idx]->dst) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Provide defaults for all remaining disks. */
|
|
|
|
ndisks = def->ndisks;
|
|
|
|
if (VIR_EXPAND_N(def->disks, def->ndisks,
|
2013-07-04 10:02:00 +00:00
|
|
|
def->dom->ndisks - def->ndisks) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < def->dom->ndisks; i++) {
|
|
|
|
virDomainSnapshotDiskDefPtr disk;
|
|
|
|
|
2015-03-11 15:41:57 +00:00
|
|
|
if (virBitmapIsBitSet(map, i))
|
2012-08-14 00:09:12 +00:00
|
|
|
continue;
|
|
|
|
disk = &def->disks[ndisks++];
|
2019-02-14 15:20:25 +00:00
|
|
|
if (!(disk->src = virStorageSourceNew()))
|
2014-05-21 21:21:02 +00:00
|
|
|
goto cleanup;
|
2013-05-03 12:40:46 +00:00
|
|
|
if (VIR_STRDUP(disk->name, def->dom->disks[i]->dst) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
2015-04-14 11:35:29 +00:00
|
|
|
disk->idx = i;
|
2014-09-11 15:45:06 +00:00
|
|
|
|
|
|
|
/* Don't snapshot empty drives */
|
|
|
|
if (virStorageSourceIsEmpty(def->dom->disks[i]->src))
|
|
|
|
disk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE;
|
|
|
|
else
|
|
|
|
disk->snapshot = def->dom->disks[i]->snapshot;
|
|
|
|
|
2014-05-21 21:21:02 +00:00
|
|
|
disk->src->type = VIR_STORAGE_TYPE_FILE;
|
2012-08-14 00:09:12 +00:00
|
|
|
if (!disk->snapshot)
|
|
|
|
disk->snapshot = default_snapshot;
|
|
|
|
}
|
|
|
|
|
2016-02-11 07:42:00 +00:00
|
|
|
qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]),
|
|
|
|
virDomainSnapshotCompareDiskIndex);
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2016-02-11 08:07:05 +00:00
|
|
|
/* Generate default external file names for external snapshot locations */
|
|
|
|
if (virDomainSnapshotDefAssignExternalNames(def) < 0)
|
|
|
|
goto cleanup;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:48:31 +00:00
|
|
|
cleanup:
|
2012-08-14 00:09:12 +00:00
|
|
|
virBitmapFree(map);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-15 20:43:43 +00: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 12:13:20 +00:00
|
|
|
static int
|
2013-05-16 06:24:56 +00:00
|
|
|
virDomainSnapshotDiskDefFormat(virBufferPtr buf,
|
2017-12-12 16:55:03 +00:00
|
|
|
virDomainSnapshotDiskDefPtr disk,
|
|
|
|
virDomainXMLOptionPtr xmlopt)
|
2013-05-16 06:24:56 +00:00
|
|
|
{
|
2014-05-21 21:21:02 +00:00
|
|
|
int type = disk->src->type;
|
2013-11-12 10:37:04 +00:00
|
|
|
|
2013-05-16 06:24:56 +00:00
|
|
|
if (!disk->name)
|
2017-11-08 12:13:20 +00:00
|
|
|
return 0;
|
2013-05-16 06:24:56 +00:00
|
|
|
|
2014-03-05 10:09:46 +00:00
|
|
|
virBufferEscapeString(buf, "<disk name='%s'", disk->name);
|
2013-05-16 06:24:56 +00:00
|
|
|
if (disk->snapshot > 0)
|
|
|
|
virBufferAsprintf(buf, " snapshot='%s'",
|
|
|
|
virDomainSnapshotLocationTypeToString(disk->snapshot));
|
2013-11-12 10:37:04 +00:00
|
|
|
|
2014-05-21 21:21:02 +00:00
|
|
|
if (!disk->src->path && disk->src->format == 0) {
|
2013-05-16 06:24:56 +00:00
|
|
|
virBufferAddLit(buf, "/>\n");
|
2017-11-08 12:13:20 +00:00
|
|
|
return 0;
|
2013-05-16 06:24:56 +00:00
|
|
|
}
|
|
|
|
|
conf: move host disk type to util/
A continuation of the migration of disk details to virstoragefile.
This patch moves a single enum, but converting the name has quite
a bit of fallout.
* src/conf/domain_conf.h (virDomainDiskType): Move...
* src/util/virstoragefile.h (virStorageType): ...and rename.
* src/bhyve/bhyve_command.c (bhyveBuildDiskArgStr)
(virBhyveProcessBuildLoadCmd): Update clients.
* src/conf/domain_conf.c (virDomainDiskSourceDefParse)
(virDomainDiskDefParseXML, virDomainDiskSourceDefFormatInternal)
(virDomainDiskDefFormat, virDomainDiskGetActualType)
(virDomainDiskDefForeachPath, virDomainDiskSourceIsBlockType):
Likewise.
* src/conf/snapshot_conf.h (_virDomainSnapshotDiskDef): Likewise.
* src/conf/snapshot_conf.c (virDomainSnapshotDiskDefParseXML)
(virDomainSnapshotAlignDisks, virDomainSnapshotDiskDefFormat):
Likewise.
* src/esx/esx_driver.c (esxAutodetectSCSIControllerModel)
(esxDomainDefineXML): Likewise.
* src/locking/domain_lock.c (virDomainLockManagerAddDisk):
Likewise.
* src/lxc/lxc_controller.c
(virLXCControllerSetupLoopDeviceDisk)
(virLXCControllerSetupNBDDeviceDisk)
(virLXCControllerSetupLoopDevices, virLXCControllerSetupDisk):
Likewise.
* src/parallels/parallels_driver.c (parallelsGetHddInfo):
Likewise.
* src/phyp/phyp_driver.c (phypDiskType): Likewise.
* src/qemu/qemu_command.c (qemuGetDriveSourceString)
(qemuDomainDiskGetSourceString, qemuBuildDriveStr)
(qemuBuildCommandLine, qemuParseCommandLineDisk)
(qemuParseCommandLine): Likewise.
* src/qemu/qemu_conf.c (qemuCheckSharedDevice)
(qemuTranslateDiskSourcePool)
(qemuTranslateSnapshotDiskSourcePool): Likewise.
* src/qemu/qemu_domain.c (qemuDomainDeviceDefPostParse)
(qemuDomainDetermineDiskChain): Likewise.
* src/qemu/qemu_driver.c (qemuDomainGetBlockInfo)
(qemuDomainSnapshotPrepareDiskExternalBackingInactive)
(qemuDomainSnapshotPrepareDiskExternalBackingActive)
(qemuDomainSnapshotPrepareDiskExternalOverlayActive)
(qemuDomainSnapshotPrepareDiskExternalOverlayInactive)
(qemuDomainSnapshotPrepareDiskInternal)
(qemuDomainSnapshotPrepare)
(qemuDomainSnapshotCreateSingleDiskActive): Likewise.
* src/qemu/qemu_hotplug.c (qemuDomainChangeEjectableMedia):
Likewise.
* src/qemu/qemu_migration.c (qemuMigrationIsSafe): Likewise.
* src/security/security_apparmor.c
(AppArmorRestoreSecurityImageLabel)
(AppArmorSetSecurityImageLabel): Likewise.
* src/security/security_dac.c (virSecurityDACSetSecurityImageLabel)
(virSecurityDACRestoreSecurityImageLabelInt)
(virSecurityDACSetSecurityAllLabel): Likewise.
* src/security/security_selinux.c
(virSecuritySELinuxRestoreSecurityImageLabelInt)
(virSecuritySELinuxSetSecurityImageLabel)
(virSecuritySELinuxSetSecurityAllLabel): Likewise.
* src/storage/storage_backend.c (virStorageFileBackendForType):
Likewise.
* src/storage/storage_backend_fs.c (virStorageFileBackendFile)
(virStorageFileBackendBlock): Likewise.
* src/storage/storage_backend_gluster.c
(virStorageFileBackendGluster): Likewise.
* src/vbox/vbox_tmpl.c (vboxDomainGetXMLDesc, vboxAttachDrives)
(vboxDomainAttachDeviceImpl, vboxDomainDetachDevice): Likewise.
* src/vmware/vmware_conf.c (vmwareVmxPath): Likewise.
* src/vmx/vmx.c (virVMXParseDisk, virVMXFormatDisk)
(virVMXFormatFloppy): Likewise.
* src/xenxs/xen_sxpr.c (xenParseSxprDisks, xenParseSxpr)
(xenFormatSxprDisk): Likewise.
* src/xenxs/xen_xm.c (xenParseXM, xenFormatXMDisk): Likewise.
* tests/securityselinuxlabeltest.c (testSELinuxLoadDef):
Likewise.
* src/libvirt_private.syms (domain_conf.h): Move symbols...
(virstoragefile.h): ...as appropriate.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-03-27 21:57:49 +00:00
|
|
|
virBufferAsprintf(buf, " type='%s'>\n", virStorageTypeToString(type));
|
2014-03-05 10:09:46 +00:00
|
|
|
virBufferAdjustIndent(buf, 2);
|
2013-05-16 06:24:56 +00:00
|
|
|
|
2014-05-21 21:21:02 +00:00
|
|
|
if (disk->src->format > 0)
|
2014-03-05 10:09:46 +00:00
|
|
|
virBufferEscapeString(buf, "<driver type='%s'/>\n",
|
2014-05-21 21:21:02 +00:00
|
|
|
virStorageFileFormatTypeToString(disk->src->format));
|
2017-11-08 12:13:20 +00:00
|
|
|
if (virDomainDiskSourceFormat(buf, disk->src, 0, 0, xmlopt) < 0)
|
|
|
|
return -1;
|
2013-11-12 10:37:04 +00:00
|
|
|
|
2014-03-05 10:09:46 +00:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</disk>\n");
|
2017-11-08 12:13:20 +00:00
|
|
|
return 0;
|
2013-05-16 06:24:56 +00:00
|
|
|
}
|
|
|
|
|
2017-06-01 13:57:55 +00:00
|
|
|
|
2019-02-17 01:58:58 +00:00
|
|
|
/* Append XML describing def into buf. Return 0 on success, or -1 on
|
|
|
|
* failure with buf cleared. */
|
|
|
|
static int
|
|
|
|
virDomainSnapshotDefFormatInternal(virBufferPtr buf,
|
|
|
|
const char *uuidstr,
|
|
|
|
virDomainSnapshotDefPtr def,
|
|
|
|
virCapsPtr caps,
|
|
|
|
virDomainXMLOptionPtr xmlopt,
|
|
|
|
unsigned int flags)
|
2012-08-14 00:09:12 +00: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 14:09:33 +00:00
|
|
|
size_t i;
|
2019-02-15 20:43:43 +00:00
|
|
|
int domainflags = VIR_DOMAIN_DEF_FORMAT_INACTIVE;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2019-02-15 20:43:43 +00:00
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE)
|
|
|
|
domainflags |= VIR_DOMAIN_DEF_FORMAT_SECURE;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAddLit(buf, "<domainsnapshot>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2017-06-01 13:57:55 +00:00
|
|
|
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferEscapeString(buf, "<name>%s</name>\n", def->name);
|
2012-08-14 00:09:12 +00:00
|
|
|
if (def->description)
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferEscapeString(buf, "<description>%s</description>\n",
|
2012-08-14 00:09:12 +00:00
|
|
|
def->description);
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAsprintf(buf, "<state>%s</state>\n",
|
2012-08-14 00:09:12 +00:00
|
|
|
virDomainSnapshotStateTypeToString(def->state));
|
2017-06-01 13:57:55 +00:00
|
|
|
|
2012-08-14 00:09:12 +00:00
|
|
|
if (def->parent) {
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAddLit(buf, "<parent>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
virBufferEscapeString(buf, "<name>%s</name>\n", def->parent);
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</parent>\n");
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
2017-06-01 13:57:55 +00:00
|
|
|
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAsprintf(buf, "<creationTime>%lld</creationTime>\n",
|
2012-08-14 00:09:12 +00:00
|
|
|
def->creationTime);
|
2017-06-01 13:57:55 +00: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 15:12:23 +00:00
|
|
|
if (def->memory) {
|
2019-02-17 01:58:58 +00: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 15:12:23 +00:00
|
|
|
virDomainSnapshotLocationTypeToString(def->memory));
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferEscapeString(buf, " file='%s'", def->file);
|
|
|
|
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 15:12:23 +00:00
|
|
|
}
|
2017-06-01 13:57:55 +00: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 15:12:23 +00:00
|
|
|
if (def->ndisks) {
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAddLit(buf, "<disks>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2017-11-08 12:13:20 +00:00
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
2019-02-17 01:58:58 +00:00
|
|
|
if (virDomainSnapshotDiskDefFormat(buf, &def->disks[i], xmlopt) < 0)
|
2017-11-08 12:13:20 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</disks>\n");
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
2017-06-01 13:57:55 +00:00
|
|
|
|
2012-08-14 00:09:12 +00:00
|
|
|
if (def->dom) {
|
2019-02-17 01:58:58 +00:00
|
|
|
if (virDomainDefFormatInternal(def->dom, caps, domainflags, buf,
|
2019-02-15 20:43:43 +00:00
|
|
|
xmlopt) < 0)
|
2017-06-01 13:57:55 +00:00
|
|
|
goto error;
|
2019-02-21 15:43:49 +00:00
|
|
|
} else if (uuidstr) {
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAddLit(buf, "<domain>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</domain>\n");
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
2017-06-01 13:57:55 +00:00
|
|
|
|
2019-02-17 01:58:58 +00:00
|
|
|
if (virSaveCookieFormatBuf(buf, def->cookie,
|
2017-06-01 22:44:46 +00:00
|
|
|
virDomainXMLOptionGetSaveCookie(xmlopt)) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2019-02-15 20:43:43 +00:00
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL)
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAsprintf(buf, "<active>%d</active>\n", def->current);
|
2017-06-01 13:57:55 +00:00
|
|
|
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</domainsnapshot>\n");
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2019-02-17 01:58:58 +00:00
|
|
|
if (virBufferCheckError(buf) < 0)
|
|
|
|
goto error;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2019-02-17 01:58:58 +00:00
|
|
|
return 0;
|
2017-06-01 13:57:55 +00:00
|
|
|
|
|
|
|
error:
|
2019-02-17 01:58:58 +00:00
|
|
|
virBufferFreeAndReset(buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
virDomainSnapshotDefFormat(const char *uuidstr,
|
|
|
|
virDomainSnapshotDefPtr def,
|
|
|
|
virCapsPtr caps,
|
|
|
|
virDomainXMLOptionPtr xmlopt,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
|
|
|
|
virCheckFlags(VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE |
|
|
|
|
VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL, NULL);
|
|
|
|
if (virDomainSnapshotDefFormatInternal(&buf, uuidstr, def, caps,
|
|
|
|
xmlopt, flags) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return virBufferContentAndReset(&buf);
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
|
2019-02-17 02:13:44 +00:00
|
|
|
|
|
|
|
/* Struct and callback function used as a hash table callback; each call
|
|
|
|
* appends another snapshot XML to buf, with the caller clearing the
|
|
|
|
* buffer if any callback fails. */
|
|
|
|
struct virDomainSnapshotFormatData {
|
|
|
|
virBufferPtr buf;
|
|
|
|
const char *uuidstr;
|
|
|
|
virCapsPtr caps;
|
|
|
|
virDomainXMLOptionPtr xmlopt;
|
|
|
|
unsigned int flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
virDomainSnapshotFormatOne(void *payload,
|
|
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr snap = payload;
|
|
|
|
struct virDomainSnapshotFormatData *data = opaque;
|
|
|
|
return virDomainSnapshotDefFormatInternal(data->buf, data->uuidstr,
|
|
|
|
snap->def, data->caps,
|
|
|
|
data->xmlopt, data->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Format the XML for all snapshots in the list into buf. On error,
|
|
|
|
* clear the buffer and return -1. */
|
|
|
|
int
|
|
|
|
virDomainSnapshotObjListFormat(virBufferPtr buf,
|
|
|
|
const char *uuidstr,
|
|
|
|
virDomainSnapshotObjListPtr snapshots,
|
|
|
|
virDomainSnapshotObjPtr current_snapshot,
|
|
|
|
virCapsPtr caps,
|
|
|
|
virDomainXMLOptionPtr xmlopt,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
struct virDomainSnapshotFormatData data = {
|
|
|
|
.buf = buf,
|
|
|
|
.uuidstr = uuidstr,
|
|
|
|
.caps = caps,
|
|
|
|
.xmlopt = xmlopt,
|
|
|
|
.flags = flags,
|
|
|
|
};
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "<snapshots");
|
|
|
|
if (current_snapshot)
|
|
|
|
virBufferEscapeString(buf, " current='%s'",
|
|
|
|
current_snapshot->def->name);
|
|
|
|
virBufferAddLit(buf, ">\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
if (virDomainSnapshotForEach(snapshots, virDomainSnapshotFormatOne,
|
|
|
|
&data) < 0) {
|
|
|
|
virBufferFreeAndReset(buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</snapshots>\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-14 00:09:12 +00:00
|
|
|
/* Snapshot Obj functions */
|
|
|
|
static virDomainSnapshotObjPtr virDomainSnapshotObjNew(void)
|
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr snapshot;
|
|
|
|
|
2013-07-04 10:02:00 +00:00
|
|
|
if (VIR_ALLOC(snapshot) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
VIR_DEBUG("obj=%p", snapshot);
|
|
|
|
|
|
|
|
return snapshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void virDomainSnapshotObjFree(virDomainSnapshotObjPtr snapshot)
|
|
|
|
{
|
|
|
|
if (!snapshot)
|
|
|
|
return;
|
|
|
|
|
|
|
|
VIR_DEBUG("obj=%p", snapshot);
|
|
|
|
|
|
|
|
virDomainSnapshotDefFree(snapshot->def);
|
|
|
|
VIR_FREE(snapshot);
|
|
|
|
}
|
|
|
|
|
|
|
|
virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
|
maint: avoid 'const fooPtr' in conf
'const fooPtr' is the same as 'foo * const' (the pointer won't
change, but it's contents can). But in general, if an interface
is trying to be const-correct, it should be using 'const foo *'
(the pointer is to data that can't be changed).
Fix up remaining offenders in src/conf, and their fallout.
* src/conf/snapshot_conf.h (virDomainSnapshotAssignDef)
(virDomainSnapshotFindByName): Drop attempt at const.
* src/conf/interface_conf.h (virInterfaceObjIsActive)
(virInterfaceDefFormat): Use intended type.
(virInterfaceFindByMACString, virInterfaceFindByName)
(virInterfaceAssignDef, virInterfaceRemove): Drop attempt at
const.
* src/conf/network_conf.h (virNetworkObjIsActive)
(virNetworkDefFormat, virNetworkDefForwardIf)
(virNetworkDefGetIpByIndex, virNetworkIpDefPrefix)
(virNetworkIpDefNetmask): Use intended type.
(virNetworkFindByUUID, virNetworkFindByName, virNetworkAssignDef)
(virNetworkObjAssignDef, virNetworkRemoveInactive)
(virNetworkBridgeInUse, virNetworkSetBridgeName)
(virNetworkAllocateBridge): Drop attempt at const.
* src/conf/netdev_vlan_conf.h (virNetDevVlanFormat): Make
const-correct.
* src/conf/node_device_conf.h (virNodeDeviceHasCap)
(virNodeDeviceDefFormat): Use intended type.
(virNodeDeviceFindByName, virNodeDeviceFindBySysfsPath)
(virNodeDeviceAssignDef, virNodeDeviceObjRemove)
(virNodeDeviceGetParentHost): Drop attempt at const.
* src/conf/secret_conf.h (virSecretDefFormat): Use intended type.
* src/conf/snapshot_conf.c (virDomainSnapshotAssignDef)
(virDomainSnapshotFindByName): Fix fallout.
* src/conf/interface_conf.c (virInterfaceBridgeDefFormat)
(virInterfaceBondDefFormat, virInterfaceVlanDefFormat)
(virInterfaceProtocolDefFormat, virInterfaceDefDevFormat)
(virInterfaceDefFormat, virInterfaceFindByMACString)
(virInterfaceFindByName, virInterfaceAssignDef)
(virInterfaceRemove): Likewise.
* src/conf/network_conf.c
(VIR_ENUM_IMPL, virNetworkFindByName, virNetworkObjAssignDef)
(virNetworkAssignDef, virNetworkRemoveInactive)
(virNetworkDefGetIpByIndex, virNetworkIpDefPrefix)
(virNetworkIpDefNetmask, virNetworkDHCPHostDefParseXML)
(virNetworkIpDefFormat, virNetworkRouteDefFormat)
(virPortGroupDefFormat, virNetworkForwardNatDefFormat)
(virNetworkDefFormatInternal, virNetworkBridgeInUse)
(virNetworkAllocateBridge, virNetworkSetBridgeName)
(virNetworkDNSDefFormat, virNetworkDefFormat): Likewise.
* src/conf/netdev_vlan_conf.c (virNetDevVlanFormat): Likewise.
* src/conf/node_device_conf.c (virNodeDeviceHasCap)
(virNodeDeviceFindBySysfsPath, virNodeDeviceFindByName)
(virNodeDeviceAssignDef, virNodeDeviceObjRemove)
(virNodeDeviceDefFormat, virNodeDeviceGetParentHost): Likewise.
* src/conf/secret_conf.c (virSecretDefFormatUsage)
(virSecretDefFormat): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-10-08 16:36:37 +00:00
|
|
|
virDomainSnapshotDefPtr def)
|
2012-08-14 00:09:12 +00:00
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr snap;
|
|
|
|
|
|
|
|
if (virHashLookup(snapshots->objs, def->name) != NULL) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected domain snapshot %s already exists"),
|
|
|
|
def->name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(snap = virDomainSnapshotObjNew()))
|
|
|
|
return NULL;
|
|
|
|
snap->def = def;
|
|
|
|
|
|
|
|
if (virHashAddEntry(snapshots->objs, snap->def->name, snap) < 0) {
|
|
|
|
VIR_FREE(snap);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return snap;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Snapshot Obj List functions */
|
|
|
|
static void
|
|
|
|
virDomainSnapshotObjListDataFree(void *payload,
|
|
|
|
const void *name ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr obj = payload;
|
|
|
|
|
|
|
|
virDomainSnapshotObjFree(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
virDomainSnapshotObjListPtr
|
|
|
|
virDomainSnapshotObjListNew(void)
|
|
|
|
{
|
|
|
|
virDomainSnapshotObjListPtr snapshots;
|
2013-07-04 10:02:00 +00:00
|
|
|
if (VIR_ALLOC(snapshots) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
return NULL;
|
|
|
|
snapshots->objs = virHashCreate(50, virDomainSnapshotObjListDataFree);
|
|
|
|
if (!snapshots->objs) {
|
|
|
|
VIR_FREE(snapshots);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return snapshots;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots)
|
|
|
|
{
|
|
|
|
if (!snapshots)
|
|
|
|
return;
|
|
|
|
virHashFree(snapshots->objs);
|
|
|
|
VIR_FREE(snapshots);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct virDomainSnapshotNameData {
|
|
|
|
char **const names;
|
|
|
|
int maxnames;
|
|
|
|
unsigned int flags;
|
|
|
|
int count;
|
|
|
|
bool error;
|
|
|
|
};
|
|
|
|
|
2016-02-12 09:03:50 +00:00
|
|
|
static int virDomainSnapshotObjListCopyNames(void *payload,
|
|
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
|
|
void *opaque)
|
2012-08-14 00:09:12 +00:00
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr obj = payload;
|
|
|
|
struct virDomainSnapshotNameData *data = opaque;
|
|
|
|
|
|
|
|
if (data->error)
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-08-14 00:09:12 +00:00
|
|
|
/* Caller already sanitized flags. Filtering on DESCENDANTS was
|
|
|
|
* done by choice of iteration in the caller. */
|
|
|
|
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-08-14 00:09:12 +00:00
|
|
|
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren)
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2012-11-13 18:32:19 +00:00
|
|
|
if (data->flags & VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS) {
|
|
|
|
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_INACTIVE) &&
|
2019-02-26 20:14:36 +00:00
|
|
|
obj->def->state == VIR_DOMAIN_SNAPSHOT_SHUTOFF)
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-11-13 18:32:19 +00:00
|
|
|
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_DISK_ONLY) &&
|
2019-02-26 20:14:36 +00:00
|
|
|
obj->def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-11-13 18:32:19 +00:00
|
|
|
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_ACTIVE) &&
|
2019-02-26 20:14:36 +00:00
|
|
|
obj->def->state != VIR_DOMAIN_SNAPSHOT_SHUTOFF &&
|
|
|
|
obj->def->state != VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-11-13 18:32:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL) &&
|
|
|
|
virDomainSnapshotIsExternal(obj))
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-11-13 18:32:19 +00:00
|
|
|
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL) &&
|
|
|
|
!virDomainSnapshotIsExternal(obj))
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-11-13 18:32:19 +00:00
|
|
|
|
2012-08-14 00:09:12 +00:00
|
|
|
if (data->names && data->count < data->maxnames &&
|
2013-05-03 12:40:46 +00:00
|
|
|
VIR_STRDUP(data->names[data->count], obj->def->name) < 0) {
|
2012-08-14 00:09:12 +00:00
|
|
|
data->error = true;
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
data->count++;
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
|
|
|
|
virDomainSnapshotObjPtr from,
|
|
|
|
char **const names, int maxnames,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
struct virDomainSnapshotNameData data = { names, maxnames, flags, 0,
|
|
|
|
false };
|
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 14:09:33 +00:00
|
|
|
size_t i;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
if (!from) {
|
|
|
|
/* LIST_ROOTS and LIST_DESCENDANTS have the same bit value,
|
|
|
|
* but opposite semantics. Toggle here to get the correct
|
|
|
|
* traversal on the metaroot. */
|
|
|
|
flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
|
|
|
|
from = &snapshots->metaroot;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We handle LIST_ROOT/LIST_DESCENDANTS directly, mask that bit
|
|
|
|
* out to determine when we must use the filter callback. */
|
|
|
|
data.flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
|
|
|
|
|
|
|
|
/* If this common code is being used, we assume that all snapshots
|
|
|
|
* have metadata, and thus can handle METADATA up front as an
|
|
|
|
* all-or-none filter. XXX This might not always be true, if we
|
|
|
|
* add the ability to track qcow2 internal snapshots without the
|
|
|
|
* use of metadata. */
|
|
|
|
if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA) ==
|
|
|
|
VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA)
|
|
|
|
return 0;
|
|
|
|
data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA;
|
|
|
|
|
2012-11-13 18:32:19 +00:00
|
|
|
/* For ease of coding the visitor, it is easier to zero each group
|
|
|
|
* where all of the bits are set. */
|
2012-08-14 00:09:12 +00:00
|
|
|
if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) ==
|
|
|
|
VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES)
|
|
|
|
data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES;
|
2012-11-13 18:32:19 +00:00
|
|
|
if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS) ==
|
|
|
|
VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS)
|
|
|
|
data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS;
|
|
|
|
if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION) ==
|
|
|
|
VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION)
|
|
|
|
data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) {
|
|
|
|
if (from->def)
|
|
|
|
virDomainSnapshotForEachDescendant(from,
|
|
|
|
virDomainSnapshotObjListCopyNames,
|
|
|
|
&data);
|
|
|
|
else if (names || data.flags)
|
|
|
|
virHashForEach(snapshots->objs, virDomainSnapshotObjListCopyNames,
|
|
|
|
&data);
|
|
|
|
else
|
|
|
|
data.count = virHashSize(snapshots->objs);
|
|
|
|
} else if (names || data.flags) {
|
|
|
|
virDomainSnapshotForEachChild(from,
|
|
|
|
virDomainSnapshotObjListCopyNames, &data);
|
|
|
|
} else {
|
|
|
|
data.count = from->nchildren;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data.error) {
|
|
|
|
for (i = 0; i < data.count; i++)
|
|
|
|
VIR_FREE(names[i]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.count;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
|
|
|
|
virDomainSnapshotObjPtr from,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
return virDomainSnapshotObjListGetNames(snapshots, from, NULL, 0, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
virDomainSnapshotObjPtr
|
maint: avoid 'const fooPtr' in conf
'const fooPtr' is the same as 'foo * const' (the pointer won't
change, but it's contents can). But in general, if an interface
is trying to be const-correct, it should be using 'const foo *'
(the pointer is to data that can't be changed).
Fix up remaining offenders in src/conf, and their fallout.
* src/conf/snapshot_conf.h (virDomainSnapshotAssignDef)
(virDomainSnapshotFindByName): Drop attempt at const.
* src/conf/interface_conf.h (virInterfaceObjIsActive)
(virInterfaceDefFormat): Use intended type.
(virInterfaceFindByMACString, virInterfaceFindByName)
(virInterfaceAssignDef, virInterfaceRemove): Drop attempt at
const.
* src/conf/network_conf.h (virNetworkObjIsActive)
(virNetworkDefFormat, virNetworkDefForwardIf)
(virNetworkDefGetIpByIndex, virNetworkIpDefPrefix)
(virNetworkIpDefNetmask): Use intended type.
(virNetworkFindByUUID, virNetworkFindByName, virNetworkAssignDef)
(virNetworkObjAssignDef, virNetworkRemoveInactive)
(virNetworkBridgeInUse, virNetworkSetBridgeName)
(virNetworkAllocateBridge): Drop attempt at const.
* src/conf/netdev_vlan_conf.h (virNetDevVlanFormat): Make
const-correct.
* src/conf/node_device_conf.h (virNodeDeviceHasCap)
(virNodeDeviceDefFormat): Use intended type.
(virNodeDeviceFindByName, virNodeDeviceFindBySysfsPath)
(virNodeDeviceAssignDef, virNodeDeviceObjRemove)
(virNodeDeviceGetParentHost): Drop attempt at const.
* src/conf/secret_conf.h (virSecretDefFormat): Use intended type.
* src/conf/snapshot_conf.c (virDomainSnapshotAssignDef)
(virDomainSnapshotFindByName): Fix fallout.
* src/conf/interface_conf.c (virInterfaceBridgeDefFormat)
(virInterfaceBondDefFormat, virInterfaceVlanDefFormat)
(virInterfaceProtocolDefFormat, virInterfaceDefDevFormat)
(virInterfaceDefFormat, virInterfaceFindByMACString)
(virInterfaceFindByName, virInterfaceAssignDef)
(virInterfaceRemove): Likewise.
* src/conf/network_conf.c
(VIR_ENUM_IMPL, virNetworkFindByName, virNetworkObjAssignDef)
(virNetworkAssignDef, virNetworkRemoveInactive)
(virNetworkDefGetIpByIndex, virNetworkIpDefPrefix)
(virNetworkIpDefNetmask, virNetworkDHCPHostDefParseXML)
(virNetworkIpDefFormat, virNetworkRouteDefFormat)
(virPortGroupDefFormat, virNetworkForwardNatDefFormat)
(virNetworkDefFormatInternal, virNetworkBridgeInUse)
(virNetworkAllocateBridge, virNetworkSetBridgeName)
(virNetworkDNSDefFormat, virNetworkDefFormat): Likewise.
* src/conf/netdev_vlan_conf.c (virNetDevVlanFormat): Likewise.
* src/conf/node_device_conf.c (virNodeDeviceHasCap)
(virNodeDeviceFindBySysfsPath, virNodeDeviceFindByName)
(virNodeDeviceAssignDef, virNodeDeviceObjRemove)
(virNodeDeviceDefFormat, virNodeDeviceGetParentHost): Likewise.
* src/conf/secret_conf.c (virSecretDefFormatUsage)
(virSecretDefFormat): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-10-08 16:36:37 +00:00
|
|
|
virDomainSnapshotFindByName(virDomainSnapshotObjListPtr snapshots,
|
2012-08-14 00:09:12 +00:00
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
return name ? virHashLookup(snapshots->objs, name) : &snapshots->metaroot;
|
|
|
|
}
|
|
|
|
|
|
|
|
void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
|
|
|
|
virDomainSnapshotObjPtr snapshot)
|
|
|
|
{
|
|
|
|
virHashRemoveEntry(snapshots->objs, snapshot->def->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots,
|
|
|
|
virHashIterator iter,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
return virHashForEach(snapshots->objs, iter, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Run iter(data) on all direct children of snapshot, while ignoring all
|
|
|
|
* other entries in snapshots. Return the number of children
|
|
|
|
* visited. No particular ordering is guaranteed. */
|
|
|
|
int
|
|
|
|
virDomainSnapshotForEachChild(virDomainSnapshotObjPtr snapshot,
|
|
|
|
virHashIterator iter,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr child = snapshot->first_child;
|
|
|
|
|
|
|
|
while (child) {
|
|
|
|
virDomainSnapshotObjPtr next = child->sibling;
|
|
|
|
(iter)(child, child->def->name, data);
|
|
|
|
child = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return snapshot->nchildren;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct snapshot_act_on_descendant {
|
|
|
|
int number;
|
|
|
|
virHashIterator iter;
|
|
|
|
void *data;
|
|
|
|
};
|
|
|
|
|
2016-02-12 09:03:50 +00:00
|
|
|
static int
|
2012-08-14 00:09:12 +00:00
|
|
|
virDomainSnapshotActOnDescendant(void *payload,
|
|
|
|
const void *name,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr obj = payload;
|
|
|
|
struct snapshot_act_on_descendant *curr = data;
|
|
|
|
|
|
|
|
curr->number += 1 + virDomainSnapshotForEachDescendant(obj,
|
|
|
|
curr->iter,
|
|
|
|
curr->data);
|
|
|
|
(curr->iter)(payload, name, curr->data);
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Run iter(data) on all descendants of snapshot, while ignoring all
|
|
|
|
* other entries in snapshots. Return the number of descendants
|
|
|
|
* visited. No particular ordering is guaranteed. */
|
|
|
|
int
|
|
|
|
virDomainSnapshotForEachDescendant(virDomainSnapshotObjPtr snapshot,
|
|
|
|
virHashIterator iter,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct snapshot_act_on_descendant act;
|
|
|
|
|
|
|
|
act.number = 0;
|
|
|
|
act.iter = iter;
|
|
|
|
act.data = data;
|
|
|
|
virDomainSnapshotForEachChild(snapshot,
|
|
|
|
virDomainSnapshotActOnDescendant, &act);
|
|
|
|
|
|
|
|
return act.number;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Struct and callback function used as a hash table callback; each call
|
|
|
|
* inspects the pre-existing snapshot->def->parent field, and adjusts
|
|
|
|
* the snapshot->parent field as well as the parent's child fields to
|
|
|
|
* wire up the hierarchical relations for the given snapshot. The error
|
|
|
|
* indicator gets set if a parent is missing or a requested parent would
|
|
|
|
* cause a circular parent chain. */
|
|
|
|
struct snapshot_set_relation {
|
|
|
|
virDomainSnapshotObjListPtr snapshots;
|
|
|
|
int err;
|
|
|
|
};
|
2016-02-12 09:03:50 +00:00
|
|
|
static int
|
2012-08-14 00:09:12 +00:00
|
|
|
virDomainSnapshotSetRelations(void *payload,
|
|
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr obj = payload;
|
|
|
|
struct snapshot_set_relation *curr = data;
|
|
|
|
virDomainSnapshotObjPtr tmp;
|
|
|
|
|
|
|
|
obj->parent = virDomainSnapshotFindByName(curr->snapshots,
|
|
|
|
obj->def->parent);
|
|
|
|
if (!obj->parent) {
|
|
|
|
curr->err = -1;
|
|
|
|
obj->parent = &curr->snapshots->metaroot;
|
|
|
|
VIR_WARN("snapshot %s lacks parent", obj->def->name);
|
|
|
|
} else {
|
|
|
|
tmp = obj->parent;
|
|
|
|
while (tmp && tmp->def) {
|
|
|
|
if (tmp == obj) {
|
|
|
|
curr->err = -1;
|
|
|
|
obj->parent = &curr->snapshots->metaroot;
|
|
|
|
VIR_WARN("snapshot %s in circular chain", obj->def->name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tmp = tmp->parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
obj->parent->nchildren++;
|
|
|
|
obj->sibling = obj->parent->first_child;
|
|
|
|
obj->parent->first_child = obj;
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-08-14 00:09:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Populate parent link and child count of all snapshots, with all
|
2019-03-05 02:38:27 +00:00
|
|
|
* assigned defs having relations starting as 0/NULL. Return 0 on
|
|
|
|
* success, -1 if a parent is missing or if a circular relationship
|
|
|
|
* was requested. */
|
2012-08-14 00:09:12 +00:00
|
|
|
int
|
|
|
|
virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots)
|
|
|
|
{
|
|
|
|
struct snapshot_set_relation act = { snapshots, 0 };
|
|
|
|
|
2019-03-05 02:38:27 +00:00
|
|
|
snapshots->metaroot.nchildren = 0;
|
|
|
|
snapshots->metaroot.first_child = NULL;
|
2012-08-14 00:09:12 +00:00
|
|
|
virHashForEach(snapshots->objs, virDomainSnapshotSetRelations, &act);
|
|
|
|
return act.err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare to reparent or delete snapshot, by removing it from its
|
|
|
|
* current listed parent. Note that when bulk removing all children
|
|
|
|
* of a parent, it is faster to just 0 the count rather than calling
|
|
|
|
* this function on each child. */
|
|
|
|
void
|
|
|
|
virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot)
|
|
|
|
{
|
|
|
|
virDomainSnapshotObjPtr prev = NULL;
|
|
|
|
virDomainSnapshotObjPtr curr = NULL;
|
|
|
|
|
|
|
|
snapshot->parent->nchildren--;
|
|
|
|
curr = snapshot->parent->first_child;
|
|
|
|
while (curr != snapshot) {
|
|
|
|
if (!curr) {
|
|
|
|
VIR_WARN("inconsistent snapshot relations");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
prev = curr;
|
|
|
|
curr = curr->sibling;
|
|
|
|
}
|
|
|
|
if (prev)
|
|
|
|
prev->sibling = snapshot->sibling;
|
|
|
|
else
|
|
|
|
snapshot->parent->first_child = snapshot->sibling;
|
|
|
|
snapshot->parent = NULL;
|
|
|
|
snapshot->sibling = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
|
|
|
|
virDomainSnapshotObjPtr from,
|
|
|
|
virDomainPtr dom,
|
|
|
|
virDomainSnapshotPtr **snaps,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
int count = virDomainSnapshotObjListNum(snapshots, from, flags);
|
2012-11-29 10:55:54 +00:00
|
|
|
virDomainSnapshotPtr *list = NULL;
|
2012-08-14 00:09:12 +00:00
|
|
|
char **names;
|
|
|
|
int ret = -1;
|
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 14:09:33 +00:00
|
|
|
size_t i;
|
2012-08-14 00:09:12 +00:00
|
|
|
|
2012-11-28 13:34:51 +00:00
|
|
|
if (!snaps || count < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
return count;
|
|
|
|
if (VIR_ALLOC_N(names, count) < 0 ||
|
2013-07-04 10:02:00 +00:00
|
|
|
VIR_ALLOC_N(list, count + 1) < 0)
|
2012-08-14 00:09:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virDomainSnapshotObjListGetNames(snapshots, from, names, count,
|
|
|
|
flags) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
if ((list[i] = virGetDomainSnapshot(dom, names[i])) == NULL)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = count;
|
|
|
|
*snaps = list;
|
|
|
|
|
2014-03-25 06:48:31 +00:00
|
|
|
cleanup:
|
2012-08-14 00:09:12 +00:00
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
VIR_FREE(names[i]);
|
|
|
|
VIR_FREE(names);
|
|
|
|
if (ret < 0 && list) {
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
virObjectUnref(list[i]);
|
|
|
|
VIR_FREE(list);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2012-10-19 09:55:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
bool
|
2013-01-03 13:10:39 +00:00
|
|
|
virDomainSnapshotDefIsExternal(virDomainSnapshotDefPtr def)
|
2012-10-19 09:55:36 +00: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 14:09:33 +00:00
|
|
|
size_t i;
|
2012-10-19 09:55:36 +00:00
|
|
|
|
2013-01-03 13:10:39 +00:00
|
|
|
if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
|
2012-10-19 09:55:36 +00:00
|
|
|
return true;
|
|
|
|
|
2013-01-03 13:10:39 +00:00
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
|
|
if (def->disks[i].snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
|
2012-10-19 09:55:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2013-01-03 13:10:39 +00:00
|
|
|
|
|
|
|
bool
|
|
|
|
virDomainSnapshotIsExternal(virDomainSnapshotObjPtr snap)
|
|
|
|
{
|
|
|
|
return virDomainSnapshotDefIsExternal(snap->def);
|
|
|
|
}
|
2013-08-21 20:39:02 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
virDomainSnapshotRedefinePrep(virDomainPtr domain,
|
|
|
|
virDomainObjPtr vm,
|
|
|
|
virDomainSnapshotDefPtr *defptr,
|
|
|
|
virDomainSnapshotObjPtr *snap,
|
2017-05-19 13:07:15 +00:00
|
|
|
virDomainXMLOptionPtr xmlopt,
|
2013-08-21 20:39:02 +00:00
|
|
|
bool *update_current,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
virDomainSnapshotDefPtr def = *defptr;
|
|
|
|
int ret = -1;
|
|
|
|
int align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL;
|
2014-11-17 23:39:48 +00:00
|
|
|
bool align_match = true;
|
2013-08-21 20:39:02 +00:00
|
|
|
virDomainSnapshotObjPtr other;
|
2019-02-26 20:14:36 +00:00
|
|
|
bool external = def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT ||
|
snapshot: Permit redefine of offline external snapshot
Due to historical back-compat, bare 'virsh snapshot-create-as'
favors internal snapshots (but can't be used on domains with raw
storage), while 'virsh snapshot-create-as --disk-only' favors
external snapshots. What's more, snapshots created with
--disk-only while the domain was running are marked as snapshot
state 'disk-snapshot', while snapshots created while the domain
was offline are marked as snapshot state 'shutdown' (a
'disk-snapshot' image might not be quiescent, while a 'shutdown'
snapshot always is).
But this leads to some interesting problems: if we create a
--disk-only snapshot of an offline guest, and then immediately try
to 'virsh snapshot-create --redefine' using the resulting XML to
overwrite the existing snapashot in place, things silently succeed,
but 'virsh snapshot-create --redefine --disk-only' fails with an
error message that the snapshot state is not 'disk-only'. Worse,
if we delete the snapshot metadata first and then try to recreate
things, omitting --disk-only fails because the verification code
wants to force the default of an internal snapshot (which doesn't
work with raw disks), and using --disk-only still fails because the
snapshot XML is not 'disk-only' - making it impossible to recreate
the snapshot metadata (or to transfer it from one libvirtd host to
another). Ideally, the presence or absence of the --disk-only
flag, and the presence or absence of an existing snapshot being
overwritten, shouldn't matter; if the XML is valid for one
situation, it should always be valid to redefine the metadata for
that snapshot.
Fix things by uniformly using virDomainSnapshotDefIsExternal()
(caching the results up front, and eliminating other 'if' clauses
now rendered redundant) when deciding whether the XML being
requested for redefinition should permit external or force internal
state capture (we got it right in only one out of three places in
the function).
See also https://bugzilla.redhat.com/1680304; this fixes the
domain-agnostic problems mentioned there, but another patch is
needed to fix further oddities with the qemu driver. I did not
check for sure when the problems were introduced (git blame puts
some affected hunks as far back as 1.0.0), but it was definitely
been broken even before when commit 670e86bf (1.1.4) factored
redefine prep out of qemu code into the common snapshot_conf code.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-02-23 19:21:38 +00:00
|
|
|
virDomainSnapshotDefIsExternal(def);
|
2013-08-21 20:39:02 +00:00
|
|
|
|
|
|
|
/* Prevent circular chains */
|
|
|
|
if (def->parent) {
|
|
|
|
if (STREQ(def->name, def->parent)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("cannot set snapshot %s as its own parent"),
|
|
|
|
def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
other = virDomainSnapshotFindByName(vm->snapshots, def->parent);
|
|
|
|
if (!other) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("parent %s for snapshot %s not found"),
|
|
|
|
def->parent, def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
while (other->def->parent) {
|
|
|
|
if (STREQ(other->def->parent, def->name)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("parent %s would create cycle to %s"),
|
|
|
|
other->def->name, def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
other = virDomainSnapshotFindByName(vm->snapshots,
|
|
|
|
other->def->parent);
|
|
|
|
if (!other) {
|
|
|
|
VIR_WARN("snapshots are inconsistent for %s",
|
|
|
|
vm->def->name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that any replacement is compatible */
|
snapshot: Permit redefine of offline external snapshot
Due to historical back-compat, bare 'virsh snapshot-create-as'
favors internal snapshots (but can't be used on domains with raw
storage), while 'virsh snapshot-create-as --disk-only' favors
external snapshots. What's more, snapshots created with
--disk-only while the domain was running are marked as snapshot
state 'disk-snapshot', while snapshots created while the domain
was offline are marked as snapshot state 'shutdown' (a
'disk-snapshot' image might not be quiescent, while a 'shutdown'
snapshot always is).
But this leads to some interesting problems: if we create a
--disk-only snapshot of an offline guest, and then immediately try
to 'virsh snapshot-create --redefine' using the resulting XML to
overwrite the existing snapashot in place, things silently succeed,
but 'virsh snapshot-create --redefine --disk-only' fails with an
error message that the snapshot state is not 'disk-only'. Worse,
if we delete the snapshot metadata first and then try to recreate
things, omitting --disk-only fails because the verification code
wants to force the default of an internal snapshot (which doesn't
work with raw disks), and using --disk-only still fails because the
snapshot XML is not 'disk-only' - making it impossible to recreate
the snapshot metadata (or to transfer it from one libvirtd host to
another). Ideally, the presence or absence of the --disk-only
flag, and the presence or absence of an existing snapshot being
overwritten, shouldn't matter; if the XML is valid for one
situation, it should always be valid to redefine the metadata for
that snapshot.
Fix things by uniformly using virDomainSnapshotDefIsExternal()
(caching the results up front, and eliminating other 'if' clauses
now rendered redundant) when deciding whether the XML being
requested for redefinition should permit external or force internal
state capture (we got it right in only one out of three places in
the function).
See also https://bugzilla.redhat.com/1680304; this fixes the
domain-agnostic problems mentioned there, but another patch is
needed to fix further oddities with the qemu driver. I did not
check for sure when the problems were introduced (git blame puts
some affected hunks as far back as 1.0.0), but it was definitely
been broken even before when commit 670e86bf (1.1.4) factored
redefine prep out of qemu code into the common snapshot_conf code.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-02-23 19:21:38 +00:00
|
|
|
if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) && !external) {
|
2013-08-21 20:39:02 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("disk-only flag for snapshot %s requires "
|
|
|
|
"disk-snapshot state"),
|
|
|
|
def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->dom &&
|
|
|
|
memcmp(def->dom->uuid, domain->uuid, VIR_UUID_BUFLEN)) {
|
2019-02-21 15:43:49 +00:00
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
|
|
|
|
virUUIDFormat(domain->uuid, uuidstr);
|
2013-08-21 20:39:02 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("definition for snapshot %s must use uuid %s"),
|
|
|
|
def->name, uuidstr);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
other = virDomainSnapshotFindByName(vm->snapshots, def->name);
|
|
|
|
if (other) {
|
2019-02-26 20:14:36 +00:00
|
|
|
if ((other->def->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
|
|
|
|
other->def->state == VIR_DOMAIN_SNAPSHOT_PAUSED) !=
|
|
|
|
(def->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
|
|
|
|
def->state == VIR_DOMAIN_SNAPSHOT_PAUSED)) {
|
2013-08-21 20:39:02 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("cannot change between online and offline "
|
|
|
|
"snapshot state in snapshot %s"),
|
|
|
|
def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2019-02-26 20:14:36 +00:00
|
|
|
if ((other->def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT) !=
|
|
|
|
(def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)) {
|
2013-08-21 20:39:02 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
2018-06-11 13:59:10 +00:00
|
|
|
_("cannot change between disk only and "
|
|
|
|
"full system in snapshot %s"),
|
2013-08-21 20:39:02 +00:00
|
|
|
def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (other->def->dom) {
|
|
|
|
if (def->dom) {
|
|
|
|
if (!virDomainDefCheckABIStability(other->def->dom,
|
2017-05-19 13:07:15 +00:00
|
|
|
def->dom, xmlopt))
|
2013-08-21 20:39:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
/* Transfer the domain def */
|
|
|
|
def->dom = other->def->dom;
|
|
|
|
other->def->dom = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->dom) {
|
snapshot: Permit redefine of offline external snapshot
Due to historical back-compat, bare 'virsh snapshot-create-as'
favors internal snapshots (but can't be used on domains with raw
storage), while 'virsh snapshot-create-as --disk-only' favors
external snapshots. What's more, snapshots created with
--disk-only while the domain was running are marked as snapshot
state 'disk-snapshot', while snapshots created while the domain
was offline are marked as snapshot state 'shutdown' (a
'disk-snapshot' image might not be quiescent, while a 'shutdown'
snapshot always is).
But this leads to some interesting problems: if we create a
--disk-only snapshot of an offline guest, and then immediately try
to 'virsh snapshot-create --redefine' using the resulting XML to
overwrite the existing snapashot in place, things silently succeed,
but 'virsh snapshot-create --redefine --disk-only' fails with an
error message that the snapshot state is not 'disk-only'. Worse,
if we delete the snapshot metadata first and then try to recreate
things, omitting --disk-only fails because the verification code
wants to force the default of an internal snapshot (which doesn't
work with raw disks), and using --disk-only still fails because the
snapshot XML is not 'disk-only' - making it impossible to recreate
the snapshot metadata (or to transfer it from one libvirtd host to
another). Ideally, the presence or absence of the --disk-only
flag, and the presence or absence of an existing snapshot being
overwritten, shouldn't matter; if the XML is valid for one
situation, it should always be valid to redefine the metadata for
that snapshot.
Fix things by uniformly using virDomainSnapshotDefIsExternal()
(caching the results up front, and eliminating other 'if' clauses
now rendered redundant) when deciding whether the XML being
requested for redefinition should permit external or force internal
state capture (we got it right in only one out of three places in
the function).
See also https://bugzilla.redhat.com/1680304; this fixes the
domain-agnostic problems mentioned there, but another patch is
needed to fix further oddities with the qemu driver. I did not
check for sure when the problems were introduced (git blame puts
some affected hunks as far back as 1.0.0), but it was definitely
been broken even before when commit 670e86bf (1.1.4) factored
redefine prep out of qemu code into the common snapshot_conf code.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-02-23 19:21:38 +00:00
|
|
|
if (external) {
|
2013-08-21 20:39:02 +00:00
|
|
|
align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
|
|
|
|
align_match = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virDomainSnapshotAlignDisks(def, align_location,
|
|
|
|
align_match) < 0) {
|
|
|
|
/* revert stealing of the snapshot domain definition */
|
|
|
|
if (def->dom && !other->def->dom) {
|
|
|
|
other->def->dom = def->dom;
|
|
|
|
def->dom = NULL;
|
|
|
|
}
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (other == vm->current_snapshot) {
|
|
|
|
*update_current = true;
|
|
|
|
vm->current_snapshot = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Drop and rebuild the parent relationship, but keep all
|
|
|
|
* child relations by reusing snap. */
|
|
|
|
virDomainSnapshotDropParent(other);
|
|
|
|
virDomainSnapshotDefFree(other->def);
|
|
|
|
other->def = def;
|
|
|
|
*defptr = NULL;
|
|
|
|
*snap = other;
|
|
|
|
} else {
|
|
|
|
if (def->dom) {
|
snapshot: Permit redefine of offline external snapshot
Due to historical back-compat, bare 'virsh snapshot-create-as'
favors internal snapshots (but can't be used on domains with raw
storage), while 'virsh snapshot-create-as --disk-only' favors
external snapshots. What's more, snapshots created with
--disk-only while the domain was running are marked as snapshot
state 'disk-snapshot', while snapshots created while the domain
was offline are marked as snapshot state 'shutdown' (a
'disk-snapshot' image might not be quiescent, while a 'shutdown'
snapshot always is).
But this leads to some interesting problems: if we create a
--disk-only snapshot of an offline guest, and then immediately try
to 'virsh snapshot-create --redefine' using the resulting XML to
overwrite the existing snapashot in place, things silently succeed,
but 'virsh snapshot-create --redefine --disk-only' fails with an
error message that the snapshot state is not 'disk-only'. Worse,
if we delete the snapshot metadata first and then try to recreate
things, omitting --disk-only fails because the verification code
wants to force the default of an internal snapshot (which doesn't
work with raw disks), and using --disk-only still fails because the
snapshot XML is not 'disk-only' - making it impossible to recreate
the snapshot metadata (or to transfer it from one libvirtd host to
another). Ideally, the presence or absence of the --disk-only
flag, and the presence or absence of an existing snapshot being
overwritten, shouldn't matter; if the XML is valid for one
situation, it should always be valid to redefine the metadata for
that snapshot.
Fix things by uniformly using virDomainSnapshotDefIsExternal()
(caching the results up front, and eliminating other 'if' clauses
now rendered redundant) when deciding whether the XML being
requested for redefinition should permit external or force internal
state capture (we got it right in only one out of three places in
the function).
See also https://bugzilla.redhat.com/1680304; this fixes the
domain-agnostic problems mentioned there, but another patch is
needed to fix further oddities with the qemu driver. I did not
check for sure when the problems were introduced (git blame puts
some affected hunks as far back as 1.0.0), but it was definitely
been broken even before when commit 670e86bf (1.1.4) factored
redefine prep out of qemu code into the common snapshot_conf code.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-02-23 19:21:38 +00:00
|
|
|
if (external) {
|
2013-08-21 20:39:02 +00:00
|
|
|
align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
|
|
|
|
align_match = false;
|
|
|
|
}
|
|
|
|
if (virDomainSnapshotAlignDisks(def, align_location,
|
|
|
|
align_match) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 06:48:31 +00:00
|
|
|
cleanup:
|
2013-08-21 20:39:02 +00:00
|
|
|
return ret;
|
|
|
|
}
|