esx: Improve VMX file name parsing and formatting

For parsing try to match by datastore mount path first, if that
fails fallback to /vmfs/volumes/<datastore>/<path> parsing. This
also fixes problems with GSX on Windows. Because GSX on Windows
doesn't use /vmfs/volumes/ style file names.

For formatting use the datastore mount path too, instead of using
/vmfs/volumes/<datastore>/<path> as fixed format.
This commit is contained in:
Matthias Bolte 2010-08-07 19:58:29 +02:00
parent 6f42d3253a
commit 145d6cb05c

View File

@ -24,8 +24,6 @@
#include <config.h> #include <config.h>
#include <netdb.h>
#include "internal.h" #include "internal.h"
#include "domain_conf.h" #include "domain_conf.h"
#include "authhelper.h" #include "authhelper.h"
@ -60,177 +58,268 @@ struct _esxVMX_Data {
/*
* Parse a file name from a .vmx file and convert it to datastore path format.
* A .vmx file can contain file names in various formats:
*
* - A single name referencing a file in the same directory as the .vmx file:
*
* test1.vmdk
*
* - An absolute file name referencing a file in a datastore that is mounted at
* /vmfs/volumes/<datastore>:
*
* /vmfs/volumes/b24b7a78-9d82b4f5/test1/test1.vmdk
* /vmfs/volumes/datastore1/test1/test1.vmdk
*
* The actual mount directory is /vmfs/volumes/b24b7a78-9d82b4f5, the second
* form is a symlink to it using the datastore name. This is the typical
* setup on an ESX(i) server.
*
* - With GSX installed on Windows there are also Windows style file names
* including UNC file names:
*
* C:\Virtual Machines\test1\test1.vmdk
* \\nas1\storage1\test1\test1.vmdk
*
* The datastore path format typically looks like this:
*
* [datastore1] test1/test1.vmdk
*
* Firstly this functions checks if the given file name contains a separator.
* If it doesn't then the referenced file is in the same directory as the .vmx
* file. The datastore name and directory of the .vmx file are passed to this
* function via the opaque paramater by the caller of esxVMX_ParseConfig.
*
* Otherwise query for all known datastores and their mount directories. Then
* try to find a datastore with a mount directory that is a prefix to the given
* file name. This mechanism covers the Windows style file names too.
*
* The symlinks using the datastore name (/vmfs/volumes/datastore1) are an
* exception and need special handling. Parse the datastore name and use it
* to lookup the datastore by name to verify that it exists.
*/
static char * static char *
esxAbsolutePathToDatastorePath(esxVI_Context *ctx, const char *absolutePath) esxParseVMXFileName(const char *fileName, void *opaque)
{ {
bool success = false;
char *copyOfAbsolutePath = NULL;
char *tmp = NULL;
char *saveptr = NULL;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *datastore = NULL;
char *datastorePath = NULL; char *datastorePath = NULL;
char *preliminaryDatastoreName = NULL; esxVMX_Data *data = opaque;
char *directoryAndFileName = NULL; esxVI_String *propertyNameList = NULL;
char *datastoreName = NULL; esxVI_ObjectContent *datastoreList = NULL;
esxVI_ObjectContent *datastore = NULL;
esxVI_DatastoreHostMount *hostMount = NULL;
char *datastoreName;
char *tmp;
char *saveptr;
char *strippedFileName = NULL;
char *copyOfFileName = NULL;
char *directoryAndFileName;
if (esxVI_String_DeepCopyValue(&copyOfAbsolutePath, absolutePath) < 0) { if (strchr(fileName, '/') == NULL && strchr(fileName, '\\') == NULL) {
/* Plain file name, use same directory as for the .vmx file */
if (virAsprintf(&datastorePath, "[%s] %s/%s", data->datastoreName,
data->directoryName, fileName) < 0) {
virReportOOMError();
goto cleanup;
}
} else {
if (esxVI_String_AppendValueToList(&propertyNameList,
"summary.name") < 0 ||
esxVI_LookupDatastoreList(data->ctx, propertyNameList,
&datastoreList) < 0) {
return NULL; return NULL;
} }
/* Search for datastore by mount path */
for (datastore = datastoreList; datastore != NULL;
datastore = datastore->_next) {
esxVI_DatastoreHostMount_Free(&hostMount);
datastoreName = NULL;
if (esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj,
&hostMount) < 0 ||
esxVI_GetStringValue(datastore, "summary.name", &datastoreName,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
tmp = (char *)STRSKIP(fileName, hostMount->mountInfo->path);
if (tmp == NULL) {
continue;
}
/* Found a match. Strip leading separators */
while (*tmp == '/' || *tmp == '\\') {
++tmp;
}
if (esxVI_String_DeepCopyValue(&strippedFileName, tmp) < 0) {
goto cleanup;
}
tmp = strippedFileName;
/* Convert \ to / */
while (*tmp != '\0') {
if (*tmp == '\\') {
*tmp = '/';
}
++tmp;
}
if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
strippedFileName) < 0) {
virReportOOMError();
goto cleanup;
}
break;
}
/* Fallback to direct datastore name match */
if (datastorePath == NULL && STRPREFIX(fileName, "/vmfs/volumes/")) {
if (esxVI_String_DeepCopyValue(&copyOfFileName, fileName) < 0) {
goto cleanup;
}
/* Expected format: '/vmfs/volumes/<datastore>/<path>' */ /* Expected format: '/vmfs/volumes/<datastore>/<path>' */
if ((tmp = STRSKIP(copyOfAbsolutePath, "/vmfs/volumes/")) == NULL || if ((tmp = STRSKIP(copyOfFileName, "/vmfs/volumes/")) == NULL ||
(preliminaryDatastoreName = strtok_r(tmp, "/", &saveptr)) == NULL || (datastoreName = strtok_r(tmp, "/", &saveptr)) == NULL ||
(directoryAndFileName = strtok_r(NULL, "", &saveptr)) == NULL) { (directoryAndFileName = strtok_r(NULL, "", &saveptr)) == NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Absolute path '%s' doesn't have expected format " _("File name '%s' doesn't have expected format "
"'/vmfs/volumes/<datastore>/<path>'"), absolutePath); "'/vmfs/volumes/<datastore>/<path>'"), fileName);
goto cleanup; goto cleanup;
} }
if (esxVI_String_AppendValueToList(&propertyNameList, esxVI_ObjectContent_Free(&datastoreList);
"summary.name") < 0 ||
esxVI_LookupDatastoreByAbsolutePath(ctx, absolutePath, if (esxVI_LookupDatastoreByName(data->ctx, datastoreName,
propertyNameList, &datastore, NULL, &datastoreList,
esxVI_Occurrence_OptionalItem) < 0) { esxVI_Occurrence_OptionalItem) < 0) {
goto cleanup; goto cleanup;
} }
if (datastore == NULL) { if (datastoreList == NULL) {
if (esxVI_LookupDatastoreByName(ctx, preliminaryDatastoreName, ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
propertyNameList, &datastore, _("File name '%s' refers to non-existing datastore '%s'"),
esxVI_Occurrence_OptionalItem) < 0) { fileName, datastoreName);
goto cleanup; goto cleanup;
} }
}
if (datastore != NULL) {
if (esxVI_GetStringValue(datastore, "summary.name", &datastoreName,
esxVI_Occurrence_RequiredItem)) {
goto cleanup;
}
}
if (datastoreName == NULL) {
VIR_WARN("Could not retrieve datastore name for absolute "
"path '%s', falling back to preliminary name '%s'",
absolutePath, preliminaryDatastoreName);
datastoreName = preliminaryDatastoreName;
}
if (virAsprintf(&datastorePath, "[%s] %s", datastoreName, if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
directoryAndFileName) < 0) { directoryAndFileName) < 0) {
virReportOOMError(); virReportOOMError();
goto cleanup; goto cleanup;
} }
/* FIXME: Check if referenced path/file really exists */
success = true;
cleanup:
if (! success) {
VIR_FREE(datastorePath);
} }
VIR_FREE(copyOfAbsolutePath); if (datastorePath == NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Could not find datastore for '%s'"), fileName);
goto cleanup;
}
}
cleanup:
esxVI_String_Free(&propertyNameList); esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&datastore); esxVI_ObjectContent_Free(&datastoreList);
esxVI_DatastoreHostMount_Free(&hostMount);
VIR_FREE(strippedFileName);
VIR_FREE(copyOfFileName);
return datastorePath; return datastorePath;
} }
/*
* This function does the inverse of esxParseVMXFileName. It takes an file name
* in datastore path format and converts it to a file name that can be used in
* a .vmx file.
*
* The datastore path format and the formats found in a .vmx file are described
* in the documentation of esxParseVMXFileName.
*
* Firstly parse the datastore path. Then use the datastore name to lookup the
* datastore and it's mount path. Finally concatenate the mount path, directory
* and file name to an absolute path and return it. Detect the seperator type
* based on the mount path.
*/
static char * static char *
esxParseVMXFileName(const char *fileName, void *opaque) esxFormatVMXFileName(const char *datastorePath, void *opaque)
{
char *src = NULL;
esxVMX_Data *data = opaque;
if (STRPREFIX(fileName, "/vmfs/volumes/")) {
/* Found absolute path referencing a file inside a datastore */
return esxAbsolutePathToDatastorePath(data->ctx, fileName);
} else if (STRPREFIX(fileName, "/")) {
/* Found absolute path referencing a file outside a datastore */
src = strdup(fileName);
if (src == NULL) {
virReportOOMError();
return NULL;
}
/* FIXME: Check if referenced path/file really exists */
return src;
} else if (strchr(fileName, '/') != NULL) {
/* Found relative path, this is not supported */
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Found relative path '%s' in VMX file, this is not "
"supported"), fileName);
return NULL;
} else {
/* Found single file name referencing a file inside a datastore */
if (virAsprintf(&src, "[%s] %s/%s", data->datastoreName,
data->directoryName, fileName) < 0) {
virReportOOMError();
return NULL;
}
/* FIXME: Check if referenced path/file really exists */
return src;
}
}
static char *
esxFormatVMXFileName(const char *src, void *opaque ATTRIBUTE_UNUSED)
{ {
bool success = false; bool success = false;
esxVMX_Data *data = opaque;
char *datastoreName = NULL; char *datastoreName = NULL;
char *directoryName = NULL; char *directoryName = NULL;
char *fileName = NULL; char *fileName = NULL;
esxVI_ObjectContent *datastore = NULL;
esxVI_DatastoreHostMount *hostMount = NULL;
char separator = '/';
virBuffer buffer = VIR_BUFFER_INITIALIZER;
char *tmp;
int length;
char *absolutePath = NULL; char *absolutePath = NULL;
if (STRPREFIX(src, "[")) { /* Parse datastore path and lookup datastore */
/* Found potential datastore path */ if (esxUtil_ParseDatastorePath(datastorePath, &datastoreName,
if (esxUtil_ParseDatastorePath(src, &datastoreName, &directoryName, &directoryName, &fileName) < 0) {
&fileName) < 0) {
goto cleanup; goto cleanup;
} }
if (directoryName == NULL) { if (esxVI_LookupDatastoreByName(data->ctx, datastoreName,
if (virAsprintf(&absolutePath, "/vmfs/volumes/%s/%s", NULL, &datastore,
datastoreName, fileName) < 0) { esxVI_Occurrence_RequiredItem) < 0 ||
virReportOOMError(); esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj,
&hostMount) < 0) {
goto cleanup; goto cleanup;
} }
} else {
if (virAsprintf(&absolutePath, "/vmfs/volumes/%s/%s/%s",
datastoreName, directoryName, fileName) < 0) {
virReportOOMError();
goto cleanup;
}
}
} else if (STRPREFIX(src, "/")) {
/* Found absolute path */
absolutePath = strdup(src);
if (absolutePath == NULL) { /* Detect separator type */
if (strchr(hostMount->mountInfo->path, '\\') != NULL) {
separator = '\\';
}
/* Strip trailing separators */
length = strlen(hostMount->mountInfo->path);
while (length > 0 && hostMount->mountInfo->path[length - 1] == separator) {
--length;
}
/* Format as <mount>[/<directory>]/<file> */
virBufferAdd(&buffer, hostMount->mountInfo->path, length);
if (directoryName != NULL) {
/* Convert / to \ when necessary */
if (separator != '/') {
tmp = directoryName;
while (*tmp != '\0') {
if (*tmp == '/') {
*tmp = separator;
}
++tmp;
}
}
virBufferAddChar(&buffer, separator);
virBufferAdd(&buffer, directoryName, -1);
}
virBufferAddChar(&buffer, separator);
virBufferAdd(&buffer, fileName, -1);
if (virBufferError(&buffer)) {
virReportOOMError(); virReportOOMError();
goto cleanup; goto cleanup;
} }
} else {
/* Found relative path, this is not supported */ absolutePath = virBufferContentAndReset(&buffer);
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Found relative path '%s' in domain XML, this is not "
"supported"), src);
goto cleanup;
}
/* FIXME: Check if referenced path/file really exists */ /* FIXME: Check if referenced path/file really exists */
@ -238,12 +327,15 @@ esxFormatVMXFileName(const char *src, void *opaque ATTRIBUTE_UNUSED)
cleanup: cleanup:
if (! success) { if (! success) {
virBufferFreeAndReset(&buffer);
VIR_FREE(absolutePath); VIR_FREE(absolutePath);
} }
VIR_FREE(datastoreName); VIR_FREE(datastoreName);
VIR_FREE(directoryName); VIR_FREE(directoryName);
VIR_FREE(fileName); VIR_FREE(fileName);
esxVI_ObjectContent_Free(&datastore);
esxVI_DatastoreHostMount_Free(&hostMount);
return absolutePath; return absolutePath;
} }