[libvirt] [PATCHv2 RESEND 0/5] Handling of undefine and redefine snapshots with VirtualBox 4.2

Hi, This is a serie of patches in order to support undefining and redefining snapshots with VirtualBox 4.2. The serie of patches is rather big, and adds among other things some utility functions unrelated to VirtualBox in patches 1 & 2. The code review could be done in several parts: e.g. patches 1 & 2 separately to validate the utility functions. This 2nd version brings a different function for string replacement, because of the license problem with the first one. The VirtualBox API provides only high level operations to manipulate snapshots, so it not possible to support flags like VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE and VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY with only API calls. Following an IRC talk with Eric Blake, the decision was taken to emulate these behaviours by manipulating directly the .vbox XML files. The first two patches are some util methods for handling uuid and strings that will be used after. The third patch brings more details in the snapshot XML returned by libvirt. We will need those modifications in order to redefine the snapshots. The fourth patch brings the support of the VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE and VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT flags in virDomainSnapshotCreateXML. The fifth and last patch brings the support of the VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY flag in virDomainSnapshotDelete. The patches are only for Virtualbox 4.2 Regards, Manuel VIVES Manuel VIVES (5): viruuid.h/c: Util method for finding uuid patterns in some strings virstring.h/c: Util method for making some find and replace in strings vbox_tmpl.c: Better XML description for snapshots vbox_tmpl.c: Patch for redefining snapshots vbox_tmpl.c: Add methods for undefining snapshots src/conf/domain_conf.c | 20 +- src/libvirt_private.syms | 2 + src/util/virstring.c | 48 ++ src/util/virstring.h | 2 + src/util/viruuid.c | 79 ++ src/util/viruuid.h | 1 + src/vbox/vbox_tmpl.c | 1846 +++++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 1878 insertions(+), 120 deletions(-) -- 1.7.10.4

--- src/libvirt_private.syms | 1 + src/util/viruuid.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/viruuid.h | 1 + 3 files changed, 81 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 35f0f1b..a6af540 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2080,6 +2080,7 @@ virValidateWWN; # util/viruuid.h virGetHostUUID; +virSearchUuid; virSetHostUUIDStr; virUUIDFormat; virUUIDGenerate; diff --git a/src/util/viruuid.c b/src/util/viruuid.c index c5fa9a8..3786393 100644 --- a/src/util/viruuid.c +++ b/src/util/viruuid.c @@ -34,6 +34,7 @@ #include <sys/stat.h> #include <time.h> #include <unistd.h> +#include <regex.h> #include "c-ctype.h" #include "internal.h" @@ -43,11 +44,14 @@ #include "viralloc.h" #include "virfile.h" #include "virrandom.h" +#include "virstring.h" #ifndef ENODATA # define ENODATA EIO #endif +#define VIR_FROM_THIS VIR_FROM_NONE + static unsigned char host_uuid[VIR_UUID_BUFLEN]; static int @@ -333,3 +337,78 @@ int virGetHostUUID(unsigned char *uuid) return ret; } + + +/** + * virSearchUuid: + * Return the nth occurrence of a substring in sourceString which matches an uuid pattern + * If there is no substring, ret is not modified + * + * @sourceString: String to parse + * @occurrence: We will return the nth occurrence of uuid in substring, if equals to 0 (or negative), will return the first occurence + * @ret: nth occurrence substring matching an uuid pattern + * @code + char *source = "6853a496-1c10-472e-867a-8244937bd6f0 773ab075-4cd7-4fc2-8b6e-21c84e9cb391 bbb3c75c-d60f-43b0-b802-fd56b84a4222 60c04aa1-0375-4654-8d9f-e149d9885273 4548d465-9891-4c34-a184-3b1c34a26aa8"; + char *ret1=NULL; + char *ret2=NULL; + char *ret3=NULL; + char *ret4=NULL; + virSearchUuid(source, 4,&ret1); //ret1 = "60c04aa1-0375-4654-8d9f-e149d9885273" + virSearchUuid(source, 0,&ret2); //ret2 = "6853a496-1c10-472e-867a-8244937bd6f0" + virSearchUuid(source, 1,&ret3); //ret3 = "6853a496-1c10-472e-867a-8244937bd6f0" + virSearchUuid(source, -4,&ret4); //ret4 = "6853a496-1c10-472e-867a-8244937bd6f0" + * @endcode + */ + +char ** +virSearchUuid(const char *sourceString, int occurrence,char **ret) +{ + int position = ((occurrence -1) > 0) ? (occurrence -1) : 0; + + const char *uuidRegex = "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})"; + regex_t pregUuidBracket; + size_t i = 0; + size_t nmatch = 0; + regmatch_t *pmatch = NULL; + if (regcomp(&pregUuidBracket, uuidRegex, REG_EXTENDED) != 0) { + VIR_DEBUG("Error while compiling regular expression"); + goto cleanup; + } + nmatch = pregUuidBracket.re_nsub; + if (VIR_ALLOC_N(pmatch, nmatch) != 0) { + virReportOOMError(); + goto cleanup; + } + while (i < (position+1)) { + if (regexec(&pregUuidBracket, sourceString, nmatch, pmatch, 0) == 0) { + char *substring = NULL; + int start = pmatch[0].rm_so; + int end = pmatch[0].rm_eo; + size_t size = end - start; + if (VIR_ALLOC_N(substring, (size + 1)) != 0) { + virReportOOMError(); + goto cleanup; + } + if (substring) { + if (virStrncpy(substring, &sourceString[start], size, size + 1)) { + substring[size] = '\0'; + if (VIR_STRDUP(*ret, substring) < 0) { + VIR_DEBUG("cannot duplicate %s", substring); + goto cleanup; + } + } + VIR_FREE(substring); + } + sourceString = &sourceString[end]; + } + ++i; + } + regfree(&pregUuidBracket); + VIR_FREE(pmatch); + return ret; + +cleanup: + regfree(&pregUuidBracket); + VIR_FREE(pmatch); + return ret; +} diff --git a/src/util/viruuid.h b/src/util/viruuid.h index bebd338..276e85f 100644 --- a/src/util/viruuid.h +++ b/src/util/viruuid.h @@ -40,4 +40,5 @@ int virUUIDParse(const char *uuidstr, const char *virUUIDFormat(const unsigned char *uuid, char *uuidstr) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +char ** virSearchUuid(const char *sourceString, int occurrence, char **ret); #endif /* __VIR_UUID_H__ */ -- 1.7.10.4

On 09/02/2013 06:05 PM, Manuel VIVES wrote:
--- src/libvirt_private.syms | 1 + src/util/viruuid.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/viruuid.h | 1 + 3 files changed, 81 insertions(+)
@@ -333,3 +337,78 @@ int virGetHostUUID(unsigned char *uuid)
return ret; } + + +/** + * virSearchUuid: + * Return the nth occurrence of a substring in sourceString which matches an uuid pattern + * If there is no substring, ret is not modified + * + * @sourceString: String to parse + * @occurrence: We will return the nth occurrence of uuid in substring, if equals to 0 (or negative), will return the first occurence + * @ret: nth occurrence substring matching an uuid pattern + * @code + char *source = "6853a496-1c10-472e-867a-8244937bd6f0 773ab075-4cd7-4fc2-8b6e-21c84e9cb391 bbb3c75c-d60f-43b0-b802-fd56b84a4222 60c04aa1-0375-4654-8d9f-e149d9885273 4548d465-9891-4c34-a184-3b1c34a26aa8"; + char *ret1=NULL; + char *ret2=NULL; + char *ret3=NULL; + char *ret4=NULL; + virSearchUuid(source, 4,&ret1); //ret1 = "60c04aa1-0375-4654-8d9f-e149d9885273" + virSearchUuid(source, 0,&ret2); //ret2 = "6853a496-1c10-472e-867a-8244937bd6f0" + virSearchUuid(source, 1,&ret3); //ret3 = "6853a496-1c10-472e-867a-8244937bd6f0" + virSearchUuid(source, -4,&ret4); //ret4 = "6853a496-1c10-472e-867a-8244937bd6f0" + * @endcode + */ + +char ** +virSearchUuid(const char *sourceString, int occurrence,char **ret)
char ** func(..., char **ret) seems redundant. Either drop the 'ret' parameter completely and return just char, or change the return value to int (e.g. -1 = error, 0 = not found, 1 = found).
+{ + int position = ((occurrence -1) > 0) ? (occurrence -1) : 0; + + const char *uuidRegex = "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})"; + regex_t pregUuidBracket; + size_t i = 0; + size_t nmatch = 0; + regmatch_t *pmatch = NULL; + if (regcomp(&pregUuidBracket, uuidRegex, REG_EXTENDED) != 0) { + VIR_DEBUG("Error while compiling regular expression");
This should be virReportError.
+ goto cleanup;
and a return here, or you'll call regfree on an uninitialized variable.
+ } + nmatch = pregUuidBracket.re_nsub; + if (VIR_ALLOC_N(pmatch, nmatch) != 0) { + virReportOOMError();
VIR_ALLOC already reports an error.
+ goto cleanup; + } + while (i < (position+1)) {
You should exit the cycle on the first regexec failure, there's no point in calling it again and again with the same arguments.
+ if (regexec(&pregUuidBracket, sourceString, nmatch, pmatch, 0) == 0) {
All this
+ char *substring = NULL; + int start = pmatch[0].rm_so; + int end = pmatch[0].rm_eo; + size_t size = end - start; + if (VIR_ALLOC_N(substring, (size + 1)) != 0) { + virReportOOMError(); + goto cleanup; + } + if (substring) { + if (virStrncpy(substring, &sourceString[start], size, size + 1)) { + substring[size] = '\0'; + if (VIR_STRDUP(*ret, substring) < 0) { + VIR_DEBUG("cannot duplicate %s", substring); + goto cleanup; + } + } + VIR_FREE(substring); + }
can be replaced by: regoff_t start = pmatch[0].rm_so; regoff_t end = pmatch[0].rm_eo; if (VIR_STRNDUP(*ret, sourceString + start, end - start) < 0) goto cleanup; But even then, by copying every UUID to *ret, you'd leak the string buffer allocated for every one except the last one.
+ sourceString = &sourceString[end]; + } + ++i; + }
These three lines are redundant:
+ regfree(&pregUuidBracket); + VIR_FREE(pmatch); + return ret;
+ +cleanup: + regfree(&pregUuidBracket); + VIR_FREE(pmatch); + return ret; +}
And I'm sorry, but I have no opinion on the rest of the patches. Jan

--- src/libvirt_private.syms | 1 + src/util/virstring.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virstring.h | 2 ++ 3 files changed, 51 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a6af540..b5f83c3 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1920,6 +1920,7 @@ virStringListLength; virStringSplit; virStrncpy; virStrndup; +virStrReplace; virStrToDouble; virStrToLong_i; virStrToLong_l; diff --git a/src/util/virstring.c b/src/util/virstring.c index d11db5c..a30a4ef 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -616,3 +616,51 @@ size_t virStringListLength(char **strings) return i; } + +/* + virStrReplace(haystack, oldneedle, newneedle) -- + Search haystack and replace all occurences of oldneedle with newneedle. + Return a string with all the replacements in case of success, NULL in case + of failure +*/ +char * +virStrReplace(char *haystack, + const char *oldneedle, const char *newneedle) +{ + char *ret; + size_t i, count = 0; + size_t newneedle_len = strlen(newneedle); + size_t oldneedle_len = strlen(oldneedle); + size_t totalLength = 0; + if (strlen(oldneedle) == 0) { + ignore_value(VIR_STRDUP(ret, haystack)); + goto cleanup; + } + + for (i = 0; haystack[i] != '\0'; i++) { + if (strstr(&haystack[i], oldneedle) == &haystack[i]) { + count++; + i += oldneedle_len - 1; + } + } + if (VIR_ALLOC_N(ret, (i + count * (newneedle_len - oldneedle_len))) < 0) { + ret = NULL; + goto cleanup; + } + totalLength = sizeof(char *)*(i + count * (newneedle_len - oldneedle_len)); + i = 0; + while (*haystack) { + if (strstr(haystack, oldneedle) == haystack) { + if (virStrcpy(&ret[i], newneedle, totalLength) == NULL) { + ret = NULL; + goto cleanup; + } + i += newneedle_len; + haystack += oldneedle_len; + } else + ret[i++] = *haystack++; + } + ret[i] = '\0'; +cleanup: + return ret; +} diff --git a/src/util/virstring.h b/src/util/virstring.h index b390150..90522bd 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -223,4 +223,6 @@ size_t virStringListLength(char **strings); virAsprintfInternal(false, 0, NULL, NULL, 0, \ strp, __VA_ARGS__) +char * virStrReplace(char *haystack, const char *oldneedle, const char *newneedle); + #endif /* __VIR_STRING_H__ */ -- 1.7.10.4

It will be needed for the futur patches because we will redefine snapshots --- src/conf/domain_conf.c | 20 ++- src/vbox/vbox_tmpl.c | 427 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 427 insertions(+), 20 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f1623f1..c98ff63 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -16810,15 +16810,17 @@ virDomainDefFormatInternal(virDomainDefPtr def, if (virDomainChrDefFormat(buf, &console, flags) < 0) goto error; } - if (STREQ(def->os.type, "hvm") && - def->nconsoles == 0 && - def->nserials > 0) { - virDomainChrDef console; - memcpy(&console, def->serials[n], sizeof(console)); - console.deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE; - console.targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; - if (virDomainChrDefFormat(buf, &console, flags) < 0) - goto error; + if (def->os.type) { + if (STREQ(def->os.type, "hvm") && + def->nconsoles == 0 && + def->nserials > 0) { + virDomainChrDef console; + memcpy(&console, def->serials[n], sizeof(console)); + console.deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE; + console.targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; + if (virDomainChrDefFormat(buf, &console, flags) < 0) + goto error; + } } for (n = 0; n < def->nchannels; n++) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 5e5ea85..ded179f 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -38,6 +38,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <libxml/xmlwriter.h> #include "internal.h" #include "datatypes.h" @@ -58,6 +59,8 @@ #include "fdstream.h" #include "viruri.h" #include "virstring.h" +#include "virtime.h" +#include "virutil.h" /* This one changes from version to version. */ #if VBOX_API_VERSION == 2002 @@ -271,10 +274,16 @@ static vboxGlobalData *g_pVBoxGlobalData = NULL; #endif /* VBOX_API_VERSION >= 4000 */ +#define reportInternalErrorIfNS_FAILED(message) \ + if (NS_FAILED(rc)) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(message)); \ + goto cleanup; \ + } + + static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml); static int vboxDomainCreate(virDomainPtr dom); static int vboxDomainUndefineFlags(virDomainPtr dom, unsigned int flags); - static void vboxDriverLock(vboxGlobalData *data) { virMutexLock(&data->lock); } @@ -283,6 +292,12 @@ static void vboxDriverUnlock(vboxGlobalData *data) { virMutexUnlock(&data->lock); } +typedef enum { + VBOX_STORAGE_DELETE_FLAG = 0, +#if VBOX_API_VERSION >= 4002 + VBOX_STORAGE_CLOSE_FLAG = 1, +#endif +} vboxStorageDeleteOrCloseFlags; #if VBOX_API_VERSION == 2002 static void nsIDtoChar(unsigned char *uuid, const nsID *iid) { @@ -5907,7 +5922,8 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL); if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps, - data->xmlopt, 0, 0))) + data->xmlopt, -1, VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | + VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE))) goto cleanup; if (def->ndisks) { @@ -5915,7 +5931,6 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, _("disk snapshots not supported yet")); goto cleanup; } - vboxIIDFromUUID(&domiid, dom->uuid); rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); if (NS_FAILED(rc)) { @@ -5923,7 +5938,6 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, _("no domain with matching UUID")); goto cleanup; } - rc = machine->vtbl->GetState(machine, &state); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -5998,6 +6012,344 @@ cleanup: return ret; } +#if VBOX_API_VERSION >=4002 +static +int vboxSnapshotGetReadWriteDisks(virDomainSnapshotDefPtr def, + virDomainSnapshotPtr snapshot) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID domiid = VBOX_IID_INITIALIZER; + IMachine *machine = NULL; + ISnapshot *snap = NULL; + IMachine *snapMachine = NULL; + bool error = false; + vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER; + PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {}; + PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {}; + int diskCount = 0; + nsresult rc; + vboxIID snapIid = VBOX_IID_INITIALIZER; + char *snapshotUuidStr = NULL; + size_t i = 0; + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + reportInternalErrorIfNS_FAILED("no domain with matching UUID"); + if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) { + ret = -1; + goto cleanup; + } + rc = snap->vtbl->GetId(snap, &snapIid.value); + reportInternalErrorIfNS_FAILED("Could not get snapshot id"); + + VBOX_UTF16_TO_UTF8(snapIid.value, &snapshotUuidStr); + rc = snap->vtbl->GetMachine(snap, &snapMachine); + reportInternalErrorIfNS_FAILED("could not get machine"); + def->ndisks = 0; + rc = vboxArrayGet(&mediumAttachments, snapMachine, snapMachine->vtbl->GetMediumAttachments); + reportInternalErrorIfNS_FAILED("no medium attachments"); + /* get the number of attachments */ + for (i = 0; i < mediumAttachments.count; i++) { + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (imediumattach) { + IMedium *medium = NULL; + + rc = imediumattach->vtbl->GetMedium(imediumattach, &medium); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (medium) { + def->ndisks++; + VBOX_RELEASE(medium); + } + } + } + /* Allocate mem, if fails return error */ + if (VIR_ALLOC_N(def->disks, def->ndisks) < 0) { + virReportOOMError(); + error = true; + } + if (!error) + error = !vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort); + + /* get the attachment details here */ + for (i = 0; i < mediumAttachments.count && diskCount < def->ndisks && !error; i++) { + IStorageController *storageController = NULL; + PRUnichar *storageControllerName = NULL; + PRUint32 deviceType = DeviceType_Null; + PRUint32 storageBus = StorageBus_Null; + IMedium *disk = NULL; + PRUnichar *childLocUtf16 = NULL; + char *childLocUtf8 = NULL; + PRUint32 deviceInst = 0; + PRInt32 devicePort = 0; + PRInt32 deviceSlot = 0; + vboxArray children = VBOX_ARRAY_INITIALIZER; + vboxArray snapshotIids = VBOX_ARRAY_INITIALIZER; + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + size_t j = 0; + size_t k = 0; + if (!imediumattach) + continue; + rc = imediumattach->vtbl->GetMedium(imediumattach, &disk); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (!disk) + continue; + rc = imediumattach->vtbl->GetController(imediumattach, &storageControllerName); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (!storageControllerName) { + VBOX_RELEASE(disk); + continue; + } + rc= vboxArrayGet(&children, disk, disk->vtbl->GetChildren); + reportInternalErrorIfNS_FAILED("cannot get children disk"); + rc = vboxArrayGetWithPtrArg(&snapshotIids, disk, disk->vtbl->GetSnapshotIds, domiid.value); + reportInternalErrorIfNS_FAILED("cannot get snapshot ids"); + for (j = 0; j < children.count; ++j) { + IMedium *child = children.items[j]; + for (k = 0; k < snapshotIids.count; ++k) { + PRUnichar *diskSnapId = snapshotIids.items[k]; + char *diskSnapIdStr = NULL; + VBOX_UTF16_TO_UTF8(diskSnapId, &diskSnapIdStr); + if (STREQ(diskSnapIdStr, snapshotUuidStr)) { + rc = machine->vtbl->GetStorageControllerByName(machine, + storageControllerName, + &storageController); + VBOX_UTF16_FREE(storageControllerName); + if (!storageController) { + VBOX_RELEASE(child); + break; + } + rc = child->vtbl->GetLocation(child, &childLocUtf16); + reportInternalErrorIfNS_FAILED("cannot get disk location"); + VBOX_UTF16_TO_UTF8(childLocUtf16, &childLocUtf8); + VBOX_UTF16_FREE(childLocUtf16); + ignore_value(VIR_STRDUP(def->disks[diskCount].file, childLocUtf8)); + if (!(def->disks[diskCount].file)) { + VBOX_RELEASE(child); + VBOX_RELEASE(storageController); + virReportOOMError(); + error = true; + break; + } + VBOX_UTF8_FREE(childLocUtf8); + + rc = storageController->vtbl->GetBus(storageController, &storageBus); + reportInternalErrorIfNS_FAILED("cannot get storage controller bus"); + rc = imediumattach->vtbl->GetType(imediumattach, &deviceType); + reportInternalErrorIfNS_FAILED("cannot get medium attachment type"); + rc = imediumattach->vtbl->GetPort(imediumattach, &devicePort); + reportInternalErrorIfNS_FAILED("cannot get medium attachchment type"); + rc = imediumattach->vtbl->GetDevice(imediumattach, &deviceSlot); + reportInternalErrorIfNS_FAILED("cannot get medium attachment device"); + def->disks[diskCount].name = vboxGenerateMediumName(storageBus, + deviceInst, + devicePort, + deviceSlot, + maxPortPerInst, + maxSlotPerPort); + } + VBOX_UTF8_FREE(diskSnapIdStr); + } + } + VBOX_RELEASE(storageController); + VBOX_RELEASE(disk); + diskCount++; + } + vboxArrayRelease(&mediumAttachments); + /* cleanup on error */ + if (error) { + for (i = 0; i < def->dom->ndisks; i++) { + VIR_FREE(def->dom->disks[i]); + } + VIR_FREE(def->dom->disks); + def->dom->ndisks = 0; + return -1; + } + return 0; + +cleanup: + VBOX_RELEASE(snap); + return ret; +} + +static +int vboxSnapshotGetReadOnlyDisks(virDomainSnapshotPtr snapshot, + virDomainSnapshotDefPtr def) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID domiid = VBOX_IID_INITIALIZER; + ISnapshot *snap = NULL; + IMachine *machine = NULL; + IMachine *snapMachine = NULL; + IStorageController *storageController = NULL; + IMedium *disk = NULL; + nsresult rc; + vboxIIDFromUUID(&domiid, dom->uuid); + bool error = false; + vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER; + size_t i = 0; + PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {}; + PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {}; + int diskCount = 0; + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + return -1; + } + + if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) + return -1; + rc = snap->vtbl->GetMachine(snap, &snapMachine); + reportInternalErrorIfNS_FAILED("cannot get machine"); + /* + * Get READ ONLY disks + * In the snapshot metadata, these are the disks written inside the <domain> node + */ + rc = vboxArrayGet(&mediumAttachments, snapMachine, snapMachine->vtbl->GetMediumAttachments); + reportInternalErrorIfNS_FAILED("cannot get medium attachments"); + /* get the number of attachments */ + for (i = 0; i < mediumAttachments.count; i++) { + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (imediumattach) { + IMedium *medium = NULL; + + rc = imediumattach->vtbl->GetMedium(imediumattach, &medium); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (medium) { + def->dom->ndisks++; + VBOX_RELEASE(medium); + } + } + } + + /* Allocate mem, if fails return error */ + if (VIR_ALLOC_N(def->dom->disks, def->dom->ndisks) >= 0) { + for (i = 0; i < def->dom->ndisks; i++) { + if (VIR_ALLOC(def->dom->disks[i]) < 0) { + virReportOOMError(); + error = true; + break; + } + } + } else { + virReportOOMError(); + error = true; + } + + if (!error) + error = !vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort); + + /* get the attachment details here */ + for (i = 0; i < mediumAttachments.count && diskCount < def->dom->ndisks && !error; i++) { + PRUnichar *storageControllerName = NULL; + PRUint32 deviceType = DeviceType_Null; + PRUint32 storageBus = StorageBus_Null; + PRBool readOnly = PR_FALSE; + PRUnichar *mediumLocUtf16 = NULL; + char *mediumLocUtf8 = NULL; + PRUint32 deviceInst = 0; + PRInt32 devicePort = 0; + PRInt32 deviceSlot = 0; + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (!imediumattach) + continue; + rc = imediumattach->vtbl->GetMedium(imediumattach, &disk); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (!disk) + continue; + rc = imediumattach->vtbl->GetController(imediumattach, &storageControllerName); + reportInternalErrorIfNS_FAILED("cannot get storage controller name"); + if (!storageControllerName) { + continue; + } + rc = machine->vtbl->GetStorageControllerByName(machine, + storageControllerName, + &storageController); + reportInternalErrorIfNS_FAILED("cannot get storage controller"); + VBOX_UTF16_FREE(storageControllerName); + if (!storageController) { + continue; + } + rc = disk->vtbl->GetLocation(disk, &mediumLocUtf16); + reportInternalErrorIfNS_FAILED("cannot get disk location"); + VBOX_UTF16_TO_UTF8(mediumLocUtf16, &mediumLocUtf8); + VBOX_UTF16_FREE(mediumLocUtf16); + ignore_value(VIR_STRDUP(def->dom->disks[diskCount]->src, mediumLocUtf8)); + + if (!(def->dom->disks[diskCount]->src)) { + virReportOOMError(); + ret = -1; + goto cleanup; + } + VBOX_UTF8_FREE(mediumLocUtf8); + + rc = storageController->vtbl->GetBus(storageController, &storageBus); + reportInternalErrorIfNS_FAILED("cannot get storage controller bus"); + if (storageBus == StorageBus_IDE) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_IDE; + } else if (storageBus == StorageBus_SATA) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SATA; + } else if (storageBus == StorageBus_SCSI) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SCSI; + } else if (storageBus == StorageBus_Floppy) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_FDC; + } + + rc = imediumattach->vtbl->GetType(imediumattach, &deviceType); + reportInternalErrorIfNS_FAILED("cannot get medium attachment type"); + if (deviceType == DeviceType_HardDisk) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_DISK; + else if (deviceType == DeviceType_Floppy) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + else if (deviceType == DeviceType_DVD) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + + rc = imediumattach->vtbl->GetPort(imediumattach, &devicePort); + reportInternalErrorIfNS_FAILED("cannot get medium attachment port"); + rc = imediumattach->vtbl->GetDevice(imediumattach, &deviceSlot); + reportInternalErrorIfNS_FAILED("cannot get device"); + rc = disk->vtbl->GetReadOnly(disk, &readOnly); + reportInternalErrorIfNS_FAILED("cannot get read only attribute"); + if (readOnly == PR_TRUE) + def->dom->disks[diskCount]->readonly = true; + def->dom->disks[diskCount]->type = VIR_DOMAIN_DISK_TYPE_FILE; + def->dom->disks[diskCount]->dst = vboxGenerateMediumName(storageBus, + deviceInst, + devicePort, + deviceSlot, + maxPortPerInst, + maxSlotPerPort); + if (!def->dom->disks[diskCount]->dst) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not generate medium name for the disk " + "at: controller instance:%u, port:%d, slot:%d"), + deviceInst, devicePort, deviceSlot); + ret = -1; + goto cleanup; + } + diskCount ++; + } + /* cleanup on error */ + if (error) { + for (i = 0; i < def->dom->ndisks; i++) { + VIR_FREE(def->dom->disks[i]); + } + VIR_FREE(def->dom->disks); + def->dom->ndisks = 0; + ret = -1; + goto cleanup; + } + ret = 0; +cleanup: + VBOX_RELEASE(disk); + VBOX_RELEASE(storageController); + vboxArrayRelease(&mediumAttachments); + VBOX_RELEASE(snap); + return ret; +} +#endif + static char * vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -6015,6 +6367,10 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, PRInt64 timestamp; PRBool online = PR_FALSE; char uuidstr[VIR_UUID_STRING_BUFLEN]; +#if VBOX_API_VERSION >=4002 + PRUint32 memorySize = 0; + PRUint32 CPUCount = 0; +#endif virCheckFlags(0, NULL); @@ -6029,11 +6385,40 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) goto cleanup; - if (VIR_ALLOC(def) < 0) + if (VIR_ALLOC(def) < 0 + || VIR_ALLOC(def->dom) < 0) goto cleanup; if (VIR_STRDUP(def->name, snapshot->name) < 0) goto cleanup; +#if VBOX_API_VERSION >=4002 + /* Register def->dom properties for them to be saved inside the snapshot XMl + * Otherwise, there is a problem while parsing the xml + */ + def->dom->virtType = VIR_DOMAIN_VIRT_VBOX; + def->dom->id = dom->id; + memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); + ignore_value(VIR_STRDUP(def->dom->name, dom->name)); + machine->vtbl->GetMemorySize(machine, &memorySize); + def->dom->mem.cur_balloon = memorySize * 1024; + /* Currently setting memory and maxMemory as same, cause + * the notation here seems to be inconsistent while + * reading and while dumping xml + */ + def->dom->mem.max_balloon = memorySize * 1024; + ignore_value(VIR_STRDUP(def->dom->os.type, "hvm")); + def->dom->os.arch = virArchFromHost(); + machine->vtbl->GetCPUCount(machine, &CPUCount); + def->dom->maxvcpus = def->dom->vcpus = CPUCount; + if (vboxSnapshotGetReadWriteDisks(def, snapshot) < 0) { + VIR_DEBUG("Could not get read write disks for snapshot"); + } + + if (vboxSnapshotGetReadOnlyDisks(snapshot, def) <0) { + VIR_DEBUG("Could not get Readonly disks for snapshot"); + } +#endif /* VBOX_API_VERSION >= 4002 */ + rc = snap->vtbl->GetDescription(snap, &str16); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -6098,8 +6483,8 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, def->state = VIR_DOMAIN_SHUTOFF; virUUIDFormat(dom->uuid, uuidstr); + memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); ret = virDomainSnapshotDefFormat(uuidstr, def, flags, 0); - cleanup: virDomainSnapshotDefFree(def); VBOX_RELEASE(parent); @@ -6804,6 +7189,8 @@ cleanup: return ret; } + + static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -6839,8 +7226,9 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, goto cleanup; } - /* VBOX snapshots do not require any libvirt metadata, making this - * flag trivial once we know we have a valid snapshot. */ + /* In case we just want to delete the metadata, we will edit the vbox file in order + *to remove the node concerning the snapshot + */ if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) { ret = 0; goto cleanup; @@ -8662,8 +9050,9 @@ cleanup: return ret; } -static int vboxStorageVolDelete(virStorageVolPtr vol, - unsigned int flags) +static int vboxStorageDeleteOrClose(virStorageVolPtr vol, + unsigned int flags, + unsigned int flagDeleteOrClose) { VBOX_OBJECT_CHECK(vol->conn, int, -1); vboxIID hddIID = VBOX_IID_INITIALIZER; @@ -8818,8 +9207,18 @@ static int vboxStorageVolDelete(virStorageVolPtr vol, if (machineIdsSize == 0 || machineIdsSize == deregister) { IProgress *progress = NULL; - +#if VBOX_API_VERSION >= 4002 + if (flagDeleteOrClose & VBOX_STORAGE_CLOSE_FLAG) { + rc = hardDisk->vtbl->Close(hardDisk); + if (NS_SUCCEEDED(rc)) { + DEBUGIID("HardDisk closed, UUID", hddIID.value); + ret = 0; + } + } +#endif + if (flagDeleteOrClose & VBOX_STORAGE_DELETE_FLAG){ rc = hardDisk->vtbl->DeleteStorage(hardDisk, &progress); + } if (NS_SUCCEEDED(rc) && progress) { progress->vtbl->WaitForCompletion(progress, -1); @@ -8838,6 +9237,12 @@ static int vboxStorageVolDelete(virStorageVolPtr vol, return ret; } +static int vboxStorageVolDelete(virStorageVolPtr vol, + unsigned int flags) +{ + return vboxStorageDeleteOrClose(vol, flags, VBOX_STORAGE_DELETE_FLAG); +} + static int vboxStorageVolGetInfo(virStorageVolPtr vol, virStorageVolInfoPtr info) { VBOX_OBJECT_CHECK(vol->conn, int, -1); IHardDisk *hardDisk = NULL; -- 1.7.10.4

Hi, On Mon, Sep 02, 2013 at 06:05:51PM +0200, Manuel VIVES wrote:
It will be needed for the futur patches because we will redefine snapshots --- src/conf/domain_conf.c | 20 ++- src/vbox/vbox_tmpl.c | 427 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 427 insertions(+), 20 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f1623f1..c98ff63 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -16810,15 +16810,17 @@ virDomainDefFormatInternal(virDomainDefPtr def, if (virDomainChrDefFormat(buf, &console, flags) < 0) goto error; } - if (STREQ(def->os.type, "hvm") && - def->nconsoles == 0 && - def->nserials > 0) { - virDomainChrDef console; - memcpy(&console, def->serials[n], sizeof(console)); - console.deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE; - console.targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; - if (virDomainChrDefFormat(buf, &console, flags) < 0) - goto error; + if (def->os.type) { + if (STREQ(def->os.type, "hvm") && + def->nconsoles == 0 && + def->nserials > 0) {
Maybe I'm missing something but this looks like: if (STREQ_NULLABLE(def->os.type, "hvm") && def->nconsoles == 0 && def->nserials > 0) { is simpler. Cheers, -- Guido
+ virDomainChrDef console; + memcpy(&console, def->serials[n], sizeof(console)); + console.deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE; + console.targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; + if (virDomainChrDefFormat(buf, &console, flags) < 0) + goto error; + } }
for (n = 0; n < def->nchannels; n++) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 5e5ea85..ded179f 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -38,6 +38,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <libxml/xmlwriter.h>
#include "internal.h" #include "datatypes.h" @@ -58,6 +59,8 @@ #include "fdstream.h" #include "viruri.h" #include "virstring.h" +#include "virtime.h" +#include "virutil.h"
/* This one changes from version to version. */ #if VBOX_API_VERSION == 2002 @@ -271,10 +274,16 @@ static vboxGlobalData *g_pVBoxGlobalData = NULL;
#endif /* VBOX_API_VERSION >= 4000 */
+#define reportInternalErrorIfNS_FAILED(message) \ + if (NS_FAILED(rc)) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(message)); \ + goto cleanup; \ + } + + static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml); static int vboxDomainCreate(virDomainPtr dom); static int vboxDomainUndefineFlags(virDomainPtr dom, unsigned int flags); - static void vboxDriverLock(vboxGlobalData *data) { virMutexLock(&data->lock); } @@ -283,6 +292,12 @@ static void vboxDriverUnlock(vboxGlobalData *data) { virMutexUnlock(&data->lock); }
+typedef enum { + VBOX_STORAGE_DELETE_FLAG = 0, +#if VBOX_API_VERSION >= 4002 + VBOX_STORAGE_CLOSE_FLAG = 1, +#endif +} vboxStorageDeleteOrCloseFlags; #if VBOX_API_VERSION == 2002
static void nsIDtoChar(unsigned char *uuid, const nsID *iid) { @@ -5907,7 +5922,8 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL);
if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps, - data->xmlopt, 0, 0))) + data->xmlopt, -1, VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | + VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE))) goto cleanup;
if (def->ndisks) { @@ -5915,7 +5931,6 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, _("disk snapshots not supported yet")); goto cleanup; } - vboxIIDFromUUID(&domiid, dom->uuid); rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); if (NS_FAILED(rc)) { @@ -5923,7 +5938,6 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, _("no domain with matching UUID")); goto cleanup; } - rc = machine->vtbl->GetState(machine, &state); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -5998,6 +6012,344 @@ cleanup: return ret; }
+#if VBOX_API_VERSION >=4002 +static +int vboxSnapshotGetReadWriteDisks(virDomainSnapshotDefPtr def, + virDomainSnapshotPtr snapshot) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID domiid = VBOX_IID_INITIALIZER; + IMachine *machine = NULL; + ISnapshot *snap = NULL; + IMachine *snapMachine = NULL; + bool error = false; + vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER; + PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {}; + PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {}; + int diskCount = 0; + nsresult rc; + vboxIID snapIid = VBOX_IID_INITIALIZER; + char *snapshotUuidStr = NULL; + size_t i = 0; + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + reportInternalErrorIfNS_FAILED("no domain with matching UUID"); + if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) { + ret = -1; + goto cleanup; + } + rc = snap->vtbl->GetId(snap, &snapIid.value); + reportInternalErrorIfNS_FAILED("Could not get snapshot id"); + + VBOX_UTF16_TO_UTF8(snapIid.value, &snapshotUuidStr); + rc = snap->vtbl->GetMachine(snap, &snapMachine); + reportInternalErrorIfNS_FAILED("could not get machine"); + def->ndisks = 0; + rc = vboxArrayGet(&mediumAttachments, snapMachine, snapMachine->vtbl->GetMediumAttachments); + reportInternalErrorIfNS_FAILED("no medium attachments"); + /* get the number of attachments */ + for (i = 0; i < mediumAttachments.count; i++) { + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (imediumattach) { + IMedium *medium = NULL; + + rc = imediumattach->vtbl->GetMedium(imediumattach, &medium); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (medium) { + def->ndisks++; + VBOX_RELEASE(medium); + } + } + } + /* Allocate mem, if fails return error */ + if (VIR_ALLOC_N(def->disks, def->ndisks) < 0) { + virReportOOMError(); + error = true; + } + if (!error) + error = !vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort); + + /* get the attachment details here */ + for (i = 0; i < mediumAttachments.count && diskCount < def->ndisks && !error; i++) { + IStorageController *storageController = NULL; + PRUnichar *storageControllerName = NULL; + PRUint32 deviceType = DeviceType_Null; + PRUint32 storageBus = StorageBus_Null; + IMedium *disk = NULL; + PRUnichar *childLocUtf16 = NULL; + char *childLocUtf8 = NULL; + PRUint32 deviceInst = 0; + PRInt32 devicePort = 0; + PRInt32 deviceSlot = 0; + vboxArray children = VBOX_ARRAY_INITIALIZER; + vboxArray snapshotIids = VBOX_ARRAY_INITIALIZER; + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + size_t j = 0; + size_t k = 0; + if (!imediumattach) + continue; + rc = imediumattach->vtbl->GetMedium(imediumattach, &disk); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (!disk) + continue; + rc = imediumattach->vtbl->GetController(imediumattach, &storageControllerName); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (!storageControllerName) { + VBOX_RELEASE(disk); + continue; + } + rc= vboxArrayGet(&children, disk, disk->vtbl->GetChildren); + reportInternalErrorIfNS_FAILED("cannot get children disk"); + rc = vboxArrayGetWithPtrArg(&snapshotIids, disk, disk->vtbl->GetSnapshotIds, domiid.value); + reportInternalErrorIfNS_FAILED("cannot get snapshot ids"); + for (j = 0; j < children.count; ++j) { + IMedium *child = children.items[j]; + for (k = 0; k < snapshotIids.count; ++k) { + PRUnichar *diskSnapId = snapshotIids.items[k]; + char *diskSnapIdStr = NULL; + VBOX_UTF16_TO_UTF8(diskSnapId, &diskSnapIdStr); + if (STREQ(diskSnapIdStr, snapshotUuidStr)) { + rc = machine->vtbl->GetStorageControllerByName(machine, + storageControllerName, + &storageController); + VBOX_UTF16_FREE(storageControllerName); + if (!storageController) { + VBOX_RELEASE(child); + break; + } + rc = child->vtbl->GetLocation(child, &childLocUtf16); + reportInternalErrorIfNS_FAILED("cannot get disk location"); + VBOX_UTF16_TO_UTF8(childLocUtf16, &childLocUtf8); + VBOX_UTF16_FREE(childLocUtf16); + ignore_value(VIR_STRDUP(def->disks[diskCount].file, childLocUtf8)); + if (!(def->disks[diskCount].file)) { + VBOX_RELEASE(child); + VBOX_RELEASE(storageController); + virReportOOMError(); + error = true; + break; + } + VBOX_UTF8_FREE(childLocUtf8); + + rc = storageController->vtbl->GetBus(storageController, &storageBus); + reportInternalErrorIfNS_FAILED("cannot get storage controller bus"); + rc = imediumattach->vtbl->GetType(imediumattach, &deviceType); + reportInternalErrorIfNS_FAILED("cannot get medium attachment type"); + rc = imediumattach->vtbl->GetPort(imediumattach, &devicePort); + reportInternalErrorIfNS_FAILED("cannot get medium attachchment type"); + rc = imediumattach->vtbl->GetDevice(imediumattach, &deviceSlot); + reportInternalErrorIfNS_FAILED("cannot get medium attachment device"); + def->disks[diskCount].name = vboxGenerateMediumName(storageBus, + deviceInst, + devicePort, + deviceSlot, + maxPortPerInst, + maxSlotPerPort); + } + VBOX_UTF8_FREE(diskSnapIdStr); + } + } + VBOX_RELEASE(storageController); + VBOX_RELEASE(disk); + diskCount++; + } + vboxArrayRelease(&mediumAttachments); + /* cleanup on error */ + if (error) { + for (i = 0; i < def->dom->ndisks; i++) { + VIR_FREE(def->dom->disks[i]); + } + VIR_FREE(def->dom->disks); + def->dom->ndisks = 0; + return -1; + } + return 0; + +cleanup: + VBOX_RELEASE(snap); + return ret; +} + +static +int vboxSnapshotGetReadOnlyDisks(virDomainSnapshotPtr snapshot, + virDomainSnapshotDefPtr def) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID domiid = VBOX_IID_INITIALIZER; + ISnapshot *snap = NULL; + IMachine *machine = NULL; + IMachine *snapMachine = NULL; + IStorageController *storageController = NULL; + IMedium *disk = NULL; + nsresult rc; + vboxIIDFromUUID(&domiid, dom->uuid); + bool error = false; + vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER; + size_t i = 0; + PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {}; + PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {}; + int diskCount = 0; + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + return -1; + } + + if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) + return -1; + rc = snap->vtbl->GetMachine(snap, &snapMachine); + reportInternalErrorIfNS_FAILED("cannot get machine"); + /* + * Get READ ONLY disks + * In the snapshot metadata, these are the disks written inside the <domain> node + */ + rc = vboxArrayGet(&mediumAttachments, snapMachine, snapMachine->vtbl->GetMediumAttachments); + reportInternalErrorIfNS_FAILED("cannot get medium attachments"); + /* get the number of attachments */ + for (i = 0; i < mediumAttachments.count; i++) { + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (imediumattach) { + IMedium *medium = NULL; + + rc = imediumattach->vtbl->GetMedium(imediumattach, &medium); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (medium) { + def->dom->ndisks++; + VBOX_RELEASE(medium); + } + } + } + + /* Allocate mem, if fails return error */ + if (VIR_ALLOC_N(def->dom->disks, def->dom->ndisks) >= 0) { + for (i = 0; i < def->dom->ndisks; i++) { + if (VIR_ALLOC(def->dom->disks[i]) < 0) { + virReportOOMError(); + error = true; + break; + } + } + } else { + virReportOOMError(); + error = true; + } + + if (!error) + error = !vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort); + + /* get the attachment details here */ + for (i = 0; i < mediumAttachments.count && diskCount < def->dom->ndisks && !error; i++) { + PRUnichar *storageControllerName = NULL; + PRUint32 deviceType = DeviceType_Null; + PRUint32 storageBus = StorageBus_Null; + PRBool readOnly = PR_FALSE; + PRUnichar *mediumLocUtf16 = NULL; + char *mediumLocUtf8 = NULL; + PRUint32 deviceInst = 0; + PRInt32 devicePort = 0; + PRInt32 deviceSlot = 0; + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (!imediumattach) + continue; + rc = imediumattach->vtbl->GetMedium(imediumattach, &disk); + reportInternalErrorIfNS_FAILED("cannot get medium"); + if (!disk) + continue; + rc = imediumattach->vtbl->GetController(imediumattach, &storageControllerName); + reportInternalErrorIfNS_FAILED("cannot get storage controller name"); + if (!storageControllerName) { + continue; + } + rc = machine->vtbl->GetStorageControllerByName(machine, + storageControllerName, + &storageController); + reportInternalErrorIfNS_FAILED("cannot get storage controller"); + VBOX_UTF16_FREE(storageControllerName); + if (!storageController) { + continue; + } + rc = disk->vtbl->GetLocation(disk, &mediumLocUtf16); + reportInternalErrorIfNS_FAILED("cannot get disk location"); + VBOX_UTF16_TO_UTF8(mediumLocUtf16, &mediumLocUtf8); + VBOX_UTF16_FREE(mediumLocUtf16); + ignore_value(VIR_STRDUP(def->dom->disks[diskCount]->src, mediumLocUtf8)); + + if (!(def->dom->disks[diskCount]->src)) { + virReportOOMError(); + ret = -1; + goto cleanup; + } + VBOX_UTF8_FREE(mediumLocUtf8); + + rc = storageController->vtbl->GetBus(storageController, &storageBus); + reportInternalErrorIfNS_FAILED("cannot get storage controller bus"); + if (storageBus == StorageBus_IDE) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_IDE; + } else if (storageBus == StorageBus_SATA) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SATA; + } else if (storageBus == StorageBus_SCSI) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SCSI; + } else if (storageBus == StorageBus_Floppy) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_FDC; + } + + rc = imediumattach->vtbl->GetType(imediumattach, &deviceType); + reportInternalErrorIfNS_FAILED("cannot get medium attachment type"); + if (deviceType == DeviceType_HardDisk) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_DISK; + else if (deviceType == DeviceType_Floppy) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + else if (deviceType == DeviceType_DVD) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + + rc = imediumattach->vtbl->GetPort(imediumattach, &devicePort); + reportInternalErrorIfNS_FAILED("cannot get medium attachment port"); + rc = imediumattach->vtbl->GetDevice(imediumattach, &deviceSlot); + reportInternalErrorIfNS_FAILED("cannot get device"); + rc = disk->vtbl->GetReadOnly(disk, &readOnly); + reportInternalErrorIfNS_FAILED("cannot get read only attribute"); + if (readOnly == PR_TRUE) + def->dom->disks[diskCount]->readonly = true; + def->dom->disks[diskCount]->type = VIR_DOMAIN_DISK_TYPE_FILE; + def->dom->disks[diskCount]->dst = vboxGenerateMediumName(storageBus, + deviceInst, + devicePort, + deviceSlot, + maxPortPerInst, + maxSlotPerPort); + if (!def->dom->disks[diskCount]->dst) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not generate medium name for the disk " + "at: controller instance:%u, port:%d, slot:%d"), + deviceInst, devicePort, deviceSlot); + ret = -1; + goto cleanup; + } + diskCount ++; + } + /* cleanup on error */ + if (error) { + for (i = 0; i < def->dom->ndisks; i++) { + VIR_FREE(def->dom->disks[i]); + } + VIR_FREE(def->dom->disks); + def->dom->ndisks = 0; + ret = -1; + goto cleanup; + } + ret = 0; +cleanup: + VBOX_RELEASE(disk); + VBOX_RELEASE(storageController); + vboxArrayRelease(&mediumAttachments); + VBOX_RELEASE(snap); + return ret; +} +#endif + static char * vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -6015,6 +6367,10 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, PRInt64 timestamp; PRBool online = PR_FALSE; char uuidstr[VIR_UUID_STRING_BUFLEN]; +#if VBOX_API_VERSION >=4002 + PRUint32 memorySize = 0; + PRUint32 CPUCount = 0; +#endif
virCheckFlags(0, NULL);
@@ -6029,11 +6385,40 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) goto cleanup;
- if (VIR_ALLOC(def) < 0) + if (VIR_ALLOC(def) < 0 + || VIR_ALLOC(def->dom) < 0) goto cleanup; if (VIR_STRDUP(def->name, snapshot->name) < 0) goto cleanup;
+#if VBOX_API_VERSION >=4002 + /* Register def->dom properties for them to be saved inside the snapshot XMl + * Otherwise, there is a problem while parsing the xml + */ + def->dom->virtType = VIR_DOMAIN_VIRT_VBOX; + def->dom->id = dom->id; + memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); + ignore_value(VIR_STRDUP(def->dom->name, dom->name)); + machine->vtbl->GetMemorySize(machine, &memorySize); + def->dom->mem.cur_balloon = memorySize * 1024; + /* Currently setting memory and maxMemory as same, cause + * the notation here seems to be inconsistent while + * reading and while dumping xml + */ + def->dom->mem.max_balloon = memorySize * 1024; + ignore_value(VIR_STRDUP(def->dom->os.type, "hvm")); + def->dom->os.arch = virArchFromHost(); + machine->vtbl->GetCPUCount(machine, &CPUCount); + def->dom->maxvcpus = def->dom->vcpus = CPUCount; + if (vboxSnapshotGetReadWriteDisks(def, snapshot) < 0) { + VIR_DEBUG("Could not get read write disks for snapshot"); + } + + if (vboxSnapshotGetReadOnlyDisks(snapshot, def) <0) { + VIR_DEBUG("Could not get Readonly disks for snapshot"); + } +#endif /* VBOX_API_VERSION >= 4002 */ + rc = snap->vtbl->GetDescription(snap, &str16); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -6098,8 +6483,8 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, def->state = VIR_DOMAIN_SHUTOFF;
virUUIDFormat(dom->uuid, uuidstr); + memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); ret = virDomainSnapshotDefFormat(uuidstr, def, flags, 0); - cleanup: virDomainSnapshotDefFree(def); VBOX_RELEASE(parent); @@ -6804,6 +7189,8 @@ cleanup: return ret; }
+ + static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -6839,8 +7226,9 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, goto cleanup; }
- /* VBOX snapshots do not require any libvirt metadata, making this - * flag trivial once we know we have a valid snapshot. */ + /* In case we just want to delete the metadata, we will edit the vbox file in order + *to remove the node concerning the snapshot + */ if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) { ret = 0; goto cleanup; @@ -8662,8 +9050,9 @@ cleanup: return ret; }
-static int vboxStorageVolDelete(virStorageVolPtr vol, - unsigned int flags) +static int vboxStorageDeleteOrClose(virStorageVolPtr vol, + unsigned int flags, + unsigned int flagDeleteOrClose) { VBOX_OBJECT_CHECK(vol->conn, int, -1); vboxIID hddIID = VBOX_IID_INITIALIZER; @@ -8818,8 +9207,18 @@ static int vboxStorageVolDelete(virStorageVolPtr vol,
if (machineIdsSize == 0 || machineIdsSize == deregister) { IProgress *progress = NULL; - +#if VBOX_API_VERSION >= 4002 + if (flagDeleteOrClose & VBOX_STORAGE_CLOSE_FLAG) { + rc = hardDisk->vtbl->Close(hardDisk); + if (NS_SUCCEEDED(rc)) { + DEBUGIID("HardDisk closed, UUID", hddIID.value); + ret = 0; + } + } +#endif + if (flagDeleteOrClose & VBOX_STORAGE_DELETE_FLAG){ rc = hardDisk->vtbl->DeleteStorage(hardDisk, &progress); + }
if (NS_SUCCEEDED(rc) && progress) { progress->vtbl->WaitForCompletion(progress, -1); @@ -8838,6 +9237,12 @@ static int vboxStorageVolDelete(virStorageVolPtr vol, return ret; }
+static int vboxStorageVolDelete(virStorageVolPtr vol, + unsigned int flags) +{ + return vboxStorageDeleteOrClose(vol, flags, VBOX_STORAGE_DELETE_FLAG); +} + static int vboxStorageVolGetInfo(virStorageVolPtr vol, virStorageVolInfoPtr info) { VBOX_OBJECT_CHECK(vol->conn, int, -1); IHardDisk *hardDisk = NULL; -- 1.7.10.4
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

The snapshots are saved in xml files, and then can be redefined --- src/vbox/vbox_tmpl.c | 848 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 840 insertions(+), 8 deletions(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index ded179f..2b07dec 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -61,6 +61,7 @@ #include "virstring.h" #include "virtime.h" #include "virutil.h" +#include "dirname.h" /* This one changes from version to version. */ #if VBOX_API_VERSION == 2002 @@ -274,6 +275,12 @@ static vboxGlobalData *g_pVBoxGlobalData = NULL; #endif /* VBOX_API_VERSION >= 4000 */ +/*This error is a bit specific + *In the VBOX API it is named E_ACCESSDENIED + *It is returned when the called object is not ready. In + *particular when we do any call on a disk which has been closed +*/ +#define VBOX_E_ACCESSDENIED 0x80070005 #define reportInternalErrorIfNS_FAILED(message) \ if (NS_FAILED(rc)) { \ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(message)); \ @@ -284,6 +291,8 @@ static vboxGlobalData *g_pVBoxGlobalData = NULL; static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml); static int vboxDomainCreate(virDomainPtr dom); static int vboxDomainUndefineFlags(virDomainPtr dom, unsigned int flags); +static virStorageVolPtr vboxStorageVolLookupByPath(virConnectPtr conn, const char *path); +static int vboxStorageDeleteOrClose(virStorageVolPtr vol, unsigned int flags, unsigned int flagDeleteOrClose); static void vboxDriverLock(vboxGlobalData *data) { virMutexLock(&data->lock); } @@ -5896,6 +5905,823 @@ cleanup: return snapshot; } +#if VBOX_API_VERSION >=4002 +static void +vboxSnapshotXmlRetrieveSnapshotNodeByName(xmlNodePtr a_node, + const char *name, + xmlNodePtr *snap_node) +{ + xmlNodePtr cur_node = NULL; + + for (cur_node = a_node; cur_node; cur_node = cur_node->next) { + if (cur_node->type == XML_ELEMENT_NODE) { + if (!xmlStrcmp(cur_node->name, (const xmlChar *) "Snapshot") && + STREQ(virXMLPropString(cur_node, "name"), name)) { + *snap_node = cur_node; + return; + } + } + if (cur_node->children) + vboxSnapshotXmlRetrieveSnapshotNodeByName(cur_node->children, name, snap_node); + } +} + + + + +static int +vboxDetachAndCloseDisks(virDomainPtr dom, + IMedium *disk) +{ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + nsresult rc; + PRUnichar *location = NULL; + vboxArray childrenDiskArray = VBOX_ARRAY_INITIALIZER; + virStorageVolPtr volPtr = NULL; + char *location_utf8 = NULL; + PRUint32 dummyState = 0; + size_t i = 0; + if (disk == NULL) { + VIR_DEBUG("Null pointer to disk"); + return -1; + } + rc = disk->vtbl->GetLocation(disk, &location); + if (rc == VBOX_E_ACCESSDENIED) { + VIR_DEBUG("Disk already closed"); + goto cleanup; + } + reportInternalErrorIfNS_FAILED("cannot get disk location"); + rc = vboxArrayGet(&childrenDiskArray, disk, disk->vtbl->GetChildren); + reportInternalErrorIfNS_FAILED("cannot get children disks"); + for (i = 0; i < childrenDiskArray.count; ++i) { + IMedium *childDisk = childrenDiskArray.items[i]; + if (childDisk) { + vboxDetachAndCloseDisks(dom, childDisk); + } + } + rc = disk->vtbl->RefreshState(disk, &dummyState); + reportInternalErrorIfNS_FAILED("cannot refresh state"); + VBOX_UTF16_TO_UTF8(location, &location_utf8); + volPtr = vboxStorageVolLookupByPath(dom->conn, location_utf8); + + if (volPtr) { + VIR_DEBUG("Closing %s", location_utf8); + if (vboxStorageDeleteOrClose(volPtr, 0, VBOX_STORAGE_CLOSE_FLAG) != 0) { + VIR_DEBUG("Error while closing disk"); + } + } + VBOX_UTF8_FREE(location_utf8); +cleanup: + VBOX_UTF16_FREE(location); + vboxArrayRelease(&childrenDiskArray); + return ret; +} + +static void +vboxSnapshotXmlAddChild(xmlNodePtr parent, + xmlNodePtr child) +{ + /*Used in order to add child without writing the stuff concerning xml namespaces*/ + xmlBufferPtr tmpBuf = xmlBufferCreate(); + char *tmpString = NULL; + xmlNodePtr tmpNode = NULL; + xmlNodeDump(tmpBuf, parent->doc, child, 0, 0); + ignore_value(VIR_STRDUP(tmpString, (char *)xmlBufferContent(tmpBuf))); + xmlParseInNodeContext(parent, tmpString, (int)strlen(tmpString), 0, &tmpNode); + if (tmpNode) { + if (xmlAddChild(parent, xmlCopyNode(tmpNode, 1)) == NULL) { + VIR_DEBUG("Error while adding %s to %s", (char *)tmpNode->name, (char *)parent->name); + } + } + xmlFree(tmpNode); + xmlBufferFree(tmpBuf); +} + +static void +vboxSnapshotXmlRetrieveMachineNode(xmlNodePtr root, + xmlNodePtr *machineNode) +{ + xmlNodePtr cur = root->xmlChildrenNode; + while (cur && xmlIsBlankNode(cur)) { + cur = cur -> next; + } + if (xmlStrcmp(cur->name, (const xmlChar *) "Machine")) { + VIR_DEBUG("Problem, found %s, Machine expected", (char *)cur->name); + return; + } + *machineNode = cur; +} + +static void +vboxSnapshotXmlRetrieveSnapshotsNode(xmlNodePtr snapshotNode, + xmlNodePtr *snapshotsNode) +{ + xmlNodePtr cur_node = NULL; + + for (cur_node = snapshotNode; cur_node; cur_node = cur_node->next) { + if (cur_node->type == XML_ELEMENT_NODE) { + if (!xmlStrcmp(cur_node->name, (const xmlChar *) "Snapshots")) { + *snapshotsNode = cur_node; + return; + } + } + if (cur_node->children) + vboxSnapshotXmlRetrieveSnapshotsNode(cur_node->children, snapshotsNode); + } +} + +static void +vboxSnapshotXmlRetrieveRootNodeByName(xmlNodePtr root, + const char *name, + xmlNodePtr *returnNode) +{ + xmlNodePtr cur = root->xmlChildrenNode; + while (cur && xmlIsBlankNode(cur)) { + cur = cur -> next; + } + if (xmlStrcmp(cur->name, (const xmlChar *) "Machine")) { + VIR_DEBUG("Problem, found %s, Machine expected", (char *)cur->name); + } + cur=cur->xmlChildrenNode; + while (cur != NULL) { + if (!xmlStrcmp(cur->name, (const xmlChar*) name)) { + *returnNode = cur; + return; + } + cur = cur -> next; + } +} + +static void +vboxSnapshotXmlAppendDiskToMediaRegistry(xmlNodePtr *inMediaRegistry, + char *parentId, + char *childId, + char *childLocation, + char *childFormat) +{ + /*This function will modify the inMediaregistry node to append all the informations about the child disk + */ + xmlNodePtr cur_node = NULL; + for (cur_node = *inMediaRegistry; cur_node; cur_node = cur_node->next) { + if (cur_node) { + if (cur_node->type == XML_ELEMENT_NODE + && !xmlStrcmp(cur_node->name, (const xmlChar *) "HardDisk") + && xmlHasProp(cur_node, BAD_CAST "uuid") != NULL + && STREQ((char *)xmlHasProp(cur_node, BAD_CAST "uuid")->children->content, parentId)) { + + xmlNodePtr childDiskNode = xmlNewTextChild(cur_node, NULL, BAD_CAST "HardDisk", NULL); + xmlNewProp(childDiskNode, BAD_CAST "uuid", BAD_CAST childId); + xmlNewProp(childDiskNode, BAD_CAST "location", BAD_CAST childLocation); + xmlNewProp(childDiskNode, BAD_CAST "format", BAD_CAST childFormat); + return; + } + } + if (&(cur_node->children)) + vboxSnapshotXmlAppendDiskToMediaRegistry(&(cur_node->children), parentId, childId, childLocation, childFormat); + + } +} + +static int +vboxSnapshotGenerateVboxXML(xmlNodePtr rootElementVboxXML, + char *storageControllerString, + char *parent, + char *defName, + char *timeStamp, + char *uuid, + xmlNodePtr *snapshotNodeReturned) +{ + xmlDocPtr doc; + xmlNodePtr snapshotNode; + xmlNodePtr snapshotsNode; + xmlNodePtr machineHardwareNode = NULL; + char *uuidstrWithBrackets = NULL; + char *timeVboxFormat = NULL; + int ret = -1; + /*We change the date format from "yyyy-MM-dd hh:mm:ss.msec+timeZone" to "yyyy-MM-ddThh:mm:ssZ"*/ + char *date = virStringSplit(virStringJoin((const char**)virStringSplit(virStringSplit(timeStamp, "+",0)[0]," ",0),"T"), ".",0)[0]; + + /*Retrieve hardware*/ + vboxSnapshotXmlRetrieveRootNodeByName(rootElementVboxXML, "Hardware", &machineHardwareNode); + /* Create a new XML DOM tree, to which the XML document will be + * written */ + doc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION); + if (doc == NULL) { + VIR_DEBUG("Error creating the xml document tree\n"); + ret = -1; + goto cleanup; + } + /* Create a new XML node, to which the XML document will be + * appended */ + snapshotNode = xmlNewDocNode(doc, NULL, BAD_CAST "Snapshot", NULL); + if (snapshotNode == NULL) { + VIR_DEBUG("Error creating the xml node Snapshot\n"); + ret = -1; + goto cleanup; + } + if (virAsprintf(&uuidstrWithBrackets, "{%s}", uuid) != -1) { + xmlNewProp(snapshotNode, BAD_CAST "uuid", BAD_CAST uuidstrWithBrackets); + VIR_FREE(uuidstrWithBrackets); + } + xmlNewProp(snapshotNode, BAD_CAST "name", BAD_CAST defName); + if (virAsprintf(&timeVboxFormat, "%sZ", date) != -1) { + xmlNewProp(snapshotNode, BAD_CAST "timeStamp", BAD_CAST timeVboxFormat); + VIR_FREE(timeVboxFormat); + } + xmlDocSetRootElement(doc, snapshotNode); + if (machineHardwareNode) { + vboxSnapshotXmlAddChild(snapshotNode, machineHardwareNode); + } + xmlNodePtr listStorageControllerNodes; + xmlParseInNodeContext(snapshotNode, storageControllerString, (int)strlen(storageControllerString), 0, &listStorageControllerNodes); + if (listStorageControllerNodes) { + if (xmlAddChild(snapshotNode, xmlCopyNode(listStorageControllerNodes, 1)) == NULL) { + VIR_DEBUG("Could not add listStorageControllerNodes node to snapshot node"); + ret = -1; + goto cleanup; + } + } + snapshotsNode = xmlNewDocNode(doc, NULL, BAD_CAST "Snapshots", NULL); + if (snapshotsNode == NULL) { + VIR_DEBUG("Error creating the xml node Snapshots\n"); + ret = -1; + goto cleanup; + } + if (snapshotsNode) { + if (xmlAddChild(snapshotNode, xmlCopyNode(snapshotsNode, 1)) == NULL) { + VIR_DEBUG("Could not add snapshotsNode node to snapshot node"); + ret = -1; + goto cleanup; + } + } + if (parent) { + xmlNodePtr rootSnapshotNode = NULL; + xmlNodePtr parentNode = NULL; + xmlNodePtr parentSnapshotsNode = NULL; + vboxSnapshotXmlRetrieveRootNodeByName(rootElementVboxXML, "Snapshot", &rootSnapshotNode); + if (!rootSnapshotNode) { + VIR_DEBUG("Could not retrieve Snapshot node"); + ret = -1; + goto cleanup; + } + vboxSnapshotXmlRetrieveSnapshotNodeByName(rootSnapshotNode, parent, &parentNode); + if (!parentNode) { + VIR_DEBUG("Could not retrieve Snapshot node of %s", parent); + ret = -1; + goto cleanup; + } + vboxSnapshotXmlRetrieveSnapshotsNode(parentNode->children, &parentSnapshotsNode); + if (!parentSnapshotsNode) { + VIR_DEBUG("Could not retrieve Snapshots node"); + ret = -1; + goto cleanup; + } + if (xmlAddChild(parentSnapshotsNode, xmlCopyNode(snapshotNode, 1)) == NULL) { + VIR_DEBUG("Could not add snapshotsNode node to its parent"); + ret = -1; + goto cleanup; + } else { + *snapshotNodeReturned = xmlCopyNode(rootSnapshotNode, 1); + } + } else { + *snapshotNodeReturned = xmlCopyNode(snapshotNode,1); + } + ret = 0; +cleanup: + return ret; +} + +static xmlBufferPtr +vboxSnapshotGenerateXmlSkeleton(char *domainUuid, + char *machineName, + char *snapshotUuid, + char *snapshotFolder, + PRInt64 lastStateChange, + bool isCurrent, + char *savedCurrentSnapshotUuid) +{ + char *snapshotUuidWithBrackets = NULL; + char *lastStateChangeStr = NULL; + /*Let's write a new xml + */ + xmlTextWriterPtr writer; + xmlBufferPtr buf = xmlBufferCreate(); + xmlBufferPtr ret = NULL; + int resultXml; + char *uuidWithBracket = NULL; + + /* Create a new XmlWriter with no compression. */ + writer = xmlNewTextWriterMemory(buf, 0); + if (writer == NULL) { + VIR_DEBUG("Error creating the xml writer\n"); + goto cleanup; + } + + /* Start the document with the xml default for the version, + * encoding ISO 8859-1 and the default for the standalone + * declaration. */ + resultXml = xmlTextWriterStartDocument(writer, NULL, "ISO-8859-1", NULL); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterStartDocument\n"); + goto cleanup; + } + /* Write a comment as child of root. + * Please observe, that the input to the xmlTextWriter functions + * HAS to be in UTF-8, even if the output XML is encoded + * in iso-8859-1 */ + resultXml = xmlTextWriterWriteFormatComment(writer, "WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE \n" + "OVERWRITTEN AND LOST.\n" + "Changes to this xml configuration should be made using Virtualbox \n" + "or other application using the libvirt API"); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteComment\n"); + goto cleanup; + } + xmlTextWriterWriteRaw(writer,BAD_CAST "\n"); /*Break line after comment*/ + + /* Start an element named "VirtualBox". Since thist is the first + * element, this will be the root element of the document. */ + resultXml = xmlTextWriterStartElement(writer, BAD_CAST "VirtualBox"); + if (resultXml < 0) { + VIR_DEBUG("Error creating the xml node\n"); + goto cleanup; + } + resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "version",BAD_CAST "1.12-linux"); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n"); + goto cleanup; + } + /* Start an element named "Machine" as child of VirtualBox. */ + resultXml = xmlTextWriterStartElement(writer, BAD_CAST "Machine"); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterStartElement\n"); + goto cleanup; + } + /* Add an attribute with name "uuid" and value "{uuid}" to Machine. */ + if (virAsprintf(&uuidWithBracket,"{%s}",domainUuid) != -1) { + resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "uuid",BAD_CAST uuidWithBracket); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n"); + goto cleanup; + } + VIR_FREE(uuidWithBracket); + } + + if (!machineName) { + VIR_DEBUG("No machine name"); + goto cleanup; + } + resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST machineName); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n"); + goto cleanup; + } + resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "OSType", BAD_CAST "Other"); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n"); + goto cleanup; + } + + if (!snapshotFolder) { + VIR_DEBUG("No machine snapshotFolder"); + goto cleanup; + } + resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "snapshotFolder", BAD_CAST snapshotFolder); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n"); + goto cleanup; + } + if (virAsprintf(&lastStateChangeStr, "%ld", lastStateChange) < 0) { + VIR_DEBUG("Could not convert lastStateChange from long to string"); + goto cleanup; + } + resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "lastStateChange", BAD_CAST lastStateChangeStr); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n"); + goto cleanup; + } + /* Current Snapshot + * https://www.virtualbox.org/sdkref/interface_i_machine.html#ac785dbe04eccc079... + * There is always a current snapshot, except when there is no snapshot (obvious) or when they have all been deleted (obvious) + * or when the current snapshot is deleted and it does not have a parent (not so obvious). + * This can happen only when the last snapshot is deleted because there can only be one snapshot with no parent, the first one. + * For the moment we always put the snapshot we are defining as current snapshot. When all the snapshots are redefined, you can always set + * the current snapshot to one you have previously saved. + */ + if (isCurrent) { + if (virAsprintf(&snapshotUuidWithBrackets, "{%s}", snapshotUuid) != -1) { + resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "currentSnapshot", BAD_CAST snapshotUuidWithBrackets); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n"); + goto cleanup; + } + VIR_FREE(snapshotUuidWithBrackets); + } + } + if (savedCurrentSnapshotUuid != NULL) { + if (virAsprintf(&snapshotUuidWithBrackets, "{%s}", savedCurrentSnapshotUuid) != -1) { + resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "currentSnapshot", BAD_CAST snapshotUuidWithBrackets); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n"); + goto cleanup; + } + VIR_FREE(snapshotUuidWithBrackets); + } + } + + /* Here we could close the elements Machine and VirtualBox using the + * function xmlTextWriterEndElement, but since we do not want to + * write any other elements, we simply call xmlTextWriterEndDocument, + * which will do all the work. */ + resultXml = xmlTextWriterEndDocument(writer); + if (resultXml < 0) { + VIR_DEBUG("Error at xmlTextWriterEndDocument\n"); + goto cleanup; + } + xmlFreeTextWriter(writer); + /* Now we have a file containing the skeleton for the xml we want to build + * It should look like this: + * <?xml version="1.0" encoding="ISO-8859-1"?> + * <!--comment + * --> + * <VirtualBox> + * <Machine uuid="{anUuid}" name="the name" OSType="Other" snapshotFolder="/path/to/SnapshotFolder" lastStateChange="a date"/> + * </VirtualBox> + */ + ret = buf; +cleanup: + return ret; +} + +static int +vboxSnapshotRedefine(virDomainPtr dom, + virDomainSnapshotDefPtr def, + bool isCurrent) +{ + /* + * If your snapshot has a parent, + * it will only be redefined if you have already + * redefined the parent. + * + */ + + VBOX_OBJECT_CHECK(dom->conn, int, 0); + vboxIID domiid = VBOX_IID_INITIALIZER; + IMachine *machine = NULL; + nsresult rc; + PRUnichar *settingsFilePath = NULL; + char *settingsFilePath_Utf8 = NULL; + size_t it = 0; + + IMachine *newMachine = NULL; + xmlDocPtr xml = NULL; + bool snapshotHasParents = !(def->parent == NULL); + + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + ret = -1; + goto cleanup; + } + + + rc = machine->vtbl->SaveSettings(machine); + rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePath); + reportInternalErrorIfNS_FAILED("cannot get settings file path"); + VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePath_Utf8); + xml = virXMLParse(settingsFilePath_Utf8, NULL, NULL); + if (xml) { + xmlNodePtr cur = NULL; + xmlNodePtr machineHardwareNode = NULL; + xmlNodePtr extraDataNode = NULL; + xmlNodePtr mediaRegistryNodeOriginal = NULL; + xmlNodePtr snapshotStorageControllersNode = NULL; + xmlNodePtr modifiedMachineStorageControllersNode = NULL; + char *newMachineStorageControllersString = NULL; + char *newSnapshotStorageControllersString = NULL; + char snapshotuuidstr[VIR_UUID_STRING_BUFLEN]; + unsigned char snapshotUuid[VIR_UUID_BUFLEN]; + + char *domainUuid = NULL; + PRUnichar *machineNameUtf16 = NULL; + char *machineName = NULL; + PRUnichar *snapshotFolderUtf16 = NULL; + PRInt64 lastStateChange = 0; + ISnapshot *savedCurrentSnapshot = NULL; + vboxIID savedCurrentSnapshotIid = VBOX_IID_INITIALIZER; + char *savedCurrentSnapshotUuid = NULL; + cur = xmlDocGetRootElement(xml); + xmlBufferPtr buf = xmlBufferCreate(); + if (cur == NULL) { + VIR_DEBUG("empty document\n"); + ret = -1; + goto cleanup; + } + /*Retrieve hardware*/ + vboxSnapshotXmlRetrieveRootNodeByName(cur, "Hardware", &machineHardwareNode); + if (!machineHardwareNode) { + VIR_DEBUG("Could not retrieve Hardware node"); + ret = -1; + goto cleanup; + } + /*Retrieve storageController*/ + buf = xmlBufferCreate(); + vboxSnapshotXmlRetrieveRootNodeByName(cur, "StorageControllers", &snapshotStorageControllersNode); + if (!snapshotStorageControllersNode) { + VIR_DEBUG("Could not retrieve StorageControllers node"); + ret = -1; + goto cleanup; + } + xmlNodeDump(buf, xml, snapshotStorageControllersNode, 0, 0); + ignore_value(VIR_STRDUP(newMachineStorageControllersString, (char *)xmlBufferContent(buf))); + ignore_value(VIR_STRDUP(newSnapshotStorageControllersString, newMachineStorageControllersString)); + xmlBufferFree(buf); + /*Retrieve ExtraData*/ + vboxSnapshotXmlRetrieveRootNodeByName(cur, "ExtraData", &extraDataNode); + if (!extraDataNode) { + VIR_DEBUG("Could not retrieve ExtraData node"); + ret = -1; + goto cleanup; + } + /*Retrieve MediaRegistry*/ + vboxSnapshotXmlRetrieveRootNodeByName(cur, "MediaRegistry", &mediaRegistryNodeOriginal); + if (!mediaRegistryNodeOriginal) { + VIR_DEBUG("Could not retrieve MediaRegistry node"); + ret = -1; + goto cleanup; + } + vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER; + rc = vboxArrayGet(&mediumAttachments, machine, machine->vtbl->GetMediumAttachments); + reportInternalErrorIfNS_FAILED("cannot get medium attachments"); + + /*Read only disks*/ + for (it = 0; it<def->dom->ndisks; ++it) { + IMedium *mediumReadOnly = NULL; + PRUnichar *locationReadOnlyDiskUtf16 = NULL; + PRUnichar *mediumReadOnlyFormatUtf16 = NULL; + PRUnichar *mediumReadOnlyIdUtf16 = NULL; + char *mediumReadOnlyFormat = NULL; + char *mediumReadOnlyId = NULL; + char *mediaRegistryNodeOriginalString = NULL; + VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src, &locationReadOnlyDiskUtf16); + data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationReadOnlyDiskUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &mediumReadOnly); + if (mediumReadOnly) { + rc = mediumReadOnly->vtbl->GetId(mediumReadOnly, &mediumReadOnlyIdUtf16); + reportInternalErrorIfNS_FAILED("cannot get medium id"); + rc = mediumReadOnly->vtbl->GetFormat(mediumReadOnly, &mediumReadOnlyFormatUtf16); + reportInternalErrorIfNS_FAILED("cannot get medium format"); + VBOX_UTF16_TO_UTF8(mediumReadOnlyIdUtf16, &mediumReadOnlyId); + VBOX_UTF16_TO_UTF8(mediumReadOnlyFormatUtf16, &mediumReadOnlyFormat); + char *uuidToReplace = NULL; + virSearchUuid(newSnapshotStorageControllersString, it+1,&uuidToReplace); + if (STRNEQ(uuidToReplace, mediumReadOnlyId)) { + char *tmp = NULL; + tmp = virStrReplace(newSnapshotStorageControllersString, uuidToReplace, mediumReadOnlyId); + VIR_FREE(newSnapshotStorageControllersString); + if (VIR_STRDUP(newSnapshotStorageControllersString, tmp) < 0) { + VIR_DEBUG("Error while using strdup"); + } + VIR_FREE(tmp); + } + IMedium *mediumReadOnlyParent = NULL; + rc = mediumReadOnly->vtbl->GetParent(mediumReadOnly, &mediumReadOnlyParent); + reportInternalErrorIfNS_FAILED("cannot get medium parent"); + if (mediumReadOnlyParent) { + PRUnichar *parentIdUtf16 = NULL; + char *parentId = NULL; + char *parentIdWithBrackets = NULL; + char *childIdWithBrackets = NULL; + rc = mediumReadOnlyParent->vtbl->GetId(mediumReadOnlyParent, &parentIdUtf16); + reportInternalErrorIfNS_FAILED("cannot get medium id"); + if (parentIdUtf16) { + VBOX_UTF16_TO_UTF8(parentIdUtf16, &parentId); + if (virAsprintf(&parentIdWithBrackets, "{%s}", parentId) != -1 && virAsprintf(&childIdWithBrackets, "{%s}", mediumReadOnlyId) != -1) { + buf = xmlBufferCreate(); + xmlNodeDump(buf, mediaRegistryNodeOriginal->doc, mediaRegistryNodeOriginal, 0, 0); + ignore_value(VIR_STRDUP(mediaRegistryNodeOriginalString, (char *)xmlBufferContent(buf))); + if (strstr(mediaRegistryNodeOriginalString, childIdWithBrackets) == NULL) { + vboxSnapshotXmlAppendDiskToMediaRegistry(&mediaRegistryNodeOriginal->children, parentIdWithBrackets, childIdWithBrackets, def->dom->disks[it]->src, mediumReadOnlyFormat); + } else { + VIR_DEBUG("Already added"); + } + VIR_FREE(parentIdWithBrackets); + VIR_FREE(childIdWithBrackets); + } + VBOX_UTF8_FREE(parentId); + } + VBOX_RELEASE(mediumReadOnlyParent); + VBOX_UTF16_FREE(parentIdUtf16); + } + VBOX_UTF8_FREE(mediumReadOnlyFormat); + VBOX_UTF8_FREE(mediumReadOnlyId); + VBOX_RELEASE(mediumReadOnly); + } + VBOX_UTF16_FREE(mediumReadOnlyIdUtf16); + VBOX_UTF16_FREE(mediumReadOnlyFormatUtf16); + VBOX_UTF16_FREE(locationReadOnlyDiskUtf16); + } + + /*Read Write disks*/ + for (it = 0; it< def->ndisks; ++it) { + IMedium *mediumReadWrite = NULL; + PRUnichar *mediumReadWriteFormatUtf16 = NULL; + PRUnichar *mediumReadWriteIdUtf16 = NULL; + PRUnichar *locationReadWriteDiskUtf16 = NULL; + char *mediumReadWriteFormat = NULL; + char *mediumReadWriteId = NULL; + char *mediaRegistryNodeOriginalString = NULL; + VBOX_UTF8_TO_UTF16(def->disks[it].file, &locationReadWriteDiskUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationReadWriteDiskUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &mediumReadWrite); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + "%s", + _("Could not open medium"),def->disks[it].file); + ret = -1; + goto cleanup; + } + if (mediumReadWrite) { + rc = mediumReadWrite->vtbl->GetId(mediumReadWrite, &mediumReadWriteIdUtf16); + reportInternalErrorIfNS_FAILED("cannot get medium id"); + rc = mediumReadWrite->vtbl->GetFormat(mediumReadWrite, &mediumReadWriteFormatUtf16); + reportInternalErrorIfNS_FAILED("cannot get medium format"); + VBOX_UTF16_TO_UTF8(mediumReadWriteIdUtf16, &mediumReadWriteId); + VBOX_UTF16_TO_UTF8(mediumReadWriteFormatUtf16, &mediumReadWriteFormat); + char *uuidToReplace = NULL; + virSearchUuid(newMachineStorageControllersString, it+1,&uuidToReplace); + if (STRNEQ(uuidToReplace, mediumReadWriteId)) { + char *tmp = NULL; + tmp = virStrReplace(newMachineStorageControllersString, uuidToReplace, mediumReadWriteId); + VIR_FREE(newMachineStorageControllersString); + if (VIR_STRDUP(newMachineStorageControllersString, tmp) < 0) { + VIR_DEBUG("Error while using strdup"); + } + VIR_FREE(tmp); + } + buf = xmlBufferCreate(); + xmlNodeDump(buf, mediaRegistryNodeOriginal->doc, mediaRegistryNodeOriginal, 0, 0); + ignore_value(VIR_STRDUP(mediaRegistryNodeOriginalString, (char *)xmlBufferContent(buf))); + xmlBufferFree(buf); + if (mediaRegistryNodeOriginalString && (strstr(mediaRegistryNodeOriginalString, mediumReadWriteId) == NULL)) { + IMedium *mediumReadWriteParent = NULL; + rc = mediumReadWrite->vtbl->GetParent(mediumReadWrite, &mediumReadWriteParent); + reportInternalErrorIfNS_FAILED("cannot get disk parent"); + if (mediumReadWriteParent) { + PRUnichar *parentIdUtf16 = NULL; + char *parentId = NULL; + char *parentIdWithBrackets = NULL; + char *childIdWithBrackets = NULL; + rc = mediumReadWriteParent->vtbl->GetId(mediumReadWriteParent, &parentIdUtf16); + reportInternalErrorIfNS_FAILED("cannot get disk id"); + if (parentIdUtf16) { + VBOX_UTF16_TO_UTF8(parentIdUtf16, &parentId); + if (virAsprintf(&parentIdWithBrackets, "{%s}", parentId) != -1 && virAsprintf(&childIdWithBrackets, "{%s}", mediumReadWriteId) != -1) { + vboxSnapshotXmlAppendDiskToMediaRegistry(&(mediaRegistryNodeOriginal->children), parentIdWithBrackets, childIdWithBrackets, def->disks[it].file, mediumReadWriteFormat); + VIR_FREE(parentIdWithBrackets); + VIR_FREE(childIdWithBrackets); + } + VBOX_UTF8_FREE(parentId); + } + VBOX_RELEASE(mediumReadWriteParent); + VBOX_UTF16_FREE(parentIdUtf16); + } + } + VBOX_UTF8_FREE(mediumReadWriteFormat); + VBOX_UTF8_FREE(mediumReadWriteId); + VBOX_RELEASE(mediumReadWrite); + } + VBOX_UTF16_FREE(mediumReadWriteIdUtf16); + VBOX_UTF16_FREE(mediumReadWriteFormatUtf16); + VBOX_UTF16_FREE(locationReadWriteDiskUtf16); + } + vboxArrayRelease(&mediumAttachments); + + /*Generation of the skeleton*/ + + VBOX_UTF16_TO_UTF8((PRUnichar *)domiid.value, &domainUuid); + rc = machine->vtbl->GetName(machine, &machineNameUtf16); + reportInternalErrorIfNS_FAILED("cannot get machine name"); + rc = machine->vtbl->GetSnapshotFolder(machine, &snapshotFolderUtf16); + reportInternalErrorIfNS_FAILED("cannot get snapshot folder"); + char *snapshotFolder = NULL; + VBOX_UTF16_TO_UTF8(snapshotFolderUtf16, &snapshotFolder); + VBOX_UTF16_FREE(snapshotFolderUtf16); + VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName); + VBOX_UTF16_FREE(machineNameUtf16); + if (snapshotUuid == NULL) { + VIR_DEBUG("Bad initialization"); + } + if (virUUIDGenerate(snapshotUuid) == -1) { + VIR_DEBUG("Could not generate a snapshot uuid"); + } else { + virUUIDFormat(snapshotUuid, snapshotuuidstr); + VIR_DEBUG("UUID: %s", snapshotuuidstr); + } + rc = machine->vtbl->GetCurrentSnapshot(machine, &savedCurrentSnapshot); + reportInternalErrorIfNS_FAILED("cannot get current snapshot"); + if (savedCurrentSnapshot) { + savedCurrentSnapshot->vtbl->GetId(savedCurrentSnapshot, &savedCurrentSnapshotIid.value); + VBOX_UTF16_TO_UTF8(savedCurrentSnapshotIid.value, &savedCurrentSnapshotUuid); + vboxIIDUnalloc(&savedCurrentSnapshotIid); + VBOX_RELEASE(savedCurrentSnapshot); + } + rc = machine ->vtbl->GetLastStateChange(machine, &lastStateChange); + reportInternalErrorIfNS_FAILED("cannot get last state change"); + xmlBufferPtr skeletonBuf = vboxSnapshotGenerateXmlSkeleton(domainUuid, machineName, snapshotuuidstr, snapshotFolder, lastStateChange, isCurrent, savedCurrentSnapshotUuid); + /* Now we can reopen our file in tree mode and append node we want. */ + xmlDocPtr newXml = virXMLParse(NULL, (char *)skeletonBuf->content, NULL); + if (newXml) { + xmlNodePtr newXmlRootElement = xmlDocGetRootElement(newXml); + xmlNodePtr newXmlMachineNode = NULL; + xmlNodePtr snapshotNodeToAppend = NULL; + IMedium *array[] = { NULL }; + IProgress *progress = NULL; + vboxArray media = VBOX_ARRAY_INITIALIZER; + + vboxSnapshotXmlRetrieveMachineNode(newXmlRootElement, &newXmlMachineNode); + if (newXmlMachineNode) { + if (mediaRegistryNodeOriginal) { + vboxSnapshotXmlAddChild(newXmlMachineNode, mediaRegistryNodeOriginal); + } + if (extraDataNode) { + vboxSnapshotXmlAddChild(newXmlMachineNode, extraDataNode); + } + } + if (!snapshotHasParents) { + vboxSnapshotGenerateVboxXML(xmlDocGetRootElement(xml), newSnapshotStorageControllersString , NULL,def->name,virTimeStringThen(def->creationTime*1000),snapshotuuidstr, &snapshotNodeToAppend); + } else { + vboxSnapshotGenerateVboxXML(xmlDocGetRootElement(xml), newSnapshotStorageControllersString , def->parent,def->name,virTimeStringThen(def->creationTime*1000),snapshotuuidstr, &snapshotNodeToAppend); + } + if (snapshotNodeToAppend) { + if (xmlAddChild(newXmlMachineNode, xmlCopyNode(snapshotNodeToAppend, 1)) == NULL) { + VIR_DEBUG("Error while building the xml"); + } + } + if (newXmlMachineNode) { + if (machineHardwareNode) { + vboxSnapshotXmlAddChild(newXmlMachineNode, machineHardwareNode); + } + xmlParseInNodeContext(newXmlMachineNode, newMachineStorageControllersString, (int)strlen(newMachineStorageControllersString), 0, &modifiedMachineStorageControllersNode); + if (modifiedMachineStorageControllersNode) { + if (xmlAddChild(newXmlMachineNode, xmlCopyNode(modifiedMachineStorageControllersNode, 1)) == NULL) { + VIR_DEBUG("Could not add modifiedSnapshotStorageControllersNode node to snapshot node"); + } + } + } + xmlNewProp(newXmlRootElement, BAD_CAST "xmlns", BAD_CAST "http://www.innotek.de/VirtualBox-settings"); + rc = vboxArrayGetWithUintArg(&media, machine, machine->vtbl->Unregister, CleanupMode_DetachAllReturnHardDisksOnly); + for (it = 0; it < media.count; ++it) { + IMedium *disk = media.items[it]; + if (disk) + vboxDetachAndCloseDisks(dom, disk); + } + rc = machine->vtbl->Delete(machine, 0, array, &progress); + reportInternalErrorIfNS_FAILED("cannot delete machine"); + if (progress != NULL) { + progress->vtbl->WaitForCompletion(progress, -1); + VBOX_RELEASE(progress); + } + vboxArrayRelease(&media); + if (virFileMakePath(mdir_name(settingsFilePath_Utf8)) < 0) + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + " %s", + _("Cannot create path :"), + settingsFilePath_Utf8); + if (xmlSaveFormatFileEnc(settingsFilePath_Utf8, newXml, "ISO-8859-1", 1) <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + " %s", + _("Cannot save xml file to:"), + settingsFilePath_Utf8); + } + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine); + unsigned int failedCounter = 10; + while (NS_FAILED(rc) && (rc == 0x80004005 /*NSCOM standard error*/) && failedCounter != 0) { + /*There is some random fails happening and they are not the error + *supposed to happen. + *I didn't have some solution on #vbox-dev + *but it appears that with another try it works so that's why we are trying 10 times maximum + */ + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine); + failedCounter--; + } + reportInternalErrorIfNS_FAILED("cannot open machine"); + if (newMachine) { + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, newMachine); + reportInternalErrorIfNS_FAILED("cannot register machine"); + } + else { + ret = -1; + goto cleanup; + } + } + } + ret = 0; +cleanup: + xmlFreeDoc(xml); + return ret; +} +#endif static virDomainSnapshotPtr vboxDomainSnapshotCreateXML(virDomainPtr dom, const char *xmlDesc, @@ -5917,20 +6743,20 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, #else PRInt32 result; #endif +#if VBOX_API_VERSION >= 4002 + bool isCurrent = false; +#endif /* VBox has no snapshot metadata, so this flag is trivial. */ - virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL); + virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA | + VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE | + VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT, NULL); if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps, data->xmlopt, -1, VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE))) goto cleanup; - if (def->ndisks) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disk snapshots not supported yet")); - goto cleanup; - } vboxIIDFromUUID(&domiid, dom->uuid); rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); if (NS_FAILED(rc)) { @@ -5938,6 +6764,14 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, _("no domain with matching UUID")); goto cleanup; } +#if VBOX_API_VERSION >= 4002 + isCurrent = flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT; + if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) { + vboxSnapshotRedefine(dom, def, isCurrent); + ret = virGetDomainSnapshot(dom, def->name); + goto cleanup; + } +#endif rc = machine->vtbl->GetState(machine, &state); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -7189,8 +8023,6 @@ cleanup: return ret; } - - static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) -- 1.7.10.4

All the informations concerning snapshots (and snapshot disks) will be deleted from the vbox xml. But the differencing disks will be kept so you will be able to redefine the snapshots. --- src/vbox/vbox_tmpl.c | 387 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 2b07dec..4bc1054 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -6082,6 +6082,68 @@ vboxSnapshotXmlAppendDiskToMediaRegistry(xmlNodePtr *inMediaRegistry, } } + +static void +vboxRemoveAllDisksExceptParentFromMediaRegistry(xmlNodePtr mediaRegistryNode){ + xmlNodePtr cur_node = NULL; + for (cur_node = mediaRegistryNode; cur_node; cur_node = cur_node->next) { + if (cur_node) { + if (cur_node->type == XML_ELEMENT_NODE + && !xmlStrcmp(cur_node->name, (const xmlChar *) "HardDisk")) { + xmlNodePtr child = NULL; + for (child = cur_node->children; child; child = child->next) { + /*We look over all the children + *If there is a node element, we delete it + */ + if (child->type == XML_ELEMENT_NODE) { + xmlUnlinkNode(child); + xmlFreeNode(child); + } + } + } + } + if ((cur_node->children)) + vboxRemoveAllDisksExceptParentFromMediaRegistry((cur_node->children)); + } +} + +static void +vboxRemoveDiskFromMediaRegistryIfNoChildren(xmlNodePtr mediaRegistryNode, + char *diskLocation) +{ + /* + *This function will remove a disk from the media registry only if it doesn't + *have any children + */ + xmlNodePtr cur_node = NULL; + for (cur_node = mediaRegistryNode; cur_node; cur_node = cur_node->next) { + if (cur_node) { + if (cur_node->type == XML_ELEMENT_NODE + && !xmlStrcmp(cur_node->name, (const xmlChar *) "HardDisk") + && xmlHasProp(cur_node, BAD_CAST "location") != NULL + && strstr(diskLocation, (char *)xmlHasProp(cur_node, BAD_CAST "location")->children->content) != NULL) { + + xmlNodePtr child = NULL; + bool deleteNode = true; + for (child = cur_node->children; child; child = child->next) { + /*We look over all the children + *If there is a node element, we don't delete it + */ + if (child->type == XML_ELEMENT_NODE) + deleteNode = false; + } + if (deleteNode) { + xmlUnlinkNode(cur_node); + xmlFreeNode(cur_node); + } + return; + } + } + if ((cur_node->children)) + vboxRemoveDiskFromMediaRegistryIfNoChildren((cur_node->children), diskLocation); + } +} + static int vboxSnapshotGenerateVboxXML(xmlNodePtr rootElementVboxXML, char *storageControllerString, @@ -8022,7 +8084,314 @@ cleanup: vboxArrayRelease(&children); return ret; } +#if VBOX_API_VERSION >= 4002 +static int +vboxCloseDisk(virDomainPtr dom, + IMedium *baseDisk) { + VBOX_OBJECT_CHECK(dom->conn, int, -1); + nsresult rc; + vboxArray childrenDiskArray = VBOX_ARRAY_INITIALIZER; + size_t i = 0; + if (!baseDisk) + return -1; + + rc = vboxArrayGet(&childrenDiskArray, baseDisk, baseDisk->vtbl->GetChildren); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get children disks")); + ret = -1; + goto cleanup; + } + for (i=0; i < childrenDiskArray.count; ++i) + vboxCloseDisk(dom, childrenDiskArray.items[i]); + + baseDisk->vtbl->Close(baseDisk); + ret = 0; +cleanup: + vboxArrayRelease(&childrenDiskArray); + return ret; +} + +static int +vboxDomainSnapshotDeleteMetadataOnly(virDomainSnapshotPtr snapshot) +{ + /*This function will remove the node in the vbox xml corresponding to + *the snapshot. It is usually called by vboxDomainSnapshotDelete() with + *the flag VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY. + *If you want to use it anywhere else, be careful, if the snapshot you want to delete has children, + *the result is not granted, they will probably will be deleted in the xml, but you may have + *a problem with hard drives + * + *If the snapshot which is being deleted is the current, we will set the current snapshot of the machine to + *its parent. + * + *Before the writing of the modified xml file, we undefine the machine from vbox + *After the modification, we redefine the machine + */ + + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + IMachine *machine = NULL; + IMachine *newMachine = NULL; + nsresult rc; + ISnapshot *snap = NULL; + vboxIID domiid = VBOX_IID_INITIALIZER; + vboxArray machineDisks = VBOX_ARRAY_INITIALIZER; + IMedium *dummyArray[] = { NULL }; + PRUnichar *settingsFilePath = NULL; + char *settingsFilePathUtf8 = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr xmlRootElement = NULL; + xmlNodePtr snapshotToDelete = NULL; + IProgress *progress = NULL; + vboxIID snapIid = VBOX_IID_INITIALIZER; + char *snapshotUuidStr = NULL; + char *snapshotUuidStrWithBrackets = NULL; + ISnapshot *parentSnapshot = NULL; + vboxIID parentIId = VBOX_IID_INITIALIZER; + char *parentSnapshotUuidStr = NULL; + char *parentSnapshotUuidStrWithBrackets = NULL; + virDomainSnapshotDefPtr def = NULL; + char *snapXmlDesc = NULL; + xmlNodePtr mediaRegistryNode = NULL; + xmlNodePtr storageControllersNode = NULL; + xmlBufferPtr buf = xmlBufferCreate(); + xmlNodePtr newStorageControllersNode = NULL; + xmlNodePtr machineNode = NULL; + bool snapshotHasParent = false; + char *snapshotStorageControllersString = NULL; + size_t i = 0; + unsigned int numberOfDisks = 0; + unsigned int failedCounter = 10; + + + VIR_DEBUG("vboxDomainSnapshotDeleteMetadataOnly()"); + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + /*Get snapshot*/ + snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name); + if (!snap) + goto cleanup; + rc = snap->vtbl->GetId(snap, &snapIid.value); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get snapshot uuid")); + ret = -1; + goto cleanup; + } + snapXmlDesc = vboxDomainSnapshotGetXMLDesc(snapshot, 0); + if (!(def = virDomainSnapshotDefParseString(snapXmlDesc, data->caps, + data->xmlopt, -1, VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE))) + goto cleanup; + snapshotHasParent = !(def->parent == NULL); + /*Get its parent because it's possible that we have to put the parent uuid in + *the current snapshot attribute*/ + rc = snap->vtbl->GetParent(snap, &parentSnapshot); + reportInternalErrorIfNS_FAILED("cannot get parent snapshot"); + if (parentSnapshot) { + rc = parentSnapshot->vtbl->GetId(parentSnapshot, &parentIId.value); + reportInternalErrorIfNS_FAILED("cannot get parent snapshot id"); + VBOX_UTF16_TO_UTF8(parentIId.value, &parentSnapshotUuidStr); + if (virAsprintf(&parentSnapshotUuidStrWithBrackets, "{%s}", parentSnapshotUuidStr) < 0) { + VIR_DEBUG("problem while using virasprintf"); + goto cleanup; + } + } + + rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePath); + reportInternalErrorIfNS_FAILED("cannot get settings file path"); + + /*We ask libxml2 to parse the xmlfile*/ + VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePathUtf8); + xml = virXMLParse(settingsFilePathUtf8, NULL, NULL); + xmlKeepBlanksDefault(1); + if (!xml) { + VIR_DEBUG("Cannot parse settings file"); + ret = -1; + goto cleanup; + } + + xmlRootElement = xmlDocGetRootElement(xml); + if (!xmlRootElement) { + VIR_DEBUG("Cannot find any root element in the xml"); + ret = -1; + goto cleanup; + } + + vboxSnapshotXmlRetrieveSnapshotNodeByName(xmlRootElement->children, snapshot->name, &snapshotToDelete); + if (!snapshotToDelete) { + VIR_DEBUG("Cannot find any snapshot to delete"); + ret = -1; + goto cleanup; + } + /*We manage the case where the snapshot we want to delete is the current one */ + VBOX_UTF16_TO_UTF8(snapIid.value, &snapshotUuidStr); + if (virAsprintf(&snapshotUuidStrWithBrackets, "{%s}", snapshotUuidStr) < 0) { + VIR_DEBUG("problem while using virasprintf"); + ret = -1; + goto cleanup; + } + vboxSnapshotXmlRetrieveMachineNode(xmlRootElement, &machineNode); + /*we change the current snapshot if necessary*/ + if (STREQ(virXMLPropString(machineNode, "currentSnapshot"), snapshotUuidStrWithBrackets)) { + if (parentSnapshotUuidStrWithBrackets != NULL) { + xmlSetProp(machineNode, BAD_CAST "currentSnapshot", BAD_CAST parentSnapshotUuidStrWithBrackets); + } else { + xmlUnsetProp(machineNode, BAD_CAST "currentSnapshot"); + } + } + vboxSnapshotXmlRetrieveRootNodeByName(xmlRootElement, "MediaRegistry", &mediaRegistryNode); + if (!mediaRegistryNode) { + VIR_DEBUG("Problem while retrieving MediaRegistry node"); + ret = -1; + goto cleanup; + } + vboxSnapshotXmlRetrieveRootNodeByName(xmlRootElement, "StorageControllers", &storageControllersNode); + if (!storageControllersNode) { + VIR_DEBUG("Problem while retrieving StorageControllers node"); + ret = -1; + goto cleanup; + } + xmlNodeDump(buf, xml, storageControllersNode, 0, 0); + if (VIR_STRDUP(snapshotStorageControllersString, (char *)xmlBufferContent(buf)) < 0) { + VIR_DEBUG("Could not duplicate %s", (char *)xmlBufferContent(buf)); + } + xmlBufferFree(buf); + if (snapshotHasParent) + numberOfDisks = def->dom->ndisks; + else + numberOfDisks = def->ndisks; + + for (i = 0; i < numberOfDisks; ++i) { + IMedium *diskSnapshot = NULL; + IMedium *baseDiskSnapshot = NULL; + PRUnichar *locationDiskSnapshotUtf16 = NULL; + PRUnichar *baseDiskIdUtf16 = NULL; + char *baseDiskId = NULL; + char *diskPath = NULL; + char *uuidToReplace = NULL; + if (snapshotHasParent) + diskPath = def->dom->disks[i]->src; + else + diskPath = def->disks[i].file; + VBOX_UTF8_TO_UTF16(diskPath, &locationDiskSnapshotUtf16); + + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationDiskSnapshotUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &diskSnapshot); + reportInternalErrorIfNS_FAILED("cannot open medium"); + if (!diskSnapshot) { + VIR_DEBUG("Could not open %s", def->dom->disks[i]->src); + ret = -1; + goto cleanup; + } + rc = diskSnapshot->vtbl->GetBase(diskSnapshot, &baseDiskSnapshot); + reportInternalErrorIfNS_FAILED("cannot get base disk"); + if (!baseDiskSnapshot) { + VIR_DEBUG("Could not get base medium"); + ret = -1; + goto cleanup; + } + rc = baseDiskSnapshot->vtbl->GetId(baseDiskSnapshot, &baseDiskIdUtf16); + reportInternalErrorIfNS_FAILED("cannot get base disk id"); + VBOX_UTF16_TO_UTF8(baseDiskIdUtf16, &baseDiskId); + VIR_DEBUG("Removing disk %s from media registry", diskPath); + if (snapshotHasParent) { + vboxRemoveDiskFromMediaRegistryIfNoChildren(mediaRegistryNode->children, diskPath); + } else { + vboxRemoveAllDisksExceptParentFromMediaRegistry(mediaRegistryNode->children); + virSearchUuid(snapshotStorageControllersString, i+1,&uuidToReplace); + char *tmp = NULL; + tmp = virStrReplace(snapshotStorageControllersString, uuidToReplace, baseDiskId); + VIR_FREE(snapshotStorageControllersString); + if (VIR_STRDUP(snapshotStorageControllersString, tmp) < 0) { + VIR_DEBUG("Error while using strdup"); + } + VIR_FREE(tmp); + xmlParseInNodeContext(machineNode, snapshotStorageControllersString, (int)strlen(snapshotStorageControllersString), 0, &newStorageControllersNode); + } + VBOX_UTF16_FREE(locationDiskSnapshotUtf16); + VBOX_UTF16_FREE(baseDiskIdUtf16); + } + /*we remove the machine from vbox*/ + rc = vboxArrayGetWithUintArg(&machineDisks, machine, machine->vtbl->Unregister, CleanupMode_DetachAllReturnNone); + reportInternalErrorIfNS_FAILED("cannot unregister machine"); + rc = machine->vtbl->Delete(machine, 0, dummyArray, &progress); + reportInternalErrorIfNS_FAILED("cannot delete machine"); + if (progress != NULL) { + progress->vtbl->WaitForCompletion(progress, -1); + VBOX_RELEASE(progress); + } + + for (i = 0; i < numberOfDisks; ++i) { + IMedium *diskSnapshot= NULL; + PRUnichar *locationDiskSnapshotUtf16 = NULL; + IMedium *baseDiskSnapshot = NULL; + + VBOX_UTF8_TO_UTF16(def->dom->disks[i]->src, &locationDiskSnapshotUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationDiskSnapshotUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &diskSnapshot); + reportInternalErrorIfNS_FAILED("cannot open medium"); + rc = diskSnapshot->vtbl->GetBase(diskSnapshot, &baseDiskSnapshot); + reportInternalErrorIfNS_FAILED("cannot get disk base"); + vboxCloseDisk(dom, baseDiskSnapshot); + } + + /*we delete the xml part corresponding to the snapshot*/ + xmlUnlinkNode(snapshotToDelete); + xmlFreeNode(snapshotToDelete); + + /*We delete the part corresponding to the storagecontrollers if we need to*/ + if (numberOfDisks > 0 && !snapshotHasParent) { + xmlUnlinkNode(storageControllersNode); + xmlFreeNode(storageControllersNode); + xmlAddChild(machineNode, newStorageControllersNode); + } + + /*we save the modified xml*/ + if (virFileMakePath(mdir_name(settingsFilePathUtf8)) < 0) + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + " %s", + _("Cannot create path :"), + settingsFilePathUtf8); + if (xmlSaveFormatFileEnc(settingsFilePathUtf8, xml, "ISO-8859-1", 1) <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + " %s", + _("Cannot save xml file to:"), + settingsFilePathUtf8); + } + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine); + while (NS_FAILED(rc) && (rc == 0x80004005 /*NSCOM standard error*/) && failedCounter != 0) { + /*There is some random fails happening and they are not the error + *supposed to happen. + *I didn't have some solution on #vbox-dev + *but it appears that with another try it works so that's why we are trying 10 times maximum + * + */ + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine); + failedCounter--; + } + reportInternalErrorIfNS_FAILED("cannot open machine"); + if (newMachine) { + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, newMachine); + reportInternalErrorIfNS_FAILED("cannot register machine"); + } + else { + ret = -1; + goto cleanup; + } + + ret = 0; +cleanup: + vboxArrayRelease(&machineDisks); + VBOX_UTF16_FREE(settingsFilePath); + return ret; +} +#endif static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -8035,6 +8404,7 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, IConsole *console = NULL; PRUint32 state; nsresult rc; + vboxArray snapChildren = VBOX_ARRAY_INITIALIZER; virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN | VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY, -1); @@ -8062,6 +8432,23 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, *to remove the node concerning the snapshot */ if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) { + rc = vboxArrayGet(&snapChildren, snap, snap->vtbl->GetChildren); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get snapshot children")); + ret = -1; + goto cleanup; + } + if (snapChildren.count != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot delete metadata of a snapshot with children")); + ret = -1; + goto cleanup; + } else { +#if VBOX_API_VERSION >= 4002 + vboxDomainSnapshotDeleteMetadataOnly(snapshot); +#endif + } ret = 0; goto cleanup; } -- 1.7.10.4
participants (3)
-
Guido Günther
-
Ján Tomko
-
Manuel VIVES