Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
---
src/conf/domain_conf.c | 402 ++++++++++++++++++++++++++++++++++++++++++++--
src/conf/domain_conf.h | 53 ++++++
src/libvirt_private.syms | 10 ++
3 files changed, 455 insertions(+), 10 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index e260dce..1971b9a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -28,6 +28,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
+#include <sys/time.h>
#include "virterror_internal.h"
#include "datatypes.h"
@@ -742,6 +743,8 @@ static void virDomainObjFree(virDomainObjPtr dom)
virMutexDestroy(&dom->lock);
+ virDomainSnapshotObjListDeinit(&dom->snapshots);
+
VIR_FREE(dom);
}
@@ -794,6 +797,8 @@ static virDomainObjPtr virDomainObjNew(virCapsPtr caps)
domain->state = VIR_DOMAIN_SHUTOFF;
domain->refs = 1;
+ virDomainSnapshotObjListInit(&domain->snapshots);
+
VIR_DEBUG("obj=%p", domain);
return domain;
}
@@ -6096,22 +6101,25 @@ static virDomainObjPtr virDomainLoadConfig(virCapsPtr caps,
if ((configFile = virDomainConfigFile(configDir, name)) == NULL)
goto error;
- if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL)
- goto error;
-
- if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
- goto error;
-
if (!(def = virDomainDefParseFile(caps, configFile,
VIR_DOMAIN_XML_INACTIVE)))
goto error;
- if ((dom = virDomainFindByName(doms, def->name))) {
- virDomainObjUnlock(dom);
- dom = NULL;
- newVM = 0;
+ /* if the domain is already in our hashtable, we don't need to do
+ * anything further
+ */
+ if ((dom = virDomainFindByUUID(doms, def->uuid))) {
+ VIR_FREE(configFile);
+ virDomainDefFree(def);
+ return dom;
}
+ if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL)
+ goto error;
+
+ if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
+ goto error;
+
if (!(dom = virDomainAssignDef(caps, doms, def, false)))
goto error;
@@ -6509,4 +6517,378 @@ cleanup:
return -1;
}
+/* Snapshot Def functions */
+void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
+{
+ if (!def)
+ return;
+
+ VIR_FREE(def->name);
+ VIR_FREE(def->description);
+ VIR_FREE(def->parent);
+ VIR_FREE(def);
+}
+
+virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
+ int newSnapshot)
+{
+ xmlXPathContextPtr ctxt = NULL;
+ xmlDocPtr xml = NULL;
+ xmlNodePtr root;
+ virDomainSnapshotDefPtr def = NULL;
+ virDomainSnapshotDefPtr ret = NULL;
+ char *creation = NULL, *state = NULL;
+ struct timeval tv;
+ struct tm time_info;
+ char timestr[100];
+
+ xml = virXMLParse(NULL, xmlStr, "domainsnapshot.xml");
+ if (!xml) {
+ virDomainReportError(VIR_ERR_XML_ERROR,
+ "%s",_("failed to parse snapshot xml
document"));
+ return NULL;
+ }
+
+ if ((root = xmlDocGetRootElement(xml)) == NULL) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing root element"));
+ goto cleanup;
+ }
+
+ if (!xmlStrEqual(root->name, BAD_CAST "domainsnapshot")) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("incorrect root element"));
+ goto cleanup;
+ }
+
+ ctxt = xmlXPathNewContext(xml);
+ if (ctxt == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC(def) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ctxt->node = root;
+
+ def->name = virXPathString("string(./name)", ctxt);
+ if (def->name == NULL) {
+ /* make up a name */
+ gettimeofday(&tv, NULL);
+ localtime_r(&tv.tv_sec, &time_info);
+ strftime(timestr, sizeof(timestr), "%F_%T", &time_info);
+ def->name = strdup(timestr);
+ }
+ if (def->name == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ def->description = virXPathString("string(./description)", ctxt);
+
+ if (!newSnapshot) {
+ creation = virXPathString("string(./creationTime)", ctxt);
+ if (creation == NULL) {
+ /* there was no creation time in an existing snapshot; this
+ * should never happen
+ */
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing creationTime from existing
snapshot"));
+ goto cleanup;
+ }
+
+ if (strptime(creation, "%Y-%m-%d_%T", &time_info) == NULL) {
+ /* we saw a creation time we couldn't parse. We shouldn't
+ * ever see this, so throw an error
+ */
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Could not convert '%s' to a time
value"),
+ creation);
+ goto cleanup;
+ }
+ def->creationTime = mktime(&time_info);
+
+ 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
+ */
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing state from existing snapshot"));
+ goto cleanup;
+ }
+ def->state = virDomainStateTypeFromString(state);
+ if (def->state < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Invalid state '%s' in domain snapshot
XML"),
+ state);
+ goto cleanup;
+ }
+ }
+ else {
+ gettimeofday(&tv, NULL);
+ def->creationTime = tv.tv_sec;
+ }
+
+ ret = def;
+
+cleanup:
+ VIR_FREE(creation);
+ VIR_FREE(state);
+ if (ctxt)
+ xmlXPathFreeContext(ctxt);
+ if (ret == NULL)
+ virDomainSnapshotDefFree(def);
+ xmlFreeDoc(xml);
+
+ return ret;
+}
+
+char *virDomainSnapshotDefFormat(char *domain_uuid,
+ virDomainSnapshotDefPtr def)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ char timestr[100];
+ struct tm time_info;
+
+ virBufferAddLit(&buf, "<domainsnapshot>\n");
+ virBufferVSprintf(&buf, " <name>%s</name>\n",
def->name);
+ if (def->description)
+ virBufferVSprintf(&buf, "
<description>%s</description>\n",
+ def->description);
+ virBufferVSprintf(&buf, " <state>%s</state>\n",
+ virDomainStateTypeToString(def->state));
+ if (def->parent) {
+ virBufferAddLit(&buf, " <parent>\n");
+ virBufferVSprintf(&buf, " <name>%s</name>\n",
def->parent);
+ virBufferAddLit(&buf, " </parent>\n");
+ }
+ localtime_r(&def->creationTime, &time_info);
+ strftime(timestr, sizeof(timestr), "%F_%T", &time_info);
+ virBufferVSprintf(&buf, "
<creationTime>%s</creationTime>\n", timestr);
+ virBufferAddLit(&buf, " <domain>\n");
+ virBufferVSprintf(&buf, " <uuid>%s</uuid>\n",
domain_uuid);
+ virBufferAddLit(&buf, " </domain>\n");
+ virBufferAddLit(&buf, "</domainsnapshot>\n");
+
+ if (virBufferError(&buf)) {
+ virBufferFreeAndReset(&buf);
+ virReportOOMError();
+ return NULL;
+ }
+
+ return virBufferContentAndReset(&buf);
+}
+
+/* Snapshot Obj functions */
+static virDomainSnapshotObjPtr virDomainSnapshotObjNew(void)
+{
+ virDomainSnapshotObjPtr snapshot;
+
+ if (VIR_ALLOC(snapshot) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ snapshot->refs = 1;
+
+ VIR_DEBUG("obj=%p", snapshot);
+
+ return snapshot;
+}
+
+static void virDomainSnapshotObjFree(virDomainSnapshotObjPtr snapshot)
+{
+ if (!snapshot)
+ return;
+
+ VIR_DEBUG("obj=%p", snapshot);
+
+ virDomainSnapshotDefFree(snapshot->def);
+}
+
+int virDomainSnapshotObjUnref(virDomainSnapshotObjPtr snapshot)
+{
+ snapshot->refs--;
+ VIR_DEBUG("obj=%p refs=%d", snapshot, snapshot->refs);
+ if (snapshot->refs == 0) {
+ virDomainSnapshotObjFree(snapshot);
+ return 0;
+ }
+ return snapshot->refs;
+}
+
+virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr
snapshots,
+ const virDomainSnapshotDefPtr def)
+{
+ virDomainSnapshotObjPtr snap;
+
+ if (virHashLookup(snapshots->objs, def->name) != NULL) {
+ virDomainReportError(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);
+ virReportOOMError();
+ return NULL;
+ }
+
+ return snap;
+}
+
+/* Snapshot Obj List functions */
+int virDomainSnapshotObjListInit(virDomainSnapshotObjListPtr snapshots)
+{
+ snapshots->objs = virHashCreate(50);
+ if (!snapshots->objs) {
+ virReportOOMError();
+ return -1;
+ }
+ return 0;
+}
+
+static void virDomainSnapshotObjListDeallocator(void *payload,
+ const char *name ATTRIBUTE_UNUSED)
+{
+ virDomainSnapshotObjPtr obj = payload;
+
+ virDomainSnapshotObjUnref(obj);
+}
+
+void virDomainSnapshotObjListDeinit(virDomainSnapshotObjListPtr snapshots)
+{
+ if (snapshots->objs)
+ virHashFree(snapshots->objs, virDomainSnapshotObjListDeallocator);
+}
+
+struct virDomainSnapshotNameData {
+ int oom;
+ int numnames;
+ int maxnames;
+ char **const names;
+};
+
+static void virDomainSnapshotObjListCopyNames(void *payload,
+ const char *name ATTRIBUTE_UNUSED,
+ void *opaque)
+{
+ virDomainSnapshotObjPtr obj = payload;
+ struct virDomainSnapshotNameData *data = opaque;
+
+ if (data->oom)
+ return;
+
+ if (data->numnames < data->maxnames) {
+ if (!(data->names[data->numnames] = strdup(obj->def->name)))
+ data->oom = 1;
+ else
+ data->numnames++;
+ }
+}
+
+int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
+ char **const names, int maxnames)
+{
+ struct virDomainSnapshotNameData data = { 0, 0, maxnames, names };
+ int i;
+
+ virHashForEach(snapshots->objs, virDomainSnapshotObjListCopyNames, &data);
+ if (data.oom) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ return data.numnames;
+
+cleanup:
+ for (i = 0; i < data.numnames; i++)
+ VIR_FREE(data.names[i]);
+ return -1;
+}
+
+static void virDomainSnapshotObjListCount(void *payload ATTRIBUTE_UNUSED,
+ const char *name ATTRIBUTE_UNUSED,
+ void *data)
+{
+ int *count = data;
+
+ (*count)++;
+}
+
+int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots)
+{
+ int count = 0;
+
+ virHashForEach(snapshots->objs, virDomainSnapshotObjListCount, &count);
+
+ return count;
+}
+
+static int virDomainSnapshotObjListSearchName(const void *payload,
+ const char *name ATTRIBUTE_UNUSED,
+ const void *data)
+{
+ virDomainSnapshotObjPtr obj = (virDomainSnapshotObjPtr)payload;
+ int want = 0;
+
+ if (STREQ(obj->def->name, (const char *)data))
+ want = 1;
+
+ return want;
+}
+
+virDomainSnapshotObjPtr virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr
snapshots,
+ const char *name)
+{
+ return virHashSearch(snapshots->objs, virDomainSnapshotObjListSearchName, name);
+}
+
+void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
+ virDomainSnapshotObjPtr snapshot)
+{
+ virHashRemoveEntry(snapshots->objs, snapshot->def->name,
+ virDomainSnapshotObjListDeallocator);
+}
+
+struct snapshot_has_children {
+ char *name;
+ int number;
+};
+
+static void virDomainSnapshotCountChildren(void *payload,
+ const char *name ATTRIBUTE_UNUSED,
+ void *data)
+{
+ virDomainSnapshotObjPtr obj = payload;
+ struct snapshot_has_children *curr = data;
+
+ if (obj->def->parent && STREQ(obj->def->parent, curr->name))
+ curr->number++;
+}
+
+int virDomainSnapshotHasChildren(virDomainSnapshotObjPtr snap,
+ virDomainSnapshotObjListPtr snapshots)
+{
+ struct snapshot_has_children children;
+
+ children.name = snap->def->name;
+ children.number = 0;
+ virHashForEach(snapshots->objs, virDomainSnapshotCountChildren, &children);
+
+ return children.number;
+}
+
+
#endif /* ! PROXY */
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 023196f..8d5276b 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -729,6 +729,55 @@ struct _virDomainClockDef {
# define VIR_DOMAIN_CPUMASK_LEN 1024
+
+/* Snapshot state */
+typedef struct _virDomainSnapshotDef virDomainSnapshotDef;
+typedef virDomainSnapshotDef *virDomainSnapshotDefPtr;
+struct _virDomainSnapshotDef {
+ char *name;
+ char *description;
+ char *parent;
+ time_t creationTime;
+ int state;
+};
+
+typedef struct _virDomainSnapshotObj virDomainSnapshotObj;
+typedef virDomainSnapshotObj *virDomainSnapshotObjPtr;
+struct _virDomainSnapshotObj {
+ int refs;
+
+ virDomainSnapshotDefPtr def;
+};
+
+typedef struct _virDomainSnapshotObjList virDomainSnapshotObjList;
+typedef virDomainSnapshotObjList *virDomainSnapshotObjListPtr;
+struct _virDomainSnapshotObjList {
+ /* name string -> virDomainSnapshotObj mapping
+ * for O(1), lockless lookup-by-name */
+ virHashTable *objs;
+};
+
+virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
+ int newSnapshot);
+void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def);
+char *virDomainSnapshotDefFormat(char *domain_uuid,
+ virDomainSnapshotDefPtr def);
+virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr
snapshots,
+ const virDomainSnapshotDefPtr def);
+
+int virDomainSnapshotObjListInit(virDomainSnapshotObjListPtr objs);
+void virDomainSnapshotObjListDeinit(virDomainSnapshotObjListPtr objs);
+int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
+ char **const names, int maxnames);
+int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots);
+virDomainSnapshotObjPtr virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr
snapshots,
+ const char *name);
+void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
+ virDomainSnapshotObjPtr snapshot);
+int virDomainSnapshotObjUnref(virDomainSnapshotObjPtr snapshot);
+int virDomainSnapshotHasChildren(virDomainSnapshotObjPtr snap,
+ virDomainSnapshotObjListPtr snapshots);
+
/* Guest VM main configuration */
typedef struct _virDomainDef virDomainDef;
typedef virDomainDef *virDomainDefPtr;
@@ -798,6 +847,8 @@ struct _virDomainDef {
virSecurityLabelDef seclabel;
virDomainWatchdogDefPtr watchdog;
virCPUDefPtr cpu;
+
+ virDomainSnapshotDefPtr current_snapshot;
};
/* Guest VM runtime state */
@@ -816,6 +867,8 @@ struct _virDomainObj {
virDomainDefPtr def; /* The current definition */
virDomainDefPtr newDef; /* New definition to activate at shutdown */
+ virDomainSnapshotObjList snapshots;
+
void *privateData;
void (*privateDataFreeFunc)(void *);
};
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c402720..2bdd97b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -206,6 +206,16 @@ virDomainTimerTickpolicyTypeToString;
virDomainTimerTickpolicyTypeFromString;
virDomainTimerModeTypeToString;
virDomainTimerModeTypeFromString;
+virDomainSnapshotObjListGetNames;
+virDomainSnapshotObjListNum;
+virDomainSnapshotFindByName;
+virDomainSnapshotObjListAdd;
+virDomainSnapshotObjListRemove;
+virDomainSnapshotHasChildren;
+virDomainSnapshotObjUnref;
+virDomainSnapshotDefParseString;
+virDomainSnapshotDefFormat;
+virDomainSnapshotAssignDef;
# domain_event.h
--
1.6.6.1