mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-09 06:25:19 +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 virDomainMomentObj *virDomainMomentObjPtr;
|
||||
|
||||
typedef struct _virDomainMomentObjList virDomainMomentObjList;
|
||||
typedef virDomainMomentObjList *virDomainMomentObjListPtr;
|
||||
|
||||
typedef struct _virDomainNVRAMDef virDomainNVRAMDef;
|
||||
typedef virDomainNVRAMDef *virDomainNVRAMDefPtr;
|
||||
|
||||
|
@ -29,10 +29,25 @@
|
||||
#include "virstring.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
|
||||
|
||||
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
|
||||
* other entries in moments. Return the number of children
|
||||
* visited. No particular ordering is guaranteed. */
|
||||
@ -165,3 +180,347 @@ virDomainMomentMoveChildren(virDomainMomentObjPtr from,
|
||||
from->nchildren = 0;
|
||||
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 "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
|
||||
* multiple virDomainMoment objects. The opaque type
|
||||
* virDomainMomentObjList then maintains both a hash of these structs
|
||||
@ -60,4 +64,33 @@ void virDomainMomentMoveChildren(virDomainMomentObjPtr from,
|
||||
void virDomainMomentSetParent(virDomainMomentObjPtr moment,
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user