mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-09 14:35:25 +00:00
snapshot: Create new virDomainMomentObjList type
The new code here very heavily resembles the code in virDomainSnapshotObjList. There are still a couple of spots that are tied a little too heavily to snapshots (the free function lacks a polymorphic cleanup until we refactor code to use virObject; and an upcoming patch will add internal VIR_DOMAIN_MOMENT_LIST flags to replace the snapshot flag bits), but in general this is fairly close to the state needed to add checkpoint lists. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
This commit is contained in:
parent
5d23cd1c52
commit
dc8d3dc6cd
@ -223,6 +223,9 @@ typedef virDomainMomentDef *virDomainMomentDefPtr;
|
|||||||
typedef struct _virDomainMomentObj virDomainMomentObj;
|
typedef struct _virDomainMomentObj virDomainMomentObj;
|
||||||
typedef virDomainMomentObj *virDomainMomentObjPtr;
|
typedef virDomainMomentObj *virDomainMomentObjPtr;
|
||||||
|
|
||||||
|
typedef struct _virDomainMomentObjList virDomainMomentObjList;
|
||||||
|
typedef virDomainMomentObjList *virDomainMomentObjListPtr;
|
||||||
|
|
||||||
typedef struct _virDomainNVRAMDef virDomainNVRAMDef;
|
typedef struct _virDomainNVRAMDef virDomainNVRAMDef;
|
||||||
typedef virDomainNVRAMDef *virDomainNVRAMDefPtr;
|
typedef virDomainNVRAMDef *virDomainNVRAMDefPtr;
|
||||||
|
|
||||||
|
@ -29,10 +29,25 @@
|
|||||||
#include "virstring.h"
|
#include "virstring.h"
|
||||||
#include "moment_conf.h"
|
#include "moment_conf.h"
|
||||||
|
|
||||||
|
/* FIXME: using virObject would allow us to not need this */
|
||||||
|
#include "snapshot_conf.h"
|
||||||
|
#include "virdomainsnapshotobjlist.h"
|
||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_DOMAIN
|
#define VIR_FROM_THIS VIR_FROM_DOMAIN
|
||||||
|
|
||||||
VIR_LOG_INIT("conf.virdomainmomentobjlist");
|
VIR_LOG_INIT("conf.virdomainmomentobjlist");
|
||||||
|
|
||||||
|
/* Opaque struct */
|
||||||
|
struct _virDomainMomentObjList {
|
||||||
|
/* name string -> virDomainMomentObj mapping
|
||||||
|
* for O(1), lockless lookup-by-name */
|
||||||
|
virHashTable *objs;
|
||||||
|
|
||||||
|
virDomainMomentObj metaroot; /* Special parent of all root moments */
|
||||||
|
virDomainMomentObjPtr current; /* The current moment, if any */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Run iter(data) on all direct children of moment, while ignoring all
|
/* Run iter(data) on all direct children of moment, while ignoring all
|
||||||
* other entries in moments. Return the number of children
|
* other entries in moments. Return the number of children
|
||||||
* visited. No particular ordering is guaranteed. */
|
* visited. No particular ordering is guaranteed. */
|
||||||
@ -165,3 +180,347 @@ virDomainMomentMoveChildren(virDomainMomentObjPtr from,
|
|||||||
from->nchildren = 0;
|
from->nchildren = 0;
|
||||||
from->first_child = NULL;
|
from->first_child = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static virDomainMomentObjPtr
|
||||||
|
virDomainMomentObjNew(void)
|
||||||
|
{
|
||||||
|
virDomainMomentObjPtr moment;
|
||||||
|
|
||||||
|
if (VIR_ALLOC(moment) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
VIR_DEBUG("obj=%p", moment);
|
||||||
|
|
||||||
|
return moment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
virDomainMomentObjFree(virDomainMomentObjPtr moment)
|
||||||
|
{
|
||||||
|
if (!moment)
|
||||||
|
return;
|
||||||
|
|
||||||
|
VIR_DEBUG("obj=%p", moment);
|
||||||
|
|
||||||
|
/* FIXME: Make this polymorphic by inheriting from virObject */
|
||||||
|
virDomainSnapshotDefFree(virDomainSnapshotObjGetDef(moment));
|
||||||
|
VIR_FREE(moment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Add def to the list and return a matching object, or NULL on error */
|
||||||
|
virDomainMomentObjPtr
|
||||||
|
virDomainMomentAssignDef(virDomainMomentObjListPtr moments,
|
||||||
|
virDomainMomentDefPtr def)
|
||||||
|
{
|
||||||
|
virDomainMomentObjPtr moment;
|
||||||
|
|
||||||
|
if (virHashLookup(moments->objs, def->name) != NULL) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("unexpected domain moment %s already exists"),
|
||||||
|
def->name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(moment = virDomainMomentObjNew()))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (virHashAddEntry(moments->objs, moment->def->name, moment) < 0) {
|
||||||
|
VIR_FREE(moment);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
moment->def = def;
|
||||||
|
|
||||||
|
return moment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
virDomainMomentObjListDataFree(void *payload,
|
||||||
|
const void *name ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
virDomainMomentObjPtr obj = payload;
|
||||||
|
|
||||||
|
virDomainMomentObjFree(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virDomainMomentObjListPtr
|
||||||
|
virDomainMomentObjListNew(void)
|
||||||
|
{
|
||||||
|
virDomainMomentObjListPtr moments;
|
||||||
|
|
||||||
|
if (VIR_ALLOC(moments) < 0)
|
||||||
|
return NULL;
|
||||||
|
moments->objs = virHashCreate(50, virDomainMomentObjListDataFree);
|
||||||
|
if (!moments->objs) {
|
||||||
|
VIR_FREE(moments);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return moments;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
virDomainMomentObjListFree(virDomainMomentObjListPtr moments)
|
||||||
|
{
|
||||||
|
if (!moments)
|
||||||
|
return;
|
||||||
|
virHashFree(moments->objs);
|
||||||
|
VIR_FREE(moments);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Struct and callback for collecting a list of names of moments that
|
||||||
|
* meet a particular filter. */
|
||||||
|
struct virDomainMomentNameData {
|
||||||
|
char **const names;
|
||||||
|
int maxnames;
|
||||||
|
unsigned int flags;
|
||||||
|
int count;
|
||||||
|
bool error;
|
||||||
|
virDomainMomentObjListFilter filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int virDomainMomentObjListCopyNames(void *payload,
|
||||||
|
const void *name ATTRIBUTE_UNUSED,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
virDomainMomentObjPtr obj = payload;
|
||||||
|
struct virDomainMomentNameData *data = opaque;
|
||||||
|
|
||||||
|
if (data->error)
|
||||||
|
return 0;
|
||||||
|
/* Caller already sanitized flags. Filtering on DESCENDANTS was
|
||||||
|
* done by choice of iteration in the caller. */
|
||||||
|
/* TODO: Create VIR_DOMAIN_MOMENT_LIST names */
|
||||||
|
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->filter(obj, data->flags))
|
||||||
|
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
|
||||||
|
virDomainMomentObjListGetNames(virDomainMomentObjListPtr moments,
|
||||||
|
virDomainMomentObjPtr from,
|
||||||
|
char **const names,
|
||||||
|
int maxnames,
|
||||||
|
unsigned int flags,
|
||||||
|
virDomainMomentObjListFilter filter)
|
||||||
|
{
|
||||||
|
struct virDomainMomentNameData data = { names, maxnames, flags, 0,
|
||||||
|
false, filter };
|
||||||
|
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. */
|
||||||
|
/* TODO: Create VIR_DOMAIN_MOMENT_LIST names */
|
||||||
|
flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
|
||||||
|
from = &moments->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 moments
|
||||||
|
* 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, in which case this check should move into the
|
||||||
|
* filter callback. */
|
||||||
|
if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA) ==
|
||||||
|
VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA)
|
||||||
|
return 0;
|
||||||
|
data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA;
|
||||||
|
|
||||||
|
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)))
|
||||||
|
virDomainMomentForEachDescendant(from,
|
||||||
|
virDomainMomentObjListCopyNames,
|
||||||
|
&data);
|
||||||
|
else if (names || data.flags)
|
||||||
|
virHashForEach(moments->objs, virDomainMomentObjListCopyNames,
|
||||||
|
&data);
|
||||||
|
else
|
||||||
|
data.count = virHashSize(moments->objs);
|
||||||
|
} else if (names || data.flags) {
|
||||||
|
virDomainMomentForEachChild(from,
|
||||||
|
virDomainMomentObjListCopyNames, &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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virDomainMomentObjPtr
|
||||||
|
virDomainMomentFindByName(virDomainMomentObjListPtr moments,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
return name ? virHashLookup(moments->objs, name) : &moments->metaroot;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the current moment, or NULL */
|
||||||
|
virDomainMomentObjPtr
|
||||||
|
virDomainMomentGetCurrent(virDomainMomentObjListPtr moments)
|
||||||
|
{
|
||||||
|
return moments->current;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the current moment's name, or NULL */
|
||||||
|
const char *
|
||||||
|
virDomainMomentGetCurrentName(virDomainMomentObjListPtr moments)
|
||||||
|
{
|
||||||
|
if (moments->current)
|
||||||
|
return moments->current->def->name;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return true if name matches the current moment */
|
||||||
|
bool
|
||||||
|
virDomainMomentIsCurrentName(virDomainMomentObjListPtr moments,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
return moments->current && STREQ(moments->current->def->name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Update the current moment, using NULL if no current remains */
|
||||||
|
void
|
||||||
|
virDomainMomentSetCurrent(virDomainMomentObjListPtr moments,
|
||||||
|
virDomainMomentObjPtr moment)
|
||||||
|
{
|
||||||
|
moments->current = moment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the number of moments in the list */
|
||||||
|
int
|
||||||
|
virDomainMomentObjListSize(virDomainMomentObjListPtr moments)
|
||||||
|
{
|
||||||
|
return virHashSize(moments->objs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Remove moment from the list; return true if it was current */
|
||||||
|
bool
|
||||||
|
virDomainMomentObjListRemove(virDomainMomentObjListPtr moments,
|
||||||
|
virDomainMomentObjPtr moment)
|
||||||
|
{
|
||||||
|
bool ret = moments->current == moment;
|
||||||
|
|
||||||
|
virHashRemoveEntry(moments->objs, moment->def->name);
|
||||||
|
if (ret)
|
||||||
|
moments->current = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Remove all moments tracked in the list */
|
||||||
|
void
|
||||||
|
virDomainMomentObjListRemoveAll(virDomainMomentObjListPtr moments)
|
||||||
|
{
|
||||||
|
virHashRemoveAll(moments->objs);
|
||||||
|
virDomainMomentDropChildren(&moments->metaroot);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Call iter on each member of the list, in unspecified order */
|
||||||
|
int
|
||||||
|
virDomainMomentForEach(virDomainMomentObjListPtr moments,
|
||||||
|
virHashIterator iter,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
return virHashForEach(moments->objs, iter, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Struct and callback function used as a hash table callback; each call
|
||||||
|
* inspects the pre-existing moment->def->parent field, and adjusts
|
||||||
|
* the moment->parent field as well as the parent's child fields to
|
||||||
|
* wire up the hierarchical relations for the given moment. The error
|
||||||
|
* indicator gets set if a parent is missing or a requested parent would
|
||||||
|
* cause a circular parent chain. */
|
||||||
|
struct moment_set_relation {
|
||||||
|
virDomainMomentObjListPtr moments;
|
||||||
|
int err;
|
||||||
|
};
|
||||||
|
static int
|
||||||
|
virDomainMomentSetRelations(void *payload,
|
||||||
|
const void *name ATTRIBUTE_UNUSED,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
virDomainMomentObjPtr obj = payload;
|
||||||
|
struct moment_set_relation *curr = data;
|
||||||
|
virDomainMomentObjPtr tmp;
|
||||||
|
virDomainMomentObjPtr parent;
|
||||||
|
|
||||||
|
parent = virDomainMomentFindByName(curr->moments, obj->def->parent);
|
||||||
|
if (!parent) {
|
||||||
|
curr->err = -1;
|
||||||
|
parent = &curr->moments->metaroot;
|
||||||
|
VIR_WARN("moment %s lacks parent", obj->def->name);
|
||||||
|
} else {
|
||||||
|
tmp = parent;
|
||||||
|
while (tmp && tmp->def) {
|
||||||
|
if (tmp == obj) {
|
||||||
|
curr->err = -1;
|
||||||
|
parent = &curr->moments->metaroot;
|
||||||
|
VIR_WARN("moment %s in circular chain", obj->def->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tmp = tmp->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virDomainMomentSetParent(obj, parent);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Populate parent link and child count of all moments, 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
|
||||||
|
virDomainMomentUpdateRelations(virDomainMomentObjListPtr moments)
|
||||||
|
{
|
||||||
|
struct moment_set_relation act = { moments, 0 };
|
||||||
|
|
||||||
|
virDomainMomentDropChildren(&moments->metaroot);
|
||||||
|
virHashForEach(moments->objs, virDomainMomentSetRelations, &act);
|
||||||
|
if (act.err)
|
||||||
|
moments->current = NULL;
|
||||||
|
return act.err;
|
||||||
|
}
|
||||||
|
@ -27,6 +27,10 @@
|
|||||||
# include "virconftypes.h"
|
# include "virconftypes.h"
|
||||||
# include "virhash.h"
|
# include "virhash.h"
|
||||||
|
|
||||||
|
/* Filter that returns true if a given moment matches the filter flags */
|
||||||
|
typedef bool (*virDomainMomentObjListFilter)(virDomainMomentObjPtr obj,
|
||||||
|
unsigned int flags);
|
||||||
|
|
||||||
/* Struct that allows tracing hierarchical relationships between
|
/* Struct that allows tracing hierarchical relationships between
|
||||||
* multiple virDomainMoment objects. The opaque type
|
* multiple virDomainMoment objects. The opaque type
|
||||||
* virDomainMomentObjList then maintains both a hash of these structs
|
* virDomainMomentObjList then maintains both a hash of these structs
|
||||||
@ -60,4 +64,33 @@ void virDomainMomentMoveChildren(virDomainMomentObjPtr from,
|
|||||||
void virDomainMomentSetParent(virDomainMomentObjPtr moment,
|
void virDomainMomentSetParent(virDomainMomentObjPtr moment,
|
||||||
virDomainMomentObjPtr parent);
|
virDomainMomentObjPtr parent);
|
||||||
|
|
||||||
|
virDomainMomentObjListPtr virDomainMomentObjListNew(void);
|
||||||
|
void virDomainMomentObjListFree(virDomainMomentObjListPtr moments);
|
||||||
|
|
||||||
|
virDomainMomentObjPtr virDomainMomentAssignDef(virDomainMomentObjListPtr moments,
|
||||||
|
virDomainMomentDefPtr def);
|
||||||
|
|
||||||
|
int virDomainMomentObjListGetNames(virDomainMomentObjListPtr moments,
|
||||||
|
virDomainMomentObjPtr from,
|
||||||
|
char **const names,
|
||||||
|
int maxnames,
|
||||||
|
unsigned int flags,
|
||||||
|
virDomainMomentObjListFilter filter);
|
||||||
|
virDomainMomentObjPtr virDomainMomentFindByName(virDomainMomentObjListPtr moments,
|
||||||
|
const char *name);
|
||||||
|
int virDomainMomentObjListSize(virDomainMomentObjListPtr moments);
|
||||||
|
virDomainMomentObjPtr virDomainMomentGetCurrent(virDomainMomentObjListPtr moments);
|
||||||
|
const char *virDomainMomentGetCurrentName(virDomainMomentObjListPtr moments);
|
||||||
|
bool virDomainMomentIsCurrentName(virDomainMomentObjListPtr moments,
|
||||||
|
const char *name);
|
||||||
|
void virDomainMomentSetCurrent(virDomainMomentObjListPtr moments,
|
||||||
|
virDomainMomentObjPtr moment);
|
||||||
|
bool virDomainMomentObjListRemove(virDomainMomentObjListPtr moments,
|
||||||
|
virDomainMomentObjPtr moment);
|
||||||
|
void virDomainMomentObjListRemoveAll(virDomainMomentObjListPtr moments);
|
||||||
|
int virDomainMomentForEach(virDomainMomentObjListPtr moments,
|
||||||
|
virHashIterator iter,
|
||||||
|
void *data);
|
||||||
|
int virDomainMomentUpdateRelations(virDomainMomentObjListPtr moments);
|
||||||
|
|
||||||
#endif /* LIBVIRT_VIRDOMAINMOMENTOBJLIST_H */
|
#endif /* LIBVIRT_VIRDOMAINMOMENTOBJLIST_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user