diff --git a/po/POTFILES.in b/po/POTFILES.in index 6c301166929e83970e175f356f74c6b01e69e204..7a91eb4a9bec83d3b13ae0ed009fde7fdafdc4d7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -19,6 +19,7 @@ src/conf/node_device_conf.c src/conf/nwfilter_conf.c src/conf/nwfilter_params.c src/conf/secret_conf.c +src/conf/snapshot_conf.c src/conf/storage_conf.c src/conf/storage_encryption_conf.c src/conf/virconsole.c diff --git a/src/Makefile.am b/src/Makefile.am index 2074571252adbcd57d43cbcf63e7c7a4e48cf077..995e032d58358f992a88983fdc621e67e4e52923 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -153,7 +153,8 @@ DOMAIN_CONF_SOURCES = \ conf/capabilities.c conf/capabilities.h \ conf/domain_conf.c conf/domain_conf.h \ conf/domain_audit.c conf/domain_audit.h \ - conf/domain_nwfilter.c conf/domain_nwfilter.h + conf/domain_nwfilter.c conf/domain_nwfilter.h \ + conf/snapshot_conf.c conf/snapshot_conf.h DOMAIN_EVENT_SOURCES = \ conf/domain_event.c conf/domain_event.h diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 822d309e07617c5d54cdb8e4b6118d9982361cd3..a31beced8a7b75f9020bd646b4dc2cba57c978e0 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -23,18 +23,17 @@ #include -#include -#include -#include -#include #include -#include +#include #include +#include +#include #include "internal.h" #include "virterror_internal.h" #include "datatypes.h" #include "domain_conf.h" +#include "snapshot_conf.h" #include "memory.h" #include "verify.h" #include "xml.h" @@ -226,12 +225,6 @@ VIR_ENUM_IMPL(virDomainDiskCopyOnRead, VIR_DOMAIN_DISK_COPY_ON_READ_LAST, "on", "off") -VIR_ENUM_IMPL(virDomainDiskSnapshot, VIR_DOMAIN_DISK_SNAPSHOT_LAST, - "default", - "no", - "internal", - "external") - VIR_ENUM_IMPL(virDomainController, VIR_DOMAIN_CONTROLLER_TYPE_LAST, "ide", "fdc", @@ -528,18 +521,6 @@ VIR_ENUM_IMPL(virDomainState, VIR_DOMAIN_LAST, "crashed", "pmsuspended") -/* virDomainSnapshotState is really virDomainState plus one extra state */ -VIR_ENUM_IMPL(virDomainSnapshotState, VIR_DOMAIN_SNAPSHOT_STATE_LAST, - "nostate", - "running", - "blocked", - "paused", - "shutdown", - "shutoff", - "crashed", - "pmsuspended", - "disk-snapshot") - #define VIR_DOMAIN_NOSTATE_LAST (VIR_DOMAIN_NOSTATE_UNKNOWN + 1) VIR_ENUM_IMPL(virDomainNostateReason, VIR_DOMAIN_NOSTATE_LAST, "unknown") @@ -661,15 +642,6 @@ VIR_ENUM_IMPL(virDomainNumatuneMemPlacementMode, #define VIR_DOMAIN_XML_WRITE_FLAGS VIR_DOMAIN_XML_SECURE #define VIR_DOMAIN_XML_READ_FLAGS VIR_DOMAIN_XML_INACTIVE -struct _virDomainSnapshotObjList { - /* name string -> virDomainSnapshotObj mapping - * for O(1), lockless lookup-by-name */ - virHashTable *objs; - - virDomainSnapshotObj metaroot; /* Special parent of all root snapshots */ -}; - - static virClassPtr virDomainObjClass; static void virDomainObjDispose(void *obj); @@ -14145,856 +14117,6 @@ cleanup: return -1; } -/* Snapshot Def functions */ -static void -virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk) -{ - VIR_FREE(disk->name); - VIR_FREE(disk->file); - VIR_FREE(disk->driverType); -} - -void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def) -{ - int i; - - if (!def) - return; - - VIR_FREE(def->name); - VIR_FREE(def->description); - VIR_FREE(def->parent); - for (i = 0; i < def->ndisks; i++) - virDomainSnapshotDiskDefClear(&def->disks[i]); - VIR_FREE(def->disks); - virDomainDefFree(def->dom); - VIR_FREE(def); -} - -static int -virDomainSnapshotDiskDefParseXML(xmlNodePtr node, - virDomainSnapshotDiskDefPtr def) -{ - int ret = -1; - char *snapshot = NULL; - xmlNodePtr cur; - - 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) { - def->snapshot = virDomainDiskSnapshotTypeFromString(snapshot); - if (def->snapshot <= 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown disk snapshot setting '%s'"), - snapshot); - goto cleanup; - } - } - - cur = node->children; - while (cur) { - if (cur->type == XML_ELEMENT_NODE) { - if (!def->file && - xmlStrEqual(cur->name, BAD_CAST "source")) { - def->file = virXMLPropString(cur, "file"); - } else if (!def->driverType && - xmlStrEqual(cur->name, BAD_CAST "driver")) { - def->driverType = virXMLPropString(cur, "type"); - } - } - cur = cur->next; - } - - if (!def->snapshot && (def->file || def->driverType)) - def->snapshot = VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL; - - ret = 0; -cleanup: - VIR_FREE(snapshot); - if (ret < 0) - virDomainSnapshotDiskDefClear(def); - return ret; -} - -/* flags is bitwise-or of virDomainSnapshotParseFlags. - * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then - * caps and expectedVirtTypes are ignored. - */ -virDomainSnapshotDefPtr -virDomainSnapshotDefParseString(const char *xmlStr, - virCapsPtr caps, - unsigned int expectedVirtTypes, - unsigned int flags) -{ - xmlXPathContextPtr ctxt = NULL; - xmlDocPtr xml = NULL; - virDomainSnapshotDefPtr def = NULL; - virDomainSnapshotDefPtr ret = NULL; - xmlNodePtr *nodes = NULL; - int i; - char *creation = NULL, *state = NULL; - struct timeval tv; - int active; - char *tmp; - int keepBlanksDefault = xmlKeepBlanksDefault(0); - - xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt); - if (!xml) { - xmlKeepBlanksDefault(keepBlanksDefault); - return NULL; - } - xmlKeepBlanksDefault(keepBlanksDefault); - - if (VIR_ALLOC(def) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (!xmlStrEqual(ctxt->node->name, BAD_CAST "domainsnapshot")) { - virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot")); - 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; - } else { - ignore_value(virAsprintf(&def->name, "%lld", - (long long)tv.tv_sec)); - } - } - - if (def->name == NULL) { - virReportOOMError(); - goto cleanup; - } - - 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) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid state '%s' in domain snapshot XML"), - state); - goto cleanup; - } - - /* Older snapshots were created with just /, 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))) { - xmlNodePtr domainNode = virXPathNode("./domain", ctxt); - - VIR_FREE(tmp); - if (!domainNode) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing domain in snapshot")); - goto cleanup; - } - def->dom = virDomainDefParseNode(caps, xml, domainNode, - expectedVirtTypes, - (VIR_DOMAIN_XML_INACTIVE | - VIR_DOMAIN_XML_SECURE)); - if (!def->dom) - goto cleanup; - } else { - VIR_WARN("parsing older snapshot that lacks domain"); - } - } else { - def->creationTime = tv.tv_sec; - } - - if ((i = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0) - goto cleanup; - if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS || - (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE && - def->state == VIR_DOMAIN_DISK_SNAPSHOT)) { - def->ndisks = i; - if (def->ndisks && VIR_ALLOC_N(def->disks, def->ndisks) < 0) { - virReportOOMError(); - goto cleanup; - } - for (i = 0; i < def->ndisks; i++) { - if (virDomainSnapshotDiskDefParseXML(nodes[i], &def->disks[i]) < 0) - goto cleanup; - } - VIR_FREE(nodes); - } else if (i) { - 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; - } - - ret = def; - -cleanup: - VIR_FREE(creation); - VIR_FREE(state); - VIR_FREE(nodes); - xmlXPathFreeContext(ctxt); - if (ret == NULL) - virDomainSnapshotDefFree(def); - xmlFreeDoc(xml); - - return ret; -} - -static int -disksorter(const void *a, const void *b) -{ - const virDomainSnapshotDiskDef *diska = a; - const virDomainSnapshotDiskDef *diskb = b; - - /* Integer overflow shouldn't be a problem here. */ - return diska->index - diskb->index; -} - -/* 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 - * dom->disks. If require_match, also require that existing - * def->disks snapshot states do not override explicit def->dom - * settings. */ -int -virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def, - int default_snapshot, - bool require_match) -{ - int ret = -1; - virBitmapPtr map = NULL; - int i; - int ndisks; - bool inuse; - - 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; - } - - if (!(map = virBitmapAlloc(def->dom->ndisks))) { - virReportOOMError(); - 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; - } - disk_snapshot = def->dom->disks[idx]->snapshot; - - if (virBitmapGetBit(map, idx, &inuse) < 0 || inuse) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk '%s' specified twice"), - disk->name); - goto cleanup; - } - ignore_value(virBitmapSetBit(map, idx)); - disk->index = idx; - if (!disk_snapshot) - disk_snapshot = default_snapshot; - if (!disk->snapshot) { - disk->snapshot = disk_snapshot; - } else if (disk_snapshot && require_match && - disk->snapshot != disk_snapshot) { - const char *tmp = virDomainDiskSnapshotTypeToString(disk_snapshot); - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk '%s' must use snapshot mode '%s'"), - disk->name, tmp); - goto cleanup; - } - if (disk->file && - disk->snapshot != VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("file '%s' for disk '%s' requires " - "use of external snapshot mode"), - disk->file, disk->name); - goto cleanup; - } - if (STRNEQ(disk->name, def->dom->disks[idx]->dst)) { - VIR_FREE(disk->name); - if (!(disk->name = strdup(def->dom->disks[idx]->dst))) { - virReportOOMError(); - goto cleanup; - } - } - } - - /* Provide defaults for all remaining disks. */ - ndisks = def->ndisks; - if (VIR_EXPAND_N(def->disks, def->ndisks, - def->dom->ndisks - def->ndisks) < 0) { - virReportOOMError(); - goto cleanup; - } - - for (i = 0; i < def->dom->ndisks; i++) { - virDomainSnapshotDiskDefPtr disk; - - ignore_value(virBitmapGetBit(map, i, &inuse)); - if (inuse) - continue; - disk = &def->disks[ndisks++]; - if (!(disk->name = strdup(def->dom->disks[i]->dst))) { - virReportOOMError(); - goto cleanup; - } - disk->index = i; - disk->snapshot = def->dom->disks[i]->snapshot; - if (!disk->snapshot) - disk->snapshot = default_snapshot; - } - - qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]), disksorter); - - /* Generate any default external file names, but only if the - * backing file is a regular file. */ - for (i = 0; i < def->ndisks; i++) { - virDomainSnapshotDiskDefPtr disk = &def->disks[i]; - - if (disk->snapshot == VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL && - !disk->file) { - const char *original = def->dom->disks[i]->src; - const char *tmp; - struct stat sb; - - if (!original) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("cannot generate external snapshot name " - "for disk '%s' without source"), - disk->name); - goto cleanup; - } - if (stat(original, &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); - goto cleanup; - } - - tmp = strrchr(original, '.'); - if (!tmp || strchr(tmp, '/')) { - ignore_value(virAsprintf(&disk->file, "%s.%s", - original, def->name)); - } else { - if ((tmp - original) > INT_MAX) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("integer overflow")); - goto cleanup; - } - ignore_value(virAsprintf(&disk->file, "%.*s.%s", - (int) (tmp - original), original, - def->name)); - } - if (!disk->file) { - virReportOOMError(); - goto cleanup; - } - } - } - - ret = 0; - -cleanup: - virBitmapFree(map); - return ret; -} - -char *virDomainSnapshotDefFormat(const char *domain_uuid, - virDomainSnapshotDefPtr def, - unsigned int flags, - int internal) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - int i; - - virCheckFlags(VIR_DOMAIN_XML_SECURE | - VIR_DOMAIN_XML_UPDATE_CPU, NULL); - - flags |= VIR_DOMAIN_XML_INACTIVE; - - virBufferAddLit(&buf, "\n"); - virBufferEscapeString(&buf, " %s\n", def->name); - if (def->description) - virBufferEscapeString(&buf, " %s\n", - def->description); - virBufferAsprintf(&buf, " %s\n", - virDomainSnapshotStateTypeToString(def->state)); - if (def->parent) { - virBufferAddLit(&buf, " \n"); - virBufferEscapeString(&buf, " %s\n", def->parent); - virBufferAddLit(&buf, " \n"); - } - virBufferAsprintf(&buf, " %lld\n", - def->creationTime); - /* For now, only output on disk-snapshot */ - if (def->state == VIR_DOMAIN_DISK_SNAPSHOT) { - virBufferAddLit(&buf, " \n"); - for (i = 0; i < def->ndisks; i++) { - virDomainSnapshotDiskDefPtr disk = &def->disks[i]; - - if (!disk->name) - continue; - - virBufferEscapeString(&buf, " name); - if (disk->snapshot) - virBufferAsprintf(&buf, " snapshot='%s'", - virDomainDiskSnapshotTypeToString(disk->snapshot)); - if (disk->file || disk->driverType) { - virBufferAddLit(&buf, ">\n"); - if (disk->driverType) - virBufferEscapeString(&buf, " \n", - disk->driverType); - if (disk->file) - virBufferEscapeString(&buf, " \n", - disk->file); - virBufferAddLit(&buf, " \n"); - } else { - virBufferAddLit(&buf, "/>\n"); - } - } - virBufferAddLit(&buf, " \n"); - } - if (def->dom) { - virBufferAdjustIndent(&buf, 2); - if (virDomainDefFormatInternal(def->dom, flags, &buf) < 0) { - virBufferFreeAndReset(&buf); - return NULL; - } - virBufferAdjustIndent(&buf, -2); - } else if (domain_uuid) { - virBufferAddLit(&buf, " \n"); - virBufferAsprintf(&buf, " %s\n", domain_uuid); - virBufferAddLit(&buf, " \n"); - } - if (internal) - virBufferAsprintf(&buf, " %d\n", def->current); - virBufferAddLit(&buf, "\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; - } - - 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, - const 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) { - virReportOOMError(); - 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 void virDomainSnapshotObjListCopyNames(void *payload, - const void *name ATTRIBUTE_UNUSED, - void *opaque) -{ - virDomainSnapshotObjPtr obj = payload; - struct virDomainSnapshotNameData *data = opaque; - - if (data->error) - return; - /* 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; - if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren) - return; - - if (data->names && data->count < data->maxnames && - !(data->names[data->count] = strdup(obj->def->name))) { - data->error = true; - virReportOOMError(); - return; - } - data->count++; -} - -int -virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots, - virDomainSnapshotObjPtr from, - char **const names, int maxnames, - unsigned int flags) -{ - struct virDomainSnapshotNameData data = { names, maxnames, flags, 0, - false }; - int 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 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; - - /* For ease of coding the visitor, it is easier to zero the LEAVES - * group if both bits are set. */ - if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) == - VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) - data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES; - - 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 -virDomainSnapshotFindByName(const 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); -} - -/* 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; -}; - -static void -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); -} - -/* 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; -}; -static void -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; -} - -/* Populate parent link and child count of all snapshots, with all - * 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 }; - - 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 virDomainChrDefForeach(virDomainDefPtr def, bool abortOnError, virDomainChrDefIterator iter, @@ -15845,49 +14967,6 @@ cleanup: return ret; } -int -virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots, - virDomainSnapshotObjPtr from, - virDomainPtr dom, - virDomainSnapshotPtr **snaps, - unsigned int flags) -{ - int count = virDomainSnapshotObjListNum(snapshots, from, flags); - virDomainSnapshotPtr *list; - char **names; - int ret = -1; - int i; - - if (!snaps) - return count; - if (VIR_ALLOC_N(names, count) < 0 || - VIR_ALLOC_N(list, count + 1) < 0) { - virReportOOMError(); - 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; -} - virSecurityLabelDefPtr virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6033641294a794078332a3bf1b3d02bd4c34b9c5..ef4feaa6ea120e48bca73faf63711ae8550143f6 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -101,6 +101,12 @@ typedef virDomainChrDef *virDomainChrDefPtr; typedef struct _virDomainMemballoonDef virDomainMemballoonDef; typedef virDomainMemballoonDef *virDomainMemballoonDefPtr; +typedef struct _virDomainSnapshotObj virDomainSnapshotObj; +typedef virDomainSnapshotObj *virDomainSnapshotObjPtr; + +typedef struct _virDomainSnapshotObjList virDomainSnapshotObjList; +typedef virDomainSnapshotObjList *virDomainSnapshotObjListPtr; + /* Flags for the 'type' field in virDomainDeviceDef */ typedef enum { VIR_DOMAIN_DEVICE_NONE = 0, @@ -501,21 +507,6 @@ enum virDomainDiskCopyOnRead { VIR_DOMAIN_DISK_COPY_ON_READ_LAST }; -enum virDomainDiskSnapshot { - VIR_DOMAIN_DISK_SNAPSHOT_DEFAULT = 0, - VIR_DOMAIN_DISK_SNAPSHOT_NO, - VIR_DOMAIN_DISK_SNAPSHOT_INTERNAL, - VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL, - - VIR_DOMAIN_DISK_SNAPSHOT_LAST -}; - -enum virDomainSnapshotState { - /* Inherit the VIR_DOMAIN_* states from virDomainState. */ - VIR_DOMAIN_DISK_SNAPSHOT = VIR_DOMAIN_LAST, - VIR_DOMAIN_SNAPSHOT_STATE_LAST -}; - enum virDomainStartupPolicy { VIR_DOMAIN_STARTUP_POLICY_DEFAULT = 0, VIR_DOMAIN_STARTUP_POLICY_MANDATORY, @@ -587,7 +578,7 @@ struct _virDomainDiskDef { int ioeventfd; int event_idx; int copy_on_read; - int snapshot; /* enum virDomainDiskSnapshot */ + int snapshot; /* enum virDomainDiskSnapshot, snapshot_conf.h */ int startupPolicy; /* enum virDomainStartupPolicy */ unsigned int readonly : 1; unsigned int shared : 1; @@ -1713,102 +1704,6 @@ enum virDomainTaintFlags { VIR_DOMAIN_TAINT_LAST }; -/* Items related to snapshot state */ - -/* Stores disk-snapshot information */ -typedef struct _virDomainSnapshotDiskDef virDomainSnapshotDiskDef; -typedef virDomainSnapshotDiskDef *virDomainSnapshotDiskDefPtr; -struct _virDomainSnapshotDiskDef { - char *name; /* name matching the dom->disks that matches name */ - int snapshot; /* enum virDomainDiskSnapshot */ - char *file; /* new source file when snapshot is external */ - char *driverType; /* file format type of new file */ -}; - -/* Stores the complete snapshot metadata */ -typedef struct _virDomainSnapshotDef virDomainSnapshotDef; -typedef virDomainSnapshotDef *virDomainSnapshotDefPtr; -struct _virDomainSnapshotDef { - /* Public XML. */ - char *name; - char *description; - char *parent; - long long creationTime; /* in seconds */ - int state; /* enum virDomainSnapshotState */ - - size_t ndisks; /* should not exceed dom->ndisks */ - virDomainSnapshotDiskDef *disks; - - virDomainDefPtr dom; - - /* Internal use. */ - bool current; /* At most one snapshot in the list should have this set */ -}; - -typedef struct _virDomainSnapshotObj virDomainSnapshotObj; -typedef virDomainSnapshotObj *virDomainSnapshotObjPtr; -struct _virDomainSnapshotObj { - virDomainSnapshotDefPtr def; /* non-NULL except for metaroot */ - - virDomainSnapshotObjPtr parent; /* non-NULL except for metaroot, before - virDomainSnapshotUpdateRelations, or - after virDomainSnapshotDropParent */ - virDomainSnapshotObjPtr sibling; /* NULL if last child of parent */ - size_t nchildren; - virDomainSnapshotObjPtr first_child; /* NULL if no children */ -}; - -typedef struct _virDomainSnapshotObjList virDomainSnapshotObjList; -typedef virDomainSnapshotObjList *virDomainSnapshotObjListPtr; - -virDomainSnapshotObjListPtr virDomainSnapshotObjListNew(void); -void virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots); - -typedef enum { - VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE = 1 << 0, - VIR_DOMAIN_SNAPSHOT_PARSE_DISKS = 1 << 1, - VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL = 1 << 2, -} virDomainSnapshotParseFlags; - -virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, - virCapsPtr caps, - unsigned int expectedVirtTypes, - unsigned int flags); -void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def); -char *virDomainSnapshotDefFormat(const char *domain_uuid, - virDomainSnapshotDefPtr def, - unsigned int flags, - int internal); -int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot, - int default_snapshot, - bool require_match); -virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots, - const 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(const virDomainSnapshotObjListPtr snapshots, - const char *name); -void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots, - virDomainSnapshotObjPtr snapshot); -int virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots, - virHashIterator iter, - void *data); -int virDomainSnapshotForEachChild(virDomainSnapshotObjPtr snapshot, - virHashIterator iter, - void *data); -int virDomainSnapshotForEachDescendant(virDomainSnapshotObjPtr snapshot, - virHashIterator iter, - void *data); -int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots); -void virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot); - /* Guest VM runtime state */ typedef struct _virDomainStateReason virDomainStateReason; struct _virDomainStateReason { @@ -2202,7 +2097,6 @@ VIR_ENUM_DECL(virDomainDiskErrorPolicy) VIR_ENUM_DECL(virDomainDiskProtocol) VIR_ENUM_DECL(virDomainDiskIo) VIR_ENUM_DECL(virDomainDiskSecretType) -VIR_ENUM_DECL(virDomainDiskSnapshot) VIR_ENUM_DECL(virDomainDiskTray) VIR_ENUM_DECL(virDomainIoEventFd) VIR_ENUM_DECL(virDomainVirtioEventIdx) @@ -2253,7 +2147,6 @@ VIR_ENUM_DECL(virDomainGraphicsSpiceClipboardCopypaste) VIR_ENUM_DECL(virDomainGraphicsSpiceMouseMode) VIR_ENUM_DECL(virDomainNumatuneMemMode) VIR_ENUM_DECL(virDomainNumatuneMemPlacementMode) -VIR_ENUM_DECL(virDomainSnapshotState) /* from libvirt.h */ VIR_ENUM_DECL(virDomainState) VIR_ENUM_DECL(virDomainNostateReason) @@ -2316,25 +2209,7 @@ virDomainNetDefPtr virDomainNetFind(virDomainDefPtr def, VIR_CONNECT_LIST_DOMAINS_FILTERS_AUTOSTART | \ VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT) -# define VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA \ - (VIR_DOMAIN_SNAPSHOT_LIST_METADATA | \ - VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA) - -# define VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES \ - (VIR_DOMAIN_SNAPSHOT_LIST_LEAVES | \ - VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) - -# define VIR_DOMAIN_SNAPSHOT_FILTERS_ALL \ - (VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA | \ - VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) - int virDomainList(virConnectPtr conn, virHashTablePtr domobjs, virDomainPtr **domains, unsigned int flags); -int virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots, - virDomainSnapshotObjPtr from, - virDomainPtr dom, - virDomainSnapshotPtr **snaps, - unsigned int flags); - #endif /* __DOMAIN_CONF_H */ diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c new file mode 100644 index 0000000000000000000000000000000000000000..b865918e212570e4819e3c614a0cf30688418b2f --- /dev/null +++ b/src/conf/snapshot_conf.c @@ -0,0 +1,969 @@ +/* + * snapshot_conf.c: domain snapshot XML processing + * + * Copyright (C) 2006-2012 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 + * . + * + * Author: Eric Blake + */ + +#include + +#include +#include +#include +#include + +#include "internal.h" +#include "bitmap.h" +#include "buf.h" +#include "count-one-bits.h" +#include "datatypes.h" +#include "domain_conf.h" +#include "logging.h" +#include "memory.h" +#include "netdev_bandwidth_conf.h" +#include "netdev_vport_profile_conf.h" +#include "nwfilter_conf.h" +#include "secret_conf.h" +#include "snapshot_conf.h" +#include "storage_file.h" +#include "util.h" +#include "uuid.h" +#include "virfile.h" +#include "virterror_internal.h" +#include "xml.h" + +#define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT + +VIR_ENUM_IMPL(virDomainDiskSnapshot, VIR_DOMAIN_DISK_SNAPSHOT_LAST, + "default", + "no", + "internal", + "external") + +/* virDomainSnapshotState is really virDomainState plus one extra state */ +VIR_ENUM_IMPL(virDomainSnapshotState, VIR_DOMAIN_SNAPSHOT_STATE_LAST, + "nostate", + "running", + "blocked", + "paused", + "shutdown", + "shutoff", + "crashed", + "pmsuspended", + "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) +{ + VIR_FREE(disk->name); + VIR_FREE(disk->file); + VIR_FREE(disk->driverType); +} + +void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def) +{ + int i; + + if (!def) + return; + + VIR_FREE(def->name); + VIR_FREE(def->description); + VIR_FREE(def->parent); + for (i = 0; i < def->ndisks; i++) + virDomainSnapshotDiskDefClear(&def->disks[i]); + VIR_FREE(def->disks); + virDomainDefFree(def->dom); + VIR_FREE(def); +} + +static int +virDomainSnapshotDiskDefParseXML(xmlNodePtr node, + virDomainSnapshotDiskDefPtr def) +{ + int ret = -1; + char *snapshot = NULL; + xmlNodePtr cur; + + 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) { + def->snapshot = virDomainDiskSnapshotTypeFromString(snapshot); + if (def->snapshot <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown disk snapshot setting '%s'"), + snapshot); + goto cleanup; + } + } + + cur = node->children; + while (cur) { + if (cur->type == XML_ELEMENT_NODE) { + if (!def->file && + xmlStrEqual(cur->name, BAD_CAST "source")) { + def->file = virXMLPropString(cur, "file"); + } else if (!def->driverType && + xmlStrEqual(cur->name, BAD_CAST "driver")) { + def->driverType = virXMLPropString(cur, "type"); + } + } + cur = cur->next; + } + + if (!def->snapshot && (def->file || def->driverType)) + def->snapshot = VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL; + + ret = 0; +cleanup: + VIR_FREE(snapshot); + if (ret < 0) + virDomainSnapshotDiskDefClear(def); + return ret; +} + +/* flags is bitwise-or of virDomainSnapshotParseFlags. + * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then + * caps and expectedVirtTypes are ignored. + */ +virDomainSnapshotDefPtr +virDomainSnapshotDefParseString(const char *xmlStr, + virCapsPtr caps, + unsigned int expectedVirtTypes, + unsigned int flags) +{ + xmlXPathContextPtr ctxt = NULL; + xmlDocPtr xml = NULL; + virDomainSnapshotDefPtr def = NULL; + virDomainSnapshotDefPtr ret = NULL; + xmlNodePtr *nodes = NULL; + int i; + char *creation = NULL, *state = NULL; + struct timeval tv; + int active; + char *tmp; + int keepBlanksDefault = xmlKeepBlanksDefault(0); + + xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt); + if (!xml) { + xmlKeepBlanksDefault(keepBlanksDefault); + return NULL; + } + xmlKeepBlanksDefault(keepBlanksDefault); + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!xmlStrEqual(ctxt->node->name, BAD_CAST "domainsnapshot")) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot")); + 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; + } else { + ignore_value(virAsprintf(&def->name, "%lld", + (long long)tv.tv_sec)); + } + } + + if (def->name == NULL) { + virReportOOMError(); + goto cleanup; + } + + 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) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid state '%s' in domain snapshot XML"), + state); + goto cleanup; + } + + /* Older snapshots were created with just /, 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))) { + xmlNodePtr domainNode = virXPathNode("./domain", ctxt); + + VIR_FREE(tmp); + if (!domainNode) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing domain in snapshot")); + goto cleanup; + } + def->dom = virDomainDefParseNode(caps, xml, domainNode, + expectedVirtTypes, + (VIR_DOMAIN_XML_INACTIVE | + VIR_DOMAIN_XML_SECURE)); + if (!def->dom) + goto cleanup; + } else { + VIR_WARN("parsing older snapshot that lacks domain"); + } + } else { + def->creationTime = tv.tv_sec; + } + + if ((i = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0) + goto cleanup; + if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS || + (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE && + def->state == VIR_DOMAIN_DISK_SNAPSHOT)) { + def->ndisks = i; + if (def->ndisks && VIR_ALLOC_N(def->disks, def->ndisks) < 0) { + virReportOOMError(); + goto cleanup; + } + for (i = 0; i < def->ndisks; i++) { + if (virDomainSnapshotDiskDefParseXML(nodes[i], &def->disks[i]) < 0) + goto cleanup; + } + VIR_FREE(nodes); + } else if (i) { + 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; + } + + ret = def; + +cleanup: + VIR_FREE(creation); + VIR_FREE(state); + VIR_FREE(nodes); + xmlXPathFreeContext(ctxt); + if (ret == NULL) + virDomainSnapshotDefFree(def); + xmlFreeDoc(xml); + + return ret; +} + +static int +disksorter(const void *a, const void *b) +{ + const virDomainSnapshotDiskDef *diska = a; + const virDomainSnapshotDiskDef *diskb = b; + + /* Integer overflow shouldn't be a problem here. */ + return diska->index - diskb->index; +} + +/* 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 + * dom->disks. If require_match, also require that existing + * def->disks snapshot states do not override explicit def->dom + * settings. */ +int +virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def, + int default_snapshot, + bool require_match) +{ + int ret = -1; + virBitmapPtr map = NULL; + int i; + int ndisks; + bool inuse; + + 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; + } + + if (!(map = virBitmapAlloc(def->dom->ndisks))) { + virReportOOMError(); + 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; + } + disk_snapshot = def->dom->disks[idx]->snapshot; + + if (virBitmapGetBit(map, idx, &inuse) < 0 || inuse) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk '%s' specified twice"), + disk->name); + goto cleanup; + } + ignore_value(virBitmapSetBit(map, idx)); + disk->index = idx; + if (!disk_snapshot) + disk_snapshot = default_snapshot; + if (!disk->snapshot) { + disk->snapshot = disk_snapshot; + } else if (disk_snapshot && require_match && + disk->snapshot != disk_snapshot) { + const char *tmp = virDomainDiskSnapshotTypeToString(disk_snapshot); + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk '%s' must use snapshot mode '%s'"), + disk->name, tmp); + goto cleanup; + } + if (disk->file && + disk->snapshot != VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("file '%s' for disk '%s' requires " + "use of external snapshot mode"), + disk->file, disk->name); + goto cleanup; + } + if (STRNEQ(disk->name, def->dom->disks[idx]->dst)) { + VIR_FREE(disk->name); + if (!(disk->name = strdup(def->dom->disks[idx]->dst))) { + virReportOOMError(); + goto cleanup; + } + } + } + + /* Provide defaults for all remaining disks. */ + ndisks = def->ndisks; + if (VIR_EXPAND_N(def->disks, def->ndisks, + def->dom->ndisks - def->ndisks) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < def->dom->ndisks; i++) { + virDomainSnapshotDiskDefPtr disk; + + ignore_value(virBitmapGetBit(map, i, &inuse)); + if (inuse) + continue; + disk = &def->disks[ndisks++]; + if (!(disk->name = strdup(def->dom->disks[i]->dst))) { + virReportOOMError(); + goto cleanup; + } + disk->index = i; + disk->snapshot = def->dom->disks[i]->snapshot; + if (!disk->snapshot) + disk->snapshot = default_snapshot; + } + + qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]), disksorter); + + /* Generate any default external file names, but only if the + * backing file is a regular file. */ + for (i = 0; i < def->ndisks; i++) { + virDomainSnapshotDiskDefPtr disk = &def->disks[i]; + + if (disk->snapshot == VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL && + !disk->file) { + const char *original = def->dom->disks[i]->src; + const char *tmp; + struct stat sb; + + if (!original) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("cannot generate external snapshot name " + "for disk '%s' without source"), + disk->name); + goto cleanup; + } + if (stat(original, &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); + goto cleanup; + } + + tmp = strrchr(original, '.'); + if (!tmp || strchr(tmp, '/')) { + ignore_value(virAsprintf(&disk->file, "%s.%s", + original, def->name)); + } else { + if ((tmp - original) > INT_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("integer overflow")); + goto cleanup; + } + ignore_value(virAsprintf(&disk->file, "%.*s.%s", + (int) (tmp - original), original, + def->name)); + } + if (!disk->file) { + virReportOOMError(); + goto cleanup; + } + } + } + + ret = 0; + +cleanup: + virBitmapFree(map); + return ret; +} + +char *virDomainSnapshotDefFormat(const char *domain_uuid, + virDomainSnapshotDefPtr def, + unsigned int flags, + int internal) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + int i; + + virCheckFlags(VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_UPDATE_CPU, NULL); + + flags |= VIR_DOMAIN_XML_INACTIVE; + + virBufferAddLit(&buf, "\n"); + virBufferEscapeString(&buf, " %s\n", def->name); + if (def->description) + virBufferEscapeString(&buf, " %s\n", + def->description); + virBufferAsprintf(&buf, " %s\n", + virDomainSnapshotStateTypeToString(def->state)); + if (def->parent) { + virBufferAddLit(&buf, " \n"); + virBufferEscapeString(&buf, " %s\n", def->parent); + virBufferAddLit(&buf, " \n"); + } + virBufferAsprintf(&buf, " %lld\n", + def->creationTime); + /* For now, only output on disk-snapshot */ + if (def->state == VIR_DOMAIN_DISK_SNAPSHOT) { + virBufferAddLit(&buf, " \n"); + for (i = 0; i < def->ndisks; i++) { + virDomainSnapshotDiskDefPtr disk = &def->disks[i]; + + if (!disk->name) + continue; + + virBufferEscapeString(&buf, " name); + if (disk->snapshot) + virBufferAsprintf(&buf, " snapshot='%s'", + virDomainDiskSnapshotTypeToString(disk->snapshot)); + if (disk->file || disk->driverType) { + virBufferAddLit(&buf, ">\n"); + if (disk->driverType) + virBufferEscapeString(&buf, " \n", + disk->driverType); + if (disk->file) + virBufferEscapeString(&buf, " \n", + disk->file); + virBufferAddLit(&buf, " \n"); + } else { + virBufferAddLit(&buf, "/>\n"); + } + } + virBufferAddLit(&buf, " \n"); + } + if (def->dom) { + virBufferAdjustIndent(&buf, 2); + if (virDomainDefFormatInternal(def->dom, flags, &buf) < 0) { + virBufferFreeAndReset(&buf); + return NULL; + } + virBufferAdjustIndent(&buf, -2); + } else if (domain_uuid) { + virBufferAddLit(&buf, " \n"); + virBufferAsprintf(&buf, " %s\n", domain_uuid); + virBufferAddLit(&buf, " \n"); + } + if (internal) + virBufferAsprintf(&buf, " %d\n", def->current); + virBufferAddLit(&buf, "\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; + } + + 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, + const 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) { + virReportOOMError(); + 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 void virDomainSnapshotObjListCopyNames(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *opaque) +{ + virDomainSnapshotObjPtr obj = payload; + struct virDomainSnapshotNameData *data = opaque; + + if (data->error) + return; + /* 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; + if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren) + return; + + if (data->names && data->count < data->maxnames && + !(data->names[data->count] = strdup(obj->def->name))) { + data->error = true; + virReportOOMError(); + return; + } + data->count++; +} + +int +virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots, + virDomainSnapshotObjPtr from, + char **const names, int maxnames, + unsigned int flags) +{ + struct virDomainSnapshotNameData data = { names, maxnames, flags, 0, + false }; + int 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 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; + + /* For ease of coding the visitor, it is easier to zero the LEAVES + * group if both bits are set. */ + if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) == + VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) + data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES; + + 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 +virDomainSnapshotFindByName(const 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); +} + +/* 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; +}; + +static void +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); +} + +/* 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; +}; +static void +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; +} + +/* Populate parent link and child count of all snapshots, with all + * 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 }; + + 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); + virDomainSnapshotPtr *list; + char **names; + int ret = -1; + int i; + + if (!snaps) + return count; + if (VIR_ALLOC_N(names, count) < 0 || + VIR_ALLOC_N(list, count + 1) < 0) { + virReportOOMError(); + 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; +} diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h new file mode 100644 index 0000000000000000000000000000000000000000..bb41fd6185e8e10db5b99b6b32ffb5c3de7d5989 --- /dev/null +++ b/src/conf/snapshot_conf.h @@ -0,0 +1,157 @@ +/* + * snapshot_conf.h: domain snapshot XML processing + * + * Copyright (C) 2006-2012 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 + * . + * + * Author: Eric Blake + */ + +#ifndef __SNAPSHOT_CONF_H +# define __SNAPSHOT_CONF_H + +# include "internal.h" +# include "domain_conf.h" + +/* Items related to snapshot state */ + +enum virDomainDiskSnapshot { + VIR_DOMAIN_DISK_SNAPSHOT_DEFAULT = 0, + VIR_DOMAIN_DISK_SNAPSHOT_NO, + VIR_DOMAIN_DISK_SNAPSHOT_INTERNAL, + VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL, + + VIR_DOMAIN_DISK_SNAPSHOT_LAST +}; + +enum virDomainSnapshotState { + /* Inherit the VIR_DOMAIN_* states from virDomainState. */ + VIR_DOMAIN_DISK_SNAPSHOT = VIR_DOMAIN_LAST, + VIR_DOMAIN_SNAPSHOT_STATE_LAST +}; + +/* Stores disk-snapshot information */ +typedef struct _virDomainSnapshotDiskDef virDomainSnapshotDiskDef; +typedef virDomainSnapshotDiskDef *virDomainSnapshotDiskDefPtr; +struct _virDomainSnapshotDiskDef { + char *name; /* name matching the dom->disks that matches name */ + int snapshot; /* enum virDomainDiskSnapshot */ + char *file; /* new source file when snapshot is external */ + char *driverType; /* file format type of new file */ +}; + +/* Stores the complete snapshot metadata */ +typedef struct _virDomainSnapshotDef virDomainSnapshotDef; +typedef virDomainSnapshotDef *virDomainSnapshotDefPtr; +struct _virDomainSnapshotDef { + /* Public XML. */ + char *name; + char *description; + char *parent; + long long creationTime; /* in seconds */ + int state; /* enum virDomainSnapshotState */ + + size_t ndisks; /* should not exceed dom->ndisks */ + virDomainSnapshotDiskDef *disks; + + virDomainDefPtr dom; + + /* Internal use. */ + bool current; /* At most one snapshot in the list should have this set */ +}; + +struct _virDomainSnapshotObj { + virDomainSnapshotDefPtr def; /* non-NULL except for metaroot */ + + virDomainSnapshotObjPtr parent; /* non-NULL except for metaroot, before + virDomainSnapshotUpdateRelations, or + after virDomainSnapshotDropParent */ + virDomainSnapshotObjPtr sibling; /* NULL if last child of parent */ + size_t nchildren; + virDomainSnapshotObjPtr first_child; /* NULL if no children */ +}; + +virDomainSnapshotObjListPtr virDomainSnapshotObjListNew(void); +void virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots); + +typedef enum { + VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE = 1 << 0, + VIR_DOMAIN_SNAPSHOT_PARSE_DISKS = 1 << 1, + VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL = 1 << 2, +} virDomainSnapshotParseFlags; + +virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, + virCapsPtr caps, + unsigned int expectedVirtTypes, + unsigned int flags); +void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def); +char *virDomainSnapshotDefFormat(const char *domain_uuid, + virDomainSnapshotDefPtr def, + unsigned int flags, + int internal); +int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot, + int default_snapshot, + bool require_match); +virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots, + const 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(const virDomainSnapshotObjListPtr snapshots, + const char *name); +void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots, + virDomainSnapshotObjPtr snapshot); +int virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots, + virHashIterator iter, + void *data); +int virDomainSnapshotForEachChild(virDomainSnapshotObjPtr snapshot, + virHashIterator iter, + void *data); +int virDomainSnapshotForEachDescendant(virDomainSnapshotObjPtr snapshot, + virHashIterator iter, + void *data); +int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots); +void virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot); + +# define VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA \ + (VIR_DOMAIN_SNAPSHOT_LIST_METADATA | \ + VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA) + +# define VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES \ + (VIR_DOMAIN_SNAPSHOT_LIST_LEAVES | \ + VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) + +# define VIR_DOMAIN_SNAPSHOT_FILTERS_ALL \ + (VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA | \ + VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) + +int virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots, + virDomainSnapshotObjPtr from, + virDomainPtr dom, + virDomainSnapshotPtr **snaps, + unsigned int flags); + +VIR_ENUM_DECL(virDomainDiskSnapshot) +VIR_ENUM_DECL(virDomainSnapshotState) + +#endif /* __SNAPSHOT_CONF_H */ diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 72a7acc6efcdca8b27bec7b5dc1e998b61dec3d4..e57296a9c3e0166384eadc7089ba386e4bfd4cf8 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -26,6 +26,7 @@ #include "internal.h" #include "domain_conf.h" +#include "snapshot_conf.h" #include "virauth.h" #include "util.h" #include "memory.h" diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ca62f0c5d136502248ba28e14caf80f17d706189..8c32a4dcdcfcb97682cfc6e166f51c7afa66fae2 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -38,6 +38,7 @@ #include "domain_nwfilter.h" #include "domain_audit.h" #include "domain_conf.h" +#include "snapshot_conf.h" #include "network/bridge_driver.h" #include "virnetdevtap.h" #include "base64.h" diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index b96087e8778d594994ecf998dce5a54fca8f5bff..dff53cf8da6429574e5b75c42c4d43388f9ca973 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -1,7 +1,7 @@ /* * qemu_domain.h: QEMU domain private state * - * Copyright (C) 2006-2011 Red Hat, Inc. + * Copyright (C) 2006-2012 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -26,6 +26,7 @@ # include "threads.h" # include "domain_conf.h" +# include "snapshot_conf.h" # include "qemu_monitor.h" # include "qemu_agent.h" # include "qemu_conf.h" diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 4cdb11c6421f394796dcb508f888b4f67cccae18..48f371f484a4b7c092eda7995bd7728136f30891 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -43,6 +43,7 @@ #include "internal.h" #include "datatypes.h" #include "domain_conf.h" +#include "snapshot_conf.h" #include "network_conf.h" #include "virterror_internal.h" #include "domain_event.h"