libvirt/src/esx/esx_driver.c

5300 lines
174 KiB
C
Raw Normal View History

/*
* esx_driver.c: core driver functions for managing VMware ESX hosts
*
* Copyright (C) 2010-2015 Red Hat, Inc.
* Copyright (C) 2009-2014 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, see
* <http://www.gnu.org/licenses/>.
*
*/
#include <config.h>
#include "internal.h"
#include "virdomainobjlist.h"
#include "snapshot_conf.h"
#include "virauth.h"
2012-12-12 18:06:53 +00:00
#include "viralloc.h"
#include "virfile.h"
2012-12-12 17:59:27 +00:00
#include "virlog.h"
2012-12-13 18:01:25 +00:00
#include "viruuid.h"
#include "vmx.h"
#include "virtypedparam.h"
#include "esx_driver.h"
#include "esx_interface_driver.h"
#include "esx_network_driver.h"
#include "esx_storage_driver.h"
#include "esx_private.h"
#include "esx_vi.h"
#include "esx_vi_methods.h"
#include "esx_util.h"
#include "esx_stream.h"
#include "virstring.h"
#include "viruri.h"
#define VIR_FROM_THIS VIR_FROM_ESX
VIR_LOG_INIT("esx.esx_driver");
static int esxDomainGetMaxVcpus(virDomainPtr domain);
typedef struct _esxVMX_Data esxVMX_Data;
struct _esxVMX_Data {
esxVI_Context *ctx;
char *datastorePathWithoutFileName;
};
static void
esxFreePrivate(esxPrivate **priv)
{
if (!priv || !(*priv))
return;
esxVI_Context_Free(&(*priv)->host);
esxVI_Context_Free(&(*priv)->vCenter);
esxUtil_FreeParsedUri(&(*priv)->parsedUri);
virObjectUnref((*priv)->caps);
virObjectUnref((*priv)->xmlopt);
g_free(*priv);
}
/*
* Parse a file name from a .vmx file and convert it to datastore path format
* if possible. 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
*
* - There might also be absolute file names referencing files outside of a
* datastore:
*
* /usr/lib/vmware/isoimages/linux.iso
*
* Such file names are left as is and are not converted to datastore path
* format because this is not possible.
*
* 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 int
esxParseVMXFileName(const char *fileName,
void *opaque,
char **out,
bool allow_missing)
{
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;
g_autofree char *strippedFileName = NULL;
g_autofree char *copyOfFileName = NULL;
char *directoryAndFileName;
int ret = -1;
*out = NULL;
if (!strchr(fileName, '/') && !strchr(fileName, '\\')) {
/* Plain file name, use same directory as for the .vmx file */
*out = g_strdup_printf("%s/%s", data->datastorePathWithoutFileName,
fileName);
return 0;
}
if (esxVI_String_AppendValueToList(&propertyNameList,
"summary.name") < 0 ||
esxVI_LookupDatastoreList(data->ctx, propertyNameList,
&datastoreList) < 0) {
return -1;
}
/* Search for datastore by mount path */
for (datastore = datastoreList; datastore;
datastore = datastore->_next) {
esxVI_DatastoreHostMount_Free(&hostMount);
datastoreName = NULL;
if (esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj,
&hostMount,
esxVI_Occurrence_RequiredItem) < 0 ||
esxVI_GetStringValue(datastore, "summary.name", &datastoreName,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
tmp = (char *)STRSKIP(fileName, hostMount->mountInfo->path);
if (!tmp)
continue;
/* Found a match. Strip leading separators */
while (*tmp == '/' || *tmp == '\\')
++tmp;
strippedFileName = g_strdup(tmp);
tmp = strippedFileName;
/* Convert \ to / */
while (*tmp != '\0') {
if (*tmp == '\\')
*tmp = '/';
++tmp;
}
*out = g_strdup_printf("[%s] %s", datastoreName, strippedFileName);
break;
}
/* Fallback to direct datastore name match */
if (!*out && STRPREFIX(fileName, "/vmfs/volumes/")) {
copyOfFileName = g_strdup(fileName);
/* Expected format: '/vmfs/volumes/<datastore>/<path>' */
if (!(tmp = STRSKIP(copyOfFileName, "/vmfs/volumes/")) ||
!(datastoreName = strtok_r(tmp, "/", &saveptr)) ||
!(directoryAndFileName = strtok_r(NULL, "", &saveptr))) {
virReportError(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) {
if (allow_missing) {
ret = 0;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("File name '%s' refers to non-existing datastore '%s'"),
fileName, datastoreName);
}
goto cleanup;
}
*out = g_strdup_printf("[%s] %s", datastoreName, directoryAndFileName);
}
/* If it's an absolute path outside of a datastore just use it as is */
if (!*out && *fileName == '/') {
/* FIXME: need to deal with Windows paths here too */
*out = g_strdup(fileName);
}
if (!*out) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not handle file name '%s'"), fileName);
goto cleanup;
}
ret = 0;
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&datastoreList);
esxVI_DatastoreHostMount_Free(&hostMount);
return ret;
}
/*
* This function does the inverse of esxParseVMXFileName. It takes a file name
* in datastore path format or in absolute 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 its mount path. Finally concatenate the mount path, directory
* and file name to an absolute path and return it. Detect the separator type
* based on the mount path.
*/
static char *
esxFormatVMXFileName(const char *fileName, void *opaque)
{
g_autofree char *tmpResult = NULL;
char *result = NULL;
esxVMX_Data *data = opaque;
g_autofree char *datastoreName = NULL;
g_autofree char *directoryAndFileName = NULL;
esxVI_ObjectContent *datastore = NULL;
esxVI_DatastoreHostMount *hostMount = NULL;
char separator = '/';
g_auto(virBuffer) buffer = VIR_BUFFER_INITIALIZER;
char *tmp;
size_t length;
if (*fileName == '[') {
/* Parse datastore path and lookup datastore */
if (esxUtil_ParseDatastorePath(fileName, &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,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
/* Detect separator type */
if (strchr(hostMount->mountInfo->path, '\\'))
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);
tmpResult = virBufferContentAndReset(&buffer);
} else if (*fileName == '/') {
/* FIXME: need to deal with Windows paths here too */
tmpResult = g_strdup(fileName);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not handle file name '%s'"), fileName);
goto cleanup;
}
/* FIXME: Check if referenced path/file really exists */
result = g_steal_pointer(&tmpResult);
cleanup:
esxVI_ObjectContent_Free(&datastore);
esxVI_DatastoreHostMount_Free(&hostMount);
return result;
}
static int
esxAutodetectSCSIControllerModel(virDomainDiskDef *def, int *model,
void *opaque)
{
int result = -1;
esxVMX_Data *data = opaque;
esxVI_FileInfo *fileInfo = NULL;
esxVI_VmDiskFileInfo *vmDiskFileInfo = NULL;
const char *src = virDomainDiskGetSource(def);
if (def->device != VIR_DOMAIN_DISK_DEVICE_DISK ||
def->bus != VIR_DOMAIN_DISK_BUS_SCSI ||
conf: move host disk type to util/ A continuation of the migration of disk details to virstoragefile. This patch moves a single enum, but converting the name has quite a bit of fallout. * src/conf/domain_conf.h (virDomainDiskType): Move... * src/util/virstoragefile.h (virStorageType): ...and rename. * src/bhyve/bhyve_command.c (bhyveBuildDiskArgStr) (virBhyveProcessBuildLoadCmd): Update clients. * src/conf/domain_conf.c (virDomainDiskSourceDefParse) (virDomainDiskDefParseXML, virDomainDiskSourceDefFormatInternal) (virDomainDiskDefFormat, virDomainDiskGetActualType) (virDomainDiskDefForeachPath, virDomainDiskSourceIsBlockType): Likewise. * src/conf/snapshot_conf.h (_virDomainSnapshotDiskDef): Likewise. * src/conf/snapshot_conf.c (virDomainSnapshotDiskDefParseXML) (virDomainSnapshotAlignDisks, virDomainSnapshotDiskDefFormat): Likewise. * src/esx/esx_driver.c (esxAutodetectSCSIControllerModel) (esxDomainDefineXML): Likewise. * src/locking/domain_lock.c (virDomainLockManagerAddDisk): Likewise. * src/lxc/lxc_controller.c (virLXCControllerSetupLoopDeviceDisk) (virLXCControllerSetupNBDDeviceDisk) (virLXCControllerSetupLoopDevices, virLXCControllerSetupDisk): Likewise. * src/parallels/parallels_driver.c (parallelsGetHddInfo): Likewise. * src/phyp/phyp_driver.c (phypDiskType): Likewise. * src/qemu/qemu_command.c (qemuGetDriveSourceString) (qemuDomainDiskGetSourceString, qemuBuildDriveStr) (qemuBuildCommandLine, qemuParseCommandLineDisk) (qemuParseCommandLine): Likewise. * src/qemu/qemu_conf.c (qemuCheckSharedDevice) (qemuTranslateDiskSourcePool) (qemuTranslateSnapshotDiskSourcePool): Likewise. * src/qemu/qemu_domain.c (qemuDomainDeviceDefPostParse) (qemuDomainDetermineDiskChain): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetBlockInfo) (qemuDomainSnapshotPrepareDiskExternalBackingInactive) (qemuDomainSnapshotPrepareDiskExternalBackingActive) (qemuDomainSnapshotPrepareDiskExternalOverlayActive) (qemuDomainSnapshotPrepareDiskExternalOverlayInactive) (qemuDomainSnapshotPrepareDiskInternal) (qemuDomainSnapshotPrepare) (qemuDomainSnapshotCreateSingleDiskActive): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainChangeEjectableMedia): Likewise. * src/qemu/qemu_migration.c (qemuMigrationIsSafe): Likewise. * src/security/security_apparmor.c (AppArmorRestoreSecurityImageLabel) (AppArmorSetSecurityImageLabel): Likewise. * src/security/security_dac.c (virSecurityDACSetSecurityImageLabel) (virSecurityDACRestoreSecurityImageLabelInt) (virSecurityDACSetSecurityAllLabel): Likewise. * src/security/security_selinux.c (virSecuritySELinuxRestoreSecurityImageLabelInt) (virSecuritySELinuxSetSecurityImageLabel) (virSecuritySELinuxSetSecurityAllLabel): Likewise. * src/storage/storage_backend.c (virStorageFileBackendForType): Likewise. * src/storage/storage_backend_fs.c (virStorageFileBackendFile) (virStorageFileBackendBlock): Likewise. * src/storage/storage_backend_gluster.c (virStorageFileBackendGluster): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainGetXMLDesc, vboxAttachDrives) (vboxDomainAttachDeviceImpl, vboxDomainDetachDevice): Likewise. * src/vmware/vmware_conf.c (vmwareVmxPath): Likewise. * src/vmx/vmx.c (virVMXParseDisk, virVMXFormatDisk) (virVMXFormatFloppy): Likewise. * src/xenxs/xen_sxpr.c (xenParseSxprDisks, xenParseSxpr) (xenFormatSxprDisk): Likewise. * src/xenxs/xen_xm.c (xenParseXM, xenFormatXMDisk): Likewise. * tests/securityselinuxlabeltest.c (testSELinuxLoadDef): Likewise. * src/libvirt_private.syms (domain_conf.h): Move symbols... (virstoragefile.h): ...as appropriate. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-03-27 21:57:49 +00:00
virDomainDiskGetType(def) != VIR_STORAGE_TYPE_FILE ||
!src || !STRPREFIX(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, src,
2010-08-28 19:49:07 +00:00
false, &fileInfo,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
vmDiskFileInfo = esxVI_VmDiskFileInfo_DynamicCast(fileInfo);
if (!vmDiskFileInfo || !vmDiskFileInfo->controllerType) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup controller model for '%s'"), src);
goto cleanup;
}
if (STRCASEEQ(vmDiskFileInfo->controllerType,
"VirtualBusLogicController")) {
*model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_BUSLOGIC;
} else if (STRCASEEQ(vmDiskFileInfo->controllerType,
"VirtualLsiLogicController")) {
*model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC;
} else if (STRCASEEQ(vmDiskFileInfo->controllerType,
"VirtualLsiLogicSASController")) {
*model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068;
} else if (STRCASEEQ(vmDiskFileInfo->controllerType,
"ParaVirtualSCSIController")) {
*model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VMPVSCSI;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Found unexpected controller model '%s' for disk '%s'"),
vmDiskFileInfo->controllerType, 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;
}
for (dynamicProperty = hostSystem->propSet; dynamicProperty;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "hardware.cpuFeature")) {
if (esxVI_HostCpuIdInfo_CastListFromAnyType
(dynamicProperty->val, &hostCpuIdInfoList) < 0) {
goto cleanup;
}
for (hostCpuIdInfo = hostCpuIdInfoList; hostCpuIdInfo;
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 {
virReportError(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;
char *uuid_string = 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 ||
esxVI_GetStringValue(hostSystem, "hardware.systemInfo.uuid",
&uuid_string, esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
if (strlen(uuid_string) > 0) {
if (virUUIDParse(uuid_string, uuid) < 0) {
VIR_WARN("Could not parse host UUID from string '%s'", uuid_string);
/* HostSystem has an invalid UUID, ignore it */
memset(uuid, 0, VIR_UUID_BUFLEN);
}
} else {
/* HostSystem has an empty UUID */
memset(uuid, 0, VIR_UUID_BUFLEN);
}
result = 0;
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&hostSystem);
return result;
}
static virCaps *
esxCapsInit(esxPrivate *priv)
{
esxVI_Boolean supportsLongMode = esxSupportsLongMode(priv);
virCaps *caps = NULL;
virCapsGuest *guest = NULL;
if (supportsLongMode == esxVI_Boolean_Undefined)
return NULL;
if (supportsLongMode == esxVI_Boolean_True) {
capabilities: use bool instead of int While preparing to add a capability for active commit, I noticed that the existing code was abusing int for boolean values. * src/conf/capabilities.h (_virCapsGuestFeature, _virCapsHost) (virCapabilitiesNew, virCapabilitiesAddGuestFeature): Improve types. * src/conf/capabilities.c (virCapabilitiesNew) (virCapabilitiesAddGuestFeature): Adjust signature. * src/bhyve/bhyve_capabilities.c (virBhyveCapsBuild): Update clients. * src/esx/esx_driver.c (esxCapsInit): Likewise. * src/libxl/libxl_conf.c (libxlMakeCapabilities): Likewise. * src/lxc/lxc_conf.c (virLXCDriverCapsInit): Likewise. * src/openvz/openvz_conf.c (openvzCapsInit): Likewise. * src/parallels/parallels_driver.c (parallelsBuildCapabilities): Likewise. * src/phyp/phyp_driver.c (phypCapsInit): Likewise. * src/qemu/qemu_capabilities.c (virQEMUCapsInit) (virQEMUCapsInitGuestFromBinary): Likewise. * src/security/virt-aa-helper.c (get_definition): Likewise. * src/test/test_driver.c (testBuildCapabilities): Likewise. * src/uml/uml_conf.c (umlCapsInit): Likewise. * src/vbox/vbox_tmpl.c (vboxCapsInit): Likewise. * src/vmware/vmware_conf.c (vmwareCapsInit): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorBuildCapabilities): Likewise. * src/xenapi/xenapi_driver.c (getCapsObject): Likewise. * tests/qemucaps2xmltest.c (testGetCaps): Likewise. * tests/testutils.c (virTestGenericCapsInit): Likewise. * tests/testutilslxc.c (testLXCCapsInit): Likewise. * tests/testutilsqemu.c (testQemuCapsInit): Likewise. * tests/testutilsxen.c (testXenCapsInit): Likewise. * tests/vircaps2xmltest.c (buildVirCapabilities): Likewise. * tests/vircapstest.c (buildNUMATopology): Likewise. * tests/vmx2xmltest.c (testCapsInit): Likewise. * tests/xml2vmxtest.c (testCapsInit): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-07-14 12:56:13 +00:00
caps = virCapabilitiesNew(VIR_ARCH_X86_64, true, true);
} else {
capabilities: use bool instead of int While preparing to add a capability for active commit, I noticed that the existing code was abusing int for boolean values. * src/conf/capabilities.h (_virCapsGuestFeature, _virCapsHost) (virCapabilitiesNew, virCapabilitiesAddGuestFeature): Improve types. * src/conf/capabilities.c (virCapabilitiesNew) (virCapabilitiesAddGuestFeature): Adjust signature. * src/bhyve/bhyve_capabilities.c (virBhyveCapsBuild): Update clients. * src/esx/esx_driver.c (esxCapsInit): Likewise. * src/libxl/libxl_conf.c (libxlMakeCapabilities): Likewise. * src/lxc/lxc_conf.c (virLXCDriverCapsInit): Likewise. * src/openvz/openvz_conf.c (openvzCapsInit): Likewise. * src/parallels/parallels_driver.c (parallelsBuildCapabilities): Likewise. * src/phyp/phyp_driver.c (phypCapsInit): Likewise. * src/qemu/qemu_capabilities.c (virQEMUCapsInit) (virQEMUCapsInitGuestFromBinary): Likewise. * src/security/virt-aa-helper.c (get_definition): Likewise. * src/test/test_driver.c (testBuildCapabilities): Likewise. * src/uml/uml_conf.c (umlCapsInit): Likewise. * src/vbox/vbox_tmpl.c (vboxCapsInit): Likewise. * src/vmware/vmware_conf.c (vmwareCapsInit): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorBuildCapabilities): Likewise. * src/xenapi/xenapi_driver.c (getCapsObject): Likewise. * tests/qemucaps2xmltest.c (testGetCaps): Likewise. * tests/testutils.c (virTestGenericCapsInit): Likewise. * tests/testutilslxc.c (testLXCCapsInit): Likewise. * tests/testutilsqemu.c (testQemuCapsInit): Likewise. * tests/testutilsxen.c (testXenCapsInit): Likewise. * tests/vircaps2xmltest.c (buildVirCapabilities): Likewise. * tests/vircapstest.c (buildNUMATopology): Likewise. * tests/vmx2xmltest.c (testCapsInit): Likewise. * tests/xml2vmxtest.c (testCapsInit): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-07-14 12:56:13 +00:00
caps = virCapabilitiesNew(VIR_ARCH_I686, true, true);
}
if (!caps)
return NULL;
virCapabilitiesAddHostMigrateTransport(caps, "vpxmigr");
if (esxLookupHostSystemBiosUuid(priv, caps->host.host_uuid) < 0)
goto failure;
/* i686 */
guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM,
VIR_ARCH_I686,
NULL, NULL, 0,
NULL);
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_VMWARE,
NULL, NULL, 0, NULL);
/* x86_64 */
if (supportsLongMode == esxVI_Boolean_True) {
guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM,
VIR_ARCH_X86_64,
NULL, NULL,
0, NULL);
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_VMWARE,
NULL, NULL, 0, NULL);
}
return caps;
failure:
virObjectUnref(caps);
return NULL;
}
static int
esxConnectToHost(esxPrivate *priv,
virConnectPtr conn,
virConnectAuthPtr auth,
char **vCenterIPAddress)
{
int result = -1;
g_autofree char *ipAddress = NULL;
g_autofree char *username = NULL;
g_autofree char *password = NULL;
g_autofree char *url = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *hostSystem = NULL;
esxVI_Boolean inMaintenanceMode = esxVI_Boolean_Undefined;
esxVI_ProductLine expectedProductLine = STRCASEEQ(conn->uri->scheme, "esx")
? esxVI_ProductLine_ESX
: esxVI_ProductLine_GSX;
ESX_VI_CHECK_ARG_LIST(vCenterIPAddress);
if (esxUtil_ResolveHostname(conn->uri->server, &ipAddress) < 0)
return -1;
if (conn->uri->user) {
username = g_strdup(conn->uri->user);
} else {
if (!(username = virAuthGetUsername(conn, auth, "esx", "root",
conn->uri->server)))
goto cleanup;
}
if (!(password = virAuthGetPassword(conn, auth, "esx", username,
conn->uri->server)))
goto cleanup;
url = g_strdup_printf("%s://%s:%d/sdk", priv->parsedUri->transport,
conn->uri->server, conn->uri->port);
if (esxVI_Context_Alloc(&priv->host) < 0 ||
esxVI_Context_Connect(priv->host, url, ipAddress, username, password,
priv->parsedUri) < 0 ||
esxVI_Context_LookupManagedObjects(priv->host) < 0) {
goto cleanup;
}
if (priv->host->productLine != expectedProductLine) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Expecting '%s' to be a %s host but found a %s host"),
conn->uri->server,
esxVI_ProductLineToDisplayName(expectedProductLine),
esxVI_ProductLineToDisplayName(priv->host->productLine));
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_WARN("The server is in maintenance mode");
*vCenterIPAddress = g_strdup(*vCenterIPAddress);
result = 0;
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&hostSystem);
return result;
}
static int
esxConnectToVCenter(esxPrivate *priv,
virConnectPtr conn,
virConnectAuthPtr auth,
const char *hostname,
const char *hostSystemIPAddress)
{
g_autofree char *ipAddress = NULL;
g_autofree char *username = NULL;
g_autofree char *password = NULL;
g_autofree char *url = NULL;
if (!hostSystemIPAddress &&
(!priv->parsedUri->path || STREQ(priv->parsedUri->path, "/"))) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Path has to specify the datacenter and compute resource"));
return -1;
}
if (esxUtil_ResolveHostname(hostname, &ipAddress) < 0)
return -1;
if (conn->uri->user) {
username = g_strdup(conn->uri->user);
} else {
if (!(username = virAuthGetUsername(conn, auth, "esx", "administrator",
hostname)))
return -1;
}
if (!(password = virAuthGetPassword(conn, auth, "esx", username, hostname)))
return -1;
url = g_strdup_printf("%s://%s:%d/sdk", priv->parsedUri->transport, hostname,
conn->uri->port);
if (esxVI_Context_Alloc(&priv->vCenter) < 0 ||
esxVI_Context_Connect(priv->vCenter, url, ipAddress, username,
password, priv->parsedUri) < 0) {
return -1;
}
if (priv->vCenter->productLine != esxVI_ProductLine_VPX) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Expecting '%s' to be a %s host but found a %s host"),
hostname,
esxVI_ProductLineToDisplayName(esxVI_ProductLine_VPX),
esxVI_ProductLineToDisplayName(priv->vCenter->productLine));
return -1;
}
if (hostSystemIPAddress) {
if (esxVI_Context_LookupManagedObjectsByHostSystemIp(priv->vCenter, hostSystemIPAddress) < 0)
return -1;
} else {
if (esxVI_Context_LookupManagedObjectsByPath(priv->vCenter,
priv->parsedUri->path) < 0) {
return -1;
}
}
return 0;
}
/*
* URI format: {vpx|esx|gsx}://[<username>@]<hostname>[:<port>]/[<path>][?<query parameter>...]
* <path> = [<folder>/...]<datacenter>/[<folder>/...]<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. As datacenters and computeresources can be organized in
* folders those have to be included in <path>.
*
* 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 is 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 is 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
esxConnectOpen(virConnectPtr conn, virConnectAuthPtr auth,
virConf *conf G_GNUC_UNUSED,
unsigned int flags)
{
virDrvOpenStatus result = VIR_DRV_OPEN_ERROR;
esxPrivate *priv = NULL;
g_autofree char *potentialVCenterIPAddress = NULL;
g_autofree char *vCenterIPAddress = NULL;
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
if (STRCASENEQ(conn->uri->scheme, "vpx") &&
STRNEQ(conn->uri->path, "/")) {
VIR_WARN("Ignoring unexpected path '%s' for non-vpx scheme '%s'",
conn->uri->path, conn->uri->scheme);
}
/* Allocate per-connection private data */
priv = g_new0(esxPrivate, 1);
if (esxUtil_ParseUri(&priv->parsedUri, conn->uri) < 0)
goto cleanup;
priv->maxVcpus = -1;
priv->supportsVMotion = esxVI_Boolean_Undefined;
priv->supportsLongMode = esxVI_Boolean_Undefined;
priv->supportsScreenshot = esxVI_Boolean_Undefined;
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->parsedUri->transport, "https")) {
conn->uri->port = 443;
} else {
conn->uri->port = 80;
}
} else { /* GSX */
if (STRCASEEQ(priv->parsedUri->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, conn, auth,
&potentialVCenterIPAddress) < 0) {
goto cleanup;
}
/* Connect to vCenter */
if (priv->parsedUri->vCenter) {
if (STREQ(priv->parsedUri->vCenter, "*")) {
if (!potentialVCenterIPAddress) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("This host is not managed by a vCenter"));
goto cleanup;
}
vCenterIPAddress = g_strdup(potentialVCenterIPAddress);
} else {
if (esxUtil_ResolveHostname(priv->parsedUri->vCenter,
&vCenterIPAddress) < 0) {
goto cleanup;
}
if (potentialVCenterIPAddress &&
STRNEQ(vCenterIPAddress, potentialVCenterIPAddress)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("This host is managed by a vCenter with IP "
"address %s, but a mismatching vCenter '%s' "
"(%s) has been specified"),
potentialVCenterIPAddress, priv->parsedUri->vCenter,
vCenterIPAddress);
goto cleanup;
}
}
if (esxConnectToVCenter(priv, conn, auth,
vCenterIPAddress,
priv->host->ipAddress) < 0) {
goto cleanup;
}
}
priv->primary = priv->host;
} else { /* VPX */
/* Connect to vCenter */
if (esxConnectToVCenter(priv, conn, auth,
conn->uri->server,
NULL) < 0) {
goto cleanup;
}
priv->primary = priv->vCenter;
}
/* Setup capabilities */
priv->caps = esxCapsInit(priv);
if (!priv->caps)
goto cleanup;
if (!(priv->xmlopt = virVMXDomainXMLConfInit(priv->caps)))
goto cleanup;
conn->privateData = g_steal_pointer(&priv);
result = VIR_DRV_OPEN_SUCCESS;
cleanup:
esxFreePrivate(&priv);
return result;
}
static int
esxConnectClose(virConnectPtr conn)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
int result = 0;
if (priv->host) {
if (esxVI_EnsureSession(priv->host) < 0 ||
esxVI_Logout(priv->host) < 0) {
result = -1;
}
}
if (priv->vCenter) {
if (esxVI_EnsureSession(priv->vCenter) < 0 ||
esxVI_Logout(priv->vCenter) < 0) {
result = -1;
}
}
esxFreePrivate(&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 ||
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 esxVI_Boolean
esxSupportsScreenshot(esxPrivate *priv)
{
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *hostSystem = NULL;
if (priv->supportsScreenshot != esxVI_Boolean_Undefined)
return priv->supportsScreenshot;
if (esxVI_EnsureSession(priv->primary) < 0)
return esxVI_Boolean_Undefined;
if (esxVI_String_AppendValueToList(&propertyNameList,
"capability.screenshotSupported") < 0 ||
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
&hostSystem) < 0 ||
esxVI_GetBoolean(hostSystem, "capability.screenshotSupported",
&priv->supportsScreenshot,
esxVI_Occurrence_RequiredItem) < 0)
goto cleanup;
cleanup:
/*
* If we goto cleanup in case of an error then priv->supportsScreenshot is
* still esxVI_Boolean_Undefined, therefore we don't need to set it.
*/
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&hostSystem);
return priv->supportsScreenshot;
}
static int
esxConnectSupportsFeature(virConnectPtr conn, int feature)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
esxVI_Boolean supportsVMotion = esxVI_Boolean_Undefined;
switch ((virDrvFeature) 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 &&
supportsVMotion == esxVI_Boolean_True ? 1 : 0;
lib: Fix calling of virNetworkUpdate() driver callback The order in which virNetworkUpdate() accepts @section and @command arguments is not the same as in which it passes them onto networkUpdate() callback. Until recently, it did not really matter, because calling the API on client side meant arguments were encoded in reversed order (compared to the public API), but then on the server it was fixed again - because the server decoded RPC (still swapped), called public API (still swapped) and in turn called the network driver callback (with reversing the order - so magically fixing the order). Long story short, if the public API is called even number of times those swaps cancel each other out. The problem is when the API is called an odd numbed of times - which happens with split daemons and the right URI. There's one call in the client (e.g. virsh net-update), the other in a hypervisor daemon (say virtqemud) which ends up calling the API in the virnetworkd. The fix is obvious - fix the order in which arguments are passed to the callback. But, to maintain compatibility with older, yet unfixed, daemons new connection feature is introduced. The feature is detected just before calling the callback and allows client to pass arguments in correct order (talking to fixed daemon) or in reversed order (talking to older daemon). Unfortunately, older client talking to newer daemon can't be fixed. Let's hope that it's less frequent scenario. Fixes: 574b9bc66b6b10cc4cf50f299c3f0ff55f2cbefb Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1870552 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
2021-03-16 09:33:26 +00:00
case VIR_DRV_FEATURE_NETWORK_UPDATE_HAS_CORRECT_ORDER:
return 1;
case VIR_DRV_FEATURE_FD_PASSING:
case VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION:
case VIR_DRV_FEATURE_MIGRATION_DIRECT:
case VIR_DRV_FEATURE_MIGRATION_OFFLINE:
case VIR_DRV_FEATURE_MIGRATION_P2P:
case VIR_DRV_FEATURE_MIGRATION_PARAMS:
case VIR_DRV_FEATURE_MIGRATION_V2:
case VIR_DRV_FEATURE_MIGRATION_V3:
case VIR_DRV_FEATURE_PROGRAM_KEEPALIVE:
case VIR_DRV_FEATURE_REMOTE:
case VIR_DRV_FEATURE_REMOTE_CLOSE_CALLBACK:
case VIR_DRV_FEATURE_REMOTE_EVENT_CALLBACK:
case VIR_DRV_FEATURE_TYPED_PARAM_STRING:
case VIR_DRV_FEATURE_XML_MIGRATABLE:
default:
return 0;
}
}
static const char *
esxConnectGetType(virConnectPtr conn G_GNUC_UNUSED)
{
return "ESX";
}
static int
esxConnectGetVersion(virConnectPtr conn, unsigned long *version)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
*version = priv->primary->productVersion;
return 0;
}
static char *
esxConnectGetHostname(virConnectPtr conn)
{
2010-01-31 14:29:02 +00:00
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;
}
for (dynamicProperty = hostSystem->propSet; dynamicProperty;
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 || strlen(hostName) < 1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or empty 'hostName' property"));
goto cleanup;
}
if (!domainName || strlen(domainName) < 1) {
complete = g_strdup(hostName);
} else {
complete = g_strdup_printf("%s.%s", hostName, domainName);
}
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&hostSystem);
return complete;
}
static int
esxNodeGetInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo)
{
int result = -1;
2010-01-31 14:29:02 +00:00
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;
}
for (dynamicProperty = hostSystem->propSet; dynamicProperty;
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') {
2009-09-23 12:37:26 +00:00
if (STRPREFIX(ptr, " ")) {
memmove(ptr, ptr + 1, strlen(ptr + 1) + 1);
continue;
} else if (STRPREFIX(ptr, "(R)") || STRPREFIX(ptr, "(C)")) {
2009-09-23 12:37:26 +00:00
memmove(ptr, ptr + 3, strlen(ptr + 3) + 1);
continue;
} else if (STRPREFIX(ptr, "(TM)")) {
memmove(ptr, ptr + 4, strlen(ptr + 4) + 1);
continue;
}
++ptr;
}
/* Make sure the string fits in mode */
dynamicProperty->val->string[sizeof(nodeinfo->model) - 1] = '\0';
if (virStrcpyStatic(nodeinfo->model, dynamicProperty->val->string) < 0) {
virReportError(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 *
esxConnectGetCapabilities(virConnectPtr conn)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
return virCapabilitiesFormatXML(priv->caps);
}
static int
esxConnectListDomains(virConnectPtr conn, int *ids, int maxids)
{
bool success = false;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
esxVI_ObjectContent *virtualMachineList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_VirtualMachinePowerState powerState;
int count = 0;
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;
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) {
virReportError(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
esxConnectNumOfDomains(virConnectPtr conn)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
return esxVI_LookupNumberOfDomainsByPowerState
(priv->primary, esxVI_VirtualMachinePowerState_PoweredOn, false);
}
static virDomainPtr
esxDomainLookupByID(virConnectPtr conn, int id)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachineList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachinePowerState powerState;
int id_candidate = -1;
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;
virtualMachine = virtualMachine->_next) {
g_autofree char *name_candidate = NULL;
if (esxVI_GetVirtualMachinePowerState(virtualMachine,
&powerState) < 0) {
goto cleanup;
}
/* Only running/suspended domains have an ID != -1 */
if (powerState == esxVI_VirtualMachinePowerState_PoweredOff)
continue;
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, id);
if (!domain)
goto cleanup;
break;
}
if (!domain)
virReportError(VIR_ERR_NO_DOMAIN, _("No domain with ID %d"), id);
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachineList);
return domain;
}
static virDomainPtr
esxDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachinePowerState powerState;
int id = -1;
g_autofree 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,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0 ||
esxVI_GetVirtualMachineIdentity(virtualMachine, &id, &name, NULL) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
/* Only running/suspended virtual machines have an ID != -1 */
if (powerState == esxVI_VirtualMachinePowerState_PoweredOff)
id = -1;
domain = virGetDomain(conn, name, uuid, id);
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
return domain;
}
static virDomainPtr
esxDomainLookupByName(virConnectPtr conn, const char *name)
{
2010-01-31 14:29:02 +00:00
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_RequiredItem) < 0) {
goto cleanup;
}
if (esxVI_GetVirtualMachineIdentity(virtualMachine, &id, NULL, uuid) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
/* Only running/suspended virtual machines have an ID != -1 */
if (powerState == esxVI_VirtualMachinePowerState_PoweredOff)
id = -1;
domain = virGetDomain(conn, name, uuid, id);
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
return domain;
}
static int
esxDomainSuspend(virDomainPtr domain)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_VirtualMachinePowerState powerState;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree 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->parsedUri->autoAnswer) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
virReportError(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,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(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);
return result;
}
static int
esxDomainResume(virDomainPtr domain)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_VirtualMachinePowerState powerState;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree 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->parsedUri->autoAnswer) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_Suspended) {
virReportError(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,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(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);
return result;
}
static int
esxDomainShutdownFlags(virDomainPtr domain, unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_VirtualMachinePowerState powerState;
virCheckFlags(0, -1);
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_String_AppendValueToList(&propertyNameList,
"runtime.powerState") < 0 ||
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
propertyNameList, &virtualMachine,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
virReportError(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
esxDomainShutdown(virDomainPtr domain)
{
return esxDomainShutdownFlags(domain, 0);
}
static int
esxDomainReboot(virDomainPtr domain, unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_VirtualMachinePowerState powerState;
virCheckFlags(0, -1);
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_String_AppendValueToList(&propertyNameList,
"runtime.powerState") < 0 ||
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
propertyNameList, &virtualMachine,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
virReportError(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
esxDomainDestroyFlags(virDomainPtr domain,
unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
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;
g_autofree char *taskInfoErrorMessage = NULL;
virCheckFlags(0, -1);
if (priv->vCenter) {
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->parsedUri->autoAnswer) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not powered on"));
goto cleanup;
}
if (esxVI_PowerOffVM_Task(ctx, virtualMachine->obj, &task) < 0 ||
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_WaitForTaskCompletion(ctx, task, domain->uuid,
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not destroy domain: %s"),
taskInfoErrorMessage);
goto cleanup;
}
2010-07-30 17:50:12 +00:00
domain->id = -1;
result = 0;
cleanup:
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_String_Free(&propertyNameList);
esxVI_ManagedObjectReference_Free(&task);
return result;
}
static int
esxDomainDestroy(virDomainPtr dom)
{
return esxDomainDestroyFlags(dom, 0);
}
static char *
esxDomainGetOSType(virDomainPtr domain G_GNUC_UNUSED)
{
return g_strdup("hvm");
}
xml: use long long internally, to centralize overflow checks On 64-bit platforms, unsigned long and unsigned long long are identical, so we don't have to worry about overflow checks. On 32-bit platforms, anywhere we narrow unsigned long long back to unsigned long, we have to worry about overflow; it's easier to do this in one place by having most of the code use the same or wider types, and only doing the narrowing at the last minute. Therefore, the memory set commands remain unsigned long, and the memory get command now centralizes the overflow check into libvirt.c, so that drivers don't have to repeat the work. This also fixes a bug where xen returned the wrong value on failure (most APIs return -1 on failure, but getMaxMemory must return 0 on failure). * src/driver.h (virDrvDomainGetMaxMemory): Use long long. * src/libvirt.c (virDomainGetMaxMemory): Raise overflow. * src/test/test_driver.c (testGetMaxMemory): Fix driver. * src/rpc/gendispatch.pl (name_to_ProcName): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorGetMaxMemory): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainGetMaxMemory): Likewise. * src/xen/xend_internal.c (xenDaemonDomainGetMaxMemory): Likewise. * src/xen/xend_internal.h (xenDaemonDomainGetMaxMemory): Likewise. * src/xen/xm_internal.c (xenXMDomainGetMaxMemory): Likewise. * src/xen/xm_internal.h (xenXMDomainGetMaxMemory): Likewise. * src/xen/xs_internal.c (xenStoreDomainGetMaxMemory): Likewise. * src/xen/xs_internal.h (xenStoreDomainGetMaxMemory): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainGetMaxMemory): Likewise. * src/esx/esx_driver.c (esxDomainGetMaxMemory): Likewise. * src/libxl/libxl_driver.c (libxlDomainGetMaxMemory): Likewise. * src/qemu/qemu_driver.c (qemudDomainGetMaxMemory): Likewise. * src/lxc/lxc_driver.c (lxcDomainGetMaxMemory): Likewise. * src/uml/uml_driver.c (umlDomainGetMaxMemory): Likewise.
2012-03-03 00:47:16 +00:00
static unsigned long long
esxDomainGetMaxMemory(virDomainPtr domain)
{
2010-01-31 14:29:02 +00:00
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,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
for (dynamicProperty = virtualMachine->propSet; dynamicProperty;
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) {
virReportError(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;
2010-01-31 14:29:02 +00:00
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;
g_autofree 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->parsedUri->autoAnswer) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
virReportError(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;
}
/* max-memory must be a multiple of 4096 kilobyte */
spec->memoryMB->value =
VIR_DIV_UP(memory, 4096) * 4; /* Scale from kilobytes to megabytes */
if (esxVI_ReconfigVM_Task(priv->primary, virtualMachine->obj, spec,
&task) < 0 ||
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(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);
return result;
}
static int
esxDomainSetMemoryFlags(virDomainPtr domain,
unsigned long memory,
unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachineConfigSpec *spec = NULL;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree char *taskInfoErrorMessage = NULL;
virCheckFlags(0, -1);
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
(priv->primary, domain->uuid, NULL, &virtualMachine,
priv->parsedUri->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,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(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);
return result;
}
static int
esxDomainSetMemory(virDomainPtr domain, unsigned long memory)
{
return esxDomainSetMemoryFlags(domain, memory, 0);
}
/*
* libvirt exposed virtual CPU usage in absolute time, ESX doesn't provide this
* information in this format. It exposes it in 20 seconds slots, but it's hard
* to get a reliable absolute time from this. Therefore, disable the code that
* queries the performance counters here for now, but keep it as example for how
* to query a selected performance counter for its values.
*/
#define ESX_QUERY_FOR_USED_CPU_TIME 0
static int
esxDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
{
int result = -1;
2010-01-31 14:29:02 +00:00
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;
#if ESX_QUERY_FOR_USED_CPU_TIME
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;
#endif
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,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
info->state = VIR_DOMAIN_NOSTATE;
for (dynamicProperty = virtualMachine->propSet; dynamicProperty;
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;
#if ESX_QUERY_FOR_USED_CPU_TIME
/* Verify the cached 'used CPU time' performance counter ID */
/* FIXME: Currently no host for a vpx:// connection */
if (priv->host) {
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;
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;
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_WARN("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) {
goto cleanup;
}
for (perfEntityMetricBase = perfEntityMetricBaseList;
perfEntityMetricBase;
perfEntityMetricBase = perfEntityMetricBase->_next) {
VIR_DEBUG("perfEntityMetric ...");
perfEntityMetric =
esxVI_PerfEntityMetric_DynamicCast(perfEntityMetricBase);
if (!perfEntityMetric) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("QueryPerf returned object with unexpected type '%s'"),
esxVI_Type_ToString(perfEntityMetricBase->_type));
goto cleanup;
}
perfMetricIntSeries =
esxVI_PerfMetricIntSeries_DynamicCast(perfEntityMetric->value);
if (!perfMetricIntSeries) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("QueryPerf returned object with unexpected type '%s'"),
esxVI_Type_ToString(perfEntityMetric->value->_type));
goto cleanup;
}
for (; perfMetricIntSeries;
perfMetricIntSeries = perfMetricIntSeries->_next) {
VIR_DEBUG("perfMetricIntSeries ...");
for (value = perfMetricIntSeries->value;
value;
value = value->_next) {
VIR_DEBUG("value %lld", (long long int)value->value);
}
}
}
VIR_DEBUG("usedCpuTimeCounterId %d END", priv->usedCpuTimeCounterId);
/*
* FIXME: Cannot map between relative used-cpu-time and absolute
* info->cpuTime
*/
}
}
#endif
result = 0;
cleanup:
#if ESX_QUERY_FOR_USED_CPU_TIME
/*
* Remove values owned by data structures to prevent them from being freed
* by the call to esxVI_PerfQuerySpec_Free().
*/
if (querySpec) {
querySpec->entity = NULL;
querySpec->format = NULL;
if (querySpec->metricId)
querySpec->metricId->instance = NULL;
}
#endif
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
#if ESX_QUERY_FOR_USED_CPU_TIME
esxVI_PerfMetricId_Free(&perfMetricIdList);
esxVI_Int_Free(&counterIdList);
esxVI_PerfCounterInfo_Free(&perfCounterInfoList);
esxVI_PerfQuerySpec_Free(&querySpec);
esxVI_PerfEntityMetricBase_Free(&perfEntityMetricBaseList);
#endif
return result;
}
static int
esxDomainGetState(virDomainPtr domain,
int *state,
int *reason,
unsigned int flags)
{
int result = -1;
esxPrivate *priv = domain->conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachinePowerState powerState;
virCheckFlags(0, -1);
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;
}
*state = esxVI_VirtualMachinePowerState_ConvertToLibvirt(powerState);
if (reason)
*reason = 0;
result = 0;
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
return result;
}
static char *
esxDomainScreenshot(virDomainPtr domain, virStreamPtr stream,
unsigned int screen, unsigned int flags)
{
char *mimeType = NULL;
esxPrivate *priv = domain->conn->privateData;
esxVI_Boolean supportsScreenshot = esxVI_Boolean_Undefined;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachinePowerState powerState;
g_auto(virBuffer) buffer = VIR_BUFFER_INITIALIZER;
char *url = NULL;
virCheckFlags(0, NULL);
if (screen != 0) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Screen cannot be selected"));
return NULL;
}
supportsScreenshot = esxSupportsScreenshot(priv);
if (supportsScreenshot == esxVI_Boolean_Undefined)
return NULL;
if (supportsScreenshot != esxVI_Boolean_True) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("Screenshot feature is unsupported"));
return NULL;
}
if (esxVI_EnsureSession(priv->primary) < 0)
return NULL;
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) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not powered on"));
goto cleanup;
}
/* Build URL */
virBufferAsprintf(&buffer, "%s://%s:%d/screen?id=", priv->parsedUri->transport,
domain->conn->uri->server, domain->conn->uri->port);
virBufferURIEncodeString(&buffer, virtualMachine->obj->value);
url = virBufferContentAndReset(&buffer);
if (esxStreamOpenDownload(stream, priv, url, 0, 0) < 0)
goto cleanup;
mimeType = g_strdup("image/png");
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
return mimeType;
}
static int
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
esxDomainSetVcpusFlags(virDomainPtr domain, unsigned int nvcpus,
unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
int maxVcpus;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachineConfigSpec *spec = NULL;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree char *taskInfoErrorMessage = NULL;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE, -1);
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
if (nvcpus < 1) {
virReportError(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) {
virReportError(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->parsedUri->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,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(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);
return result;
}
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
static int
esxDomainSetVcpus(virDomainPtr domain, unsigned int nvcpus)
{
return esxDomainSetVcpusFlags(domain, nvcpus, VIR_DOMAIN_AFFECT_LIVE);
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
}
static int
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
esxDomainGetVcpusFlags(virDomainPtr domain, unsigned int flags)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *hostSystem = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_VCPU_MAXIMUM, -1);
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
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;
}
for (dynamicProperty = hostSystem->propSet; dynamicProperty;
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;
}
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
static int
esxDomainGetMaxVcpus(virDomainPtr domain)
{
return esxDomainGetVcpusFlags(domain, (VIR_DOMAIN_AFFECT_LIVE |
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
VIR_DOMAIN_VCPU_MAXIMUM));
}
static char *
drivers: prefer unsigned int for flags Now that the public APIs always use unsigned flags, the internal driver callbacks might as well do likewise. * src/driver.h (vrDrvOpen, virDrvDomainCoreDump) (virDrvDomainGetXMLDesc, virDrvNetworkGetXMLDesc) (virDrvNWFilterGetXMLDesc): Update type. * src/remote/remote_protocol.x (remote_open_args) (remote_domain_core_dump_args, remote_domain_get_xml_desc_args) (remote_network_get_xml_desc_args) (remote_nwfilter_get_xml_desc_args): Likewise. * src/test/test_driver.c: Update clients. * src/remote/remote_driver.c: Likewise. * src/xen/xen_hypervisor.c: Likewise. * src/xen/xen_hypervisor.h: Likewise. * src/xen/xen_driver.c: Likewise. * src/xen/xend_internal.c: Likewise. * src/xen/xend_internal.h: Likewise. * src/xen/xm_internal.c: Likewise. * src/xen/xm_internal.h: Likewise. * src/xen/xs_internal.c: Likewise. * src/xen/xs_internal.h: Likewise. * src/xen/xen_inotify.c: Likewise. * src/xen/xen_inotify.h: Likewise. * src/phyp/phyp_driver.c: Likewise. * src/openvz/openvz_driver.c: Likewise. * src/vmware/vmware_driver.c: Likewise. * src/vbox/vbox_driver.c: Likewise. * src/vbox/vbox_tmpl.c: Likewise. * src/xenapi/xenapi_driver.c: Likewise. * src/esx/esx_driver.c: Likewise. * src/esx/esx_interface_driver.c: Likewise. * src/esx/esx_network_driver.c: Likewise. * src/esx/esx_storage_driver.c: Likewise. * src/esx/esx_device_monitor.c: Likewise. * src/esx/esx_secret_driver.c: Likewise. * src/esx/esx_nwfilter_driver.c: Likewise. * src/interface/netcf_driver.c: Likewise. * src/nwfilter/nwfilter_driver.c: Likewise. * src/libxl/libxl_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/lxc/lxc_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/network/bridge_driver.c: Likewise. * src/secret/secret_driver.c: Likewise. * src/storage/storage_driver.c: Likewise. * src/node_device/node_device_hal.c: Likewise. * src/node_device/node_device_udev.c: Likewise. * src/remote_protocol-structs: Likewise.
2011-07-06 20:40:19 +00:00
esxDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachinePowerState powerState;
int id;
g_autofree char *moref = NULL;
char *vmPathName = NULL;
g_autofree char *datastoreName = NULL;
g_autofree char *directoryName = NULL;
g_autofree char *directoryAndFileName = NULL;
g_auto(virBuffer) buffer = VIR_BUFFER_INITIALIZER;
g_autofree char *url = NULL;
g_autofree char *vmx = NULL;
virVMXContext ctx;
esxVMX_Data data;
virDomainDef *def = NULL;
char *xml = NULL;
domain: Fix unknown flags diagnosis in virDomainGetXMLDesc Many drivers had a comment that they did not validate the incoming 'flags' to virDomainGetXMLDesc() because they were relying on virDomainDefFormat() to do it instead. This used to be the case (at least since 461e0f1a and friends in 0.9.4 added unknown flag checking in general), but regressed in commit 0ecd6851 (1.2.12), when all of the drivers were changed to pass 'flags' through the new helper virDomainDefFormatConvertXMLFlags(). Since this helper silently ignores unknown flags, we need to implement flag checking in each driver instead. Annoyingly, this means that any new flag values added will silently be ignored when targeting an older libvirt, rather than our usual practice of loudly diagnosing an unsupported flag. Add comments in domain_conf.[ch] to remind us to be extra vigilant about the impact when adding flags (a new flag to add data is safe if the older server omitting the requested data doesn't break things in the newer client; a new flag to suppress data rather than enhancing the existing VIR_DOMAIN_XML_SECURE may form a data leak or even a security hole). In the qemu driver, there are multiple callers all funnelling to qemuDomainDefFormatBufInternal(); many of them already validated flags (and often only a subset of the full set of possible flags), but for ease of maintenance, we can also check flags at the common helper function. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-02-14 20:25:01 +00:00
virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL);
memset(&data, 0, sizeof(data));
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 ||
vmx: Expose VMware Managed Object Reference (moref) in XML. If you use the VDDK library to access virtual machines remotely, you really need to know the Managed Object Reference ("moref") of the VM. This must be passed each time you connect to the API. For example nbdkit's VDDK plugin requires a moref to be passed to mount up a VM's disk remotely: nbdkit vddk user=root password=+/tmp/rootpw \ server=esxi.example.com thumbprint=xx:xx:xx:... \ vm=moref=2 \ file="[datastore1] Fedora/Fedora.vmdk" Getting the moref is a huge pain. To get some idea of what it is, why it is needed, and how much trouble it is to get it, see: https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-1-overview.html https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-2-technical.html However the moref is available conveniently in the internals of the libvirt VMX driver. This patch exposes it as a custom XML element using the same "vmware:" namespace which was previously used for the datacenterpath (see libvirt commit 636a99058758a044). It appears in the XML like this: <domain type='vmware' xmlns:vmware='http://libvirt.org/schemas/domain/vmware/1.0'> <name>Fedora</name> ... <vmware:datacenterpath>ha-datacenter</vmware:datacenterpath> <vmware:moref>2</vmware:moref> </domain> Note that the moref can appear as either a simple ID (for esx:// connections) or as a "vm-<ID>" (for vpx:// connections). It should be treated by users as an opaque string. Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
2017-08-25 13:36:58 +00:00
esxVI_GetVirtualMachineMORef(virtualMachine, &moref) < 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;
}
virBufferAsprintf(&buffer, "%s://%s:%d/folder/", priv->parsedUri->transport,
domain->conn->uri->server, domain->conn->uri->port);
virBufferURIEncodeString(&buffer, directoryAndFileName);
virBufferAddLit(&buffer, "?dcPath=");
virBufferURIEncodeString(&buffer, priv->primary->datacenterPath);
virBufferAddLit(&buffer, "&dsName=");
virBufferURIEncodeString(&buffer, datastoreName);
url = virBufferContentAndReset(&buffer);
if (esxVI_CURL_Download(priv->primary->curl, url, &vmx, 0, NULL) < 0)
goto cleanup;
data.ctx = priv->primary;
if (!directoryName) {
data.datastorePathWithoutFileName = g_strdup_printf("[%s]", datastoreName);
} else {
data.datastorePathWithoutFileName = g_strdup_printf("[%s] %s",
datastoreName, directoryName);
}
ctx.opaque = &data;
ctx.parseFileName = esxParseVMXFileName;
ctx.formatFileName = NULL;
ctx.autodetectSCSIControllerModel = NULL;
ctx.datacenterPath = priv->primary->datacenterPath;
vmx: Expose VMware Managed Object Reference (moref) in XML. If you use the VDDK library to access virtual machines remotely, you really need to know the Managed Object Reference ("moref") of the VM. This must be passed each time you connect to the API. For example nbdkit's VDDK plugin requires a moref to be passed to mount up a VM's disk remotely: nbdkit vddk user=root password=+/tmp/rootpw \ server=esxi.example.com thumbprint=xx:xx:xx:... \ vm=moref=2 \ file="[datastore1] Fedora/Fedora.vmdk" Getting the moref is a huge pain. To get some idea of what it is, why it is needed, and how much trouble it is to get it, see: https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-1-overview.html https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-2-technical.html However the moref is available conveniently in the internals of the libvirt VMX driver. This patch exposes it as a custom XML element using the same "vmware:" namespace which was previously used for the datacenterpath (see libvirt commit 636a99058758a044). It appears in the XML like this: <domain type='vmware' xmlns:vmware='http://libvirt.org/schemas/domain/vmware/1.0'> <name>Fedora</name> ... <vmware:datacenterpath>ha-datacenter</vmware:datacenterpath> <vmware:moref>2</vmware:moref> </domain> Note that the moref can appear as either a simple ID (for esx:// connections) or as a "vm-<ID>" (for vpx:// connections). It should be treated by users as an opaque string. Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
2017-08-25 13:36:58 +00:00
ctx.moref = moref;
def = virVMXParseConfig(&ctx, priv->xmlopt, priv->caps, vmx);
if (def) {
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff)
def->id = id;
xml = virDomainDefFormat(def, priv->xmlopt,
virDomainDefFormatConvertXMLFlags(flags));
}
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
g_free(data.datastorePathWithoutFileName);
virDomainDefFree(def);
return xml;
}
static char *
esxConnectDomainXMLFromNative(virConnectPtr conn, const char *nativeFormat,
const char *nativeConfig,
unsigned int flags)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
virVMXContext ctx;
esxVMX_Data data;
virDomainDef *def = NULL;
char *xml = NULL;
virCheckFlags(0, NULL);
memset(&data, 0, sizeof(data));
if (STRNEQ(nativeFormat, VMX_CONFIG_FORMAT_ARGV)) {
virReportError(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;
ctx.datacenterPath = NULL;
vmx: Expose VMware Managed Object Reference (moref) in XML. If you use the VDDK library to access virtual machines remotely, you really need to know the Managed Object Reference ("moref") of the VM. This must be passed each time you connect to the API. For example nbdkit's VDDK plugin requires a moref to be passed to mount up a VM's disk remotely: nbdkit vddk user=root password=+/tmp/rootpw \ server=esxi.example.com thumbprint=xx:xx:xx:... \ vm=moref=2 \ file="[datastore1] Fedora/Fedora.vmdk" Getting the moref is a huge pain. To get some idea of what it is, why it is needed, and how much trouble it is to get it, see: https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-1-overview.html https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-2-technical.html However the moref is available conveniently in the internals of the libvirt VMX driver. This patch exposes it as a custom XML element using the same "vmware:" namespace which was previously used for the datacenterpath (see libvirt commit 636a99058758a044). It appears in the XML like this: <domain type='vmware' xmlns:vmware='http://libvirt.org/schemas/domain/vmware/1.0'> <name>Fedora</name> ... <vmware:datacenterpath>ha-datacenter</vmware:datacenterpath> <vmware:moref>2</vmware:moref> </domain> Note that the moref can appear as either a simple ID (for esx:// connections) or as a "vm-<ID>" (for vpx:// connections). It should be treated by users as an opaque string. Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
2017-08-25 13:36:58 +00:00
ctx.moref = NULL;
def = virVMXParseConfig(&ctx, priv->xmlopt, priv->caps, nativeConfig);
if (def)
xml = virDomainDefFormat(def, priv->xmlopt,
VIR_DOMAIN_DEF_FORMAT_INACTIVE);
virDomainDefFree(def);
return xml;
}
static char *
esxConnectDomainXMLToNative(virConnectPtr conn, const char *nativeFormat,
const char *domainXml,
unsigned int flags)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
int virtualHW_version;
virVMXContext ctx;
esxVMX_Data data;
virDomainDef *def = NULL;
char *vmx = NULL;
virCheckFlags(0, NULL);
memset(&data, 0, sizeof(data));
if (STRNEQ(nativeFormat, VMX_CONFIG_FORMAT_ARGV)) {
virReportError(VIR_ERR_INVALID_ARG,
_("Unsupported config format '%s'"), nativeFormat);
return NULL;
}
virtualHW_version = esxVI_ProductVersionToDefaultVirtualHWVersion
(priv->primary->productLine, priv->primary->productVersion);
if (virtualHW_version < 0)
return NULL;
def = virDomainDefParseString(domainXml, priv->xmlopt,
NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE);
if (!def)
return NULL;
data.ctx = priv->primary;
data.datastorePathWithoutFileName = NULL;
ctx.opaque = &data;
ctx.parseFileName = NULL;
ctx.formatFileName = esxFormatVMXFileName;
ctx.autodetectSCSIControllerModel = esxAutodetectSCSIControllerModel;
ctx.datacenterPath = NULL;
vmx: Expose VMware Managed Object Reference (moref) in XML. If you use the VDDK library to access virtual machines remotely, you really need to know the Managed Object Reference ("moref") of the VM. This must be passed each time you connect to the API. For example nbdkit's VDDK plugin requires a moref to be passed to mount up a VM's disk remotely: nbdkit vddk user=root password=+/tmp/rootpw \ server=esxi.example.com thumbprint=xx:xx:xx:... \ vm=moref=2 \ file="[datastore1] Fedora/Fedora.vmdk" Getting the moref is a huge pain. To get some idea of what it is, why it is needed, and how much trouble it is to get it, see: https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-1-overview.html https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-2-technical.html However the moref is available conveniently in the internals of the libvirt VMX driver. This patch exposes it as a custom XML element using the same "vmware:" namespace which was previously used for the datacenterpath (see libvirt commit 636a99058758a044). It appears in the XML like this: <domain type='vmware' xmlns:vmware='http://libvirt.org/schemas/domain/vmware/1.0'> <name>Fedora</name> ... <vmware:datacenterpath>ha-datacenter</vmware:datacenterpath> <vmware:moref>2</vmware:moref> </domain> Note that the moref can appear as either a simple ID (for esx:// connections) or as a "vm-<ID>" (for vpx:// connections). It should be treated by users as an opaque string. Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
2017-08-25 13:36:58 +00:00
ctx.moref = NULL;
vmx = virVMXFormatConfig(&ctx, priv->xmlopt, def, virtualHW_version);
virDomainDefFree(def);
return vmx;
}
static int
esxConnectListDefinedDomains(virConnectPtr conn, char **const names, int maxnames)
{
bool success = false;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachineList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachinePowerState powerState;
int count = 0;
size_t i;
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;
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
esxConnectNumOfDefinedDomains(virConnectPtr conn)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
return esxVI_LookupNumberOfDomainsByPowerState
(priv->primary, esxVI_VirtualMachinePowerState_PoweredOn, true);
}
static int
esxDomainCreateWithFlags(virDomainPtr domain, unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_VirtualMachinePowerState powerState;
2010-07-30 15:06:20 +00:00
int id = -1;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree 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->parsedUri->autoAnswer) < 0 ||
2010-07-30 15:06:20 +00:00
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0 ||
esxVI_GetVirtualMachineIdentity(virtualMachine, &id, NULL, NULL) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
virReportError(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,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not start domain: %s"),
taskInfoErrorMessage);
goto cleanup;
}
2010-07-30 15:06:20 +00:00
domain->id = id;
result = 0;
cleanup:
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_String_Free(&propertyNameList);
esxVI_ManagedObjectReference_Free(&task);
return result;
}
static int
esxDomainCreate(virDomainPtr domain)
{
return esxDomainCreateWithFlags(domain, 0);
}
static virDomainPtr
esxDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
virDomainDef *def = NULL;
g_autofree char *vmx = NULL;
size_t i;
virDomainDiskDef *disk = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
int virtualHW_version;
virVMXContext ctx;
esxVMX_Data data;
g_autofree char *datastoreName = NULL;
g_autofree char *directoryName = NULL;
g_autofree char *escapedName = NULL;
g_auto(virBuffer) buffer = VIR_BUFFER_INITIALIZER;
g_autofree char *url = NULL;
g_autofree char *datastoreRelatedPath = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *hostSystem = NULL;
esxVI_ManagedObjectReference *resourcePool = NULL;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree char *taskInfoErrorMessage = NULL;
virDomainPtr domain = NULL;
const char *src;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
if (flags & VIR_DOMAIN_DEFINE_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
memset(&data, 0, sizeof(data));
if (esxVI_EnsureSession(priv->primary) < 0)
return NULL;
/* Parse domain XML */
def = virDomainDefParseString(xml, priv->xmlopt,
NULL, parse_flags);
if (!def)
return NULL;
if (virXMLCheckIllegalChars("name", def->name, "\n") < 0)
goto cleanup;
/* Check if an existing domain should be edited */
if (esxVI_LookupVirtualMachineByUuid(priv->primary, def->uuid, NULL,
&virtualMachine,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_OptionalItem) < 0) {
goto cleanup;
}
if (!virtualMachine &&
esxVI_LookupVirtualMachineByName(priv->primary, def->name, NULL,
&virtualMachine,
esxVI_Occurrence_OptionalItem) < 0) {
goto cleanup;
}
if (virtualMachine) {
/* FIXME */
virReportError(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->productLine, 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;
ctx.datacenterPath = NULL;
vmx: Expose VMware Managed Object Reference (moref) in XML. If you use the VDDK library to access virtual machines remotely, you really need to know the Managed Object Reference ("moref") of the VM. This must be passed each time you connect to the API. For example nbdkit's VDDK plugin requires a moref to be passed to mount up a VM's disk remotely: nbdkit vddk user=root password=+/tmp/rootpw \ server=esxi.example.com thumbprint=xx:xx:xx:... \ vm=moref=2 \ file="[datastore1] Fedora/Fedora.vmdk" Getting the moref is a huge pain. To get some idea of what it is, why it is needed, and how much trouble it is to get it, see: https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-1-overview.html https://blogs.vmware.com/vsphere/2012/02/uniquely-identifying-virtual-machines-in-vsphere-and-vcloud-part-2-technical.html However the moref is available conveniently in the internals of the libvirt VMX driver. This patch exposes it as a custom XML element using the same "vmware:" namespace which was previously used for the datacenterpath (see libvirt commit 636a99058758a044). It appears in the XML like this: <domain type='vmware' xmlns:vmware='http://libvirt.org/schemas/domain/vmware/1.0'> <name>Fedora</name> ... <vmware:datacenterpath>ha-datacenter</vmware:datacenterpath> <vmware:moref>2</vmware:moref> </domain> Note that the moref can appear as either a simple ID (for esx:// connections) or as a "vm-<ID>" (for vpx:// connections). It should be treated by users as an opaque string. Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
2017-08-25 13:36:58 +00:00
ctx.moref = NULL;
vmx = virVMXFormatConfig(&ctx, priv->xmlopt, def, virtualHW_version);
if (!vmx)
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 normally 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) {
virReportError(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 &&
conf: move host disk type to util/ A continuation of the migration of disk details to virstoragefile. This patch moves a single enum, but converting the name has quite a bit of fallout. * src/conf/domain_conf.h (virDomainDiskType): Move... * src/util/virstoragefile.h (virStorageType): ...and rename. * src/bhyve/bhyve_command.c (bhyveBuildDiskArgStr) (virBhyveProcessBuildLoadCmd): Update clients. * src/conf/domain_conf.c (virDomainDiskSourceDefParse) (virDomainDiskDefParseXML, virDomainDiskSourceDefFormatInternal) (virDomainDiskDefFormat, virDomainDiskGetActualType) (virDomainDiskDefForeachPath, virDomainDiskSourceIsBlockType): Likewise. * src/conf/snapshot_conf.h (_virDomainSnapshotDiskDef): Likewise. * src/conf/snapshot_conf.c (virDomainSnapshotDiskDefParseXML) (virDomainSnapshotAlignDisks, virDomainSnapshotDiskDefFormat): Likewise. * src/esx/esx_driver.c (esxAutodetectSCSIControllerModel) (esxDomainDefineXML): Likewise. * src/locking/domain_lock.c (virDomainLockManagerAddDisk): Likewise. * src/lxc/lxc_controller.c (virLXCControllerSetupLoopDeviceDisk) (virLXCControllerSetupNBDDeviceDisk) (virLXCControllerSetupLoopDevices, virLXCControllerSetupDisk): Likewise. * src/parallels/parallels_driver.c (parallelsGetHddInfo): Likewise. * src/phyp/phyp_driver.c (phypDiskType): Likewise. * src/qemu/qemu_command.c (qemuGetDriveSourceString) (qemuDomainDiskGetSourceString, qemuBuildDriveStr) (qemuBuildCommandLine, qemuParseCommandLineDisk) (qemuParseCommandLine): Likewise. * src/qemu/qemu_conf.c (qemuCheckSharedDevice) (qemuTranslateDiskSourcePool) (qemuTranslateSnapshotDiskSourcePool): Likewise. * src/qemu/qemu_domain.c (qemuDomainDeviceDefPostParse) (qemuDomainDetermineDiskChain): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetBlockInfo) (qemuDomainSnapshotPrepareDiskExternalBackingInactive) (qemuDomainSnapshotPrepareDiskExternalBackingActive) (qemuDomainSnapshotPrepareDiskExternalOverlayActive) (qemuDomainSnapshotPrepareDiskExternalOverlayInactive) (qemuDomainSnapshotPrepareDiskInternal) (qemuDomainSnapshotPrepare) (qemuDomainSnapshotCreateSingleDiskActive): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainChangeEjectableMedia): Likewise. * src/qemu/qemu_migration.c (qemuMigrationIsSafe): Likewise. * src/security/security_apparmor.c (AppArmorRestoreSecurityImageLabel) (AppArmorSetSecurityImageLabel): Likewise. * src/security/security_dac.c (virSecurityDACSetSecurityImageLabel) (virSecurityDACRestoreSecurityImageLabelInt) (virSecurityDACSetSecurityAllLabel): Likewise. * src/security/security_selinux.c (virSecuritySELinuxRestoreSecurityImageLabelInt) (virSecuritySELinuxSetSecurityImageLabel) (virSecuritySELinuxSetSecurityAllLabel): Likewise. * src/storage/storage_backend.c (virStorageFileBackendForType): Likewise. * src/storage/storage_backend_fs.c (virStorageFileBackendFile) (virStorageFileBackendBlock): Likewise. * src/storage/storage_backend_gluster.c (virStorageFileBackendGluster): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainGetXMLDesc, vboxAttachDrives) (vboxDomainAttachDeviceImpl, vboxDomainDetachDevice): Likewise. * src/vmware/vmware_conf.c (vmwareVmxPath): Likewise. * src/vmx/vmx.c (virVMXParseDisk, virVMXFormatDisk) (virVMXFormatFloppy): Likewise. * src/xenxs/xen_sxpr.c (xenParseSxprDisks, xenParseSxpr) (xenFormatSxprDisk): Likewise. * src/xenxs/xen_xm.c (xenParseXM, xenFormatXMDisk): Likewise. * tests/securityselinuxlabeltest.c (testSELinuxLoadDef): Likewise. * src/libvirt_private.syms (domain_conf.h): Move symbols... (virstoragefile.h): ...as appropriate. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-03-27 21:57:49 +00:00
virDomainDiskGetType(def->disks[i]) == VIR_STORAGE_TYPE_FILE) {
disk = def->disks[i];
break;
}
}
if (!disk) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Domain XML doesn't contain any file-based harddisks, "
"cannot deduce datastore and path for VMX file"));
goto cleanup;
}
src = virDomainDiskGetSource(disk);
if (!src) {
virReportError(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(src, &datastoreName, &directoryName,
NULL) < 0) {
goto cleanup;
}
if (!virStringHasCaseSuffix(src, ".vmdk")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Expecting source '%s' of first file-based harddisk to "
"be a VMDK image"), src);
goto cleanup;
}
virBufferAsprintf(&buffer, "%s://%s:%d/folder/", priv->parsedUri->transport,
conn->uri->server, conn->uri->port);
if (directoryName) {
virBufferURIEncodeString(&buffer, directoryName);
virBufferAddChar(&buffer, '/');
}
escapedName = esxUtil_EscapeDatastoreItem(def->name);
if (!escapedName)
goto cleanup;
virBufferURIEncodeString(&buffer, escapedName);
virBufferAddLit(&buffer, ".vmx?dcPath=");
virBufferURIEncodeString(&buffer, priv->primary->datacenterPath);
virBufferAddLit(&buffer, "&dsName=");
virBufferURIEncodeString(&buffer, datastoreName);
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_CURL_Upload(priv->primary->curl, url, vmx) < 0)
goto cleanup;
/* Register the domain */
if (directoryName) {
datastoreRelatedPath = g_strdup_printf("[%s] %s/%s.vmx", datastoreName,
directoryName, escapedName);
} else {
datastoreRelatedPath = g_strdup_printf("[%s] %s.vmx", datastoreName,
escapedName);
}
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,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_OptionalItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not define domain: %s"),
taskInfoErrorMessage);
goto cleanup;
}
domain = virGetDomain(conn, def->name, def->uuid, -1);
/* FIXME: Add proper rollback in case of an error */
cleanup:
virDomainDefFree(def);
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&hostSystem);
esxVI_ManagedObjectReference_Free(&resourcePool);
esxVI_ManagedObjectReference_Free(&task);
return domain;
}
static virDomainPtr
esxDomainDefineXML(virConnectPtr conn, const char *xml)
{
return esxDomainDefineXMLFlags(conn, xml, 0);
}
static int
esxDomainUndefineFlags(virDomainPtr domain,
unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_Context *ctx = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_VirtualMachinePowerState powerState;
snapshot: prevent stranding snapshot data on domain destruction Just as leaving managed save metadata behind can cause problems when creating a new domain that happens to collide with the name of the just-deleted domain, the same is true of leaving any snapshot metadata behind. For safety sake, extend the semantic change of commit b26a9fa9 to also cover snapshot metadata as a reason to reject undefining an inactive domain. A future patch will make sure that shutdown of a transient domain automatically deletes snapshot metadata (whether by destroy, shutdown, or guest-initiated action). Management apps of transient domains should take care to capture xml of snapshots, if it is necessary to recreate the snapshot metadata on a later transient domain with the same name and uuid. This also documents a new flag that hypervisors can choose to support as a shortcut for taking care of the metadata as part of the undefine process; however, nontrivial driver support for these flags will be deferred to future patches. Note that ESX and VBox can never be transient; therefore, they do not have to worry about automatic cleanup after shutdown (the persistent domain still remains); likewise they never store snapshot metadata, so the undefine flag is trivial. The nontrivial work remaining is thus in the qemu driver. * include/libvirt/libvirt.h.in (VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA): New flag. * src/libvirt.c (virDomainUndefine, virDomainUndefineFlags): Document new limitations and flag. * src/esx/esx_driver.c (esxDomainUndefineFlags): Trivial implementation. * src/vbox/vbox_tmpl.c (vboxDomainUndefineFlags): Likewise. * src/qemu/qemu_driver.c (qemuDomainUndefineFlags): Enforce the limitations.
2011-08-12 15:07:08 +00:00
/* No managed save, so we explicitly reject
* VIR_DOMAIN_UNDEFINE_MANAGED_SAVE. No snapshot metadata for
* ESX, so we can trivially ignore that flag. */
virCheckFlags(VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA, -1);
if (priv->vCenter) {
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,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0 ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_Suspended &&
powerState != esxVI_VirtualMachinePowerState_PoweredOff) {
virReportError(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
esxDomainUndefine(virDomainPtr domain)
{
return esxDomainUndefineFlags(domain, 0);
}
2010-12-30 00:36:31 +00:00
static int
esxDomainGetAutostart(virDomainPtr domain, int *autostart)
{
int result = -1;
esxPrivate *priv = domain->conn->privateData;
esxVI_AutoStartDefaults *defaults = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_AutoStartPowerInfo *powerInfo = NULL;
esxVI_AutoStartPowerInfo *powerInfoList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
*autostart = 0;
if (esxVI_EnsureSession(priv->primary) < 0)
2010-12-30 00:36:31 +00:00
return -1;
/* Check general autostart config */
if (esxVI_LookupAutoStartDefaults(priv->primary, &defaults) < 0)
2010-12-30 00:36:31 +00:00
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)
2010-12-30 00:36:31 +00:00
goto cleanup;
if (!powerInfoList) {
2010-12-30 00:36:31 +00:00
/* 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;
2010-12-30 00:36:31 +00:00
powerInfo = powerInfo->_next) {
if (STREQ(powerInfo->key->value, virtualMachine->obj->value)) {
if (STRCASEEQ(powerInfo->startAction, "powerOn"))
2010-12-30 00:36:31 +00:00
*autostart = 1;
break;
}
}
result = 0;
cleanup:
2010-12-30 00:36:31 +00:00
esxVI_String_Free(&propertyNameList);
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)
2010-12-30 00:36:31 +00:00
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)
2010-12-30 00:36:31 +00:00
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;
2010-12-30 00:36:31 +00:00
powerInfo = powerInfo->_next) {
if (STRNEQ(powerInfo->key->value, virtualMachine->obj->value)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Cannot enable general autostart option "
"without affecting other domains"));
2010-12-30 00:36:31 +00:00
goto cleanup;
}
}
/* Enable autostart in general */
if (esxVI_AutoStartDefaults_Alloc(&spec->defaults) < 0)
2010-12-30 00:36:31 +00:00
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) {
esxVI_AutoStartPowerInfo_Free(&newPowerInfo);
2010-12-30 00:36:31 +00:00
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) {
2010-12-30 00:36:31 +00:00
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:
*
* - https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.ResourceAllocationInfo.html
* - https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.SharesInfo.html
* - https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.SharesInfo.Level.html
*
*
* Available parameters:
*
* - reservation (VIR_TYPED_PARAM_LLONG >= 0, in megaherz)
*
* The amount of CPU resource that is guaranteed to be available to the domain.
*
*
* - limit (VIR_TYPED_PARAM_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_TYPED_PARAM_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 G_GNUC_UNUSED, int *nparams)
{
char *type;
type = g_strdup("allocation");
if (nparams)
*nparams = 3; /* reservation, limit, shares */
return type;
}
static int
esxDomainGetSchedulerParametersFlags(virDomainPtr domain,
virTypedParameterPtr params, int *nparams,
unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
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;
size_t i = 0;
virCheckFlags(0, -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,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
for (dynamicProperty = virtualMachine->propSet;
dynamicProperty && mask != 7 && i < 3 && i < *nparams;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "config.cpuAllocation.reservation") &&
2009-09-23 12:37:26 +00:00
! (mask & (1 << 0))) {
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
esxVI_Type_Long) < 0) {
goto cleanup;
}
if (virTypedParameterAssign(&params[i],
VIR_DOMAIN_SCHEDULER_RESERVATION,
VIR_TYPED_PARAM_LLONG,
dynamicProperty->val->int64) < 0)
goto cleanup;
mask |= 1 << 0;
++i;
} else if (STREQ(dynamicProperty->name,
"config.cpuAllocation.limit") &&
2009-09-23 12:37:26 +00:00
! (mask & (1 << 1))) {
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
esxVI_Type_Long) < 0) {
goto cleanup;
}
if (virTypedParameterAssign(&params[i],
VIR_DOMAIN_SCHEDULER_LIMIT,
VIR_TYPED_PARAM_LLONG,
dynamicProperty->val->int64) < 0)
goto cleanup;
mask |= 1 << 1;
++i;
} else if (STREQ(dynamicProperty->name,
"config.cpuAllocation.shares") &&
2009-09-23 12:37:26 +00:00
! (mask & (1 << 2))) {
if (virTypedParameterAssign(&params[i],
VIR_DOMAIN_SCHEDULER_SHARES,
VIR_TYPED_PARAM_INT, 0) < 0)
goto cleanup;
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;
case esxVI_SharesLevel_Undefined:
default:
virReportEnumRangeError(esxVI_SharesLevel, sharesInfo->level);
esxVI_SharesInfo_Free(&sharesInfo);
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
esxDomainGetSchedulerParameters(virDomainPtr domain,
virTypedParameterPtr params, int *nparams)
{
return esxDomainGetSchedulerParametersFlags(domain, params, nparams, 0);
}
static int
esxDomainSetSchedulerParametersFlags(virDomainPtr domain,
virTypedParameterPtr params, int nparams,
unsigned int flags)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachineConfigSpec *spec = NULL;
esxVI_SharesInfo *sharesInfo = NULL;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree char *taskInfoErrorMessage = NULL;
size_t i;
virCheckFlags(0, -1);
if (virTypedParamsValidate(params, nparams,
VIR_DOMAIN_SCHEDULER_RESERVATION,
VIR_TYPED_PARAM_LLONG,
VIR_DOMAIN_SCHEDULER_LIMIT,
VIR_TYPED_PARAM_LLONG,
VIR_DOMAIN_SCHEDULER_SHARES,
VIR_TYPED_PARAM_INT,
NULL) < 0)
return -1;
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
(priv->primary, domain->uuid, NULL, &virtualMachine,
priv->parsedUri->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, VIR_DOMAIN_SCHEDULER_RESERVATION)) {
if (esxVI_Long_Alloc(&spec->cpuAllocation->reservation) < 0)
goto cleanup;
if (params[i].value.l < 0) {
virReportError(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, VIR_DOMAIN_SCHEDULER_LIMIT)) {
if (esxVI_Long_Alloc(&spec->cpuAllocation->limit) < 0)
goto cleanup;
if (params[i].value.l < -1) {
virReportError(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, VIR_DOMAIN_SCHEDULER_SHARES)) {
if (esxVI_SharesInfo_Alloc(&sharesInfo) < 0 ||
esxVI_Int_Alloc(&sharesInfo->shares) < 0) {
goto cleanup;
}
spec->cpuAllocation->shares = g_steal_pointer(&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:
virReportError(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;
}
}
}
}
if (esxVI_ReconfigVM_Task(priv->primary, virtualMachine->obj, spec,
&task) < 0 ||
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not change scheduler parameters: %s"),
taskInfoErrorMessage);
goto cleanup;
}
result = 0;
cleanup:
esxVI_SharesInfo_Free(&sharesInfo);
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_VirtualMachineConfigSpec_Free(&spec);
esxVI_ManagedObjectReference_Free(&task);
return result;
}
static int
esxDomainSetSchedulerParameters(virDomainPtr domain,
virTypedParameterPtr params, int nparams)
{
return esxDomainSetSchedulerParametersFlags(domain, params, nparams, 0);
}
/* The subset of migration flags we are able to support. */
#define ESX_MIGRATION_FLAGS \
(VIR_MIGRATE_PERSIST_DEST | \
VIR_MIGRATE_UNDEFINE_SOURCE | \
VIR_MIGRATE_LIVE | \
VIR_MIGRATE_PAUSED)
static int
esxDomainMigratePrepare(virConnectPtr dconn,
char **cookie G_GNUC_UNUSED,
int *cookielen G_GNUC_UNUSED,
const char *uri_in G_GNUC_UNUSED,
char **uri_out,
unsigned long flags,
const char *dname G_GNUC_UNUSED,
unsigned long resource G_GNUC_UNUSED)
{
esxPrivate *priv = dconn->privateData;
virCheckFlags(ESX_MIGRATION_FLAGS, -1);
if (!uri_in) {
*uri_out = g_strdup_printf("vpxmigr://%s/%s/%s", priv->vCenter->ipAddress,
priv->vCenter->computeResource->resourcePool->value,
priv->vCenter->hostSystem->_reference->value);
}
return 0;
}
static int
esxDomainMigratePerform(virDomainPtr domain,
const char *cookie G_GNUC_UNUSED,
int cookielen G_GNUC_UNUSED,
const char *uri,
unsigned long flags,
const char *dname,
unsigned long bandwidth G_GNUC_UNUSED)
{
int result = -1;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = domain->conn->privateData;
virURI *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;
g_autofree char *taskInfoErrorMessage = NULL;
virCheckFlags(ESX_MIGRATION_FLAGS, -1);
if (!priv->vCenter) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Migration not possible without a vCenter"));
return -1;
}
if (dname) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Renaming domains on migration not supported"));
return -1;
}
if (esxVI_EnsureSession(priv->vCenter) < 0)
return -1;
/* Parse migration URI */
if (!(parsedUri = virURIParse(uri)))
return -1;
if (!parsedUri->scheme || STRCASENEQ(parsedUri->scheme, "vpxmigr")) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Only vpxmigr:// migration URIs are supported"));
goto cleanup;
}
if (STRCASENEQ(priv->vCenter->ipAddress, parsedUri->server)) {
virReportError(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 || !path_hostSystem) {
virReportError(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->parsedUri->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) {
/*
* FIXME: Need to report the complete list of events. Limit reporting
* to the first event for now.
*/
if (eventList->fullFormattedMessage) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not migrate domain, validation reported a "
"problem: %s"), eventList->fullFormattedMessage);
} else {
virReportError(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,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not migrate domain, migration task finished with "
"an error: %s"),
taskInfoErrorMessage);
goto cleanup;
}
result = 0;
cleanup:
virURIFree(parsedUri);
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_Event_Free(&eventList);
esxVI_ManagedObjectReference_Free(&task);
return result;
}
static virDomainPtr
esxDomainMigrateFinish(virConnectPtr dconn, const char *dname,
const char *cookie G_GNUC_UNUSED,
int cookielen G_GNUC_UNUSED,
const char *uri G_GNUC_UNUSED,
unsigned long flags)
{
virCheckFlags(ESX_MIGRATION_FLAGS, NULL);
return esxDomainLookupByName(dconn, dname);
}
static unsigned long long
esxNodeGetFreeMemory(virConnectPtr conn)
{
unsigned long long result = 0;
unsigned long long usageBytes = 0;
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *hostSystem = NULL;
esxVI_Int *memoryUsage = NULL;
esxVI_Long *memorySize = NULL;
if (esxVI_EnsureSession(priv->primary) < 0)
return 0;
/* Get memory usage of host system */
if (esxVI_String_AppendValueListToList(&propertyNameList,
"summary.quickStats.overallMemoryUsage\0"
"hardware.memorySize\0") < 0 ||
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
&hostSystem) < 0 ||
esxVI_GetInt(hostSystem, "summary.quickStats.overallMemoryUsage",
&memoryUsage, esxVI_Occurrence_RequiredItem) < 0 ||
esxVI_GetLong(hostSystem, "hardware.memorySize", &memorySize,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
usageBytes = (unsigned long long)(memoryUsage->value) * 1048576;
result = memorySize->value - usageBytes;
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&hostSystem);
esxVI_Int_Free(&memoryUsage);
esxVI_Long_Free(&memorySize);
return result;
}
static int
esxConnectIsEncrypted(virConnectPtr conn)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
return STRCASEEQ(priv->parsedUri->transport, "https");
}
static int
esxConnectIsSecure(virConnectPtr conn)
{
2010-01-31 14:29:02 +00:00
esxPrivate *priv = conn->privateData;
return STRCASEEQ(priv->parsedUri->transport, "https");
}
static int
esxConnectIsAlive(virConnectPtr conn)
{
esxPrivate *priv = conn->privateData;
/* XXX we should be able to do something better than this but this is
* simple, safe, and good enough for now. In worst case, the function will
* return true even though the connection is not alive.
*/
if (priv->primary)
return 1;
else
return 0;
}
static int
esxDomainIsActive(virDomainPtr domain)
{
int result = -1;
2010-01-31 14:29:02 +00:00
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,
2009-11-22 20:18:45 +00:00
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)
{
/* ESX has no concept of transient domains, so all of them are
* persistent. However, we do want to check for existence. */
int result = -1;
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
NULL, &virtualMachine,
esxVI_Occurrence_RequiredItem) < 0)
goto cleanup;
result = 1;
cleanup:
esxVI_ObjectContent_Free(&virtualMachine);
return result;
}
static int
esxDomainIsUpdated(virDomainPtr domain G_GNUC_UNUSED)
{
/* ESX domains never have a persistent state that differs from
* current state. However, we do want to check for existence. */
int result = -1;
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
NULL, &virtualMachine,
esxVI_Occurrence_RequiredItem) < 0)
goto cleanup;
result = 0;
cleanup:
esxVI_ObjectContent_Free(&virtualMachine);
return result;
}
static virDomainSnapshotPtr
esxDomainSnapshotCreateXML(virDomainPtr domain, const char *xmlDesc,
unsigned int flags)
{
esxPrivate *priv = domain->conn->privateData;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachineSnapshotTree *rootSnapshotList = NULL;
esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree char *taskInfoErrorMessage = NULL;
virDomainSnapshotPtr snapshot = NULL;
bool diskOnly = (flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) != 0;
bool quiesce = (flags & VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE) != 0;
g_autoptr(virDomainSnapshotDef) def = NULL;
unsigned int parse_flags = 0;
/* ESX supports disk-only and quiesced snapshots; libvirt tracks no
* snapshot metadata so supporting that flag is trivial. */
virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY |
VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE |
VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA |
VIR_DOMAIN_SNAPSHOT_CREATE_VALIDATE, NULL);
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_VALIDATE)
parse_flags = VIR_DOMAIN_SNAPSHOT_PARSE_VALIDATE;
if (esxVI_EnsureSession(priv->primary) < 0)
return NULL;
def = virDomainSnapshotDefParseString(xmlDesc,
priv->xmlopt, NULL, NULL, parse_flags);
if (!def)
return NULL;
if (def->ndisks) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk snapshots not supported yet"));
return NULL;
}
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
(priv->primary, domain->uuid, NULL, &virtualMachine,
priv->parsedUri->autoAnswer) < 0 ||
esxVI_LookupRootSnapshotTreeList(priv->primary, domain->uuid,
&rootSnapshotList) < 0 ||
esxVI_GetSnapshotTreeByName(rootSnapshotList, def->parent.name,
&snapshotTree, NULL,
esxVI_Occurrence_OptionalItem) < 0) {
goto cleanup;
}
if (snapshotTree) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("Snapshot '%s' already exists"), def->parent.name);
goto cleanup;
}
if (esxVI_CreateSnapshot_Task(priv->primary, virtualMachine->obj,
def->parent.name, def->parent.description,
diskOnly ? esxVI_Boolean_False : esxVI_Boolean_True,
quiesce ? esxVI_Boolean_True : esxVI_Boolean_False,
&task) < 0 ||
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not create snapshot: %s"),
taskInfoErrorMessage);
goto cleanup;
}
snapshot = virGetDomainSnapshot(domain, def->parent.name);
cleanup:
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList);
esxVI_ManagedObjectReference_Free(&task);
return snapshot;
}
static char *
esxDomainSnapshotGetXMLDesc(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.parent.name = snapshot->name;
def.parent.description = snapshotTree->description;
def.parent.parent_name = snapshotTreeParent ? snapshotTreeParent->name : NULL;
if (esxVI_DateTime_ConvertToCalendarTime(snapshotTree->createTime,
&def.parent.creationTime) < 0) {
goto cleanup;
}
def.state = esxVI_VirtualMachinePowerState_ConvertToLibvirt
(snapshotTree->state);
virUUIDFormat(snapshot->domain->uuid, uuid_string);
xml = virDomainSnapshotDefFormat(uuid_string, &def, priv->xmlopt,
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;
bool recurse;
bool leaves;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_METADATA |
VIR_DOMAIN_SNAPSHOT_LIST_LEAVES, -1);
recurse = (flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS) == 0;
leaves = (flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) != 0;
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
/* ESX snapshots do not require libvirt to maintain any metadata. */
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_METADATA)
return 0;
if (esxVI_LookupRootSnapshotTreeList(priv->primary, domain->uuid,
&rootSnapshotTreeList) < 0) {
return -1;
}
count = esxVI_GetNumberOfSnapshotTrees(rootSnapshotTreeList, recurse,
leaves);
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;
bool recurse;
bool leaves;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_METADATA |
VIR_DOMAIN_SNAPSHOT_LIST_LEAVES, -1);
recurse = (flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS) == 0;
leaves = (flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) != 0;
if (!names || nameslen < 0) {
virReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument"));
return -1;
}
if (nameslen == 0 || (flags & VIR_DOMAIN_SNAPSHOT_LIST_METADATA))
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,
recurse, leaves);
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList);
return result;
}
static int
esxDomainSnapshotNumChildren(virDomainSnapshotPtr snapshot, unsigned int flags)
{
int count = -1;
esxPrivate *priv = snapshot->domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL;
bool recurse;
bool leaves;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
VIR_DOMAIN_SNAPSHOT_LIST_METADATA |
VIR_DOMAIN_SNAPSHOT_LIST_LEAVES, -1);
recurse = (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) != 0;
leaves = (flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) != 0;
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_LookupRootSnapshotTreeList(priv->primary, snapshot->domain->uuid,
&rootSnapshotTreeList) < 0 ||
esxVI_GetSnapshotTreeByName(rootSnapshotTreeList, snapshot->name,
&snapshotTree, NULL,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
/* ESX snapshots do not require libvirt to maintain any metadata. */
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_METADATA) {
count = 0;
goto cleanup;
}
count = esxVI_GetNumberOfSnapshotTrees(snapshotTree->childSnapshotList,
recurse, leaves);
cleanup:
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList);
return count;
}
static int
esxDomainSnapshotListChildrenNames(virDomainSnapshotPtr snapshot,
char **names, int nameslen,
unsigned int flags)
{
int result = -1;
esxPrivate *priv = snapshot->domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL;
bool recurse;
bool leaves;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
VIR_DOMAIN_SNAPSHOT_LIST_METADATA |
VIR_DOMAIN_SNAPSHOT_LIST_LEAVES, -1);
recurse = (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) != 0;
leaves = (flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) != 0;
if (!names || nameslen < 0) {
virReportError(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, snapshot->domain->uuid,
&rootSnapshotTreeList) < 0 ||
esxVI_GetSnapshotTreeByName(rootSnapshotTreeList, snapshot->name,
&snapshotTree, NULL,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
/* ESX snapshots do not require libvirt to maintain any metadata. */
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_METADATA) {
result = 0;
goto cleanup;
}
result = esxVI_GetSnapshotTreeNames(snapshotTree->childSnapshotList,
names, nameslen, recurse, leaves);
cleanup:
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;
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,
NULL,
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,
&currentSnapshotTree,
esxVI_Occurrence_OptionalItem) < 0) {
return -1;
}
if (currentSnapshotTree) {
esxVI_VirtualMachineSnapshotTree_Free(&currentSnapshotTree);
return 1;
}
return 0;
}
static virDomainSnapshotPtr
esxDomainSnapshotGetParent(virDomainSnapshotPtr snapshot, unsigned int flags)
{
esxPrivate *priv = snapshot->domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *rootSnapshotList = NULL;
esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL;
esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL;
virDomainSnapshotPtr parent = NULL;
virCheckFlags(0, NULL);
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;
}
if (!snapshotTreeParent) {
virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
_("snapshot '%s' does not have a parent"),
snapshotTree->name);
goto cleanup;
}
parent = virGetDomainSnapshot(snapshot->domain, snapshotTreeParent->name);
cleanup:
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList);
return parent;
}
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,
&currentSnapshotTree,
esxVI_Occurrence_RequiredItem) < 0) {
return NULL;
}
snapshot = virGetDomainSnapshot(domain, currentSnapshotTree->name);
esxVI_VirtualMachineSnapshotTree_Free(&currentSnapshotTree);
return snapshot;
}
static int
esxDomainSnapshotIsCurrent(virDomainSnapshotPtr snapshot, unsigned int flags)
{
esxPrivate *priv = snapshot->domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL;
esxVI_VirtualMachineSnapshotTree *rootSnapshotList = NULL;
esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
/* Check that snapshot exists. */
if (esxVI_LookupRootSnapshotTreeList(priv->primary, snapshot->domain->uuid,
&rootSnapshotList) < 0 ||
esxVI_GetSnapshotTreeByName(rootSnapshotList, snapshot->name,
&snapshotTree, NULL,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
if (esxVI_LookupCurrentSnapshotTree(priv->primary, snapshot->domain->uuid,
&currentSnapshotTree,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
ret = STREQ(snapshot->name, currentSnapshotTree->name);
cleanup:
esxVI_VirtualMachineSnapshotTree_Free(&currentSnapshotTree);
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList);
return ret;
}
static int
esxDomainSnapshotHasMetadata(virDomainSnapshotPtr snapshot, unsigned int flags)
{
esxPrivate *priv = snapshot->domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *rootSnapshotList = NULL;
esxVI_VirtualMachineSnapshotTree *snapshotTree = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
/* Check that snapshot exists. If so, there is no metadata. */
if (esxVI_LookupRootSnapshotTreeList(priv->primary, snapshot->domain->uuid,
&rootSnapshotList) < 0 ||
esxVI_GetSnapshotTreeByName(rootSnapshotList, snapshot->name,
&snapshotTree, NULL,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
ret = 0;
cleanup:
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList);
return ret;
}
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_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree 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, NULL,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
if (esxVI_RevertToSnapshot_Task(priv->primary, snapshotTree->snapshot, NULL,
esxVI_Boolean_Undefined, &task) < 0 ||
esxVI_WaitForTaskCompletion(priv->primary, task, snapshot->domain->uuid,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(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);
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_Boolean removeChildren = esxVI_Boolean_False;
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
g_autofree char *taskInfoErrorMessage = NULL;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY, -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, NULL,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
/* ESX snapshots do not require any libvirt metadata, making this
* flag trivial once we know we have a valid snapshot. */
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) {
result = 0;
goto cleanup;
}
if (esxVI_RemoveSnapshot_Task(priv->primary, snapshotTree->snapshot,
removeChildren, &task) < 0 ||
esxVI_WaitForTaskCompletion(priv->primary, task, snapshot->domain->uuid,
esx: Improve blocked task detection and fix race condition esxVI_WaitForTaskCompletion can take a UUID to lookup the corresponding domain and check if the current task for it is blocked by a question. It calls another function to do this: esxVI_LookupAndHandleVirtualMachineQuestion looks up the VirtualMachine and checks for a question. If there is a question it calls esxVI_HandleVirtualMachineQuestion to handle it. If there was no question or it has been answered the call to esxVI_LookupAndHandleVirtualMachineQuestion returns 0. If any error occurred during the lookup and answering process -1 is returned. The problem with this is, that -1 is also returned when there was no error but the question could not be answered. So esxVI_WaitForTaskCompletion cannot distinguish between this two situations and reports that a question is blocking the task even when there was actually another problem. This inherent problem didn't surface until vSphere 4.1 when you try to define a new domain. The driver tries to lookup the domain that is just in the process of being registered. There seems to be some kind of race condition and the driver manages to issue a lookup command before the ESX server was able to register the domain. This used to work before. Due to the return value problem described above the driver reported a false error message in that case. To solve this esxVI_WaitForTaskCompletion now takes an additional occurrence parameter that describes whether or not to expect the domain to be existent. Also add a new parameter to esxVI_LookupAndHandleVirtualMachineQuestion that allows to distinguish if the call returned -1 because of an actual error or because the question could not be answered.
2010-07-25 15:45:19 +00:00
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(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);
return result;
}
static int
esxDomainSetMemoryParameters(virDomainPtr domain, virTypedParameterPtr 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;
g_autofree char *taskInfoErrorMessage = NULL;
size_t i;
virCheckFlags(0, -1);
if (virTypedParamsValidate(params, nparams,
VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
VIR_TYPED_PARAM_ULLONG,
NULL) < 0)
return -1;
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_LookupVirtualMachineByUuidAndPrepareForTask
(priv->primary, domain->uuid, NULL, &virtualMachine,
priv->parsedUri->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)) {
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 */
}
}
if (esxVI_ReconfigVM_Task(priv->primary, virtualMachine->obj, spec,
&task) < 0 ||
esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid,
esxVI_Occurrence_RequiredItem,
priv->parsedUri->autoAnswer, &taskInfoState,
&taskInfoErrorMessage) < 0) {
goto cleanup;
}
if (taskInfoState != esxVI_TaskInfoState_Success) {
virReportError(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);
return result;
}
static int
esxDomainGetMemoryParameters(virDomainPtr domain, virTypedParameterPtr 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 (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;
}
/* Scale from megabytes to kilobytes */
if (virTypedParameterAssign(params, VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
VIR_TYPED_PARAM_ULLONG,
reservation->value * 1024) < 0)
goto cleanup;
*nparams = 1;
result = 0;
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_Long_Free(&reservation);
return result;
}
#define MATCH(FLAG) (flags & (FLAG))
static int
esxConnectListAllDomains(virConnectPtr conn,
virDomainPtr **domains,
unsigned int flags)
{
int ret = -1;
esxPrivate *priv = conn->privateData;
bool needIdentity;
bool needPowerState;
virDomainPtr dom;
virDomainPtr *doms = NULL;
size_t ndoms = 0;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachineList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_AutoStartDefaults *autoStartDefaults = NULL;
esxVI_VirtualMachinePowerState powerState;
esxVI_AutoStartPowerInfo *powerInfoList = NULL;
esxVI_AutoStartPowerInfo *powerInfo = NULL;
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
int id;
unsigned char uuid[VIR_UUID_BUFLEN];
int count = 0;
bool autostart;
int state;
esxVI_DynamicProperty *dynamicProperty = NULL;
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
/* check for flags that would produce empty output lists:
* - persistence: all esx machines are persistent
* - managed save: esx doesn't support managed save
*/
if ((MATCH(VIR_CONNECT_LIST_DOMAINS_TRANSIENT) &&
!MATCH(VIR_CONNECT_LIST_DOMAINS_PERSISTENT)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) &&
!MATCH(VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE))) {
if (domains)
*domains = g_new0(virDomainPtr, 1);
ret = 0;
goto cleanup;
}
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
/* check system default autostart value */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_AUTOSTART)) {
if (esxVI_LookupAutoStartDefaults(priv->primary,
&autoStartDefaults) < 0) {
goto cleanup;
}
if (autoStartDefaults->enabled == esxVI_Boolean_True) {
if (esxVI_LookupAutoStartPowerInfoList(priv->primary,
&powerInfoList) < 0) {
goto cleanup;
}
}
}
needIdentity = MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT) ||
domains;
if (needIdentity) {
/* Request required data for esxVI_GetVirtualMachineIdentity */
if (esxVI_String_AppendValueListToList(&propertyNameList,
"configStatus\0"
"name\0"
"config.uuid\0") < 0) {
goto cleanup;
}
}
needPowerState = MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE) ||
MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE) ||
domains;
if (needPowerState) {
if (esxVI_String_AppendValueToList(&propertyNameList,
"runtime.powerState") < 0) {
goto cleanup;
}
}
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT)) {
if (esxVI_String_AppendValueToList(&propertyNameList,
"snapshot.rootSnapshotList") < 0) {
goto cleanup;
}
}
if (esxVI_LookupVirtualMachineList(priv->primary, propertyNameList,
&virtualMachineList) < 0)
goto cleanup;
if (domains) {
doms = g_new0(virDomainPtr, 1);
ndoms = 1;
}
for (virtualMachine = virtualMachineList; virtualMachine;
virtualMachine = virtualMachine->_next) {
g_autofree char *name = NULL;
if (needIdentity) {
if (esxVI_GetVirtualMachineIdentity(virtualMachine, &id,
&name, uuid) < 0) {
goto cleanup;
}
}
if (needPowerState) {
if (esxVI_GetVirtualMachinePowerState(virtualMachine,
&powerState) < 0) {
goto cleanup;
}
}
/* filter by active state */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE) &&
!((MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE) &&
powerState != esxVI_VirtualMachinePowerState_PoweredOff) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE) &&
powerState == esxVI_VirtualMachinePowerState_PoweredOff)))
continue;
/* filter by snapshot existence */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT)) {
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList);
for (dynamicProperty = virtualMachine->propSet; dynamicProperty;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "snapshot.rootSnapshotList")) {
if (esxVI_VirtualMachineSnapshotTree_CastListFromAnyType
(dynamicProperty->val, &rootSnapshotTreeList) < 0) {
goto cleanup;
}
break;
}
}
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT) &&
rootSnapshotTreeList) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT) &&
!rootSnapshotTreeList)))
continue;
}
/* filter by autostart */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_AUTOSTART)) {
autostart = false;
if (autoStartDefaults->enabled == esxVI_Boolean_True) {
for (powerInfo = powerInfoList; powerInfo;
powerInfo = powerInfo->_next) {
if (STREQ(powerInfo->key->value, virtualMachine->obj->value)) {
if (STRCASEEQ(powerInfo->startAction, "powerOn"))
autostart = true;
break;
}
}
}
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_AUTOSTART) &&
autostart) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART) &&
!autostart)))
continue;
}
/* filter by domain state */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE)) {
state = esxVI_VirtualMachinePowerState_ConvertToLibvirt(powerState);
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_RUNNING) &&
state == VIR_DOMAIN_RUNNING) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_PAUSED) &&
state == VIR_DOMAIN_PAUSED) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_SHUTOFF) &&
state == VIR_DOMAIN_SHUTOFF) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_OTHER) &&
(state != VIR_DOMAIN_RUNNING &&
state != VIR_DOMAIN_PAUSED &&
state != VIR_DOMAIN_SHUTOFF))))
continue;
}
/* just count the machines */
if (!doms) {
count++;
continue;
}
VIR_RESIZE_N(doms, ndoms, count, 2);
/* Only running/suspended virtual machines have an ID != -1 */
if (powerState == esxVI_VirtualMachinePowerState_PoweredOff)
id = -1;
if (!(dom = virGetDomain(conn, name, uuid, id)))
goto cleanup;
doms[count++] = dom;
}
if (doms) {
*domains = g_steal_pointer(&doms);
}
ret = count;
cleanup:
if (doms) {
for (id = 0; id < count; id++)
virObjectUnref(doms[id]);
g_free(doms);
}
esxVI_AutoStartDefaults_Free(&autoStartDefaults);
esxVI_AutoStartPowerInfo_Free(&powerInfoList);
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachineList);
esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotTreeList);
return ret;
}
#undef MATCH
static int
esxDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags)
{
int result = -1;
esxPrivate *priv = domain->conn->privateData;
esxVI_ManagedObjectReference *managedObjectReference = NULL;
char uuid_string[VIR_UUID_STRING_BUFLEN] = "";
virCheckFlags(0, -1);
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
virUUIDFormat(domain->uuid, uuid_string);
if (esxVI_FindByUuid(priv->primary, priv->primary->datacenter->_reference,
uuid_string, esxVI_Boolean_True,
esxVI_Boolean_Undefined,
&managedObjectReference) < 0) {
return -1;
}
if (!managedObjectReference) {
virReportError(VIR_ERR_NO_DOMAIN,
_("Could not find domain with UUID '%s'"),
uuid_string);
goto cleanup;
}
result = 0;
cleanup:
esxVI_ManagedObjectReference_Free(&managedObjectReference);
return result;
}
static char *
esxDomainGetHostname(virDomainPtr domain,
unsigned int flags)
{
esxPrivate *priv = domain->conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachinePowerState powerState;
char *hostname = NULL;
char *new_hostname = NULL;
virCheckFlags(0, NULL);
if (esxVI_EnsureSession(priv->primary) < 0)
return NULL;
if (esxVI_String_AppendValueListToList(&propertyNameList,
"runtime.powerState\0"
"guest.hostName") < 0 ||
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
propertyNameList, &virtualMachine,
esxVI_Occurrence_OptionalItem) ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not powered on"));
goto cleanup;
}
if (esxVI_GetStringValue(virtualMachine, "guest.hostName",
&hostname, esxVI_Occurrence_OptionalItem) < 0) {
goto cleanup;
}
if (!hostname) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("hostName field not available (missing VMware Tools?)"));
goto cleanup;
}
new_hostname = g_strdup(hostname);
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
return new_hostname;
}
static int
esxParseIPAddress(const char *ipAddress, int prefixLength,
virDomainIPAddress *addr)
{
virSocketAddr tmp_addr;
virIPAddrType addr_type;
if (virSocketAddrParseAny(&tmp_addr, ipAddress, AF_UNSPEC, false) <= 0)
return 0;
switch (VIR_SOCKET_ADDR_FAMILY(&tmp_addr)) {
case AF_INET:
addr_type = VIR_IP_ADDR_TYPE_IPV4;
break;
case AF_INET6:
addr_type = VIR_IP_ADDR_TYPE_IPV6;
break;
default:
return 0;
}
addr->type = addr_type;
addr->addr = g_strdup(ipAddress);
addr->prefix = prefixLength;
return 1;
}
static int
esxDomainInterfaceAddresses(virDomainPtr domain,
virDomainInterfacePtr **ifaces,
unsigned int source,
unsigned int flags)
{
int result = -1;
esxPrivate *priv = domain->conn->privateData;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_VirtualMachinePowerState powerState;
esxVI_DynamicProperty *dynamicProperty;
esxVI_GuestNicInfo *guestNicInfoList = NULL;
esxVI_GuestNicInfo *guestNicInfo = NULL;
virDomainInterfacePtr *ifaces_ret = NULL;
size_t ifaces_count = 0;
size_t i;
int ret;
virCheckFlags(0, -1);
if (source != VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
_("Unknown IP address data source %d"),
source);
return -1;
}
if (esxVI_EnsureSession(priv->primary) < 0)
return -1;
if (esxVI_String_AppendValueListToList(&propertyNameList,
"runtime.powerState\0"
"guest.net") < 0 ||
esxVI_LookupVirtualMachineByUuid(priv->primary, domain->uuid,
propertyNameList, &virtualMachine,
esxVI_Occurrence_RequiredItem) ||
esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) {
goto cleanup;
}
if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not powered on"));
goto cleanup;
}
for (dynamicProperty = virtualMachine->propSet; dynamicProperty;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "guest.net")) {
if (esxVI_GuestNicInfo_CastListFromAnyType
(dynamicProperty->val, &guestNicInfoList) < 0) {
goto cleanup;
}
}
}
if (!guestNicInfoList) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Missing property '%s' in answer"),
"guest.net");
goto cleanup;
}
for (guestNicInfo = guestNicInfoList; guestNicInfo;
guestNicInfo = guestNicInfo->_next) {
virDomainInterfacePtr iface = NULL;
size_t addrs_count = 0;
if (guestNicInfo->connected != esxVI_Boolean_True ||
!guestNicInfo->network) {
continue;
}
VIR_EXPAND_N(ifaces_ret, ifaces_count, 1);
ifaces_ret[ifaces_count - 1] = g_new0(virDomainInterface, 1);
iface = ifaces_ret[ifaces_count - 1];
iface->naddrs = 0;
iface->name = g_strdup(guestNicInfo->network);
iface->hwaddr = g_strdup(guestNicInfo->macAddress);
if (guestNicInfo->ipConfig) {
esxVI_NetIpConfigInfoIpAddress *ipAddress;
for (ipAddress = guestNicInfo->ipConfig->ipAddress; ipAddress;
ipAddress = ipAddress->_next) {
virDomainIPAddress ip_addr;
ret = esxParseIPAddress(ipAddress->ipAddress,
ipAddress->prefixLength->value, &ip_addr);
if (ret < 0)
goto cleanup;
else if (ret == 0)
continue;
VIR_APPEND_ELEMENT(iface->addrs, addrs_count, ip_addr);
}
} else {
esxVI_String *str;
for (str = guestNicInfo->ipAddress; str;
str = str->_next) {
virDomainIPAddress ip_addr;
/* Not even the netmask seems available... */
ret = esxParseIPAddress(str->value, 0, &ip_addr);
if (ret < 0)
goto cleanup;
else if (ret == 0)
continue;
VIR_APPEND_ELEMENT(iface->addrs, addrs_count, ip_addr);
}
}
iface->naddrs = addrs_count;
}
*ifaces = ifaces_ret;
result = ifaces_count;
cleanup:
if (result < 0) {
if (ifaces_ret) {
for (i = 0; i < ifaces_count; i++)
virDomainInterfaceFree(ifaces_ret[i]);
}
}
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_GuestNicInfo_Free(&guestNicInfoList);
return result;
}
static virHypervisorDriver esxHypervisorDriver = {
.name = "ESX",
.connectOpen = esxConnectOpen, /* 0.7.0 */
.connectClose = esxConnectClose, /* 0.7.0 */
.connectSupportsFeature = esxConnectSupportsFeature, /* 0.7.0 */
.connectGetType = esxConnectGetType, /* 0.7.0 */
.connectGetVersion = esxConnectGetVersion, /* 0.7.0 */
.connectGetHostname = esxConnectGetHostname, /* 0.7.0 */
.nodeGetInfo = esxNodeGetInfo, /* 0.7.0 */
.connectGetCapabilities = esxConnectGetCapabilities, /* 0.7.1 */
.connectListDomains = esxConnectListDomains, /* 0.7.0 */
.connectNumOfDomains = esxConnectNumOfDomains, /* 0.7.0 */
.connectListAllDomains = esxConnectListAllDomains, /* 0.10.2 */
.domainLookupByID = esxDomainLookupByID, /* 0.7.0 */
.domainLookupByUUID = esxDomainLookupByUUID, /* 0.7.0 */
.domainLookupByName = esxDomainLookupByName, /* 0.7.0 */
.domainSuspend = esxDomainSuspend, /* 0.7.0 */
.domainResume = esxDomainResume, /* 0.7.0 */
.domainShutdown = esxDomainShutdown, /* 0.7.0 */
.domainShutdownFlags = esxDomainShutdownFlags, /* 0.9.10 */
.domainReboot = esxDomainReboot, /* 0.7.0 */
.domainDestroy = esxDomainDestroy, /* 0.7.0 */
.domainDestroyFlags = esxDomainDestroyFlags, /* 0.9.4 */
.domainGetOSType = esxDomainGetOSType, /* 0.7.0 */
.domainGetMaxMemory = esxDomainGetMaxMemory, /* 0.7.0 */
.domainSetMaxMemory = esxDomainSetMaxMemory, /* 0.7.0 */
.domainSetMemory = esxDomainSetMemory, /* 0.7.0 */
.domainSetMemoryFlags = esxDomainSetMemoryFlags, /* 5.6.0 */
.domainSetMemoryParameters = esxDomainSetMemoryParameters, /* 0.8.6 */
.domainGetMemoryParameters = esxDomainGetMemoryParameters, /* 0.8.6 */
.domainGetInfo = esxDomainGetInfo, /* 0.7.0 */
.domainGetState = esxDomainGetState, /* 0.9.2 */
.domainScreenshot = esxDomainScreenshot, /* 1.2.10 */
.domainSetVcpus = esxDomainSetVcpus, /* 0.7.0 */
.domainSetVcpusFlags = esxDomainSetVcpusFlags, /* 0.8.5 */
.domainGetVcpusFlags = esxDomainGetVcpusFlags, /* 0.8.5 */
.domainGetMaxVcpus = esxDomainGetMaxVcpus, /* 0.7.0 */
.domainGetXMLDesc = esxDomainGetXMLDesc, /* 0.7.0 */
.connectDomainXMLFromNative = esxConnectDomainXMLFromNative, /* 0.7.0 */
.connectDomainXMLToNative = esxConnectDomainXMLToNative, /* 0.7.2 */
.connectListDefinedDomains = esxConnectListDefinedDomains, /* 0.7.0 */
.connectNumOfDefinedDomains = esxConnectNumOfDefinedDomains, /* 0.7.0 */
.domainCreate = esxDomainCreate, /* 0.7.0 */
.domainCreateWithFlags = esxDomainCreateWithFlags, /* 0.8.2 */
.domainDefineXML = esxDomainDefineXML, /* 0.7.2 */
.domainDefineXMLFlags = esxDomainDefineXMLFlags, /* 1.2.12 */
.domainUndefine = esxDomainUndefine, /* 0.7.1 */
.domainUndefineFlags = esxDomainUndefineFlags, /* 0.9.4 */
.domainGetAutostart = esxDomainGetAutostart, /* 0.9.0 */
.domainSetAutostart = esxDomainSetAutostart, /* 0.9.0 */
.domainGetSchedulerType = esxDomainGetSchedulerType, /* 0.7.0 */
.domainGetSchedulerParameters = esxDomainGetSchedulerParameters, /* 0.7.0 */
.domainGetSchedulerParametersFlags = esxDomainGetSchedulerParametersFlags, /* 0.9.2 */
.domainSetSchedulerParameters = esxDomainSetSchedulerParameters, /* 0.7.0 */
.domainSetSchedulerParametersFlags = esxDomainSetSchedulerParametersFlags, /* 0.9.2 */
.domainMigratePrepare = esxDomainMigratePrepare, /* 0.7.0 */
.domainMigratePerform = esxDomainMigratePerform, /* 0.7.0 */
.domainMigrateFinish = esxDomainMigrateFinish, /* 0.7.0 */
.nodeGetFreeMemory = esxNodeGetFreeMemory, /* 0.7.2 */
.connectIsEncrypted = esxConnectIsEncrypted, /* 0.7.3 */
.connectIsSecure = esxConnectIsSecure, /* 0.7.3 */
.domainIsActive = esxDomainIsActive, /* 0.7.3 */
.domainIsPersistent = esxDomainIsPersistent, /* 0.7.3 */
.domainIsUpdated = esxDomainIsUpdated, /* 0.8.6 */
.domainSnapshotCreateXML = esxDomainSnapshotCreateXML, /* 0.8.0 */
.domainSnapshotGetXMLDesc = esxDomainSnapshotGetXMLDesc, /* 0.8.0 */
.domainSnapshotNum = esxDomainSnapshotNum, /* 0.8.0 */
.domainSnapshotListNames = esxDomainSnapshotListNames, /* 0.8.0 */
.domainSnapshotNumChildren = esxDomainSnapshotNumChildren, /* 0.9.7 */
.domainSnapshotListChildrenNames = esxDomainSnapshotListChildrenNames, /* 0.9.7 */
.domainSnapshotLookupByName = esxDomainSnapshotLookupByName, /* 0.8.0 */
.domainHasCurrentSnapshot = esxDomainHasCurrentSnapshot, /* 0.8.0 */
.domainSnapshotGetParent = esxDomainSnapshotGetParent, /* 0.9.7 */
.domainSnapshotCurrent = esxDomainSnapshotCurrent, /* 0.8.0 */
.domainRevertToSnapshot = esxDomainRevertToSnapshot, /* 0.8.0 */
.domainSnapshotIsCurrent = esxDomainSnapshotIsCurrent, /* 0.9.13 */
.domainSnapshotHasMetadata = esxDomainSnapshotHasMetadata, /* 0.9.13 */
.domainSnapshotDelete = esxDomainSnapshotDelete, /* 0.8.0 */
.connectIsAlive = esxConnectIsAlive, /* 0.9.8 */
.domainHasManagedSaveImage = esxDomainHasManagedSaveImage, /* 1.2.13 */
.domainGetHostname = esxDomainGetHostname, /* 6.8.0 */
.domainInterfaceAddresses = esxDomainInterfaceAddresses, /* 6.8.0 */
};
static virConnectDriver esxConnectDriver = {
.remoteOnly = true,
.uriSchemes = (const char *[]){ "vpx", "esx", "gsx", NULL },
.hypervisorDriver = &esxHypervisorDriver,
.interfaceDriver = &esxInterfaceDriver,
.networkDriver = &esxNetworkDriver,
.storageDriver = &esxStorageDriver,
};
int
esxRegister(void)
{
return virRegisterConnectDriver(&esxConnectDriver,
false);
}