mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-02 19:31:18 +00:00
d9ad8ac392
Use it in all places where a memory or storage request size is converted
to a larger granularity. This avoids requesting too small memory or storage
sizes that could result from the truncation done by a simple division.
This extends the round up fix in 6002e0406c
to the whole codebase.
Instead of reporting errors for odd values in the VMX code round them up.
Update the QEMU Argv tests accordingly as the original memory size 219200
isn't a even multiple of 1024 and is rounded up to 215 megabyte now. Change
it to 219100 and 219136. Use two different values intentionally to make
sure that rounding up works.
Update virsh.pod accordingly, as rounding down and rejecting are replaced
by rounding up.
4674 lines
148 KiB
C
4674 lines
148 KiB
C
|
|
/*
|
|
* esx_driver.c: core driver functions for managing VMware ESX hosts
|
|
*
|
|
* Copyright (C) 2010 Red Hat, Inc.
|
|
* Copyright (C) 2009-2010 Matthias Bolte <matthias.bolte@googlemail.com>
|
|
* Copyright (C) 2009 Maximilian Wilhelm <max@rfc2324.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "internal.h"
|
|
#include "domain_conf.h"
|
|
#include "authhelper.h"
|
|
#include "util.h"
|
|
#include "memory.h"
|
|
#include "logging.h"
|
|
#include "uuid.h"
|
|
#include "vmx.h"
|
|
#include "esx_driver.h"
|
|
#include "esx_interface_driver.h"
|
|
#include "esx_network_driver.h"
|
|
#include "esx_storage_driver.h"
|
|
#include "esx_device_monitor.h"
|
|
#include "esx_secret_driver.h"
|
|
#include "esx_nwfilter_driver.h"
|
|
#include "esx_private.h"
|
|
#include "esx_vi.h"
|
|
#include "esx_vi_methods.h"
|
|
#include "esx_util.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_ESX
|
|
|
|
static int esxDomainGetMaxVcpus(virDomainPtr domain);
|
|
|
|
typedef struct _esxVMX_Data esxVMX_Data;
|
|
|
|
struct _esxVMX_Data {
|
|
esxVI_Context *ctx;
|
|
char *datastorePathWithoutFileName;
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* Parse a file name from a .vmx file and convert it to datastore path format.
|
|
* A .vmx file can contain file names in various formats:
|
|
*
|
|
* - A single name referencing a file in the same directory as the .vmx file:
|
|
*
|
|
* test1.vmdk
|
|
*
|
|
* - An absolute file name referencing a file in a datastore that is mounted at
|
|
* /vmfs/volumes/<datastore>:
|
|
*
|
|
* /vmfs/volumes/b24b7a78-9d82b4f5/test1/test1.vmdk
|
|
* /vmfs/volumes/datastore1/test1/test1.vmdk
|
|
*
|
|
* The actual mount directory is /vmfs/volumes/b24b7a78-9d82b4f5, the second
|
|
* form is a symlink to it using the datastore name. This is the typical
|
|
* setup on an ESX(i) server.
|
|
*
|
|
* - With GSX installed on Windows there are also Windows style file names
|
|
* including UNC file names:
|
|
*
|
|
* C:\Virtual Machines\test1\test1.vmdk
|
|
* \\nas1\storage1\test1\test1.vmdk
|
|
*
|
|
* The datastore path format typically looks like this:
|
|
*
|
|
* [datastore1] test1/test1.vmdk
|
|
*
|
|
* Firstly this functions checks if the given file name contains a separator.
|
|
* If it doesn't then the referenced file is in the same directory as the .vmx
|
|
* file. The datastore name and directory of the .vmx file are passed to this
|
|
* function via the opaque parameter by the caller of virVMXParseConfig.
|
|
*
|
|
* Otherwise query for all known datastores and their mount directories. Then
|
|
* try to find a datastore with a mount directory that is a prefix to the given
|
|
* file name. This mechanism covers the Windows style file names too.
|
|
*
|
|
* The symlinks using the datastore name (/vmfs/volumes/datastore1) are an
|
|
* exception and need special handling. Parse the datastore name and use it
|
|
* to lookup the datastore by name to verify that it exists.
|
|
*/
|
|
static char *
|
|
esxParseVMXFileName(const char *fileName, void *opaque)
|
|
{
|
|
char *datastorePath = NULL;
|
|
esxVMX_Data *data = opaque;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *datastoreList = NULL;
|
|
esxVI_ObjectContent *datastore = NULL;
|
|
esxVI_DatastoreHostMount *hostMount = NULL;
|
|
char *datastoreName;
|
|
char *tmp;
|
|
char *saveptr;
|
|
char *strippedFileName = NULL;
|
|
char *copyOfFileName = NULL;
|
|
char *directoryAndFileName;
|
|
|
|
if (strchr(fileName, '/') == NULL && strchr(fileName, '\\') == NULL) {
|
|
/* Plain file name, use same directory as for the .vmx file */
|
|
if (virAsprintf(&datastorePath, "%s/%s",
|
|
data->datastorePathWithoutFileName, fileName) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"summary.name") < 0 ||
|
|
esxVI_LookupDatastoreList(data->ctx, propertyNameList,
|
|
&datastoreList) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Search for datastore by mount path */
|
|
for (datastore = datastoreList; datastore != NULL;
|
|
datastore = datastore->_next) {
|
|
esxVI_DatastoreHostMount_Free(&hostMount);
|
|
datastoreName = NULL;
|
|
|
|
if (esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj,
|
|
&hostMount) < 0 ||
|
|
esxVI_GetStringValue(datastore, "summary.name", &datastoreName,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
tmp = (char *)STRSKIP(fileName, hostMount->mountInfo->path);
|
|
|
|
if (tmp == NULL) {
|
|
continue;
|
|
}
|
|
|
|
/* Found a match. Strip leading separators */
|
|
while (*tmp == '/' || *tmp == '\\') {
|
|
++tmp;
|
|
}
|
|
|
|
if (esxVI_String_DeepCopyValue(&strippedFileName, tmp) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
tmp = strippedFileName;
|
|
|
|
/* Convert \ to / */
|
|
while (*tmp != '\0') {
|
|
if (*tmp == '\\') {
|
|
*tmp = '/';
|
|
}
|
|
|
|
++tmp;
|
|
}
|
|
|
|
if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
|
|
strippedFileName) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Fallback to direct datastore name match */
|
|
if (datastorePath == NULL && STRPREFIX(fileName, "/vmfs/volumes/")) {
|
|
if (esxVI_String_DeepCopyValue(©OfFileName, fileName) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Expected format: '/vmfs/volumes/<datastore>/<path>' */
|
|
if ((tmp = STRSKIP(copyOfFileName, "/vmfs/volumes/")) == NULL ||
|
|
(datastoreName = strtok_r(tmp, "/", &saveptr)) == NULL ||
|
|
(directoryAndFileName = strtok_r(NULL, "", &saveptr)) == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("File name '%s' doesn't have expected format "
|
|
"'/vmfs/volumes/<datastore>/<path>'"), fileName);
|
|
goto cleanup;
|
|
}
|
|
|
|
esxVI_ObjectContent_Free(&datastoreList);
|
|
|
|
if (esxVI_LookupDatastoreByName(data->ctx, datastoreName,
|
|
NULL, &datastoreList,
|
|
esxVI_Occurrence_OptionalItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (datastoreList == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("File name '%s' refers to non-existing datastore '%s'"),
|
|
fileName, datastoreName);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
|
|
directoryAndFileName) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (datastorePath == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not find datastore for '%s'"), fileName);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&datastoreList);
|
|
esxVI_DatastoreHostMount_Free(&hostMount);
|
|
VIR_FREE(strippedFileName);
|
|
VIR_FREE(copyOfFileName);
|
|
|
|
return datastorePath;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* This function does the inverse of esxParseVMXFileName. It takes an file name
|
|
* in datastore path format and converts it to a file name that can be used in
|
|
* a .vmx file.
|
|
*
|
|
* The datastore path format and the formats found in a .vmx file are described
|
|
* in the documentation of esxParseVMXFileName.
|
|
*
|
|
* Firstly parse the datastore path. Then use the datastore name to lookup the
|
|
* datastore and it's mount path. Finally concatenate the mount path, directory
|
|
* and file name to an absolute path and return it. Detect the seperator type
|
|
* based on the mount path.
|
|
*/
|
|
static char *
|
|
esxFormatVMXFileName(const char *datastorePath, void *opaque)
|
|
{
|
|
bool success = false;
|
|
esxVMX_Data *data = opaque;
|
|
char *datastoreName = NULL;
|
|
char *directoryAndFileName = NULL;
|
|
esxVI_ObjectContent *datastore = NULL;
|
|
esxVI_DatastoreHostMount *hostMount = NULL;
|
|
char separator = '/';
|
|
virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
|
char *tmp;
|
|
size_t length;
|
|
char *absolutePath = NULL;
|
|
|
|
/* Parse datastore path and lookup datastore */
|
|
if (esxUtil_ParseDatastorePath(datastorePath, &datastoreName, NULL,
|
|
&directoryAndFileName) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_LookupDatastoreByName(data->ctx, datastoreName, NULL, &datastore,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj,
|
|
&hostMount) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Detect separator type */
|
|
if (strchr(hostMount->mountInfo->path, '\\') != NULL) {
|
|
separator = '\\';
|
|
}
|
|
|
|
/* Strip trailing separators */
|
|
length = strlen(hostMount->mountInfo->path);
|
|
|
|
while (length > 0 && hostMount->mountInfo->path[length - 1] == separator) {
|
|
--length;
|
|
}
|
|
|
|
/* Format as <mount>[/<directory>]/<file>, convert / to \ when necessary */
|
|
virBufferAdd(&buffer, hostMount->mountInfo->path, length);
|
|
|
|
if (separator != '/') {
|
|
tmp = directoryAndFileName;
|
|
|
|
while (*tmp != '\0') {
|
|
if (*tmp == '/') {
|
|
*tmp = separator;
|
|
}
|
|
|
|
++tmp;
|
|
}
|
|
}
|
|
|
|
virBufferAddChar(&buffer, separator);
|
|
virBufferAdd(&buffer, directoryAndFileName, -1);
|
|
|
|
if (virBufferError(&buffer)) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
absolutePath = virBufferContentAndReset(&buffer);
|
|
|
|
/* FIXME: Check if referenced path/file really exists */
|
|
|
|
success = true;
|
|
|
|
cleanup:
|
|
if (! success) {
|
|
virBufferFreeAndReset(&buffer);
|
|
VIR_FREE(absolutePath);
|
|
}
|
|
|
|
VIR_FREE(datastoreName);
|
|
VIR_FREE(directoryAndFileName);
|
|
esxVI_ObjectContent_Free(&datastore);
|
|
esxVI_DatastoreHostMount_Free(&hostMount);
|
|
|
|
return absolutePath;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxAutodetectSCSIControllerModel(virDomainDiskDefPtr def, int *model,
|
|
void *opaque)
|
|
{
|
|
int result = -1;
|
|
esxVMX_Data *data = opaque;
|
|
esxVI_FileInfo *fileInfo = NULL;
|
|
esxVI_VmDiskFileInfo *vmDiskFileInfo = NULL;
|
|
|
|
if (def->device != VIR_DOMAIN_DISK_DEVICE_DISK ||
|
|
def->bus != VIR_DOMAIN_DISK_BUS_SCSI ||
|
|
def->type != VIR_DOMAIN_DISK_TYPE_FILE ||
|
|
def->src == NULL ||
|
|
! STRPREFIX(def->src, "[")) {
|
|
/*
|
|
* This isn't a file-based SCSI disk device with a datastore related
|
|
* source path => do nothing.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
if (esxVI_LookupFileInfoByDatastorePath(data->ctx, def->src,
|
|
false, &fileInfo,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
vmDiskFileInfo = esxVI_VmDiskFileInfo_DynamicCast(fileInfo);
|
|
|
|
if (vmDiskFileInfo == NULL || vmDiskFileInfo->controllerType == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not lookup controller model for '%s'"), def->src);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (STRCASEEQ(vmDiskFileInfo->controllerType,
|
|
"VirtualBusLogicController")) {
|
|
*model = VIR_DOMAIN_CONTROLLER_MODEL_BUSLOGIC;
|
|
} else if (STRCASEEQ(vmDiskFileInfo->controllerType,
|
|
"VirtualLsiLogicController")) {
|
|
*model = VIR_DOMAIN_CONTROLLER_MODEL_LSILOGIC;
|
|
} else if (STRCASEEQ(vmDiskFileInfo->controllerType,
|
|
"VirtualLsiLogicSASController")) {
|
|
*model = VIR_DOMAIN_CONTROLLER_MODEL_LSISAS1068;
|
|
} else if (STRCASEEQ(vmDiskFileInfo->controllerType,
|
|
"ParaVirtualSCSIController")) {
|
|
*model = VIR_DOMAIN_CONTROLLER_MODEL_VMPVSCSI;
|
|
} else {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Found unexpected controller model '%s' for disk '%s'"),
|
|
vmDiskFileInfo->controllerType, def->src);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_FileInfo_Free(&fileInfo);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static esxVI_Boolean
|
|
esxSupportsLongMode(esxPrivate *priv)
|
|
{
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostSystem = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
esxVI_HostCpuIdInfo *hostCpuIdInfoList = NULL;
|
|
esxVI_HostCpuIdInfo *hostCpuIdInfo = NULL;
|
|
esxVI_ParsedHostCpuIdInfo parsedHostCpuIdInfo;
|
|
char edxLongModeBit = '?';
|
|
|
|
if (priv->supportsLongMode != esxVI_Boolean_Undefined) {
|
|
return priv->supportsLongMode;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return esxVI_Boolean_Undefined;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"hardware.cpuFeature") < 0 ||
|
|
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
|
|
&hostSystem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hostSystem == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not retrieve the HostSystem object"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name, "hardware.cpuFeature")) {
|
|
if (esxVI_HostCpuIdInfo_CastListFromAnyType
|
|
(dynamicProperty->val, &hostCpuIdInfoList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (hostCpuIdInfo = hostCpuIdInfoList; hostCpuIdInfo != NULL;
|
|
hostCpuIdInfo = hostCpuIdInfo->_next) {
|
|
if (hostCpuIdInfo->level->value == -2147483647) { /* 0x80000001 */
|
|
if (esxVI_ParseHostCpuIdInfo(&parsedHostCpuIdInfo,
|
|
hostCpuIdInfo) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
edxLongModeBit = parsedHostCpuIdInfo.edx[29];
|
|
|
|
if (edxLongModeBit == '1') {
|
|
priv->supportsLongMode = esxVI_Boolean_True;
|
|
} else if (edxLongModeBit == '0') {
|
|
priv->supportsLongMode = esxVI_Boolean_False;
|
|
} else {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Bit 29 (Long Mode) of HostSystem property "
|
|
"'hardware.cpuFeature[].edx' with value '%s' "
|
|
"has unexpected value '%c', expecting '0' "
|
|
"or '1'"), hostCpuIdInfo->edx, edxLongModeBit);
|
|
goto cleanup;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
/*
|
|
* If we goto cleanup in case of an error then priv->supportsLongMode
|
|
* is still esxVI_Boolean_Undefined, therefore we don't need to set it.
|
|
*/
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostSystem);
|
|
esxVI_HostCpuIdInfo_Free(&hostCpuIdInfoList);
|
|
|
|
return priv->supportsLongMode;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxLookupHostSystemBiosUuid(esxPrivate *priv, unsigned char *uuid)
|
|
{
|
|
int result = -1;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostSystem = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"hardware.systemInfo.uuid") < 0 ||
|
|
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
|
|
&hostSystem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hostSystem == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not retrieve the HostSystem object"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name, "hardware.systemInfo.uuid")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_String) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (strlen(dynamicProperty->val->string) > 0) {
|
|
if (virUUIDParse(dynamicProperty->val->string, uuid) < 0) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse UUID from string '%s'"),
|
|
dynamicProperty->val->string);
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
/* HostSystem has an empty UUID */
|
|
memset(uuid, 0, VIR_UUID_BUFLEN);
|
|
}
|
|
|
|
break;
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostSystem);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static virCapsPtr
|
|
esxCapsInit(esxPrivate *priv)
|
|
{
|
|
esxVI_Boolean supportsLongMode = esxSupportsLongMode(priv);
|
|
virCapsPtr caps = NULL;
|
|
virCapsGuestPtr guest = NULL;
|
|
|
|
if (supportsLongMode == esxVI_Boolean_Undefined) {
|
|
return NULL;
|
|
}
|
|
|
|
if (supportsLongMode == esxVI_Boolean_True) {
|
|
caps = virCapabilitiesNew("x86_64", 1, 1);
|
|
} else {
|
|
caps = virCapabilitiesNew("i686", 1, 1);
|
|
}
|
|
|
|
if (caps == NULL) {
|
|
virReportOOMError();
|
|
return NULL;
|
|
}
|
|
|
|
virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x00, 0x0c, 0x29 });
|
|
virCapabilitiesAddHostMigrateTransport(caps, "vpxmigr");
|
|
|
|
caps->hasWideScsiBus = true;
|
|
|
|
if (esxLookupHostSystemBiosUuid(priv, caps->host.host_uuid) < 0) {
|
|
goto failure;
|
|
}
|
|
|
|
/* i686 */
|
|
guest = virCapabilitiesAddGuest(caps, "hvm", "i686", 32, NULL, NULL, 0,
|
|
NULL);
|
|
|
|
if (guest == NULL) {
|
|
goto failure;
|
|
}
|
|
|
|
if (virCapabilitiesAddGuestDomain(guest, "vmware", NULL, NULL, 0,
|
|
NULL) == NULL) {
|
|
goto failure;
|
|
}
|
|
|
|
/* x86_64 */
|
|
if (supportsLongMode == esxVI_Boolean_True) {
|
|
guest = virCapabilitiesAddGuest(caps, "hvm", "x86_64", 64, NULL, NULL,
|
|
0, NULL);
|
|
|
|
if (guest == NULL) {
|
|
goto failure;
|
|
}
|
|
|
|
if (virCapabilitiesAddGuestDomain(guest, "vmware", NULL, NULL, 0,
|
|
NULL) == NULL) {
|
|
goto failure;
|
|
}
|
|
}
|
|
|
|
return caps;
|
|
|
|
failure:
|
|
virCapabilitiesFree(caps);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxConnectToHost(esxPrivate *priv, virConnectAuthPtr auth,
|
|
const char *hostname, int port,
|
|
const char *predefinedUsername,
|
|
esxUtil_ParsedUri *parsedUri,
|
|
esxVI_ProductVersion expectedProductVersion,
|
|
char **vCenterIpAddress)
|
|
{
|
|
int result = -1;
|
|
char ipAddress[NI_MAXHOST] = "";
|
|
char *username = NULL;
|
|
char *password = NULL;
|
|
char *url = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostSystem = NULL;
|
|
esxVI_Boolean inMaintenanceMode = esxVI_Boolean_Undefined;
|
|
|
|
if (vCenterIpAddress == NULL || *vCenterIpAddress != NULL) {
|
|
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
|
|
return -1;
|
|
}
|
|
|
|
if (esxUtil_ResolveHostname(hostname, ipAddress, NI_MAXHOST) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (predefinedUsername != NULL) {
|
|
username = strdup(predefinedUsername);
|
|
|
|
if (username == NULL) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
username = virRequestUsername(auth, "root", hostname);
|
|
|
|
if (username == NULL) {
|
|
ESX_ERROR(VIR_ERR_AUTH_FAILED, "%s", _("Username request failed"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
password = virRequestPassword(auth, username, hostname);
|
|
|
|
if (password == NULL) {
|
|
ESX_ERROR(VIR_ERR_AUTH_FAILED, "%s", _("Password request failed"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virAsprintf(&url, "%s://%s:%d/sdk", priv->transport, hostname,
|
|
port) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_Context_Alloc(&priv->host) < 0 ||
|
|
esxVI_Context_Connect(priv->host, url, ipAddress, username, password,
|
|
parsedUri) < 0 ||
|
|
esxVI_Context_LookupObjectsByPath(priv->host, parsedUri) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (expectedProductVersion == esxVI_ProductVersion_ESX) {
|
|
if (priv->host->productVersion != esxVI_ProductVersion_ESX35 &&
|
|
priv->host->productVersion != esxVI_ProductVersion_ESX40 &&
|
|
priv->host->productVersion != esxVI_ProductVersion_ESX41 &&
|
|
priv->host->productVersion != esxVI_ProductVersion_ESX4x) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("%s is neither an ESX 3.5 host nor an ESX 4.x host"),
|
|
hostname);
|
|
goto cleanup;
|
|
}
|
|
} else { /* GSX */
|
|
if (priv->host->productVersion != esxVI_ProductVersion_GSX20) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("%s isn't a GSX 2.0 host"), hostname);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Query the host for maintenance mode and vCenter IP address */
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"runtime.inMaintenanceMode\0"
|
|
"summary.managementServerIp\0") < 0 ||
|
|
esxVI_LookupHostSystemProperties(priv->host, propertyNameList,
|
|
&hostSystem) < 0 ||
|
|
esxVI_GetBoolean(hostSystem, "runtime.inMaintenanceMode",
|
|
&inMaintenanceMode,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_GetStringValue(hostSystem, "summary.managementServerIp",
|
|
vCenterIpAddress,
|
|
esxVI_Occurrence_OptionalItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Warn if host is in maintenance mode */
|
|
if (inMaintenanceMode == esxVI_Boolean_True) {
|
|
VIR_WARN0("The server is in maintenance mode");
|
|
}
|
|
|
|
if (*vCenterIpAddress != NULL) {
|
|
*vCenterIpAddress = strdup(*vCenterIpAddress);
|
|
|
|
if (*vCenterIpAddress == NULL) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(password);
|
|
VIR_FREE(username);
|
|
VIR_FREE(url);
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostSystem);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxConnectToVCenter(esxPrivate *priv, virConnectAuthPtr auth,
|
|
const char *hostname, int port,
|
|
const char *predefinedUsername,
|
|
const char *hostSystemIpAddress,
|
|
esxUtil_ParsedUri *parsedUri)
|
|
{
|
|
int result = -1;
|
|
char ipAddress[NI_MAXHOST] = "";
|
|
char *username = NULL;
|
|
char *password = NULL;
|
|
char *url = NULL;
|
|
|
|
if (hostSystemIpAddress == NULL &&
|
|
(parsedUri->path_datacenter == NULL ||
|
|
parsedUri->path_computeResource == NULL)) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Path has to specify the datacenter and compute resource"));
|
|
return -1;
|
|
}
|
|
|
|
if (esxUtil_ResolveHostname(hostname, ipAddress, NI_MAXHOST) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (predefinedUsername != NULL) {
|
|
username = strdup(predefinedUsername);
|
|
|
|
if (username == NULL) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
username = virRequestUsername(auth, "administrator", hostname);
|
|
|
|
if (username == NULL) {
|
|
ESX_ERROR(VIR_ERR_AUTH_FAILED, "%s", _("Username request failed"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
password = virRequestPassword(auth, username, hostname);
|
|
|
|
if (password == NULL) {
|
|
ESX_ERROR(VIR_ERR_AUTH_FAILED, "%s", _("Password request failed"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virAsprintf(&url, "%s://%s:%d/sdk", priv->transport, hostname,
|
|
port) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_Context_Alloc(&priv->vCenter) < 0 ||
|
|
esxVI_Context_Connect(priv->vCenter, url, ipAddress, username,
|
|
password, parsedUri) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (priv->vCenter->productVersion != esxVI_ProductVersion_VPX25 &&
|
|
priv->vCenter->productVersion != esxVI_ProductVersion_VPX40 &&
|
|
priv->vCenter->productVersion != esxVI_ProductVersion_VPX41 &&
|
|
priv->vCenter->productVersion != esxVI_ProductVersion_VPX4x) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("%s is neither a vCenter 2.5 server nor a vCenter "
|
|
"4.x server"), hostname);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hostSystemIpAddress != NULL) {
|
|
if (esxVI_Context_LookupObjectsByHostSystemIp(priv->vCenter,
|
|
hostSystemIpAddress) < 0) {
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
if (esxVI_Context_LookupObjectsByPath(priv->vCenter, parsedUri) < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(password);
|
|
VIR_FREE(username);
|
|
VIR_FREE(url);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* URI format: {vpx|esx|gsx}://[<username>@]<hostname>[:<port>]/[<path>][?<query parameter> ...]
|
|
* <path> = <datacenter>/<computeresource>[/<hostsystem>]
|
|
*
|
|
* If no port is specified the default port is set dependent on the scheme and
|
|
* transport parameter:
|
|
* - vpx+http 80
|
|
* - vpx+https 443
|
|
* - esx+http 80
|
|
* - esx+https 443
|
|
* - gsx+http 8222
|
|
* - gsx+https 8333
|
|
*
|
|
* For a vpx:// connection <path> references a host managed by the vCenter.
|
|
* In case the host is part of a cluster then <computeresource> is the cluster
|
|
* name. Otherwise <computeresource> and <hostsystem> are equal and the later
|
|
* can be omitted.
|
|
*
|
|
* Optional query parameters:
|
|
* - transport={http|https}
|
|
* - vcenter={<vcenter>|*} only useful for an esx:// connection
|
|
* - no_verify={0|1}
|
|
* - auto_answer={0|1}
|
|
* - proxy=[{http|socks|socks4|socks4a|socks5}://]<hostname>[:<port>]
|
|
*
|
|
* If no transport parameter is specified https is used.
|
|
*
|
|
* The vcenter parameter is only necessary for migration, because the vCenter
|
|
* server is in charge to initiate a migration between two ESX hosts. The
|
|
* vcenter parameter can be set to an explicitly hostname or to *. If set to *,
|
|
* the driver will check if the ESX host is managed by a vCenter and connect to
|
|
* it. If the ESX host is not managed by a vCenter an error is reported.
|
|
*
|
|
* If the no_verify parameter is set to 1, this disables libcurl client checks
|
|
* of the server's certificate. The default value it 0.
|
|
*
|
|
* If the auto_answer parameter is set to 1, the driver will respond to all
|
|
* virtual machine questions with the default answer, otherwise virtual machine
|
|
* questions will be reported as errors. The default value it 0.
|
|
*
|
|
* The proxy parameter allows to specify a proxy for to be used by libcurl.
|
|
* The default for the optional <type> part is http and socks is synonymous for
|
|
* socks5. The optional <port> part allows to override the default port 1080.
|
|
*/
|
|
static virDrvOpenStatus
|
|
esxOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags ATTRIBUTE_UNUSED)
|
|
{
|
|
virDrvOpenStatus result = VIR_DRV_OPEN_ERROR;
|
|
esxPrivate *priv = NULL;
|
|
esxUtil_ParsedUri *parsedUri = NULL;
|
|
char *potentialVCenterIpAddress = NULL;
|
|
char vCenterIpAddress[NI_MAXHOST] = "";
|
|
|
|
/* Decline if the URI is NULL or the scheme is not one of {vpx|esx|gsx} */
|
|
if (conn->uri == NULL || conn->uri->scheme == NULL ||
|
|
(STRCASENEQ(conn->uri->scheme, "vpx") &&
|
|
STRCASENEQ(conn->uri->scheme, "esx") &&
|
|
STRCASENEQ(conn->uri->scheme, "gsx"))) {
|
|
return VIR_DRV_OPEN_DECLINED;
|
|
}
|
|
|
|
/* Decline URIs without server part, or missing auth */
|
|
if (conn->uri->server == NULL || auth == NULL || auth->cb == NULL) {
|
|
return VIR_DRV_OPEN_DECLINED;
|
|
}
|
|
|
|
/* Allocate per-connection private data */
|
|
if (VIR_ALLOC(priv) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxUtil_ParseUri(&parsedUri, conn->uri) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
priv->transport = parsedUri->transport;
|
|
parsedUri->transport = NULL;
|
|
|
|
priv->maxVcpus = -1;
|
|
priv->supportsVMotion = esxVI_Boolean_Undefined;
|
|
priv->supportsLongMode = esxVI_Boolean_Undefined;
|
|
priv->autoAnswer = parsedUri->autoAnswer ? esxVI_Boolean_True
|
|
: esxVI_Boolean_False;
|
|
priv->usedCpuTimeCounterId = -1;
|
|
|
|
/*
|
|
* Set the port dependent on the transport protocol if no port is
|
|
* specified. This allows us to rely on the port parameter being
|
|
* correctly set when building URIs later on, without the need to
|
|
* distinguish between the situations port == 0 and port != 0
|
|
*/
|
|
if (conn->uri->port == 0) {
|
|
if (STRCASEEQ(conn->uri->scheme, "vpx") ||
|
|
STRCASEEQ(conn->uri->scheme, "esx")) {
|
|
if (STRCASEEQ(priv->transport, "https")) {
|
|
conn->uri->port = 443;
|
|
} else {
|
|
conn->uri->port = 80;
|
|
}
|
|
} else { /* GSX */
|
|
if (STRCASEEQ(priv->transport, "https")) {
|
|
conn->uri->port = 8333;
|
|
} else {
|
|
conn->uri->port = 8222;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (STRCASEEQ(conn->uri->scheme, "esx") ||
|
|
STRCASEEQ(conn->uri->scheme, "gsx")) {
|
|
/* Connect to host */
|
|
if (esxConnectToHost(priv, auth, conn->uri->server, conn->uri->port,
|
|
conn->uri->user, parsedUri,
|
|
STRCASEEQ(conn->uri->scheme, "esx")
|
|
? esxVI_ProductVersion_ESX
|
|
: esxVI_ProductVersion_GSX,
|
|
&potentialVCenterIpAddress) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Connect to vCenter */
|
|
if (parsedUri->vCenter != NULL) {
|
|
if (STREQ(parsedUri->vCenter, "*")) {
|
|
if (potentialVCenterIpAddress == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("This host is not managed by a vCenter"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virStrcpyStatic(vCenterIpAddress,
|
|
potentialVCenterIpAddress) == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("vCenter IP address %s too big for destination"),
|
|
potentialVCenterIpAddress);
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
if (esxUtil_ResolveHostname(parsedUri->vCenter,
|
|
vCenterIpAddress, NI_MAXHOST) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (potentialVCenterIpAddress != NULL &&
|
|
STRNEQ(vCenterIpAddress, potentialVCenterIpAddress)) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("This host is managed by a vCenter with IP "
|
|
"address %s, but a mismachting vCenter '%s' "
|
|
"(%s) has been specified"),
|
|
potentialVCenterIpAddress, parsedUri->vCenter,
|
|
vCenterIpAddress);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (esxConnectToVCenter(priv, auth, vCenterIpAddress,
|
|
conn->uri->port, NULL,
|
|
priv->host->ipAddress, parsedUri) < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
priv->primary = priv->host;
|
|
} else { /* VPX */
|
|
/* Connect to vCenter */
|
|
if (esxConnectToVCenter(priv, auth, conn->uri->server, conn->uri->port,
|
|
conn->uri->user, NULL, parsedUri) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
priv->primary = priv->vCenter;
|
|
}
|
|
|
|
conn->privateData = priv;
|
|
|
|
/* Setup capabilities */
|
|
priv->caps = esxCapsInit(priv);
|
|
|
|
if (priv->caps == NULL) {
|
|
goto cleanup;
|
|
}
|
|
|
|
result = VIR_DRV_OPEN_SUCCESS;
|
|
|
|
cleanup:
|
|
if (result == VIR_DRV_OPEN_ERROR && priv != NULL) {
|
|
esxVI_Context_Free(&priv->host);
|
|
esxVI_Context_Free(&priv->vCenter);
|
|
|
|
virCapabilitiesFree(priv->caps);
|
|
|
|
VIR_FREE(priv->transport);
|
|
VIR_FREE(priv);
|
|
}
|
|
|
|
esxUtil_FreeParsedUri(&parsedUri);
|
|
VIR_FREE(potentialVCenterIpAddress);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxClose(virConnectPtr conn)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
int result = 0;
|
|
|
|
if (priv->host != NULL) {
|
|
if (esxVI_EnsureSession(priv->host) < 0 ||
|
|
esxVI_Logout(priv->host) < 0) {
|
|
result = -1;
|
|
}
|
|
|
|
esxVI_Context_Free(&priv->host);
|
|
}
|
|
|
|
if (priv->vCenter != NULL) {
|
|
if (esxVI_EnsureSession(priv->vCenter) < 0 ||
|
|
esxVI_Logout(priv->vCenter) < 0) {
|
|
result = -1;
|
|
}
|
|
|
|
esxVI_Context_Free(&priv->vCenter);
|
|
}
|
|
|
|
virCapabilitiesFree(priv->caps);
|
|
|
|
VIR_FREE(priv->transport);
|
|
VIR_FREE(priv);
|
|
|
|
conn->privateData = NULL;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static esxVI_Boolean
|
|
esxSupportsVMotion(esxPrivate *priv)
|
|
{
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostSystem = NULL;
|
|
|
|
if (priv->supportsVMotion != esxVI_Boolean_Undefined) {
|
|
return priv->supportsVMotion;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return esxVI_Boolean_Undefined;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"capability.vmotionSupported") < 0 ||
|
|
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
|
|
&hostSystem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hostSystem == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not retrieve the HostSystem object"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_GetBoolean(hostSystem, "capability.vmotionSupported",
|
|
&priv->supportsVMotion,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
/*
|
|
* If we goto cleanup in case of an error then priv->supportsVMotion is
|
|
* still esxVI_Boolean_Undefined, therefore we don't need to set it.
|
|
*/
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostSystem);
|
|
|
|
return priv->supportsVMotion;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxSupportsFeature(virConnectPtr conn, int feature)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_Boolean supportsVMotion = esxVI_Boolean_Undefined;
|
|
|
|
switch (feature) {
|
|
case VIR_DRV_FEATURE_MIGRATION_V1:
|
|
supportsVMotion = esxSupportsVMotion(priv);
|
|
|
|
if (supportsVMotion == esxVI_Boolean_Undefined) {
|
|
return -1;
|
|
}
|
|
|
|
/* Migration is only possible via a vCenter and if VMotion is enabled */
|
|
return priv->vCenter != NULL &&
|
|
supportsVMotion == esxVI_Boolean_True ? 1 : 0;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
|
esxGetType(virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
{
|
|
return "ESX";
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxGetVersion(virConnectPtr conn, unsigned long *version)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
|
|
if (virParseVersionString(priv->primary->service->about->version,
|
|
version) < 0) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse version number from '%s'"),
|
|
priv->primary->service->about->version);
|
|
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxGetHostname(virConnectPtr conn)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostSystem = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
const char *hostName = NULL;
|
|
const char *domainName = NULL;
|
|
char *complete = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList
|
|
(&propertyNameList,
|
|
"config.network.dnsConfig.hostName\0"
|
|
"config.network.dnsConfig.domainName\0") < 0 ||
|
|
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
|
|
&hostSystem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hostSystem == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not retrieve the HostSystem object"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name,
|
|
"config.network.dnsConfig.hostName")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_String) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
hostName = dynamicProperty->val->string;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"config.network.dnsConfig.domainName")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_String) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
domainName = dynamicProperty->val->string;
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
if (hostName == NULL || strlen(hostName) < 1) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Missing or empty 'hostName' property"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (domainName == NULL || strlen(domainName) < 1) {
|
|
complete = strdup(hostName);
|
|
|
|
if (complete == NULL) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
if (virAsprintf(&complete, "%s.%s", hostName, domainName) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
/*
|
|
* If we goto cleanup in case of an error then complete is still NULL,
|
|
* either strdup returned NULL or virAsprintf failed. When virAsprintf
|
|
* fails it guarantees setting complete to NULL
|
|
*/
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostSystem);
|
|
|
|
return complete;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxNodeGetInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostSystem = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
int64_t cpuInfo_hz = 0;
|
|
int16_t cpuInfo_numCpuCores = 0;
|
|
int16_t cpuInfo_numCpuPackages = 0;
|
|
int16_t cpuInfo_numCpuThreads = 0;
|
|
int64_t memorySize = 0;
|
|
int32_t numaInfo_numNodes = 0;
|
|
char *ptr = NULL;
|
|
|
|
memset(nodeinfo, 0, sizeof (*nodeinfo));
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"hardware.cpuInfo.hz\0"
|
|
"hardware.cpuInfo.numCpuCores\0"
|
|
"hardware.cpuInfo.numCpuPackages\0"
|
|
"hardware.cpuInfo.numCpuThreads\0"
|
|
"hardware.memorySize\0"
|
|
"hardware.numaInfo.numNodes\0"
|
|
"summary.hardware.cpuModel\0") < 0 ||
|
|
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
|
|
&hostSystem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hostSystem == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not retrieve the HostSystem object"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name, "hardware.cpuInfo.hz")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Long) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
cpuInfo_hz = dynamicProperty->val->int64;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"hardware.cpuInfo.numCpuCores")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Short) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
cpuInfo_numCpuCores = dynamicProperty->val->int16;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"hardware.cpuInfo.numCpuPackages")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Short) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
cpuInfo_numCpuPackages = dynamicProperty->val->int16;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"hardware.cpuInfo.numCpuThreads")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Short) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
cpuInfo_numCpuThreads = dynamicProperty->val->int16;
|
|
} else if (STREQ(dynamicProperty->name, "hardware.memorySize")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Long) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
memorySize = dynamicProperty->val->int64;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"hardware.numaInfo.numNodes")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Int) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
numaInfo_numNodes = dynamicProperty->val->int32;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"summary.hardware.cpuModel")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_String) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ptr = dynamicProperty->val->string;
|
|
|
|
/* Strip the string to fit more relevant information in 32 chars */
|
|
while (*ptr != '\0') {
|
|
if (STRPREFIX(ptr, " ")) {
|
|
memmove(ptr, ptr + 1, strlen(ptr + 1) + 1);
|
|
continue;
|
|
} else if (STRPREFIX(ptr, "(R)") || STRPREFIX(ptr, "(C)")) {
|
|
memmove(ptr, ptr + 3, strlen(ptr + 3) + 1);
|
|
continue;
|
|
} else if (STRPREFIX(ptr, "(TM)")) {
|
|
memmove(ptr, ptr + 4, strlen(ptr + 4) + 1);
|
|
continue;
|
|
}
|
|
|
|
++ptr;
|
|
}
|
|
|
|
if (virStrncpy(nodeinfo->model, dynamicProperty->val->string,
|
|
sizeof(nodeinfo->model) - 1,
|
|
sizeof(nodeinfo->model)) == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("CPU Model %s too long for destination"),
|
|
dynamicProperty->val->string);
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
nodeinfo->memory = memorySize / 1024; /* Scale from bytes to kilobytes */
|
|
nodeinfo->cpus = cpuInfo_numCpuCores;
|
|
nodeinfo->mhz = cpuInfo_hz / (1000 * 1000); /* Scale from hz to mhz */
|
|
nodeinfo->nodes = numaInfo_numNodes;
|
|
nodeinfo->sockets = cpuInfo_numCpuPackages;
|
|
nodeinfo->cores = cpuInfo_numCpuPackages > 0
|
|
? cpuInfo_numCpuCores / cpuInfo_numCpuPackages
|
|
: 0;
|
|
nodeinfo->threads = cpuInfo_numCpuCores > 0
|
|
? cpuInfo_numCpuThreads / cpuInfo_numCpuCores
|
|
: 0;
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostSystem);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxGetCapabilities(virConnectPtr conn)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
char *xml = virCapabilitiesFormatXML(priv->caps);
|
|
|
|
if (xml == NULL) {
|
|
virReportOOMError();
|
|
return NULL;
|
|
}
|
|
|
|
return xml;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxListDomains(virConnectPtr conn, int *ids, int maxids)
|
|
{
|
|
bool success = false;
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_ObjectContent *virtualMachineList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
int count = 0;
|
|
|
|
if (ids == NULL || maxids < 0) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument"));
|
|
return -1;
|
|
}
|
|
|
|
if (maxids == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineList(priv->primary, propertyNameList,
|
|
&virtualMachineList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (virtualMachine = virtualMachineList; virtualMachine != NULL;
|
|
virtualMachine = virtualMachine->_next) {
|
|
if (esxVI_GetVirtualMachinePowerState(virtualMachine,
|
|
&powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
|
|
continue;
|
|
}
|
|
|
|
if (esxUtil_ParseVirtualMachineIDString(virtualMachine->obj->value,
|
|
&ids[count]) < 0 ||
|
|
ids[count] <= 0) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to parse positive integer from '%s'"),
|
|
virtualMachine->obj->value);
|
|
goto cleanup;
|
|
}
|
|
|
|
count++;
|
|
|
|
if (count >= maxids) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
success = true;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachineList);
|
|
|
|
return success ? count : -1;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxNumberOfDomains(virConnectPtr conn)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return esxVI_LookupNumberOfDomainsByPowerState
|
|
(priv->primary, esxVI_VirtualMachinePowerState_PoweredOn,
|
|
esxVI_Boolean_False);
|
|
}
|
|
|
|
|
|
|
|
static virDomainPtr
|
|
esxDomainLookupByID(virConnectPtr conn, int id)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachineList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
int id_candidate = -1;
|
|
char *name_candidate = NULL;
|
|
unsigned char uuid_candidate[VIR_UUID_BUFLEN];
|
|
virDomainPtr domain = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"configStatus\0"
|
|
"name\0"
|
|
"runtime.powerState\0"
|
|
"config.uuid\0") < 0 ||
|
|
esxVI_LookupVirtualMachineList(priv->primary, propertyNameList,
|
|
&virtualMachineList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (virtualMachine = virtualMachineList; virtualMachine != NULL;
|
|
virtualMachine = virtualMachine->_next) {
|
|
if (esxVI_GetVirtualMachinePowerState(virtualMachine,
|
|
&powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Only running/suspended domains have an ID != -1 */
|
|
if (powerState == esxVI_VirtualMachinePowerState_PoweredOff) {
|
|
continue;
|
|
}
|
|
|
|
VIR_FREE(name_candidate);
|
|
|
|
if (esxVI_GetVirtualMachineIdentity(virtualMachine,
|
|
&id_candidate, &name_candidate,
|
|
uuid_candidate) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (id != id_candidate) {
|
|
continue;
|
|
}
|
|
|
|
domain = virGetDomain(conn, name_candidate, uuid_candidate);
|
|
|
|
if (domain == NULL) {
|
|
goto cleanup;
|
|
}
|
|
|
|
domain->id = id;
|
|
|
|
break;
|
|
}
|
|
|
|
if (domain == NULL) {
|
|
ESX_ERROR(VIR_ERR_NO_DOMAIN, _("No domain with ID %d"), id);
|
|
}
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachineList);
|
|
VIR_FREE(name_candidate);
|
|
|
|
return domain;
|
|
}
|
|
|
|
|
|
|
|
static virDomainPtr
|
|
esxDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
int id = -1;
|
|
char *name = NULL;
|
|
virDomainPtr domain = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"name\0"
|
|
"runtime.powerState\0") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, uuid, propertyNameList,
|
|
&virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_GetVirtualMachineIdentity(virtualMachine, &id, &name, NULL) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
domain = virGetDomain(conn, name, uuid);
|
|
|
|
if (domain == NULL) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Only running/suspended virtual machines have an ID != -1 */
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
|
|
domain->id = id;
|
|
} else {
|
|
domain->id = -1;
|
|
}
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
VIR_FREE(name);
|
|
|
|
return domain;
|
|
}
|
|
|
|
|
|
|
|
static virDomainPtr
|
|
esxDomainLookupByName(virConnectPtr conn, const char *name)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
int id = -1;
|
|
unsigned char uuid[VIR_UUID_BUFLEN];
|
|
virDomainPtr domain = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"configStatus\0"
|
|
"runtime.powerState\0"
|
|
"config.uuid\0") < 0 ||
|
|
esxVI_LookupVirtualMachineByName(priv->primary, name, propertyNameList,
|
|
&virtualMachine,
|
|
esxVI_Occurrence_OptionalItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virtualMachine == NULL) {
|
|
ESX_ERROR(VIR_ERR_NO_DOMAIN, _("No domain with name '%s'"), name);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_GetVirtualMachineIdentity(virtualMachine, &id, NULL, uuid) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
domain = virGetDomain(conn, name, uuid);
|
|
|
|
if (domain == NULL) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Only running/suspended virtual machines have an ID != -1 */
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
|
|
domain->id = id;
|
|
} else {
|
|
domain->id = -1;
|
|
}
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
|
|
return domain;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSuspend(virDomainPtr domain)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, propertyNameList, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not powered on"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_SuspendVM_Task(priv->primary, virtualMachine->obj, &task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Could not suspend domain: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainResume(virDomainPtr domain)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, propertyNameList, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_Suspended) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not suspended"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_PowerOnVM_Task(priv->primary, virtualMachine->obj, NULL,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Could not resume domain: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainShutdown(virDomainPtr domain)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
propertyNameList, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not powered on"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_ShutdownGuest(priv->primary, virtualMachine->obj) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
propertyNameList, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not powered on"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_RebootGuest(priv->primary, virtualMachine->obj) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainDestroy(virDomainPtr domain)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_Context *ctx = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
if (priv->vCenter != NULL) {
|
|
ctx = priv->vCenter;
|
|
} else {
|
|
ctx = priv->host;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(ctx) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(ctx, domain->uuid, propertyNameList, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not powered on"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_PowerOffVM_Task(ctx, virtualMachine->obj, &task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(ctx, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Could not destroy domain: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
domain->id = -1;
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxDomainGetOSType(virDomainPtr domain ATTRIBUTE_UNUSED)
|
|
{
|
|
char *osType = strdup("hvm");
|
|
|
|
if (osType == NULL) {
|
|
virReportOOMError();
|
|
return NULL;
|
|
}
|
|
|
|
return osType;
|
|
}
|
|
|
|
|
|
|
|
static unsigned long
|
|
esxDomainGetMaxMemory(virDomainPtr domain)
|
|
{
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
unsigned long memoryMB = 0;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"config.hardware.memoryMB") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
propertyNameList, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (dynamicProperty = virtualMachine->propSet; dynamicProperty != NULL;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name, "config.hardware.memoryMB")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Int) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (dynamicProperty->val->int32 < 0) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Got invalid memory size %d"),
|
|
dynamicProperty->val->int32);
|
|
} else {
|
|
memoryMB = dynamicProperty->val->int32;
|
|
}
|
|
|
|
break;
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
|
|
return memoryMB * 1024; /* Scale from megabyte to kilobyte */
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSetMaxMemory(virDomainPtr domain, unsigned long memory)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
esxVI_VirtualMachineConfigSpec *spec = NULL;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, propertyNameList, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not powered off"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_VirtualMachineConfigSpec_Alloc(&spec) < 0 ||
|
|
esxVI_Long_Alloc(&spec->memoryMB) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
spec->memoryMB->value =
|
|
VIR_DIV_UP(memory, 1024); /* Scale from kilobytes to megabytes */
|
|
|
|
if (esxVI_ReconfigVM_Task(priv->primary, virtualMachine->obj, spec,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not set max-memory to %lu kilobytes: %s"), memory,
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_VirtualMachineConfigSpec_Free(&spec);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSetMemory(virDomainPtr domain, unsigned long memory)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachineConfigSpec *spec = NULL;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, NULL, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_VirtualMachineConfigSpec_Alloc(&spec) < 0 ||
|
|
esxVI_ResourceAllocationInfo_Alloc(&spec->memoryAllocation) < 0 ||
|
|
esxVI_Long_Alloc(&spec->memoryAllocation->limit) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
spec->memoryAllocation->limit->value =
|
|
VIR_DIV_UP(memory, 1024); /* Scale from kilobytes to megabytes */
|
|
|
|
if (esxVI_ReconfigVM_Task(priv->primary, virtualMachine->obj, spec,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not set memory to %lu kilobytes: %s"), memory,
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_VirtualMachineConfigSpec_Free(&spec);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
int64_t memory_limit = -1;
|
|
esxVI_PerfMetricId *perfMetricId = NULL;
|
|
esxVI_PerfMetricId *perfMetricIdList = NULL;
|
|
esxVI_Int *counterId = NULL;
|
|
esxVI_Int *counterIdList = NULL;
|
|
esxVI_PerfCounterInfo *perfCounterInfo = NULL;
|
|
esxVI_PerfCounterInfo *perfCounterInfoList = NULL;
|
|
esxVI_PerfQuerySpec *querySpec = NULL;
|
|
esxVI_PerfEntityMetricBase *perfEntityMetricBase = NULL;
|
|
esxVI_PerfEntityMetricBase *perfEntityMetricBaseList = NULL;
|
|
esxVI_PerfEntityMetric *perfEntityMetric = NULL;
|
|
esxVI_PerfMetricIntSeries *perfMetricIntSeries = NULL;
|
|
esxVI_Long *value = NULL;
|
|
|
|
memset(info, 0, sizeof (*info));
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"runtime.powerState\0"
|
|
"config.hardware.memoryMB\0"
|
|
"config.hardware.numCPU\0"
|
|
"config.memoryAllocation.limit\0") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
propertyNameList, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
info->state = VIR_DOMAIN_NOSTATE;
|
|
|
|
for (dynamicProperty = virtualMachine->propSet; dynamicProperty != NULL;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name, "runtime.powerState")) {
|
|
if (esxVI_VirtualMachinePowerState_CastFromAnyType
|
|
(dynamicProperty->val, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
info->state = esxVI_VirtualMachinePowerState_ConvertToLibvirt
|
|
(powerState);
|
|
} else if (STREQ(dynamicProperty->name, "config.hardware.memoryMB")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Int) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
info->maxMem = dynamicProperty->val->int32 * 1024; /* Scale from megabyte to kilobyte */
|
|
} else if (STREQ(dynamicProperty->name, "config.hardware.numCPU")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Int) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
info->nrVirtCpu = dynamicProperty->val->int32;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"config.memoryAllocation.limit")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Long) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
memory_limit = dynamicProperty->val->int64;
|
|
|
|
if (memory_limit > 0) {
|
|
memory_limit *= 1024; /* Scale from megabyte to kilobyte */
|
|
}
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
/* memory_limit < 0 means no memory limit is set */
|
|
info->memory = memory_limit < 0 ? info->maxMem : memory_limit;
|
|
|
|
/* Verify the cached 'used CPU time' performance counter ID */
|
|
/* FIXME: Currently no host for a vpx:// connection */
|
|
if (priv->host != NULL) {
|
|
if (info->state == VIR_DOMAIN_RUNNING && priv->usedCpuTimeCounterId >= 0) {
|
|
if (esxVI_Int_Alloc(&counterId) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
counterId->value = priv->usedCpuTimeCounterId;
|
|
|
|
if (esxVI_Int_AppendToList(&counterIdList, counterId) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_QueryPerfCounter(priv->host, counterIdList,
|
|
&perfCounterInfo) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (STRNEQ(perfCounterInfo->groupInfo->key, "cpu") ||
|
|
STRNEQ(perfCounterInfo->nameInfo->key, "used") ||
|
|
STRNEQ(perfCounterInfo->unitInfo->key, "millisecond")) {
|
|
VIR_DEBUG("Cached usedCpuTimeCounterId %d is invalid",
|
|
priv->usedCpuTimeCounterId);
|
|
|
|
priv->usedCpuTimeCounterId = -1;
|
|
}
|
|
|
|
esxVI_Int_Free(&counterIdList);
|
|
esxVI_PerfCounterInfo_Free(&perfCounterInfo);
|
|
}
|
|
|
|
/*
|
|
* Query the PerformanceManager for the 'used CPU time' performance
|
|
* counter ID and cache it, if it's not already cached.
|
|
*/
|
|
if (info->state == VIR_DOMAIN_RUNNING && priv->usedCpuTimeCounterId < 0) {
|
|
if (esxVI_QueryAvailablePerfMetric(priv->host, virtualMachine->obj,
|
|
NULL, NULL, NULL,
|
|
&perfMetricIdList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (perfMetricId = perfMetricIdList; perfMetricId != NULL;
|
|
perfMetricId = perfMetricId->_next) {
|
|
VIR_DEBUG("perfMetricId counterId %d, instance '%s'",
|
|
perfMetricId->counterId->value, perfMetricId->instance);
|
|
|
|
counterId = NULL;
|
|
|
|
if (esxVI_Int_DeepCopy(&counterId, perfMetricId->counterId) < 0 ||
|
|
esxVI_Int_AppendToList(&counterIdList, counterId) < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (esxVI_QueryPerfCounter(priv->host, counterIdList,
|
|
&perfCounterInfoList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (perfCounterInfo = perfCounterInfoList; perfCounterInfo != NULL;
|
|
perfCounterInfo = perfCounterInfo->_next) {
|
|
VIR_DEBUG("perfCounterInfo key %d, nameInfo '%s', groupInfo '%s', "
|
|
"unitInfo '%s', rollupType %d, statsType %d",
|
|
perfCounterInfo->key->value,
|
|
perfCounterInfo->nameInfo->key,
|
|
perfCounterInfo->groupInfo->key,
|
|
perfCounterInfo->unitInfo->key,
|
|
perfCounterInfo->rollupType,
|
|
perfCounterInfo->statsType);
|
|
|
|
if (STREQ(perfCounterInfo->groupInfo->key, "cpu") &&
|
|
STREQ(perfCounterInfo->nameInfo->key, "used") &&
|
|
STREQ(perfCounterInfo->unitInfo->key, "millisecond")) {
|
|
priv->usedCpuTimeCounterId = perfCounterInfo->key->value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (priv->usedCpuTimeCounterId < 0) {
|
|
VIR_WARN0("Could not find 'used CPU time' performance counter");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Query the PerformanceManager for the 'used CPU time' performance
|
|
* counter value.
|
|
*/
|
|
if (info->state == VIR_DOMAIN_RUNNING && priv->usedCpuTimeCounterId >= 0) {
|
|
VIR_DEBUG("usedCpuTimeCounterId %d BEGIN", priv->usedCpuTimeCounterId);
|
|
|
|
if (esxVI_PerfQuerySpec_Alloc(&querySpec) < 0 ||
|
|
esxVI_Int_Alloc(&querySpec->maxSample) < 0 ||
|
|
esxVI_PerfMetricId_Alloc(&querySpec->metricId) < 0 ||
|
|
esxVI_Int_Alloc(&querySpec->metricId->counterId) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
querySpec->entity = virtualMachine->obj;
|
|
querySpec->maxSample->value = 1;
|
|
querySpec->metricId->counterId->value = priv->usedCpuTimeCounterId;
|
|
querySpec->metricId->instance = (char *)"";
|
|
querySpec->format = (char *)"normal";
|
|
|
|
if (esxVI_QueryPerf(priv->host, querySpec,
|
|
&perfEntityMetricBaseList) < 0) {
|
|
querySpec->entity = NULL;
|
|
querySpec->metricId->instance = NULL;
|
|
querySpec->format = NULL;
|
|
goto cleanup;
|
|
}
|
|
|
|
for (perfEntityMetricBase = perfEntityMetricBaseList;
|
|
perfEntityMetricBase != NULL;
|
|
perfEntityMetricBase = perfEntityMetricBase->_next) {
|
|
VIR_DEBUG0("perfEntityMetric ...");
|
|
|
|
perfEntityMetric =
|
|
esxVI_PerfEntityMetric_DynamicCast(perfEntityMetricBase);
|
|
|
|
if (perfEntityMetric == NULL) {
|
|
VIR_ERROR(_("QueryPerf returned object with unexpected type '%s'"),
|
|
esxVI_Type_ToString(perfEntityMetricBase->_type));
|
|
}
|
|
|
|
perfMetricIntSeries =
|
|
esxVI_PerfMetricIntSeries_DynamicCast(perfEntityMetric->value);
|
|
|
|
if (perfMetricIntSeries == NULL) {
|
|
VIR_ERROR(_("QueryPerf returned object with unexpected type '%s'"),
|
|
esxVI_Type_ToString(perfEntityMetric->value->_type));
|
|
}
|
|
|
|
for (; perfMetricIntSeries != NULL;
|
|
perfMetricIntSeries = perfMetricIntSeries->_next) {
|
|
VIR_DEBUG0("perfMetricIntSeries ...");
|
|
|
|
for (value = perfMetricIntSeries->value;
|
|
value != NULL;
|
|
value = value->_next) {
|
|
VIR_DEBUG("value %lld", (long long int)value->value);
|
|
}
|
|
}
|
|
}
|
|
|
|
querySpec->entity = NULL;
|
|
querySpec->metricId->instance = NULL;
|
|
querySpec->format = NULL;
|
|
|
|
VIR_DEBUG("usedCpuTimeCounterId %d END", priv->usedCpuTimeCounterId);
|
|
|
|
/*
|
|
* FIXME: Cannot map between realtive used-cpu-time and absolute
|
|
* info->cpuTime
|
|
*/
|
|
}
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_PerfMetricId_Free(&perfMetricIdList);
|
|
esxVI_Int_Free(&counterIdList);
|
|
esxVI_PerfCounterInfo_Free(&perfCounterInfoList);
|
|
esxVI_PerfQuerySpec_Free(&querySpec);
|
|
esxVI_PerfEntityMetricBase_Free(&perfEntityMetricBaseList);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSetVcpusFlags(virDomainPtr domain, unsigned int nvcpus,
|
|
unsigned int flags)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
int maxVcpus;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachineConfigSpec *spec = NULL;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
if (flags != VIR_DOMAIN_VCPU_LIVE) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, _("unsupported flags: (0x%x)"), flags);
|
|
return -1;
|
|
}
|
|
|
|
if (nvcpus < 1) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Requested number of virtual CPUs must at least be 1"));
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
maxVcpus = esxDomainGetMaxVcpus(domain);
|
|
|
|
if (maxVcpus < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (nvcpus > maxVcpus) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG,
|
|
_("Requested number of virtual CPUs is greater than max "
|
|
"allowable number of virtual CPUs for the domain: %d > %d"),
|
|
nvcpus, maxVcpus);
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, NULL, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_VirtualMachineConfigSpec_Alloc(&spec) < 0 ||
|
|
esxVI_Int_Alloc(&spec->numCPUs) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
spec->numCPUs->value = nvcpus;
|
|
|
|
if (esxVI_ReconfigVM_Task(priv->primary, virtualMachine->obj, spec,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not set number of virtual CPUs to %d: %s"), nvcpus,
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_VirtualMachineConfigSpec_Free(&spec);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSetVcpus(virDomainPtr domain, unsigned int nvcpus)
|
|
{
|
|
return esxDomainSetVcpusFlags(domain, nvcpus, VIR_DOMAIN_VCPU_LIVE);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainGetVcpusFlags(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostSystem = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
|
|
if (flags != (VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_MAXIMUM)) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, _("unsupported flags: (0x%x)"), flags);
|
|
return -1;
|
|
}
|
|
|
|
if (priv->maxVcpus > 0) {
|
|
return priv->maxVcpus;
|
|
}
|
|
|
|
priv->maxVcpus = -1;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"capability.maxSupportedVcpus") < 0 ||
|
|
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
|
|
&hostSystem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hostSystem == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not retrieve the HostSystem object"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name, "capability.maxSupportedVcpus")) {
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Int) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
priv->maxVcpus = dynamicProperty->val->int32;
|
|
break;
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostSystem);
|
|
|
|
return priv->maxVcpus;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainGetMaxVcpus(virDomainPtr domain)
|
|
{
|
|
return esxDomainGetVcpusFlags(domain, (VIR_DOMAIN_VCPU_LIVE |
|
|
VIR_DOMAIN_VCPU_MAXIMUM));
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxDomainDumpXML(virDomainPtr domain, int flags)
|
|
{
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
int id;
|
|
char *vmPathName = NULL;
|
|
char *datastoreName = NULL;
|
|
char *directoryName = NULL;
|
|
char *directoryAndFileName = NULL;
|
|
virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
|
char *url = NULL;
|
|
char *vmx = NULL;
|
|
virVMXContext ctx;
|
|
esxVMX_Data data;
|
|
virDomainDefPtr def = NULL;
|
|
char *xml = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"config.files.vmPathName\0"
|
|
"runtime.powerState\0") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
propertyNameList, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0 ||
|
|
esxVI_GetVirtualMachineIdentity(virtualMachine, &id, NULL, NULL) < 0 ||
|
|
esxVI_GetStringValue(virtualMachine, "config.files.vmPathName",
|
|
&vmPathName, esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxUtil_ParseDatastorePath(vmPathName, &datastoreName, &directoryName,
|
|
&directoryAndFileName) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
virBufferVSprintf(&buffer, "%s://%s:%d/folder/", priv->transport,
|
|
domain->conn->uri->server, domain->conn->uri->port);
|
|
virBufferURIEncodeString(&buffer, directoryAndFileName);
|
|
virBufferAddLit(&buffer, "?dcPath=");
|
|
virBufferURIEncodeString(&buffer, priv->primary->datacenter->name);
|
|
virBufferAddLit(&buffer, "&dsName=");
|
|
virBufferURIEncodeString(&buffer, datastoreName);
|
|
|
|
if (virBufferError(&buffer)) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
url = virBufferContentAndReset(&buffer);
|
|
|
|
if (esxVI_Context_DownloadFile(priv->primary, url, &vmx) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
data.ctx = priv->primary;
|
|
|
|
if (directoryName == NULL) {
|
|
if (virAsprintf(&data.datastorePathWithoutFileName, "[%s]",
|
|
datastoreName) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
if (virAsprintf(&data.datastorePathWithoutFileName, "[%s] %s",
|
|
datastoreName, directoryName) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ctx.opaque = &data;
|
|
ctx.parseFileName = esxParseVMXFileName;
|
|
ctx.formatFileName = NULL;
|
|
ctx.autodetectSCSIControllerModel = NULL;
|
|
|
|
def = virVMXParseConfig(&ctx, priv->caps, vmx);
|
|
|
|
if (def != NULL) {
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
|
|
def->id = id;
|
|
}
|
|
|
|
xml = virDomainDefFormat(def, flags);
|
|
}
|
|
|
|
cleanup:
|
|
if (url == NULL) {
|
|
virBufferFreeAndReset(&buffer);
|
|
}
|
|
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
VIR_FREE(datastoreName);
|
|
VIR_FREE(directoryName);
|
|
VIR_FREE(directoryAndFileName);
|
|
VIR_FREE(url);
|
|
VIR_FREE(data.datastorePathWithoutFileName);
|
|
VIR_FREE(vmx);
|
|
virDomainDefFree(def);
|
|
|
|
return xml;
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxDomainXMLFromNative(virConnectPtr conn, const char *nativeFormat,
|
|
const char *nativeConfig,
|
|
unsigned int flags ATTRIBUTE_UNUSED)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
virVMXContext ctx;
|
|
esxVMX_Data data;
|
|
virDomainDefPtr def = NULL;
|
|
char *xml = NULL;
|
|
|
|
if (STRNEQ(nativeFormat, "vmware-vmx")) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG,
|
|
_("Unsupported config format '%s'"), nativeFormat);
|
|
return NULL;
|
|
}
|
|
|
|
data.ctx = priv->primary;
|
|
data.datastorePathWithoutFileName = (char *)"[?] ?";
|
|
|
|
ctx.opaque = &data;
|
|
ctx.parseFileName = esxParseVMXFileName;
|
|
ctx.formatFileName = NULL;
|
|
ctx.autodetectSCSIControllerModel = NULL;
|
|
|
|
def = virVMXParseConfig(&ctx, priv->caps, nativeConfig);
|
|
|
|
if (def != NULL) {
|
|
xml = virDomainDefFormat(def, VIR_DOMAIN_XML_INACTIVE);
|
|
}
|
|
|
|
virDomainDefFree(def);
|
|
|
|
return xml;
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxDomainXMLToNative(virConnectPtr conn, const char *nativeFormat,
|
|
const char *domainXml,
|
|
unsigned int flags ATTRIBUTE_UNUSED)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
int virtualHW_version;
|
|
virVMXContext ctx;
|
|
esxVMX_Data data;
|
|
virDomainDefPtr def = NULL;
|
|
char *vmx = NULL;
|
|
|
|
if (STRNEQ(nativeFormat, "vmware-vmx")) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG,
|
|
_("Unsupported config format '%s'"), nativeFormat);
|
|
return NULL;
|
|
}
|
|
|
|
virtualHW_version = esxVI_ProductVersionToDefaultVirtualHWVersion
|
|
(priv->primary->productVersion);
|
|
|
|
if (virtualHW_version < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
def = virDomainDefParseString(priv->caps, domainXml, 0);
|
|
|
|
if (def == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data.ctx = priv->primary;
|
|
data.datastorePathWithoutFileName = NULL;
|
|
|
|
ctx.opaque = &data;
|
|
ctx.parseFileName = NULL;
|
|
ctx.formatFileName = esxFormatVMXFileName;
|
|
ctx.autodetectSCSIControllerModel = esxAutodetectSCSIControllerModel;
|
|
|
|
vmx = virVMXFormatConfig(&ctx, priv->caps, def, virtualHW_version);
|
|
|
|
virDomainDefFree(def);
|
|
|
|
return vmx;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxListDefinedDomains(virConnectPtr conn, char **const names, int maxnames)
|
|
{
|
|
bool success = false;
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachineList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
int count = 0;
|
|
int i;
|
|
|
|
if (names == NULL || maxnames < 0) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument"));
|
|
return -1;
|
|
}
|
|
|
|
if (maxnames == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"name\0"
|
|
"runtime.powerState\0") < 0 ||
|
|
esxVI_LookupVirtualMachineList(priv->primary, propertyNameList,
|
|
&virtualMachineList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (virtualMachine = virtualMachineList; virtualMachine != NULL;
|
|
virtualMachine = virtualMachine->_next) {
|
|
if (esxVI_GetVirtualMachinePowerState(virtualMachine,
|
|
&powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState == esxVI_VirtualMachinePowerState_PoweredOn) {
|
|
continue;
|
|
}
|
|
|
|
names[count] = NULL;
|
|
|
|
if (esxVI_GetVirtualMachineIdentity(virtualMachine, NULL, &names[count],
|
|
NULL) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
++count;
|
|
|
|
if (count >= maxnames) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
success = true;
|
|
|
|
cleanup:
|
|
if (! success) {
|
|
for (i = 0; i < count; ++i) {
|
|
VIR_FREE(names[i]);
|
|
}
|
|
|
|
count = -1;
|
|
}
|
|
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachineList);
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxNumberOfDefinedDomains(virConnectPtr conn)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return esxVI_LookupNumberOfDomainsByPowerState
|
|
(priv->primary, esxVI_VirtualMachinePowerState_PoweredOn,
|
|
esxVI_Boolean_True);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainCreateWithFlags(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
int id = -1;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, propertyNameList, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0 ||
|
|
esxVI_GetVirtualMachineIdentity(virtualMachine, &id, NULL, NULL) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not powered off"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_PowerOnVM_Task(priv->primary, virtualMachine->obj, NULL,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Could not start domain: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
domain->id = id;
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainCreate(virDomainPtr domain)
|
|
{
|
|
return esxDomainCreateWithFlags(domain, 0);
|
|
}
|
|
|
|
|
|
|
|
static virDomainPtr
|
|
esxDomainDefineXML(virConnectPtr conn, const char *xml)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
virDomainDefPtr def = NULL;
|
|
char *vmx = NULL;
|
|
int i;
|
|
virDomainDiskDefPtr disk = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
int virtualHW_version;
|
|
virVMXContext ctx;
|
|
esxVMX_Data data;
|
|
char *datastoreName = NULL;
|
|
char *directoryName = NULL;
|
|
char *escapedName = NULL;
|
|
virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
|
char *url = NULL;
|
|
char *datastoreRelatedPath = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostSystem = NULL;
|
|
esxVI_ManagedObjectReference *resourcePool = NULL;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
virDomainPtr domain = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Parse domain XML */
|
|
def = virDomainDefParseString(priv->caps, xml,
|
|
VIR_DOMAIN_XML_INACTIVE);
|
|
|
|
if (def == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Check if an existing domain should be edited */
|
|
if (esxVI_LookupVirtualMachineByUuid(priv->primary, def->uuid, NULL,
|
|
&virtualMachine,
|
|
esxVI_Occurrence_OptionalItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virtualMachine == NULL &&
|
|
esxVI_LookupVirtualMachineByName(priv->primary, def->name, NULL,
|
|
&virtualMachine,
|
|
esxVI_Occurrence_OptionalItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virtualMachine != NULL) {
|
|
/* FIXME */
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Domain already exists, editing existing domains is not "
|
|
"supported yet"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Build VMX from domain XML */
|
|
virtualHW_version = esxVI_ProductVersionToDefaultVirtualHWVersion
|
|
(priv->primary->productVersion);
|
|
|
|
if (virtualHW_version < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
data.ctx = priv->primary;
|
|
data.datastorePathWithoutFileName = NULL;
|
|
|
|
ctx.opaque = &data;
|
|
ctx.parseFileName = NULL;
|
|
ctx.formatFileName = esxFormatVMXFileName;
|
|
ctx.autodetectSCSIControllerModel = esxAutodetectSCSIControllerModel;
|
|
|
|
vmx = virVMXFormatConfig(&ctx, priv->caps, def, virtualHW_version);
|
|
|
|
if (vmx == NULL) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Build VMX datastore URL. Use the source of the first file-based harddisk
|
|
* to deduce the datastore and path for the VMX file. Don't just use the
|
|
* first disk, because it may be CDROM disk and ISO images are normaly not
|
|
* located in the virtual machine's directory. This approach to deduce the
|
|
* datastore isn't perfect but should work in the majority of cases.
|
|
*/
|
|
if (def->ndisks < 1) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Domain XML doesn't contain any disks, cannot deduce "
|
|
"datastore and path for VMX file"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < def->ndisks; ++i) {
|
|
if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
|
|
def->disks[i]->type == VIR_DOMAIN_DISK_TYPE_FILE) {
|
|
disk = def->disks[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (disk == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Domain XML doesn't contain any file-based harddisks, "
|
|
"cannot deduce datastore and path for VMX file"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (disk->src == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("First file-based harddisk has no source, cannot deduce "
|
|
"datastore and path for VMX file"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxUtil_ParseDatastorePath(disk->src, &datastoreName, &directoryName,
|
|
NULL) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (! virFileHasSuffix(disk->src, ".vmdk")) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Expecting source '%s' of first file-based harddisk to "
|
|
"be a VMDK image"), disk->src);
|
|
goto cleanup;
|
|
}
|
|
|
|
virBufferVSprintf(&buffer, "%s://%s:%d/folder/", priv->transport,
|
|
conn->uri->server, conn->uri->port);
|
|
|
|
if (directoryName != NULL) {
|
|
virBufferURIEncodeString(&buffer, directoryName);
|
|
virBufferAddChar(&buffer, '/');
|
|
}
|
|
|
|
escapedName = esxUtil_EscapeDatastoreItem(def->name);
|
|
|
|
if (escapedName == NULL) {
|
|
goto cleanup;
|
|
}
|
|
|
|
virBufferURIEncodeString(&buffer, escapedName);
|
|
virBufferAddLit(&buffer, ".vmx?dcPath=");
|
|
virBufferURIEncodeString(&buffer, priv->primary->datacenter->name);
|
|
virBufferAddLit(&buffer, "&dsName=");
|
|
virBufferURIEncodeString(&buffer, datastoreName);
|
|
|
|
if (virBufferError(&buffer)) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
url = virBufferContentAndReset(&buffer);
|
|
|
|
/* Check, if VMX file already exists */
|
|
/* FIXME */
|
|
|
|
/* Upload VMX file */
|
|
VIR_DEBUG("Uploading .vmx config, url='%s' vmx='%s'", url, vmx);
|
|
|
|
if (esxVI_Context_UploadFile(priv->primary, url, vmx) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Register the domain */
|
|
if (directoryName != NULL) {
|
|
if (virAsprintf(&datastoreRelatedPath, "[%s] %s/%s.vmx", datastoreName,
|
|
directoryName, escapedName) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
if (virAsprintf(&datastoreRelatedPath, "[%s] %s.vmx", datastoreName,
|
|
escapedName) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (esxVI_RegisterVM_Task(priv->primary, priv->primary->datacenter->vmFolder,
|
|
datastoreRelatedPath, NULL, esxVI_Boolean_False,
|
|
priv->primary->computeResource->resourcePool,
|
|
priv->primary->hostSystem->_reference,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, def->uuid,
|
|
esxVI_Occurrence_OptionalItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Could not define domain: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
domain = virGetDomain(conn, def->name, def->uuid);
|
|
|
|
if (domain != NULL) {
|
|
domain->id = -1;
|
|
}
|
|
|
|
/* FIXME: Add proper rollback in case of an error */
|
|
|
|
cleanup:
|
|
if (url == NULL) {
|
|
virBufferFreeAndReset(&buffer);
|
|
}
|
|
|
|
virDomainDefFree(def);
|
|
VIR_FREE(vmx);
|
|
VIR_FREE(datastoreName);
|
|
VIR_FREE(directoryName);
|
|
VIR_FREE(escapedName);
|
|
VIR_FREE(url);
|
|
VIR_FREE(datastoreRelatedPath);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostSystem);
|
|
esxVI_ManagedObjectReference_Free(&resourcePool);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return domain;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainUndefine(virDomainPtr domain)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_Context *ctx = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
|
|
if (priv->vCenter != NULL) {
|
|
ctx = priv->vCenter;
|
|
} else {
|
|
ctx = priv->host;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(ctx) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(ctx, domain->uuid, propertyNameList,
|
|
&virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_Suspended &&
|
|
powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not suspended or powered off"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_UnregisterVM(ctx, virtualMachine->obj) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainGetAutostart(virDomainPtr domain, int *autostart)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_AutoStartDefaults *defaults = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *hostAutoStartManager = NULL;
|
|
esxVI_AutoStartPowerInfo *powerInfo = NULL;
|
|
esxVI_AutoStartPowerInfo *powerInfoList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
|
|
*autostart = 0;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* Check general autostart config */
|
|
if (esxVI_LookupAutoStartDefaults(priv->primary, &defaults) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (defaults->enabled != esxVI_Boolean_True) {
|
|
/* Autostart is disabled in general, exit early here */
|
|
result = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Check specific autostart config */
|
|
if (esxVI_LookupAutoStartPowerInfoList(priv->primary, &powerInfoList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerInfoList == NULL) {
|
|
/* powerInfo list is empty, exit early here */
|
|
result = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
NULL, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (powerInfo = powerInfoList; powerInfo != NULL;
|
|
powerInfo = powerInfo->_next) {
|
|
if (STREQ(powerInfo->key->value, virtualMachine->obj->value)) {
|
|
if (STRCASEEQ(powerInfo->startAction, "powerOn")) {
|
|
*autostart = 1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&hostAutoStartManager);
|
|
esxVI_AutoStartDefaults_Free(&defaults);
|
|
esxVI_AutoStartPowerInfo_Free(&powerInfoList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSetAutostart(virDomainPtr domain, int autostart)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_HostAutoStartManagerConfig *spec = NULL;
|
|
esxVI_AutoStartDefaults *defaults = NULL;
|
|
esxVI_AutoStartPowerInfo *powerInfoList = NULL;
|
|
esxVI_AutoStartPowerInfo *powerInfo = NULL;
|
|
esxVI_AutoStartPowerInfo *newPowerInfo = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
NULL, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_HostAutoStartManagerConfig_Alloc(&spec) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (autostart) {
|
|
/*
|
|
* There is a general autostart option that affects the autostart
|
|
* behavior of all domains. If it's disabled then no domain does
|
|
* autostart. If it's enabled then the autostart behavior depends on
|
|
* the per-domain autostart config.
|
|
*/
|
|
if (esxVI_LookupAutoStartDefaults(priv->primary, &defaults) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (defaults->enabled != esxVI_Boolean_True) {
|
|
/*
|
|
* Autostart is disabled in general. Check if no other domain is
|
|
* in the list of autostarted domains, so it's safe to enable the
|
|
* general autostart option without affecting the autostart
|
|
* behavior of other domains.
|
|
*/
|
|
if (esxVI_LookupAutoStartPowerInfoList(priv->primary,
|
|
&powerInfoList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (powerInfo = powerInfoList; powerInfo != NULL;
|
|
powerInfo = powerInfo->_next) {
|
|
if (STRNEQ(powerInfo->key->value, virtualMachine->obj->value)) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Cannot enable general autostart option "
|
|
"without affecting other domains"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Enable autostart in general */
|
|
if (esxVI_AutoStartDefaults_Alloc(&spec->defaults) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
spec->defaults->enabled = esxVI_Boolean_True;
|
|
}
|
|
}
|
|
|
|
if (esxVI_AutoStartPowerInfo_Alloc(&newPowerInfo) < 0 ||
|
|
esxVI_Int_Alloc(&newPowerInfo->startOrder) < 0 ||
|
|
esxVI_Int_Alloc(&newPowerInfo->startDelay) < 0 ||
|
|
esxVI_Int_Alloc(&newPowerInfo->stopDelay) < 0 ||
|
|
esxVI_AutoStartPowerInfo_AppendToList(&spec->powerInfo,
|
|
newPowerInfo) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
newPowerInfo->key = virtualMachine->obj;
|
|
newPowerInfo->startOrder->value = -1; /* no specific start order */
|
|
newPowerInfo->startDelay->value = -1; /* use system default */
|
|
newPowerInfo->waitForHeartbeat = esxVI_AutoStartWaitHeartbeatSetting_SystemDefault;
|
|
newPowerInfo->startAction = autostart ? (char *)"powerOn" : (char *)"none";
|
|
newPowerInfo->stopDelay->value = -1; /* use system default */
|
|
newPowerInfo->stopAction = (char *)"none";
|
|
|
|
if (esxVI_ReconfigureAutostart
|
|
(priv->primary,
|
|
priv->primary->hostSystem->configManager->autoStartManager,
|
|
spec) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
if (newPowerInfo != NULL) {
|
|
newPowerInfo->key = NULL;
|
|
newPowerInfo->startAction = NULL;
|
|
newPowerInfo->stopAction = NULL;
|
|
}
|
|
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_HostAutoStartManagerConfig_Free(&spec);
|
|
esxVI_AutoStartDefaults_Free(&defaults);
|
|
esxVI_AutoStartPowerInfo_Free(&powerInfoList);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* The scheduler interface exposes basically the CPU ResourceAllocationInfo:
|
|
*
|
|
* - http://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.ResourceAllocationInfo.html
|
|
* - http://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.SharesInfo.html
|
|
* - http://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.SharesInfo.Level.html
|
|
*
|
|
*
|
|
* Available parameters:
|
|
*
|
|
* - reservation (VIR_DOMAIN_SCHED_FIELD_LLONG >= 0, in megaherz)
|
|
*
|
|
* The amount of CPU resource that is guaranteed to be available to the domain.
|
|
*
|
|
*
|
|
* - limit (VIR_DOMAIN_SCHED_FIELD_LLONG >= 0, or -1, in megaherz)
|
|
*
|
|
* The CPU utilization of the domain will be limited to this value, even if
|
|
* more CPU resources are available. If the limit is set to -1, the CPU
|
|
* utilization of the domain is unlimited. If the limit is not set to -1, it
|
|
* must be greater than or equal to the reservation.
|
|
*
|
|
*
|
|
* - shares (VIR_DOMAIN_SCHED_FIELD_INT >= 0, or in {-1, -2, -3}, no unit)
|
|
*
|
|
* Shares are used to determine relative CPU allocation between domains. In
|
|
* general, a domain with more shares gets proportionally more of the CPU
|
|
* resource. The special values -1, -2 and -3 represent the predefined
|
|
* SharesLevel 'low', 'normal' and 'high'.
|
|
*/
|
|
static char *
|
|
esxDomainGetSchedulerType(virDomainPtr domain ATTRIBUTE_UNUSED, int *nparams)
|
|
{
|
|
char *type = strdup("allocation");
|
|
|
|
if (type == NULL) {
|
|
virReportOOMError();
|
|
return NULL;
|
|
}
|
|
|
|
*nparams = 3; /* reservation, limit, shares */
|
|
|
|
return type;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainGetSchedulerParameters(virDomainPtr domain,
|
|
virSchedParameterPtr params, int *nparams)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
esxVI_SharesInfo *sharesInfo = NULL;
|
|
unsigned int mask = 0;
|
|
int i = 0;
|
|
|
|
if (*nparams < 3) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Parameter array must have space for 3 items"));
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
|
"config.cpuAllocation.reservation\0"
|
|
"config.cpuAllocation.limit\0"
|
|
"config.cpuAllocation.shares\0") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
propertyNameList, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (dynamicProperty = virtualMachine->propSet;
|
|
dynamicProperty != NULL && mask != 7 && i < 3;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name, "config.cpuAllocation.reservation") &&
|
|
! (mask & (1 << 0))) {
|
|
snprintf (params[i].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s",
|
|
"reservation");
|
|
|
|
params[i].type = VIR_DOMAIN_SCHED_FIELD_LLONG;
|
|
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Long) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
params[i].value.l = dynamicProperty->val->int64;
|
|
mask |= 1 << 0;
|
|
++i;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"config.cpuAllocation.limit") &&
|
|
! (mask & (1 << 1))) {
|
|
snprintf (params[i].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s",
|
|
"limit");
|
|
|
|
params[i].type = VIR_DOMAIN_SCHED_FIELD_LLONG;
|
|
|
|
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
esxVI_Type_Long) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
params[i].value.l = dynamicProperty->val->int64;
|
|
mask |= 1 << 1;
|
|
++i;
|
|
} else if (STREQ(dynamicProperty->name,
|
|
"config.cpuAllocation.shares") &&
|
|
! (mask & (1 << 2))) {
|
|
snprintf (params[i].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s",
|
|
"shares");
|
|
|
|
params[i].type = VIR_DOMAIN_SCHED_FIELD_INT;
|
|
|
|
if (esxVI_SharesInfo_CastFromAnyType(dynamicProperty->val,
|
|
&sharesInfo) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
switch (sharesInfo->level) {
|
|
case esxVI_SharesLevel_Custom:
|
|
params[i].value.i = sharesInfo->shares->value;
|
|
break;
|
|
|
|
case esxVI_SharesLevel_Low:
|
|
params[i].value.i = -1;
|
|
break;
|
|
|
|
case esxVI_SharesLevel_Normal:
|
|
params[i].value.i = -2;
|
|
break;
|
|
|
|
case esxVI_SharesLevel_High:
|
|
params[i].value.i = -3;
|
|
break;
|
|
|
|
default:
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Shares level has unknown value %d"),
|
|
(int)sharesInfo->level);
|
|
goto cleanup;
|
|
}
|
|
|
|
esxVI_SharesInfo_Free(&sharesInfo);
|
|
|
|
mask |= 1 << 2;
|
|
++i;
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
*nparams = i;
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSetSchedulerParameters(virDomainPtr domain,
|
|
virSchedParameterPtr params, int nparams)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachineConfigSpec *spec = NULL;
|
|
esxVI_SharesInfo *sharesInfo = NULL;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
int i;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, NULL, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_VirtualMachineConfigSpec_Alloc(&spec) < 0 ||
|
|
esxVI_ResourceAllocationInfo_Alloc(&spec->cpuAllocation) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < nparams; ++i) {
|
|
if (STREQ (params[i].field, "reservation") &&
|
|
params[i].type == VIR_DOMAIN_SCHED_FIELD_LLONG) {
|
|
if (esxVI_Long_Alloc(&spec->cpuAllocation->reservation) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (params[i].value.l < 0) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG,
|
|
_("Could not set reservation to %lld MHz, expecting "
|
|
"positive value"), params[i].value.l);
|
|
goto cleanup;
|
|
}
|
|
|
|
spec->cpuAllocation->reservation->value = params[i].value.l;
|
|
} else if (STREQ (params[i].field, "limit") &&
|
|
params[i].type == VIR_DOMAIN_SCHED_FIELD_LLONG) {
|
|
if (esxVI_Long_Alloc(&spec->cpuAllocation->limit) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (params[i].value.l < -1) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG,
|
|
_("Could not set limit to %lld MHz, expecting "
|
|
"positive value or -1 (unlimited)"),
|
|
params[i].value.l);
|
|
goto cleanup;
|
|
}
|
|
|
|
spec->cpuAllocation->limit->value = params[i].value.l;
|
|
} else if (STREQ (params[i].field, "shares") &&
|
|
params[i].type == VIR_DOMAIN_SCHED_FIELD_INT) {
|
|
if (esxVI_SharesInfo_Alloc(&sharesInfo) < 0 ||
|
|
esxVI_Int_Alloc(&sharesInfo->shares) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
spec->cpuAllocation->shares = sharesInfo;
|
|
|
|
if (params[i].value.i >= 0) {
|
|
spec->cpuAllocation->shares->level = esxVI_SharesLevel_Custom;
|
|
spec->cpuAllocation->shares->shares->value = params[i].value.i;
|
|
} else {
|
|
switch (params[i].value.i) {
|
|
case -1:
|
|
spec->cpuAllocation->shares->level = esxVI_SharesLevel_Low;
|
|
spec->cpuAllocation->shares->shares->value = -1;
|
|
break;
|
|
|
|
case -2:
|
|
spec->cpuAllocation->shares->level =
|
|
esxVI_SharesLevel_Normal;
|
|
spec->cpuAllocation->shares->shares->value = -1;
|
|
break;
|
|
|
|
case -3:
|
|
spec->cpuAllocation->shares->level =
|
|
esxVI_SharesLevel_High;
|
|
spec->cpuAllocation->shares->shares->value = -1;
|
|
break;
|
|
|
|
default:
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG,
|
|
_("Could not set shares to %d, expecting positive "
|
|
"value or -1 (low), -2 (normal) or -3 (high)"),
|
|
params[i].value.i);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
} else {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, _("Unknown field '%s'"),
|
|
params[i].field);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (esxVI_ReconfigVM_Task(priv->primary, virtualMachine->obj, spec,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not change scheduler parameters: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_VirtualMachineConfigSpec_Free(&spec);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainMigratePrepare(virConnectPtr dconn,
|
|
char **cookie ATTRIBUTE_UNUSED,
|
|
int *cookielen ATTRIBUTE_UNUSED,
|
|
const char *uri_in ATTRIBUTE_UNUSED,
|
|
char **uri_out,
|
|
unsigned long flags ATTRIBUTE_UNUSED,
|
|
const char *dname ATTRIBUTE_UNUSED,
|
|
unsigned long resource ATTRIBUTE_UNUSED)
|
|
{
|
|
esxPrivate *priv = dconn->privateData;
|
|
|
|
if (uri_in == NULL) {
|
|
if (virAsprintf(uri_out, "vpxmigr://%s/%s/%s",
|
|
priv->vCenter->ipAddress,
|
|
priv->vCenter->computeResource->resourcePool->value,
|
|
priv->vCenter->hostSystem->_reference->value) < 0) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainMigratePerform(virDomainPtr domain,
|
|
const char *cookie ATTRIBUTE_UNUSED,
|
|
int cookielen ATTRIBUTE_UNUSED,
|
|
const char *uri,
|
|
unsigned long flags ATTRIBUTE_UNUSED,
|
|
const char *dname,
|
|
unsigned long bandwidth ATTRIBUTE_UNUSED)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
xmlURIPtr parsedUri = NULL;
|
|
char *saveptr;
|
|
char *path_resourcePool;
|
|
char *path_hostSystem;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_ManagedObjectReference resourcePool;
|
|
esxVI_ManagedObjectReference hostSystem;
|
|
esxVI_Event *eventList = NULL;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
if (priv->vCenter == NULL) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Migration not possible without a vCenter"));
|
|
return -1;
|
|
}
|
|
|
|
if (dname != NULL) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Renaming domains on migration not supported"));
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(priv->vCenter) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* Parse migration URI */
|
|
parsedUri = xmlParseURI(uri);
|
|
|
|
if (parsedUri == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
|
|
if (parsedUri->scheme == NULL || STRCASENEQ(parsedUri->scheme, "vpxmigr")) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Only vpxmigr:// migration URIs are supported"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (STRCASENEQ(priv->vCenter->ipAddress, parsedUri->server)) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Migration source and destination have to refer to "
|
|
"the same vCenter"));
|
|
goto cleanup;
|
|
}
|
|
|
|
path_resourcePool = strtok_r(parsedUri->path, "/", &saveptr);
|
|
path_hostSystem = strtok_r(NULL, "", &saveptr);
|
|
|
|
if (path_resourcePool == NULL || path_hostSystem == NULL) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Migration URI has to specify resource pool and host system"));
|
|
goto cleanup;
|
|
}
|
|
|
|
resourcePool._next = NULL;
|
|
resourcePool._type = esxVI_Type_ManagedObjectReference;
|
|
resourcePool.type = (char *)"ResourcePool";
|
|
resourcePool.value = path_resourcePool;
|
|
|
|
hostSystem._next = NULL;
|
|
hostSystem._type = esxVI_Type_ManagedObjectReference;
|
|
hostSystem.type = (char *)"HostSystem";
|
|
hostSystem.value = path_hostSystem;
|
|
|
|
/* Lookup VirtualMachine */
|
|
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->vCenter, domain->uuid, NULL, &virtualMachine,
|
|
priv->autoAnswer) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Validate the purposed migration */
|
|
if (esxVI_ValidateMigration(priv->vCenter, virtualMachine->obj,
|
|
esxVI_VirtualMachinePowerState_Undefined, NULL,
|
|
&resourcePool, &hostSystem, &eventList) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (eventList != NULL) {
|
|
/*
|
|
* FIXME: Need to report the complete list of events. Limit reporting
|
|
* to the first event for now.
|
|
*/
|
|
if (eventList->fullFormattedMessage != NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not migrate domain, validation reported a "
|
|
"problem: %s"), eventList->fullFormattedMessage);
|
|
} else {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not migrate domain, validation reported a "
|
|
"problem"));
|
|
}
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Perform the purposed migration */
|
|
if (esxVI_MigrateVM_Task(priv->vCenter, virtualMachine->obj,
|
|
&resourcePool, &hostSystem,
|
|
esxVI_VirtualMachineMovePriority_DefaultPriority,
|
|
esxVI_VirtualMachinePowerState_Undefined,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->vCenter, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not migrate domain, migration task finished with "
|
|
"an error: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
xmlFreeURI(parsedUri);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_Event_Free(&eventList);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static virDomainPtr
|
|
esxDomainMigrateFinish(virConnectPtr dconn, const char *dname,
|
|
const char *cookie ATTRIBUTE_UNUSED,
|
|
int cookielen ATTRIBUTE_UNUSED,
|
|
const char *uri ATTRIBUTE_UNUSED,
|
|
unsigned long flags ATTRIBUTE_UNUSED)
|
|
{
|
|
return esxDomainLookupByName(dconn, dname);
|
|
}
|
|
|
|
|
|
|
|
static unsigned long long
|
|
esxNodeGetFreeMemory(virConnectPtr conn)
|
|
{
|
|
unsigned long long result = 0;
|
|
esxPrivate *priv = conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *resourcePool = NULL;
|
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
esxVI_ResourcePoolResourceUsage *resourcePoolResourceUsage = NULL;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Get memory usage of resource pool */
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.memory") < 0 ||
|
|
esxVI_LookupObjectContentByType(priv->primary,
|
|
priv->primary->computeResource->resourcePool,
|
|
"ResourcePool", propertyNameList,
|
|
&resourcePool,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (dynamicProperty = resourcePool->propSet; dynamicProperty != NULL;
|
|
dynamicProperty = dynamicProperty->_next) {
|
|
if (STREQ(dynamicProperty->name, "runtime.memory")) {
|
|
if (esxVI_ResourcePoolResourceUsage_CastFromAnyType
|
|
(dynamicProperty->val, &resourcePoolResourceUsage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
break;
|
|
} else {
|
|
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
}
|
|
}
|
|
|
|
if (resourcePoolResourceUsage == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not retrieve memory usage of resource pool"));
|
|
goto cleanup;
|
|
}
|
|
|
|
result = resourcePoolResourceUsage->unreservedForVm->value;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&resourcePool);
|
|
esxVI_ResourcePoolResourceUsage_Free(&resourcePoolResourceUsage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxIsEncrypted(virConnectPtr conn)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
|
|
if (STRCASEEQ(priv->transport, "https")) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxIsSecure(virConnectPtr conn)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
|
|
if (STRCASEEQ(priv->transport, "https")) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainIsActive(virDomainPtr domain)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_VirtualMachinePowerState powerState;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
|
"runtime.powerState") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
propertyNameList, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
|
|
result = 1;
|
|
} else {
|
|
result = 0;
|
|
}
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_String_Free(&propertyNameList);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainIsPersistent(virDomainPtr domain ATTRIBUTE_UNUSED)
|
|
{
|
|
/* ESX has no concept of transient domains, so all of them are persistent */
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainIsUpdated(virDomainPtr domain ATTRIBUTE_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static virDomainSnapshotPtr
|
|
esxDomainSnapshotCreateXML(virDomainPtr domain, const char *xmlDesc,
|
|
unsigned int flags)
|
|
{
|
|
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;
|
|
char *taskInfoErrorMessage = NULL;
|
|
virDomainSnapshotPtr snapshot = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
def = virDomainSnapshotDefParseString(xmlDesc, 1);
|
|
|
|
if (def == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, NULL, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_LookupRootSnapshotTreeList(priv->primary, domain->uuid,
|
|
&rootSnapshotList) < 0 ||
|
|
esxVI_GetSnapshotTreeByName(rootSnapshotList, def->name,
|
|
&snapshotTree, &snapshotTreeParent,
|
|
esxVI_Occurrence_OptionalItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (snapshotTree != NULL) {
|
|
ESX_ERROR(VIR_ERR_OPERATION_INVALID,
|
|
_("Snapshot '%s' already exists"), def->name);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_CreateSnapshot_Task(priv->primary, virtualMachine->obj,
|
|
def->name, def->description,
|
|
esxVI_Boolean_True,
|
|
esxVI_Boolean_False, &task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Could not create snapshot: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
snapshot = virGetDomainSnapshot(domain, def->name);
|
|
|
|
cleanup:
|
|
virDomainSnapshotDefFree(def);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot,
|
|
unsigned int flags)
|
|
{
|
|
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;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
memset(&def, 0, sizeof (def));
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_LookupRootSnapshotTreeList(priv->primary, snapshot->domain->uuid,
|
|
&rootSnapshotList) < 0 ||
|
|
esxVI_GetSnapshotTreeByName(rootSnapshotList, snapshot->name,
|
|
&snapshotTree, &snapshotTreeParent,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
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 cleanup;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSnapshotNum(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
int count;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupRootSnapshotTreeList(priv->primary, domain->uuid,
|
|
&rootSnapshotTreeList) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
count = esxVI_GetNumberOfSnapshotTrees(rootSnapshotTreeList);
|
|
|
|
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList);
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen,
|
|
unsigned int flags)
|
|
{
|
|
int result;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
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->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupRootSnapshotTreeList(priv->primary, domain->uuid,
|
|
&rootSnapshotTreeList) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
result = esxVI_GetSnapshotTreeNames(rootSnapshotTreeList, names, nameslen);
|
|
|
|
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static virDomainSnapshotPtr
|
|
esxDomainSnapshotLookupByName(virDomainPtr domain, const char *name,
|
|
unsigned int flags)
|
|
{
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
|
|
esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL;
|
|
esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL;
|
|
virDomainSnapshotPtr snapshot = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_LookupRootSnapshotTreeList(priv->primary, 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)
|
|
{
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupCurrentSnapshotTree(priv->primary, domain->uuid,
|
|
¤tSnapshotTree,
|
|
esxVI_Occurrence_OptionalItem) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (currentSnapshotTree != NULL) {
|
|
esxVI_VirtualMachineSnapshotTree_Free(¤tSnapshotTree);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static virDomainSnapshotPtr
|
|
esxDomainSnapshotCurrent(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL;
|
|
virDomainSnapshotPtr snapshot = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (esxVI_LookupCurrentSnapshotTree(priv->primary, domain->uuid,
|
|
¤tSnapshotTree,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
snapshot = virGetDomainSnapshot(domain, currentSnapshotTree->name);
|
|
|
|
esxVI_VirtualMachineSnapshotTree_Free(¤tSnapshotTree);
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, unsigned int flags)
|
|
{
|
|
int result = -1;
|
|
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;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupRootSnapshotTreeList(priv->primary, snapshot->domain->uuid,
|
|
&rootSnapshotList) < 0 ||
|
|
esxVI_GetSnapshotTreeByName(rootSnapshotList, snapshot->name,
|
|
&snapshotTree, &snapshotTreeParent,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_RevertToSnapshot_Task(priv->primary, snapshotTree->snapshot, NULL,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, snapshot->domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not revert to snapshot '%s': %s"), snapshot->name,
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags)
|
|
{
|
|
int result = -1;
|
|
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;
|
|
char *taskInfoErrorMessage = NULL;
|
|
|
|
virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN) {
|
|
removeChildren = esxVI_Boolean_True;
|
|
}
|
|
|
|
if (esxVI_LookupRootSnapshotTreeList(priv->primary, snapshot->domain->uuid,
|
|
&rootSnapshotList) < 0 ||
|
|
esxVI_GetSnapshotTreeByName(rootSnapshotList, snapshot->name,
|
|
&snapshotTree, &snapshotTreeParent,
|
|
esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (esxVI_RemoveSnapshot_Task(priv->primary, snapshotTree->snapshot,
|
|
removeChildren, &task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, snapshot->domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not delete snapshot '%s': %s"), snapshot->name,
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainSetMemoryParameters(virDomainPtr domain, virMemoryParameterPtr params,
|
|
int nparams, unsigned int flags)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_VirtualMachineConfigSpec *spec = NULL;
|
|
esxVI_ManagedObjectReference *task = NULL;
|
|
esxVI_TaskInfoState taskInfoState;
|
|
char *taskInfoErrorMessage = NULL;
|
|
int i;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
|
|
(priv->primary, domain->uuid, NULL, &virtualMachine,
|
|
priv->autoAnswer) < 0 ||
|
|
esxVI_VirtualMachineConfigSpec_Alloc(&spec) < 0 ||
|
|
esxVI_ResourceAllocationInfo_Alloc(&spec->memoryAllocation) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < nparams; ++i) {
|
|
if (STREQ (params[i].field, VIR_DOMAIN_MEMORY_MIN_GUARANTEE) &&
|
|
params[i].type == VIR_DOMAIN_SCHED_FIELD_ULLONG) {
|
|
if (esxVI_Long_Alloc(&spec->memoryAllocation->reservation) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
spec->memoryAllocation->reservation->value =
|
|
VIR_DIV_UP(params[i].value.ul, 1024); /* Scale from kilobytes to megabytes */
|
|
} else {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, _("Unknown field '%s'"),
|
|
params[i].field);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (esxVI_ReconfigVM_Task(priv->primary, virtualMachine->obj, spec,
|
|
&task) < 0 ||
|
|
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
|
|
esxVI_Occurrence_RequiredItem,
|
|
priv->autoAnswer, &taskInfoState,
|
|
&taskInfoErrorMessage) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (taskInfoState != esxVI_TaskInfoState_Success) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not change memory parameters: %s"),
|
|
taskInfoErrorMessage);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_VirtualMachineConfigSpec_Free(&spec);
|
|
esxVI_ManagedObjectReference_Free(&task);
|
|
VIR_FREE(taskInfoErrorMessage);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxDomainGetMemoryParameters(virDomainPtr domain, virMemoryParameterPtr params,
|
|
int *nparams, unsigned int flags)
|
|
{
|
|
int result = -1;
|
|
esxPrivate *priv = domain->conn->privateData;
|
|
esxVI_String *propertyNameList = NULL;
|
|
esxVI_ObjectContent *virtualMachine = NULL;
|
|
esxVI_Long *reservation = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (*nparams == 0) {
|
|
*nparams = 1; /* min_guarantee */
|
|
return 0;
|
|
}
|
|
|
|
if (*nparams < 1) {
|
|
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Parameter array must have space for 1 item"));
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (esxVI_String_AppendValueToList
|
|
(&propertyNameList, "config.memoryAllocation.reservation") < 0 ||
|
|
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
|
|
propertyNameList, &virtualMachine,
|
|
esxVI_Occurrence_RequiredItem) < 0 ||
|
|
esxVI_GetLong(virtualMachine, "config.memoryAllocation.reservation",
|
|
&reservation, esxVI_Occurrence_RequiredItem) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virStrcpyStatic(params[0].field,
|
|
VIR_DOMAIN_MEMORY_MIN_GUARANTEE) == NULL) {
|
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
_("Field %s too big for destination"),
|
|
VIR_DOMAIN_MEMORY_MIN_GUARANTEE);
|
|
goto cleanup;
|
|
}
|
|
|
|
params[0].type = VIR_DOMAIN_SCHED_FIELD_ULLONG;
|
|
params[0].value.ul = reservation->value * 1024; /* Scale from megabytes to kilobytes */
|
|
|
|
*nparams = 1;
|
|
result = 0;
|
|
|
|
cleanup:
|
|
esxVI_String_Free(&propertyNameList);
|
|
esxVI_ObjectContent_Free(&virtualMachine);
|
|
esxVI_Long_Free(&reservation);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static virDriver esxDriver = {
|
|
VIR_DRV_ESX,
|
|
"ESX",
|
|
esxOpen, /* open */
|
|
esxClose, /* close */
|
|
esxSupportsFeature, /* supports_feature */
|
|
esxGetType, /* type */
|
|
esxGetVersion, /* version */
|
|
NULL, /* libvirtVersion (impl. in libvirt.c) */
|
|
esxGetHostname, /* hostname */
|
|
NULL, /* getMaxVcpus */
|
|
esxNodeGetInfo, /* nodeGetInfo */
|
|
esxGetCapabilities, /* getCapabilities */
|
|
esxListDomains, /* listDomains */
|
|
esxNumberOfDomains, /* numOfDomains */
|
|
NULL, /* domainCreateXML */
|
|
esxDomainLookupByID, /* domainLookupByID */
|
|
esxDomainLookupByUUID, /* domainLookupByUUID */
|
|
esxDomainLookupByName, /* domainLookupByName */
|
|
esxDomainSuspend, /* domainSuspend */
|
|
esxDomainResume, /* domainResume */
|
|
esxDomainShutdown, /* domainShutdown */
|
|
esxDomainReboot, /* domainReboot */
|
|
esxDomainDestroy, /* domainDestroy */
|
|
esxDomainGetOSType, /* domainGetOSType */
|
|
esxDomainGetMaxMemory, /* domainGetMaxMemory */
|
|
esxDomainSetMaxMemory, /* domainSetMaxMemory */
|
|
esxDomainSetMemory, /* domainSetMemory */
|
|
esxDomainGetInfo, /* domainGetInfo */
|
|
NULL, /* domainSave */
|
|
NULL, /* domainRestore */
|
|
NULL, /* domainCoreDump */
|
|
esxDomainSetVcpus, /* domainSetVcpus */
|
|
esxDomainSetVcpusFlags, /* domainSetVcpusFlags */
|
|
esxDomainGetVcpusFlags, /* domainGetVcpusFlags */
|
|
NULL, /* domainPinVcpu */
|
|
NULL, /* domainGetVcpus */
|
|
esxDomainGetMaxVcpus, /* domainGetMaxVcpus */
|
|
NULL, /* domainGetSecurityLabel */
|
|
NULL, /* nodeGetSecurityModel */
|
|
esxDomainDumpXML, /* domainDumpXML */
|
|
esxDomainXMLFromNative, /* domainXMLFromNative */
|
|
esxDomainXMLToNative, /* domainXMLToNative */
|
|
esxListDefinedDomains, /* listDefinedDomains */
|
|
esxNumberOfDefinedDomains, /* numOfDefinedDomains */
|
|
esxDomainCreate, /* domainCreate */
|
|
esxDomainCreateWithFlags, /* domainCreateWithFlags */
|
|
esxDomainDefineXML, /* domainDefineXML */
|
|
esxDomainUndefine, /* domainUndefine */
|
|
NULL, /* domainAttachDevice */
|
|
NULL, /* domainAttachDeviceFlags */
|
|
NULL, /* domainDetachDevice */
|
|
NULL, /* domainDetachDeviceFlags */
|
|
NULL, /* domainUpdateDeviceFlags */
|
|
esxDomainGetAutostart, /* domainGetAutostart */
|
|
esxDomainSetAutostart, /* domainSetAutostart */
|
|
esxDomainGetSchedulerType, /* domainGetSchedulerType */
|
|
esxDomainGetSchedulerParameters, /* domainGetSchedulerParameters */
|
|
esxDomainSetSchedulerParameters, /* domainSetSchedulerParameters */
|
|
esxDomainMigratePrepare, /* domainMigratePrepare */
|
|
esxDomainMigratePerform, /* domainMigratePerform */
|
|
esxDomainMigrateFinish, /* domainMigrateFinish */
|
|
NULL, /* domainBlockStats */
|
|
NULL, /* domainInterfaceStats */
|
|
NULL, /* domainMemoryStats */
|
|
NULL, /* domainBlockPeek */
|
|
NULL, /* domainMemoryPeek */
|
|
NULL, /* domainGetBlockInfo */
|
|
NULL, /* nodeGetCellsFreeMemory */
|
|
esxNodeGetFreeMemory, /* nodeGetFreeMemory */
|
|
NULL, /* domainEventRegister */
|
|
NULL, /* domainEventDeregister */
|
|
NULL, /* domainMigratePrepare2 */
|
|
NULL, /* domainMigrateFinish2 */
|
|
NULL, /* nodeDeviceDettach */
|
|
NULL, /* nodeDeviceReAttach */
|
|
NULL, /* nodeDeviceReset */
|
|
NULL, /* domainMigratePrepareTunnel */
|
|
esxIsEncrypted, /* isEncrypted */
|
|
esxIsSecure, /* isSecure */
|
|
esxDomainIsActive, /* domainIsActive */
|
|
esxDomainIsPersistent, /* domainIsPersistent */
|
|
esxDomainIsUpdated, /* domainIsUpdated */
|
|
NULL, /* cpuCompare */
|
|
NULL, /* cpuBaseline */
|
|
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 */
|
|
NULL, /* qemuDomainMonitorCommand */
|
|
esxDomainSetMemoryParameters, /* domainSetMemoryParameters */
|
|
esxDomainGetMemoryParameters, /* domainGetMemoryParameters */
|
|
NULL, /* domainOpenConsole */
|
|
};
|
|
|
|
|
|
|
|
int
|
|
esxRegister(void)
|
|
{
|
|
if (virRegisterDriver(&esxDriver) < 0 ||
|
|
esxInterfaceRegister() < 0 ||
|
|
esxNetworkRegister() < 0 ||
|
|
esxStorageRegister() < 0 ||
|
|
esxDeviceRegister() < 0 ||
|
|
esxSecretRegister() < 0 ||
|
|
esxNWFilterRegister() < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|