diff --git a/bootstrap.conf b/bootstrap.conf index ac2f8e69d3..ca9332da1d 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -52,9 +52,11 @@ stpcpy strchrnul strndup strerror +strptime strsep sys_stat time_r +timegm useless-if-before-free vasprintf verify diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index aed4a626a1..4ed98909a6 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -563,6 +563,7 @@ esxClose(virConnectPtr conn) esxVI_Logout(priv->host) < 0) { result = -1; } + esxVI_Context_Free(&priv->host); if (priv->vCenter != NULL) { @@ -570,6 +571,7 @@ esxClose(virConnectPtr conn) esxVI_Logout(priv->vCenter) < 0) { result = -1; } + esxVI_Context_Free(&priv->vCenter); } @@ -1742,23 +1744,8 @@ esxDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) goto failure; } - switch (powerState) { - case esxVI_VirtualMachinePowerState_PoweredOff: - info->state = VIR_DOMAIN_SHUTOFF; - break; - - case esxVI_VirtualMachinePowerState_PoweredOn: - info->state = VIR_DOMAIN_RUNNING; - break; - - case esxVI_VirtualMachinePowerState_Suspended: - info->state = VIR_DOMAIN_PAUSED; - break; - - default: - info->state = VIR_DOMAIN_NOSTATE; - break; - } + info->state = esxVI_VirtualMachinePowerState_ConvertToLibvirt + (powerState); } else if (STREQ(dynamicProperty->name, "config.hardware.memoryMB")) { if (esxVI_AnyType_ExpectType(dynamicProperty->val, esxVI_Type_Int) < 0) { @@ -2329,7 +2316,6 @@ esxListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) count = -1; goto cleanup; - } @@ -3308,6 +3294,425 @@ esxDomainIsPersistent(virDomainPtr domain ATTRIBUTE_UNUSED) +static virDomainSnapshotPtr +esxDomainSnapshotCreateXML(virDomainPtr domain, const char *xmlDesc, + unsigned int flags ATTRIBUTE_UNUSED) +{ + esxPrivate *priv = domain->conn->privateData; + virDomainSnapshotDefPtr def = NULL; + esxVI_ObjectContent *virtualMachine = NULL; + esxVI_VirtualMachineSnapshotTree *rootSnapshotList = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL; + esxVI_ManagedObjectReference *task = NULL; + esxVI_TaskInfoState taskInfoState; + virDomainSnapshotPtr snapshot = NULL; + + if (esxVI_EnsureSession(priv->host) < 0) { + goto failure; + } + + def = virDomainSnapshotDefParseString(xmlDesc, 1); + + if (def == NULL) { + goto failure; + } + + if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask + (priv->host, domain->uuid, NULL, &virtualMachine, + priv->autoAnswer) < 0 || + esxVI_LookupRootSnapshotTreeList(priv->host, domain->uuid, + &rootSnapshotList) < 0 || + esxVI_GetSnapshotTreeByName(rootSnapshotList, def->name, + &snapshotTree, &snapshotTreeParent, + esxVI_Occurrence_OptionalItem) < 0) { + goto failure; + } + + if (snapshotTree != NULL) { + ESX_ERROR(VIR_ERR_OPERATION_INVALID, + _("Snapshot '%s' already exists"), def->name); + goto failure; + } + + if (esxVI_CreateSnapshot_Task(priv->host, virtualMachine->obj, + def->name, def->description, + esxVI_Boolean_True, + esxVI_Boolean_False, &task) < 0 || + esxVI_WaitForTaskCompletion(priv->host, task, domain->uuid, + priv->autoAnswer, &taskInfoState) < 0) { + goto failure; + } + + if (taskInfoState != esxVI_TaskInfoState_Success) { + ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not create snapshot")); + goto failure; + } + + snapshot = virGetDomainSnapshot(domain, def->name); + + cleanup: + virDomainSnapshotDefFree(def); + esxVI_ObjectContent_Free(&virtualMachine); + esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList); + esxVI_ManagedObjectReference_Free(&task); + + return snapshot; + + failure: + domain = NULL; + + goto cleanup; +} + + + +static char * +esxDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot, + unsigned int flags ATTRIBUTE_UNUSED) +{ + esxPrivate *priv = snapshot->domain->conn->privateData; + esxVI_VirtualMachineSnapshotTree *rootSnapshotList = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL; + virDomainSnapshotDef def; + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + char *xml = NULL; + + memset(&def, 0, sizeof (virDomainSnapshotDef)); + + if (esxVI_EnsureSession(priv->host) < 0) { + goto failure; + } + + if (esxVI_LookupRootSnapshotTreeList(priv->host, snapshot->domain->uuid, + &rootSnapshotList) < 0 || + esxVI_GetSnapshotTreeByName(rootSnapshotList, snapshot->name, + &snapshotTree, &snapshotTreeParent, + esxVI_Occurrence_RequiredItem) < 0) { + goto failure; + } + + def.name = snapshot->name; + def.description = snapshotTree->description; + def.parent = snapshotTreeParent != NULL ? snapshotTreeParent->name : NULL; + + if (esxVI_DateTime_ConvertToCalendarTime(snapshotTree->createTime, + &def.creationTime) < 0) { + goto failure; + } + + def.state = esxVI_VirtualMachinePowerState_ConvertToLibvirt + (snapshotTree->state); + + virUUIDFormat(snapshot->domain->uuid, uuid_string); + + xml = virDomainSnapshotDefFormat(uuid_string, &def, 0); + + cleanup: + esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList); + + return xml; + + failure: + VIR_FREE(xml); + + goto cleanup; +} + + + +static int +esxDomainSnapshotNum(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED) +{ + int result = 0; + esxPrivate *priv = domain->conn->privateData; + esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL; + + if (esxVI_EnsureSession(priv->host) < 0) { + goto failure; + } + + if (esxVI_LookupRootSnapshotTreeList(priv->host, domain->uuid, + &rootSnapshotTreeList) < 0) { + goto failure; + } + + result = esxVI_GetNumberOfSnapshotTrees(rootSnapshotTreeList); + + cleanup: + esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList); + + return result; + + failure: + result = -1; + + goto cleanup; +} + + + +static int +esxDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen, + unsigned int flags ATTRIBUTE_UNUSED) +{ + int result = 0; + esxPrivate *priv = domain->conn->privateData; + esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL; + + if (names == NULL || nameslen < 0) { + ESX_ERROR(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument")); + return -1; + } + + if (nameslen == 0) { + return 0; + } + + if (esxVI_EnsureSession(priv->host) < 0) { + goto failure; + } + + if (esxVI_LookupRootSnapshotTreeList(priv->host, domain->uuid, + &rootSnapshotTreeList) < 0) { + goto failure; + } + + result = esxVI_GetSnapshotTreeNames(rootSnapshotTreeList, names, nameslen); + + cleanup: + esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList); + + return result; + + failure: + result = -1; + + goto cleanup; +} + + + +static virDomainSnapshotPtr +esxDomainSnapshotLookupByName(virDomainPtr domain, const char *name, + unsigned int flags ATTRIBUTE_UNUSED) +{ + esxPrivate *priv = domain->conn->privateData; + esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL; + virDomainSnapshotPtr snapshot = NULL; + + if (esxVI_EnsureSession(priv->host) < 0) { + goto cleanup; + } + + if (esxVI_LookupRootSnapshotTreeList(priv->host, domain->uuid, + &rootSnapshotTreeList) < 0 || + esxVI_GetSnapshotTreeByName(rootSnapshotTreeList, name, &snapshotTree, + &snapshotTreeParent, + esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + snapshot = virGetDomainSnapshot(domain, name); + + cleanup: + esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList); + + return snapshot; +} + + + +static int +esxDomainHasCurrentSnapshot(virDomainPtr domain, unsigned int flags) +{ + int result = 0; + esxPrivate *priv = domain->conn->privateData; + esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL; + + if (flags != 0) { + ESX_ERROR(VIR_ERR_INVALID_ARG, + _("Unsupported flags (0x%x) passed to %s"), + flags, __FUNCTION__); + return -1; + } + + if (esxVI_EnsureSession(priv->host) < 0) { + goto failure; + } + + if (esxVI_LookupCurrentSnapshotTree(priv->host, domain->uuid, + ¤tSnapshotTree, + esxVI_Occurrence_OptionalItem) < 0) { + goto failure; + } + + if (currentSnapshotTree != NULL) { + result = 1; + } + + cleanup: + esxVI_VirtualMachineSnapshotTree_Free(¤tSnapshotTree); + + return result; + + failure: + result = -1; + + goto cleanup; +} + + + +static virDomainSnapshotPtr +esxDomainSnapshotCurrent(virDomainPtr domain, unsigned int flags) +{ + virDomainSnapshotPtr snapshot = NULL; + esxPrivate *priv = domain->conn->privateData; + esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL; + + if (flags != 0) { + ESX_ERROR(VIR_ERR_INVALID_ARG, + _("Unsupported flags (0x%x) passed to %s"), + flags, __FUNCTION__); + return NULL; + } + + if (esxVI_EnsureSession(priv->host) < 0) { + goto cleanup; + } + + if (esxVI_LookupCurrentSnapshotTree(priv->host, domain->uuid, + ¤tSnapshotTree, + esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + snapshot = virGetDomainSnapshot(domain, currentSnapshotTree->name); + + cleanup: + esxVI_VirtualMachineSnapshotTree_Free(¤tSnapshotTree); + + return snapshot; +} + + + +static int +esxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, unsigned int flags) +{ + int result = 0; + esxPrivate *priv = snapshot->domain->conn->privateData; + esxVI_VirtualMachineSnapshotTree *rootSnapshotList = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL; + esxVI_ManagedObjectReference *task = NULL; + esxVI_TaskInfoState taskInfoState; + + if (flags != 0) { + ESX_ERROR(VIR_ERR_INVALID_ARG, + _("Unsupported flags (0x%x) passed to %s"), + flags, __FUNCTION__); + return -1; + } + + if (esxVI_EnsureSession(priv->host) < 0) { + goto failure; + } + + if (esxVI_LookupRootSnapshotTreeList(priv->host, snapshot->domain->uuid, + &rootSnapshotList) < 0 || + esxVI_GetSnapshotTreeByName(rootSnapshotList, snapshot->name, + &snapshotTree, &snapshotTreeParent, + esxVI_Occurrence_RequiredItem) < 0) { + goto failure; + } + + if (esxVI_RevertToSnapshot_Task(priv->host, snapshotTree->snapshot, NULL, + &task) < 0 || + esxVI_WaitForTaskCompletion(priv->host, task, snapshot->domain->uuid, + priv->autoAnswer, &taskInfoState) < 0) { + goto failure; + } + + if (taskInfoState != esxVI_TaskInfoState_Success) { + ESX_ERROR(VIR_ERR_INTERNAL_ERROR, + _("Could not revert to snapshot '%s'"), snapshot->name); + goto failure; + } + + cleanup: + esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList); + esxVI_ManagedObjectReference_Free(&task); + + return result; + + failure: + result = -1; + + goto cleanup; +} + + + +static int +esxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) +{ + int result = 0; + esxPrivate *priv = snapshot->domain->conn->privateData; + esxVI_VirtualMachineSnapshotTree *rootSnapshotList = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL; + esxVI_Boolean removeChildren = esxVI_Boolean_False; + esxVI_ManagedObjectReference *task = NULL; + esxVI_TaskInfoState taskInfoState; + + if (esxVI_EnsureSession(priv->host) < 0) { + goto failure; + } + + if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN) { + removeChildren = esxVI_Boolean_True; + } + + if (esxVI_LookupRootSnapshotTreeList(priv->host, snapshot->domain->uuid, + &rootSnapshotList) < 0 || + esxVI_GetSnapshotTreeByName(rootSnapshotList, snapshot->name, + &snapshotTree, &snapshotTreeParent, + esxVI_Occurrence_RequiredItem) < 0) { + goto failure; + } + + if (esxVI_RemoveSnapshot_Task(priv->host, snapshotTree->snapshot, + removeChildren, &task) < 0 || + esxVI_WaitForTaskCompletion(priv->host, task, snapshot->domain->uuid, + priv->autoAnswer, &taskInfoState) < 0) { + goto failure; + } + + if (taskInfoState != esxVI_TaskInfoState_Success) { + ESX_ERROR(VIR_ERR_INTERNAL_ERROR, + _("Could not delete snapshot '%s'"), snapshot->name); + goto failure; + } + + cleanup: + esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList); + esxVI_ManagedObjectReference_Free(&task); + + return result; + + failure: + result = -1; + + goto cleanup; +} + + + static virDriver esxDriver = { VIR_DRV_ESX, "ESX", @@ -3388,23 +3793,23 @@ static virDriver esxDriver = { esxDomainIsPersistent, /* domainIsPersistent */ NULL, /* cpuCompare */ NULL, /* cpuBaseline */ - NULL, /* domainGetJobInfo */ - NULL, /* domainAbortJob */ - NULL, /* domainMigrateSetMaxDowntime */ - NULL, /* domainEventRegisterAny */ - NULL, /* domainEventDeregisterAny */ - NULL, /* domainManagedSave */ - NULL, /* domainHasManagedSaveImage */ - NULL, /* domainManagedSaveRemove */ - NULL, /* domainSnapshotCreateXML */ - NULL, /* domainSnapshotDumpXML */ - NULL, /* domainSnapshotNum */ - NULL, /* domainSnapshotListNames */ - NULL, /* domainSnapshotLookupByName */ - NULL, /* domainHasCurrentSnapshot */ - NULL, /* domainSnapshotCurrent */ - NULL, /* domainRevertToSnapshot */ - NULL, /* domainSnapshotDelete */ + NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ + NULL, /* domainMigrateSetMaxDowntime */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ + NULL, /* domainManagedSave */ + NULL, /* domainHasManagedSaveImage */ + NULL, /* domainManagedSaveRemove */ + esxDomainSnapshotCreateXML, /* domainSnapshotCreateXML */ + esxDomainSnapshotDumpXML, /* domainSnapshotDumpXML */ + esxDomainSnapshotNum, /* domainSnapshotNum */ + esxDomainSnapshotListNames, /* domainSnapshotListNames */ + esxDomainSnapshotLookupByName, /* domainSnapshotLookupByName */ + esxDomainHasCurrentSnapshot, /* domainHasCurrentSnapshot */ + esxDomainSnapshotCurrent, /* domainSnapshotCurrent */ + esxDomainRevertToSnapshot, /* domainRevertToSnapshot */ + esxDomainSnapshotDelete, /* domainSnapshotDelete */ }; diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index c37dfa10f9..1a71558c47 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -1718,6 +1718,152 @@ esxVI_GetVirtualMachineIdentity(esxVI_ObjectContent *virtualMachine, +int +esxVI_GetNumberOfSnapshotTrees + (esxVI_VirtualMachineSnapshotTree *snapshotTreeList) +{ + int count = 0; + esxVI_VirtualMachineSnapshotTree *snapshotTree; + + for (snapshotTree = snapshotTreeList; snapshotTree != NULL; + snapshotTree = snapshotTree->_next) { + count += 1 + esxVI_GetNumberOfSnapshotTrees + (snapshotTree->childSnapshotList); + } + + return count; +} + + + +int +esxVI_GetSnapshotTreeNames(esxVI_VirtualMachineSnapshotTree *snapshotTreeList, + char **names, int nameslen) +{ + int count = 0; + int result; + int i; + esxVI_VirtualMachineSnapshotTree *snapshotTree; + + for (snapshotTree = snapshotTreeList; + snapshotTree != NULL && count < nameslen; + snapshotTree = snapshotTree->_next) { + names[count] = strdup(snapshotTree->name); + + if (names[count] == NULL) { + virReportOOMError(); + goto failure; + } + + count++; + + if (count >= nameslen) { + break; + } + + result = esxVI_GetSnapshotTreeNames(snapshotTree->childSnapshotList, + names + count, nameslen - count); + + if (result < 0) { + goto failure; + } + + count += result; + } + + return count; + + failure: + for (i = 0; i < count; ++i) { + VIR_FREE(names[i]); + } + + return -1; +} + + + +int +esxVI_GetSnapshotTreeByName + (esxVI_VirtualMachineSnapshotTree *snapshotTreeList, const char *name, + esxVI_VirtualMachineSnapshotTree **snapshotTree, + esxVI_VirtualMachineSnapshotTree **snapshotTreeParent, + esxVI_Occurrence occurrence) +{ + esxVI_VirtualMachineSnapshotTree *candidate; + + if (snapshotTree == NULL || *snapshotTree != NULL || + snapshotTreeParent == NULL || *snapshotTreeParent != NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + for (candidate = snapshotTreeList; candidate != NULL; + candidate = candidate->_next) { + if (STREQ(candidate->name, name)) { + *snapshotTree = candidate; + *snapshotTreeParent = NULL; + return 1; + } + + if (esxVI_GetSnapshotTreeByName(candidate->childSnapshotList, name, + snapshotTree, snapshotTreeParent, + occurrence) > 0) { + if (*snapshotTreeParent == NULL) { + *snapshotTreeParent = candidate; + } + + return 1; + } + } + + if (occurrence == esxVI_Occurrence_OptionalItem) { + return 0; + } else { + ESX_VI_ERROR(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("Could not find snapshot with name '%s'"), name); + + return -1; + } +} + + + +int +esxVI_GetSnapshotTreeBySnapshot + (esxVI_VirtualMachineSnapshotTree *snapshotTreeList, + esxVI_ManagedObjectReference *snapshot, + esxVI_VirtualMachineSnapshotTree **snapshotTree) +{ + esxVI_VirtualMachineSnapshotTree *candidate; + + if (snapshotTree == NULL || *snapshotTree != NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + for (candidate = snapshotTreeList; candidate != NULL; + candidate = candidate->_next) { + if (STREQ(candidate->snapshot->value, snapshot->value)) { + *snapshotTree = candidate; + return 0; + } + + if (esxVI_GetSnapshotTreeBySnapshot(candidate->childSnapshotList, + snapshot, snapshotTree) >= 0) { + return 0; + } + } + + ESX_VI_ERROR(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("Could not find domain snapshot with internal name '%s'"), + snapshot->value); + + return -1; +} + + + int esxVI_LookupResourcePoolByHostSystem (esxVI_Context *ctx, esxVI_ObjectContent *hostSystem, @@ -2335,6 +2481,149 @@ esxVI_LookupAndHandleVirtualMachineQuestion(esxVI_Context *ctx, +int +esxVI_LookupRootSnapshotTreeList + (esxVI_Context *ctx, const unsigned char *virtualMachineUuid, + esxVI_VirtualMachineSnapshotTree **rootSnapshotTreeList) +{ + int result = 0; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *virtualMachine = NULL; + esxVI_DynamicProperty *dynamicProperty = NULL; + + if (rootSnapshotTreeList == NULL || *rootSnapshotTreeList != NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (esxVI_String_AppendValueToList(&propertyNameList, + "snapshot.rootSnapshotList") < 0 || + esxVI_LookupVirtualMachineByUuid(ctx, virtualMachineUuid, + propertyNameList, &virtualMachine, + esxVI_Occurrence_RequiredItem) < 0) { + goto failure; + } + + for (dynamicProperty = virtualMachine->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "snapshot.rootSnapshotList")) { + if (esxVI_VirtualMachineSnapshotTree_CastListFromAnyType + (dynamicProperty->val, rootSnapshotTreeList) < 0) { + goto failure; + } + + break; + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + if (*rootSnapshotTreeList == NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not lookup root snapshot list")); + goto failure; + } + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&virtualMachine); + + return result; + + failure: + esxVI_VirtualMachineSnapshotTree_Free(rootSnapshotTreeList); + + result = -1; + + goto cleanup; +} + + + +int +esxVI_LookupCurrentSnapshotTree + (esxVI_Context *ctx, const unsigned char *virtualMachineUuid, + esxVI_VirtualMachineSnapshotTree **currentSnapshotTree, + esxVI_Occurrence occurrence) +{ + int result = 0; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *virtualMachine = NULL; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_ManagedObjectReference *currentSnapshot = NULL; + esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL; + esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL; + + if (currentSnapshotTree == NULL || *currentSnapshotTree != NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (esxVI_String_AppendValueListToList(&propertyNameList, + "snapshot.currentSnapshot\0" + "snapshot.rootSnapshotList\0") < 0 || + esxVI_LookupVirtualMachineByUuid(ctx, virtualMachineUuid, + propertyNameList, &virtualMachine, + esxVI_Occurrence_RequiredItem) < 0) { + goto failure; + } + + for (dynamicProperty = virtualMachine->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "snapshot.currentSnapshot")) { + if (esxVI_ManagedObjectReference_CastFromAnyType + (dynamicProperty->val, ¤tSnapshot) < 0) { + goto failure; + } + } else if (STREQ(dynamicProperty->name, "snapshot.rootSnapshotList")) { + if (esxVI_VirtualMachineSnapshotTree_CastListFromAnyType + (dynamicProperty->val, &rootSnapshotTreeList) < 0) { + goto failure; + } + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + if (currentSnapshot == NULL) { + if (occurrence == esxVI_Occurrence_OptionalItem) { + return 0; + } else { + ESX_VI_ERROR(VIR_ERR_NO_DOMAIN_SNAPSHOT, "%s", + _("Domain has no current snapshot")); + goto failure; + } + } + + if (rootSnapshotTreeList == NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not lookup root snapshot list")); + goto failure; + } + + if (esxVI_GetSnapshotTreeBySnapshot(rootSnapshotTreeList, currentSnapshot, + &snapshotTree) < 0 || + esxVI_VirtualMachineSnapshotTree_DeepCopy(currentSnapshotTree, + snapshotTree) < 0) { + goto failure; + } + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&virtualMachine); + esxVI_ManagedObjectReference_Free(¤tSnapshot); + esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList); + + return result; + + failure: + result = -1; + + goto cleanup; +} + + + int esxVI_HandleVirtualMachineQuestion (esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachine, @@ -2422,9 +2711,7 @@ esxVI_HandleVirtualMachineQuestion return result; failure: - if (possibleAnswers == NULL) { - possibleAnswers = virBufferContentAndReset(&buffer); - } + virBufferFreeAndReset(&buffer); result = -1; diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h index 9b65e85ad8..a8d4cc3670 100644 --- a/src/esx/esx_vi.h +++ b/src/esx/esx_vi.h @@ -233,6 +233,24 @@ int esxVI_LookupNumberOfDomainsByPowerState int esxVI_GetVirtualMachineIdentity(esxVI_ObjectContent *virtualMachine, int *id, char **name, unsigned char *uuid); +int esxVI_GetNumberOfSnapshotTrees + (esxVI_VirtualMachineSnapshotTree *snapshotTreeList); + +int esxVI_GetSnapshotTreeNames + (esxVI_VirtualMachineSnapshotTree *snapshotTreeList, char **names, + int nameslen); + +int esxVI_GetSnapshotTreeByName + (esxVI_VirtualMachineSnapshotTree *snapshotTreeList, const char *name, + esxVI_VirtualMachineSnapshotTree **snapshotTree, + esxVI_VirtualMachineSnapshotTree **snapshotTreeParent, + esxVI_Occurrence occurrence); + +int esxVI_GetSnapshotTreeBySnapshot + (esxVI_VirtualMachineSnapshotTree *snapshotTreeList, + esxVI_ManagedObjectReference *snapshot, + esxVI_VirtualMachineSnapshotTree **snapshotTree); + int esxVI_LookupResourcePoolByHostSystem (esxVI_Context *ctx, esxVI_ObjectContent *hostSystem, esxVI_ManagedObjectReference **resourcePool); @@ -274,6 +292,15 @@ int esxVI_LookupAndHandleVirtualMachineQuestion(esxVI_Context *ctx, const unsigned char *uuid, esxVI_Boolean autoAnswer); +int esxVI_LookupRootSnapshotTreeList + (esxVI_Context *ctx, const unsigned char *virtualMachineUuid, + esxVI_VirtualMachineSnapshotTree **rootSnapshotTreeList); + +int esxVI_LookupCurrentSnapshotTree + (esxVI_Context *ctx, const unsigned char *virtualMachineUuid, + esxVI_VirtualMachineSnapshotTree **currentSnapshotTree, + esxVI_Occurrence occurrence); + int esxVI_HandleVirtualMachineQuestion (esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachine, diff --git a/src/esx/esx_vi_generator.input b/src/esx/esx_vi_generator.input index 06dddbf2a4..9c545ebc6d 100644 --- a/src/esx/esx_vi_generator.input +++ b/src/esx/esx_vi_generator.input @@ -424,3 +424,15 @@ object VirtualMachineQuestionInfo ChoiceOption choice r VirtualMachineMessage message i end + + +object VirtualMachineSnapshotTree + ManagedObjectReference snapshot r + ManagedObjectReference vm r + String name r + String description r + DateTime createTime r + VirtualMachinePowerState state r + Boolean quiesced r + VirtualMachineSnapshotTree childSnapshotList ol +end diff --git a/src/esx/esx_vi_generator.py b/src/esx/esx_vi_generator.py index 5ca61380b1..b933d5bdfa 100755 --- a/src/esx/esx_vi_generator.py +++ b/src/esx/esx_vi_generator.py @@ -95,6 +95,8 @@ class Property: return " ESX_VI__TEMPLATE__PROPERTY__DEEP_COPY_LIST(%s, %s)\n" % (self.type, self.name) elif self.type == "String": return " ESX_VI__TEMPLATE__PROPERTY__DEEP_COPY_VALUE(String, %s)\n" % self.name + elif self.is_enum(): + return " (*dest)->%s = src->%s;\n" % (self.name, self.name) else: return " ESX_VI__TEMPLATE__PROPERTY__DEEP_COPY(%s, %s)\n" % (self.type, self.name) @@ -841,17 +843,19 @@ additional_object_features = { "Event" : Object.FEATURE__LI "SharesInfo" : Object.FEATURE__ANY_TYPE, "TaskInfo" : Object.FEATURE__ANY_TYPE | Object.FEATURE__LIST, "UserSession" : Object.FEATURE__ANY_TYPE, - "VirtualMachineQuestionInfo" : Object.FEATURE__ANY_TYPE } + "VirtualMachineQuestionInfo" : Object.FEATURE__ANY_TYPE, + "VirtualMachineSnapshotTree" : Object.FEATURE__DEEP_COPY | Object.FEATURE__ANY_TYPE } -removed_object_features = { "DynamicProperty" : Object.FEATURE__SERIALIZE, - "ObjectContent" : Object.FEATURE__SERIALIZE, - "ObjectUpdate" : Object.FEATURE__SERIALIZE, - "PropertyChange" : Object.FEATURE__SERIALIZE, - "PropertyFilterUpdate" : Object.FEATURE__SERIALIZE, - "TaskInfo" : Object.FEATURE__SERIALIZE, - "UpdateSet" : Object.FEATURE__SERIALIZE, - "VirtualMachineConfigInfo" : Object.FEATURE__SERIALIZE } +removed_object_features = { "DynamicProperty" : Object.FEATURE__SERIALIZE, + "ObjectContent" : Object.FEATURE__SERIALIZE, + "ObjectUpdate" : Object.FEATURE__SERIALIZE, + "PropertyChange" : Object.FEATURE__SERIALIZE, + "PropertyFilterUpdate" : Object.FEATURE__SERIALIZE, + "TaskInfo" : Object.FEATURE__SERIALIZE, + "UpdateSet" : Object.FEATURE__SERIALIZE, + "VirtualMachineConfigInfo" : Object.FEATURE__SERIALIZE, + "VirtualMachineSnapshotTree" : Object.FEATURE__SERIALIZE } @@ -948,7 +952,8 @@ for obj in objects_by_name.values(): if obj.features & Object.FEATURE__DEEP_COPY: for property in obj.properties: if property.occurrence != Property.OCCURRENCE__IGNORED and \ - property.type not in predefined_objects: + property.type not in predefined_objects and \ + property.type in objects_by_name: objects_by_name[property.type].features |= Object.FEATURE__DEEP_COPY # detect extended_by relation diff --git a/src/esx/esx_vi_methods.c b/src/esx/esx_vi_methods.c index 3e095c70bf..b2b3e8d4a7 100644 --- a/src/esx/esx_vi_methods.c +++ b/src/esx/esx_vi_methods.c @@ -491,6 +491,92 @@ ESX_VI__METHOD(RegisterVM_Task, +/* esxVI_CreateSnapshot_Task */ +ESX_VI__METHOD(CreateSnapshot_Task, + (esxVI_Context *ctx, + esxVI_ManagedObjectReference *virtualMachine, + const char *name, const char *description, + esxVI_Boolean memory, esxVI_Boolean quiesce, + esxVI_ManagedObjectReference **task), + RequiredItem, +{ + ESX_VI__METHOD__PARAMETER__CHECK_OUTPUT(task) +}, +{ + ESX_VI__METHOD__PARAMETER__REQUIRE_THIS(virtualMachine) + ESX_VI__METHOD__PARAMETER__REQUIRE(name) + ESX_VI__METHOD__PARAMETER__REQUIRE(memory) + ESX_VI__METHOD__PARAMETER__REQUIRE(quiesce) +}, +{ + ESX_VI__METHOD__PARAMETER__SERIALIZE_THIS(ManagedObjectReference, + virtualMachine) + ESX_VI__METHOD__PARAMETER__SERIALIZE_VALUE(String, name) + ESX_VI__METHOD__PARAMETER__SERIALIZE_VALUE(String, description) + ESX_VI__METHOD__PARAMETER__SERIALIZE(Boolean, memory) + ESX_VI__METHOD__PARAMETER__SERIALIZE(Boolean, quiesce) +}, +{ + if (esxVI_ManagedObjectReference_Deserialize(response->node, task) < 0) { + goto failure; + } +}) + + + +/* esxVI_RevertToSnapshot_Task */ +ESX_VI__METHOD(RevertToSnapshot_Task, + (esxVI_Context *ctx, + esxVI_ManagedObjectReference *virtualMachineSnapshot, + esxVI_ManagedObjectReference *host, + esxVI_ManagedObjectReference **task), + RequiredItem, +{ + ESX_VI__METHOD__PARAMETER__CHECK_OUTPUT(task) +}, +{ + ESX_VI__METHOD__PARAMETER__REQUIRE_THIS(virtualMachineSnapshot) +}, +{ + ESX_VI__METHOD__PARAMETER__SERIALIZE_THIS(ManagedObjectReference, + virtualMachineSnapshot) + ESX_VI__METHOD__PARAMETER__SERIALIZE(ManagedObjectReference, host) +}, +{ + if (esxVI_ManagedObjectReference_Deserialize(response->node, task) < 0) { + goto failure; + } +}) + + + +/* esxVI_RemoveSnapshot_Task */ +ESX_VI__METHOD(RemoveSnapshot_Task, + (esxVI_Context *ctx, + esxVI_ManagedObjectReference *virtualMachineSnapshot, + esxVI_Boolean removeChildren, + esxVI_ManagedObjectReference **task), + RequiredItem, +{ + ESX_VI__METHOD__PARAMETER__CHECK_OUTPUT(task) +}, +{ + ESX_VI__METHOD__PARAMETER__REQUIRE_THIS(virtualMachineSnapshot) + ESX_VI__METHOD__PARAMETER__REQUIRE(removeChildren) +}, +{ + ESX_VI__METHOD__PARAMETER__SERIALIZE_THIS(ManagedObjectReference, + virtualMachineSnapshot) + ESX_VI__METHOD__PARAMETER__SERIALIZE(Boolean, removeChildren) +}, +{ + if (esxVI_ManagedObjectReference_Deserialize(response->node, task) < 0) { + goto failure; + } +}) + + + /* esxVI_CancelTask */ ESX_VI__METHOD(CancelTask, (esxVI_Context *ctx, diff --git a/src/esx/esx_vi_methods.h b/src/esx/esx_vi_methods.h index 40bff511b4..9ff8b4b412 100644 --- a/src/esx/esx_vi_methods.h +++ b/src/esx/esx_vi_methods.h @@ -80,6 +80,20 @@ int esxVI_RegisterVM_Task(esxVI_Context *ctx, esxVI_ManagedObjectReference *host, esxVI_ManagedObjectReference **task); +int esxVI_CreateSnapshot_Task(esxVI_Context *ctx, + esxVI_ManagedObjectReference *virtualMachine, + const char *name, const char *description, + esxVI_Boolean memory, esxVI_Boolean quiesce, + esxVI_ManagedObjectReference **task); + +int esxVI_RevertToSnapshot_Task + (esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachineSnapshot, + esxVI_ManagedObjectReference *host, esxVI_ManagedObjectReference **task); + +int esxVI_RemoveSnapshot_Task + (esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachineSnapshot, + esxVI_Boolean removeChildren, esxVI_ManagedObjectReference **task); + int esxVI_CancelTask(esxVI_Context *ctx, esxVI_ManagedObjectReference *task); int esxVI_UnregisterVM(esxVI_Context *ctx, diff --git a/src/esx/esx_vi_types.c b/src/esx/esx_vi_types.c index a69ea447ab..ed4674b3b2 100644 --- a/src/esx/esx_vi_types.c +++ b/src/esx/esx_vi_types.c @@ -1177,6 +1177,12 @@ ESX_VI__TEMPLATE__VALIDATE(DateTime, ESX_VI__TEMPLATE__PROPERTY__REQUIRE(value); }) +/* esxVI_DateTime_DeepCopy */ +ESX_VI__TEMPLATE__DEEP_COPY(DateTime, +{ + ESX_VI__TEMPLATE__PROPERTY__DEEP_COPY_VALUE(String, value) +}) + /* esxVI_DateTime_Serialize */ ESX_VI__TEMPLATE__SERIALIZE(DateTime, { @@ -1213,6 +1219,104 @@ esxVI_DateTime_Deserialize(xmlNodePtr node, esxVI_DateTime **dateTime) return -1; } +int +esxVI_DateTime_ConvertToCalendarTime(esxVI_DateTime *dateTime, + time_t *secondsSinceEpoch) +{ + char value[64] = ""; + char *tmp; + struct tm tm; + int milliseconds; + char sign; + int tz_hours; + int tz_minutes; + int tz_offset = 0; + + if (dateTime == NULL || secondsSinceEpoch == NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (virStrcpyStatic(value, dateTime->value) == NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, + _("xsd:dateTime value '%s' too long for destination"), + dateTime->value); + return -1; + } + + /* + * expected format: [-]CCYY-MM-DDTHH:MM:SS[.ssssss][((+|-)HH:MM|Z)] + * typical example: 2010-04-05T12:13:55.316789+02:00 + * + * see http://www.w3.org/TR/xmlschema-2/#dateTime + * + * map negative years to 0, since the base for time_t is the year 1970. + */ + if (*value == '-') { + *secondsSinceEpoch = 0; + return 0; + } + + tmp = strptime(value, "%Y-%m-%dT%H:%M:%S", &tm); + + if (tmp == NULL) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, + _("xsd:dateTime value '%s' has unexpected format"), + dateTime->value); + return -1; + } + + if (*tmp != '\0') { + /* skip .ssssss part if present */ + if (*tmp == '.' && + virStrToLong_i(tmp + 1, &tmp, 10, &milliseconds) < 0) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, + _("xsd:dateTime value '%s' has unexpected format"), + dateTime->value); + return -1; + } + + /* parse timezone offset if present. if missing assume UTC */ + if (*tmp == '+' || *tmp == '-') { + sign = *tmp; + + if (virStrToLong_i(tmp + 1, &tmp, 10, &tz_hours) < 0 || + *tmp != ':' || + virStrToLong_i(tmp + 1, NULL, 10, &tz_minutes) < 0) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, + _("xsd:dateTime value '%s' has unexpected format"), + dateTime->value); + return -1; + } + + tz_offset = tz_hours * 60 * 60 + tz_minutes * 60; + + if (sign == '-') { + tz_offset = -tz_offset; + } + } else if (STREQ(tmp, "Z")) { + /* Z refers to UTC. tz_offset is already initialized to zero */ + } else { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, + _("xsd:dateTime value '%s' has unexpected format"), + dateTime->value); + return -1; + } + } + + /* + * xsd:dateTime represents local time relative to the optional timezone + * given as offset. pretend the local time is in UTC and use timegm in + * order to avoid interference with the timezone to this computer. + * apply timezone correction afterwards, because it's simpler than + * handling all the possible over- and underflows when trying to apply + * it to the tm struct. + */ + *secondsSinceEpoch = timegm(&tm) - tz_offset; + + return 0; +} + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -1344,4 +1448,31 @@ esxVI_ManagedObjectReference_Deserialize return -1; } + + #include "esx_vi_types.generated.c" + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * VI Enum: VirtualMachinePowerState (Additions) + */ + +int +esxVI_VirtualMachinePowerState_ConvertToLibvirt + (esxVI_VirtualMachinePowerState powerState) +{ + switch (powerState) { + case esxVI_VirtualMachinePowerState_PoweredOff: + return VIR_DOMAIN_SHUTOFF; + + case esxVI_VirtualMachinePowerState_PoweredOn: + return VIR_DOMAIN_RUNNING; + + case esxVI_VirtualMachinePowerState_Suspended: + return VIR_DOMAIN_PAUSED; + + default: + return VIR_DOMAIN_NOSTATE; + } +} diff --git a/src/esx/esx_vi_types.h b/src/esx/esx_vi_types.h index d3c7115b47..4bedca9ca1 100644 --- a/src/esx/esx_vi_types.h +++ b/src/esx/esx_vi_types.h @@ -230,9 +230,12 @@ struct _esxVI_DateTime { int esxVI_DateTime_Alloc(esxVI_DateTime **dateTime); void esxVI_DateTime_Free(esxVI_DateTime **dateTime); int esxVI_DateTime_Validate(esxVI_DateTime *dateTime); +int esxVI_DateTime_DeepCopy(esxVI_DateTime **dest, esxVI_DateTime *src); int esxVI_DateTime_Serialize(esxVI_DateTime *dateTime, const char *element, virBufferPtr output); int esxVI_DateTime_Deserialize(xmlNodePtr node, esxVI_DateTime **dateTime); +int esxVI_DateTime_ConvertToCalendarTime(esxVI_DateTime *dateTime, + time_t *secondsSinceEpoch); @@ -295,4 +298,13 @@ int esxVI_ManagedObjectReference_Deserialize # include "esx_vi_types.generated.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * VI Enum: VirtualMachinePowerState (Additions) + */ + +int esxVI_VirtualMachinePowerState_ConvertToLibvirt + (esxVI_VirtualMachinePowerState powerState); + #endif /* __ESX_VI_TYPES_H__ */ diff --git a/tests/esxutilstest.c b/tests/esxutilstest.c index 10f6a3c1d8..2a13282cd4 100644 --- a/tests/esxutilstest.c +++ b/tests/esxutilstest.c @@ -11,6 +11,7 @@ # include "testutils.h" # include "util.h" # include "esx/esx_util.h" +# include "esx/esx_vi_types.h" static char *progname; @@ -164,6 +165,64 @@ testParseDatastoreRelatedPath(const void *data ATTRIBUTE_UNUSED) +struct testDateTime { + const char *dateTime; + time_t calendarTime; +}; + +static struct testDateTime times[] = { + /* different timezones */ + { "2010-04-08T05:45:11-07:00", 1270730711 }, + { "2010-04-08T07:45:11-05:00", 1270730711 }, + { "2010-04-08T12:45:11+00:00", 1270730711 }, + { "2010-04-08T14:45:11+02:00", 1270730711 }, + { "2010-04-08T22:15:11+09:30", 1270730711 }, + { "2010-04-09T01:30:11+12:45", 1270730711 }, + + /* optional parts */ + { "2010-04-08T12:45:11Z", 1270730711 }, + { "2010-04-08T12:45:11", 1270730711 }, + { "-2010-04-08T14:45:11+02:00", 0 }, + { "2010-04-08T14:45:11.529576+02:00", 1270730711 }, + + /* borders */ + { "1970-01-01T00:00:00+00:00", 0 }, + { "2038-01-19T03:14:07+00:00", 2147483647 }, + + /* random */ + { "1999-08-02T01:19:55+02:00", 933549595 }, + { "2004-03-07T23:23:55+02:00", 1078694635 }, + { "1984-10-27T14:33:45+02:00", 467728425 }, + { "1970-01-12T16:11:04+02:00", 1001464 }, + { "2014-07-20T13:35:38+02:00", 1405856138 }, + { "2032-06-24T17:04:49+02:00", 1971702289 }, +}; + +static int +testConvertDateTimeToCalendarTime(const void *data ATTRIBUTE_UNUSED) +{ + int i; + esxVI_DateTime dateTime; + time_t calendarTime; + + for (i = 0; i < ARRAY_CARDINALITY(times); ++i) { + dateTime.value = (char *)times[i].dateTime; + + if (esxVI_DateTime_ConvertToCalendarTime(&dateTime, + &calendarTime) < 0) { + return -1; + } + + if (times[i].calendarTime != calendarTime) { + return -1; + } + } + + return 0; +} + + + static int mymain(int argc, char **argv) { @@ -194,6 +253,7 @@ mymain(int argc, char **argv) DO_TEST(IndexToDiskName); DO_TEST(DiskNameToIndex); DO_TEST(ParseDatastoreRelatedPath); + DO_TEST(ConvertDateTimeToCalendarTime); return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }