--- 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 | 480 +++++++++++++++ src/vmware/vmware_conf.h | 82 +++ src/vmware/vmware_driver.c | 1372 +++++++++++++++++++++++++++++++++++++++++++ src/vmware/vmware_driver.h | 25 + 11 files changed, 2010 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/configure.ac b/configure.ac index d26bc68..f96a326 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 2820ac1..71c7608 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -99,6 +99,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 a9a1986..78eaa0b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -217,8 +217,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 \ @@ -254,6 +252,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 \ @@ -599,6 +601,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 @@ -951,7 +968,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 b770e5e..a03ea25 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 b4951c2..8243c5e 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..5e873e1 --- /dev/null +++ b/src/vmware/vmware_conf.c @@ -0,0 +1,480 @@ +/*---------------------------------------------------------------------------*/ +/* 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 <sys/utsname.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; + + 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) +{ + FILE *fp; + virDomainDefPtr vmdef = NULL; + virDomainObjPtr vm = NULL; + char vmxPath[1024]; + char * vmx = NULL; + vmwareDomainPtr pDomain; + char *directoryName = NULL; + char *fileName = NULL; + int ret = -1; + esxVMX_Context ctx; + + ctx.parseFileName = esxCopyVMXFileName; + + fp = driver->type == + TYPE_PLAYER ? popen(VMRUN " -T " "player" " list 2>/dev/null", + "r") : popen(VMRUN " -T " "ws" + " list 2>/dev/null", "r"); + if (fp == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", _("popen failed")); + return -1; + } + + while (!feof(fp)) { + if (fgets(vmxPath, 1024, fp) == NULL) { + if (feof(fp)) + break; + + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to parse vmrun list output")); + goto cleanup; + } + if (vmxPath[0] != '/') + continue; + + /* remove trailing newline */ + int 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) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse config file")); + 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 no_memory; + } + + 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: + virDomainDefFree(vmdef); + VIR_FORCE_FCLOSE(fp); + VIR_FREE(directoryName); + VIR_FREE(fileName); + VIR_FREE(vmx); + if (vm) + virDomainObjUnref(vm); + return ret; + +no_memory: + virReportOOMError(); + goto cleanup; +} + +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; + FILE *fp = NULL; + char str[50]; + char *tmp; + int ret = -1; + + if (driver->type == TYPE_PLAYER) { + if ((fp = popen("vmplayer -v 2>/dev/null", "r")) == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", _("popen failed")); + goto cleanup; + } + if (!feof(fp) && fgets(str, 49, fp)) { + if ((tmp = STRSKIP(str, "VMware Player ")) == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("vmplayer -v parsing error")); + goto cleanup; + } + if (virParseVersionString(tmp, &version) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("version parsing error")); + goto cleanup; + } + driver->version = version; + ret = 0; + } else { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("vmplayer -v reading error")); + } + } else if (driver->type == TYPE_WORKSTATION) { + if ((fp = popen("vmware -v 2>/dev/null", "r")) == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", _("popen failed")); + goto cleanup; + } + if (!feof(fp) && fgets(str, 49, fp)) { + if ((tmp = STRSKIP(str, "VMware Workstation ")) == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("vmware -v parsing error")); + goto cleanup; + } + if (virParseVersionString(tmp, &version) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("version parsing error")); + goto cleanup; + } + driver->version = version; + ret = 0; + } else { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("vmware -v reading error")); + } + } + +cleanup: + VIR_FORCE_FCLOSE(fp); + 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..5615bef --- /dev/null +++ b/src/vmware/vmware_driver.c @@ -0,0 +1,1372 @@ +/*---------------------------------------------------------------------------*/ +/* 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 "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; + + 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"), + 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 */ + + 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 ATTRIBUTE_UNUSED) +{ + struct vmware_driver *driver = conn->privateData; + int type; + + vmwareDriverLock(driver); + type = driver->type; + vmwareDriverUnlock(driver); + 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) { + vmwareError(VIR_ERR_OPERATION_FAILED, _("cannot create xml")); + goto cleanup; + } + + if (vmwareVmxPath(vmdef, &vmxPath) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("retrieve vmx path.. failed")); + goto cleanup; + } + + /* create vmx file */ + if (virFileWriteStr(vmxPath, vmx) < 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; + } + + vmwareDriverLock(driver); + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareDriverUnlock(driver); + 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, 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; + } + + vmwareDriverLock(driver); + vmwareSetSentinal(cmd, vmw_types[driver->type]); + vmwareDriverUnlock(driver); + 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) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse vmxPath")); + goto cleanup; + } + if (vmwareParsePath(copyPath, &tDirectoryName, &tFileName) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse path")); + goto cleanup; + } + + /* dir */ + if (virFileMakePath(tDirectoryName) != 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", _("make path error")); + goto cleanup; + } + + vmwareMakePath(fDirectoryName, vm->def->name, (char *) "vmss", &fvmss); + vmwareMakePath(tDirectoryName, vm->def->name, (char *) "vmss", &tvmss); + vmwareMakePath(fDirectoryName, vm->def->name, (char *) "vmem", &fvmem); + vmwareMakePath(tDirectoryName, vm->def->name, (char *) "vmem", &tvmem); + vmwareMakePath(fDirectoryName, vm->def->name, (char *) "vmx", &fvmx); + vmwareMakePath(tDirectoryName, vm->def->name, (char *) "vmx", &tvmx); + + /* create linker file */ + int fd; + + if ((fd = + open(path, O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR)) == -1) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("Failed to open linker file '%s'"), path); + goto cleanup; + } + if (safewrite + (fd, ((vmwareDomainPtr) vm->privateData)->vmxPath, + strlen(((vmwareDomainPtr) vm->privateData)->vmxPath)) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create linker file")); + 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) + ) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to move file")); + 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; + 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) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse path")); + goto cleanup; + } + sep = strrchr(fFileName, '.'); + if (sep != NULL) + *sep = '\0'; + vmwareMakePath(fFileName, fFileName, (char *) "vmss", &fvmss); + 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) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse path")); + goto cleanup; + } + if (vmwareParsePath(copyvmxPath, &tDirectoryName, &tFileName) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse vmxPath")); + 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; + } + + vmwareMakePath(fDirectoryName, tFileName, (char *) "vmss", &fvmss); + vmwareMakePath(tDirectoryName, tFileName, (char *) "vmss", &tvmss); + vmwareMakePath(fDirectoryName, tFileName, (char *) "vmem", &fvmem); + vmwareMakePath(tDirectoryName, tFileName, (char *) "vmem", &tvmem); + vmwareMakePath(fDirectoryName, tFileName, (char *) "vmx", &fvmx); + vmwareMakePath(tDirectoryName, tFileName, (char *) "vmx", &tvmx); + + if ((vmwareMoveFile(fvmss, tvmss) < 0) + || (vmwareMoveFile(fvmem, tvmem) < 0) + || (vmwareMoveFile(fvmx, tvmx) < 0) + ) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to move files")); + 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) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse config file")); + goto cleanup; + } + vmwareDriverUnlock(driver); + + xml = virDomainDefFormat(vmdef, VIR_DOMAIN_XML_SECURE); + + if ((dom = vmwareDomainDefineXML(conn, xml)) == NULL) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to define domain")); + goto cleanup; + } + + /* esxVMX_ParseConfig don't care about vmx checkpoint property for now, so we add it here + * TODO + */ + FILE *pFile = NULL; + + 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); + fclose(pFile); + + 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) { + vmwareError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create domain")); + 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) { + vmwareError(VIR_ERR_OPERATION_FAILED, _("cannot create xml")); + goto cleanup; + } + + if (vmwareVmxPath(vmdef, &vmxPath) < 0) { + vmwareError(VIR_ERR_INTERNAL_ERROR, + _("retrieve vmx path.. failed")); + goto cleanup; + } + + /* create vmx file */ + if (virFileWriteStr(vmxPath, vmx) < 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 delete 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list