diff --git a/configure.ac b/configure.ac index ab69e5228b..654b9a84a6 100644 --- a/configure.ac +++ b/configure.ac @@ -219,6 +219,8 @@ AC_ARG_WITH([libssh2], AC_HELP_STRING([--with-libssh2=@<:@PFX@:>@], [libssh2 location @<:@default=/usr/local/lib@:>@]),[],[with_libssh2=yes]) AC_ARG_WITH([phyp], AC_HELP_STRING([--with-phyp], [add PHYP support @<:@default=check@:>@]),[],[with_phyp=check]) +AC_ARG_WITH([xenapi], + AC_HELP_STRING([--with-xenapi], [add XenAPI support @<:@default=check@:>@]),[],[with_xenapi=check]) AC_ARG_WITH([vbox], AC_HELP_STRING([--with-vbox], [add VirtualBox support @<:@default=yes@:>@]),[],[with_vbox=yes]) AC_ARG_WITH([lxc], @@ -341,6 +343,46 @@ if test "$with_libvirtd" = "yes" ; then fi AM_CONDITIONAL([WITH_LIBVIRTD], [test "$with_libvirtd" = "yes"]) + +old_LIBS="$LIBS" +old_CFLAGS="$CFLAGS" +LIBXENSERVER_LIBS="" +LIBXENSERVER_CFLAGS="" +dnl search for the XenServer library +if test "$with_xenapi" != "no" ; then + if test "$with_xenapi" != "yes" -a "$with_xenapi" != "check" ; then + LIBXENSERVER_CFLAGS="-I$with_xenapi/include" + LIBXENSERVER_LIBS="-L$with_xenapi" + fi + fail=0 + CFLAGS="$CFLAGS $LIBXENSERVER_CFLAGS" + LIBS="$LIBS $LIBXENSERVER_LIBS" + AC_CHECK_LIB([xenserver], [xen_vm_start], [ + with_xenapi=yes + LIBXENSERVER_LIBS="$LIBXENSERVER_LIBS -lxenserver" + ],[ + if test "$with_xenapi" = "yes"; then + fail=1 + fi + with_xenapi=no + ]) +fi + +LIBS="$old_LIBS" +CFLAGS="$old_CFLAGS" + +if test $fail = 1; then + AC_MSG_ERROR([You must install the XenServer Library to compile XenAPI driver with -lxenserver]) +fi + +if test "$with_xenapi" = "yes"; then + AC_DEFINE_UNQUOTED([WITH_XENAPI], 1, [whether XenAPI driver is enabled]) +fi + +AC_SUBST([LIBXENSERVER_CFLAGS]) +AC_SUBST([LIBXENSERVER_LIBS]) + + old_LIBS="$LIBS" old_CFLAGS="$CFLAGS" XEN_LIBS="" @@ -1446,31 +1488,50 @@ AC_SUBST([LIBPARTED_CFLAGS]) AC_SUBST([LIBPARTED_LIBS]) dnl -dnl check for libcurl (ESX) +dnl check for libcurl (ESX/XenAPI) dnl LIBCURL_CFLAGS="" LIBCURL_LIBS="" -LIBCURL_FOUND="no" -if test "$with_esx" = "yes" -o "$with_esx" = "check"; then +if test "$with_esx" = "yes" -o "$with_esx" = "check" -o "$with_xenapi" = "yes" -o "$with_xenapi" = "check"; then PKG_CHECK_MODULES(LIBCURL, libcurl >= $LIBCURL_REQUIRED, [ - LIBCURL_FOUND=yes - with_esx="yes" + if test "$with_esx" = "check"; then + with_esx=yes + fi + + if test "$with_xenapi" = "check"; then + with_xenapi=yes + fi ], [ if test "$with_esx" = "check"; then with_esx=no - AC_MSG_NOTICE([libcurl is required for ESX driver, disabling it]) - else + AC_MSG_NOTICE([libcurl is required for the ESX driver, disabling it]) + elif test "$with_esx" = "yes"; then AC_MSG_ERROR([libcurl >= $LIBCURL_REQUIRED is required for the ESX driver]) fi + + if test "$with_xenapi" = "check"; then + with_xenapi=no + AC_MSG_NOTICE([libcurl is required for the XenAPI driver, disabling it]) + elif test "$with_xenapi" = "yes"; then + AC_MSG_ERROR([libcurl >= $LIBCURL_REQUIRED is required for the XenAPI driver]) + fi ]) fi + + if test "$with_esx" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_ESX], 1, [whether ESX driver is enabled]) fi AM_CONDITIONAL([WITH_ESX], [test "$with_esx" = "yes"]) +if test "$with_xenapi" = "yes" ; then + AC_DEFINE_UNQUOTED([WITH_XENAPI], 1, [whether XenAPI driver is enabled]) +fi +AM_CONDITIONAL([WITH_XENAPI], [test "$with_xenapi" = "yes"]) + + AC_SUBST([LIBCURL_CFLAGS]) AC_SUBST([LIBCURL_LIBS]) @@ -1909,6 +1970,7 @@ AC_MSG_NOTICE([ QEMU: $with_qemu]) AC_MSG_NOTICE([ UML: $with_uml]) AC_MSG_NOTICE([ OpenVZ: $with_openvz]) AC_MSG_NOTICE([ VBox: $with_vbox]) +AC_MSG_NOTICE([ XenAPI: $with_xenapi]) AC_MSG_NOTICE([ LXC: $with_lxc]) AC_MSG_NOTICE([ PHYP: $with_phyp]) AC_MSG_NOTICE([ ONE: $with_one]) @@ -2007,6 +2069,11 @@ AC_MSG_NOTICE([ xen: $XEN_CFLAGS $XEN_LIBS]) else AC_MSG_NOTICE([ xen: no]) fi +if test "$with_xenapi" = "yes" ; then +AC_MSG_NOTICE([ xenapi: $LIBXENSERVER_CFLAGS $LIBXENSERVER_LIBS]) +else +AC_MSG_NOTICE([ xenapi: no]) +fi if test "$with_hal" = "yes" ; then AC_MSG_NOTICE([ hal: $HAL_CFLAGS $HAL_LIBS]) else diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index c766416d31..d298447382 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -69,6 +69,7 @@ typedef enum { VIR_FROM_PHYP, /* Error from IBM power hypervisor */ VIR_FROM_SECRET, /* Error from secret storage */ VIR_FROM_CPU, /* Error from CPU driver */ + VIR_FROM_XENAPI /* Error from XenAPI */ } virErrorDomain; diff --git a/src/Makefile.am b/src/Makefile.am index 4c12586b4a..c43797a7d1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -205,6 +205,11 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_security_dac.h \ qemu/qemu_security_dac.c +XENAPI_DRIVER_SOURCES = \ + xenapi/xenapi_driver.c xenapi/xenapi_driver.h \ + xenapi_driver_private.h \ + xenapi/xenapi_utils.c xenapi/xenapi_utils.h + UML_DRIVER_SOURCES = \ uml/uml_conf.c uml/uml_conf.h \ uml/uml_driver.c uml/uml_driver.h @@ -467,6 +472,22 @@ libvirt_driver_vbox_la_LIBADD = $(DLOPEN_LIBS) libvirt_driver_vbox_la_SOURCES = $(VBOX_DRIVER_SOURCES) endif +if WITH_XENAPI +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_xenapi.la +else +noinst_LTLIBRARIES += libvirt_driver_xenapi.la +libvirt_la_LIBADD += libvirt_driver_xenapi.la +endif +libvirt_driver_xenapi_la_CFLAGS = $(LIBXENSERVER_CFLAGS) $(LIBCURL_CFLAGS) \ + -I@top_srcdir@/src/conf +libvirt_driver_xenapi_la_LDFLAGS = $(LIBXENSERVER_LIBS) $(LIBCURL_LIBS) +if WITH_DRIVER_MODULES +libvirt_driver_xenapi_la_LDFLAGS += -module -avoid-version +endif +libvirt_driver_xenapi_la_SOURCES = $(XENAPI_DRIVER_SOURCES) +endif + if WITH_QEMU if WITH_DRIVER_MODULES mod_LTLIBRARIES += libvirt_driver_qemu.la @@ -723,6 +744,7 @@ EXTRA_DIST += \ $(OPENVZ_DRIVER_SOURCES) \ $(PHYP_DRIVER_SOURCES) \ $(VBOX_DRIVER_SOURCES) \ + $(XENAPI_DRIVER_SOURCES) \ $(ESX_DRIVER_SOURCES) \ $(NETWORK_DRIVER_SOURCES) \ $(INTERFACE_DRIVER_SOURCES) \ diff --git a/src/driver.h b/src/driver.h index 8b12a9c733..a64bba026e 100644 --- a/src/driver.h +++ b/src/driver.h @@ -27,6 +27,7 @@ typedef enum { VIR_DRV_ONE = 9, VIR_DRV_ESX = 10, VIR_DRV_PHYP = 11, + VIR_DRV_XENAPI = 12 } virDrvNo; diff --git a/src/libvirt.c b/src/libvirt.c index 935ec3af87..1d9b8781b8 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -64,6 +64,9 @@ # ifdef WITH_ESX # include "esx/esx_driver.h" # endif +# ifdef WITH_XENAPI +# include "xenapi/xenapi_driver.h" +# endif #endif #define VIR_FROM_THIS VIR_FROM_NONE @@ -357,6 +360,7 @@ virInitialize(void) virDriverLoadModule("openvz"); virDriverLoadModule("vbox"); virDriverLoadModule("esx"); + virDriverLoadModule("xenapi"); virDriverLoadModule("remote"); #else # ifdef WITH_TEST @@ -377,6 +381,9 @@ virInitialize(void) # ifdef WITH_ESX if (esxRegister() == -1) return -1; # endif +# ifdef WITH_XENAPI + if (xenapiRegister() == -1) return -1; +# endif # ifdef WITH_REMOTE if (remoteRegister () == -1) return -1; # endif @@ -1031,6 +1038,10 @@ virGetVersion(unsigned long *libVer, const char *type, if (STRCASEEQ(type, "ESX")) *typeVer = LIBVIR_VERSION_NUMBER; # endif +# if WITH_XENAPI + if (STRCASEEQ(type, "XenAPI")) + *typeVer = LIBVIR_VERSION_NUMBER; +# endif # if WITH_REMOTE if (STRCASEEQ(type, "Remote")) *typeVer = remoteVersion(); diff --git a/src/util/virterror.c b/src/util/virterror.c index cbd0ca8e87..0e8bdb3194 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -85,6 +85,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_XEN: dom = "Xen "; break; + case VIR_FROM_XENAPI: + dom = "XenAPI "; + break; case VIR_FROM_XML: dom = "XML "; break; diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c new file mode 100644 index 0000000000..8d5c8bbe71 --- /dev/null +++ b/src/xenapi/xenapi_driver.c @@ -0,0 +1,1766 @@ +/* + * xenapi_driver.c: Xen API driver. + * Copyright (C) 2009, 2010 Citrix Ltd. + * + * 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 + * + * Author: Sharadha Prabhakar + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "internal.h" +#include "domain_conf.h" +#include "virterror_internal.h" +#include "datatypes.h" +#include "util.h" +#include "uuid.h" +#include "memory.h" +#include "buf.h" +#include "xenapi_driver.h" +#include "xenapi_driver_private.h" +#include "xenapi_utils.h" + + +/* + * getCapsObject + * + * Build the capabilities of the hypervisor + * Return virCapsPtr on success or NULL on failure + */ +static virCapsPtr +getCapsObject (void) +{ + virCapsPtr caps = virCapabilitiesNew("x86_64", 0, 0); + if (!caps) { + virReportOOMError(); + return NULL; + } + virCapsGuestPtr guest1 = virCapabilitiesAddGuest(caps, "hvm", "x86_64", 0, "", "", 0, NULL); + if (!guest1) + goto error_cleanup; + virCapsGuestDomainPtr domain1 = virCapabilitiesAddGuestDomain(guest1, "xen", "", "", 0, NULL); + if (!domain1) + goto error_cleanup; + virCapsGuestPtr guest2 = virCapabilitiesAddGuest(caps, "xen", "x86_64", 0, "", "", 0, NULL); + if (!guest2) + goto error_cleanup; + virCapsGuestDomainPtr domain2 = virCapabilitiesAddGuestDomain(guest2, "xen", "", "", 0, NULL); + if (!domain2) + goto error_cleanup; + + return caps; + + error_cleanup: + virCapabilitiesFree(caps); + return NULL; +} + +/* + * XenapiOpen + * + * Authenticates and creates a session with the server + * Return VIR_DRV_OPEN_SUCCESS on success, else VIR_DRV_OPEN_ERROR + */ +static virDrvOpenStatus +xenapiOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags ATTRIBUTE_UNUSED) +{ + char *passwd = NULL; + xen_session *session; + struct _xenapiPrivate *privP; + + if (STRCASENEQ(conn->uri->scheme, "XenAPI")) { + return VIR_DRV_OPEN_DECLINED; + } + if (conn->uri->server == NULL) { + xenapiSessionErrorHandler(conn, VIR_ERR_AUTH_FAILED, "Server name not in URI"); + return VIR_DRV_OPEN_ERROR; + } + if (auth) { + passwd = xenapiUtil_RequestPassword(auth, conn->uri->user, conn->uri->server); + } else { + xenapiSessionErrorHandler(conn, VIR_ERR_AUTH_FAILED, "Authentication Credentials not found"); + return VIR_DRV_OPEN_ERROR; + } + if (!passwd || !conn->uri->user) { + xenapiSessionErrorHandler(conn, VIR_ERR_AUTH_FAILED, "Username/Password not valid"); + if (passwd) VIR_FREE(passwd); + return VIR_DRV_OPEN_ERROR; + } + if (VIR_ALLOC(privP) < 0) { + virReportOOMError(); + return VIR_DRV_OPEN_ERROR; + } + if (virAsprintf(&privP->url, "https://%s", conn->uri->server) < 0) { + virReportOOMError(); + VIR_FREE(passwd); + return VIR_DRV_OPEN_ERROR; + } + xenapiUtil_ParseQuery(conn, conn->uri, &privP->noVerify); + xmlInitParser(); + xmlKeepBlanksDefault(0); + xen_init(); + curl_global_init(CURL_GLOBAL_ALL); + + session = xen_session_login_with_password(call_func, privP, conn->uri->user, passwd, xen_api_latest_version); + + if (session && session->ok) { + privP->session = session; + if (!(privP->caps = getCapsObject())) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Capabilities not found"); + VIR_FREE(passwd); + return VIR_DRV_OPEN_ERROR; + } + conn->privateData = privP; + VIR_FREE(passwd); + return VIR_DRV_OPEN_SUCCESS; + } else { + xenapiSessionErrorHandler(conn, VIR_ERR_AUTH_FAILED, ""); + if (session) xenSessionFree(session); + VIR_FREE(privP); + VIR_FREE(passwd); + return VIR_DRV_OPEN_ERROR; + } +} + +/* + * xenapiClose: + * + * Returns 0 on successful session logout + * + */ +static int +xenapiClose (virConnectPtr conn) +{ + xen_session_logout(((struct _xenapiPrivate *)(conn->privateData))->session); + virCapabilitiesFree(((struct _xenapiPrivate *)(conn->privateData))->caps); + VIR_FREE(((struct _xenapiPrivate *)(conn->privateData))->url); + VIR_FREE(conn->privateData); + return 0; +} + +/* + * + * xenapiSupportsFeature + * + * Returns 0 + */ +static int +xenapiSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) +{ + switch (feature) { + case VIR_DRV_FEATURE_MIGRATION_V2: + case VIR_DRV_FEATURE_MIGRATION_P2P: + default: + return 0; + } +} + +/* + * xenapiType: + * + * + * Returns name of the driver + */ +static const char * +xenapiType (virConnectPtr conn ATTRIBUTE_UNUSED) +{ + return "XenAPI"; +} + + +/* + * xenapiGetVersion: + * + * Gets the version of XenAPI + * + */ +static int +xenapiGetVersion (virConnectPtr conn, unsigned long *hvVer) +{ + xen_host host; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + xen_string_string_map *result = NULL; + int i; + char *version = NULL; + unsigned long major = 0, minor = 0, release = 0; + if (!(xen_session_get_this_host(session, &host, session))) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + return -1; + } + if (!(xen_host_get_software_version(session, &result, host))) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_host_free(host); + return -1; + } + xen_host_free(host); + if (result && result->size > 0) { + for (i = 0; i < result->size; i++) { + if (STREQ(result->contents[i].key, "xen")) { + if (!(version = strdup(result->contents[i].val))) { + xen_string_string_map_free(result); + virReportOOMError(); + return -1; + } + break; + } + } + if (version) { + if (sscanf(version, "%ld.%ld.%ld", &major, &minor, &release) != 3) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Couldn't get version info"); + xen_string_string_map_free(result); + VIR_FREE(version); + return -1; + } + *hvVer = major * 1000000 + minor * 1000 + release; + VIR_FREE(version); + xen_string_string_map_free(result); + return 0; + } + } + return -1; +} + + +/* + * xenapiGetHostname: + * + * + * Returns the hostname on success, or NULL on failure + */ +static char * +xenapiGetHostname (virConnectPtr conn) +{ + char *result = NULL; + xen_host host; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + if (!(xen_session_get_this_host(session, &host, session))) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + return NULL; + } + if (!(xen_host_get_hostname(session, &result, host))) + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_host_free(host); + return result; +} + + +/* + * xenapiGetMaxVcpus: + * + * + * Returns a hardcoded value for Maximum VCPUS + */ +static int +xenapiGetMaxVcpus (virConnectPtr conn ATTRIBUTE_UNUSED, const char *type ATTRIBUTE_UNUSED) +{ + /* this is hardcoded for simplicity and set to a resonable value compared + to the actual value */ + return 16; +} + + +/* + * xenapiNodeGetInfo: + * + * + * Returns Node details on success or else -1 + */ +static int +xenapiNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info) +{ + int64_t memory, mhz; + xen_host_cpu_set *host_cpu_set; + xen_host_cpu host_cpu; + xen_host_metrics_set *xen_met_set; + char *modelname; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + info->nodes = 1; + info->threads = 1; + info->sockets = 1; + + if (xen_host_metrics_get_all(session, &xen_met_set)) { + xen_host_metrics_get_memory_total(session, &memory, xen_met_set->contents[0]); + info->memory = (unsigned long)(memory / 1024); + xen_host_metrics_set_free(xen_met_set); + } else { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Unable to get host metric Information"); + return -1; + } + if (xen_host_cpu_get_all(session, &host_cpu_set)) { + host_cpu = host_cpu_set->contents[0]; + xen_host_cpu_get_modelname(session, &modelname, host_cpu); + if (!virStrncpy(info->model, modelname, LIBVIRT_MODELNAME_LEN - 1, LIBVIRT_MODELNAME_LEN)) { + virReportOOMError(); + xen_host_cpu_set_free(host_cpu_set); + VIR_FREE(modelname); + return -1; + } + xen_host_cpu_get_speed(session, &mhz, host_cpu); + info->mhz = (unsigned long)mhz; + info->cpus = host_cpu_set->size; + info->cores = host_cpu_set->size; + + xen_host_cpu_set_free(host_cpu_set); + VIR_FREE(modelname); + return 0; + } + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Unable to get Host CPU set"); + return -1; +} + +/* + * xenapiGetCapabilities: + * + * + * Returns capabilities as an XML string + */ +static char * +xenapiGetCapabilities (virConnectPtr conn) +{ + virCapsPtr caps = ((struct _xenapiPrivate *)(conn->privateData))->caps; + if (caps) { + char *xml = virCapabilitiesFormatXML(caps); + if (!xml) goto cleanup; + return xml; + } + cleanup: + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Capabilities not available"); + return NULL; +} + + +/* + * xenapiListDomains + * + * Collects the list of active domains, and store their ID in @maxids + * Returns the number of domain found or -1 in case of error + */ +static int +xenapiListDomains (virConnectPtr conn, int *ids, int maxids) +{ + /* vm.list */ + xen_host host; + xen_vm_set *result = NULL; + int64_t t0; + int i; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + if (xen_session_get_this_host(session, &host, session)) { + xen_host_get_resident_vms(session, &result, host); + xen_host_free(host); + } else + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + if (result != NULL) { + for (i = 0; (i < (result->size)) && (i < maxids); i++) { + xen_vm_get_domid(session, &t0, result->contents[i]); + if (t0 > (int64_t)INT_MAX || t0 < (int64_t)INT_MIN) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "DomainID can't fit in 32 bits"); + xen_vm_set_free(result); + return -1; + } + ids[i] = (int)t0; + } + xen_vm_set_free(result); + return i; + } + return -1; +} + +/* + * xenapiNumOfDomains + * + * + * Returns the number of domains found or -1 in case of error + */ +static int +xenapiNumOfDomains (virConnectPtr conn) +{ + /* #(vm.list) */ + xen_vm_set *result = NULL; + xen_host host = NULL; + int numDomains = -1; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + + xen_session_get_this_host(session, &host, session); + if (host != NULL) { + xen_host_get_resident_vms(session, &result, host); + if (result != NULL) { + numDomains = result->size; + xen_vm_set_free(result); + } + xen_host_free(host); + } + if (!session->ok) + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + return numDomains; +} + +/* + * xenapiDomainCreateXML + * + * Launches a new domain based on the XML description + * Returns the domain pointer or NULL in case of error + */ +static virDomainPtr +xenapiDomainCreateXML (virConnectPtr conn, + const char *xmlDesc, + unsigned int flags ATTRIBUTE_UNUSED) +{ + xen_vm_record *record = NULL; + xen_vm vm = NULL; + virDomainPtr domP = NULL; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + virCapsPtr caps = ((struct _xenapiPrivate *)(conn->privateData))->caps; + if (!caps) + return NULL; + + virDomainDefPtr defPtr = virDomainDefParseString(caps, xmlDesc, flags); + createVMRecordFromXml(conn, defPtr, &record, &vm); + virDomainDefFree(defPtr); + if (record) { + unsigned char raw_uuid[VIR_UUID_BUFLEN]; + virUUIDParse(record->uuid, raw_uuid); + if (vm) { + if (xen_vm_start(session, vm, false, false)) { + domP = virGetDomain(conn, record->name_label, raw_uuid); + if (!domP) { + xen_vm_record_free(record); + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Domain Pointer is invalid"); + return domP; + } + domP->id = record->domid; + xen_vm_free(vm); + } + else + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + } + xen_vm_record_free(record); + } + else + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + return domP; +} + +/* + * xenapiDomainLookupByID + * + * + * Returns a valid domain pointer of the domain with ID same as the one passed + * or NULL in case of error + */ +static virDomainPtr +xenapiDomainLookupByID (virConnectPtr conn, int id) +{ + int i; + int64_t domID; + char *uuid; + xen_host host; + xen_vm_set *result; + xen_vm_record *record; + unsigned char raw_uuid[VIR_UUID_BUFLEN]; + virDomainPtr domP=NULL; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + + xen_session_get_this_host(session, &host, session); + if (host != NULL && session->ok) { + xen_host_get_resident_vms(session, &result, host); + if (result != NULL ) { + for (i = 0; i < result->size; i++) { + xen_vm_get_domid(session, &domID, result->contents[i]); + if (domID == id) { + xen_vm_get_record(session, &record, result->contents[i]); + xen_vm_get_uuid(session, &uuid, result->contents[i]); + virUUIDParse(uuid, raw_uuid); + domP = virGetDomain(conn, record->name_label, raw_uuid); + if (domP) { + int64_t domid = -1; + xen_vm_get_domid(session, &domid, result->contents[i]); + domP->id = domid; + } else { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Domain Pointer not valid"); + domP = NULL; + } + xen_uuid_free(uuid); + xen_vm_record_free(record); + break; + } + } + xen_vm_set_free(result); + } else { + xenapiSessionErrorHandler(conn, VIR_ERR_NO_DOMAIN, NULL); + } + xen_host_free(host); + } else { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + } + return domP; +} + +/* + * xenapiDomainLookupByUUID + * + * Returns the domain pointer of domain with matching UUID + * or -1 in case of error + */ +static virDomainPtr +xenapiDomainLookupByUUID (virConnectPtr conn, + const unsigned char *uuid) +{ + /* vm.get_by_uuid */ + xen_vm vm; + xen_vm_record *record; + char uuidStr[VIR_UUID_STRING_BUFLEN]; + virDomainPtr domP = NULL; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + virUUIDFormat(uuid,uuidStr); + if (xen_vm_get_by_uuid(session, &vm, uuidStr)) { + xen_vm_get_record(session, &record, vm); + if (record != NULL) { + domP = virGetDomain(conn, record->name_label, uuid); + if (!domP) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Domain Pointer not valid"); + domP = NULL; + } else { + domP->id = record->domid; + } + xen_vm_record_free(record); + } + else + xenapiSessionErrorHandler(conn, VIR_ERR_NO_DOMAIN, NULL); + xen_vm_free(vm); + } else + xenapiSessionErrorHandler(conn, VIR_ERR_NO_DOMAIN, NULL); + return domP; +} + +/* + * xenapiDomainLookupByName + * + * Returns the domain pointer of domain with matching name + * or -1 in case of error + */ +static virDomainPtr +xenapiDomainLookupByName (virConnectPtr conn, + const char *name) +{ + /* vm.get_by_name_label */ + xen_vm_set *vms = NULL; + xen_vm vm; + char *uuid = NULL; + unsigned char raw_uuid[VIR_UUID_BUFLEN]; + virDomainPtr domP = NULL; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, (char *)name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return NULL; + } + vm = vms->contents[0]; + xen_vm_get_uuid(session, &uuid, vm); + if (uuid!=NULL) { + virUUIDParse(uuid, raw_uuid); + domP = virGetDomain(conn, name, raw_uuid); + if (domP != NULL) { + int64_t domid = -1; + xen_vm_get_domid(session, &domid, vm); + domP->id = domid; + xen_uuid_free(uuid); + xen_vm_set_free(vms); + return domP; + } else { + xen_uuid_free(uuid); + xen_vm_set_free(vms); + if (!session->ok) + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Couldn't get the Domain Pointer"); + return NULL; + } + } + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(conn, VIR_ERR_NO_DOMAIN, ""); + return NULL; +} + +/* + * xenapiDomainSuspend + * + * a VM is paused + * Returns 0 on success or -1 in case of error + */ +static int +xenapiDomainSuspend (virDomainPtr dom) +{ + /* vm.pause() */ + xen_vm vm; + xen_vm_set *vms=NULL; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } else { + vm = vms->contents[0]; + if (!xen_vm_pause(session, vm)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + return 0; + } + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + return -1; +} + +/* + * xenapiDomainResume + * + * Resumes a VM + * Returns 0 on success or -1 in case of error + */ +static int +xenapiDomainResume (virDomainPtr dom) +{ + /* vm.unpause() */ + xen_vm vm; + xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } else { + vm = vms->contents[0]; + if (!xen_vm_unpause(session, vm)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + return 0; + } + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + return -1; +} + +/* + * xenapiDomainShutdown + * + * shutsdown a VM + * Returns 0 on success or -1 in case of error + */ +static int +xenapiDomainShutdown (virDomainPtr dom) +{ + /* vm.clean_shutdown */ + xen_vm vm; + xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } else { + vm = vms->contents[0]; + if (!xen_vm_clean_shutdown(session, vm)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + return 0; + } + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + return -1; +} + +/* + * xenapiDomainReboot + * + * Reboots a VM + * Returns 0 on success or -1 in case of error + */ +static int +xenapiDomainReboot (virDomainPtr dom, unsigned int flags ATTRIBUTE_UNUSED) +{ + /* vm.clean_reboot */ + xen_vm vm; + struct xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if (!xen_vm_clean_reboot(session, vm)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + return 0; + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + return -1; +} + +/* + * xenapiDomaindestroy + * + * A VM is hard shutdown + * Returns 0 on success or -1 in case of error + */ +static int +xenapiDomainDestroy (virDomainPtr dom) +{ + /* vm.hard_shutdown */ + xen_vm vm; + struct xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if (!xen_vm_hard_shutdown(session, vm)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + return 0; + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + return -1; +} + +/* + * xenapiDomainGetOSType + * + * + * Returns OS version on success or NULL in case of error + */ +static char * +xenapiDomainGetOSType (virDomainPtr dom) +{ + xen_vm vm=NULL; + xen_vm_set *vms; + char *ostype = NULL; + char *boot_policy=NULL; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return NULL; + } + vm = vms->contents[0]; + if (!xen_vm_get_hvm_boot_policy(session, &boot_policy, vm)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + goto cleanup; + } + if (!(ostype = (STREQ(boot_policy,"BIOS order") ? strdup("hvm") : strdup("xen")))) + virReportOOMError(); + VIR_FREE(boot_policy); + } else + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + + cleanup: + if (vms) xen_vm_set_free(vms); + return ostype; +} +/* + * xenapiDomainGetMaxMemory + * + * Returns maximum static memory for VM on success + * or 0 in case of error + */ +static unsigned long +xenapiDomainGetMaxMemory (virDomainPtr dom) +{ + int64_t mem_static_max = 0; + xen_vm vm; + xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return 0; + } + vm = vms->contents[0]; + xen_vm_get_memory_static_max(session, &mem_static_max, vm); + xen_vm_set_free(vms); + return (unsigned long)(mem_static_max / 1024); + } else { + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, NULL); + return 0; + } +} + +/* + * xenapiDomainSetMaxMemory + * + * Sets maximum static memory for VM on success + * Returns 0 on success or -1 in case of error + */ +static int +xenapiDomainSetMaxMemory (virDomainPtr dom, unsigned long memory) +{ + /* vm.set_memory_static_max */ + xen_vm vm; + struct xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if (!(xen_vm_set_memory_static_max(session, vm, memory * 1024))) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + } else { + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, NULL); + return -1; + } + return 0; +} + +/* + * xenapiDomainGetInfo: + * + * Fills a structure with domain information + * Returns 0 on success or -1 in case of error + */ +static int +xenapiDomainGetInfo (virDomainPtr dom, virDomainInfoPtr info) +{ + int64_t maxmem = 0, memory = 0, vcpu = 0; + xen_vm vm; + xen_vm_record *record; + xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + info->cpuTime = 0; /* CPU time is not advertised */ + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + xen_vm_get_memory_static_max(session, &maxmem, vm); + info->maxMem = (maxmem / 1024); + enum xen_vm_power_state state = XEN_VM_POWER_STATE_UNKNOWN; + xen_vm_get_power_state(session, &state, vm); + info->state = mapPowerState(state); + xen_vm_get_record(session, &record, vm); + if (record != NULL) { + xen_vm_metrics_get_memory_actual(session, &memory, record->metrics->u.handle); + info->memory = (memory / 1024); + xen_vm_record_free(record); + } + xen_vm_get_vcpus_max(session, &vcpu, vm); + info->nrVirtCpu = vcpu; + xen_vm_set_free(vms); + return 0; + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, NULL); + return -1; +} + + +/* + * xenapiDomainSetVcpus + * + * Sets the VCPUs on the domain + * Return 0 on success or -1 in case of error + */ +static int +xenapiDomainSetVcpus (virDomainPtr dom, unsigned int nvcpus) +{ + + /* vm.set_vcpus_max */ + xen_vm vm; + xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if (xen_vm_set_vcpus_number_live(session, vm, (int64_t)nvcpus)) { + xen_vm_set_free(vms); + return 0; + } + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, NULL); + return -1; +} + +/* + * xenapiDomainPinVcpu + * + * Dynamically change the real CPUs which can be allocated to a virtual CPU + * Returns 0 on success or -1 in case of error + */ +static int +xenapiDomainPinVcpu (virDomainPtr dom, unsigned int vcpu ATTRIBUTE_UNUSED, + unsigned char *cpumap, int maplen) +{ + char *value = NULL; + xen_vm vm; + xen_vm_set *vms; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if ((value = mapDomainPinVcpu(cpumap, maplen))) { + xen_vm_remove_from_vcpus_params(session, vm, (char *)"mask"); + if (xen_vm_add_to_vcpus_params(session, vm, (char *)"mask", value)) { + xen_vm_set_free(vms); + VIR_FREE(value); + return 0; + } + VIR_FREE(value); + } else { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + return -1; +} + +/* + * xenapiDomainGetVcpus + * + * Gets Vcpu information + * Return number of structures filled on success or -1 in case of error + */ +static int +xenapiDomainGetVcpus (virDomainPtr dom, + virVcpuInfoPtr info, int maxinfo, + unsigned char *cpumaps, int maplen) +{ + + xen_vm_set *vms = NULL; + xen_vm vm = NULL; + xen_string_string_map *vcpu_params = NULL; + int nvcpus = 0, cpus = 0, i; + virDomainInfo domInfo; + virNodeInfo nodeInfo; + virVcpuInfoPtr ifptr; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + char *mask = NULL; + if (cpumaps != NULL && maplen < 1) + return -1; + if (xenapiDomainGetInfo(dom, &domInfo) == 0) { + nvcpus = domInfo.nrVirtCpu; + } else { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Couldn't fetch Domain Information"); + return -1; + } + if (xenapiNodeGetInfo(dom->conn, &nodeInfo) == 0) + cpus = nodeInfo.cpus; + else { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Couldn't fetch Node Information"); + return -1; + } + if (nvcpus > maxinfo) + nvcpus = maxinfo; + if (cpumaps != NULL) + memset(cpumaps, 0, maxinfo * maplen); + if (!xen_vm_get_by_name_label(session, &vms, dom->name)) + return -1; + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if (!xen_vm_get_vcpus_params(session, &vcpu_params, vm)) { + xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + return -1; + } + for (i = 0; i < vcpu_params->size; i++) { + if (STREQ(vcpu_params->contents[i].key, "mask")) { + if (!(mask = strdup(vcpu_params->contents[i].val))){ + xen_vm_set_free(vms); + xen_string_string_map_free(vcpu_params); + virReportOOMError(); + return -1; + } + break; + } + } + xen_string_string_map_free(vcpu_params); + for (i = 0, ifptr = info; i < nvcpus; i++, ifptr++) { + ifptr->number = i; + ifptr->state = VIR_VCPU_RUNNING; + ifptr->cpuTime = 0; + ifptr->cpu = 0; + if (mask != NULL) + getCpuBitMapfromString(mask, VIR_GET_CPUMAP(cpumaps, maplen, i), maplen); + } + VIR_FREE(mask); + xen_vm_set_free(vms); + return i; +} + +/* + * xenapiDomainGetMaxVcpus + * + * + * Returns maximum number of Vcpus on success or -1 in case of error + */ +static int +xenapiDomainGetMaxVcpus (virDomainPtr dom) +{ + xen_vm vm; + xen_vm_set *vms; + int64_t maxvcpu = 0; + enum xen_vm_power_state state; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + xen_vm_get_power_state(session, &state, vm); + if (state == XEN_VM_POWER_STATE_RUNNING) { + xen_vm_get_vcpus_max(session, &maxvcpu, vm); + } else { + maxvcpu = xenapiGetMaxVcpus(dom->conn, NULL); + } + xen_vm_set_free(vms); + return (int)maxvcpu; + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + return -1; +} + +/* + * xenapiDomainDumpXML + * + * + * Returns XML string of the domain configuration on success or -1 in case of error + */ +static char * +xenapiDomainDumpXML (virDomainPtr dom, int flags ATTRIBUTE_UNUSED) +{ + xen_vm vm=NULL; + xen_vm_set *vms; + xen_string_string_map *result=NULL; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + virDomainDefPtr defPtr = NULL; + + if (!xen_vm_get_by_name_label(session, &vms, dom->name)) return NULL; + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return NULL; + } + if (VIR_ALLOC(defPtr) < 0) { + virReportOOMError(); + xen_vm_set_free(vms); + return NULL; + } + vm = vms->contents[0]; + defPtr->virtType = VIR_DOMAIN_VIRT_XEN; + defPtr->id = dom->id; + memcpy(defPtr->uuid, dom->uuid, VIR_UUID_BUFLEN); + if (!(defPtr->name = strdup(dom->name))) + goto error_cleanup; + char *boot_policy = NULL; + xen_vm_get_hvm_boot_policy(session, &boot_policy, vm); + if (STREQ(boot_policy,"BIOS order")) { + if (!(defPtr->os.type = strdup("hvm"))) { + VIR_FREE(boot_policy); + goto error_cleanup; + } + xen_vm_get_hvm_boot_params(session, &result, vm); + if (result != NULL) { + int i; + for (i = 0; i < result->size; i++) { + if (STREQ(result->contents[i].key, "order")) { + int cnt = 0; + while(result->contents[i].val[cnt] != '\0') { + defPtr->os.bootDevs[cnt] = map2LibvirtBootOrder(result->contents[i].val[cnt]); + cnt++; + } + defPtr->os.nBootDevs = cnt; + break; + } + } + xen_string_string_map_free(result); + } + VIR_FREE(boot_policy); + } else { + char *value = NULL; + if (!(defPtr->os.type = strdup("xen"))) { + VIR_FREE(boot_policy); + goto error_cleanup; + } + if (!(defPtr->os.loader = strdup("pygrub"))) { + VIR_FREE(boot_policy); + goto error_cleanup; + } + xen_vm_get_pv_kernel(session, &value, vm); + if (STRNEQ(value, "")) { + if (!(defPtr->os.kernel = strdup(value))) { + VIR_FREE(boot_policy); + VIR_FREE(value); + goto error_cleanup; + } + VIR_FREE(value); + } + xen_vm_get_pv_ramdisk(session, &value, vm); + if (STRNEQ(value, "")) { + if (!(defPtr->os.initrd = strdup(value))) { + VIR_FREE(boot_policy); + VIR_FREE(value); + goto error_cleanup; + } + VIR_FREE(value); + } + xen_vm_get_pv_args(session, &value, vm); + if (STRNEQ(value, "")) { + if(!(defPtr->os.cmdline = strdup(value))) { + VIR_FREE(boot_policy); + VIR_FREE(value); + goto error_cleanup; + } + VIR_FREE(value); + } + VIR_FREE(boot_policy); + if (!(defPtr->os.bootloader = strdup("pygrub"))) + goto error_cleanup; + } + char *val = NULL; + xen_vm_get_pv_bootloader_args(session, &val, vm); + if (STRNEQ(val, "")) { + if (!(defPtr->os.bootloaderArgs = strdup(val))) { + VIR_FREE(val); + goto error_cleanup; + } + VIR_FREE(val); + } + unsigned long memory=0; + memory = xenapiDomainGetMaxMemory(dom); + defPtr->maxmem = memory; + int64_t dynamic_mem=0; + if (xen_vm_get_memory_dynamic_max(session, &dynamic_mem, vm)) { + defPtr->memory = (unsigned long) (dynamic_mem / 1024); + } else { + defPtr->memory = memory; + } + defPtr->vcpus = xenapiDomainGetMaxVcpus(dom); + enum xen_on_normal_exit action; + if (xen_vm_get_actions_after_shutdown(session, &action, vm)) { + defPtr->onPoweroff = xenapiNormalExitEnum2virDomainLifecycle(action); + } + if (xen_vm_get_actions_after_reboot(session, &action, vm)) { + defPtr->onReboot = xenapiNormalExitEnum2virDomainLifecycle(action); + } + enum xen_on_crash_behaviour crash; + if (xen_vm_get_actions_after_crash(session, &crash, vm)) { + defPtr->onCrash = xenapiCrashExitEnum2virDomainLifecycle(action); + } + xen_vm_get_platform(session, &result, vm); + if (result != NULL) { + int i; + for(i = 0; i < result->size; i++) { + if (STREQ(result->contents[i].val, "true")) { + if (STREQ(result->contents[i].key, "acpi")) + defPtr->features = defPtr->features | (1<contents[i].key, "apic")) + defPtr->features = defPtr->features | (1<contents[i].key, "pae")) + defPtr->features = defPtr->features | (1<nnets = vif_set->size; + if (VIR_ALLOC_N(defPtr->nets, vif_set->size) < 0) { + xen_vif_set_free(vif_set); + goto error_cleanup; + } + for (i = 0; i < vif_set->size; i++) { + if (VIR_ALLOC(defPtr->nets[i]) < 0) { + xen_vif_set_free(vif_set); + goto error_cleanup; + } + defPtr->nets[i]->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + vif = vif_set->contents[i]; + xen_vif_get_network(session, &network, vif); + if (network != NULL) { + xen_network_get_bridge(session, &bridge, network); + if (bridge != NULL) + defPtr->nets[i]->data.bridge.brname = bridge; + xen_network_free(network); + } + xen_vif_get_record(session, &vif_rec, vif); + if (vif_rec != NULL) { + if (virParseMacAddr((const char *)vif_rec->mac,defPtr->nets[i]->mac) < 0) + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, + "Unable to parse given mac address"); + xen_vif_record_free(vif_rec); + } + } + xen_vif_set_free(vif_set); + } + if (vms) xen_vm_set_free(vms); + char *xml = virDomainDefFormat(defPtr, 0); + virDomainDefFree(defPtr); + return xml; + + error_cleanup: + virReportOOMError(); + xen_vm_set_free(vms); + virDomainDefFree(defPtr); + return NULL; + +} + +/* + * xenapiListDefinedDomains + * + * list the defined but inactive domains, stores the pointers to the names in @names + * Returns number of names provided in the array or -1 in case of error + */ +static int +xenapiListDefinedDomains (virConnectPtr conn, char **const names, + int maxnames) +{ + int i,j=0,doms; + xen_vm_set *result; + xen_vm_record *record; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + xen_vm_get_all(session, &result); + if (result != NULL) { + for (i = 0; i < result->size && j < maxnames; i++) { + xen_vm_get_record(session, &record, result->contents[i]); + if (record != NULL) { + if (record->is_a_template == 0) { + char *usenames = NULL; + if (!(usenames = strdup(record->name_label))) { + virReportOOMError(); + xen_vm_record_free(record); + xen_vm_set_free(result); + while (--j >= 0) VIR_FREE(names[j]); + return -1; + } + names[j++] = usenames; + } + xen_vm_record_free(record); + } else { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Couldn't get VM record"); + xen_vm_set_free(result); + while (--j >= 0) VIR_FREE(names[j]); + return -1; + } + } + doms = j; + xen_vm_set_free(result); + return doms; + } + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + return -1; +} + +/* + * xenapiNumOfDefinedDomains + * + * Provides the number of defined but inactive domains + * Returns number of domains found on success or -1 in case of error + */ +static int +xenapiNumOfDefinedDomains (virConnectPtr conn) +{ + xen_vm_set *result; + xen_vm_record *record; + int DomNum = 0, i; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + xen_vm_get_all(session, &result); + if (result != NULL) { + for (i = 0; i < result->size; i++) { + xen_vm_get_record(session, &record, result->contents[i]); + if (record == NULL && !session->ok) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(result); + return -1; + } + if (record->is_a_template == 0) + DomNum++; + xen_vm_record_free(record); + } + xen_vm_set_free(result); + return DomNum; + } + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + return -1; +} + +/* + * xenapiDomainCreate + * + * starts a VM + * Return 0 on success or -1 in case of error + */ +static int +xenapiDomainCreate (virDomainPtr dom) +{ + xen_vm_set *vms; + xen_vm vm; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if (!xen_vm_start(session, vm, false, false)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + } else { + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, NULL); + return -1; + } + return 0; +} + +/* + * xenapiDomainDefineXML + * + * Defines a domain from the given XML but does not start it + * Returns 0 on success or -1 in case of error + */ +static virDomainPtr +xenapiDomainDefineXML (virConnectPtr conn, const char *xml) +{ + xen_vm_record *record=NULL; + xen_vm vm=NULL; + virDomainPtr domP=NULL; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + virCapsPtr caps = ((struct _xenapiPrivate *)(conn->privateData))->caps; + if (!caps) + return NULL; + virDomainDefPtr defPtr = virDomainDefParseString(caps, xml, 0); + if (!defPtr) + return NULL; + if (createVMRecordFromXml(conn, defPtr, &record, &vm) != 0) { + if (!session->ok) + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + else + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Couldn't get VM information from XML"); + virDomainDefFree(defPtr); + return NULL; + } + if (record != NULL) { + unsigned char raw_uuid[VIR_UUID_BUFLEN]; + virUUIDParse(record->uuid, raw_uuid); + domP = virGetDomain(conn, record->name_label, raw_uuid); + if (!domP && !session->ok) + xenapiSessionErrorHandler(conn, VIR_ERR_NO_DOMAIN, NULL); + xen_vm_record_free(record); + } + else if (vm != NULL) + xen_vm_free(vm); + virDomainDefFree(defPtr); + return domP; +} + +/* + * xenapiDomainUndefine + * + * destroys a domain + * Return 0 on success or -1 in case of error + */ +static int +xenapiDomainUndefine (virDomainPtr dom) +{ + struct xen_vm_set *vms; + xen_vm vm; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if (!xen_vm_destroy(session, vm)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + return 0; + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + return -1; +} + +/* + * xenapiDomainGetAutostart + * + * Provides a boolean value indicating whether the domain configured + * to be automatically started when the host machine boots + * Return 0 on success or -1 in case of error + */ +static int +xenapiDomainGetAutostart (virDomainPtr dom, int *autostart) +{ + int i,flag=0; + xen_vm_set *vms; + xen_vm vm; + xen_string_string_map *result; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + if (!xen_vm_get_other_config(session, &result, vm)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + for (i = 0; i < result->size; i++) { + if (STREQ(result->contents[i].key, "auto_poweron")) { + flag = 1; + if (STREQ(result->contents[i].val, "true")) + *autostart = 1; + else + *autostart = 0; + break; + } + } + xen_vm_set_free(vms); + xen_string_string_map_free(result); + if (flag == 0) return -1; + return 0; + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + return -1; +} + +/* + * xenapiDomainSetAutostart + * + * Configure the domain to be automatically started when the host machine boots + * Return 0 on success or -1 in case of error + */ +static int +xenapiDomainSetAutostart (virDomainPtr dom, int autostart) +{ + xen_vm_set *vms; + xen_vm vm; + char *value; + xen_session *session = ((struct _xenapiPrivate *)(dom->conn->privateData))->session; + if (xen_vm_get_by_name_label(session, &vms, dom->name) && vms->size > 0) { + if (vms->size != 1) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, "Domain name is not unique"); + xen_vm_set_free(vms); + return -1; + } + vm = vms->contents[0]; + xen_vm_remove_from_other_config(session, vm, (char *)"auto_poweron"); + if (autostart==1) + value = (char *)"true"; + else + value = (char *)"false"; + if (!xen_vm_add_to_other_config(session, vm, (char *)"auto_poweron", value)) { + xenapiSessionErrorHandler(dom->conn, VIR_ERR_INTERNAL_ERROR, NULL); + xen_vm_set_free(vms); + return -1; + } + xen_vm_set_free(vms); + return 0; + } + if (vms) xen_vm_set_free(vms); + xenapiSessionErrorHandler(dom->conn, VIR_ERR_NO_DOMAIN, ""); + return -1; +} + +static char * +xenapiDomainGetSchedulerType (virDomainPtr dom ATTRIBUTE_UNUSED, int *nparams) +{ + char *result = NULL; + *nparams = 0; + if (!(result = strdup("credit"))) + virReportOOMError(); + return result; +} + +/* + * xenapiNodeGetFreeMemory + * + * provides the free memory available on the Node + * Returns memory size on success or 0 in case of error + */ +static unsigned long long +xenapiNodeGetFreeMemory (virConnectPtr conn) +{ + xen_host_metrics_set *xen_met_set; + unsigned long long freeMem = 0; + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + xen_host_metrics_get_all(session, &xen_met_set); + if (xen_met_set != NULL) { + if (!xen_host_metrics_get_memory_free(session, (int64_t *)&freeMem, xen_met_set->contents[0])) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Couldn't get host metrics - memory information"); + freeMem = 0; + } + xen_host_metrics_set_free(xen_met_set); + } else { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, "Couldn't get host metrics"); + } + return freeMem; +} + +/* + * xenapiNodeGetCellsFreeMemory + * + * + * Returns the number of entries filled in freeMems, or -1 in case of error. + */ +static int +xenapiNodeGetCellsFreeMemory (virConnectPtr conn, unsigned long long *freeMems, + int startCell, int maxCells) +{ + if (maxCells > 1 && startCell > 0) { + xenapiSessionErrorHandler(conn, VIR_ERR_NO_SUPPORT, ""); + return -1; + } else { + freeMems[0] = xenapiNodeGetFreeMemory(conn); + return 1; + } +} + +/* The interface which we export upwards to libvirt.c. */ +static virDriver xenapiDriver = { + VIR_DRV_XENAPI, + "XenAPI", + xenapiOpen, /* open */ + xenapiClose, /* close */ + xenapiSupportsFeature, /* supports_feature */ + xenapiType, /* type */ + xenapiGetVersion, /* version */ + NULL, /*getlibvirtVersion */ + xenapiGetHostname, /* getHostname */ + xenapiGetMaxVcpus, /* getMaxVcpus */ + xenapiNodeGetInfo, /* nodeGetInfo */ + xenapiGetCapabilities, /* getCapabilities */ + xenapiListDomains, /* listDomains */ + xenapiNumOfDomains, /* numOfDomains */ + xenapiDomainCreateXML, /* domainCreateXML */ + xenapiDomainLookupByID, /* domainLookupByID */ + xenapiDomainLookupByUUID, /* domainLookupByUUID */ + xenapiDomainLookupByName, /* domainLookupByName */ + xenapiDomainSuspend, /* domainSuspend */ + xenapiDomainResume, /* domainResume */ + xenapiDomainShutdown, /* domainShutdown */ + xenapiDomainReboot, /* domainReboot */ + xenapiDomainDestroy, /* domainDestroy */ + xenapiDomainGetOSType, /* domainGetOSType */ + xenapiDomainGetMaxMemory, /* domainGetMaxMemory */ + xenapiDomainSetMaxMemory, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + xenapiDomainGetInfo, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + NULL, /* domainCoreDump */ + xenapiDomainSetVcpus, /* domainSetVcpus */ + xenapiDomainPinVcpu, /* domainPinVcpu */ + xenapiDomainGetVcpus, /* domainGetVcpus */ + xenapiDomainGetMaxVcpus, /* domainGetMaxVcpus */ + NULL, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ + xenapiDomainDumpXML, /* domainDumpXML */ + NULL, /* domainXmlFromNative */ + NULL, /* domainXmlToNative */ + xenapiListDefinedDomains, /* listDefinedDomains */ + xenapiNumOfDefinedDomains, /* numOfDefinedDomains */ + xenapiDomainCreate, /* domainCreate */ + xenapiDomainDefineXML, /* domainDefineXML */ + xenapiDomainUndefine, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainAttachDeviceFlags */ + NULL, /* domainDetachDevice */ + NULL, /* domainDetachDeviceFlags */ + xenapiDomainGetAutostart, /* domainGetAutostart */ + xenapiDomainSetAutostart, /* domainSetAutostart */ + xenapiDomainGetSchedulerType, /* domainGetSchedulerType */ + NULL, /* domainGetSchedulerParameters */ + NULL, /* domainSetSchedulerParameters */ + NULL, /* domainMigratePrepare */ + NULL, /* domainMigratePerform */ + NULL, /* domainMigrateFinish */ + NULL, /* domainBlockStats */ + NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ + NULL, /* domainBlockPeek */ + NULL, /* domainMemoryPeek */ + xenapiNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ + xenapiNodeGetFreeMemory, /* getFreeMemory */ + NULL, /* domainEventRegister */ + NULL, /* domainEventDeregister */ + NULL, /* domainMigratePrepare2 */ + NULL, /* domainMigrateFinish2 */ + NULL, /* nodeDeviceDettach */ + NULL, /* nodeDeviceReAttach */ + NULL, /* nodeDeviceReset */ + NULL, /* domainMigratePrepareTunnel */ + NULL, /* isEncrypted */ + NULL, /* isSecure */ + NULL, /* domainIsActive */ + NULL, /* domainIsPersistent */ + NULL, /* cpuCompare */ + NULL, /* cpuBaseline */ + NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ +}; + +/** + * xenapiRegister: + * + * + * Returns the driver priority or -1 in case of error. + */ +int +xenapiRegister (void) +{ + return virRegisterDriver (&xenapiDriver); +} + +/* + * write_func + * used by curl to read data from the server + */ +size_t +write_func(void *ptr, size_t size, size_t nmemb, void *comms_) +{ + xen_comms *comms = comms_; + size_t n = size * nmemb; + #ifdef PRINT_XML + printf("\n\n---Result from server -----------------------\n"); + printf("%s\n",((char*) ptr)); + fflush(stdout); + #endif + return (size_t) (comms->func(ptr, n, comms->handle) ? n : 0); +} + +/* + * call_func + * sets curl options, used with xen_session_login_with_password + */ +int +call_func(const void *data, size_t len, void *user_handle, + void *result_handle, xen_result_func result_func) +{ + //(void)user_handle; + struct _xenapiPrivate *priv = (struct _xenapiPrivate *)user_handle; + #ifdef PRINT_XML + + printf("\n\n---Data to server: -----------------------\n"); + printf("%s\n",((char*) data)); + fflush(stdout); + #endif + CURL *curl = curl_easy_init(); + if (!curl) { + return -1; + } + xen_comms comms = { + .func = result_func, + .handle = result_handle + }; + curl_easy_setopt(curl, CURLOPT_URL, priv->url); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + #ifdef CURLOPT_MUTE + curl_easy_setopt(curl, CURLOPT_MUTE, 1); + #endif + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_func); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &comms); + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, priv->noVerify?0:1); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, priv->noVerify?0:2); + CURLcode result = curl_easy_perform(curl); + curl_easy_cleanup(curl); + return result; +} diff --git a/src/xenapi/xenapi_driver.h b/src/xenapi/xenapi_driver.h new file mode 100644 index 0000000000..a47c1327c7 --- /dev/null +++ b/src/xenapi/xenapi_driver.h @@ -0,0 +1,28 @@ +/* + * xenapi_driver.h.c: Xen API driver header file to be included in libvirt.c. + * Copyright (C) 2009, 2010 Citrix Ltd. + * + * 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 + * + * Author: Sharadha Prabhakar + */ + + +#ifndef __VIR_XENAPI_PRIV_H__ +# define __VIR_XENAPI_PRIV_H__ + +extern int xenapiRegister(void); + +#endif /* __VIR_XENAPI_PRIV_H__ */ diff --git a/src/xenapi/xenapi_driver_private.h b/src/xenapi/xenapi_driver_private.h new file mode 100644 index 0000000000..2e96771177 --- /dev/null +++ b/src/xenapi/xenapi_driver_private.h @@ -0,0 +1,63 @@ +/* + * xenapi_driver_private.h: Xen API driver's private header file. + * Copyright (C) 2009, 2010 Citrix Ltd. + * + * 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 + * + * Author: Sharadha Prabhakar + */ + + +#ifndef __VIR_XENAPI_H__ +# define __VIR_XENAPI_H__ + +# include +# include +# include "virterror_internal.h" + +//# define PRINT_XML +# define VIR_FROM_THIS VIR_FROM_XENAPI +# define LIBVIRT_MODELNAME_LEN (32) +# define xenapiSessionErrorHandler(conn, errNum, buf) \ + xenapiSessionErrorHandle(conn, errNum, buf, \ + __FILE__, __FUNCTION__, __LINE__) + +void +xenapiSessionErrorHandle(virConnectPtr conn, virErrorNumber errNum, + const char *buf, const char *filename, + const char *func, size_t lineno); + +typedef struct +{ + xen_result_func func; + void *handle; +} xen_comms; + + +int +call_func(const void *data, size_t len, void *user_handle, + void *result_handle, xen_result_func result_func); +size_t +write_func(void *ptr, size_t size, size_t nmemb, void *comms); + +/* xenAPI driver's private data structure */ +struct _xenapiPrivate { + xen_session *session; + char *url; + int noVerify; + virCapsPtr caps; +}; + +#endif /* __VIR_XENAPI_H__ */ diff --git a/src/xenapi/xenapi_utils.c b/src/xenapi/xenapi_utils.c new file mode 100644 index 0000000000..4b9a6d85c4 --- /dev/null +++ b/src/xenapi/xenapi_utils.c @@ -0,0 +1,571 @@ +/* + * xenapi_utils.c: Xen API driver -- utils parts. + * Copyright (C) 2009, 2010 Citrix Ltd. + * + * 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 + * + * Author: Sharadha Prabhakar + */ + +#include + +#include +#include +#include +#include +#include +#include "internal.h" +#include "domain_conf.h" +#include "virterror_internal.h" +#include "datatypes.h" +#include "util.h" +#include "uuid.h" +#include "memory.h" +#include "buf.h" +#include "logging.h" +#include "qparams.h" +#include "xenapi_driver_private.h" +#include "xenapi_utils.h" + +void +xenSessionFree(xen_session *session) +{ + int i; + if (session->error_description != NULL) { + for (i = 0; i < session->error_description_count; i++) + VIR_FREE(session->error_description[i]); + VIR_FREE(session->error_description); + } + VIR_FREE(session->session_id); + VIR_FREE(session); +} + +char * +xenapiUtil_RequestPassword(virConnectAuthPtr auth, const char *username, + const char *hostname) +{ + unsigned int ncred; + virConnectCredential cred; + char *prompt; + + memset(&cred, 0, sizeof (virConnectCredential)); + + if (virAsprintf(&prompt, "Enter %s password for %s", username, + hostname) < 0) { + return NULL; + } + + for (ncred = 0; ncred < auth->ncredtype; ncred++) { + if (auth->credtype[ncred] != VIR_CRED_PASSPHRASE && + auth->credtype[ncred] != VIR_CRED_NOECHOPROMPT) { + continue; + } + + cred.type = auth->credtype[ncred]; + cred.prompt = prompt; + cred.challenge = hostname; + cred.defresult = NULL; + cred.result = NULL; + cred.resultlen = 0; + + if ((*(auth->cb))(&cred, 1, auth->cbdata) < 0) { + VIR_FREE(cred.result); + } + + break; + } + + VIR_FREE(prompt); + + return cred.result; +} + +int +xenapiUtil_ParseQuery(virConnectPtr conn, xmlURIPtr uri, int *noVerify) +{ + int result = 0; + int i; + struct qparam_set *queryParamSet = NULL; + struct qparam *queryParam = NULL; + +#ifdef HAVE_XMLURI_QUERY_RAW + queryParamSet = qparam_query_parse(uri->query_raw); +#else + queryParamSet = qparam_query_parse(uri->query); +#endif + + if (queryParamSet == NULL) { + goto failure; + } + + for (i = 0; i < queryParamSet->n; i++) { + queryParam = &queryParamSet->p[i]; + if (STRCASEEQ(queryParam->name, "no_verify")) { + if (noVerify == NULL) { + continue; + } + if (virStrToLong_i(queryParam->value, NULL, 10, noVerify) < 0 || + (*noVerify != 0 && *noVerify != 1)) { + xenapiSessionErrorHandler(conn, VIR_ERR_INVALID_ARG, + "Query parameter 'no_verify' has unexpected value (should be 0 or 1)"); + goto failure; + } + } + } + + cleanup: + if (queryParamSet != NULL) { + free_qparam_set(queryParamSet); + } + + return result; + + failure: + result = -1; + + goto cleanup; +} + + + +enum xen_on_normal_exit +actionShutdownLibvirt2XenapiEnum(enum virDomainLifecycleAction action) +{ + enum xen_on_normal_exit num = XEN_ON_NORMAL_EXIT_RESTART; + if (action == VIR_DOMAIN_LIFECYCLE_DESTROY) + num = XEN_ON_NORMAL_EXIT_DESTROY; + else if (action == VIR_DOMAIN_LIFECYCLE_RESTART) + num = XEN_ON_NORMAL_EXIT_RESTART; + return num; +} + + +enum xen_on_crash_behaviour +actionCrashLibvirt2XenapiEnum(enum virDomainLifecycleAction action) +{ + enum xen_on_crash_behaviour num = XEN_ON_CRASH_BEHAVIOUR_RESTART; + if (action == VIR_DOMAIN_LIFECYCLE_DESTROY) + num = XEN_ON_CRASH_BEHAVIOUR_DESTROY; + else if (action == VIR_DOMAIN_LIFECYCLE_RESTART) + num = XEN_ON_CRASH_BEHAVIOUR_RESTART; + else if (action == VIR_DOMAIN_LIFECYCLE_PRESERVE) + num = XEN_ON_CRASH_BEHAVIOUR_PRESERVE; + else if (action == VIR_DOMAIN_LIFECYCLE_RESTART_RENAME) + num = XEN_ON_CRASH_BEHAVIOUR_RENAME_RESTART; + return num; +} + +/* generate XenAPI boot order format from libvirt format */ +char * +createXenAPIBootOrderString(int nboot, int *bootDevs) +{ + virBuffer ret = VIR_BUFFER_INITIALIZER; + char *val = NULL; + int i; + for (i = 0; i < nboot; i++) { + if (bootDevs[i] == VIR_DOMAIN_BOOT_FLOPPY) + val = (char *)"a"; + else if (bootDevs[i] == VIR_DOMAIN_BOOT_DISK) + val = (char *)"c"; + else if (bootDevs[i] == VIR_DOMAIN_BOOT_CDROM) + val = (char *)"d"; + else if (bootDevs[i] == VIR_DOMAIN_BOOT_NET) + val = (char *)"n"; + if (val) + virBufferEscapeString(&ret,"%s",val); + } + return virBufferContentAndReset(&ret); +} + +/* convert boot order string to libvirt boot order enum */ +enum virDomainBootOrder +map2LibvirtBootOrder(char c) { + switch (c) { + case 'a': + return VIR_DOMAIN_BOOT_FLOPPY; + case 'c': + return VIR_DOMAIN_BOOT_DISK; + case 'd': + return VIR_DOMAIN_BOOT_CDROM; + case 'n': + return VIR_DOMAIN_BOOT_NET; + default: + return -1; + } +} + +enum virDomainLifecycleAction +xenapiNormalExitEnum2virDomainLifecycle(enum xen_on_normal_exit action) +{ + enum virDomainLifecycleAction num = VIR_DOMAIN_LIFECYCLE_RESTART; + if (action == XEN_ON_NORMAL_EXIT_DESTROY) + num = VIR_DOMAIN_LIFECYCLE_DESTROY; + else if (action == XEN_ON_NORMAL_EXIT_RESTART) + num = VIR_DOMAIN_LIFECYCLE_RESTART; + return num; +} + + +enum virDomainLifecycleAction +xenapiCrashExitEnum2virDomainLifecycle(enum xen_on_crash_behaviour action) +{ + enum virDomainLifecycleAction num = VIR_DOMAIN_LIFECYCLE_RESTART; + if (action == XEN_ON_CRASH_BEHAVIOUR_DESTROY) + num = VIR_DOMAIN_LIFECYCLE_DESTROY; + else if (action == XEN_ON_CRASH_BEHAVIOUR_RESTART) + num = VIR_DOMAIN_LIFECYCLE_RESTART; + else if (action == XEN_ON_CRASH_BEHAVIOUR_PRESERVE) + num = VIR_DOMAIN_LIFECYCLE_PRESERVE; + else if (action == XEN_ON_CRASH_BEHAVIOUR_RENAME_RESTART) + num = VIR_DOMAIN_LIFECYCLE_RESTART_RENAME; + return num; +} + + + +/* returns 'file' or 'block' for the storage type */ +int +getStorageVolumeType(char *type) +{ + if (STREQ(type,"lvmoiscsi") || + STREQ(type,"lvmohba") || + STREQ(type,"lvm") || + STREQ(type,"file") || + STREQ(type,"iso") || + STREQ(type,"ext") || + STREQ(type,"nfs")) + return (int)VIR_STORAGE_VOL_FILE; + else if (STREQ(type,"iscsi") || + STREQ(type,"equal") || + STREQ(type,"hba") || + STREQ(type,"cslg") || + STREQ(type,"udev") || + STREQ(type,"netapp")) + return (int)VIR_STORAGE_VOL_BLOCK; + return -1; +} + +/* returns error description if any received from the server */ +char * +returnErrorFromSession(xen_session *session) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + for (i = 0; i < session->error_description_count - 1; i++) { + if (!i) + virBufferEscapeString(&buf, "%s", session->error_description[i]); + else + virBufferEscapeString(&buf, " : %s", session->error_description[i]); + } + return virBufferContentAndReset(&buf); +} + +/* converts bitmap to string of the form '1,2...' */ +char * +mapDomainPinVcpu(unsigned char *cpumap, int maplen) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + size_t len; + char *ret = NULL; + int i, j; + for (i = 0; i < maplen; i++) { + for (j = 0; j < 8; j++) { + if (cpumap[i] & (1 << j)) { + virBufferVSprintf(&buf, "%d,", (8*i)+j); + } + } + } + if (virBufferError(&buf)) { + virReportOOMError(); + virBufferFreeAndReset(&buf); + return NULL; + } + ret = virBufferContentAndReset(&buf); + len = strlen(ret); + if (len > 0 && ret[len - 1] == ',') + ret[len - 1] = 0; + return ret; +} + +/* obtains the CPU bitmap from the string passed */ +void +getCpuBitMapfromString(char *mask, unsigned char *cpumap, int maplen) +{ + int pos; + int max_bits = maplen * 8; + char *num = NULL, *bp = NULL; + bzero(cpumap, maplen); + num = strtok_r(mask, ",", &bp); + while (num != NULL) { + if (sscanf(num, "%d", &pos) != 1) + return; + if (pos < 0 || pos > max_bits - 1) + VIR_WARN ("number in str %d exceeds cpumap's max bits %d", pos, max_bits); + else + (cpumap)[pos / 8] |= (1 << (pos % 8)); + num = strtok_r(NULL, ",", &bp); + } +} + + +/* mapping XenServer power state to Libvirt power state */ +virDomainState +mapPowerState(enum xen_vm_power_state state) +{ + virDomainState virState; + switch (state) { + case XEN_VM_POWER_STATE_HALTED: + case XEN_VM_POWER_STATE_SUSPENDED: + virState = VIR_DOMAIN_SHUTOFF; + break; + case XEN_VM_POWER_STATE_PAUSED: + virState = VIR_DOMAIN_PAUSED; + break; + case XEN_VM_POWER_STATE_RUNNING: + virState = VIR_DOMAIN_RUNNING; + break; + case XEN_VM_POWER_STATE_UNKNOWN: + case XEN_VM_POWER_STATE_UNDEFINED: + virState = VIR_DOMAIN_NOSTATE; + break; + default: + virState = VIR_DOMAIN_NOSTATE; + break; + } + return virState; +} + +/* allocate a flexible array and fill values(key,val) */ +int +allocStringMap (xen_string_string_map **strings, char *key, char *val) +{ + int sz = ((*strings) == NULL) ? 0 : (*strings)->size; + sz++; + if (VIR_REALLOC_N(*strings, sizeof(xen_string_string_map) + + sizeof(xen_string_string_map_contents) * sz) < 0) { + virReportOOMError(); + return -1; + } + (*strings)->size = sz; + if (!((*strings)->contents[sz-1].key = strdup(key))) goto error; + if (!((*strings)->contents[sz-1].val = strdup(val))) goto error; + return 0; + error: + xen_string_string_map_free(*strings); + virReportOOMError(); + return -1; +} + +/* Error handling function returns error messages from the server if any */ +void +xenapiSessionErrorHandle(virConnectPtr conn, virErrorNumber errNum, + const char *buf, const char *filename, const char *func, + size_t lineno) +{ + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + + if (buf == NULL) { + char *ret = returnErrorFromSession(session); + virReportErrorHelper(conn, VIR_FROM_XENAPI, errNum, filename, func, lineno, _("%s"), ret); + xen_session_clear_error(session); + VIR_FREE(ret); + } else { + virReportErrorHelper(conn, VIR_FROM_XENAPI, errNum, filename, func, lineno, _("%s"), buf); + } +} + +/* creates network intereface for VM */ +int +createVifNetwork (virConnectPtr conn, xen_vm vm, char *device, + char *bridge, char *mac) +{ + xen_session *session = ((struct _xenapiPrivate *)(conn->privateData))->session; + xen_vm xvm = NULL; + char *uuid = NULL; + xen_vm_get_uuid(session, &uuid, vm); + if (uuid) { + if (!xen_vm_get_by_uuid(session, &xvm, uuid)) + return -1; + VIR_FREE(uuid); + } + xen_vm_record_opt *vm_opt = xen_vm_record_opt_alloc(); + vm_opt->is_record = 0; + vm_opt->u.handle = xvm; + xen_network_set *net_set = NULL; + xen_network_record *net_rec = NULL; + int cnt; + if (xen_network_get_all(session, &net_set)) { + for(cnt = 0; cnt < net_set->size; cnt++) { + if (xen_network_get_record(session, &net_rec, net_set->contents[cnt])) { + if (STREQ(net_rec->bridge, bridge)) { + break; + } else { + xen_network_record_free(net_rec); + } + } + } + } + if (cnt < net_set->size && net_rec) { + xen_network network = NULL; + xen_network_get_by_uuid(session, &network, net_rec->uuid); + xen_network_record_opt *network_opt = xen_network_record_opt_alloc(); + network_opt->is_record = 0; + network_opt->u.handle = network; + xen_vif_record *vif_record = xen_vif_record_alloc(); + vif_record->mac = mac; + vif_record->vm = vm_opt; + vif_record->network = network_opt; + xen_vif vif = NULL; + + vif_record->other_config = xen_string_string_map_alloc(0); + vif_record->runtime_properties = xen_string_string_map_alloc(0); + vif_record->qos_algorithm_params = xen_string_string_map_alloc(0); + vif_record->device = strdup(device); + xen_vif_create(session, &vif, vif_record); + if (!vif) { + xen_vif_free(vif); + xen_vif_record_free(vif_record); + xen_network_record_free(net_rec); + xen_network_set_free(net_set); + return 0; + } + xen_vif_record_free(vif_record); + xen_network_record_free(net_rec); + } + if (net_set != NULL) xen_network_set_free(net_set); + return -1; +} + +/* Create a VM record from the XML description */ +int +createVMRecordFromXml (virConnectPtr conn, virDomainDefPtr def, + xen_vm_record **record, xen_vm *vm) +{ + char uuidStr[VIR_UUID_STRING_BUFLEN]; + *record = xen_vm_record_alloc(); + if (!((*record)->name_label = strdup(def->name))) + goto error_cleanup; + if (def->uuid) { + virUUIDFormat(def->uuid, uuidStr); + if (!((*record)->uuid = strdup(uuidStr))) + goto error_cleanup; + } + if (STREQ(def->os.type, "hvm")) { + char *boot_order = NULL; + if (!((*record)->hvm_boot_policy = strdup("BIOS order"))) + goto error_cleanup; + if (def->os.nBootDevs != 0) + boot_order = createXenAPIBootOrderString(def->os.nBootDevs, &def->os.bootDevs[0]); + if (boot_order != NULL) { + xen_string_string_map *hvm_boot_params = NULL; + allocStringMap(&hvm_boot_params, (char *)"order", boot_order); + (*record)->hvm_boot_params = hvm_boot_params; + VIR_FREE(boot_order); + } + } else if (STREQ(def->os.type, "xen")) { + if (!((*record)->pv_bootloader = strdup("pygrub"))) + goto error_cleanup; + if (def->os.kernel) { + if (!((*record)->pv_kernel = strdup(def->os.kernel))) + goto error_cleanup; + } + if (def->os.initrd) { + if (!((*record)->pv_ramdisk = strdup(def->os.initrd))) + goto error_cleanup; + } + if (def->os.cmdline) { + if (!((*record)->pv_args = strdup(def->os.cmdline))) + goto error_cleanup; + } + (*record)->hvm_boot_params = xen_string_string_map_alloc(0); + } + if (def->os.bootloaderArgs) + if (!((*record)->pv_bootloader_args = strdup(def->os.bootloaderArgs))) + goto error_cleanup; + + if (def->memory) + (*record)->memory_static_max = (int64_t) (def->memory * 1024); + if (def->maxmem) + (*record)->memory_dynamic_max = (int64_t) (def->maxmem * 1024); + else + (*record)->memory_dynamic_max = (*record)->memory_static_max; + + if (def->vcpus) { + (*record)->vcpus_max = (int64_t) def->vcpus; + (*record)->vcpus_at_startup = (int64_t) def->vcpus; + } + if (def->onPoweroff) + (*record)->actions_after_shutdown = actionShutdownLibvirt2XenapiEnum(def->onPoweroff); + if (def->onReboot) + (*record)->actions_after_reboot = actionShutdownLibvirt2XenapiEnum(def->onReboot); + if (def->onCrash) + (*record)->actions_after_crash = actionCrashLibvirt2XenapiEnum(def->onCrash); + + xen_string_string_map *strings = NULL; + if (def->features) { + if (def->features & (1 << VIR_DOMAIN_FEATURE_ACPI)) + allocStringMap(&strings, (char *)"acpi", (char *)"true"); + if (def->features & (1 << VIR_DOMAIN_FEATURE_APIC)) + allocStringMap(&strings, (char *)"apic", (char *)"true"); + if (def->features & (1 << VIR_DOMAIN_FEATURE_PAE)) + allocStringMap(&strings, (char *)"pae", (char *)"true"); + } + if (strings != NULL) + (*record)->platform = strings; + + (*record)->vcpus_params = xen_string_string_map_alloc(0); + (*record)->other_config = xen_string_string_map_alloc(0); + (*record)->last_boot_cpu_flags = xen_string_string_map_alloc(0); + (*record)->xenstore_data = xen_string_string_map_alloc(0); + (*record)->hvm_shadow_multiplier = 1.000; + if (!xen_vm_create(((struct _xenapiPrivate *)(conn->privateData))->session, + vm, *record)) { + xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, NULL); + return -1; + } + + int device_number = 0; + char *bridge = NULL, *mac = NULL; + int i; + for (i = 0; i < def->nnets; i++) { + if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { + if (def->nets[i]->data.bridge.brname) + if (!(bridge = strdup(def->nets[i]->data.bridge.brname))) + goto error_cleanup; + if (def->nets[i]->mac) { + char macStr[VIR_MAC_STRING_BUFLEN]; + virFormatMacAddr(def->nets[i]->mac, macStr); + if (!(mac = strdup(macStr))) { + if (bridge) VIR_FREE(bridge); + goto error_cleanup; + } + } + if (mac != NULL && bridge != NULL) { + char device[NETWORK_DEVID_SIZE] = "\0"; + sprintf(device, "%d", device_number); + createVifNetwork(conn, *vm, device, bridge, mac); + VIR_FREE(bridge); + device_number++; + } + if (bridge) VIR_FREE(bridge); + } + } + return 0; + + error_cleanup: + virReportOOMError(); + xen_vm_record_free(*record); + return -1; +} diff --git a/src/xenapi/xenapi_utils.h b/src/xenapi/xenapi_utils.h new file mode 100644 index 0000000000..c062a1e27b --- /dev/null +++ b/src/xenapi/xenapi_utils.h @@ -0,0 +1,85 @@ +/* + * xenapi_utils.h: Xen API driver -- utils header + * Copyright (C) 2009, 2010 Citrix Ltd. + * + * 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 + * + * Author: Sharadha Prabhakar + */ + +#ifndef __VIR_XENAPI_UTILS__ +# define __VIR_XENAPI_UTILS__ + +# include +# include +# include +# include "internal.h" +# include "domain_conf.h" + +# define NETWORK_DEVID_SIZE (12) + +typedef uint64_t cpumap_t; + +void +xenSessionFree(xen_session *session); + +char * +xenapiUtil_RequestPassword(virConnectAuthPtr auth, const char *username, + const char *hostname); + +int +xenapiUtil_ParseQuery(virConnectPtr conn, xmlURIPtr uri, int *noVerify); + +enum xen_on_normal_exit +actionShutdownLibvirt2XenapiEnum(enum virDomainLifecycleAction action); + +enum xen_on_crash_behaviour +actionCrashLibvirt2XenapiEnum(enum virDomainLifecycleAction action); + +char * +createXenAPIBootOrderString(int nboot, int *bootDevs); + +enum virDomainBootOrder map2LibvirtBootOrder(char c); + +enum virDomainLifecycleAction +xenapiNormalExitEnum2virDomainLifecycle(enum xen_on_normal_exit action); + +enum virDomainLifecycleAction +xenapiCrashExitEnum2virDomainLifecycle(enum xen_on_crash_behaviour action); + +void getCpuBitMapfromString(char *mask, unsigned char *cpumap, int maplen); + +int getStorageVolumeType(char *type); + +char *returnErrorFromSession(xen_session *session); + +virDomainState +mapPowerState(enum xen_vm_power_state state); + +char * +mapDomainPinVcpu(unsigned char *cpumap, int maplen); + +int +createVMRecordFromXml (virConnectPtr conn, virDomainDefPtr defPtr, + xen_vm_record **record, xen_vm *vm); + +int +allocStringMap (xen_string_string_map **strings, char *key, char *val); + +int +createVifNetwork(virConnectPtr conn, xen_vm vm, char *device, + char *bridge, char *mac); + +#endif /* __VIR_XENAPI_UTILS__ */