2010/12/2 Jean-Baptiste Rouault <jean-baptiste.rouault(a)diateam.net>:
---
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/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;
virMutexDestroy(&driver->lock) is missing here.
+ virDomainObjListDeinit(&driver->domains);
+ virCapabilitiesFree(driver->caps);
+ VIR_FREE(driver);
+}
+
+
+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) {
Use fgets(vmxPath, sizeof(vmxPath), fp) here instead.
+ 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);
strlen returns size_t, not int. Also move size_t len to the block of
other variables at the beginning of the function.
+ 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;
Just goto cleanup here, as you already reported the OOM error. Now you
can remove the no_memory label and avoid the jump backwards to the
cleanup label.
+ }
+
+ 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;
+}
+
+
+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
+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
+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;
+}
+
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
+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);
Should be _() instead of (). Add vmwareError to the list of
msg_gen_function's in cfg.mk to let "make syntax-check" verify this.
Also you might feed a NULL string for %s, that's not save. Use
NULLSTR(conn->uri->path) here in the vmwareError call, or reorder the
testing logic.
+ 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 */
You should probably check early if vmrun is available at all, For
example like this.
char *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;
+ }
+static const char *
+vmwareGetType(virConnectPtr conn ATTRIBUTE_UNUSED)
conn is not unused here, so you don't need to mark it as such.
+{
+ struct vmware_driver *driver = conn->privateData;
+ int type;
+
+ vmwareDriverLock(driver);
+ type = driver->type;
+ vmwareDriverUnlock(driver);
+ return type == TYPE_PLAYER ? "vmware player" : "vmware
workstation";
+}
+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"));
Don't report your own error here, esxVMX_FormatConfig did this already
when it returns NULL. Your error message will overwrite the more
detailed error message reported by esxVMX_FormatConfig.
+ 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) {
This will need the 3. argument for the extended virFileWriteStr function.
+ vmwareError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to write vmx file '%s'"), vmxPath);
+ goto cleanup;
+ }
+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]);
All access to the driver struct is made with the driver lock held,
expect this one. Actually accessing driver->type unlocked should be
okay, as driver->type is set in the open function and then only read.
In some functions you take the lock to access driver->type in some you
don't. Choose one way and then be consistent.
+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
+ };
virDomainReboot is meant to reboot the guest OS as in executing the
reboot command from inside the guest OS. It's not meant to do a
hardware level reset as in pressing the reset button of an actual
computer.
I'm not sure "vmrun reset" is the right thing here.
+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"));
Don't report and error here, vmwareParsePath does this already.
+ goto cleanup;
+ }
+ if (vmwareParsePath(copyPath, &tDirectoryName, &tFileName) < 0) {
+ vmwareError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to parse path"));
Don't report and error here, vmwareParsePath does this already.
+ 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);
The the return values of all this vmwareMakePath calls.
+ /* 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"));
Don't report and error here, vmwareMoveFile does this already.
+ 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"));
Don't report and error here, vmwareParsePath does this already.
+ goto cleanup;
+ }
+ sep = strrchr(fFileName, '.');
+ if (sep != NULL)
+ *sep = '\0';
+ vmwareMakePath(fFileName, fFileName, (char *) "vmss", &fvmss);
You should check the return value of vmwareMakePath
+ 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"));
Don't report and error here, vmwareMoveFile does this already.
+ goto cleanup;
+ }
+ if (vmwareParsePath(copyvmxPath, &tDirectoryName, &tFileName) < 0) {
+ vmwareError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to parse vmxPath"));
Don't report and error here, vmwareMoveFile does this already.
+ 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"));
Don't report and error here, vmwareMoveFile does this already.
+ 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"));
Don't report and error here, esxVMX_ParseConfig does this already.
+ 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"));
Don't report an error here, vmwareDomainDefineXML does this already.
+ 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"));
Don't report an error here, vmwareStartVM does this already.
> + 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
+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"));
s/delete/undefine/
At the end of my review I noticed that you overwrite already reported
errors in many places. I only commented on some of them so you should
check for this in the whole driver.
I think the next version of this driver will be ready to be added to
the official codebase :)
Matthias