[libvirt] [Patch v3] Add VMware Workstation and Player driver

* Changes since v2: remove unnecessary mutex locks use vmrun reset soft convert popen to virCommand remove duplicate errors various other fixes

--- cfg.mk | 1 + configure.ac | 7 + include/libvirt/virterror.h | 1 + po/POTFILES.in | 2 + src/Makefile.am | 24 +- src/driver.h | 3 +- src/libvirt.c | 15 + src/util/virterror.c | 3 + src/vmware/vmware_conf.c | 453 +++++++++++++++ src/vmware/vmware_conf.h | 82 +++ src/vmware/vmware_driver.c | 1351 +++++++++++++++++++++++++++++++++++++++++++ src/vmware/vmware_driver.h | 25 + 12 files changed, 1963 insertions(+), 4 deletions(-) create mode 100644 src/vmware/vmware_conf.c create mode 100644 src/vmware/vmware_conf.h create mode 100644 src/vmware/vmware_driver.c create mode 100644 src/vmware/vmware_driver.h diff --git a/cfg.mk b/cfg.mk index 29de9d9..4c4a0a7 100644 --- a/cfg.mk +++ b/cfg.mk @@ -387,6 +387,7 @@ msg_gen_function += virXMLError msg_gen_function += virXenInotifyError msg_gen_function += virXenStoreError msg_gen_function += virXendError +msg_gen_function += vmwareError msg_gen_function += xenapiSessionErrorHandler msg_gen_function += xenUnifiedError msg_gen_function += xenXMError diff --git a/configure.ac b/configure.ac index dde2cde..0e050d3 100644 --- a/configure.ac +++ b/configure.ac @@ -227,6 +227,8 @@ AC_ARG_WITH([uml], AC_HELP_STRING([--with-uml], [add UML support @<:@default=check@:>@]),[],[with_uml=check]) AC_ARG_WITH([openvz], AC_HELP_STRING([--with-openvz], [add OpenVZ support @<:@default=yes@:>@]),[],[with_openvz=yes]) +AC_ARG_WITH([vmware], + AC_HELP_STRING([--with-vmware], [add VMware support @<:@default=yes@:>@]),[],[with_vmware=yes]) AC_ARG_WITH([libssh2], AC_HELP_STRING([--with-libssh2=@<:@PFX@:>@], [libssh2 location @<:@default=/usr/local/lib@:>@]),[],[with_libssh2=yes]) AC_ARG_WITH([phyp], @@ -316,6 +318,10 @@ if test "$with_openvz" = "yes"; then fi AM_CONDITIONAL([WITH_OPENVZ], [test "$with_openvz" = "yes"]) +if test "$with_vmware" = "yes"; then + AC_DEFINE_UNQUOTED([WITH_VMWARE], 1, [whether VMware driver is enabled]) +fi +AM_CONDITIONAL([WITH_VMWARE], [test "$with_vmware" = "yes"]) dnl dnl check for XDR @@ -2274,6 +2280,7 @@ AC_MSG_NOTICE([ Xen: $with_xen]) AC_MSG_NOTICE([ QEMU: $with_qemu]) AC_MSG_NOTICE([ UML: $with_uml]) AC_MSG_NOTICE([ OpenVZ: $with_openvz]) +AC_MSG_NOTICE([ VMware: $with_vmware]) AC_MSG_NOTICE([ VBox: $with_vbox]) AC_MSG_NOTICE([ XenAPI: $with_xenapi]) AC_MSG_NOTICE([ LXC: $with_lxc]) diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index eaeb477..a1f88f4 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -52,6 +52,7 @@ typedef enum { VIR_FROM_TEST, /* Error from test driver */ VIR_FROM_REMOTE, /* Error from remote driver */ VIR_FROM_OPENVZ, /* Error from OpenVZ driver */ + VIR_FROM_VMWARE, /* Error from VMware driver */ VIR_FROM_XENXM, /* Error at Xen XM layer */ VIR_FROM_STATS_LINUX,/* Error in the Linux Stats code */ VIR_FROM_LXC, /* Error from Linux Container driver */ diff --git a/po/POTFILES.in b/po/POTFILES.in index e7be0d3..5561ace 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -100,6 +100,8 @@ src/util/xml.c src/vbox/vbox_XPCOMCGlue.c src/vbox/vbox_driver.c src/vbox/vbox_tmpl.c +src/vmware/vmware_conf.c +src/vmware/vmware_driver.c src/xen/xen_driver.c src/xen/xen_hypervisor.c src/xen/xen_inotify.c diff --git a/src/Makefile.am b/src/Makefile.am index 0923d60..aa6fc3e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -218,8 +218,6 @@ check-local: remote_protocol-structs TEST_DRIVER_SOURCES = \ test/test_driver.c test/test_driver.h - - # Now the Hypervisor specific drivers XEN_DRIVER_SOURCES = \ xen/sexpr.c xen/sexpr.h \ @@ -255,6 +253,10 @@ OPENVZ_DRIVER_SOURCES = \ openvz/openvz_conf.c openvz/openvz_conf.h \ openvz/openvz_driver.c openvz/openvz_driver.h +VMWARE_DRIVER_SOURCES = \ + vmware/vmware_driver.c vmware/vmware.h \ + vmware/vmware_conf.c vmware/vmware_conf.h + VBOX_DRIVER_SOURCES = \ vbox/vbox_XPCOMCGlue.c vbox/vbox_XPCOMCGlue.h \ vbox/vbox_driver.c vbox/vbox_driver.h \ @@ -600,6 +602,21 @@ endif libvirt_driver_openvz_la_SOURCES = $(OPENVZ_DRIVER_SOURCES) endif +if WITH_VMWARE +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_vmware.la +else +noinst_LTLIBRARIES += libvirt_driver_vmware.la +libvirt_la_BUILT_LIBADD += libvirt_driver_vmware.la +endif +libvirt_driver_vmware_la_CFLAGS = \ + -I@top_srcdir@/src/conf +if WITH_DRIVER_MODULES +libvirt_driver_vmware_la_LDFLAGS = -module -avoid-version +endif +libvirt_driver_vmware_la_SOURCES = $(VMWARE_DRIVER_SOURCES) +endif + if WITH_VBOX if WITH_DRIVER_MODULES mod_LTLIBRARIES += libvirt_driver_vbox.la @@ -952,7 +969,8 @@ EXTRA_DIST += \ $(SECURITY_DRIVER_SELINUX_SOURCES) \ $(SECURITY_DRIVER_APPARMOR_SOURCES) \ $(SECRET_DRIVER_SOURCES) \ - $(VBOX_DRIVER_EXTRA_DIST) + $(VBOX_DRIVER_EXTRA_DIST) \ + $(VMWARE_DRIVER_SOURCES) check-local: augeas-check diff --git a/src/driver.h b/src/driver.h index 75305fe..03a388a 100644 --- a/src/driver.h +++ b/src/driver.h @@ -27,7 +27,8 @@ typedef enum { VIR_DRV_ONE = 9, VIR_DRV_ESX = 10, VIR_DRV_PHYP = 11, - VIR_DRV_XENAPI = 12 + VIR_DRV_XENAPI = 12, + VIR_DRV_VMWARE = 13 } virDrvNo; diff --git a/src/libvirt.c b/src/libvirt.c index 4188b45..82e183e 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -54,6 +54,9 @@ # ifdef WITH_OPENVZ # include "openvz/openvz_driver.h" # endif +# ifdef WITH_VMWARE +# include "vmware/vmware_driver.h" +# endif # ifdef WITH_PHYP # include "phyp/phyp_driver.h" # endif @@ -365,6 +368,9 @@ virInitialize(void) # ifdef WITH_OPENVZ virDriverLoadModule("openvz"); # endif +# ifdef WITH_VMWARE + virDriverLoadModule("vmware"); +# endif # ifdef WITH_VBOX virDriverLoadModule("vbox"); # endif @@ -387,6 +393,9 @@ virInitialize(void) # ifdef WITH_OPENVZ if (openvzRegister() == -1) return -1; # endif +# ifdef WITH_VMWARE + if (vmwareRegister() == -1) return -1; +# endif # ifdef WITH_PHYP if (phypRegister() == -1) return -1; # endif @@ -1120,6 +1129,12 @@ virGetVersion(unsigned long *libVer, const char *type, if (STRCASEEQ(type, "OpenVZ")) *typeVer = LIBVIR_VERSION_NUMBER; # endif +# if WITH_VMWARE + if (STRCASEEQ(type, "vmware player")) + *typeVer = LIBVIR_VERSION_NUMBER; + if (STRCASEEQ(type, "vmware workstation")) + *typeVer = LIBVIR_VERSION_NUMBER; +# endif # if WITH_VBOX if (STRCASEEQ(type, "VBox")) *typeVer = LIBVIR_VERSION_NUMBER; diff --git a/src/util/virterror.c b/src/util/virterror.c index 6e49fa2..6b9653e 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -134,6 +134,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_OPENVZ: dom = "OpenVZ "; break; + case VIR_FROM_VMWARE: + dom = "VMware "; + break; case VIR_FROM_XENXM: dom = "Xen XM "; break; diff --git a/src/vmware/vmware_conf.c b/src/vmware/vmware_conf.c new file mode 100644 index 0000000..2255b52 --- /dev/null +++ b/src/vmware/vmware_conf.c @@ -0,0 +1,453 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright 2010, diateam (www.diateam.net) + * + * 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 <string.h> +#include <sys/utsname.h> + +#include "command.h" +#include "cpu/cpu.h" +#include "memory.h" +#include "nodeinfo.h" +#include "util/files.h" +#include "uuid.h" +#include "virterror_internal.h" +#include "../esx/esx_vmx.h" + +#include "vmware_conf.h" + +#define VMWARE_MAX_ARG 20 + +/* Free all memory associated with a vmware_driver structure */ +void +vmwareFreeDriver(struct vmware_driver *driver) +{ + if (!driver) + return; + + virMutexDestroy(&driver->lock); + virDomainObjListDeinit(&driver->domains); + virCapabilitiesFree(driver->caps); + VIR_FREE(driver); +} + +virCapsPtr +vmwareCapsInit(void) +{ + struct utsname utsname; + virCapsPtr caps = NULL; + virCapsGuestPtr guest = NULL; + virCPUDefPtr cpu = NULL; + union cpuData *data = NULL; + + uname(&utsname); + + if ((caps = virCapabilitiesNew(utsname.machine, 0, 0)) == NULL) + goto error; + + if (nodeCapsInitNUMA(caps) < 0) + goto error; + + virCapabilitiesSetMacPrefix(caps, (unsigned char[]) {0x52, 0x54, 0x00}); + + /* i686 guests are always supported */ + if ((guest = virCapabilitiesAddGuest(caps, + "hvm", + "i686", + 32, + NULL, NULL, 0, NULL)) == NULL) + goto error; + + if (virCapabilitiesAddGuestDomain(guest, + "vmware", + NULL, NULL, 0, NULL) == NULL) + goto error; + + /* check if x86_64 guests are supported */ + if (VIR_ALLOC(cpu) < 0 + || !(cpu->arch = strdup(utsname.machine))) { + virReportOOMError(); + goto error; + } + + cpu->type = VIR_CPU_TYPE_HOST; + + if (!(data = cpuNodeData(cpu->arch)) + || cpuDecode(cpu, data, NULL, 0, NULL) < 0) { + goto error; + } + + if (cpuHasFeature(utsname.machine, data, "vmx") + || cpuHasFeature(utsname.machine, data, "svm")) { + + if ((guest = virCapabilitiesAddGuest(caps, + "hvm", + "x86_64", + 64, + NULL, NULL, 0, NULL)) == NULL) + goto error; + + if (virCapabilitiesAddGuestDomain(guest, + "vmware", + NULL, NULL, 0, NULL) == NULL) + goto error; + } + +cleanup: + virCPUDefFree(cpu); + cpuDataFree(utsname.machine, data); + + return caps; + +error: + virCapabilitiesFree(caps); + goto cleanup; +} + +int +vmwareLoadDomains(struct vmware_driver *driver) +{ + virDomainDefPtr vmdef = NULL; + virDomainObjPtr vm = NULL; + char *vmxPath = NULL; + char *vmx = NULL; + vmwareDomainPtr pDomain; + char *directoryName = NULL; + char *fileName = NULL; + int ret = -1; + esxVMX_Context ctx; + size_t len = 0; + char *outbuf = NULL; + char *str; + char *saveptr = NULL; + virCommandPtr cmd; + + ctx.parseFileName = esxCopyVMXFileName; + + cmd = virCommandNewArgList(VMRUN, "-T", + driver->type == TYPE_PLAYER ? "player" : "ws", + "list", NULL); + virCommandSetOutputBuffer(cmd, &outbuf); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + for(str = outbuf ; (vmxPath = strtok_r(str, "\n", &saveptr)) != NULL; + str = NULL) { + + if (vmxPath[0] != '/') + continue; + + /* remove trailing newline */ + len = strlen(vmxPath); + if (len && vmxPath[len-1] == '\n') + vmxPath[len-1] = '\0'; + + if (virFileReadAll(vmxPath, 10000, &vmx) == -1) { + perror("error reading vmx file"); + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to read vmx file %s"), vmxPath); + goto cleanup; + } + + if ((vmdef = + esxVMX_ParseConfig(&ctx, driver->caps, vmx, + esxVI_ProductVersion_ESX4x)) == NULL) { + goto cleanup; + } + + if (!(vm = virDomainAssignDef(driver->caps, + &driver->domains, vmdef, false))) + goto cleanup; + + pDomain = vm->privateData; + + pDomain->vmxPath = strdup(vmxPath); + if (pDomain->vmxPath == NULL) { + virReportOOMError(); + goto cleanup; + } + + vmwareDomainConfigDisplay(pDomain, vmdef); + + //vmrun list only reports running vms + vm->state = VIR_DOMAIN_RUNNING; + vm->def->id = driver->nextvmid++; + vm->persistent = 1; + + virDomainObjUnlock(vm); + + vmdef = NULL; + vm = NULL; + } + + ret = 0; + +cleanup: + virCommandFree(cmd); + VIR_FREE(outbuf); + virDomainDefFree(vmdef); + VIR_FREE(directoryName); + VIR_FREE(fileName); + VIR_FREE(vmx); + if (vm) + virDomainObjUnref(vm); + return ret; +} + +void +vmwareSetSentinal(const char **prog, const char *key) +{ + const char **tmp = prog; + + while (tmp && *tmp) { + if (*tmp == PROGRAM_SENTINAL) { + *tmp = key; + break; + } + tmp++; + } +} + +int +vmwareExtractVersion(struct vmware_driver *driver) +{ + unsigned long version = 0; + char *tmp; + int ret = -1; + virCommandPtr cmd; + char * outbuf = NULL; + const char * bin = (driver->type == TYPE_PLAYER) ? "vmplayer" : "vmware"; + const char * pattern = (driver->type == TYPE_PLAYER) ? + "VMware Player " : "VMware Workstation "; + + cmd = virCommandNewArgList(bin, "-v", NULL); + virCommandSetOutputBuffer(cmd, &outbuf); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if ((tmp = STRSKIP(outbuf, pattern)) == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse %s version"), bin); + goto cleanup; + } + + if (virParseVersionString(tmp, &version) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("version parsing error")); + goto cleanup; + } + + driver->version = version; + ret = 0; + +cleanup: + virCommandFree(cmd); + VIR_FREE(outbuf); + return ret; +} + +int +vmwareDomainConfigDisplay(vmwareDomainPtr pDomain, virDomainDefPtr def) +{ + int i = 0; + + if (def->ngraphics == 0) { + pDomain->gui = true; + return 0; + } else { + pDomain->gui = false; + for (i = 0; i < def->ngraphics; i++) { + if (def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP) { + pDomain->gui = true; + return 0; + } + } + return 0; + } +} + +int +vmwareParsePath(char *path, char **directory, char **filename) +{ + char *separator; + + separator = strrchr(path, '/'); + + if (separator != NULL) { + *separator++ = '\0'; + + if (*separator == '\0') { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("path '%s' doesn't reference a file"), path); + return -1; + } + + if ((*directory = strdup(path)) == NULL) + goto no_memory; + if ((*filename = strdup(separator)) == NULL) + goto no_memory; + + } else { + if ((*filename = strdup(separator)) == NULL) + goto no_memory; + } + + return 0; + +no_memory: + virReportOOMError(); + return -1; +} + +int +vmwareConstructVmxPath(char *directoryName, char *name, char **vmxPath) +{ + if (directoryName != NULL) { + if (virAsprintf(vmxPath, "%s/%s.vmx", directoryName, name) < 0) { + virReportOOMError(); + return -1; + } + } else { + if (virAsprintf(vmxPath, "%s.vmx", name) < 0) { + virReportOOMError(); + return -1; + } + } + return 0; +} + +int +vmwareVmxPath(virDomainDefPtr vmdef, char **vmxPath) +{ + virDomainDiskDefPtr disk = NULL; + char *directoryName = NULL; + char *fileName = NULL; + int ret = -1; + int i = 0; + + /* + * Build VMX URL. Use the source of the first file-based harddisk + * to deduce the path for the VMX file. Don't just use the + * first disk, because it may be CDROM disk and ISO images are normaly not + * located in the virtual machine's directory. This approach + * isn't perfect but should work in the majority of cases. + */ + if (vmdef->ndisks < 1) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Domain XML doesn't contain any disks, " + "cannot deduce datastore and path for VMX file")); + goto cleanup; + } + + for (i = 0; i < vmdef->ndisks; ++i) { + if (vmdef->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK && + vmdef->disks[i]->type == VIR_DOMAIN_DISK_TYPE_FILE) { + disk = vmdef->disks[i]; + break; + } + } + + if (disk == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Domain XML doesn't contain any file-based harddisks, " + "cannot deduce datastore and path for VMX file")); + goto cleanup; + } + + if (disk->src == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("First file-based harddisk has no source, cannot " + "deduce datastore and path for VMX file")); + goto cleanup; + } + + if (vmwareParsePath(disk->src, &directoryName, &fileName) < 0) { + goto cleanup; + } + + if (!virFileHasSuffix(fileName, ".vmdk")) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("Expecting source '%s' of first file-based harddisk " + "to be a VMDK image"), disk->src); + goto cleanup; + } + + if (vmwareConstructVmxPath(directoryName, vmdef->name, vmxPath) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FREE(directoryName); + VIR_FREE(fileName); + return ret; +} + +int +vmwareMoveFile(char *srcFile, char *dstFile) +{ + const char *cmdmv[] = + { "mv", PROGRAM_SENTINAL, PROGRAM_SENTINAL, NULL }; + + if (!virFileExists(srcFile)) { + vmwareError(VIR_ERR_INTERNAL_ERROR, _("file %s does not exist"), + srcFile); + return -1; + } + + if (STREQ(srcFile, dstFile)) + return 0; + + vmwareSetSentinal(cmdmv, srcFile); + vmwareSetSentinal(cmdmv, dstFile); + if (virRun(cmdmv, NULL) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to move file to %s "), dstFile); + return -1; + } + + return 0; +} + +int +vmwareMakePath(char *srcDir, char *srcName, char *srcExt, char **outpath) +{ + if (virAsprintf(outpath, "%s/%s.%s", srcDir, srcName, srcExt) < 0) { + virReportOOMError(); + return -1; + } + return 0; +} + +char * +esxCopyVMXFileName(const char *datastorePath, void *opaque ATTRIBUTE_UNUSED) +{ + char *path = strdup(datastorePath); + + if (path == NULL) { + virReportOOMError(); + return NULL; + } + + return path; +} diff --git a/src/vmware/vmware_conf.h b/src/vmware/vmware_conf.h new file mode 100644 index 0000000..a41c846 --- /dev/null +++ b/src/vmware/vmware_conf.h @@ -0,0 +1,82 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright 2010, diateam (www.diateam.net) + * + * 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 + */ +/*---------------------------------------------------------------------------*/ + +#ifndef VMWARE_CONF_H +# define VMWARE_CONF_H + +# define VMRUN "vmrun" +# define NOGUI "nogui" + +# include "internal.h" +# include "domain_conf.h" +# include "threads.h" + +# define VIR_FROM_THIS VIR_FROM_VMWARE +# define PROGRAM_SENTINAL ((char *)0x1) + +# define vmwareError(code, ...) \ + virReportErrorHelper(NULL, VIR_FROM_VMWARE, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +# define TYPE_PLAYER 0 +# define TYPE_WORKSTATION 1 + +struct vmware_driver { + virMutex lock; + virCapsPtr caps; + + virDomainObjList domains; + int version; + int type; + int nextvmid; +}; + +typedef struct _vmwareDomain { + char *vmxPath; + bool gui; + +} vmwareDomain, *vmwareDomainPtr; + +void vmwareFreeDriver(struct vmware_driver *driver); + +virCapsPtr vmwareCapsInit(void); + +int vmwareLoadDomains(struct vmware_driver *driver); + +void vmwareSetSentinal(const char **prog, const char *key); + +int vmwareExtractVersion(struct vmware_driver *driver); + +int vmwareDomainConfigDisplay(vmwareDomainPtr domain, virDomainDefPtr vmdef); + +int vmwareParsePath(char *path, char **directory, char **filename); + +int vmwareConstructVmxPath(char *directoryName, char *name, + char **vmxPath); + +int vmwareVmxPath(virDomainDefPtr vmdef, char **vmxPath); + +int vmwareMoveFile(char *srcFile, char *dstFile); + +int vmwareMakePath(char *srcDir, char *srcName, char *srcExt, + char **outpath); + +char * esxCopyVMXFileName(const char *datastorePath, void *opaque); + +#endif diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c new file mode 100644 index 0000000..5d8a8d5 --- /dev/null +++ b/src/vmware/vmware_driver.c @@ -0,0 +1,1351 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright 2010, diateam (www.diateam.net) + * + * 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 <fcntl.h> + +#include "files.h" +#include "memory.h" +#include "uuid.h" + +#include "../esx/esx_vmx.h" +#include "vmware_conf.h" +#include "vmware_driver.h" + +const char *vmw_types[] = { "player", "ws" }; + +static void +vmwareDriverLock(struct vmware_driver *driver) +{ + virMutexLock(&driver->lock); +} + +static void +vmwareDriverUnlock(struct vmware_driver *driver) +{ + virMutexUnlock(&driver->lock); +} + +static void * +vmwareDataAllocFunc(void) +{ + vmwareDomainPtr dom; + + if (VIR_ALLOC(dom) < 0) + return NULL; + + dom->vmxPath = NULL; + dom->gui = true; + + return dom; +} + +static void +vmwareDataFreeFunc(void *data) +{ + vmwareDomainPtr dom = data; + + VIR_FREE(dom->vmxPath); + VIR_FREE(dom); +} + +static virDrvOpenStatus +vmwareOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + struct vmware_driver *driver; + char * vmrun = NULL; + + if (conn->uri == NULL) { + /* @TODO accept */ + return VIR_DRV_OPEN_DECLINED; + } else { + if (conn->uri->scheme == NULL || + (STRNEQ(conn->uri->scheme, "vmwareplayer") && + STRNEQ(conn->uri->scheme, "vmwarews"))) + return VIR_DRV_OPEN_DECLINED; + + /* If server name is given, its for remote driver */ + if (conn->uri->server != NULL) + return VIR_DRV_OPEN_DECLINED; + + /* If path isn't /session, then they typoed, so tell them correct path */ + if (conn->uri->path == NULL || STRNEQ(conn->uri->path, "/session")) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("unexpected VMware URI path '%s', try vmwareplayer:///session or vmwarews:///session"), + NULLSTR(conn->uri->path)); + return VIR_DRV_OPEN_ERROR; + } + } + + /* We now know the URI is definitely for this driver, so beyond + * here, don't return DECLINED, always use ERROR */ + + vmrun = virFindFileInPath(VMRUN); + + if (vmrun == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("%s utility is missing"), VMRUN); + return VIR_DRV_OPEN_ERROR; + } else { + VIR_FREE(vmrun); + } + + if (VIR_ALLOC(driver) < 0) { + virReportOOMError(); + return VIR_DRV_OPEN_ERROR; + } + + if (virMutexInit(&driver->lock) < 0) + goto cleanup; + + driver->type = STRNEQ(conn->uri->scheme, "vmwareplayer") ? + TYPE_WORKSTATION : TYPE_PLAYER; + + if (virDomainObjListInit(&driver->domains) < 0) + goto cleanup; + + if (!(driver->caps = vmwareCapsInit())) + goto cleanup; + + driver->caps->privateDataAllocFunc = vmwareDataAllocFunc; + driver->caps->privateDataFreeFunc = vmwareDataFreeFunc; + + driver->nextvmid = 1; + if (vmwareLoadDomains(driver) < 0) + goto cleanup; + + if (vmwareExtractVersion(driver) < 0) + goto cleanup; + + conn->privateData = driver; + + return VIR_DRV_OPEN_SUCCESS; + + cleanup: + vmwareFreeDriver(driver); + return VIR_DRV_OPEN_ERROR; +}; + +static int +vmwareClose(virConnectPtr conn) +{ + struct vmware_driver *driver = conn->privateData; + + vmwareFreeDriver(driver); + + conn->privateData = NULL; + + return 0; +} + +static const char * +vmwareGetType(virConnectPtr conn) +{ + struct vmware_driver *driver = conn->privateData; + int type; + + type = driver->type; + return type == TYPE_PLAYER ? "vmware player" : "vmware workstation"; +} + +static int +vmwareGetVersion(virConnectPtr conn, unsigned long *version) +{ + struct vmware_driver *driver = conn->privateData; + + vmwareDriverLock(driver); + *version = driver->version; + vmwareDriverUnlock(driver); + return 0; +} + +static int +vmwareStartVM(struct vmware_driver *driver, virDomainObjPtr vm) +{ + const char *cmd[] = { + VMRUN, "-T", PROGRAM_SENTINAL, "start", + PROGRAM_SENTINAL, PROGRAM_SENTINAL, NULL + }; + + if (vm->state != VIR_DOMAIN_SHUTOFF) { + vmwareError(VIR_ERR_OPERATION_DENIED, "%s", + _("domain is not in shutoff state")); + return -1; + } + + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareSetSentinal(cmd, ((vmwareDomainPtr) vm->privateData)->vmxPath); + if (!((vmwareDomainPtr) vm->privateData)->gui) + vmwareSetSentinal(cmd, NOGUI); + else + vmwareSetSentinal(cmd, NULL); + + if (virRun(cmd, NULL) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, _("Could not exec %s"), VMRUN); + return -1; + } + + vm->def->id = driver->nextvmid++; + vm->state = VIR_DOMAIN_RUNNING; + + return 0; +} + +static int +vmwareStopVM(struct vmware_driver *driver, virDomainObjPtr vm) +{ + const char *cmd[] = { + VMRUN, "-T", PROGRAM_SENTINAL, "stop", + PROGRAM_SENTINAL, "soft", NULL + }; + + if (vm->state != VIR_DOMAIN_RUNNING) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("domain is not in running state")); + return -1; + } + + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareSetSentinal(cmd, ((vmwareDomainPtr) vm->privateData)->vmxPath); + + if (virRun(cmd, NULL) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, _("Could not exec %s"), VMRUN); + return -1; + } + + vm->def->id = -1; + vm->state = VIR_DOMAIN_SHUTOFF; + + return 0; +} + +static virDomainPtr +vmwareDomainDefineXML(virConnectPtr conn, const char *xml) +{ + struct vmware_driver *driver = conn->privateData; + virDomainDefPtr vmdef = NULL; + virDomainObjPtr vm = NULL; + virDomainPtr dom = NULL; + char *vmx = NULL; + char *directoryName = NULL; + char *fileName = NULL; + char *vmxPath = NULL; + vmwareDomainPtr pDomain = NULL; + esxVMX_Context ctx; + + ctx.formatFileName = esxCopyVMXFileName; + + vmwareDriverLock(driver); + if ((vmdef = virDomainDefParseString(driver->caps, xml, + VIR_DOMAIN_XML_INACTIVE)) == NULL) + goto cleanup; + + vm = virDomainFindByName(&driver->domains, vmdef->name); + if (vm) { + vmwareError(VIR_ERR_OPERATION_FAILED, + _("Already an VMWARE VM active with the id '%s'"), + vmdef->name); + goto cleanup; + } + + /* generate vmx file */ + vmx = esxVMX_FormatConfig(&ctx, driver->caps, vmdef, + esxVI_ProductVersion_ESX4x); + if (vmx == NULL) + goto cleanup; + + if (vmwareVmxPath(vmdef, &vmxPath) < 0) + goto cleanup; + + /* create vmx file */ + if (virFileWriteStr(vmxPath, vmx, S_IRUSR|S_IWUSR) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("Failed to write vmx file '%s'"), vmxPath); + goto cleanup; + } + + /* assign def */ + if (!(vm = virDomainAssignDef(driver->caps, + &driver->domains, vmdef, false))) + goto cleanup; + + pDomain = vm->privateData; + if ((pDomain->vmxPath = strdup(vmxPath)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + vmwareDomainConfigDisplay(pDomain, vmdef); + + vmdef = NULL; + vm->persistent = 1; + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = -1; + + cleanup: + virDomainDefFree(vmdef); + VIR_FREE(vmx); + VIR_FREE(directoryName); + VIR_FREE(fileName); + VIR_FREE(vmxPath); + if (vm) + virDomainObjUnlock(vm); + vmwareDriverUnlock(driver); + return dom; +} + +static int +vmwareDomainShutdown(virDomainPtr dom) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + vmwareDriverLock(driver); + + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (!vm) { + vmwareError(VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + if (vmwareStopVM(driver, vm) < 0) + goto cleanup; + + if (!vm->persistent) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + } + + ret = 0; + cleanup: + if (vm) + virDomainObjUnlock(vm); + vmwareDriverUnlock(driver); + return ret; +} + +static int +vmwareDomainSuspend(virDomainPtr dom) +{ + struct vmware_driver *driver = dom->conn->privateData; + + virDomainObjPtr vm; + const char *cmd[] = { + VMRUN, "-T", PROGRAM_SENTINAL, "pause", + PROGRAM_SENTINAL, NULL + }; + int ret = -1; + + if (driver->type == TYPE_PLAYER) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("vmplayer does not support libvirt suspend/resume" + " (vmware pause/unpause) operation ")); + return ret; + } + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareSetSentinal(cmd, ((vmwareDomainPtr) vm->privateData)->vmxPath); + if (vm->state != VIR_DOMAIN_RUNNING) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("domain is not in running state")); + goto cleanup; + } + + if (virRun(cmd, NULL) < 0) + goto cleanup; + + vm->state = VIR_DOMAIN_PAUSED; + ret = 0; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int +vmwareDomainResume(virDomainPtr dom) +{ + struct vmware_driver *driver = dom->conn->privateData; + + virDomainObjPtr vm; + const char *cmd[] = { + VMRUN, "-T", PROGRAM_SENTINAL, "unpause", PROGRAM_SENTINAL, + NULL + }; + int ret = -1; + + if (driver->type == TYPE_PLAYER) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("vmplayer does not support libvirt suspend/resume" + "(vmware pause/unpause) operation ")); + return ret; + } + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareSetSentinal(cmd, ((vmwareDomainPtr) vm->privateData)->vmxPath); + if (vm->state != VIR_DOMAIN_PAUSED) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("domain is not in suspend state")); + goto cleanup; + } + + if (virRun(cmd, NULL) < 0) + goto cleanup; + + vm->state = VIR_DOMAIN_RUNNING; + ret = 0; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int +vmwareDomainReboot(virDomainPtr dom, unsigned int flags ATTRIBUTE_UNUSED) +{ + struct vmware_driver *driver = dom->conn->privateData; + + virDomainObjPtr vm; + const char *cmd[] = { + VMRUN, "-T", PROGRAM_SENTINAL, + "reset", PROGRAM_SENTINAL, "soft", NULL + }; + int ret = -1; + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareSetSentinal(cmd, ((vmwareDomainPtr) vm->privateData)->vmxPath); + + + if (vm->state != VIR_DOMAIN_RUNNING) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("domain is not in running state")); + goto cleanup; + } + + if (virRun(cmd, NULL) < 0) + goto cleanup; + + vmwareDriverLock(driver); + vm->def->id = driver->nextvmid++; + vmwareDriverUnlock(driver); + vm->state = VIR_DOMAIN_RUNNING; + ret = 0; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int +vmwareDomainSave(virDomainPtr dom, const char *path) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + const char *cmdsuspend[] = { + VMRUN, "-T", PROGRAM_SENTINAL, + "suspend", PROGRAM_SENTINAL, NULL + }; + int ret = -1; + char *fDirectoryName = NULL; + char *fFileName = NULL; + char *tDirectoryName = NULL; + char *tFileName = NULL; + char *copyPath = NULL; + char *copyvmxPath = NULL; + char *fvmss = NULL; + char *tvmss = NULL; + char *fvmem = NULL; + char *tvmem = NULL; + char *fvmx = NULL; + char *tvmx = NULL; + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + /* vmware suspend */ + vmwareSetSentinal(cmdsuspend, vmw_types[driver->type]); + vmwareSetSentinal(cmdsuspend, + ((vmwareDomainPtr) vm->privateData)->vmxPath); + + if (vm->state != VIR_DOMAIN_RUNNING) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("domain is not in running state")); + goto cleanup; + } + + if (virRun(cmdsuspend, NULL) < 0) + goto cleanup; + + /* create files path */ + copyvmxPath = strdup(((vmwareDomainPtr) vm->privateData)->vmxPath); + if(copyvmxPath == NULL) { + virReportOOMError(); + goto cleanup; + } + + if((copyPath = strdup(path)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (vmwareParsePath(copyvmxPath, &fDirectoryName, &fFileName) < 0) + goto cleanup; + if (vmwareParsePath(copyPath, &tDirectoryName, &tFileName) < 0) + goto cleanup; + + /* dir */ + if (virFileMakePath(tDirectoryName) != 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", _("make path error")); + goto cleanup; + } + + if (vmwareMakePath(fDirectoryName, vm->def->name,(char *) "vmss", &fvmss) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, vm->def->name, (char *) "vmss", &tvmss) < 0) + goto cleanup; + if (vmwareMakePath(fDirectoryName, vm->def->name, (char *) "vmem", &fvmem) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, vm->def->name, (char *) "vmem", &tvmem) < 0) + goto cleanup; + if (vmwareMakePath(fDirectoryName, vm->def->name, (char *) "vmx", &fvmx) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, vm->def->name, (char *) "vmx", &tvmx) < 0) + goto cleanup; + + /* create linker file */ + if (virFileWriteStr(path, ((vmwareDomainPtr) vm->privateData)->vmxPath, + S_IRUSR|S_IWUSR) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("Failed to write save file '%s'"), path); + goto cleanup; + } + + /* we want to let saved VM inside default directory */ + if (STREQ(fDirectoryName, tDirectoryName)) + goto end; + + /* move {vmx,vmss,vmem} files */ + if ((vmwareMoveFile(fvmss, tvmss) < 0) + || (vmwareMoveFile(fvmem, tvmem) < 0) + || (vmwareMoveFile(fvmx, tvmx) < 0)) { + goto cleanup; + } + + end: + vm->def->id = -1; + vm->state = VIR_DOMAIN_SHUTOFF; + ret = 0; + + cleanup: + VIR_FREE(fDirectoryName); + VIR_FREE(fFileName); + VIR_FREE(tDirectoryName); + VIR_FREE(tFileName); + VIR_FREE(copyPath); + VIR_FREE(copyvmxPath); + + VIR_FREE(fvmss); + VIR_FREE(tvmss); + VIR_FREE(fvmem); + VIR_FREE(tvmem); + VIR_FREE(fvmx); + VIR_FREE(tvmx); + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int +vmwareDomainRestore(virConnectPtr conn, const char *path) +{ + struct vmware_driver *driver = conn->privateData; + virDomainDefPtr vmdef = NULL; + virDomainPtr dom = NULL; + virDomainObjPtr vm = NULL; + int ret = -1; + char *vmxPath = NULL; + char *copypath = NULL; + char *copyvmxPath = NULL; + char *tDirectoryName = NULL; + char *tFileName = NULL; + char *fDirectoryName = NULL; + char *fFileName = NULL; + char *vmx = NULL; + char *xml = NULL; + char *sep = NULL; + char *baseVmss; + char *fvmss = NULL; + char *tvmss = NULL; + char *fvmem = NULL; + char *tvmem = NULL; + char *fvmx = NULL; + char *tvmx = NULL; + FILE *pFile = NULL; + esxVMX_Context ctx; + + ctx.parseFileName = esxCopyVMXFileName; + + if (!virFileExists(path)) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("file %s does not exist"), path); + goto cleanup; + } + if (virFileHasSuffix(path, ".vmx")) { //we want to restore a vm saved in its default directory + if((tvmx = strdup(path)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if((copypath = strdup(path)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (vmwareParsePath(copypath, &fDirectoryName, &fFileName) < 0) + goto cleanup; + + sep = strrchr(fFileName, '.'); + if (sep != NULL) + *sep = '\0'; + + if (vmwareMakePath(fFileName, fFileName, (char *) "vmss", &fvmss) < 0) + goto cleanup; + + baseVmss = basename(fvmss); + } else { //we want to restore a vm saved elsewhere + if (virFileReadAll(path, 1024, &vmxPath) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to read vmxPath")); + goto cleanup; + } + + if (!virFileHasSuffix(vmxPath, ".vmx")) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("%s is not a .vmx file"), vmxPath); + goto cleanup; + } + + /* move files */ + if((copyvmxPath = strdup(vmxPath)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if((copypath = strdup(path)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (vmwareParsePath(copypath, &fDirectoryName, &fFileName) < 0) + goto cleanup; + + if (vmwareParsePath(copyvmxPath, &tDirectoryName, &tFileName) < 0) + goto cleanup; + + sep = strrchr(tFileName, '.'); + if (sep != NULL) + *sep = '\0'; + + if (virFileMakePath(tDirectoryName) != 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("make path error")); + goto cleanup; + } + + if (vmwareMakePath(fDirectoryName, tFileName,(char *) "vmss", &fvmss) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, tFileName, (char *) "vmss", &tvmss) < 0) + goto cleanup; + if (vmwareMakePath(fDirectoryName, tFileName, (char *) "vmem", &fvmem) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, tFileName, (char *) "vmem", &tvmem) < 0) + goto cleanup; + if (vmwareMakePath(fDirectoryName, tFileName, (char *) "vmx", &fvmx) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, tFileName, (char *) "vmx", &tvmx) < 0) + goto cleanup; + + if ((vmwareMoveFile(fvmss, tvmss) < 0) + || (vmwareMoveFile(fvmem, tvmem) < 0) + || (vmwareMoveFile(fvmx, tvmx) < 0)) { + goto cleanup; + } + + baseVmss = basename(tvmss); + } + + if (virFileReadAll(tvmx, 10000, &vmx) == -1) { + perror("error reading vmx file"); + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to read vmx file %s"), tvmx); + goto cleanup; + } + + vmwareDriverLock(driver); + if ((vmdef = + esxVMX_ParseConfig(&ctx, driver->caps, vmx, + esxVI_ProductVersion_ESX4x)) == NULL) { + goto cleanup; + } + vmwareDriverUnlock(driver); + + xml = virDomainDefFormat(vmdef, VIR_DOMAIN_XML_SECURE); + + if ((dom = vmwareDomainDefineXML(conn, xml)) == NULL) + goto cleanup; + + /* esxVMX_ParseConfig doesn't care about vmx checkpoint property for + * now, so we add it here + * TODO + */ + + if ((pFile = fopen(tvmx, "a+")) == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to reopen vmx file")); + goto cleanup; + } + + fputs("checkpoint.vmState = \"", pFile); + fputs(baseVmss, pFile); + fputs("\"", pFile); + if (VIR_FCLOSE(pFile) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to save vmx file %s"), tvmx); + goto cleanup; + } + + vmwareDriverLock(driver); + vm = virDomainFindByName(&driver->domains, dom->name); + + if (!vm) { + vmwareError(VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching id")); + goto cleanup; + } + + /* start */ + if (vmwareStartVM(driver, vm) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(vmxPath); + VIR_FREE(copypath); + VIR_FREE(copyvmxPath); + VIR_FREE(tDirectoryName); + VIR_FREE(tFileName); + VIR_FREE(fDirectoryName); + VIR_FREE(fFileName); + VIR_FREE(vmx); + VIR_FREE(xml); + + VIR_FREE(fvmss); + VIR_FREE(tvmss); + VIR_FREE(fvmem); + VIR_FREE(tvmem); + VIR_FREE(fvmx); + VIR_FREE(tvmx); + + if (dom) + virUnrefDomain(dom); + if (vm) + virDomainObjUnlock(vm); + vmwareDriverUnlock(driver); + return ret; +} + +static virDomainPtr +vmwareDomainCreateXML(virConnectPtr conn, const char *xml, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct vmware_driver *driver = conn->privateData; + virDomainDefPtr vmdef = NULL; + virDomainObjPtr vm = NULL; + virDomainPtr dom = NULL; + char *vmx = NULL; + char *vmxPath = NULL; + vmwareDomainPtr pDomain = NULL; + esxVMX_Context ctx; + + ctx.formatFileName = esxCopyVMXFileName; + + vmwareDriverLock(driver); + + if ((vmdef = virDomainDefParseString(driver->caps, xml, + VIR_DOMAIN_XML_INACTIVE)) == NULL) + goto cleanup; + + vm = virDomainFindByName(&driver->domains, vmdef->name); + if (vm) { + vmwareError(VIR_ERR_OPERATION_FAILED, + _("Already an VMWARE VM active with the id '%s'"), + vmdef->name); + goto cleanup; + } + + /* generate vmx file */ + vmx = esxVMX_FormatConfig(&ctx, driver->caps, vmdef, + esxVI_ProductVersion_ESX4x); + if (vmx == NULL) + goto cleanup; + + if (vmwareVmxPath(vmdef, &vmxPath) < 0) + goto cleanup; + + /* create vmx file */ + if (virFileWriteStr(vmxPath, vmx, S_IRUSR|S_IWUSR) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("Failed to write vmx file '%s'"), vmxPath); + goto cleanup; + } + + /* assign def */ + if (!(vm = virDomainAssignDef(driver->caps, + &driver->domains, vmdef, false))) + goto cleanup; + + pDomain = vm->privateData; + pDomain->vmxPath = strdup(vmxPath); + + vmwareDomainConfigDisplay(pDomain, vmdef); + vmdef = NULL; + + if (vmwareStartVM(driver, vm) < 0) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + +cleanup: + virDomainDefFree(vmdef); + VIR_FREE(vmx); + VIR_FREE(vmxPath); + if(vm) + virDomainObjUnlock(vm); + vmwareDriverUnlock(driver); + return dom; +} + +static int +vmwareDomainCreateWithFlags(virDomainPtr dom, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + vmwareError(VIR_ERR_NO_DOMAIN, + _("No domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + vmwareError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is already running")); + goto cleanup; + } + + ret = vmwareStartVM(driver, vm); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + vmwareDriverUnlock(driver); + return ret; +} + +static int +vmwareDomainCreate(virDomainPtr dom) +{ + return vmwareDomainCreateWithFlags(dom, 0); +} + +static int +vmwareDomainUndefine(virDomainPtr dom) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(dom->uuid, uuidstr); + vmwareError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + vmwareError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot undefine active domain")); + goto cleanup; + } + + if (!vm->persistent) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot undefine transient domain")); + goto cleanup; + } + + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + ret = 0; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + vmwareDriverUnlock(driver); + return ret; +} + +static virDomainPtr +vmwareDomainLookupByID(virConnectPtr conn, int id) +{ + struct vmware_driver *driver = conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom = NULL; + + vmwareDriverLock(driver); + vm = virDomainFindByID(&driver->domains, id); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return dom; +} + +static char * +vmwareGetOSType(virDomainPtr dom) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + char *ret = NULL; + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + + if (!(ret = strdup(vm->def->os.type))) + virReportOOMError(); + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + + +static virDomainPtr +vmwareDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + struct vmware_driver *driver = conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom = NULL; + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, uuid); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return dom; +} + +static virDomainPtr +vmwareDomainLookupByName(virConnectPtr conn, const char *name) +{ + struct vmware_driver *driver = conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom = NULL; + + vmwareDriverLock(driver); + vm = virDomainFindByName(&driver->domains, name); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return dom; +} + +static int +vmwareDomainIsActive(virDomainPtr dom) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr obj; + int ret = -1; + + vmwareDriverLock(driver); + obj = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + if (!obj) { + vmwareError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + ret = virDomainObjIsActive(obj); + + cleanup: + if (obj) + virDomainObjUnlock(obj); + return ret; +} + + +static int +vmwareDomainIsPersistent(virDomainPtr dom) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr obj; + int ret = -1; + + vmwareDriverLock(driver); + obj = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + if (!obj) { + vmwareError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + ret = obj->persistent; + + cleanup: + if (obj) + virDomainObjUnlock(obj); + return ret; +} + + +static char * +vmwareDomainDumpXML(virDomainPtr dom, int flags) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + char *ret = NULL; + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + ret = virDomainDefFormat(vm->def, flags); + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int +vmwareNumDefinedDomains(virConnectPtr conn) +{ + struct vmware_driver *driver = conn->privateData; + int n; + + vmwareDriverLock(driver); + n = virDomainObjListNumOfDomains(&driver->domains, 0); + vmwareDriverUnlock(driver); + + return n; +} + +static int +vmwareNumDomains(virConnectPtr conn) +{ + struct vmware_driver *driver = conn->privateData; + int n; + + vmwareDriverLock(driver); + n = virDomainObjListNumOfDomains(&driver->domains, 1); + vmwareDriverUnlock(driver); + + return n; +} + + +static int +vmwareListDomains(virConnectPtr conn, int *ids, int nids) +{ + struct vmware_driver *driver = conn->privateData; + int n; + + vmwareDriverLock(driver); + n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); + vmwareDriverUnlock(driver); + + return n; +} + +static int +vmwareListDefinedDomains(virConnectPtr conn, + char **const names, int nnames) +{ + struct vmware_driver *driver = conn->privateData; + int n; + + vmwareDriverLock(driver); + n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); + vmwareDriverUnlock(driver); + return n; +} + +static int +vmwareDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) +{ + struct vmware_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + vmwareDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + vmwareDriverUnlock(driver); + + if (!vm) { + vmwareError(VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + info->state = vm->state; + info->cpuTime = 0; + info->maxMem = vm->def->mem.max_balloon; + info->memory = vm->def->mem.cur_balloon; + info->nrVirtCpu = vm->def->vcpus; + ret = 0; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static virDriver vmwareDriver = { + VIR_DRV_VMWARE, + "VMWARE", + vmwareOpen, /* open */ + vmwareClose, /* close */ + NULL, /* supports_feature */ + vmwareGetType, /* type */ + vmwareGetVersion, /* version */ + NULL, /* libvirtVersion (impl. in libvirt.c) */ + NULL, /* getHostname */ + NULL, /* getMaxVcpus */ + NULL, /* nodeGetInfo */ + NULL, /* getCapabilities */ + vmwareListDomains, /* listDomains */ + vmwareNumDomains, /* numOfDomains */ + vmwareDomainCreateXML, /* domainCreateXML */ + vmwareDomainLookupByID, /* domainLookupByID */ + vmwareDomainLookupByUUID, /* domainLookupByUUID */ + vmwareDomainLookupByName, /* domainLookupByName */ + vmwareDomainSuspend, /* domainSuspend */ + vmwareDomainResume, /* domainResume */ + vmwareDomainShutdown, /* domainShutdown */ + vmwareDomainReboot, /* domainReboot */ + vmwareDomainShutdown, /* domainDestroy */ + vmwareGetOSType, /* domainGetOSType */ + NULL, /* domainGetMaxMemory */ + NULL, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + vmwareDomainGetInfo, /* domainGetInfo */ + vmwareDomainSave, /* domainSave */ + vmwareDomainRestore, /* domainRestore */ + NULL, /* domainCoreDump */ + NULL, /* domainSetVcpus */ + NULL, /* domainSetVcpusFlags */ + NULL, /* domainGetVcpusFlags */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + NULL, /* domainGetMaxVcpus */ + NULL, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ + vmwareDomainDumpXML, /* domainDumpXML */ + NULL, /* domainXmlFromNative */ + NULL, /* domainXmlToNative */ + vmwareListDefinedDomains, /* listDefinedDomains */ + vmwareNumDefinedDomains, /* numOfDefinedDomains */ + vmwareDomainCreate, /* domainCreate */ + vmwareDomainCreateWithFlags,/* domainCreateWithFlags */ + vmwareDomainDefineXML, /* domainDefineXML */ + vmwareDomainUndefine, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainAttachDeviceFlags */ + NULL, /* domainDetachDevice */ + NULL, /* domainDetachDeviceFlags */ + NULL, /* domainUpdateDeviceFlags */ + NULL, /* domainGetAutostart */ + NULL, /* domainSetAutostart */ + NULL, /* domainGetSchedulerType */ + NULL, /* domainGetSchedulerParameters */ + NULL, /* domainSetSchedulerParameters */ + NULL, /* domainMigratePrepare */ + NULL, /* domainMigratePerform */ + NULL, /* domainMigrateFinish */ + NULL, /* domainBlockStats */ + NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ + NULL, /* domainBlockPeek */ + NULL, /* domainMemoryPeek */ + NULL, /* domainGetBlockInfo */ + NULL, /* nodeGetCellsFreeMemory */ + NULL, /* getFreeMemory */ + NULL, /* domainEventRegister */ + NULL, /* domainEventDeregister */ + NULL, /* domainMigratePrepare2 */ + NULL, /* domainMigrateFinish2 */ + NULL, /* nodeDeviceDettach */ + NULL, /* nodeDeviceReAttach */ + NULL, /* nodeDeviceReset */ + NULL, /* domainMigratePrepareTunnel */ + NULL, /* IsEncrypted */ + NULL, /* IsSecure */ + vmwareDomainIsActive, /* DomainIsActive */ + vmwareDomainIsPersistent, /* DomainIsPersistent */ + NULL, /* domainIsUpdated */ + NULL, /* cpuCompare */ + NULL, /* cpuBaseline */ + NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ + NULL, /* domainMigrateSetMaxDowntime */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ + NULL, /* domainManagedSave */ + NULL, /* domainHasManagedSaveImage */ + NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ + NULL, /* qemuDomainMonitorCommand */ + NULL, /* domainSetMemoryParameters */ + NULL, /* domainGetMemoryParameters */ + NULL, /* domainOpenConsole */ +}; + +int +vmwareRegister(void) +{ + if (virRegisterDriver(&vmwareDriver) < 0) + return -1; + return 0; +} diff --git a/src/vmware/vmware_driver.h b/src/vmware/vmware_driver.h new file mode 100644 index 0000000..71186de --- /dev/null +++ b/src/vmware/vmware_driver.h @@ -0,0 +1,25 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright 2010, diateam (www.diateam.net) + * + * 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 + */ +/*---------------------------------------------------------------------------*/ + +#ifndef VMWARE_DRIVER_H +# define VMWARE_DRIVER_H + +int vmwareRegister(void); + +#endif -- 1.7.0.4

On Wed, Dec 08, 2010 at 06:55:08PM +0100, Jean-Baptiste Rouault wrote:
+virCapsPtr +vmwareCapsInit(void) +{ + struct utsname utsname; + virCapsPtr caps = NULL; + virCapsGuestPtr guest = NULL; + virCPUDefPtr cpu = NULL; + union cpuData *data = NULL; + + uname(&utsname); + + if ((caps = virCapabilitiesNew(utsname.machine, 0, 0)) == NULL) + goto error; + + if (nodeCapsInitNUMA(caps) < 0) + goto error; + + virCapabilitiesSetMacPrefix(caps, (unsigned char[]) {0x52, 0x54, 0x00}); + + /* i686 guests are always supported */ + if ((guest = virCapabilitiesAddGuest(caps, + "hvm", + "i686", + 32, + NULL, NULL, 0, NULL)) == NULL) + goto error; + + if (virCapabilitiesAddGuestDomain(guest, + "vmware", + NULL, NULL, 0, NULL) == NULL) + goto error; + + /* check if x86_64 guests are supported */ + if (VIR_ALLOC(cpu) < 0 + || !(cpu->arch = strdup(utsname.machine))) { + virReportOOMError(); + goto error; + } + + cpu->type = VIR_CPU_TYPE_HOST; + + if (!(data = cpuNodeData(cpu->arch)) + || cpuDecode(cpu, data, NULL, 0, NULL) < 0) { + goto error; + } + + if (cpuHasFeature(utsname.machine, data, "vmx") + || cpuHasFeature(utsname.machine, data, "svm")) { + + if ((guest = virCapabilitiesAddGuest(caps, + "hvm", + "x86_64", + 64, + NULL, NULL, 0, NULL)) == NULL) + goto error;
FYI, you can still get CPUs which are 32-bit only and have vmx/svm supported.
+int +vmwareLoadDomains(struct vmware_driver *driver) +{ + virDomainDefPtr vmdef = NULL; + virDomainObjPtr vm = NULL; + char *vmxPath = NULL; + char *vmx = NULL; + vmwareDomainPtr pDomain; + char *directoryName = NULL; + char *fileName = NULL; + int ret = -1; + esxVMX_Context ctx; + size_t len = 0; + char *outbuf = NULL; + char *str; + char *saveptr = NULL; + virCommandPtr cmd; + + ctx.parseFileName = esxCopyVMXFileName; + + cmd = virCommandNewArgList(VMRUN, "-T", + driver->type == TYPE_PLAYER ? "player" : "ws", + "list", NULL); + virCommandSetOutputBuffer(cmd, &outbuf); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + for(str = outbuf ; (vmxPath = strtok_r(str, "\n", &saveptr)) != NULL; + str = NULL) { + + if (vmxPath[0] != '/') + continue;
+ + //vmrun list only reports running vms + vm->state = VIR_DOMAIN_RUNNING; + vm->def->id = driver->nextvmid++; + vm->persistent = 1;
The VM ID is intended to be stable for the lifetime of a VM. It seems like this could be unstable, depending on the order in which vmrun -T returns the list. Is there any way to find a more stable ID, even if it means using the VM's UNIX PID ?
+static const char * +vmwareGetType(virConnectPtr conn) +{ + struct vmware_driver *driver = conn->privateData; + int type; + + type = driver->type; + return type == TYPE_PLAYER ? "vmware player" : "vmware workstation"; +}
This should just be returning the same string that's in the type field of the virDriverPtr struct that was registered.
+static int +vmwareStartVM(struct vmware_driver *driver, virDomainObjPtr vm) +{ + const char *cmd[] = { + VMRUN, "-T", PROGRAM_SENTINAL, "start", + PROGRAM_SENTINAL, PROGRAM_SENTINAL, NULL + }; + + if (vm->state != VIR_DOMAIN_SHUTOFF) { + vmwareError(VIR_ERR_OPERATION_DENIED, "%s",
The OPERATION_DENIED code is for authentication / access control failures. You want OPERATION_INVALID here, which is for case where the VM is in the wrong lifecycle state for the operation.
+ _("domain is not in shutoff state")); + return -1; + } + + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareSetSentinal(cmd, ((vmwareDomainPtr) vm->privateData)->vmxPath); + if (!((vmwareDomainPtr) vm->privateData)->gui) + vmwareSetSentinal(cmd, NOGUI); + else + vmwareSetSentinal(cmd, NULL); + + if (virRun(cmd, NULL) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, _("Could not exec %s"), VMRUN); + return -1; + } + + vm->def->id = driver->nextvmid++; + vm->state = VIR_DOMAIN_RUNNING; + + return 0; +} +static virDomainPtr +vmwareDomainDefineXML(virConnectPtr conn, const char *xml) +{ + struct vmware_driver *driver = conn->privateData; + virDomainDefPtr vmdef = NULL; + virDomainObjPtr vm = NULL; + virDomainPtr dom = NULL; + char *vmx = NULL; + char *directoryName = NULL; + char *fileName = NULL; + char *vmxPath = NULL; + vmwareDomainPtr pDomain = NULL; + esxVMX_Context ctx; + + ctx.formatFileName = esxCopyVMXFileName; + + vmwareDriverLock(driver); + if ((vmdef = virDomainDefParseString(driver->caps, xml, + VIR_DOMAIN_XML_INACTIVE)) == NULL) + goto cleanup; + + vm = virDomainFindByName(&driver->domains, vmdef->name); + if (vm) { + vmwareError(VIR_ERR_OPERATION_FAILED, + _("Already an VMWARE VM active with the id '%s'"), + vmdef->name); + goto cleanup; + }
When checking for duplicates you need to check UUID and Name. There is a convenient function that does this if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0) goto cleanup; Also in driver entry points, it is recommended to always use the FindByUUID method, rather than FindByName or FindByID. If any of the FindBy methods fail, you should use the VIR_ERR_NO_DOMAIN error code if this failure needs to be reported to the caller. The INVALID_DOMAIN code is for invalid virDomainPtr pointers only. Daniel

On Wednesday 08 December 2010 19:29:56 Daniel P. Berrange wrote:
FYI, you can still get CPUs which are 32-bit only and have vmx/svm supported.
Indeed, I didn't know there were 32 bits CPUs with virtualization extensions. Would it be ok to check for the "lm" CPU flag to be certain that the host CPU is a 64bit one ?
+ + //vmrun list only reports running vms + vm->state = VIR_DOMAIN_RUNNING; + vm->def->id = driver->nextvmid++; + vm->persistent = 1;
The VM ID is intended to be stable for the lifetime of a VM. It seems like this could be unstable, depending on the order in which vmrun -T returns the list. Is there any way to find a more stable ID, even if it means using the VM's UNIX PID ?
I guess I could parse the first line of the VM log (file vmware.log in the vmx directory) to get the PID.
+static const char * +vmwareGetType(virConnectPtr conn) +{ + struct vmware_driver *driver = conn->privateData; + int type; + + type = driver->type; + return type == TYPE_PLAYER ? "vmware player" : "vmware workstation"; +}
This should just be returning the same string that's in the type field of the virDriverPtr struct that was registered.
Do you mean the "name" field of the _virDriver struct ? Regards, Jean-Baptiste -- Jean-Baptiste ROUAULT Ingénieur R&D - Diateam : Architectes de l'information Phone : +33 (0)9 53 16 02 70 Fax : +33 (0)2 98 050 051

On Thu, Dec 09, 2010 at 11:05:50AM +0100, Jean-Baptiste Rouault wrote:
On Wednesday 08 December 2010 19:29:56 Daniel P. Berrange wrote:
FYI, you can still get CPUs which are 32-bit only and have vmx/svm supported.
Indeed, I didn't know there were 32 bits CPUs with virtualization extensions. Would it be ok to check for the "lm" CPU flag to be certain that the host CPU is a 64bit one ?
You really want to check what the OS is running, not what the CPU is, because you can put a 32-bit OS on a 64-bit CPU. Since VMware only copes with x86 platforms you can use STREQ(utsname.machine, "x86_64") ? 64 : 32
+ + //vmrun list only reports running vms + vm->state = VIR_DOMAIN_RUNNING; + vm->def->id = driver->nextvmid++; + vm->persistent = 1;
The VM ID is intended to be stable for the lifetime of a VM. It seems like this could be unstable, depending on the order in which vmrun -T returns the list. Is there any way to find a more stable ID, even if it means using the VM's UNIX PID ?
I guess I could parse the first line of the VM log (file vmware.log in the vmx directory) to get the PID.
+static const char * +vmwareGetType(virConnectPtr conn) +{ + struct vmware_driver *driver = conn->privateData; + int type; + + type = driver->type; + return type == TYPE_PLAYER ? "vmware player" : "vmware workstation"; +}
This should just be returning the same string that's in the type field of the virDriverPtr struct that was registered.
Do you mean the "name" field of the _virDriver struct ?
Yes. Daniel

On Thursday 09 December 2010 11:23:38 Daniel P. Berrange wrote:
On Thu, Dec 09, 2010 at 11:05:50AM +0100, Jean-Baptiste Rouault wrote:
On Wednesday 08 December 2010 19:29:56 Daniel P. Berrange wrote:
FYI, you can still get CPUs which are 32-bit only and have vmx/svm supported.
Indeed, I didn't know there were 32 bits CPUs with virtualization extensions. Would it be ok to check for the "lm" CPU flag to be certain that the host CPU is a 64bit one ?
You really want to check what the OS is running, not what the CPU is, because you can put a 32-bit OS on a 64-bit CPU. Since VMware only copes with x86 platforms you can use
STREQ(utsname.machine, "x86_64") ? 64 : 32
As far as I know, it is possible to run 64-bit guests on a 32-bit host OS if there is a 64-bit CPU with vmx/svm so I think we don't care what the OS is in that case, don't we ?
+static const char * +vmwareGetType(virConnectPtr conn) +{ + struct vmware_driver *driver = conn->privateData; + int type; + + type = driver->type; + return type == TYPE_PLAYER ? "vmware player" : "vmware workstation"; +}
This should just be returning the same string that's in the type field of the virDriverPtr struct that was registered.
Do you mean the "name" field of the _virDriver struct ?
Yes.
Ok, I thought the connectGetType() API was there to give more information than the name field if relevant because virConnectGetType() returns driver->name when the type function isn't available. Jean-Baptiste -- Jean-Baptiste ROUAULT Ingénieur R&D - Diateam : Architectes de l'information Phone : +33 (0)9 53 16 02 70 Fax : +33 (0)2 98 050 051

2010/12/8 Jean-Baptiste Rouault <jean-baptiste.rouault@diateam.net>:
--- cfg.mk | 1 + configure.ac | 7 + include/libvirt/virterror.h | 1 + po/POTFILES.in | 2 + src/Makefile.am | 24 +- src/driver.h | 3 +- src/libvirt.c | 15 + src/util/virterror.c | 3 + src/vmware/vmware_conf.c | 453 +++++++++++++++ src/vmware/vmware_conf.h | 82 +++ src/vmware/vmware_driver.c | 1351 +++++++++++++++++++++++++++++++++++++++++++ src/vmware/vmware_driver.h | 25 + 12 files changed, 1963 insertions(+), 4 deletions(-) create mode 100644 src/vmware/vmware_conf.c create mode 100644 src/vmware/vmware_conf.h create mode 100644 src/vmware/vmware_driver.c create mode 100644 src/vmware/vmware_driver.h
diff --git a/src/vmware/vmware_conf.c b/src/vmware/vmware_conf.c new file mode 100644 index 0000000..2255b52 --- /dev/null +++ b/src/vmware/vmware_conf.c
+#include <config.h> + +#include <string.h> +#include <sys/utsname.h> + +#include "command.h" +#include "cpu/cpu.h" +#include "memory.h" +#include "nodeinfo.h" +#include "util/files.h" +#include "uuid.h" +#include "virterror_internal.h" +#include "../esx/esx_vmx.h" + +#include "vmware_conf.h" + +#define VMWARE_MAX_ARG 20
This define is unused.
+int +vmwareLoadDomains(struct vmware_driver *driver) +{
+ ctx.parseFileName = esxCopyVMXFileName; + + cmd = virCommandNewArgList(VMRUN, "-T", + driver->type == TYPE_PLAYER ? "player" : "ws", + "list", NULL); + virCommandSetOutputBuffer(cmd, &outbuf); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + for(str = outbuf ; (vmxPath = strtok_r(str, "\n", &saveptr)) != NULL; + str = NULL) { + + if (vmxPath[0] != '/') + continue; + + /* remove trailing newline */ + len = strlen(vmxPath); + if (len && vmxPath[len-1] == '\n') + vmxPath[len-1] = '\0'; + + if (virFileReadAll(vmxPath, 10000, &vmx) == -1) { + perror("error reading vmx file"); + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to read vmx file %s"), vmxPath);
We don't use perror in libvirt and virFileReadAll already reports and error when it fails. So just remove the perror and vmwareError calls. Yes, I know it's hard to tell which functions already report errors and which don't :)
+ goto cleanup; + }
diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c new file mode 100644 index 0000000..5d8a8d5 --- /dev/null +++ b/src/vmware/vmware_driver.c
+static int +vmwareDomainRestore(virConnectPtr conn, const char *path) +{
+ ctx.parseFileName = esxCopyVMXFileName; + + if (!virFileExists(path)) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("file %s does not exist"), path); + goto cleanup; + } + if (virFileHasSuffix(path, ".vmx")) { //we want to restore a vm saved in its default directory + if((tvmx = strdup(path)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if((copypath = strdup(path)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (vmwareParsePath(copypath, &fDirectoryName, &fFileName) < 0) + goto cleanup; + + sep = strrchr(fFileName, '.'); + if (sep != NULL) + *sep = '\0'; + + if (vmwareMakePath(fFileName, fFileName, (char *) "vmss", &fvmss) < 0) + goto cleanup; + + baseVmss = basename(fvmss); + } else { //we want to restore a vm saved elsewhere + if (virFileReadAll(path, 1024, &vmxPath) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to read vmxPath"));
virFileReadAll already reports an error.
+ goto cleanup; + } + + if (!virFileHasSuffix(vmxPath, ".vmx")) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("%s is not a .vmx file"), vmxPath); + goto cleanup; + } + + /* move files */ + if((copyvmxPath = strdup(vmxPath)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if((copypath = strdup(path)) == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (vmwareParsePath(copypath, &fDirectoryName, &fFileName) < 0) + goto cleanup; + + if (vmwareParsePath(copyvmxPath, &tDirectoryName, &tFileName) < 0) + goto cleanup; + + sep = strrchr(tFileName, '.'); + if (sep != NULL) + *sep = '\0'; + + if (virFileMakePath(tDirectoryName) != 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("make path error")); + goto cleanup; + } + + if (vmwareMakePath(fDirectoryName, tFileName,(char *) "vmss", &fvmss) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, tFileName, (char *) "vmss", &tvmss) < 0) + goto cleanup; + if (vmwareMakePath(fDirectoryName, tFileName, (char *) "vmem", &fvmem) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, tFileName, (char *) "vmem", &tvmem) < 0) + goto cleanup; + if (vmwareMakePath(fDirectoryName, tFileName, (char *) "vmx", &fvmx) < 0) + goto cleanup; + if (vmwareMakePath(tDirectoryName, tFileName, (char *) "vmx", &tvmx) < 0) + goto cleanup; + + if ((vmwareMoveFile(fvmss, tvmss) < 0) + || (vmwareMoveFile(fvmem, tvmem) < 0) + || (vmwareMoveFile(fvmx, tvmx) < 0)) { + goto cleanup; + } + + baseVmss = basename(tvmss); + } + + if (virFileReadAll(tvmx, 10000, &vmx) == -1) { + perror("error reading vmx file"); + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to read vmx file %s"), tvmx);
virFileReadAll already reports an error. Matthias
participants (3)
-
Daniel P. Berrange
-
Jean-Baptiste Rouault
-
Matthias Bolte