snapshot: Break out virDomainSnapshotObjList into its own file

snapshot_conf.h was mixing three separate types: the snapshot
definition, the snapshot object, and the snapshot object list.
Separate out the snapshot object list code into its own file, and
update includes for affected clients.

This is just code motion, but done in preparation of sharing a lot of
the object list code with checkpoints.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
Eric Blake 2019-03-14 21:19:18 -05:00
parent 21b2651e72
commit 9b75154c07
14 changed files with 652 additions and 573 deletions

View File

@ -31,6 +31,8 @@ DOMAIN_CONF_SOURCES = \
conf/virdomainobjlist.h \
conf/virdomainsnapshotobj.c \
conf/virdomainsnapshotobj.h \
conf/virdomainsnapshotobjlist.c \
conf/virdomainsnapshotobjlist.h \
$(NULL)
OBJECT_EVENT_SOURCES = \

View File

@ -57,6 +57,7 @@
#include "virnetdevmacvlan.h"
#include "virhostdev.h"
#include "virmdev.h"
#include "virdomainsnapshotobjlist.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN

View File

@ -45,6 +45,7 @@
#include "virerror.h"
#include "virxml.h"
#include "virstring.h"
#include "virdomainsnapshotobjlist.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT
@ -70,14 +71,6 @@ VIR_ENUM_IMPL(virDomainSnapshotState, VIR_DOMAIN_SNAPSHOT_LAST,
"disk-snapshot",
);
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)
@ -507,116 +500,6 @@ virDomainSnapshotRedefineValidate(virDomainSnapshotDefPtr def,
}
/* Parse a <snapshots> XML entry into snapshots, which must start empty.
* Any <domain> sub-elements of a <domainsnapshot> must match domain_uuid.
*/
int
virDomainSnapshotObjListParse(const char *xmlStr,
const unsigned char *domain_uuid,
virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr *current_snap,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags)
{
int ret = -1;
xmlDocPtr xml;
xmlNodePtr root;
xmlXPathContextPtr ctxt = NULL;
int n;
size_t i;
int keepBlanksDefault = xmlKeepBlanksDefault(0);
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
VIR_AUTOFREE(char *) current = NULL;
if (!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) ||
(flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("incorrect flags for bulk parse"));
return -1;
}
if (snapshots->metaroot.nchildren || *current_snap) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("bulk define of snapshots only possible with "
"no existing snapshot"));
return -1;
}
if (!(xml = virXMLParse(NULL, xmlStr, _("(domain_snapshot)"))))
return -1;
root = xmlDocGetRootElement(xml);
if (!virXMLNodeNameEqual(root, "snapshots")) {
virReportError(VIR_ERR_XML_ERROR,
_("unexpected root element <%s>, "
"expecting <snapshots>"), root->name);
goto cleanup;
}
ctxt = xmlXPathNewContext(xml);
if (ctxt == NULL) {
virReportOOMError();
goto cleanup;
}
ctxt->node = root;
current = virXMLPropString(root, "current");
if ((n = virXPathNodeSet("./domainsnapshot", ctxt, &nodes)) < 0)
goto cleanup;
if (!n) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("expected at least one <domainsnapshot> child"));
goto cleanup;
}
for (i = 0; i < n; i++) {
virDomainSnapshotDefPtr def;
virDomainSnapshotObjPtr snap;
def = virDomainSnapshotDefParseNode(xml, nodes[i], caps, xmlopt, flags);
if (!def)
goto cleanup;
if (!(snap = virDomainSnapshotAssignDef(snapshots, def))) {
virDomainSnapshotDefFree(def);
goto cleanup;
}
if (virDomainSnapshotRedefineValidate(def, domain_uuid, NULL, NULL,
flags) < 0)
goto cleanup;
}
if (virDomainSnapshotUpdateRelations(snapshots) < 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("<snapshots> contains inconsistent parent-child "
"relationships"));
goto cleanup;
}
if (current) {
if (!(*current_snap = virDomainSnapshotFindByName(snapshots,
current))) {
virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
_("no snapshot matching current='%s'"), current);
goto cleanup;
}
(*current_snap)->def->current = true;
}
ret = 0;
cleanup:
if (ret < 0) {
/* There were no snapshots before this call; so on error, just
* blindly delete anything created before the failure. */
virHashRemoveAll(snapshots->objs);
snapshots->metaroot.nchildren = 0;
snapshots->metaroot.first_child = NULL;
}
xmlXPathFreeContext(ctxt);
xmlFreeDoc(xml);
xmlKeepBlanksDefault(keepBlanksDefault);
return ret;
}
/**
* virDomainSnapshotDefAssignExternalNames:
* @def: snapshot def object
@ -1001,406 +884,6 @@ virDomainSnapshotDefFormat(const char *uuidstr,
}
/* 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;
}
/* Snapshot Obj functions */
static virDomainSnapshotObjPtr virDomainSnapshotObjNew(void)
{
virDomainSnapshotObjPtr snapshot;
if (VIR_ALLOC(snapshot) < 0)
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,
virDomainSnapshotDefPtr def)
{
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;
if (VIR_ALLOC(snapshots) < 0)
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;
};
static int virDomainSnapshotObjListCopyNames(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *opaque)
{
virDomainSnapshotObjPtr obj = payload;
struct virDomainSnapshotNameData *data = opaque;
if (data->error)
return 0;
/* 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)
return 0;
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren)
return 0;
if (data->flags & VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS) {
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_INACTIVE) &&
obj->def->state == VIR_DOMAIN_SNAPSHOT_SHUTOFF)
return 0;
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_DISK_ONLY) &&
obj->def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)
return 0;
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_ACTIVE) &&
obj->def->state != VIR_DOMAIN_SNAPSHOT_SHUTOFF &&
obj->def->state != VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)
return 0;
}
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL) &&
virDomainSnapshotIsExternal(obj))
return 0;
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL) &&
!virDomainSnapshotIsExternal(obj))
return 0;
if (data->names && data->count < data->maxnames &&
VIR_STRDUP(data->names[data->count], obj->def->name) < 0) {
data->error = true;
return 0;
}
data->count++;
return 0;
}
int
virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
char **const names, int maxnames,
unsigned int flags)
{
struct virDomainSnapshotNameData data = { names, maxnames, flags, 0,
false };
size_t i;
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 and LIST_TOPOLOGICAL directly,
* mask those bits out to determine when we must use the filter callback. */
data.flags &= ~(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL);
/* 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;
/* For ease of coding the visitor, it is easier to zero each group
* where all of the bits are set. */
if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) ==
VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES)
data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES;
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;
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) {
/* We could just always do a topological visit; but it is
* possible to optimize for less stack usage and time when a
* simpler full hashtable visit or counter will do. */
if (from->def || (names &&
(flags & VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL)))
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
virDomainSnapshotFindByName(virDomainSnapshotObjListPtr snapshots,
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);
}
/* 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;
};
static int
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;
return 0;
}
/* Populate parent link and child count of all snapshots, with all
* 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. */
int
virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots)
{
struct snapshot_set_relation act = { snapshots, 0 };
snapshots->metaroot.nchildren = 0;
snapshots->metaroot.first_child = NULL;
virHashForEach(snapshots->objs, virDomainSnapshotSetRelations, &act);
return act.err;
}
int
virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
virDomainPtr dom,
virDomainSnapshotPtr **snaps,
unsigned int flags)
{
int count = virDomainSnapshotObjListNum(snapshots, from, flags);
virDomainSnapshotPtr *list = NULL;
char **names;
int ret = -1;
size_t i;
if (!snaps || count < 0)
return count;
if (VIR_ALLOC_N(names, count) < 0 ||
VIR_ALLOC_N(list, count + 1) < 0)
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;
cleanup:
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;
}
bool
virDomainSnapshotDefIsExternal(virDomainSnapshotDefPtr def)
{

View File

@ -24,7 +24,6 @@
# include "internal.h"
# include "domain_conf.h"
# include "virdomainsnapshotobj.h"
/* Items related to snapshot state */
@ -94,10 +93,6 @@ struct _virDomainSnapshotDef {
bool current; /* At most one snapshot in the list should have this set */
};
virDomainSnapshotObjListPtr virDomainSnapshotObjListNew(void);
void virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots);
typedef enum {
VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE = 1 << 0,
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS = 1 << 1,
@ -121,13 +116,6 @@ virDomainSnapshotDefPtr virDomainSnapshotDefParseNode(xmlDocPtr xml,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags);
int virDomainSnapshotObjListParse(const char *xmlStr,
const unsigned char *domain_uuid,
virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr *current_snap,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags);
void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def);
char *virDomainSnapshotDefFormat(const char *uuidstr,
virDomainSnapshotDefPtr def,
@ -141,34 +129,9 @@ int virDomainSnapshotDefFormatInternal(virBufferPtr buf,
virDomainXMLOptionPtr xmlopt,
unsigned int flags);
int virDomainSnapshotObjListFormat(virBufferPtr buf,
const char *uuidstr,
virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr current_snapshot,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags);
int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot,
int default_snapshot,
bool require_match);
virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotDefPtr def);
int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
char **const names, int maxnames,
unsigned int flags);
int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
unsigned int flags);
virDomainSnapshotObjPtr virDomainSnapshotFindByName(virDomainSnapshotObjListPtr snapshots,
const char *name);
void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr snapshot);
int virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots,
virHashIterator iter,
void *data);
int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots);
# define VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA \
(VIR_DOMAIN_SNAPSHOT_LIST_METADATA | \
@ -193,12 +156,6 @@ int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots);
VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS | \
VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION)
int virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
virDomainPtr dom,
virDomainSnapshotPtr **snaps,
unsigned int flags);
bool virDomainSnapshotDefIsExternal(virDomainSnapshotDefPtr def);
bool virDomainSnapshotIsExternal(virDomainSnapshotObjPtr snap);

View File

@ -30,6 +30,7 @@
#include "virfile.h"
#include "virlog.h"
#include "virstring.h"
#include "virdomainsnapshotobjlist.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN

View File

@ -25,6 +25,7 @@
#include "internal.h"
#include "virdomainsnapshotobj.h"
#include "snapshot_conf.h"
#include "virdomainsnapshotobjlist.h"
#include "virlog.h"
#include "virerror.h"

View File

@ -0,0 +1,553 @@
/*
* virdomainsnapshotobjlist.c: handle a tree of snapshot objects
* (derived from snapshot_conf.c)
*
* Copyright (C) 2006-2019 Red Hat, Inc.
* 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
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "internal.h"
#include "virdomainsnapshotobjlist.h"
#include "snapshot_conf.h"
#include "virlog.h"
#include "virerror.h"
#include "datatypes.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT
VIR_LOG_INIT("conf.virdomainsnapshotobjlist");
struct _virDomainSnapshotObjList {
/* name string -> virDomainSnapshotObj mapping
* for O(1), lockless lookup-by-name */
virHashTable *objs;
virDomainSnapshotObj metaroot; /* Special parent of all root snapshots */
};
/* Parse a <snapshots> XML entry into snapshots, which must start empty.
* Any <domain> sub-elements of a <domainsnapshot> must match domain_uuid.
*/
int
virDomainSnapshotObjListParse(const char *xmlStr,
const unsigned char *domain_uuid,
virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr *current_snap,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags)
{
int ret = -1;
xmlDocPtr xml;
xmlNodePtr root;
xmlXPathContextPtr ctxt = NULL;
int n;
size_t i;
int keepBlanksDefault = xmlKeepBlanksDefault(0);
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
VIR_AUTOFREE(char *) current = NULL;
if (!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) ||
(flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("incorrect flags for bulk parse"));
return -1;
}
if (snapshots->metaroot.nchildren || *current_snap) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("bulk define of snapshots only possible with "
"no existing snapshot"));
return -1;
}
if (!(xml = virXMLParse(NULL, xmlStr, _("(domain_snapshot)"))))
return -1;
root = xmlDocGetRootElement(xml);
if (!virXMLNodeNameEqual(root, "snapshots")) {
virReportError(VIR_ERR_XML_ERROR,
_("unexpected root element <%s>, "
"expecting <snapshots>"), root->name);
goto cleanup;
}
ctxt = xmlXPathNewContext(xml);
if (ctxt == NULL) {
virReportOOMError();
goto cleanup;
}
ctxt->node = root;
current = virXMLPropString(root, "current");
if ((n = virXPathNodeSet("./domainsnapshot", ctxt, &nodes)) < 0)
goto cleanup;
if (!n) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("expected at least one <domainsnapshot> child"));
goto cleanup;
}
for (i = 0; i < n; i++) {
virDomainSnapshotDefPtr def;
virDomainSnapshotObjPtr snap;
def = virDomainSnapshotDefParseNode(xml, nodes[i], caps, xmlopt, flags);
if (!def)
goto cleanup;
if (!(snap = virDomainSnapshotAssignDef(snapshots, def))) {
virDomainSnapshotDefFree(def);
goto cleanup;
}
if (virDomainSnapshotRedefineValidate(def, domain_uuid, NULL, NULL,
flags) < 0)
goto cleanup;
}
if (virDomainSnapshotUpdateRelations(snapshots) < 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("<snapshots> contains inconsistent parent-child "
"relationships"));
goto cleanup;
}
if (current) {
if (!(*current_snap = virDomainSnapshotFindByName(snapshots,
current))) {
virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
_("no snapshot matching current='%s'"), current);
goto cleanup;
}
(*current_snap)->def->current = true;
}
ret = 0;
cleanup:
if (ret < 0) {
/* There were no snapshots before this call; so on error, just
* blindly delete anything created before the failure. */
virHashRemoveAll(snapshots->objs);
snapshots->metaroot.nchildren = 0;
snapshots->metaroot.first_child = NULL;
}
xmlXPathFreeContext(ctxt);
xmlFreeDoc(xml);
xmlKeepBlanksDefault(keepBlanksDefault);
return ret;
}
/* 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;
}
/* Snapshot Obj functions */
static virDomainSnapshotObjPtr virDomainSnapshotObjNew(void)
{
virDomainSnapshotObjPtr snapshot;
if (VIR_ALLOC(snapshot) < 0)
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,
virDomainSnapshotDefPtr def)
{
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;
if (VIR_ALLOC(snapshots) < 0)
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;
};
static int virDomainSnapshotObjListCopyNames(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *opaque)
{
virDomainSnapshotObjPtr obj = payload;
struct virDomainSnapshotNameData *data = opaque;
if (data->error)
return 0;
/* 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)
return 0;
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren)
return 0;
if (data->flags & VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS) {
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_INACTIVE) &&
obj->def->state == VIR_DOMAIN_SNAPSHOT_SHUTOFF)
return 0;
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_DISK_ONLY) &&
obj->def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)
return 0;
if (!(data->flags & VIR_DOMAIN_SNAPSHOT_LIST_ACTIVE) &&
obj->def->state != VIR_DOMAIN_SNAPSHOT_SHUTOFF &&
obj->def->state != VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)
return 0;
}
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL) &&
virDomainSnapshotIsExternal(obj))
return 0;
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL) &&
!virDomainSnapshotIsExternal(obj))
return 0;
if (data->names && data->count < data->maxnames &&
VIR_STRDUP(data->names[data->count], obj->def->name) < 0) {
data->error = true;
return 0;
}
data->count++;
return 0;
}
int
virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
char **const names, int maxnames,
unsigned int flags)
{
struct virDomainSnapshotNameData data = { names, maxnames, flags, 0,
false };
size_t i;
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 and LIST_TOPOLOGICAL directly,
* mask those bits out to determine when we must use the filter callback. */
data.flags &= ~(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL);
/* 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;
/* For ease of coding the visitor, it is easier to zero each group
* where all of the bits are set. */
if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) ==
VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES)
data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES;
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;
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) {
/* We could just always do a topological visit; but it is
* possible to optimize for less stack usage and time when a
* simpler full hashtable visit or counter will do. */
if (from->def || (names &&
(flags & VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL)))
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
virDomainSnapshotFindByName(virDomainSnapshotObjListPtr snapshots,
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);
}
/* 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;
};
static int
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;
return 0;
}
/* Populate parent link and child count of all snapshots, with all
* 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. */
int
virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots)
{
struct snapshot_set_relation act = { snapshots, 0 };
snapshots->metaroot.nchildren = 0;
snapshots->metaroot.first_child = NULL;
virHashForEach(snapshots->objs, virDomainSnapshotSetRelations, &act);
return act.err;
}
int
virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
virDomainPtr dom,
virDomainSnapshotPtr **snaps,
unsigned int flags)
{
int count = virDomainSnapshotObjListNum(snapshots, from, flags);
virDomainSnapshotPtr *list = NULL;
char **names;
int ret = -1;
size_t i;
if (!snaps || count < 0)
return count;
if (VIR_ALLOC_N(names, count) < 0 ||
VIR_ALLOC_N(list, count + 1) < 0)
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;
cleanup:
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;
}

View File

@ -0,0 +1,73 @@
/*
* virdomainsnapshotobjlist.h: handle a tree of snapshot objects
* (derived from snapshot_conf.h)
*
* Copyright (C) 2006-2019 Red Hat, Inc.
* 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
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef LIBVIRT_VIRDOMAINSNAPSHOTOBJLIST_H
# define LIBVIRT_VIRDOMAINSNAPSHOTOBJLIST_H
# include "internal.h"
# include "virdomainsnapshotobj.h"
# include "virbuffer.h"
virDomainSnapshotObjListPtr virDomainSnapshotObjListNew(void);
void virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots);
int virDomainSnapshotObjListParse(const char *xmlStr,
const unsigned char *domain_uuid,
virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr *current_snap,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags);
int virDomainSnapshotObjListFormat(virBufferPtr buf,
const char *uuidstr,
virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr current_snapshot,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags);
virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotDefPtr def);
int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
char **const names, int maxnames,
unsigned int flags);
int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
unsigned int flags);
virDomainSnapshotObjPtr virDomainSnapshotFindByName(virDomainSnapshotObjListPtr snapshots,
const char *name);
void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr snapshot);
int virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots,
virHashIterator iter,
void *data);
int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots);
int virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
virDomainSnapshotObjPtr from,
virDomainPtr dom,
virDomainSnapshotPtr **snaps,
unsigned int flags);
#endif /* LIBVIRT_VIRDOMAINSNAPSHOTOBJLIST_H */

View File

@ -883,30 +883,18 @@ virSecretEventValueChangedNew;
# conf/snapshot_conf.h
virDomainListSnapshots;
virDomainSnapshotAlignDisks;
virDomainSnapshotAssignDef;
virDomainSnapshotDefFormat;
virDomainSnapshotDefFree;
virDomainSnapshotDefIsExternal;
virDomainSnapshotDefParseString;
virDomainSnapshotFindByName;
virDomainSnapshotForEach;
virDomainSnapshotFormatConvertXMLFlags;
virDomainSnapshotIsExternal;
virDomainSnapshotLocationTypeFromString;
virDomainSnapshotLocationTypeToString;
virDomainSnapshotObjListFormat;
virDomainSnapshotObjListFree;
virDomainSnapshotObjListGetNames;
virDomainSnapshotObjListNew;
virDomainSnapshotObjListNum;
virDomainSnapshotObjListParse;
virDomainSnapshotObjListRemove;
virDomainSnapshotRedefinePrep;
virDomainSnapshotStateTypeFromString;
virDomainSnapshotStateTypeToString;
virDomainSnapshotUpdateRelations;
# conf/storage_adapter_conf.h
@ -995,6 +983,21 @@ virDomainSnapshotForEachChild;
virDomainSnapshotForEachDescendant;
# conf/virdomainsnapshotobjlist.h
virDomainListSnapshots;
virDomainSnapshotAssignDef;
virDomainSnapshotFindByName;
virDomainSnapshotForEach;
virDomainSnapshotObjListFormat;
virDomainSnapshotObjListFree;
virDomainSnapshotObjListGetNames;
virDomainSnapshotObjListNew;
virDomainSnapshotObjListNum;
virDomainSnapshotObjListParse;
virDomainSnapshotObjListRemove;
virDomainSnapshotUpdateRelations;
# conf/virinterfaceobj.h
virInterfaceObjEndAPI;
virInterfaceObjGetDef;

View File

@ -58,6 +58,7 @@
#include "virnuma.h"
#include "virgic.h"
#include "virmdev.h"
#include "virdomainsnapshotobjlist.h"
#if defined(__linux__)
# include <linux/capability.h>
#endif

View File

@ -55,6 +55,7 @@
#include "secret_util.h"
#include "logging/log_manager.h"
#include "locking/domain_lock.h"
#include "virdomainsnapshotobjlist.h"
#ifdef MAJOR_IN_MKDEV
# include <sys/mkdev.h>

View File

@ -101,6 +101,7 @@
#include "dirname.h"
#include "netdev_bandwidth_conf.h"
#include "virqemu.h"
#include "virdomainsnapshotobjlist.h"
#define VIR_FROM_THIS VIR_FROM_QEMU

View File

@ -60,6 +60,7 @@
#include "virtypedparam.h"
#include "virprocess.h"
#include "nwfilter_conf.h"
#include "virdomainsnapshotobjlist.h"
#define VIR_FROM_THIS VIR_FROM_QEMU

View File

@ -63,6 +63,7 @@
#include "virdomainobjlist.h"
#include "virinterfaceobj.h"
#include "virhostcpu.h"
#include "virdomainsnapshotobjlist.h"
#define VIR_FROM_THIS VIR_FROM_TEST