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

* Changes since v4: Fix a bug and a memory leak in vmwareParsePath() Remove domainSave/Restore functions because more work is needed on them

--- 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 | 13 + src/util/virterror.c | 3 + src/vmware/vmware_conf.c | 500 +++++++++++++++++++++ src/vmware/vmware_conf.h | 83 ++++ src/vmware/vmware_driver.c | 1013 +++++++++++++++++++++++++++++++++++++++++++ src/vmware/vmware_driver.h | 25 + 12 files changed, 1671 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 bda8c57..03186b3 100644 --- a/cfg.mk +++ b/cfg.mk @@ -388,6 +388,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 64e76dc..5ec524d 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 @@ -2277,6 +2283,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 196d8af..8bab5e2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -219,8 +219,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 \ @@ -256,6 +254,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 \ @@ -601,6 +603,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 @@ -953,7 +970,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 ff2ac93..ee2495a 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,10 @@ virGetVersion(unsigned long *libVer, const char *type, if (STRCASEEQ(type, "OpenVZ")) *typeVer = LIBVIR_VERSION_NUMBER; # endif +# if WITH_VMWARE + if (STRCASEEQ(type, "VMware")) + *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 b912737..96a09cc 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..aea61b1 --- /dev/null +++ b/src/vmware/vmware_conf.c @@ -0,0 +1,500 @@ +/*---------------------------------------------------------------------------*/ +/* 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 "dirname.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" + +/* 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; + + 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; + } + + /* x86_64 guests are supported if + * - Host arch is x86_64 + * Or + * - Host CPU is x86_64 with virtualization extensions + */ + if (STREQ(utsname.machine, "x86_64") || + (cpuHasFeature(utsname.machine, data, "lm") && + (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; + 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; + + if (virFileReadAll(vmxPath, 10000, &vmx) < 0) + 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); + + if ((vm->def->id = vmwareExtractPid(vmxPath)) < 0) + goto cleanup; + //vmrun list only reports running vms + vm->state = VIR_DOMAIN_RUNNING; + 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) { + VIR_FREE(*directory); + goto no_memory; + } + + } else { + if ((*filename = strdup(path)) == 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; +} + +int +vmwareExtractPid(const char * vmxPath) +{ + char *vmxDir = NULL; + char *logFilePath = NULL; + FILE *logFile = NULL; + char line[1024]; + char *tmp = NULL; + int pid = -1; + + if ((vmxDir = mdir_name(vmxPath)) == NULL) + goto cleanup; + + if (virAsprintf(&logFilePath, "%s/vmware.log", + vmxDir) < 0) { + virReportOOMError(); + goto cleanup; + } + + if ((logFile = fopen(logFilePath, "r")) == NULL) + goto cleanup; + + if (!fgets(line, sizeof(line), logFile)) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to read vmware log file")); + goto cleanup; + } + + if ((tmp = strstr(line, " pid=")) == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find pid in vmware log file")); + goto cleanup; + } + + tmp += strlen(" pid="); + + if (virStrToLong_i(tmp, &tmp, 10, &pid) < 0 || *tmp != ' ') { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot parse pid in vmware log file")); + goto cleanup; + } + +cleanup: + VIR_FREE(vmxDir); + VIR_FREE(logFilePath); + VIR_FORCE_FCLOSE(logFile); + return pid; +} + +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..b1cdf8f --- /dev/null +++ b/src/vmware/vmware_conf.h @@ -0,0 +1,83 @@ +/*---------------------------------------------------------------------------*/ +/* 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; +}; + +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); + +int vmwareExtractPid(const char * vmxPath); + +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..9662f46 --- /dev/null +++ b/src/vmware/vmware_driver.c @@ -0,0 +1,1013 @@ +/*---------------------------------------------------------------------------*/ +/* 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; + + 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 ATTRIBUTE_UNUSED) +{ + return "VMware"; +} + +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 +vmwareStopVM(struct vmware_driver *driver, virDomainObjPtr vm) +{ + const char *cmd[] = { + VMRUN, "-T", PROGRAM_SENTINAL, "stop", + PROGRAM_SENTINAL, "soft", NULL + }; + + 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 int +vmwareStartVM(struct vmware_driver *driver, virDomainObjPtr vm) +{ + const char *cmd[] = { + VMRUN, "-T", PROGRAM_SENTINAL, "start", + PROGRAM_SENTINAL, PROGRAM_SENTINAL, NULL + }; + const char *vmxPath = ((vmwareDomainPtr) vm->privateData)->vmxPath; + + if (vm->state != VIR_DOMAIN_SHUTOFF) { + vmwareError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain is not in shutoff state")); + return -1; + } + + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareSetSentinal(cmd, 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; + } + + if ((vm->def->id = vmwareExtractPid(vmxPath)) < 0) { + vmwareStopVM(driver, vm); + return -1; + } + + 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; + + if (virDomainObjIsDuplicate(&driver->domains, vmdef, 1) < 0) + 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_NO_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + if (vm->state != VIR_DOMAIN_RUNNING) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("domain is not in running state")); + 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_NO_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_NO_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; + const char * vmxPath = NULL; + + 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); + vmxPath = ((vmwareDomainPtr) vm->privateData)->vmxPath; + + if (!vm) { + vmwareError(VIR_ERR_NO_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; + + ret = 0; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + 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; + + if (virDomainObjIsDuplicate(&driver->domains, vmdef, 1) < 0) + 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_NO_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_NO_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 */ + NULL, /* domainSave */ + NULL, /* 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

2010/12/17 Jean-Baptiste Rouault <jean-baptiste.rouault@diateam.net>:
---
+int +vmwareLoadDomains(struct vmware_driver *driver) +{
+ vmwareDomainConfigDisplay(pDomain, vmdef); + + if ((vm->def->id = vmwareExtractPid(vmxPath)) < 0) + goto cleanup; + //vmrun list only reports running vms + vm->state = VIR_DOMAIN_RUNNING; + vm->persistent = 1;
We prefer C over C++ style comments, so I'll change this comment to /* */ style before pushing. ACK to v5, finally :) I've pushed this now. Now I'll move the VMX handling code from the ESX driver to the general util directory to resolve this inter-driver dependency. I'd suggest that you provide a patch to update the libvirt website and include some info about the new driver. Matthias

--- docs/drivers.html.in | 1 + docs/drvvmware.html.in | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ docs/index.html.in | 3 ++ 3 files changed, 71 insertions(+), 0 deletions(-) create mode 100644 docs/drvvmware.html.in diff --git a/docs/drivers.html.in b/docs/drivers.html.in index b97200c..ecad03a 100644 --- a/docs/drivers.html.in +++ b/docs/drivers.html.in @@ -27,6 +27,7 @@ <li><strong><a href="drvuml.html">UML</a></strong> - User Mode Linux</li> <li><strong><a href="drvvbox.html">VirtualBox</a></strong></li> <li><strong><a href="drvesx.html">VMware ESX</a></strong></li> + <li><strong><a href="drvvmware.html">VMware Workstation/Player</a></strong></li> <li><strong><a href="drvxen.html">Xen</a></strong></li> </ul> diff --git a/docs/drvvmware.html.in b/docs/drvvmware.html.in new file mode 100644 index 0000000..273e0a0 --- /dev/null +++ b/docs/drvvmware.html.in @@ -0,0 +1,67 @@ +<html> + <body> + <h1>VMware Workstation/Player hypervisors driver</h1> + <p> + The libvirt VMware Workstation driver should be able to manage any Workstation and + Player version supported by the VMware VIX API. See the compatibility list + <a href="http://www.vmware.com/support/developer/vix-api/vix110_reference/">here</a>. + </p> + <p> + This driver uses the "vmrun" utility which is distributed with the VMware VIX API. + You can download the VIX API from <a href="http://www.vmware.com/support/developer/vix-api/">here</a>. + + <h2>Connections to VMware driver</h2> + + <p> + The libvirt VMware driver provides per-user drivers (the "session" instance). + Two uris are available: + <ul> + <li>"vmwareplayer" for VMware Player</li> + <li>"vmwarews" for VMware Workstation</li> + </ul> + Some example connection URIs for the driver are: + </p> + +<pre> +vmwareplayer:///session (local access to VMware Player per-user instance) +vmwarews:///session (local access to VMware Workstation per-user instance) +vmwarews+tcp://user@example.com/session (remote access to VMware Workstation, SASl/Kerberos) +vmwarews+ssh://user@example.com/session (remote access to VMware Workstation, SSH tunnelled) +</pre> + + <h2><a name="xmlconfig">Example domain XML config</a></h2> + +<pre> +<domain type='vmware'> + <name>vmware</name> + <uuid>bea92244-8885-4562-828b-3b086731c5b1</uuid> + + <os> + <type>hvm</type> + </os> + + <memory>524288</memory> + <vcpu>1</vcpu> + + <features> + <pae/> + <acpi/> + </features> + + <devices> + <disk type='file' device='disk'> + <source file='/home/user/tmp/disk.vmdk'/> + <target bus='ide' dev='hda'/> + </disk> + + <interface type='bridge'> + <target dev='/dev/vmnet1'/> + <source bridge=''/> + <mac address='00:16:3e:5d:c7:9e'/> + </interface> + </devices> +</domain> +</pre> + + </body> +</html> diff --git a/docs/index.html.in b/docs/index.html.in index 57ea84e..501f3e3 100644 --- a/docs/index.html.in +++ b/docs/index.html.in @@ -59,6 +59,9 @@ The <a href="http://www.vmware.com/">VMware ESX and GSX</a> hypervisors </li> <li> + The <a href="http://www.vmware.com/">VMware Workstation and Player</a> hypervisors + </li> + <li> Storage on IDE/SCSI/USB disks, FibreChannel, LVM, iSCSI, NFS and filesystems </li> </ul> -- 1.7.0.4

On 12/23/2010 01:35 AM, Jean-Baptiste Rouault wrote:
--- docs/drivers.html.in | 1 + docs/drvvmware.html.in | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ docs/index.html.in | 3 ++ 3 files changed, 71 insertions(+), 0 deletions(-) create mode 100644 docs/drvvmware.html.in
ACK, and pushed. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 12/17/2010 03:28 AM, Jean-Baptiste Rouault wrote:
+++ 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])
Would you mind preparing a followup patch that modifies libvirt.spec.in to make it configurable when building an rpm whether vmware support is built in? See commit e3e31303d54e for an example. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

* libvirt.spec.in: Provide vmware conditionals. ---
Would you mind preparing a followup patch that modifies libvirt.spec.in to make it configurable when building an rpm whether vmware support is built in? See commit e3e31303d54e for an example.
I went ahead and did this, since it was breaking the 'make rpm' phase of ./autobuild.sh. However, I'd like a review before I push. libvirt.spec.in | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-) diff --git a/libvirt.spec.in b/libvirt.spec.in index ddb50cd..7c60eba 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -43,6 +43,7 @@ # Then the hypervisor drivers that talk a native remote protocol %define with_phyp 0%{!?_without_phyp:1} %define with_esx 0%{!?_without_esx:1} +%define with_vmware 0%{!?_without_vmware:1} # Then the secondary host drivers %define with_network 0%{!?_without_network:%{server_drivers}} @@ -86,7 +87,7 @@ %endif # RHEL doesn't ship OpenVZ, VBox, UML, OpenNebula, PowerHypervisor, ESX, -# or libxenserver (xenapi) +# VMWare, or libxenserver (xenapi) %if 0%{?rhel} %define with_openvz 0 %define with_vbox 0 @@ -94,6 +95,7 @@ %define with_one 0 %define with_phyp 0 %define with_esx 0 +%define with_vmware 0 %define with_xenapi 0 %endif @@ -478,6 +480,10 @@ of recent versions of Linux (and other OSes). %define _without_esx --without-esx %endif +%if ! %{with_vmware} +%define _without_vmware --without-vmware +%endif + %if ! %{with_polkit} %define _without_polkit --without-polkit %endif @@ -585,6 +591,7 @@ of recent versions of Linux (and other OSes). %{?_without_one} \ %{?_without_phyp} \ %{?_without_esx} \ + %{?_without_vmware} \ %{?_without_network} \ %{?_with_rhel5_api} \ %{?_without_storage_fs} \ -- 1.7.3.3

* libvirt.spec.in: Provide vmware conditionals. ---
Would you mind preparing a followup patch that modifies libvirt.spec.in to make it configurable when building an rpm whether vmware support is built in? See commit e3e31303d54e for an example.
I went ahead and did this, since it was breaking the 'make rpm' phase of ./autobuild.sh. However, I'd like a review before I push.
libvirt.spec.in | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-)
Thanks for doing that. ACK. Jirka

On 12/21/2010 02:59 AM, Jiri Denemark wrote:
* libvirt.spec.in: Provide vmware conditionals. ---
Would you mind preparing a followup patch that modifies libvirt.spec.in to make it configurable when building an rpm whether vmware support is built in? See commit e3e31303d54e for an example.
I went ahead and did this, since it was breaking the 'make rpm' phase of ./autobuild.sh. However, I'd like a review before I push.
libvirt.spec.in | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-)
Thanks for doing that. ACK.
Pushed. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

* autobuild.sh: Alter mingw configuration setup. --- And a followup to allow autobuild.sh completion when a mingw cross-toolchain is present. autobuild.sh | 2 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/autobuild.sh b/autobuild.sh index 91e2ab2..4cb9fde 100755 --- a/autobuild.sh +++ b/autobuild.sh @@ -83,6 +83,8 @@ if [ -x /usr/bin/i686-pc-mingw32-gcc ]; then --without-openvz \ --without-one \ --without-phyp \ + --without-xenapi \ + --without-vmware \ + --without-esx \ --without-netcf \ --without-audit \ --without-dtrace \ -- 1.7.3.3

* autobuild.sh: Alter mingw configuration setup. ---
And a followup to allow autobuild.sh completion when a mingw cross-toolchain is present.
autobuild.sh | 2 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/autobuild.sh b/autobuild.sh index 91e2ab2..4cb9fde 100755 --- a/autobuild.sh +++ b/autobuild.sh @@ -83,6 +83,8 @@ if [ -x /usr/bin/i686-pc-mingw32-gcc ]; then --without-openvz \ --without-one \ --without-phyp \ + --without-xenapi \ + --without-vmware \ + --without-esx \ --without-netcf \ --without-audit \ --without-dtrace \
ACK Jirka

On 12/21/2010 03:11 AM, Jiri Denemark wrote:
* autobuild.sh: Alter mingw configuration setup. ---
And a followup to allow autobuild.sh completion when a mingw cross-toolchain is present.
autobuild.sh | 2 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/autobuild.sh b/autobuild.sh index 91e2ab2..4cb9fde 100755 --- a/autobuild.sh +++ b/autobuild.sh @@ -83,6 +83,8 @@ if [ -x /usr/bin/i686-pc-mingw32-gcc ]; then --without-openvz \ --without-one \ --without-phyp \ + --without-xenapi \ + --without-vmware \ + --without-esx \ --without-netcf \ --without-audit \ --without-dtrace \
ACK
Hmm, ./autobuild.sh is still failing for me, because it still tries to make the mingw rpm, which did not have these changes. The failure stems from the fact that configure tries to build vmware but not esx if curl/curl.h is missing, but vmware indirectly requires both esx and curl/curl.h at the moment: CC libvirt_driver_vmware_la-vmware_driver.lo In file included from vmware/../esx/esx_vmx.h:31:0, from vmware/vmware_driver.c:28: vmware/../esx/esx_vi.h:28:24: fatal error: curl/curl.h: No such file or directory compilation terminated. I don't think I need to disable esx (and I certainly don't want to disable it from the mingw specfile), so I'll try again with a v2, perhaps waiting until after Matthias' promised patch to clean up the current esx/vmware problems. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (4)
-
Eric Blake
-
Jean-Baptiste Rouault
-
Jiri Denemark
-
Matthias Bolte