libvirt/src/esx/esx_vi.c

2511 lines
79 KiB
C
Raw Normal View History

/*
* esx_vi.c: client for the VMware VI API 2.5 to manage ESX hosts
*
build: consistently use C99 varargs macros Prior to this patch, there was an inconsistent mix between GNU and C99. For consistency, and potential portability to other compilers, stick with the C99 vararg macro syntax. * src/conf/cpu_conf.c (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/conf/domain_conf.c (virDomainReportError): Likewise. * src/conf/domain_event.c (eventReportError): Likewise. * src/conf/interface_conf.c (virInterfaceReportError): Likewise. * src/conf/network_conf.c (virNetworkReportError): Likewise. * src/conf/node_device_conf.h (virNodeDeviceReportError): Likewise. * src/conf/secret_conf.h (virSecretReportError): Likewise. * src/conf/storage_conf.h (virStorageReportError): Likewise. * src/esx/esx_device_monitor.c (ESX_ERROR): Use C99 rather than GNU vararg macro syntax. * src/esx/esx_driver.c (ESX_ERROR): Likewise. * src/esx/esx_interface_driver.c (ESX_ERROR): Likewise. * src/esx/esx_network_driver.c (ESX_ERROR): Likewise. * src/esx/esx_secret_driver.c (ESX_ERROR): Likewise. * src/esx/esx_storage_driver.c (ESX_ERROR): Likewise. * src/esx/esx_util.c (ESX_ERROR): Likewise. * src/esx/esx_vi.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_methods.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_types.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vmx.c (ESX_ERROR): Likewise. * src/util/hostusb.c (usbReportError): Use C99 rather than GNU vararg macro syntax. * src/util/json.c (virJSONError): Likewise. * src/util/macvtap.c (ReportError): Likewise. * src/util/pci.c (pciReportError): Likewise. * src/util/stats_linux.c (virStatsError): Likewise. * src/util/util.c (virUtilError): Likewise. * src/util/xml.c (virXMLError): Likewise. * src/xen/proxy_internal.c (virProxyError): Use C99 rather than GNU vararg macro syntax. * src/xen/sexpr.c (virSexprError): Likewise. * src/xen/xen_driver.c (xenUnifiedError): Likewise. * src/xen/xen_hypervisor.c (virXenError): Likewise. * src/xen/xen_inotify.c (virXenInotifyError): Likewise. * src/xen/xend_internal.c (virXendError): Likewise. * src/xen/xm_internal.c (xenXMError): Likewise. * src/xen/xs_internal.c (virXenStoreError): Likewise. * src/cpu/cpu.h (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/datatypes.c (virLibConnError): Likewise. * src/interface/netcf_driver.c (interfaceReportError): Likewise. * src/libvirt.c (virLibStreamError): Likewise. * src/lxc/lxc_conf.h (lxcError): Likewise. * src/network/bridge_driver.c (networkReportError): Likewise. * src/nodeinfo.c (nodeReportError): Likewise. * src/opennebula/one_conf.h (oneError): Likewise. * src/openvz/openvz_conf.h (openvzError): Likewise. * src/phyp/phyp_driver.c (PHYP_ERROR): Likewise. * src/qemu/qemu_conf.h (qemuReportError): Likewise. * src/remote/remote_driver.c (errorf): Likewise. * src/security/security_driver.h (virSecurityReportError): Likewise. * src/test/test_driver.c (testError): Likewise. * src/uml/uml_conf.h (umlReportError): Likewise. * src/vbox/vbox_driver.c (vboxError): Likewise. * src/vbox/vbox_tmpl.c (vboxError): Likewise.
2010-03-01 23:38:28 +00:00
* Copyright (C) 2010 Red Hat, Inc.
* Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <config.h>
#include <libxml/parser.h>
#include <libxml/xpathInternals.h>
#include "buf.h"
#include "memory.h"
#include "logging.h"
#include "util.h"
#include "uuid.h"
#include "virterror_internal.h"
#include "xml.h"
#include "esx_vi.h"
#include "esx_vi_methods.h"
#include "esx_util.h"
#define VIR_FROM_THIS VIR_FROM_ESX
build: consistently use C99 varargs macros Prior to this patch, there was an inconsistent mix between GNU and C99. For consistency, and potential portability to other compilers, stick with the C99 vararg macro syntax. * src/conf/cpu_conf.c (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/conf/domain_conf.c (virDomainReportError): Likewise. * src/conf/domain_event.c (eventReportError): Likewise. * src/conf/interface_conf.c (virInterfaceReportError): Likewise. * src/conf/network_conf.c (virNetworkReportError): Likewise. * src/conf/node_device_conf.h (virNodeDeviceReportError): Likewise. * src/conf/secret_conf.h (virSecretReportError): Likewise. * src/conf/storage_conf.h (virStorageReportError): Likewise. * src/esx/esx_device_monitor.c (ESX_ERROR): Use C99 rather than GNU vararg macro syntax. * src/esx/esx_driver.c (ESX_ERROR): Likewise. * src/esx/esx_interface_driver.c (ESX_ERROR): Likewise. * src/esx/esx_network_driver.c (ESX_ERROR): Likewise. * src/esx/esx_secret_driver.c (ESX_ERROR): Likewise. * src/esx/esx_storage_driver.c (ESX_ERROR): Likewise. * src/esx/esx_util.c (ESX_ERROR): Likewise. * src/esx/esx_vi.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_methods.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_types.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vmx.c (ESX_ERROR): Likewise. * src/util/hostusb.c (usbReportError): Use C99 rather than GNU vararg macro syntax. * src/util/json.c (virJSONError): Likewise. * src/util/macvtap.c (ReportError): Likewise. * src/util/pci.c (pciReportError): Likewise. * src/util/stats_linux.c (virStatsError): Likewise. * src/util/util.c (virUtilError): Likewise. * src/util/xml.c (virXMLError): Likewise. * src/xen/proxy_internal.c (virProxyError): Use C99 rather than GNU vararg macro syntax. * src/xen/sexpr.c (virSexprError): Likewise. * src/xen/xen_driver.c (xenUnifiedError): Likewise. * src/xen/xen_hypervisor.c (virXenError): Likewise. * src/xen/xen_inotify.c (virXenInotifyError): Likewise. * src/xen/xend_internal.c (virXendError): Likewise. * src/xen/xm_internal.c (xenXMError): Likewise. * src/xen/xs_internal.c (virXenStoreError): Likewise. * src/cpu/cpu.h (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/datatypes.c (virLibConnError): Likewise. * src/interface/netcf_driver.c (interfaceReportError): Likewise. * src/libvirt.c (virLibStreamError): Likewise. * src/lxc/lxc_conf.h (lxcError): Likewise. * src/network/bridge_driver.c (networkReportError): Likewise. * src/nodeinfo.c (nodeReportError): Likewise. * src/opennebula/one_conf.h (oneError): Likewise. * src/openvz/openvz_conf.h (openvzError): Likewise. * src/phyp/phyp_driver.c (PHYP_ERROR): Likewise. * src/qemu/qemu_conf.h (qemuReportError): Likewise. * src/remote/remote_driver.c (errorf): Likewise. * src/security/security_driver.h (virSecurityReportError): Likewise. * src/test/test_driver.c (testError): Likewise. * src/uml/uml_conf.h (umlReportError): Likewise. * src/vbox/vbox_driver.c (vboxError): Likewise. * src/vbox/vbox_tmpl.c (vboxError): Likewise.
2010-03-01 23:38:28 +00:00
#define ESX_VI_ERROR(code, ...) \
virReportErrorHelper(NULL, VIR_FROM_ESX, code, __FILE__, __FUNCTION__, \
build: consistently use C99 varargs macros Prior to this patch, there was an inconsistent mix between GNU and C99. For consistency, and potential portability to other compilers, stick with the C99 vararg macro syntax. * src/conf/cpu_conf.c (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/conf/domain_conf.c (virDomainReportError): Likewise. * src/conf/domain_event.c (eventReportError): Likewise. * src/conf/interface_conf.c (virInterfaceReportError): Likewise. * src/conf/network_conf.c (virNetworkReportError): Likewise. * src/conf/node_device_conf.h (virNodeDeviceReportError): Likewise. * src/conf/secret_conf.h (virSecretReportError): Likewise. * src/conf/storage_conf.h (virStorageReportError): Likewise. * src/esx/esx_device_monitor.c (ESX_ERROR): Use C99 rather than GNU vararg macro syntax. * src/esx/esx_driver.c (ESX_ERROR): Likewise. * src/esx/esx_interface_driver.c (ESX_ERROR): Likewise. * src/esx/esx_network_driver.c (ESX_ERROR): Likewise. * src/esx/esx_secret_driver.c (ESX_ERROR): Likewise. * src/esx/esx_storage_driver.c (ESX_ERROR): Likewise. * src/esx/esx_util.c (ESX_ERROR): Likewise. * src/esx/esx_vi.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_methods.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_types.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vmx.c (ESX_ERROR): Likewise. * src/util/hostusb.c (usbReportError): Use C99 rather than GNU vararg macro syntax. * src/util/json.c (virJSONError): Likewise. * src/util/macvtap.c (ReportError): Likewise. * src/util/pci.c (pciReportError): Likewise. * src/util/stats_linux.c (virStatsError): Likewise. * src/util/util.c (virUtilError): Likewise. * src/util/xml.c (virXMLError): Likewise. * src/xen/proxy_internal.c (virProxyError): Use C99 rather than GNU vararg macro syntax. * src/xen/sexpr.c (virSexprError): Likewise. * src/xen/xen_driver.c (xenUnifiedError): Likewise. * src/xen/xen_hypervisor.c (virXenError): Likewise. * src/xen/xen_inotify.c (virXenInotifyError): Likewise. * src/xen/xend_internal.c (virXendError): Likewise. * src/xen/xm_internal.c (xenXMError): Likewise. * src/xen/xs_internal.c (virXenStoreError): Likewise. * src/cpu/cpu.h (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/datatypes.c (virLibConnError): Likewise. * src/interface/netcf_driver.c (interfaceReportError): Likewise. * src/libvirt.c (virLibStreamError): Likewise. * src/lxc/lxc_conf.h (lxcError): Likewise. * src/network/bridge_driver.c (networkReportError): Likewise. * src/nodeinfo.c (nodeReportError): Likewise. * src/opennebula/one_conf.h (oneError): Likewise. * src/openvz/openvz_conf.h (openvzError): Likewise. * src/phyp/phyp_driver.c (PHYP_ERROR): Likewise. * src/qemu/qemu_conf.h (qemuReportError): Likewise. * src/remote/remote_driver.c (errorf): Likewise. * src/security/security_driver.h (virSecurityReportError): Likewise. * src/test/test_driver.c (testError): Likewise. * src/uml/uml_conf.h (umlReportError): Likewise. * src/vbox/vbox_driver.c (vboxError): Likewise. * src/vbox/vbox_tmpl.c (vboxError): Likewise.
2010-03-01 23:38:28 +00:00
__LINE__, __VA_ARGS__)
#define ESX_VI__SOAP__REQUEST_HEADER \
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
"<soapenv:Envelope " \
"xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
"xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" " \
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" \
"<soapenv:Body>"
#define ESX_VI__SOAP__REQUEST_FOOTER \
"</soapenv:Body>" \
"</soapenv:Envelope>"
#define ESX_VI__SOAP__RESPONSE_XPATH(_type) \
((char *)"/soapenv:Envelope/soapenv:Body/" \
"vim:"_type"Response/vim:returnval")
#define ESV_VI__XML_TAG__OPEN(_buffer, _element, _type) \
do { \
virBufferAddLit(_buffer, "<"); \
virBufferAdd(_buffer, _element, -1); \
virBufferAddLit(_buffer, " xmlns=\"urn:vim25\" xsi:type=\""); \
virBufferAdd(_buffer, _type, -1); \
virBufferAddLit(_buffer, "\">"); \
} while (0)
#define ESV_VI__XML_TAG__CLOSE(_buffer, _element) \
do { \
virBufferAddLit(_buffer, "</"); \
virBufferAdd(_buffer, _element, -1); \
virBufferAddLit(_buffer, ">"); \
} while (0)
#define ESX_VI__TEMPLATE__ALLOC(_type) \
int \
esxVI_##_type##_Alloc(esxVI_##_type **ptrptr) \
{ \
return esxVI_Alloc((void **)ptrptr, sizeof(esxVI_##_type)); \
}
#define ESX_VI__TEMPLATE__FREE(_type, _body) \
void \
esxVI_##_type##_Free(esxVI_##_type **ptrptr) \
{ \
esxVI_##_type *item = NULL; \
\
if (ptrptr == NULL || *ptrptr == NULL) { \
return; \
} \
\
item = *ptrptr; \
\
_body \
\
VIR_FREE(*ptrptr); \
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Context
*/
/* esxVI_Context_Alloc */
ESX_VI__TEMPLATE__ALLOC(Context);
/* esxVI_Context_Free */
ESX_VI__TEMPLATE__FREE(Context,
{
VIR_FREE(item->url);
VIR_FREE(item->ipAddress);
if (item->curl_handle != NULL) {
curl_easy_cleanup(item->curl_handle);
}
if (item->curl_headers != NULL) {
curl_slist_free_all(item->curl_headers);
}
virMutexDestroy(&item->curl_lock);
VIR_FREE(item->username);
VIR_FREE(item->password);
esxVI_ServiceContent_Free(&item->service);
esxVI_UserSession_Free(&item->session);
esxVI_ManagedObjectReference_Free(&item->datacenter);
esxVI_ManagedObjectReference_Free(&item->vmFolder);
esxVI_ManagedObjectReference_Free(&item->hostFolder);
esxVI_SelectionSpec_Free(&item->fullTraversalSpecList);
});
static size_t
esxVI_CURL_ReadString(char *data, size_t size, size_t nmemb, void *ptrptr)
{
const char *content = *(const char **)ptrptr;
size_t available = 0;
size_t requested = size * nmemb;
if (content == NULL) {
return 0;
}
available = strlen(content);
if (available == 0) {
return 0;
}
if (requested > available) {
requested = available;
}
memcpy(data, content, requested);
*(const char **)ptrptr = content + requested;
return requested;
}
static size_t
esxVI_CURL_WriteBuffer(char *data, size_t size, size_t nmemb, void *buffer)
{
if (buffer != NULL) {
virBufferAdd((virBufferPtr) buffer, data, size * nmemb);
return size * nmemb;
}
return 0;
}
#define ESX_VI__CURL__ENABLE_DEBUG_OUTPUT 0
#if ESX_VI__CURL__ENABLE_DEBUG_OUTPUT
static int
esxVI_CURL_Debug(CURL *curl ATTRIBUTE_UNUSED, curl_infotype type,
char *info, size_t size, void *data ATTRIBUTE_UNUSED)
{
char *buffer = NULL;
/*
* The libcurl documentation says:
*
* The data pointed to by the char * passed to this function WILL NOT
* be zero terminated, but will be exactly of the size as told by the
* size_t argument.
*
* To handle this properly in order to pass the info string to VIR_DEBUG
* a zero terminated copy of the info string has to be allocated.
*/
if (VIR_ALLOC_N(buffer, size + 1) < 0) {
return 0;
}
if (virStrncpy(buffer, info, size, size + 1) == NULL) {
VIR_FREE(buffer);
return 0;
}
switch (type) {
case CURLINFO_TEXT:
if (size > 0 && buffer[size - 1] == '\n') {
buffer[size - 1] = '\0';
}
VIR_DEBUG("CURLINFO_TEXT [[[[%s]]]]", buffer);
break;
case CURLINFO_HEADER_IN:
VIR_DEBUG("CURLINFO_HEADER_IN [[[[%s]]]]", buffer);
break;
case CURLINFO_HEADER_OUT:
VIR_DEBUG("CURLINFO_HEADER_OUT [[[[%s]]]]", buffer);
break;
case CURLINFO_DATA_IN:
VIR_DEBUG("CURLINFO_DATA_IN [[[[%s]]]]", buffer);
break;
case CURLINFO_DATA_OUT:
VIR_DEBUG("CURLINFO_DATA_OUT [[[[%s]]]]", buffer);
break;
default:
VIR_DEBUG0("unknown");
break;
}
VIR_FREE(buffer);
return 0;
}
#endif
static int
esxVI_CURL_Perform(esxVI_Context *ctx, const char *url)
{
CURLcode errorCode;
long responseCode = 0;
#if LIBCURL_VERSION_NUM >= 0x071202 /* 7.18.2 */
const char *redirectUrl = NULL;
#endif
errorCode = curl_easy_perform(ctx->curl_handle);
if (errorCode != CURLE_OK) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
2010-01-26 01:05:45 +00:00
"curl_easy_perform() returned an error: %s (%d) : %s",
curl_easy_strerror(errorCode), errorCode, ctx->curl_error);
return -1;
}
errorCode = curl_easy_getinfo(ctx->curl_handle, CURLINFO_RESPONSE_CODE,
&responseCode);
if (errorCode != CURLE_OK) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"curl_easy_getinfo(CURLINFO_RESPONSE_CODE) returned an "
2010-01-26 01:05:45 +00:00
"error: %s (%d) : %s", curl_easy_strerror(errorCode),
errorCode, ctx->curl_error);
return -1;
}
if (responseCode < 0) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"curl_easy_getinfo(CURLINFO_RESPONSE_CODE) returned a "
"negative response code");
return -1;
}
if (responseCode == 301) {
#if LIBCURL_VERSION_NUM >= 0x071202 /* 7.18.2 */
errorCode = curl_easy_getinfo(ctx->curl_handle, CURLINFO_REDIRECT_URL,
&redirectUrl);
if (errorCode != CURLE_OK) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"curl_easy_getinfo(CURLINFO_REDIRECT_URL) returned "
2010-01-26 01:05:45 +00:00
"an error: %s (%d) : %s", curl_easy_strerror(errorCode),
errorCode, ctx->curl_error);
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"The server redirects from '%s' to '%s'", url,
redirectUrl);
}
#else
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"The server redirects from '%s'", url);
#endif
return -1;
}
return responseCode;
}
int
esxVI_Context_Connect(esxVI_Context *ctx, const char *url,
const char *ipAddress, const char *username,
const char *password, int noVerify)
{
int result = 0;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *datacenterList = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
if (ctx == NULL || url == NULL || ipAddress == NULL || username == NULL ||
password == NULL || ctx->url != NULL || ctx->service != NULL ||
ctx->curl_handle != NULL || ctx->curl_headers != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
goto failure;
}
if (esxVI_String_DeepCopyValue(&ctx->url, url) < 0 ||
esxVI_String_DeepCopyValue(&ctx->ipAddress, ipAddress) < 0) {
goto failure;
}
ctx->curl_handle = curl_easy_init();
if (ctx->curl_handle == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Could not initialize CURL");
goto failure;
}
ctx->curl_headers = curl_slist_append(ctx->curl_headers, "Content-Type: "
"text/xml; charset=UTF-8");
/*
* Add a dummy expect header to stop CURL from waiting for a response code
* 100 (Continue) from the server before continuing the POST operation.
* Waiting for this response would slowdown each communication with the
* server by approx. 2 sec, because the server doesn't send the expected
* 100 (Continue) response and the wait times out resulting in wasting
* approx. 2 sec per POST operation.
*/
ctx->curl_headers = curl_slist_append(ctx->curl_headers,
"Expect: nothing");
if (ctx->curl_headers == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Could not build CURL header list");
goto failure;
}
curl_easy_setopt(ctx->curl_handle, CURLOPT_URL, ctx->url);
curl_easy_setopt(ctx->curl_handle, CURLOPT_USERAGENT, "libvirt-esx");
curl_easy_setopt(ctx->curl_handle, CURLOPT_HEADER, 0);
curl_easy_setopt(ctx->curl_handle, CURLOPT_FOLLOWLOCATION, 0);
curl_easy_setopt(ctx->curl_handle, CURLOPT_SSL_VERIFYPEER, noVerify ? 0 : 1);
curl_easy_setopt(ctx->curl_handle, CURLOPT_SSL_VERIFYHOST, noVerify ? 0 : 2);
curl_easy_setopt(ctx->curl_handle, CURLOPT_COOKIEFILE, "");
curl_easy_setopt(ctx->curl_handle, CURLOPT_HTTPHEADER, ctx->curl_headers);
curl_easy_setopt(ctx->curl_handle, CURLOPT_READFUNCTION,
esxVI_CURL_ReadString);
curl_easy_setopt(ctx->curl_handle, CURLOPT_WRITEFUNCTION,
esxVI_CURL_WriteBuffer);
2010-01-26 01:05:45 +00:00
curl_easy_setopt(ctx->curl_handle, CURLOPT_ERRORBUFFER,
ctx->curl_error);
#if ESX_VI__CURL__ENABLE_DEBUG_OUTPUT
curl_easy_setopt(ctx->curl_handle, CURLOPT_DEBUGFUNCTION, esxVI_CURL_Debug);
curl_easy_setopt(ctx->curl_handle, CURLOPT_VERBOSE, 1);
#endif
if (virMutexInit(&ctx->curl_lock) < 0) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Could not initialize CURL mutex");
goto failure;
}
ctx->username = strdup(username);
ctx->password = strdup(password);
if (ctx->username == NULL || ctx->password == NULL) {
virReportOOMError();
goto failure;
}
if (esxVI_RetrieveServiceContent(ctx, &ctx->service) < 0) {
goto failure;
}
if (STREQ(ctx->service->about->apiType, "HostAgent") ||
STREQ(ctx->service->about->apiType, "VirtualCenter")) {
if (STRPREFIX(ctx->service->about->apiVersion, "2.5")) {
ctx->apiVersion = esxVI_APIVersion_25;
} else if (STRPREFIX(ctx->service->about->apiVersion, "4.0")) {
ctx->apiVersion = esxVI_APIVersion_40;
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Expecting VI API major/minor version '2.5' or '4.0' "
"but found '%s'", ctx->service->about->apiVersion);
goto failure;
}
if (STREQ(ctx->service->about->productLineId, "gsx")) {
if (STRPREFIX(ctx->service->about->version, "2.0")) {
ctx->productVersion = esxVI_ProductVersion_GSX20;
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Expecting GSX major/minor version '2.0' but "
"found '%s'", ctx->service->about->version);
goto failure;
}
} else if (STREQ(ctx->service->about->productLineId, "esx") ||
STREQ(ctx->service->about->productLineId, "embeddedEsx")) {
if (STRPREFIX(ctx->service->about->version, "3.5")) {
ctx->productVersion = esxVI_ProductVersion_ESX35;
} else if (STRPREFIX(ctx->service->about->version, "4.0")) {
ctx->productVersion = esxVI_ProductVersion_ESX40;
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Expecting ESX major/minor version '3.5' or "
"'4.0' but found '%s'",
ctx->service->about->version);
goto failure;
}
} else if (STREQ(ctx->service->about->productLineId, "vpx")) {
if (STRPREFIX(ctx->service->about->version, "2.5")) {
ctx->productVersion = esxVI_ProductVersion_VPX25;
} else if (STRPREFIX(ctx->service->about->version, "4.0")) {
ctx->productVersion = esxVI_ProductVersion_VPX40;
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Expecting VPX major/minor version '2.5' or '4.0' "
"but found '%s'", ctx->service->about->version);
goto failure;
}
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Expecting product 'gsx' or 'esx' or 'embeddedEsx' "
"or 'vpx' but found '%s'",
ctx->service->about->productLineId);
goto failure;
}
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Expecting VI API type 'HostAgent' or 'VirtualCenter' "
"but found '%s'", ctx->service->about->apiType);
goto failure;
}
if (esxVI_Login(ctx, username, password, &ctx->session) < 0) {
goto failure;
}
esxVI_BuildFullTraversalSpecList(&ctx->fullTraversalSpecList);
if (esxVI_String_AppendValueListToList(&propertyNameList,
"vmFolder\0"
"hostFolder\0") < 0) {
goto failure;
}
/* Get pointer to Datacenter for later use */
if (esxVI_LookupObjectContentByType(ctx, ctx->service->rootFolder,
"Datacenter", propertyNameList,
esxVI_Boolean_True,
&datacenterList) < 0) {
goto failure;
}
if (datacenterList == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not retrieve the 'datacenter' object from the VI "
"host/center");
goto failure;
}
ctx->datacenter = datacenterList->obj;
datacenterList->obj = NULL;
/* Get pointer to vmFolder and hostFolder for later use */
for (dynamicProperty = datacenterList->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "vmFolder")) {
if (esxVI_ManagedObjectReference_CastFromAnyType
(dynamicProperty->val, &ctx->vmFolder, "Folder")) {
goto failure;
}
} else if (STREQ(dynamicProperty->name, "hostFolder")) {
if (esxVI_ManagedObjectReference_CastFromAnyType
(dynamicProperty->val, &ctx->hostFolder, "Folder")) {
goto failure;
}
} else {
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
}
}
if (ctx->vmFolder == NULL || ctx->hostFolder == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"The 'datacenter' object is missing the "
"'vmFolder'/'hostFolder' property");
goto failure;
}
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&datacenterList);
return result;
failure:
result = -1;
goto cleanup;
}
int
esxVI_Context_DownloadFile(esxVI_Context *ctx, const char *url, char **content)
{
virBuffer buffer = VIR_BUFFER_INITIALIZER;
int responseCode = 0;
if (content == NULL || *content != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
goto failure;
}
virMutexLock(&ctx->curl_lock);
curl_easy_setopt(ctx->curl_handle, CURLOPT_URL, url);
curl_easy_setopt(ctx->curl_handle, CURLOPT_WRITEDATA, &buffer);
curl_easy_setopt(ctx->curl_handle, CURLOPT_UPLOAD, 0);
curl_easy_setopt(ctx->curl_handle, CURLOPT_HTTPGET, 1);
responseCode = esxVI_CURL_Perform(ctx, url);
virMutexUnlock(&ctx->curl_lock);
if (responseCode < 0) {
goto failure;
} else if (responseCode != 200) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"HTTP response code %d for download from '%s'",
responseCode, url);
goto failure;
}
if (virBufferError(&buffer)) {
virReportOOMError();
goto failure;
}
*content = virBufferContentAndReset(&buffer);
return 0;
failure:
virBufferFreeAndReset(&buffer);
return -1;
}
int
esxVI_Context_UploadFile(esxVI_Context *ctx, const char *url,
const char *content)
{
int responseCode = 0;
if (content == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
virMutexLock(&ctx->curl_lock);
curl_easy_setopt(ctx->curl_handle, CURLOPT_URL, url);
curl_easy_setopt(ctx->curl_handle, CURLOPT_READDATA, &content);
curl_easy_setopt(ctx->curl_handle, CURLOPT_UPLOAD, 1);
curl_easy_setopt(ctx->curl_handle, CURLOPT_INFILESIZE, strlen(content));
responseCode = esxVI_CURL_Perform(ctx, url);
virMutexUnlock(&ctx->curl_lock);
if (responseCode < 0) {
return -1;
} else if (responseCode != 200 && responseCode != 201) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"HTTP response code %d for upload to '%s'",
responseCode, url);
return -1;
}
return 0;
}
int
esxVI_Context_Execute(esxVI_Context *ctx, const char *methodName,
const char *request, esxVI_Response **response,
esxVI_Occurrence occurrence)
{
int result = 0;
virBuffer buffer = VIR_BUFFER_INITIALIZER;
esxVI_Fault *fault = NULL;
char *xpathExpression = NULL;
xmlXPathContextPtr xpathContext = NULL;
xmlNodePtr responseNode = NULL;
if (request == NULL || response == NULL || *response != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
goto failure;
}
if (esxVI_Response_Alloc(response) < 0) {
goto failure;
}
virMutexLock(&ctx->curl_lock);
curl_easy_setopt(ctx->curl_handle, CURLOPT_URL, ctx->url);
curl_easy_setopt(ctx->curl_handle, CURLOPT_WRITEDATA, &buffer);
curl_easy_setopt(ctx->curl_handle, CURLOPT_UPLOAD, 0);
curl_easy_setopt(ctx->curl_handle, CURLOPT_POSTFIELDS, request);
curl_easy_setopt(ctx->curl_handle, CURLOPT_POSTFIELDSIZE, strlen(request));
(*response)->responseCode = esxVI_CURL_Perform(ctx, ctx->url);
virMutexUnlock(&ctx->curl_lock);
if ((*response)->responseCode < 0) {
goto failure;
}
if (virBufferError(&buffer)) {
virReportOOMError();
goto failure;
}
(*response)->content = virBufferContentAndReset(&buffer);
if ((*response)->responseCode == 500 || (*response)->responseCode == 200) {
(*response)->document = xmlReadDoc(BAD_CAST (*response)->content, "",
NULL, XML_PARSE_NONET);
if ((*response)->document == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Response for call to '%s' could not be parsed",
methodName);
goto failure;
}
if (xmlDocGetRootElement((*response)->document) == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Response for call to '%s' is an empty XML document",
methodName);
goto failure;
}
xpathContext = xmlXPathNewContext((*response)->document);
if (xpathContext == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not create XPath context");
goto failure;
}
xmlXPathRegisterNs(xpathContext, BAD_CAST "soapenv",
BAD_CAST "http://schemas.xmlsoap.org/soap/envelope/");
xmlXPathRegisterNs(xpathContext, BAD_CAST "vim", BAD_CAST "urn:vim25");
if ((*response)->responseCode == 500) {
(*response)->node =
virXPathNode("/soapenv:Envelope/soapenv:Body/soapenv:Fault",
xpathContext);
if ((*response)->node == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"HTTP response code %d for call to '%s'. "
"Fault is unknown, XPath evaluation failed",
(*response)->responseCode, methodName);
goto failure;
}
if (esxVI_Fault_Deserialize((*response)->node, &fault) < 0) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"HTTP response code %d for call to '%s'. "
"Fault is unknown, deserialization failed",
(*response)->responseCode, methodName);
goto failure;
}
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"HTTP response code %d for call to '%s'. "
"Fault: %s - %s", (*response)->responseCode,
methodName, fault->faultcode, fault->faultstring);
/* FIXME: Dump raw response until detail part gets deserialized */
VIR_DEBUG("HTTP response code %d for call to '%s' [[[[%s]]]]",
(*response)->responseCode, methodName,
(*response)->content);
goto failure;
} else {
if (virAsprintf(&xpathExpression,
"/soapenv:Envelope/soapenv:Body/vim:%sResponse",
methodName) < 0) {
virReportOOMError();
goto failure;
}
responseNode = virXPathNode(xpathExpression, xpathContext);
if (responseNode == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"XPath evaluation of response for call to '%s' "
"failed", methodName);
goto failure;
}
xpathContext->node = responseNode;
(*response)->node = virXPathNode("./vim:returnval", xpathContext);
switch (occurrence) {
case esxVI_Occurrence_RequiredItem:
if ((*response)->node == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Call to '%s' returned an empty result, "
"expecting a non-empty result", methodName);
goto failure;
}
break;
case esxVI_Occurrence_OptionalItem:
if ((*response)->node != NULL &&
(*response)->node->next != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Call to '%s' returned a list, expecting "
"exactly one item", methodName);
goto failure;
}
break;
case esxVI_Occurrence_List:
/* Any amount of items is valid */
break;
case esxVI_Occurrence_None:
if ((*response)->node != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Call to '%s' returned something, expecting "
"an empty result", methodName);
goto failure;
}
break;
default:
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Invalid argument (occurrence)");
goto failure;
}
}
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"HTTP response code %d for call to '%s'",
(*response)->responseCode, methodName);
goto failure;
}
cleanup:
VIR_FREE(xpathExpression);
xmlXPathFreeContext(xpathContext);
return result;
failure:
virBufferFreeAndReset(&buffer);
esxVI_Response_Free(response);
esxVI_Fault_Free(&fault);
result = -1;
goto cleanup;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Response
*/
/* esxVI_Response_Alloc */
ESX_VI__TEMPLATE__ALLOC(Response);
/* esxVI_Response_Free */
ESX_VI__TEMPLATE__FREE(Response,
{
VIR_FREE(item->content);
if (item->document != NULL) {
xmlFreeDoc(item->document);
}
});
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Enumeration
*/
int
esxVI_Enumeration_CastFromAnyType(const esxVI_Enumeration *enumeration,
esxVI_AnyType *anyType, int *value)
{
int i;
if (anyType == NULL || value == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
*value = 0; /* undefined */
if (STRNEQ(anyType->other, enumeration->type)) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Expecting type '%s' but found '%s'", enumeration->type,
anyType->other);
return -1;
}
for (i = 0; enumeration->values[i].name != NULL; ++i) {
if (STREQ(anyType->value, enumeration->values[i].name)) {
*value = enumeration->values[i].value;
return 0;
}
}
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Unknown value '%s' for %s", anyType->value,
enumeration->type);
return -1;
}
int
esxVI_Enumeration_Serialize(const esxVI_Enumeration *enumeration,
int value, const char *element, virBufferPtr output)
{
int i;
const char *name = NULL;
if (element == NULL || output == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (value == 0) { /* undefined */
return 0;
}
for (i = 0; enumeration->values[i].name != NULL; ++i) {
if (value == enumeration->values[i].value) {
name = enumeration->values[i].name;
break;
}
}
if (name == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
ESV_VI__XML_TAG__OPEN(output, element, enumeration->type);
virBufferAdd(output, name, -1);
ESV_VI__XML_TAG__CLOSE(output, element);
return 0;
}
int
esxVI_Enumeration_Deserialize(const esxVI_Enumeration *enumeration,
xmlNodePtr node, int *value)
{
int i;
int result = 0;
char *name = NULL;
if (value == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
goto failure;
}
*value = 0; /* undefined */
if (esxVI_String_DeserializeValue(node, &name) < 0) {
goto failure;
}
for (i = 0; enumeration->values[i].name != NULL; ++i) {
if (STREQ(name, enumeration->values[i].name)) {
*value = enumeration->values[i].value;
goto cleanup;
}
}
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Unknown value '%s' for %s",
name, enumeration->type);
cleanup:
VIR_FREE(name);
return result;
failure:
result = -1;
2009-09-23 12:37:26 +00:00
goto cleanup;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* List
*/
int
esxVI_List_Append(esxVI_List **list, esxVI_List *item)
{
esxVI_List *next = NULL;
if (list == NULL || item == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (*list == NULL) {
*list = item;
return 0;
}
next = *list;
while (next->_next != NULL) {
next = next->_next;
}
next->_next = item;
return 0;
}
int
esxVI_List_DeepCopy(esxVI_List **destList, esxVI_List *srcList,
esxVI_List_DeepCopyFunc deepCopyFunc,
esxVI_List_FreeFunc freeFunc)
{
esxVI_List *dest = NULL;
esxVI_List *src = NULL;
if (destList == NULL || *destList != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
goto failure;
}
for (src = srcList; src != NULL; src = src->_next) {
if (deepCopyFunc(&dest, src) < 0 ||
esxVI_List_Append(destList, dest) < 0) {
goto failure;
}
dest = NULL;
}
return 0;
failure:
freeFunc(&dest);
freeFunc(destList);
return -1;
}
int
esxVI_List_CastFromAnyType(esxVI_AnyType *anyType, esxVI_List **list,
esxVI_List_CastFromAnyTypeFunc castFromAnyTypeFunc,
esxVI_List_FreeFunc freeFunc)
{
int result = 0;
xmlNodePtr childNode = NULL;
esxVI_AnyType *childAnyType = NULL;
esxVI_List *item = NULL;
if (list == NULL || *list != NULL ||
castFromAnyTypeFunc == NULL || freeFunc == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (anyType == NULL) {
return 0;
}
if (! STRPREFIX(anyType->other, "ArrayOf")) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Expecting type to begin with 'ArrayOf' but found '%s'",
anyType->other);
return -1;
}
for (childNode = anyType->_node->children; childNode != NULL;
childNode = childNode->next) {
if (childNode->type != XML_ELEMENT_NODE) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Wrong XML element type %d", childNode->type);
goto failure;
}
esxVI_AnyType_Free(&childAnyType);
if (esxVI_AnyType_Deserialize(childNode, &childAnyType) < 0 ||
castFromAnyTypeFunc(childAnyType, &item) < 0 ||
esxVI_List_Append(list, item) < 0) {
goto failure;
}
item = NULL;
}
cleanup:
esxVI_AnyType_Free(&childAnyType);
return result;
failure:
freeFunc(&item);
freeFunc(list);
result = -1;
goto cleanup;
}
int
esxVI_List_Serialize(esxVI_List *list, const char *element,
virBufferPtr output,
esxVI_List_SerializeFunc serializeFunc)
{
esxVI_List *item = NULL;
if (element == NULL || output == NULL || serializeFunc == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (list == NULL) {
return 0;
}
for (item = list; item != NULL; item = item->_next) {
if (serializeFunc(item, element, output) < 0) {
return -1;
}
}
return 0;
}
int
esxVI_List_Deserialize(xmlNodePtr node, esxVI_List **list,
esxVI_List_DeserializeFunc deserializeFunc,
esxVI_List_FreeFunc freeFunc)
{
esxVI_List *item = NULL;
if (list == NULL || *list != NULL ||
deserializeFunc == NULL || freeFunc == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (node == NULL) {
return 0;
}
for (; node != NULL; node = node->next) {
if (node->type != XML_ELEMENT_NODE) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Wrong XML element type %d", node->type);
goto failure;
}
if (deserializeFunc(node, &item) < 0 ||
esxVI_List_Append(list, item) < 0) {
goto failure;
}
item = NULL;
}
return 0;
failure:
freeFunc(&item);
freeFunc(list);
return -1;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Utility and Convenience Functions
*
* Function naming scheme:
* - 'lookup' functions query the ESX or vCenter for information
* - 'get' functions get information from a local object
*/
int
esxVI_Alloc(void **ptrptr, size_t size)
{
if (ptrptr == NULL || *ptrptr != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (virAllocN(ptrptr, size, 1) < 0) {
virReportOOMError();
return -1;
}
return 0;
}
int
esxVI_BuildFullTraversalSpecItem(esxVI_SelectionSpec **fullTraversalSpecList,
const char *name, const char *type,
const char *path, const char *selectSetNames)
{
esxVI_TraversalSpec *traversalSpec = NULL;
esxVI_SelectionSpec *selectionSpec = NULL;
const char *currentSelectSetName = NULL;
if (fullTraversalSpecList == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (esxVI_TraversalSpec_Alloc(&traversalSpec) < 0 ||
esxVI_String_DeepCopyValue(&traversalSpec->_base->name, name) < 0 ||
esxVI_String_DeepCopyValue(&traversalSpec->type, type) < 0 ||
esxVI_String_DeepCopyValue(&traversalSpec->path, path) < 0) {
goto failure;
}
traversalSpec->skip = esxVI_Boolean_False;
if (selectSetNames != NULL) {
currentSelectSetName = selectSetNames;
while (currentSelectSetName != NULL && *currentSelectSetName != '\0') {
if (esxVI_SelectionSpec_Alloc(&selectionSpec) < 0 ||
esxVI_String_DeepCopyValue(&selectionSpec->name,
currentSelectSetName) < 0 ||
esxVI_SelectionSpec_AppendToList(&traversalSpec->selectSet,
selectionSpec) < 0) {
goto failure;
}
selectionSpec = NULL;
currentSelectSetName += strlen(currentSelectSetName) + 1;
}
}
if (esxVI_SelectionSpec_AppendToList(fullTraversalSpecList,
traversalSpec->_base) < 0) {
goto failure;
}
return 0;
failure:
esxVI_TraversalSpec_Free(&traversalSpec);
esxVI_SelectionSpec_Free(&selectionSpec);
return -1;
}
int
esxVI_BuildFullTraversalSpecList(esxVI_SelectionSpec **fullTraversalSpecList)
{
if (fullTraversalSpecList == NULL || *fullTraversalSpecList != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"visitFolders",
"Folder", "childEntity",
"visitFolders\0"
"datacenterToDatastore\0"
"datacenterToVmFolder\0"
"datacenterToHostFolder\0"
"computeResourceToHost\0"
"computeResourceToResourcePool\0"
"hostSystemToVm\0"
"resourcePoolToVm\0") < 0) {
goto failure;
}
/* Traversal through datastore branch */
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"datacenterToDatastore",
"Datacenter", "datastore",
NULL) < 0) {
goto failure;
}
/* Traversal through vmFolder branch */
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"datacenterToVmFolder",
"Datacenter", "vmFolder",
"visitFolders\0") < 0) {
goto failure;
}
/* Traversal through hostFolder branch */
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"datacenterToHostFolder",
"Datacenter", "hostFolder",
"visitFolders\0") < 0) {
goto failure;
}
/* Traversal through host branch */
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"computeResourceToHost",
"ComputeResource", "host",
NULL) < 0) {
goto failure;
}
/* Traversal through resourcePool branch */
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"computeResourceToResourcePool",
"ComputeResource", "resourcePool",
"resourcePoolToResourcePool\0"
"resourcePoolToVm\0") < 0) {
goto failure;
}
/* Recurse through all resource pools */
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"resourcePoolToResourcePool",
"ResourcePool", "resourcePool",
"resourcePoolToResourcePool\0"
"resourcePoolToVm\0") < 0) {
goto failure;
}
/* Recurse through all hosts */
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"hostSystemToVm",
"HostSystem", "vm",
"visitFolders\0") < 0) {
goto failure;
}
/* Recurse through all resource pools */
if (esxVI_BuildFullTraversalSpecItem(fullTraversalSpecList,
"resourcePoolToVm",
"ResourcePool", "vm", NULL) < 0) {
goto failure;
}
return 0;
failure:
esxVI_SelectionSpec_Free(fullTraversalSpecList);
return -1;
}
/*
* Can't use the SessionIsActive() function here, because at least
* 'ESX Server 3.5.0 build-64607' returns an 'method not implemented' fault if
* you try to call it. Query the session manager for the current session of
* this connection instead and re-login if there is no current session for this
* connection.
*
* Update: 'ESX 4.0.0 build-171294' doesn't implement this method.
*/
#define ESX_VI_USE_SESSION_IS_ACTIVE 0
int
esxVI_EnsureSession(esxVI_Context *ctx)
{
#if ESX_VI_USE_SESSION_IS_ACTIVE
esxVI_Boolean active = esxVI_Boolean_Undefined;
#else
int result = 0;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *sessionManager = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
esxVI_UserSession *currentSession = NULL;
#endif
if (ctx->session == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid call");
return -1;
}
#if ESX_VI_USE_SESSION_IS_ACTIVE
if (esxVI_SessionIsActive(ctx, ctx->session->key, ctx->session->userName,
&active) < 0) {
return -1;
}
if (active != esxVI_Boolean_True) {
esxVI_UserSession_Free(&ctx->session);
if (esxVI_Login(ctx, ctx->username, ctx->password,
&ctx->session) < 0) {
return -1;
}
}
return 0;
#else
if (esxVI_String_AppendValueToList(&propertyNameList,
"currentSession") < 0 ||
esxVI_LookupObjectContentByType(ctx, ctx->service->sessionManager,
"SessionManager", propertyNameList,
esxVI_Boolean_False,
&sessionManager) < 0) {
goto failure;
}
for (dynamicProperty = sessionManager->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "currentSession")) {
if (esxVI_UserSession_CastFromAnyType(dynamicProperty->val,
&currentSession) < 0) {
goto failure;
}
break;
} else {
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
}
}
if (currentSession == NULL) {
esxVI_UserSession_Free(&ctx->session);
if (esxVI_Login(ctx, ctx->username, ctx->password, &ctx->session) < 0) {
goto failure;
}
} else if (STRNEQ(ctx->session->key, currentSession->key)) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Key of the current session differs from the key at "
"last login");
goto failure;
}
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&sessionManager);
esxVI_UserSession_Free(&currentSession);
return result;
failure:
result = -1;
goto cleanup;
#endif
}
int
esxVI_LookupObjectContentByType(esxVI_Context *ctx,
esxVI_ManagedObjectReference *root,
const char *type,
esxVI_String *propertyNameList,
esxVI_Boolean recurse,
esxVI_ObjectContent **objectContentList)
{
int result = 0;
esxVI_ObjectSpec *objectSpec = NULL;
esxVI_PropertySpec *propertySpec = NULL;
esxVI_PropertyFilterSpec *propertyFilterSpec = NULL;
if (ctx->fullTraversalSpecList == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid call");
return -1;
}
if (esxVI_ObjectSpec_Alloc(&objectSpec) < 0) {
goto failure;
}
objectSpec->obj = root;
objectSpec->skip = esxVI_Boolean_False;
if (recurse == esxVI_Boolean_True) {
objectSpec->selectSet = ctx->fullTraversalSpecList;
}
if (esxVI_PropertySpec_Alloc(&propertySpec) < 0) {
goto failure;
}
propertySpec->type = (char *)type;
propertySpec->pathSet = propertyNameList;
if (esxVI_PropertyFilterSpec_Alloc(&propertyFilterSpec) < 0 ||
esxVI_PropertySpec_AppendToList(&propertyFilterSpec->propSet,
propertySpec) < 0 ||
esxVI_ObjectSpec_AppendToList(&propertyFilterSpec->objectSet,
objectSpec) < 0) {
goto failure;
}
result = esxVI_RetrieveProperties(ctx, propertyFilterSpec,
objectContentList);
cleanup:
/*
* Remove values given by the caller from the data structures to prevent
* them from being freed by the call to esxVI_PropertyFilterSpec_Free().
*/
if (objectSpec != NULL) {
objectSpec->obj = NULL;
objectSpec->selectSet = NULL;
}
if (propertySpec != NULL) {
propertySpec->type = NULL;
propertySpec->pathSet = NULL;
}
esxVI_PropertyFilterSpec_Free(&propertyFilterSpec);
return result;
failure:
result = -1;
goto cleanup;
}
int
esxVI_GetManagedEntityStatus(esxVI_ObjectContent *objectContent,
const char *propertyName,
esxVI_ManagedEntityStatus *managedEntityStatus)
{
esxVI_DynamicProperty *dynamicProperty;
for (dynamicProperty = objectContent->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, propertyName)) {
return esxVI_ManagedEntityStatus_CastFromAnyType
(dynamicProperty->val, managedEntityStatus);
}
}
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Missing '%s' property while looking for ManagedEntityStatus",
propertyName);
return -1;
}
int
esxVI_GetVirtualMachinePowerState(esxVI_ObjectContent *virtualMachine,
esxVI_VirtualMachinePowerState *powerState)
{
esxVI_DynamicProperty *dynamicProperty;
for (dynamicProperty = virtualMachine->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "runtime.powerState")) {
return esxVI_VirtualMachinePowerState_CastFromAnyType
(dynamicProperty->val, powerState);
}
}
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Missing 'runtime.powerState' property");
return -1;
}
int
esxVI_GetVirtualMachineQuestionInfo
(esxVI_ObjectContent *virtualMachine,
esxVI_VirtualMachineQuestionInfo **questionInfo)
{
esxVI_DynamicProperty *dynamicProperty;
if (questionInfo == NULL || *questionInfo != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
for (dynamicProperty = virtualMachine->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "runtime.question")) {
if (esxVI_VirtualMachineQuestionInfo_CastFromAnyType
(dynamicProperty->val, questionInfo) < 0) {
return -1;
}
}
}
return 0;
}
int
esxVI_LookupNumberOfDomainsByPowerState(esxVI_Context *ctx,
esxVI_VirtualMachinePowerState powerState,
esxVI_Boolean inverse)
{
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *virtualMachineList = NULL;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
esxVI_VirtualMachinePowerState powerState_;
int numberOfDomains = 0;
if (esxVI_String_AppendValueToList(&propertyNameList,
"runtime.powerState") < 0 ||
esxVI_LookupObjectContentByType(ctx, ctx->vmFolder, "VirtualMachine",
propertyNameList, esxVI_Boolean_True,
&virtualMachineList) < 0) {
goto failure;
}
for (virtualMachine = virtualMachineList; virtualMachine != NULL;
virtualMachine = virtualMachine->_next) {
for (dynamicProperty = virtualMachine->propSet;
dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "runtime.powerState")) {
if (esxVI_VirtualMachinePowerState_CastFromAnyType
(dynamicProperty->val, &powerState_) < 0) {
goto failure;
}
if ((inverse != esxVI_Boolean_True &&
powerState_ == powerState) ||
(inverse == esxVI_Boolean_True &&
powerState_ != powerState)) {
numberOfDomains++;
}
} else {
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
}
}
}
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&virtualMachineList);
return numberOfDomains;
failure:
numberOfDomains = -1;
goto cleanup;
}
int
esxVI_GetVirtualMachineIdentity(esxVI_ObjectContent *virtualMachine,
int *id, char **name, unsigned char *uuid)
{
const char *uuid_string = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
esxVI_ManagedEntityStatus configStatus = esxVI_ManagedEntityStatus_Undefined;
if (STRNEQ(virtualMachine->obj->type, "VirtualMachine")) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"ObjectContent does not reference a virtual machine");
return -1;
}
if (id != NULL) {
if (esxUtil_ParseVirtualMachineIDString
(virtualMachine->obj->value, id) < 0 || *id <= 0) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not parse positive integer from '%s'",
virtualMachine->obj->value);
goto failure;
}
}
if (name != NULL) {
if (*name != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
goto failure;
}
for (dynamicProperty = virtualMachine->propSet;
dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "name")) {
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
esxVI_Type_String) < 0) {
goto failure;
}
*name = strdup(dynamicProperty->val->string);
if (*name == NULL) {
virReportOOMError();
goto failure;
}
break;
}
}
if (*name == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not get name of virtual machine");
goto failure;
}
}
if (uuid != NULL) {
if (esxVI_GetManagedEntityStatus(virtualMachine, "configStatus",
&configStatus) < 0) {
goto failure;
}
if (configStatus == esxVI_ManagedEntityStatus_Green) {
for (dynamicProperty = virtualMachine->propSet;
dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "config.uuid")) {
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
esxVI_Type_String) < 0) {
goto failure;
}
uuid_string = dynamicProperty->val->string;
break;
}
}
if (uuid_string == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not get UUID of virtual machine");
goto failure;
}
if (virUUIDParse(uuid_string, uuid) < 0) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not parse UUID from string '%s'",
uuid_string);
goto failure;
}
} else {
memset(uuid, 0, VIR_UUID_BUFLEN);
VIR_WARN0("Cannot access UUID, because 'configStatus' property "
"indicates a config problem");
}
}
return 0;
failure:
if (name != NULL) {
VIR_FREE(*name);
}
return -1;
}
int
esxVI_LookupResourcePoolByHostSystem
(esxVI_Context *ctx, esxVI_ObjectContent *hostSystem,
esxVI_ManagedObjectReference **resourcePool)
{
int result = 0;
esxVI_String *propertyNameList = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
esxVI_ManagedObjectReference *managedObjectReference = NULL;
esxVI_ObjectContent *computeResource = NULL;
if (resourcePool == NULL || *resourcePool != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "parent")) {
if (esxVI_ManagedObjectReference_CastFromAnyType
(dynamicProperty->val, &managedObjectReference,
"ComputeResource") < 0) {
goto failure;
}
break;
} else {
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
}
}
if (managedObjectReference == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not retrieve compute resource of host system");
goto failure;
}
if (esxVI_String_AppendValueToList(&propertyNameList, "resourcePool") < 0 ||
esxVI_LookupObjectContentByType(ctx, managedObjectReference,
"ComputeResource", propertyNameList,
esxVI_Boolean_False,
&computeResource) < 0) {
goto failure;
}
if (computeResource == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not retrieve compute resource of host system");
goto failure;
}
for (dynamicProperty = computeResource->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "resourcePool")) {
if (esxVI_ManagedObjectReference_CastFromAnyType
(dynamicProperty->val, resourcePool, "ResourcePool") < 0) {
goto failure;
}
break;
} else {
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
}
}
if ((*resourcePool) == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not retrieve resource pool of compute resource");
goto failure;
}
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ManagedObjectReference_Free(&managedObjectReference);
esxVI_ObjectContent_Free(&computeResource);
return result;
failure:
result = -1;
goto cleanup;
}
int
esxVI_LookupHostSystemByIp(esxVI_Context *ctx, const char *ipAddress,
esxVI_String *propertyNameList,
esxVI_ObjectContent **hostSystem)
{
int result = 0;
esxVI_ManagedObjectReference *managedObjectReference = NULL;
if (hostSystem == NULL || *hostSystem != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (esxVI_FindByIp(ctx, ctx->datacenter, ipAddress, esxVI_Boolean_False,
&managedObjectReference) < 0) {
goto failure;
}
if (esxVI_LookupObjectContentByType(ctx, managedObjectReference,
"HostSystem", propertyNameList,
esxVI_Boolean_False, hostSystem) < 0) {
goto failure;
}
cleanup:
esxVI_ManagedObjectReference_Free(&managedObjectReference);
return result;
failure:
result = -1;
goto cleanup;
}
int
esxVI_LookupVirtualMachineByUuid(esxVI_Context *ctx, const unsigned char *uuid,
esxVI_String *propertyNameList,
esxVI_ObjectContent **virtualMachine,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence occurrence)
{
int result = 0;
esxVI_ManagedObjectReference *managedObjectReference = NULL;
char uuid_string[VIR_UUID_STRING_BUFLEN] = "";
if (virtualMachine == NULL || *virtualMachine != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
virUUIDFormat(uuid, uuid_string);
if (esxVI_FindByUuid(ctx, ctx->datacenter, uuid_string, esxVI_Boolean_True,
&managedObjectReference) < 0) {
goto failure;
}
if (managedObjectReference == NULL) {
2009-11-22 20:18:45 +00:00
if (occurrence == esxVI_Occurrence_OptionalItem) {
return 0;
} else {
ESX_VI_ERROR(VIR_ERR_NO_DOMAIN,
"Could not find domain with UUID '%s'", uuid_string);
goto failure;
}
}
if (esxVI_LookupObjectContentByType(ctx, managedObjectReference,
"VirtualMachine", propertyNameList,
esxVI_Boolean_False,
virtualMachine) < 0) {
goto failure;
}
cleanup:
esxVI_ManagedObjectReference_Free(&managedObjectReference);
return result;
failure:
result = -1;
goto cleanup;
}
int
esxVI_LookupVirtualMachineByUuidAndPrepareForTask
(esxVI_Context *ctx, const unsigned char *uuid,
esxVI_String *propertyNameList, esxVI_ObjectContent **virtualMachine,
esxVI_Boolean autoAnswer)
{
int result = 0;
esxVI_String *completePropertyNameList = NULL;
esxVI_VirtualMachineQuestionInfo *questionInfo = NULL;
esxVI_TaskInfo *pendingTaskInfoList = NULL;
if (esxVI_String_DeepCopyList(&completePropertyNameList,
propertyNameList) < 0 ||
esxVI_String_AppendValueListToList(&completePropertyNameList,
"runtime.question\0"
"recentTask\0") < 0 ||
esxVI_LookupVirtualMachineByUuid(ctx, uuid, completePropertyNameList,
virtualMachine,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0 ||
esxVI_GetVirtualMachineQuestionInfo(*virtualMachine,
&questionInfo) < 0 ||
esxVI_LookupPendingTaskInfoListByVirtualMachine
(ctx, *virtualMachine, &pendingTaskInfoList) < 0) {
goto failure;
}
if (questionInfo != NULL &&
esxVI_HandleVirtualMachineQuestion(ctx, (*virtualMachine)->obj,
questionInfo, autoAnswer) < 0) {
goto failure;
}
if (pendingTaskInfoList != NULL) {
ESX_VI_ERROR(VIR_ERR_OPERATION_INVALID,
"Other tasks are pending for this domain");
goto failure;
}
cleanup:
esxVI_String_Free(&completePropertyNameList);
esxVI_VirtualMachineQuestionInfo_Free(&questionInfo);
esxVI_TaskInfo_Free(&pendingTaskInfoList);
return result;
failure:
result = -1;
goto cleanup;
}
int
esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
esxVI_String *propertyNameList,
esxVI_ObjectContent **datastore,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence occurrence)
{
int result = 0;
esxVI_String *completePropertyNameList = NULL;
esxVI_ObjectContent *datastoreList = NULL;
esxVI_ObjectContent *candidate = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
esxVI_Boolean accessible = esxVI_Boolean_Undefined;
size_t offset = strlen("/vmfs/volumes/");
int numInaccessibleDatastores = 0;
if (datastore == NULL || *datastore != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
/* Get all datastores */
if (esxVI_String_DeepCopyList(&completePropertyNameList,
propertyNameList) < 0 ||
esxVI_String_AppendValueListToList(&completePropertyNameList,
"summary.accessible\0"
"summary.name\0"
"summary.url\0") < 0) {
goto failure;
}
if (esxVI_LookupObjectContentByType(ctx, ctx->datacenter, "Datastore",
completePropertyNameList,
esxVI_Boolean_True,
&datastoreList) < 0) {
goto failure;
}
if (datastoreList == NULL) {
2009-11-22 20:18:45 +00:00
if (occurrence == esxVI_Occurrence_OptionalItem) {
goto cleanup;
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "No datastores available");
goto failure;
}
}
/* Search for a matching datastore */
for (candidate = datastoreList; candidate != NULL;
candidate = candidate->_next) {
accessible = esxVI_Boolean_Undefined;
for (dynamicProperty = candidate->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "summary.accessible")) {
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
esxVI_Type_Boolean) < 0) {
goto failure;
}
accessible = dynamicProperty->val->boolean;
break;
}
}
if (accessible == esxVI_Boolean_Undefined) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Got incomplete response while querying for the "
"datastore 'summary.accessible' property");
goto failure;
}
if (accessible == esxVI_Boolean_False) {
++numInaccessibleDatastores;
}
for (dynamicProperty = candidate->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "summary.accessible")) {
/* Ignore it */
} else if (STREQ(dynamicProperty->name, "summary.name")) {
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
esxVI_Type_String) < 0) {
goto failure;
}
if (STREQ(dynamicProperty->val->string, name)) {
if (esxVI_ObjectContent_DeepCopy(datastore,
candidate) < 0) {
goto failure;
}
/* Found datastore with matching name */
goto cleanup;
}
} else if (STREQ(dynamicProperty->name, "summary.url")) {
if (accessible == esxVI_Boolean_False) {
/*
* The 'summary.url' property of an inaccessible datastore
* is invalid and cannot be used to identify the datastore.
*/
continue;
}
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
esxVI_Type_String) < 0) {
goto failure;
}
if (! STRPREFIX(dynamicProperty->val->string,
"/vmfs/volumes/")) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Datastore URL '%s' has unexpected prefix, "
"expecting '/vmfs/volumes/' prefix",
dynamicProperty->val->string);
goto failure;
}
if (STREQ(dynamicProperty->val->string + offset, name)) {
if (esxVI_ObjectContent_DeepCopy(datastore,
candidate) < 0) {
goto failure;
}
/* Found datastore with matching URL suffix */
goto cleanup;
}
} else {
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
}
}
}
2009-11-22 20:18:45 +00:00
if (occurrence != esxVI_Occurrence_OptionalItem) {
if (numInaccessibleDatastores > 0) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not find datastore '%s', maybe it's "
"inaccessible", name);
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Could not find datastore '%s'", name);
}
goto failure;
}
cleanup:
esxVI_String_Free(&completePropertyNameList);
esxVI_ObjectContent_Free(&datastoreList);
return result;
failure:
result = -1;
goto cleanup;
}
int esxVI_LookupTaskInfoByTask(esxVI_Context *ctx,
esxVI_ManagedObjectReference *task,
esxVI_TaskInfo **taskInfo)
{
int result = 0;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *objectContent = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
if (taskInfo == NULL || *taskInfo != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
if (esxVI_String_AppendValueToList(&propertyNameList, "info") < 0 ||
esxVI_LookupObjectContentByType(ctx, task, "Task", propertyNameList,
esxVI_Boolean_False,
&objectContent) < 0) {
goto failure;
}
for (dynamicProperty = objectContent->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "info")) {
if (esxVI_TaskInfo_CastFromAnyType(dynamicProperty->val,
taskInfo) < 0) {
goto failure;
}
break;
} else {
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
}
}
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&objectContent);
return result;
failure:
result = -1;
goto cleanup;
}
int
esxVI_LookupPendingTaskInfoListByVirtualMachine
(esxVI_Context *ctx, esxVI_ObjectContent *virtualMachine,
esxVI_TaskInfo **pendingTaskInfoList)
{
int result = 0;
esxVI_String *propertyNameList = NULL;
esxVI_ManagedObjectReference *recentTaskList = NULL;
esxVI_ManagedObjectReference *recentTask = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
esxVI_TaskInfo *taskInfo = NULL;
if (pendingTaskInfoList == NULL || *pendingTaskInfoList != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid argument");
return -1;
}
/* Get list of recent tasks */
for (dynamicProperty = virtualMachine->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "recentTask")) {
if (esxVI_ManagedObjectReference_CastListFromAnyType
(dynamicProperty->val, &recentTaskList, "Task") < 0) {
goto failure;
}
break;
}
}
/* Lookup task info for each task */
for (recentTask = recentTaskList; recentTask != NULL;
recentTask = recentTask->_next) {
if (esxVI_LookupTaskInfoByTask(ctx, recentTask, &taskInfo) < 0) {
goto failure;
}
if (taskInfo->state == esxVI_TaskInfoState_Queued ||
taskInfo->state == esxVI_TaskInfoState_Running) {
if (esxVI_TaskInfo_AppendToList(pendingTaskInfoList,
taskInfo) < 0) {
goto failure;
}
taskInfo = NULL;
} else {
esxVI_TaskInfo_Free(&taskInfo);
}
}
cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ManagedObjectReference_Free(&recentTaskList);
esxVI_TaskInfo_Free(&taskInfo);
return result;
failure:
esxVI_TaskInfo_Free(pendingTaskInfoList);
result = -1;
goto cleanup;
}
int
esxVI_LookupAndHandleVirtualMachineQuestion(esxVI_Context *ctx,
const unsigned char *uuid,
esxVI_Boolean autoAnswer)
{
int result = 0;
esxVI_ObjectContent *virtualMachine = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_VirtualMachineQuestionInfo *questionInfo = NULL;
if (esxVI_String_AppendValueToList(&propertyNameList,
"runtime.question") < 0 ||
esxVI_LookupVirtualMachineByUuid(ctx, uuid, propertyNameList,
&virtualMachine,
2009-11-22 20:18:45 +00:00
esxVI_Occurrence_RequiredItem) < 0 ||
esxVI_GetVirtualMachineQuestionInfo(virtualMachine,
&questionInfo) < 0) {
goto failure;
}
if (questionInfo != NULL &&
esxVI_HandleVirtualMachineQuestion(ctx, virtualMachine->obj,
questionInfo, autoAnswer) < 0) {
goto failure;
}
cleanup:
esxVI_ObjectContent_Free(&virtualMachine);
esxVI_String_Free(&propertyNameList);
esxVI_VirtualMachineQuestionInfo_Free(&questionInfo);
return result;
failure:
result = -1;
goto cleanup;
}
int
esxVI_HandleVirtualMachineQuestion
(esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachine,
esxVI_VirtualMachineQuestionInfo *questionInfo,
esxVI_Boolean autoAnswer)
{
int result = 0;
esxVI_ElementDescription *elementDescription = NULL;
virBuffer buffer = VIR_BUFFER_INITIALIZER;
esxVI_ElementDescription *answerChoice = NULL;
int answerIndex = 0;
char *possibleAnswers = NULL;
if (questionInfo->choice->choiceInfo != NULL) {
for (elementDescription = questionInfo->choice->choiceInfo;
elementDescription != NULL;
elementDescription = elementDescription->_next) {
virBufferVSprintf(&buffer, "'%s'", elementDescription->label);
if (elementDescription->_next != NULL) {
virBufferAddLit(&buffer, ", ");
}
if (answerChoice == NULL &&
questionInfo->choice->defaultIndex != NULL &&
questionInfo->choice->defaultIndex->value == answerIndex) {
answerChoice = elementDescription;
}
++answerIndex;
}
if (virBufferError(&buffer)) {
virReportOOMError();
goto failure;
}
possibleAnswers = virBufferContentAndReset(&buffer);
}
if (autoAnswer == esxVI_Boolean_True) {
if (possibleAnswers == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Pending question blocks virtual machine execution, "
"question is '%s', no possible answers",
questionInfo->text);
goto failure;
} else if (answerChoice == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Pending question blocks virtual machine execution, "
"question is '%s', possible answers are %s, but no "
"default answer is specified", questionInfo->text,
possibleAnswers);
goto failure;
}
VIR_INFO("Pending question blocks virtual machine execution, "
"question is '%s', possible answers are %s, responding "
"with default answer '%s'", questionInfo->text,
possibleAnswers, answerChoice->label);
if (esxVI_AnswerVM(ctx, virtualMachine, questionInfo->id,
answerChoice->key) < 0) {
goto failure;
}
} else {
if (possibleAnswers != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Pending question blocks virtual machine execution, "
"question is '%s', possible answers are %s",
questionInfo->text, possibleAnswers);
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"Pending question blocks virtual machine execution, "
"question is '%s', no possible answers",
questionInfo->text);
}
goto failure;
}
cleanup:
VIR_FREE(possibleAnswers);
return result;
failure:
if (possibleAnswers == NULL) {
possibleAnswers = virBufferContentAndReset(&buffer);
}
result = -1;
goto cleanup;
}
int
esxVI_WaitForTaskCompletion(esxVI_Context *ctx,
esxVI_ManagedObjectReference *task,
const unsigned char *virtualMachineUuid,
esxVI_Boolean autoAnswer,
esxVI_TaskInfoState *finalState)
{
int result = 0;
esxVI_ObjectSpec *objectSpec = NULL;
esxVI_PropertySpec *propertySpec = NULL;
esxVI_PropertyFilterSpec *propertyFilterSpec = NULL;
esxVI_ManagedObjectReference *propertyFilter = NULL;
char *version = NULL;
esxVI_UpdateSet *updateSet = NULL;
esxVI_PropertyFilterUpdate *propertyFilterUpdate = NULL;
esxVI_ObjectUpdate *objectUpdate = NULL;
esxVI_PropertyChange *propertyChange = NULL;
esxVI_AnyType *propertyValue = NULL;
esxVI_TaskInfoState state = esxVI_TaskInfoState_Undefined;
esxVI_TaskInfo *taskInfo = NULL;
version = strdup("");
if (version == NULL) {
virReportOOMError();
goto failure;
}
if (esxVI_ObjectSpec_Alloc(&objectSpec) < 0) {
goto failure;
}
objectSpec->obj = task;
objectSpec->skip = esxVI_Boolean_False;
if (esxVI_PropertySpec_Alloc(&propertySpec) < 0) {
goto failure;
}
propertySpec->type = task->type;
if (esxVI_String_AppendValueToList(&propertySpec->pathSet,
"info.state") < 0 ||
esxVI_PropertyFilterSpec_Alloc(&propertyFilterSpec) < 0 ||
esxVI_PropertySpec_AppendToList(&propertyFilterSpec->propSet,
propertySpec) < 0 ||
esxVI_ObjectSpec_AppendToList(&propertyFilterSpec->objectSet,
objectSpec) < 0 ||
esxVI_CreateFilter(ctx, propertyFilterSpec, esxVI_Boolean_True,
&propertyFilter) < 0) {
goto failure;
}
while (state != esxVI_TaskInfoState_Success &&
state != esxVI_TaskInfoState_Error) {
esxVI_UpdateSet_Free(&updateSet);
if (virtualMachineUuid != NULL) {
if (esxVI_LookupAndHandleVirtualMachineQuestion
(ctx, virtualMachineUuid, autoAnswer) < 0) {
/*
* FIXME: Disable error reporting here, so possible errors from
* esxVI_LookupTaskInfoByTask() and esxVI_CancelTask()
* don't overwrite the actual error
*/
if (esxVI_LookupTaskInfoByTask(ctx, task, &taskInfo)) {
goto failure;
}
if (taskInfo->cancelable == esxVI_Boolean_True) {
if (esxVI_CancelTask(ctx, task) < 0) {
VIR_ERROR0("Cancelable task is blocked by an "
"unanswered question but cancelation "
"failed");
}
} else {
VIR_ERROR0("Non-cancelable task is blocked by an "
"unanswered question");
}
/* FIXME: Enable error reporting here again */
goto failure;
}
}
if (esxVI_WaitForUpdates(ctx, version, &updateSet) < 0) {
goto failure;
}
VIR_FREE(version);
version = strdup(updateSet->version);
if (version == NULL) {
virReportOOMError();
goto failure;
}
if (updateSet->filterSet == NULL) {
continue;
}
for (propertyFilterUpdate = updateSet->filterSet;
propertyFilterUpdate != NULL;
propertyFilterUpdate = propertyFilterUpdate->_next) {
for (objectUpdate = propertyFilterUpdate->objectSet;
objectUpdate != NULL; objectUpdate = objectUpdate->_next) {
for (propertyChange = objectUpdate->changeSet;
propertyChange != NULL;
propertyChange = propertyChange->_next) {
if (STREQ(propertyChange->name, "info.state")) {
if (propertyChange->op == esxVI_PropertyChangeOp_Add ||
propertyChange->op == esxVI_PropertyChangeOp_Assign) {
propertyValue = propertyChange->val;
} else {
propertyValue = NULL;
}
}
}
}
}
if (propertyValue == NULL) {
continue;
}
if (esxVI_TaskInfoState_CastFromAnyType(propertyValue, &state) < 0) {
goto failure;
}
}
if (esxVI_DestroyPropertyFilter(ctx, propertyFilter) < 0) {
VIR_DEBUG0("DestroyPropertyFilter failed");
}
if (esxVI_TaskInfoState_CastFromAnyType(propertyValue, finalState) < 0) {
goto failure;
}
cleanup:
/*
* Remove values given by the caller from the data structures to prevent
* them from being freed by the call to esxVI_PropertyFilterSpec_Free().
*/
if (objectSpec != NULL) {
objectSpec->obj = NULL;
}
if (propertySpec != NULL) {
propertySpec->type = NULL;
}
esxVI_PropertyFilterSpec_Free(&propertyFilterSpec);
esxVI_ManagedObjectReference_Free(&propertyFilter);
VIR_FREE(version);
esxVI_UpdateSet_Free(&updateSet);
esxVI_TaskInfo_Free(&taskInfo);
return result;
failure:
result = -1;
goto cleanup;
}