[libvirt] [PATCH] Refactor ESX storage driver and add iSCSI support

The patch refactors the current ESX storage driver due to following reasons: 1. Given most of the public APIs exposed by the storage driver in Libvirt remains same, ESX storage driver should not implement logic specific for only one supported format (current implementation only supports VMFS). 2. Decoupling interface from specific storage implementation gives us an extensible design to hook implementation for other supported storage formats. This patch refactors the current driver to implement it as a facade pattern i.e. the driver exposes all the public libvirt APIs, but uses backend drivers to get the required task done. The backend drivers provide implementation specific to the type of storage device. File changes: ------------------ esx_storage_driver.c ----> esx_storage_driver.c (base storage driver) | |---> esx_storage_backend_vmfs.c (VMFS backend) | |---> esx_storage_backend_iscsi.c (iSCSI backend) The patch adds the backend driver to support iSCSI format storage pools and volumes for ESX host. The mapping of ESX iSCSI specifics to Libvirt is as follows: 1. ESX static iSCSI target <------> Libvirt Storage Pools 2. ESX iSCSI LUNs <------> Libvirt Storage Volumes. The above understanding is based on http://libvirt.org/storage.html. The operation supported on iSCSI pools includes: 1. List storage pools & volumes. 2. Get xml descriptor operaion on pools & volumes. 3. Lookup operation on pools & volumes by name, uuid and path (if applicable). iSCSI pools does not support operations such as: Create / remove pools and volumes --- src/Makefile.am | 16 +- src/esx/esx_driver.c | 4 +- src/esx/esx_storage_backend_iscsi.c | 794 +++++++++++++++++++ src/esx/esx_storage_backend_iscsi.h | 31 + src/esx/esx_storage_backend_vmfs.c | 1471 +++++++++++++++++++++++++++++++++++ src/esx/esx_storage_backend_vmfs.h | 31 + src/esx/esx_storage_driver.c | 1303 +++++-------------------------- src/esx/esx_vi.c | 341 +++++++- src/esx/esx_vi.h | 21 +- src/esx/esx_vi_generator.input | 308 +++++++- src/esx/esx_vi_generator.py | 19 + 11 files changed, 3218 insertions(+), 1121 deletions(-) create mode 100644 src/esx/esx_storage_backend_iscsi.c create mode 100644 src/esx/esx_storage_backend_iscsi.h create mode 100644 src/esx/esx_storage_backend_vmfs.c create mode 100644 src/esx/esx_storage_backend_vmfs.h diff --git a/src/Makefile.am b/src/Makefile.am index d35edd6..8f58a8a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -475,13 +475,15 @@ UML_DRIVER_SOURCES = \ uml/uml_conf.c uml/uml_conf.h \ uml/uml_driver.c uml/uml_driver.h -ESX_DRIVER_SOURCES = \ - esx/esx_private.h \ - esx/esx_driver.c esx/esx_driver.h \ - esx/esx_interface_driver.c esx/esx_interface_driver.h \ - esx/esx_network_driver.c esx/esx_network_driver.h \ - esx/esx_storage_driver.c esx/esx_storage_driver.h \ - esx/esx_device_monitor.c esx/esx_device_monitor.h \ +ESX_DRIVER_SOURCES = \ + esx/esx_private.h \ + esx/esx_driver.c esx/esx_driver.h \ + esx/esx_interface_driver.c esx/esx_interface_driver.h \ + esx/esx_network_driver.c esx/esx_network_driver.h \ + esx/esx_storage_driver.c esx/esx_storage_driver.h \ + esx/esx_storage_backend_vmfs.c esx/esx_storage_backend_vmfs.h \ + esx/esx_storage_backend_iscsi.c esx/esx_storage_backend_iscsi.h \ + esx/esx_device_monitor.c esx/esx_device_monitor.h \ esx/esx_secret_driver.c esx/esx_secret_driver.h \ esx/esx_nwfilter_driver.c esx/esx_nwfilter_driver.h \ esx/esx_util.c esx/esx_util.h \ diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 72a7acc..359de53 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -162,7 +162,7 @@ esxParseVMXFileName(const char *fileName, void *opaque) datastoreName = NULL; if (esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj, - &hostMount) < 0 || + &hostMount, esxVI_Occurrence_RequiredItem) < 0 || esxVI_GetStringValue(datastore, "summary.name", &datastoreName, esxVI_Occurrence_RequiredItem) < 0) { goto cleanup; @@ -306,7 +306,7 @@ esxFormatVMXFileName(const char *fileName, void *opaque) if (esxVI_LookupDatastoreByName(data->ctx, datastoreName, NULL, &datastore, esxVI_Occurrence_RequiredItem) < 0 || esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj, - &hostMount) < 0) { + &hostMount, esxVI_Occurrence_RequiredItem) < 0) { goto cleanup; } diff --git a/src/esx/esx_storage_backend_iscsi.c b/src/esx/esx_storage_backend_iscsi.c new file mode 100644 index 0000000..4f79a0f --- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.c @@ -0,0 +1,794 @@ +/* + * esx_storage_backend_iscsi.c: ESX storage backend for iSCSI handling + * + * Copyright (C) 2007-2008, 2010-2012 Red Hat, Inc. + * + * 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 + * + * Author: Ata E Husain Bohra (ata.husain@hotmail.com) + */ + +#include <config.h> + +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "internal.h" +#include "md5.h" +#include "util.h" +#include "memory.h" +#include "logging.h" +#include "uuid.h" + +#include "storage_conf.h" +#include "storage_file.h" +#include "esx_private.h" +#include "esx_vi.h" +#include "esx_vi_methods.h" +#include "esx_util.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +/* + * The UUID of a storage pool is the MD5 sum of it's mount path. Therefore, + * verify that UUID and MD5 sum match in size, because we rely on that. + */ +verify(MD5_DIGEST_SIZE == VIR_UUID_BUFLEN); + + + +static int +esxStorageBackendISCSINumberOfStoragePools(virConnectPtr conn) +{ + int count = 0; + esxPrivate *priv = conn->storagePrivateData; + esxVI_HostInternetScsiHba *hostInternetScsiHba = NULL; + bool success = false; + + if (esxVI_LookupHostInternetScsiHba( + priv->primary, &hostInternetScsiHba) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to obtain iSCSI adapter")); + goto cleanup; + } + + if (hostInternetScsiHba == NULL) { + /* iSCSI adapter may not be enabled for this host */ + return 0; + } + + /** + * ESX has two kind of targets: + * 1. staticIscsiTargets + * 2. dynamicIscsiTargets + * For each dynamic target if its reachable a static target is added. + * return iSCSI names for all static targets to avoid duplicate names. + */ + if (hostInternetScsiHba->configuredStaticTarget) { + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + ++count; + } + } + + success = true; + +cleanup: + + esxVI_HostInternetScsiHba_Free(&hostInternetScsiHba); + + return success ? count : -1; + +} + + + +static int +esxStorageBackendISCSIListStoragePools(virConnectPtr conn, + char **const names, + const int maxnames) +{ + int count = 0; + esxPrivate *priv = conn->storagePrivateData; + esxVI_HostInternetScsiHba *hostInternetScsiHba = NULL; + bool success = false; + int i = 0; + + if (maxnames ==0) { + return 0; + } + + if (esxVI_LookupHostInternetScsiHba( + priv->primary, &hostInternetScsiHba) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to obtain iSCSI adapter")); + goto cleanup; + } + + if (hostInternetScsiHba == NULL) { + /* iSCSI adapter may not be enabled for this host */ + return 0; + } + + /** + * ESX has two kind of targets: + * 1. staticIscsiTargets + * 2. dynamicIscsiTargets + * For each dynamic target if its reachable a static target is added. + * return iSCSI names for all static targets to avoid duplicate names. + */ + if (hostInternetScsiHba->configuredStaticTarget) { + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL && count < maxnames; + target = target->_next, ++count) { + names[count] = strdup(target->iScsiName); + + if (names[count] == NULL) { + virReportOOMError(); + goto cleanup; + } + } + } + + success = true; + +cleanup: + if (! success) { + for (i = 0; i < count; ++i) { + VIR_FREE(names[i]); + } + count = -1; + } + + esxVI_HostInternetScsiHba_Free(&hostInternetScsiHba); + + return success ? count : -1; +} + + + +static virStoragePoolPtr +esxStorageBackendISCSIPoolLookupByName(virConnectPtr conn, + const char *name) +{ + esxPrivate *priv = conn->storagePrivateData; + esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + virStoragePoolPtr pool = NULL; + + if (esxVI_LookupHostInternetScsiHbaStaticTargetByName( + priv->primary, name, &target, esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + /** + * HostInternetScsiHbaStaticTarget does not provide a uuid field, + * but iSsiName (or widely known as IQN) is unique across the multiple + * hosts, using it to compute key + */ + + md5_buffer(target->iScsiName, strlen(target->iScsiName), md5); + + pool = virGetStoragePool(conn, name, md5); + + cleanup: + + esxVI_HostInternetScsiHbaStaticTarget_Free(&target); + + return pool; + +} + + + +static virStoragePoolPtr +esxStorageBackendISCSIPoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + virStoragePoolPtr pool = NULL; + esxPrivate *priv = conn->storagePrivateData; + esxVI_HostInternetScsiHba *hostInternetScsiHba = NULL; + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + + if (esxVI_LookupHostInternetScsiHba( + priv->primary, &hostInternetScsiHba) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to obtain iSCSI adapter")); + goto cleanup; + } + + if (hostInternetScsiHba == NULL) { + /* iSCSI adapter may not be enabled for this host */ + return NULL; + } + + if (hostInternetScsiHba->configuredStaticTarget) { + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + md5_buffer(target->iScsiName, strlen(target->iScsiName), md5); + + if (memcmp(uuid, md5, VIR_UUID_STRING_BUFLEN) == 0) { + break; + } + } + } + + if (target == NULL) { + /* pool not found */ + goto cleanup; + } + + pool = virGetStoragePool(conn, target->iScsiName, md5); + + cleanup: + esxVI_HostInternetScsiHba_Free(&hostInternetScsiHba); + + return pool; + +} + + + +static int +esxStorageBackendISCSIPoolRefresh(virStoragePoolPtr pool, + unsigned int flags) +{ + int result = -1; + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_ManagedObjectReference *hostStorageSystem = NULL; + esxVI_HostInternetScsiHba *hostInternetScsiHba = NULL; + esxVI_ObjectContent *hostSystem = NULL; + esxVI_String *propertyNameList = NULL; + + virCheckFlags(0, -1); + + if (esxVI_String_AppendValueToList(&propertyNameList, + "configManager.storageSystem\0") < 0 || + esxVI_LookupHostSystemProperties(priv->primary, + propertyNameList, &hostSystem) < 0 || + esxVI_GetManagedObjectReference(hostSystem, + "configManager.storageSystem", &hostStorageSystem, + esxVI_Occurrence_RequiredItem) < 0 || + esxVI_LookupHostInternetScsiHba( + priv->primary, &hostInternetScsiHba) < 0) { + goto cleanup; + } + + /** + * ESX does not allow rescan on a particular target, + * rescan all the static targets + */ + if (esxVI_RescanHba(priv->primary, hostStorageSystem, + hostInternetScsiHba->device) < 0) { + goto cleanup; + } + + result = 0; + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ManagedObjectReference_Free(&hostStorageSystem); + esxVI_ObjectContent_Free(&hostSystem); + esxVI_HostInternetScsiHba_Free(&hostInternetScsiHba); + + return result; + +} + + + +static int +esxStorageBackendISCSIPoolGetInfo(virStoragePoolPtr pool ATTRIBUTE_UNUSED, + virStoragePoolInfoPtr info) +{ + /* these fields are not valid for iSCSI pool */ + info->allocation = info->capacity = info->available = 0; + info->state = esxVI_Boolean_True; + + return 0; +} + + + +static char * +esxStorageBackendISCSIPoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags) +{ + char *xml = NULL; + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_HostInternetScsiHba *hostInternetScsiHba = NULL; + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + virStoragePoolDef def; + + virCheckFlags(0, NULL); + + memset(&def, 0, sizeof(def)); + + if (esxVI_LookupHostInternetScsiHba(priv->primary, &hostInternetScsiHba)) { + goto cleanup; + } + + if (hostInternetScsiHba->configuredStaticTarget) { + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + if (STREQ(target->iScsiName, pool->name)) { + break; + } + } + } + + if (target == NULL) { + goto cleanup; + } + + def.name = pool->name; + memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN); + + def.type = VIR_STORAGE_POOL_ISCSI; + + def.source.initiator.iqn = target->iScsiName; + + def.source.nhost = 1; + if (VIR_ALLOC_N(def.source.hosts, def.source.nhost) < 0) { + virReportOOMError(); + goto cleanup; + } + + def.source.hosts[0].name = target->address; + if (target->port) { + def.source.hosts[0].port = target->port->value; + } + + /* TODO: add CHAP authentication params */ + + xml = virStoragePoolDefFormat(&def); + + cleanup: + + VIR_FREE(def.source.hosts); + esxVI_HostInternetScsiHba_Free(&hostInternetScsiHba); + + return xml; + +} + + + +static int +esxStorageBackendISCSIPoolNumberOfStorageVolumes(virStoragePoolPtr pool) +{ + int count = 0; + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_HostScsiTopologyLun *hostScsiTopologyLunList = NULL; + const esxVI_HostScsiTopologyLun *hostScsiTopologyLun = NULL; + bool success = false; + + if (esxVI_LookupHostScsiTopologyLunListByTargetName( + priv->primary, pool->name, &hostScsiTopologyLunList) < 0) { + goto cleanup; + } + + for (hostScsiTopologyLun = hostScsiTopologyLunList ; + hostScsiTopologyLun != NULL; + hostScsiTopologyLun = hostScsiTopologyLun->_next) { + ++count; + } + + success = true; + + cleanup: + + esxVI_HostScsiTopologyLun_Free(&hostScsiTopologyLunList); + + return success ? count : -1; + +} + + +static int +esxStorageBackendISCSIPoolListStorageVolumes(virStoragePoolPtr pool, + char **const names, + int maxnames) +{ + int count = 0; + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_HostScsiTopologyLun *hostScsiTopologyLunList = NULL; + const esxVI_HostScsiTopologyLun *hostScsiTopologyLun = NULL; + esxVI_ScsiLun *scsiLunList = NULL; + const esxVI_ScsiLun *scsiLun = NULL; + bool success = false; + int i = 0; + + if (esxVI_LookupHostScsiTopologyLunListByTargetName( + priv->primary, pool->name, &hostScsiTopologyLunList) < 0) { + goto cleanup; + } + + if (hostScsiTopologyLunList == NULL) { + /* iSCSI adapter may not be enabled on ESX host */ + return 0; + } + + if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + /* O^2 but still faster than hash given N is not that large */ + for (scsiLun = scsiLunList; scsiLun != NULL && count < maxnames; + scsiLun = scsiLun->_next) { + for (hostScsiTopologyLun = hostScsiTopologyLunList; + hostScsiTopologyLun != NULL && count < maxnames; + hostScsiTopologyLun = hostScsiTopologyLun->_next) { + if (STREQ(hostScsiTopologyLun->scsiLun, scsiLun->key)) { + names[count] = strdup(scsiLun->deviceName); + + if (names[count] == NULL) { + virReportOOMError(); + goto cleanup; + } + ++count; + } + } + } + + success = true; + + cleanup: + if (! success) { + for (i = 0; i < count; ++i) { + VIR_FREE(names[i]); + } + count = -1; + } + + esxVI_HostScsiTopologyLun_Free(&hostScsiTopologyLunList); + esxVI_ScsiLun_Free(&scsiLunList); + + return count; +} + + +static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByName(virStoragePoolPtr pool, + const char *name) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_ScsiLun *scsiLunList = NULL; + const esxVI_ScsiLun *scsiLun = NULL; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + + if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + for (scsiLun = scsiLunList; scsiLun != NULL; + scsiLun = scsiLun->_next) { + if (STREQ(scsiLun->deviceName, name)) { + /** + * ScsiLun provides an UUID field that is unique accross + * multiple servers. But this field length is ~55 characters + * compute MD5 hash to transform it to an acceptable + * libvirt format + */ + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string); + + /** + * ScsiLun provides displayName and canonicalName but both are + * optional and its observed that they can be NULL, using + * deviceName to create volume. + */ + volume = virGetStorageVol(pool->conn, pool->name, name, uuid_string); + break; + } + } + + cleanup: + + esxVI_ScsiLun_Free(&scsiLunList); + + return volume; + +} + + +static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByPath(virConnectPtr conn, const char *path) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = conn->storagePrivateData; + char *poolName = NULL; + esxVI_ScsiLun *scsiLunList = NULL; + const esxVI_ScsiLun *scsiLun = NULL; + const esxVI_HostScsiDisk *hostScsiDisk = NULL; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + + if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + for (scsiLun = scsiLunList ; scsiLun != NULL; + scsiLun = scsiLun->_next) { + hostScsiDisk = + esxVI_HostScsiDisk_DynamicCast((esxVI_ScsiLun *)scsiLun); + + if (hostScsiDisk != NULL && + STREQ(hostScsiDisk->devicePath, path)) { + /* Found matching device */ + if (esxVI_LookupStoragePoolNameByScsiLunKey( + priv->primary, hostScsiDisk->key, &poolName) < 0) { + goto cleanup; + } + + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string); + + volume = virGetStorageVol(conn, poolName, path, uuid_string); + break; + } + } + + cleanup: + + esxVI_ScsiLun_Free(&scsiLunList); + VIR_FREE(poolName); + + return volume; + +} + + +static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByKey(virConnectPtr conn, const char *key) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = conn->storagePrivateData; + char *poolName = NULL; + esxVI_ScsiLun *scsiLunList = NULL; + const esxVI_ScsiLun *scsiLun = NULL; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + + + /* key may be LUN device path */ + if (STRPREFIX(key, "/")) { + return esxStorageBackendISCSIVolumeLookupByPath(conn, key); + } + + if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + for (scsiLun = scsiLunList; scsiLun != NULL; + scsiLun = scsiLun->_next) { + + memset(uuid_string, '\0', sizeof(uuid_string)); + memset(md5, '\0', sizeof(md5)); + + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string); + + if (STREQ(key, uuid_string)) { + /* Found matching UUID */ + if (esxVI_LookupStoragePoolNameByScsiLunKey( + priv->primary, scsiLun->key, &poolName) < 0) { + goto cleanup; + } + + volume = virGetStorageVol(conn, poolName, + scsiLun->deviceName, uuid_string); + break; + } + } + + cleanup: + + esxVI_ScsiLun_Free(&scsiLunList); + VIR_FREE(poolName); + + return volume; + +} + + + +static virStorageVolPtr +esxStorageBackendISCSIVolumeCreateXML(virStoragePoolPtr pool ATTRIBUTE_UNUSED, + const char *xmldesc ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(0, NULL); + + /* not supported operation for iSCSI pools */ + return NULL; +} + + + +static virStorageVolPtr +esxStorageBackendISCSIVolumeCreateXMLFrom( + virStoragePoolPtr pool ATTRIBUTE_UNUSED, + const char *xmldesc ATTRIBUTE_UNUSED, + virStorageVolPtr sourceVolume ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(0, NULL); + + /* not supported operation for iSCSI pools */ + return NULL; +} + + + +static char* +esxStorageBackendISCSIVolumeGetXMLDesc(virStorageVolPtr volume, + unsigned int flags) +{ + char *xml = NULL; + esxPrivate *priv = volume->conn->storagePrivateData; + virStoragePoolDef pool; + esxVI_ScsiLun *scsiLunList = NULL; + const esxVI_ScsiLun *scsiLun = NULL; + const esxVI_HostScsiDisk *hostScsiDisk = NULL; + virStorageVolDef def; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + + virCheckFlags(0, NULL); + + memset(&pool, 0, sizeof(pool)); + memset(&def, 0, sizeof(def)); + + if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + for (scsiLun = scsiLunList; scsiLun != NULL; + scsiLun = scsiLun->_next) { + hostScsiDisk = + esxVI_HostScsiDisk_DynamicCast((esxVI_ScsiLun *)scsiLun); + + if (hostScsiDisk != NULL && + STREQ(hostScsiDisk->deviceName, volume->name)) { + break; + } + } + + if (hostScsiDisk == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could find volume with name: %s"), volume->name); + goto cleanup; + } + + pool.type = VIR_STORAGE_POOL_ISCSI; + + def.name = volume->name; + + md5_buffer(scsiLun->uuid, strlen(hostScsiDisk->uuid), md5); + + virUUIDFormat(md5, uuid_string); + + if (esxVI_String_DeepCopyValue(&def.key, uuid_string) < 0) { + goto cleanup; + } + + /* iSCSI LUN exposes a block device */ + def.type = VIR_STORAGE_VOL_BLOCK; + + def.target.path = hostScsiDisk->devicePath; + + def.capacity = hostScsiDisk->capacity->block->value * + hostScsiDisk->capacity->blockSize->value; + + def.allocation = def.capacity; + + /* iSCSI LUN(s) hosting a datastore will be auto-mounted by + * ESX host + */ + def.target.format = VIR_STORAGE_FILE_RAW; + + xml = virStorageVolDefFormat(&pool, &def); + + cleanup: + + esxVI_ScsiLun_Free(&scsiLunList); + VIR_FREE(def.key); + + return xml; + +} + +static int +esxStorageBackendISCSIVolumeDelete(virStorageVolPtr volume ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(0, -1); + + /* unsupported operation for iSCSI volume */ + return 1; + +} + + + +static int +esxStorageBackendISCSIVolumeWipe(virStorageVolPtr volume ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(0, -1); + + /* unsupported operation for iSCSI volume */ + return 1; + +} + + + +static char* +esxStorageBackendISCSIVolumeGetPath(virStorageVolPtr volume) +{ + char *path; + + if (virAsprintf(&path, "%s", volume->name) < 0) { + virReportOOMError(); + return NULL; + } + + return path; + +} + + + +virStorageDriver esxStorageBackendISCSIDrv = { + .name = "ESX ISCSI backend", + .open = NULL, /* 0.10.0 */ + .close = NULL, /* 0.10.0 */ + .numOfPools = esxStorageBackendISCSINumberOfStoragePools, /* 0.10.0 */ + .listPools = esxStorageBackendISCSIListStoragePools, /* 0.10.0 */ + .poolLookupByName = esxStorageBackendISCSIPoolLookupByName, /* 0.10.0 */ + .poolLookupByUUID = esxStorageBackendISCSIPoolLookupByUUID, /* 0.10.0 */ + .poolRefresh = esxStorageBackendISCSIPoolRefresh, /* 0.10.0 */ + .poolGetInfo = esxStorageBackendISCSIPoolGetInfo, /* 0.10.0 */ + .poolGetXMLDesc = esxStorageBackendISCSIPoolGetXMLDesc, /* 0.10.0 */ + .poolNumOfVolumes = esxStorageBackendISCSIPoolNumberOfStorageVolumes, /* 0.10.0 */ + .poolListVolumes = esxStorageBackendISCSIPoolListStorageVolumes, /* 0.10.0 */ + .volLookupByName = esxStorageBackendISCSIVolumeLookupByName, /* 0.10.0 */ + .volLookupByKey = esxStorageBackendISCSIVolumeLookupByKey, /* 0.10.0 */ + .volLookupByPath = esxStorageBackendISCSIVolumeLookupByPath, /* 0.10.0 */ + .volCreateXML = esxStorageBackendISCSIVolumeCreateXML, /* 0.10.0 */ + .volCreateXMLFrom = esxStorageBackendISCSIVolumeCreateXMLFrom, /* 0.10.0 */ + .volGetXMLDesc = esxStorageBackendISCSIVolumeGetXMLDesc, /* 0.10.0 */ + .volDelete = esxStorageBackendISCSIVolumeDelete, /* 0.10.0 */ + .volWipe = esxStorageBackendISCSIVolumeWipe, /* 0.10.0 */ + .volGetPath = esxStorageBackendISCSIVolumeGetPath, /* 0.10.0 */ + +}; diff --git a/src/esx/esx_storage_backend_iscsi.h b/src/esx/esx_storage_backend_iscsi.h new file mode 100644 index 0000000..ca756ac --- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.h @@ -0,0 +1,31 @@ +/* + * esx_storage_backend_iscsi.h: ESX storage backend for iSCSI handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * + * 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 + * + * Author: Ata E Husain Bohra (ata.husain@hotmail.com) + */ + +#ifndef __ESX_STORAGE_BACKEND_ISCSI_H__ +# define __ESX_STORAGE_BACKEND_ISCSI_H__ + +#include "driver.h" + +extern virStorageDriver esxStorageBackendISCSIDrv; + +#endif /* __ESX_STORAGE_BACKEND_ISCSI_H__ */ + diff --git a/src/esx/esx_storage_backend_vmfs.c b/src/esx/esx_storage_backend_vmfs.c new file mode 100644 index 0000000..6550196 --- /dev/null +++ b/src/esx/esx_storage_backend_vmfs.c @@ -0,0 +1,1471 @@ + +/* + * esx_storage_backend_vmfs.c: ESX storage backend for VMFS datastores + * + * Copyright (C) 2010-2011 Red Hat, Inc. + * Copyright (C) 2010 Matthias Bolte <matthias.bolte@googlemail.com> + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Ata E Husain Bohra (ata.husain@hotmail.com) + */ + +#include <config.h> + +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "internal.h" +#include "md5.h" +#include "util.h" +#include "memory.h" +#include "logging.h" +#include "uuid.h" + +#include "storage_conf.h" +#include "storage_file.h" +#include "esx_storage_backend_vmfs.h" +#include "esx_private.h" +#include "esx_vi.h" +#include "esx_vi_methods.h" +#include "esx_util.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +/* + * The UUID of a storage pool is the MD5 sum of it's mount path. Therefore, + * verify that UUID and MD5 sum match in size, because we rely on that. + */ +verify(MD5_DIGEST_SIZE == VIR_UUID_BUFLEN); + + + +static int +esxStorageBackendVMFSPoolLookupType(virConnectPtr conn, const char *poolName, + int *poolType) +{ + int result = -1; + esxPrivate *priv = conn->storagePrivateData; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *datastore = NULL; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_DatastoreInfo *datastoreInfo = NULL; + + if (esxVI_String_AppendValueToList(&propertyNameList, "info") < 0 || + esxVI_LookupDatastoreByName(priv->primary, poolName, + propertyNameList, &datastore, + esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + if (datastore == NULL) { + /* not found */ + goto cleanup; + } + + for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "info")) { + if (esxVI_DatastoreInfo_CastFromAnyType(dynamicProperty->val, + &datastoreInfo) < 0) { + goto cleanup; + } + + break; + } + } + + if (esxVI_LocalDatastoreInfo_DynamicCast(datastoreInfo) != NULL) { + *poolType = VIR_STORAGE_POOL_DIR; + } else if (esxVI_NasDatastoreInfo_DynamicCast(datastoreInfo) != NULL) { + *poolType = VIR_STORAGE_POOL_NETFS; + } else if (esxVI_VmfsDatastoreInfo_DynamicCast(datastoreInfo) != NULL) { + *poolType = VIR_STORAGE_POOL_FS; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("DatastoreInfo has unexpected type")); + goto cleanup; + } + + result = 0; + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&datastore); + esxVI_DatastoreInfo_Free(&datastoreInfo); + + return result; +} + + + + +static int +esxStorageBackendVMFSNumberOfStoragePools(virConnectPtr conn) +{ + esxPrivate *priv = conn->storagePrivateData; + esxVI_ObjectContent *datastoreList = NULL; + esxVI_ObjectContent *datastore = NULL; + int count = 0; + + if (esxVI_LookupDatastoreList(priv->primary, NULL, &datastoreList) < 0) { + goto cleanup; + } + + for (datastore = datastoreList; datastore != NULL; + datastore = datastore->_next) { + ++count; + } + + cleanup: + esxVI_ObjectContent_Free(&datastoreList); + + return count; +} + +static int +esxStorageBackendVMFSListStoragePools(virConnectPtr conn, + char **const names, + const int maxnames) +{ + esxPrivate *priv = conn->storagePrivateData; + esxVI_String *propertyNameList = NULL; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_ObjectContent *datastoreList = NULL; + esxVI_ObjectContent *datastore = NULL; + int count = 0; + int i = 0; + bool success = false; + + if (maxnames == 0) { + return 0; + } + + if (esxVI_String_AppendValueToList(&propertyNameList, + "summary.name") < 0 || + esxVI_LookupDatastoreList(priv->primary, propertyNameList, + &datastoreList) < 0) { + goto cleanup; + } + + for (datastore = datastoreList; datastore != NULL; + datastore = datastore->_next) { + for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "summary.name")) { + if (esxVI_AnyType_ExpectType(dynamicProperty->val, + esxVI_Type_String) < 0) { + goto cleanup; + } + + names[count] = strdup(dynamicProperty->val->string); + + if (names[count] == NULL) { + virReportOOMError(); + goto cleanup; + } + + ++count; + break; + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + } + + success = true; + + cleanup: + if (! success) { + for (i = 0; i < count; ++i) { + VIR_FREE(names[i]); + } + + count = -1; + } + + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&datastoreList); + + return count; +} + +static virStoragePoolPtr +esxStorageBackendVMFSPoolLookupByName(virConnectPtr conn, + const char *name) +{ + esxPrivate *priv = conn->storagePrivateData; + esxVI_ObjectContent *datastore = NULL; + esxVI_DatastoreHostMount *hostMount = NULL; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + virStoragePoolPtr pool = NULL; + + virCheckNonNullArgReturn(name, NULL); + + if (esxVI_LookupDatastoreByName(priv->primary, name, NULL, &datastore, + esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + if (datastore == NULL) { + /* not found */ + goto cleanup; + } + + /* + * Datastores don't have a UUID, but we can use the 'host.mountInfo.path' + * property as source for a UUID. The mount path is unique per host and + * cannot change during the lifetime of the datastore. + * + * The MD5 sum of the mount path can be used as UUID, assuming MD5 is + * considered to be collision-free enough for this use case. + */ + if (esxVI_LookupDatastoreHostMount(priv->primary, datastore->obj, + &hostMount, esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + md5_buffer(hostMount->mountInfo->path, + strlen(hostMount->mountInfo->path), md5); + + pool = virGetStoragePool(conn, name, md5); + + cleanup: + esxVI_ObjectContent_Free(&datastore); + esxVI_DatastoreHostMount_Free(&hostMount); + + return pool; +} + +static virStoragePoolPtr +esxStorageBackendVMFSPoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + esxPrivate *priv = conn->storagePrivateData; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *datastoreList = NULL; + esxVI_ObjectContent *datastore = NULL; + esxVI_DatastoreHostMount *hostMount = NULL; + unsigned char md5[MD5_DIGEST_SIZE]; /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + char *name = NULL; + virStoragePoolPtr pool = NULL; + + + if (esxVI_String_AppendValueToList(&propertyNameList, "summary.name") < 0 || + esxVI_LookupDatastoreList(priv->primary, propertyNameList, + &datastoreList) < 0) { + goto cleanup; + } + + for (datastore = datastoreList; datastore != NULL; + datastore = datastore->_next) { + esxVI_DatastoreHostMount_Free(&hostMount); + + if (esxVI_LookupDatastoreHostMount(priv->primary, datastore->obj, + &hostMount, esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + if (hostMount == NULL) { + /* not found */ + goto cleanup; + } + + md5_buffer(hostMount->mountInfo->path, + strlen(hostMount->mountInfo->path), md5); + + if (memcmp(uuid, md5, VIR_UUID_BUFLEN) == 0) { + break; + } + } + + if (datastore == NULL) { + goto cleanup; + } + + if (esxVI_GetStringValue(datastore, "summary.name", &name, + esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + pool = virGetStoragePool(conn, name, uuid); + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&datastoreList); + esxVI_DatastoreHostMount_Free(&hostMount); + + return pool; +} + +static int +esxStorageBackendVMFSPoolRefresh(virStoragePoolPtr pool, unsigned int flags) +{ + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_ObjectContent *datastore = NULL; + int result = -1; + + virCheckFlags(0, -1); + + if (esxVI_LookupDatastoreByName(priv->primary, pool->name, NULL, &datastore, + esxVI_Occurrence_RequiredItem) < 0 || + esxVI_RefreshDatastore(priv->primary, datastore->obj) < 0) { + goto cleanup; + } + + result = 0; + + cleanup: + esxVI_ObjectContent_Free(&datastore); + + return result; +} + + +static int +esxStorageBackendVMFSPoolGetInfo(virStoragePoolPtr pool, + virStoragePoolInfoPtr info) +{ + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *datastore = NULL; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_Boolean accessible = esxVI_Boolean_Undefined; + int result = -1; + + if (esxVI_String_AppendValueListToList(&propertyNameList, + "summary.accessible\0" + "summary.capacity\0" + "summary.freeSpace\0") < 0 || + esxVI_LookupDatastoreByName(priv->primary, pool->name, + propertyNameList, &datastore, + esxVI_Occurrence_RequiredItem) < 0 || + esxVI_GetBoolean(datastore, "summary.accessible", + &accessible, esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + if (accessible == esxVI_Boolean_True) { + info->state = VIR_STORAGE_POOL_RUNNING; + + for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "summary.capacity")) { + if (esxVI_AnyType_ExpectType(dynamicProperty->val, + esxVI_Type_Long) < 0) { + goto cleanup; + } + + info->capacity = dynamicProperty->val->int64; + } else if (STREQ(dynamicProperty->name, "summary.freeSpace")) { + if (esxVI_AnyType_ExpectType(dynamicProperty->val, + esxVI_Type_Long) < 0) { + goto cleanup; + } + + info->available = dynamicProperty->val->int64; + } + } + + info->allocation = info->capacity - info->available; + } else { + info->state = VIR_STORAGE_POOL_INACCESSIBLE; + } + + result = 0; + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&datastore); + + return result; +} + +static char * +esxStorageBackendVMFSPoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags) +{ + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *datastore = NULL; + esxVI_DatastoreHostMount *hostMount = NULL; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_Boolean accessible = esxVI_Boolean_Undefined; + virStoragePoolDef def; + esxVI_DatastoreInfo *info = NULL; + esxVI_NasDatastoreInfo *nasInfo = NULL; + char *xml = NULL; + + virCheckFlags(0, NULL); + + memset(&def, 0, sizeof(def)); + + if (esxVI_String_AppendValueListToList(&propertyNameList, + "summary.accessible\0" + "summary.capacity\0" + "summary.freeSpace\0" + "info\0") < 0 || + esxVI_LookupDatastoreByName(priv->primary, pool->name, + propertyNameList, &datastore, + esxVI_Occurrence_RequiredItem) < 0 || + esxVI_GetBoolean(datastore, "summary.accessible", + &accessible, esxVI_Occurrence_RequiredItem) < 0 || + esxVI_LookupDatastoreHostMount(priv->primary, datastore->obj, + &hostMount, esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + def.name = pool->name; + memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN); + + def.target.path = hostMount->mountInfo->path; + + if (accessible == esxVI_Boolean_True) { + for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "summary.capacity")) { + if (esxVI_AnyType_ExpectType(dynamicProperty->val, + esxVI_Type_Long) < 0) { + goto cleanup; + } + + def.capacity = dynamicProperty->val->int64; + } else if (STREQ(dynamicProperty->name, "summary.freeSpace")) { + if (esxVI_AnyType_ExpectType(dynamicProperty->val, + esxVI_Type_Long) < 0) { + goto cleanup; + } + + def.available = dynamicProperty->val->int64; + } + } + + def.allocation = def.capacity - def.available; + } + + for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "info")) { + if (esxVI_DatastoreInfo_CastFromAnyType(dynamicProperty->val, + &info) < 0) { + goto cleanup; + } + + break; + } + } + + /* See vSphere API documentation about HostDatastoreSystem for details */ + if (esxVI_LocalDatastoreInfo_DynamicCast(info) != NULL) { + def.type = VIR_STORAGE_POOL_DIR; + } else if ((nasInfo = esxVI_NasDatastoreInfo_DynamicCast(info)) != NULL) { + if (VIR_ALLOC_N(def.source.hosts, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + def.type = VIR_STORAGE_POOL_NETFS; + def.source.hosts[0].name = nasInfo->nas->remoteHost; + def.source.dir = nasInfo->nas->remotePath; + + if (STRCASEEQ(nasInfo->nas->type, "NFS")) { + def.source.format = VIR_STORAGE_POOL_NETFS_NFS; + } else if (STRCASEEQ(nasInfo->nas->type, "CIFS")) { + def.source.format = VIR_STORAGE_POOL_NETFS_CIFS; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Datastore has unexpected type '%s'"), + nasInfo->nas->type); + goto cleanup; + } + } else if (esxVI_VmfsDatastoreInfo_DynamicCast(info) != NULL) { + def.type = VIR_STORAGE_POOL_FS; + /* + * FIXME: I'm not sure how to represent the source and target of a + * VMFS based datastore in libvirt terms + */ + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("DatastoreInfo has unexpected type")); + goto cleanup; + } + + xml = virStoragePoolDefFormat(&def); + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&datastore); + esxVI_DatastoreHostMount_Free(&hostMount); + esxVI_DatastoreInfo_Free(&info); + + return xml; +} + + +static int +esxStorageBackendVMFSPoolNumberOfStorageVolumes(virStoragePoolPtr pool) +{ + bool success = false; + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_HostDatastoreBrowserSearchResults *searchResultsList = NULL; + esxVI_HostDatastoreBrowserSearchResults *searchResults = NULL; + esxVI_FileInfo *fileInfo = NULL; + int count = 0; + + if (esxVI_LookupDatastoreContentByDatastoreName(priv->primary, pool->name, + &searchResultsList) < 0) { + goto cleanup; + } + + /* Interpret search result */ + for (searchResults = searchResultsList; searchResults != NULL; + searchResults = searchResults->_next) { + for (fileInfo = searchResults->file; fileInfo != NULL; + fileInfo = fileInfo->_next) { + ++count; + } + } + + success = true; + + cleanup: + esxVI_HostDatastoreBrowserSearchResults_Free(&searchResultsList); + + return success ? count : -1; +} + +static int +esxStorageBackendVMFSPoolListStorageVolumes(virStoragePoolPtr pool, char **const names, + int maxnames) + +{ + bool success = false; + esxPrivate *priv = pool->conn->storagePrivateData; + esxVI_HostDatastoreBrowserSearchResults *searchResultsList = NULL; + esxVI_HostDatastoreBrowserSearchResults *searchResults = NULL; + esxVI_FileInfo *fileInfo = NULL; + char *directoryAndFileName = NULL; + size_t length; + int count = 0; + int i; + + if (names == NULL || maxnames < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument")); + return -1; + } + + if (maxnames == 0) { + return 0; + } + + if (esxVI_LookupDatastoreContentByDatastoreName(priv->primary, pool->name, + &searchResultsList) < 0) { + goto cleanup; + } + + /* Interpret search result */ + for (searchResults = searchResultsList; searchResults != NULL; + searchResults = searchResults->_next) { + VIR_FREE(directoryAndFileName); + + if (esxUtil_ParseDatastorePath(searchResults->folderPath, NULL, NULL, + &directoryAndFileName) < 0) { + goto cleanup; + } + + /* Strip trailing separators */ + length = strlen(directoryAndFileName); + + while (length > 0 && directoryAndFileName[length - 1] == '/') { + directoryAndFileName[length - 1] = '\0'; + --length; + } + + /* Build volume names */ + for (fileInfo = searchResults->file; fileInfo != NULL; + fileInfo = fileInfo->_next) { + if (length < 1) { + names[count] = strdup(fileInfo->path); + + if (names[count] == NULL) { + virReportOOMError(); + goto cleanup; + } + } else if (virAsprintf(&names[count], "%s/%s", directoryAndFileName, + fileInfo->path) < 0) { + virReportOOMError(); + goto cleanup; + } + + ++count; + } + } + + success = true; + + cleanup: + if (! success) { + for (i = 0; i < count; ++i) { + VIR_FREE(names[i]); + } + + count = -1; + } + + esxVI_HostDatastoreBrowserSearchResults_Free(&searchResultsList); + VIR_FREE(directoryAndFileName); + + return count; + +} + +static virStorageVolPtr +esxStorageBackendVMFSVolumeLookupByName(virStoragePoolPtr pool, + const char *name) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = pool->conn->storagePrivateData; + char *datastorePath = NULL; + char *key = NULL; + + if (virAsprintf(&datastorePath, "[%s] %s", pool->name, name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (esxVI_LookupStorageVolumeKeyByDatastorePath(priv->primary, + datastorePath, &key) < 0) { + goto cleanup; + } + + volume = virGetStorageVol(pool->conn, pool->name, name, key); + + cleanup: + VIR_FREE(datastorePath); + VIR_FREE(key); + + return volume; +} + +static virStorageVolPtr +esxStorageBackendVMFSVolumeLookupByPath(virConnectPtr conn, const char *path) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = conn->storagePrivateData; + char *datastoreName = NULL; + char *directoryAndFileName = NULL; + char *key = NULL; + + if (esxUtil_ParseDatastorePath(path, &datastoreName, NULL, + &directoryAndFileName) < 0) { + goto cleanup; + } + + if (esxVI_LookupStorageVolumeKeyByDatastorePath(priv->primary, path, + &key) < 0) { + goto cleanup; + } + + volume = virGetStorageVol(conn, datastoreName, directoryAndFileName, key); + + cleanup: + VIR_FREE(datastoreName); + VIR_FREE(directoryAndFileName); + VIR_FREE(key); + + return volume; +} + + + +static virStorageVolPtr +esxStorageBackendVMFSVolumeLookupByKey(virConnectPtr conn, const char *key) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = conn->storagePrivateData; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *datastoreList = NULL; + esxVI_ObjectContent *datastore = NULL; + char *datastoreName = NULL; + esxVI_HostDatastoreBrowserSearchResults *searchResultsList = NULL; + esxVI_HostDatastoreBrowserSearchResults *searchResults = NULL; + char *directoryAndFileName = NULL; + size_t length; + char *datastorePath = NULL; + char *volumeName = NULL; + esxVI_FileInfo *fileInfo = NULL; + char *uuid_string = NULL; + char key_candidate[VIR_UUID_STRING_BUFLEN] = ""; + + if (STRPREFIX(key, "[")) { + /* Key is probably a datastore path */ + return esxStorageBackendVMFSVolumeLookupByPath(conn, key); + } + + if (!priv->primary->hasQueryVirtualDiskUuid) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("QueryVirtualDiskUuid not available, cannot lookup storage " + "volume by UUID")); + return NULL; + } + + /* Lookup all datastores */ + if (esxVI_String_AppendValueToList(&propertyNameList, "summary.name") < 0 || + esxVI_LookupDatastoreList(priv->primary, propertyNameList, + &datastoreList) < 0) { + goto cleanup; + } + + for (datastore = datastoreList; datastore != NULL; + datastore = datastore->_next) { + datastoreName = NULL; + + if (esxVI_GetStringValue(datastore, "summary.name", &datastoreName, + esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + /* Lookup datastore content */ + esxVI_HostDatastoreBrowserSearchResults_Free(&searchResultsList); + + if (esxVI_LookupDatastoreContentByDatastoreName + (priv->primary, datastoreName, &searchResultsList) < 0) { + goto cleanup; + } + + /* Interpret search result */ + for (searchResults = searchResultsList; searchResults != NULL; + searchResults = searchResults->_next) { + VIR_FREE(directoryAndFileName); + + if (esxUtil_ParseDatastorePath(searchResults->folderPath, NULL, + NULL, &directoryAndFileName) < 0) { + goto cleanup; + } + + /* Strip trailing separators */ + length = strlen(directoryAndFileName); + + while (length > 0 && directoryAndFileName[length - 1] == '/') { + directoryAndFileName[length - 1] = '\0'; + --length; + } + + /* Build datastore path and query the UUID */ + for (fileInfo = searchResults->file; fileInfo != NULL; + fileInfo = fileInfo->_next) { + VIR_FREE(datastorePath); + + if (length < 1) { + if (virAsprintf(&volumeName, "%s", + fileInfo->path) < 0) { + virReportOOMError(); + goto cleanup; + } + } else if (virAsprintf(&volumeName, "%s/%s", + directoryAndFileName, + fileInfo->path) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&datastorePath, "[%s] %s", datastoreName, + volumeName) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (esxVI_VmDiskFileInfo_DynamicCast(fileInfo) == NULL) { + /* Only a VirtualDisk has a UUID */ + continue; + } + + VIR_FREE(uuid_string); + + if (esxVI_QueryVirtualDiskUuid + (priv->primary, datastorePath, + priv->primary->datacenter->_reference, + &uuid_string) < 0) { + goto cleanup; + } + + if (esxUtil_ReformatUuid(uuid_string, key_candidate) < 0) { + goto cleanup; + } + + if (STREQ(key, key_candidate)) { + /* Found matching UUID */ + volume = virGetStorageVol(conn, datastoreName, + volumeName, key); + goto cleanup; + } + } + } + } + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&datastoreList); + esxVI_HostDatastoreBrowserSearchResults_Free(&searchResultsList); + VIR_FREE(directoryAndFileName); + VIR_FREE(datastorePath); + VIR_FREE(volumeName); + VIR_FREE(uuid_string); + + return volume; +} + +static virStorageVolPtr +esxStorageBackendVMFSVolumeCreateXML(virStoragePoolPtr pool, const char *xmldesc, + unsigned int flags) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = pool->conn->storagePrivateData; + virStoragePoolDef poolDef; + virStorageVolDefPtr def = NULL; + char *tmp; + char *unescapedDatastorePath = NULL; + char *unescapedDirectoryName = NULL; + char *unescapedDirectoryAndFileName = NULL; + char *directoryName = NULL; + char *fileName = NULL; + char *datastorePathWithoutFileName = NULL; + char *datastorePath = NULL; + esxVI_FileInfo *fileInfo = NULL; + esxVI_FileBackedVirtualDiskSpec *virtualDiskSpec = NULL; + esxVI_ManagedObjectReference *task = NULL; + esxVI_TaskInfoState taskInfoState; + char *taskInfoErrorMessage = NULL; + char *uuid_string = NULL; + char *key = NULL; + + virCheckFlags(0, NULL); + + memset(&poolDef, 0, sizeof(poolDef)); + + if (esxVI_EnsureSession(priv->primary) < 0) { + return NULL; + } + + if (esxStorageBackendVMFSPoolLookupType( + pool->conn, pool->name, &poolDef.type) < 0) { + return NULL; + } + + /* Parse config */ + def = virStorageVolDefParseString(&poolDef, xmldesc); + + if (def == NULL) { + goto cleanup; + } + + if (def->type != VIR_STORAGE_VOL_FILE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Creating non-file volumes is not supported")); + goto cleanup; + } + + /* Validate config */ + tmp = strrchr(def->name, '/'); + + if (tmp == NULL || *def->name == '/' || tmp[1] == '\0') { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Volume name '%s' doesn't have expected format " + "'<directory>/<file>'"), def->name); + goto cleanup; + } + + if (! virFileHasSuffix(def->name, ".vmdk")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Volume name '%s' has unsupported suffix, expecting '.vmdk'"), + def->name); + goto cleanup; + } + + if (virAsprintf(&unescapedDatastorePath, "[%s] %s", pool->name, + def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (def->target.format == VIR_STORAGE_FILE_VMDK) { + /* Parse and escape datastore path */ + if (esxUtil_ParseDatastorePath(unescapedDatastorePath, NULL, + &unescapedDirectoryName, + &unescapedDirectoryAndFileName) < 0) { + goto cleanup; + } + + directoryName = esxUtil_EscapeDatastoreItem(unescapedDirectoryName); + + if (directoryName == NULL) { + goto cleanup; + } + + fileName = esxUtil_EscapeDatastoreItem(unescapedDirectoryAndFileName + + strlen(unescapedDirectoryName) + 1); + + if (fileName == NULL) { + goto cleanup; + } + + if (virAsprintf(&datastorePathWithoutFileName, "[%s] %s", pool->name, + directoryName) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&datastorePath, "[%s] %s/%s", pool->name, directoryName, + fileName) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* Create directory, if it doesn't exist yet */ + if (esxVI_LookupFileInfoByDatastorePath + (priv->primary, datastorePathWithoutFileName, true, &fileInfo, + esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + if (fileInfo == NULL) { + if (esxVI_MakeDirectory(priv->primary, datastorePathWithoutFileName, + priv->primary->datacenter->_reference, + esxVI_Boolean_True) < 0) { + goto cleanup; + } + } + + /* Create VirtualDisk */ + if (esxVI_FileBackedVirtualDiskSpec_Alloc(&virtualDiskSpec) < 0 || + esxVI_Long_Alloc(&virtualDiskSpec->capacityKb) < 0) { + goto cleanup; + } + + /* From the vSphere API documentation about VirtualDiskType ... */ + if (def->allocation == def->capacity) { + /* + * "A preallocated disk has all space allocated at creation time + * and the space is zeroed on demand as the space is used." + */ + virtualDiskSpec->diskType = (char *)"preallocated"; + } else if (def->allocation == 0) { + /* + * "Space required for thin-provisioned virtual disk is allocated + * and zeroed on demand as the space is used." + */ + virtualDiskSpec->diskType = (char *)"thin"; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unsupported capacity-to-allocation relation")); + goto cleanup; + } + + /* + * FIXME: The adapter type is a required parameter, but there is no + * way to let the user specify it in the volume XML config. Therefore, + * default to 'busLogic' here. + */ + virtualDiskSpec->adapterType = (char *)"busLogic"; + + virtualDiskSpec->capacityKb->value = + VIR_DIV_UP(def->capacity, 1024); /* Scale from byte to kilobyte */ + + if (esxVI_CreateVirtualDisk_Task + (priv->primary, datastorePath, priv->primary->datacenter->_reference, + esxVI_VirtualDiskSpec_DynamicCast(virtualDiskSpec), &task) < 0 || + esxVI_WaitForTaskCompletion(priv->primary, task, NULL, + esxVI_Occurrence_None, + priv->parsedUri->autoAnswer, + &taskInfoState, + &taskInfoErrorMessage) < 0) { + goto cleanup; + } + + if (taskInfoState != esxVI_TaskInfoState_Success) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not create volume: %s"), + taskInfoErrorMessage); + goto cleanup; + } + + if (priv->primary->hasQueryVirtualDiskUuid) { + if (VIR_ALLOC_N(key, VIR_UUID_STRING_BUFLEN) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (esxVI_QueryVirtualDiskUuid(priv->primary, datastorePath, + priv->primary->datacenter->_reference, + &uuid_string) < 0) { + goto cleanup; + } + + if (esxUtil_ReformatUuid(uuid_string, key) < 0) { + goto cleanup; + } + } else { + /* Fall back to the path as key */ + if (esxVI_String_DeepCopyValue(&key, datastorePath) < 0) { + goto cleanup; + } + } + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Creation of %s volumes is not supported"), + virStorageFileFormatTypeToString(def->target.format)); + goto cleanup; + } + + volume = virGetStorageVol(pool->conn, pool->name, def->name, key); + + cleanup: + if (virtualDiskSpec != NULL) { + virtualDiskSpec->diskType = NULL; + virtualDiskSpec->adapterType = NULL; + } + + virStorageVolDefFree(def); + VIR_FREE(unescapedDatastorePath); + VIR_FREE(unescapedDirectoryName); + VIR_FREE(unescapedDirectoryAndFileName); + VIR_FREE(directoryName); + VIR_FREE(fileName); + VIR_FREE(datastorePathWithoutFileName); + VIR_FREE(datastorePath); + esxVI_FileInfo_Free(&fileInfo); + esxVI_FileBackedVirtualDiskSpec_Free(&virtualDiskSpec); + esxVI_ManagedObjectReference_Free(&task); + VIR_FREE(taskInfoErrorMessage); + VIR_FREE(uuid_string); + VIR_FREE(key); + + return volume; +} + +static virStorageVolPtr +esxStorageBackendVMFSVolumeCreateXMLFrom(virStoragePoolPtr pool, + const char *xmldesc, + virStorageVolPtr sourceVolume, + unsigned int flags) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = pool->conn->storagePrivateData; + virStoragePoolDef poolDef; + char *sourceDatastorePath = NULL; + virStorageVolDefPtr def = NULL; + char *tmp; + char *unescapedDatastorePath = NULL; + char *unescapedDirectoryName = NULL; + char *unescapedDirectoryAndFileName = NULL; + char *directoryName = NULL; + char *fileName = NULL; + char *datastorePathWithoutFileName = NULL; + char *datastorePath = NULL; + esxVI_FileInfo *fileInfo = NULL; + esxVI_ManagedObjectReference *task = NULL; + esxVI_TaskInfoState taskInfoState; + char *taskInfoErrorMessage = NULL; + char *uuid_string = NULL; + char *key = NULL; + + virCheckFlags(0, NULL); + + memset(&poolDef, 0, sizeof(poolDef)); + + if (esxVI_EnsureSession(priv->primary) < 0) { + return NULL; + } + + if (esxStorageBackendVMFSPoolLookupType( + pool->conn, pool->name, &poolDef.type) < 0) { + return NULL; + } + + if (virAsprintf(&sourceDatastorePath, "[%s] %s", sourceVolume->pool, + sourceVolume->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* Parse config */ + def = virStorageVolDefParseString(&poolDef, xmldesc); + + if (def == NULL) { + goto cleanup; + } + + if (def->type != VIR_STORAGE_VOL_FILE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Creating non-file volumes is not supported")); + goto cleanup; + } + + /* Validate config */ + tmp = strrchr(def->name, '/'); + + if (tmp == NULL || *def->name == '/' || tmp[1] == '\0') { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Volume name '%s' doesn't have expected format " + "'<directory>/<file>'"), def->name); + goto cleanup; + } + + if (! virFileHasSuffix(def->name, ".vmdk")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Volume name '%s' has unsupported suffix, expecting '.vmdk'"), + def->name); + goto cleanup; + } + + if (virAsprintf(&unescapedDatastorePath, "[%s] %s", pool->name, + def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (def->target.format == VIR_STORAGE_FILE_VMDK) { + /* Parse and escape datastore path */ + if (esxUtil_ParseDatastorePath(unescapedDatastorePath, NULL, + &unescapedDirectoryName, + &unescapedDirectoryAndFileName) < 0) { + goto cleanup; + } + + directoryName = esxUtil_EscapeDatastoreItem(unescapedDirectoryName); + + if (directoryName == NULL) { + goto cleanup; + } + + fileName = esxUtil_EscapeDatastoreItem(unescapedDirectoryAndFileName + + strlen(unescapedDirectoryName) + 1); + + if (fileName == NULL) { + goto cleanup; + } + + if (virAsprintf(&datastorePathWithoutFileName, "[%s] %s", pool->name, + directoryName) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&datastorePath, "[%s] %s/%s", pool->name, directoryName, + fileName) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* Create directory, if it doesn't exist yet */ + if (esxVI_LookupFileInfoByDatastorePath + (priv->primary, datastorePathWithoutFileName, true, &fileInfo, + esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + if (fileInfo == NULL) { + if (esxVI_MakeDirectory(priv->primary, datastorePathWithoutFileName, + priv->primary->datacenter->_reference, + esxVI_Boolean_True) < 0) { + goto cleanup; + } + } + + /* Copy VirtualDisk */ + if (esxVI_CopyVirtualDisk_Task(priv->primary, sourceDatastorePath, + priv->primary->datacenter->_reference, + datastorePath, + priv->primary->datacenter->_reference, + NULL, esxVI_Boolean_False, &task) < 0 || + esxVI_WaitForTaskCompletion(priv->primary, task, NULL, + esxVI_Occurrence_None, + priv->parsedUri->autoAnswer, + &taskInfoState, + &taskInfoErrorMessage) < 0) { + goto cleanup; + } + + if (taskInfoState != esxVI_TaskInfoState_Success) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not copy volume: %s"), + taskInfoErrorMessage); + goto cleanup; + } + + if (priv->primary->hasQueryVirtualDiskUuid) { + if (VIR_ALLOC_N(key, VIR_UUID_STRING_BUFLEN) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (esxVI_QueryVirtualDiskUuid(priv->primary, datastorePath, + priv->primary->datacenter->_reference, + &uuid_string) < 0) { + goto cleanup; + } + + if (esxUtil_ReformatUuid(uuid_string, key) < 0) { + goto cleanup; + } + } else { + /* Fall back to the path as key */ + if (esxVI_String_DeepCopyValue(&key, datastorePath) < 0) { + goto cleanup; + } + } + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Creation of %s volumes is not supported"), + virStorageFileFormatTypeToString(def->target.format)); + goto cleanup; + } + + volume = virGetStorageVol(pool->conn, pool->name, def->name, key); + + cleanup: + VIR_FREE(sourceDatastorePath); + virStorageVolDefFree(def); + VIR_FREE(unescapedDatastorePath); + VIR_FREE(unescapedDirectoryName); + VIR_FREE(unescapedDirectoryAndFileName); + VIR_FREE(directoryName); + VIR_FREE(fileName); + VIR_FREE(datastorePathWithoutFileName); + VIR_FREE(datastorePath); + esxVI_FileInfo_Free(&fileInfo); + esxVI_ManagedObjectReference_Free(&task); + VIR_FREE(taskInfoErrorMessage); + VIR_FREE(uuid_string); + VIR_FREE(key); + + return volume; +} + + + +static char * +esxStorageBackendVMFSVolumeGetXMLDesc(virStorageVolPtr volume, unsigned int flags) +{ + esxPrivate *priv = volume->conn->storagePrivateData; + virStoragePoolDef pool; + char *datastorePath = NULL; + esxVI_FileInfo *fileInfo = NULL; + esxVI_VmDiskFileInfo *vmDiskFileInfo = NULL; + esxVI_IsoImageFileInfo *isoImageFileInfo = NULL; + esxVI_FloppyImageFileInfo *floppyImageFileInfo = NULL; + virStorageVolDef def; + char *xml = NULL; + + virCheckFlags(0, NULL); + + memset(&pool, 0, sizeof(pool)); + memset(&def, 0, sizeof(def)); + + if (esxStorageBackendVMFSPoolLookupType( + volume->conn, volume->pool, &pool.type) < 0) { + return NULL; + } + + /* Lookup file info */ + if (virAsprintf(&datastorePath, "[%s] %s", volume->pool, volume->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (esxVI_LookupFileInfoByDatastorePath(priv->primary, datastorePath, + false, &fileInfo, + esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + vmDiskFileInfo = esxVI_VmDiskFileInfo_DynamicCast(fileInfo); + isoImageFileInfo = esxVI_IsoImageFileInfo_DynamicCast(fileInfo); + floppyImageFileInfo = esxVI_FloppyImageFileInfo_DynamicCast(fileInfo); + + def.name = volume->name; + + if (esxVI_LookupStorageVolumeKeyByDatastorePath(priv->primary, datastorePath, + &def.key) < 0) { + goto cleanup; + } + + def.type = VIR_STORAGE_VOL_FILE; + def.target.path = datastorePath; + + if (vmDiskFileInfo != NULL) { + def.capacity = vmDiskFileInfo->capacityKb->value * 1024; /* Scale from kilobyte to byte */ + def.allocation = vmDiskFileInfo->fileSize->value; + + def.target.format = VIR_STORAGE_FILE_VMDK; + } else if (isoImageFileInfo != NULL) { + def.capacity = fileInfo->fileSize->value; + def.allocation = fileInfo->fileSize->value; + + def.target.format = VIR_STORAGE_FILE_ISO; + } else if (floppyImageFileInfo != NULL) { + def.capacity = fileInfo->fileSize->value; + def.allocation = fileInfo->fileSize->value; + + def.target.format = VIR_STORAGE_FILE_RAW; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("File '%s' has unknown type"), datastorePath); + goto cleanup; + } + + xml = virStorageVolDefFormat(&pool, &def); + + cleanup: + VIR_FREE(datastorePath); + esxVI_FileInfo_Free(&fileInfo); + VIR_FREE(def.key); + + return xml; +} + + + +static int +esxStorageBackendVMFSVolumeDelete(virStorageVolPtr volume, unsigned int flags) +{ + int result = -1; + esxPrivate *priv = volume->conn->storagePrivateData; + char *datastorePath = NULL; + esxVI_ManagedObjectReference *task = NULL; + esxVI_TaskInfoState taskInfoState; + char *taskInfoErrorMessage = NULL; + + virCheckFlags(0, -1); + + if (virAsprintf(&datastorePath, "[%s] %s", volume->pool, volume->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (esxVI_DeleteVirtualDisk_Task(priv->primary, datastorePath, + priv->primary->datacenter->_reference, + &task) < 0 || + esxVI_WaitForTaskCompletion(priv->primary, task, NULL, + esxVI_Occurrence_None, + priv->parsedUri->autoAnswer, + &taskInfoState, &taskInfoErrorMessage) < 0) { + goto cleanup; + } + + if (taskInfoState != esxVI_TaskInfoState_Success) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not delete volume: %s"), + taskInfoErrorMessage); + goto cleanup; + } + + result = 0; + + cleanup: + VIR_FREE(datastorePath); + esxVI_ManagedObjectReference_Free(&task); + VIR_FREE(taskInfoErrorMessage); + + return result; + +} + + + +static int +esxStorageBackendVMFSVolumeWipe(virStorageVolPtr volume, unsigned int flags) +{ + int result = -1; + esxPrivate *priv = volume->conn->storagePrivateData; + char *datastorePath = NULL; + esxVI_ManagedObjectReference *task = NULL; + esxVI_TaskInfoState taskInfoState; + char *taskInfoErrorMessage = NULL; + + virCheckFlags(0, -1); + + if (virAsprintf(&datastorePath, "[%s] %s", volume->pool, volume->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (esxVI_ZeroFillVirtualDisk_Task(priv->primary, datastorePath, + priv->primary->datacenter->_reference, + &task) < 0 || + esxVI_WaitForTaskCompletion(priv->primary, task, NULL, + esxVI_Occurrence_None, + priv->parsedUri->autoAnswer, + &taskInfoState, &taskInfoErrorMessage) < 0) { + goto cleanup; + } + + if (taskInfoState != esxVI_TaskInfoState_Success) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not wipe volume: %s"), + taskInfoErrorMessage); + goto cleanup; + } + + result = 0; + + cleanup: + VIR_FREE(datastorePath); + esxVI_ManagedObjectReference_Free(&task); + VIR_FREE(taskInfoErrorMessage); + + return result; + +} + + +static char* +esxStorageBackendVMFSVolumeGetPath(virStorageVolPtr volume) +{ + char *path; + + if (virAsprintf(&path, "[%s] %s", volume->pool, volume->name) < 0) { + virReportOOMError(); + return NULL; + } + + return path; + +} + + + + +virStorageDriver esxStorageBackendVMFSDrv = { + .name = "ESX VMFS backend", + .open = NULL, /* 0.7.6 */ + .close = NULL, /* 0.7.6 */ + .numOfPools = esxStorageBackendVMFSNumberOfStoragePools, /* 0.8.2 */ + .listPools = esxStorageBackendVMFSListStoragePools, /* 0.8.2 */ + .poolLookupByName = esxStorageBackendVMFSPoolLookupByName, /* 0.8.2 */ + .poolLookupByUUID = esxStorageBackendVMFSPoolLookupByUUID, /* 0.8.2 */ + .poolRefresh = esxStorageBackendVMFSPoolRefresh, /* 0.8.2 */ + .poolGetInfo = esxStorageBackendVMFSPoolGetInfo, /* 0.8.2 */ + .poolGetXMLDesc = esxStorageBackendVMFSPoolGetXMLDesc, /* 0.8.2 */ + .poolNumOfVolumes = esxStorageBackendVMFSPoolNumberOfStorageVolumes, /* 0.8.4 */ + .poolListVolumes = esxStorageBackendVMFSPoolListStorageVolumes, /* 0.8.4 */ + .volLookupByName = esxStorageBackendVMFSVolumeLookupByName, /* x.x.x */ + .volLookupByKey = esxStorageBackendVMFSVolumeLookupByKey, /* 0.8.4 */ + .volLookupByPath = esxStorageBackendVMFSVolumeLookupByPath, /* 0.8.4 */ + .volCreateXML = esxStorageBackendVMFSVolumeCreateXML, /* 0.8.4 */ + .volCreateXMLFrom = esxStorageBackendVMFSVolumeCreateXMLFrom, /* 0.8.7 */ + .volGetXMLDesc = esxStorageBackendVMFSVolumeGetXMLDesc, /* 0.8.4 */ + .volDelete = esxStorageBackendVMFSVolumeDelete, /* 0.8.7 */ + .volWipe = esxStorageBackendVMFSVolumeWipe, /* 0.8.7 */ + .volGetPath = esxStorageBackendVMFSVolumeGetPath, /* 0.10.0 */ + +}; diff --git a/src/esx/esx_storage_backend_vmfs.h b/src/esx/esx_storage_backend_vmfs.h new file mode 100644 index 0000000..d3adf73 --- /dev/null +++ b/src/esx/esx_storage_backend_vmfs.h @@ -0,0 +1,31 @@ +/* + * esx_storage_backend_vmfs.h: ESX storage backend for VMFS datastores + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * + * 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 + * + * Author: Ata E Husain Bohra (ata.husain@hotmail.com) + */ + +#ifndef __ESX_STORAGE_BACKEND_VMFS_H__ +# define __ESX_STORAGE_BACKEND_VMFS_H__ + +#include "driver.h" + +extern virStorageDriver esxStorageBackendVMFSDrv; + +#endif /* __ESX_STORAGE_BACKEND_VMFS_H__ */ + diff --git a/src/esx/esx_storage_driver.c b/src/esx/esx_storage_driver.c index 348bd62..d4e81f3 100644 --- a/src/esx/esx_storage_driver.c +++ b/src/esx/esx_storage_driver.c @@ -24,82 +24,67 @@ #include <config.h> -#include "md5.h" -#include "verify.h" #include "internal.h" #include "util.h" #include "memory.h" #include "logging.h" -#include "uuid.h" #include "storage_conf.h" -#include "storage_file.h" #include "esx_private.h" #include "esx_storage_driver.h" +#include "esx_storage_backend_iscsi.h" +#include "esx_storage_backend_vmfs.h" #include "esx_vi.h" #include "esx_vi_methods.h" #include "esx_util.h" #define VIR_FROM_THIS VIR_FROM_ESX -/* - * The UUID of a storage pool is the MD5 sum of it's mount path. Therefore, - * verify that UUID and MD5 sum match in size, because we rely on that. +/** + * ESX storage driver implements a facade pattern; + * the driver exposes the routines supported by Libvirt + * public interface to manage ESX storage devices. Internally + * it uses backend drivers to perform the required task. */ -verify(MD5_DIGEST_SIZE == VIR_UUID_BUFLEN); +enum { + ISCSI = 0, + VMFS, + LAST_DRIVER +}; +static virStorageDriverPtr backendDrv[LAST_DRIVER] = {NULL}; static int -esxStoragePoolLookupType(esxVI_Context *ctx, const char *poolName, - int *poolType) +esxStorageGetBackendDriver(virConnectPtr conn, const char *name, + virStorageDriverPtr *backend) { int result = -1; - esxVI_String *propertyNameList = NULL; - esxVI_ObjectContent *datastore = NULL; - esxVI_DynamicProperty *dynamicProperty = NULL; - esxVI_DatastoreInfo *datastoreInfo = NULL; - - if (esxVI_String_AppendValueToList(&propertyNameList, "info") < 0 || - esxVI_LookupDatastoreByName(ctx, poolName, propertyNameList, &datastore, - esxVI_Occurrence_RequiredItem) < 0) { - goto cleanup; - } + int i = 0; - for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; - dynamicProperty = dynamicProperty->_next) { - if (STREQ(dynamicProperty->name, "info")) { - if (esxVI_DatastoreInfo_CastFromAnyType(dynamicProperty->val, - &datastoreInfo) < 0) { - goto cleanup; - } + virCheckNonNullArgGoto(backend, cleanup); + for (i = 0; i < LAST_DRIVER; ++i) { + const virStoragePoolPtr tempPool = + backendDrv[i]->poolLookupByName(conn, name); + if (tempPool != NULL) { + *backend = backendDrv[i]; break; } } - if (esxVI_LocalDatastoreInfo_DynamicCast(datastoreInfo) != NULL) { - *poolType = VIR_STORAGE_POOL_DIR; - } else if (esxVI_NasDatastoreInfo_DynamicCast(datastoreInfo) != NULL) { - *poolType = VIR_STORAGE_POOL_NETFS; - } else if (esxVI_VmfsDatastoreInfo_DynamicCast(datastoreInfo) != NULL) { - *poolType = VIR_STORAGE_POOL_FS; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("DatastoreInfo has unexpected type")); + if (*backend == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find storage pool with name '%s'"), name); goto cleanup; } result = 0; - cleanup: - esxVI_String_Free(&propertyNameList); - esxVI_ObjectContent_Free(&datastore); - esxVI_DatastoreInfo_Free(&datastoreInfo); +cleanup: return result; -} - +} static virDrvOpenStatus esxStorageOpen(virConnectPtr conn, @@ -114,6 +99,10 @@ esxStorageOpen(virConnectPtr conn, conn->storagePrivateData = conn->privateData; + /* set backend driver pointers */ + backendDrv[ISCSI] = &esxStorageBackendISCSIDrv; + backendDrv[VMFS] = &esxStorageBackendVMFSDrv; + return VIR_DRV_OPEN_SUCCESS; } @@ -122,6 +111,13 @@ esxStorageOpen(virConnectPtr conn, static int esxStorageClose(virConnectPtr conn) { + int i = 0; + + /* reset the backend driver pointers */ + for (i = 0; i < LAST_DRIVER; ++i) { + backendDrv[i] = NULL; + } + conn->storagePrivateData = NULL; return 0; @@ -132,27 +128,28 @@ esxStorageClose(virConnectPtr conn) static int esxNumberOfStoragePools(virConnectPtr conn) { - int count = 0; esxPrivate *priv = conn->storagePrivateData; - esxVI_ObjectContent *datastoreList = NULL; - esxVI_ObjectContent *datastore = NULL; + int count = 0; + int i = 0; + bool success = false; if (esxVI_EnsureSession(priv->primary) < 0) { - return -1; + goto cleanup; } - if (esxVI_LookupDatastoreList(priv->primary, NULL, &datastoreList) < 0) { - return -1; + for (i = 0; i < LAST_DRIVER; ++i) { + int tempCount = backendDrv[i]->numOfPools(conn); + if (tempCount < 0) { + goto cleanup; + } + count += tempCount; } - for (datastore = datastoreList; datastore != NULL; - datastore = datastore->_next) { - ++count; - } + success = true; - esxVI_ObjectContent_Free(&datastoreList); + cleanup: - return count; + return success ? count : -1; } @@ -162,56 +159,32 @@ esxListStoragePools(virConnectPtr conn, char **const names, int maxnames) { bool success = false; esxPrivate *priv = conn->storagePrivateData; - esxVI_String *propertyNameList = NULL; - esxVI_DynamicProperty *dynamicProperty = NULL; - esxVI_ObjectContent *datastoreList = NULL; - esxVI_ObjectContent *datastore = NULL; int count = 0; - int i; + int i = 0; if (maxnames == 0) { return 0; } if (esxVI_EnsureSession(priv->primary) < 0) { - return -1; - } - - if (esxVI_String_AppendValueToList(&propertyNameList, - "summary.name") < 0 || - esxVI_LookupDatastoreList(priv->primary, propertyNameList, - &datastoreList) < 0) { goto cleanup; } - for (datastore = datastoreList; datastore != NULL; - datastore = datastore->_next) { - for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; - dynamicProperty = dynamicProperty->_next) { - if (STREQ(dynamicProperty->name, "summary.name")) { - if (esxVI_AnyType_ExpectType(dynamicProperty->val, - esxVI_Type_String) < 0) { - goto cleanup; - } - - names[count] = strdup(dynamicProperty->val->string); - - if (names[count] == NULL) { - virReportOOMError(); - goto cleanup; - } - - ++count; - break; - } else { - VIR_WARN("Unexpected '%s' property", dynamicProperty->name); - } + for (i = 0; i < LAST_DRIVER; ++i) { + int tempCount = + backendDrv[i]->listPools(conn, &names[count], maxnames-count); + + if (tempCount < 0) { + goto cleanup; } + + count += tempCount; } success = true; cleanup: + if (! success) { for (i = 0; i < count; ++i) { VIR_FREE(names[i]); @@ -220,10 +193,8 @@ esxListStoragePools(virConnectPtr conn, char **const names, int maxnames) count = -1; } - esxVI_String_Free(&propertyNameList); - esxVI_ObjectContent_Free(&datastoreList); - return count; + } @@ -252,43 +223,31 @@ static virStoragePoolPtr esxStoragePoolLookupByName(virConnectPtr conn, const char *name) { esxPrivate *priv = conn->storagePrivateData; - esxVI_ObjectContent *datastore = NULL; - esxVI_DatastoreHostMount *hostMount = NULL; - unsigned char md5[MD5_DIGEST_SIZE]; /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ virStoragePoolPtr pool = NULL; + int i = 0; - if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } + virCheckNonNullArgGoto(name, cleanup); - if (esxVI_LookupDatastoreByName(priv->primary, name, NULL, &datastore, - esxVI_Occurrence_RequiredItem) < 0) { + if (esxVI_EnsureSession(priv->primary) < 0) { goto cleanup; } - /* - * Datastores don't have a UUID, but we can use the 'host.mountInfo.path' - * property as source for a UUID. The mount path is unique per host and - * cannot change during the lifetime of the datastore. - * - * The MD5 sum of the mount path can be used as UUID, assuming MD5 is - * considered to be collision-free enough for this use case. - */ - if (esxVI_LookupDatastoreHostMount(priv->primary, datastore->obj, - &hostMount) < 0) { - goto cleanup; + for (i = 0; i < LAST_DRIVER; ++i) { + pool = backendDrv[i]->poolLookupByName(conn, name); + if (pool != NULL) { + break; + } } - md5_buffer(hostMount->mountInfo->path, - strlen(hostMount->mountInfo->path), md5); - - pool = virGetStoragePool(conn, name, md5); + if (pool == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find storage pool with name '%s'"), name); + } - cleanup: - esxVI_ObjectContent_Free(&datastore); - esxVI_DatastoreHostMount_Free(&hostMount); +cleanup: return pool; + } @@ -297,65 +256,31 @@ static virStoragePoolPtr esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid) { esxPrivate *priv = conn->storagePrivateData; - esxVI_String *propertyNameList = NULL; - esxVI_ObjectContent *datastoreList = NULL; - esxVI_ObjectContent *datastore = NULL; - esxVI_DatastoreHostMount *hostMount = NULL; - unsigned char md5[MD5_DIGEST_SIZE]; /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ - char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; - char *name = NULL; virStoragePoolPtr pool = NULL; + int i = 0; - if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } + virCheckNonNullArgGoto(uuid, cleanup); - if (esxVI_String_AppendValueToList(&propertyNameList, "summary.name") < 0 || - esxVI_LookupDatastoreList(priv->primary, propertyNameList, - &datastoreList) < 0) { + if (esxVI_EnsureSession(priv->primary) < 0) { goto cleanup; } - for (datastore = datastoreList; datastore != NULL; - datastore = datastore->_next) { - esxVI_DatastoreHostMount_Free(&hostMount); - - if (esxVI_LookupDatastoreHostMount(priv->primary, datastore->obj, - &hostMount) < 0) { - goto cleanup; - } - - md5_buffer(hostMount->mountInfo->path, - strlen(hostMount->mountInfo->path), md5); - - if (memcmp(uuid, md5, VIR_UUID_BUFLEN) == 0) { + for (i = 0; i < LAST_DRIVER; ++i) { + pool = backendDrv[i]->poolLookupByUUID(conn, uuid); + if (pool != NULL) { break; } } - if (datastore == NULL) { - virUUIDFormat(uuid, uuid_string); - - virReportError(VIR_ERR_NO_STORAGE_POOL, - _("Could not find datastore with UUID '%s'"), - uuid_string); - - goto cleanup; - } - - if (esxVI_GetStringValue(datastore, "summary.name", &name, - esxVI_Occurrence_RequiredItem) < 0) { - goto cleanup; + if (pool == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find storage pool with uuid '%s'"), uuid); } - pool = virGetStoragePool(conn, name, uuid); - - cleanup: - esxVI_String_Free(&propertyNameList); - esxVI_ObjectContent_Free(&datastoreList); - esxVI_DatastoreHostMount_Free(&hostMount); +cleanup: return pool; + } @@ -373,26 +298,23 @@ esxStoragePoolRefresh(virStoragePoolPtr pool, unsigned int flags) { int result = -1; esxPrivate *priv = pool->conn->storagePrivateData; - esxVI_ObjectContent *datastore = NULL; - - virCheckFlags(0, -1); + virStorageDriverPtr backend = NULL; if (esxVI_EnsureSession(priv->primary) < 0) { - return -1; + goto cleanup; } - if (esxVI_LookupDatastoreByName(priv->primary, pool->name, NULL, &datastore, - esxVI_Occurrence_RequiredItem) < 0 || - esxVI_RefreshDatastore(priv->primary, datastore->obj) < 0) { + if (esxStorageGetBackendDriver(pool->conn, pool->name, &backend) < 0 || + backend->poolRefresh(pool, flags) < 0) { goto cleanup; } result = 0; cleanup: - esxVI_ObjectContent_Free(&datastore); return result; + } @@ -402,63 +324,25 @@ esxStoragePoolGetInfo(virStoragePoolPtr pool, virStoragePoolInfoPtr info) { int result = -1; esxPrivate *priv = pool->conn->storagePrivateData; - esxVI_String *propertyNameList = NULL; - esxVI_ObjectContent *datastore = NULL; - esxVI_DynamicProperty *dynamicProperty = NULL; - esxVI_Boolean accessible = esxVI_Boolean_Undefined; + virStorageDriverPtr backend = NULL; memset(info, 0, sizeof(*info)); if (esxVI_EnsureSession(priv->primary) < 0) { - return -1; - } - - if (esxVI_String_AppendValueListToList(&propertyNameList, - "summary.accessible\0" - "summary.capacity\0" - "summary.freeSpace\0") < 0 || - esxVI_LookupDatastoreByName(priv->primary, pool->name, - propertyNameList, &datastore, - esxVI_Occurrence_RequiredItem) < 0 || - esxVI_GetBoolean(datastore, "summary.accessible", - &accessible, esxVI_Occurrence_RequiredItem) < 0) { goto cleanup; } - if (accessible == esxVI_Boolean_True) { - info->state = VIR_STORAGE_POOL_RUNNING; - - for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; - dynamicProperty = dynamicProperty->_next) { - if (STREQ(dynamicProperty->name, "summary.capacity")) { - if (esxVI_AnyType_ExpectType(dynamicProperty->val, - esxVI_Type_Long) < 0) { - goto cleanup; - } - - info->capacity = dynamicProperty->val->int64; - } else if (STREQ(dynamicProperty->name, "summary.freeSpace")) { - if (esxVI_AnyType_ExpectType(dynamicProperty->val, - esxVI_Type_Long) < 0) { - goto cleanup; - } - - info->available = dynamicProperty->val->int64; - } - } - - info->allocation = info->capacity - info->available; - } else { - info->state = VIR_STORAGE_POOL_INACCESSIBLE; + if (esxStorageGetBackendDriver(pool->conn, pool->name, &backend) < 0 || + backend->poolGetInfo(pool, info) < 0) { + goto cleanup; } result = 0; cleanup: - esxVI_String_Free(&propertyNameList); - esxVI_ObjectContent_Free(&datastore); return result; + } @@ -466,123 +350,24 @@ esxStoragePoolGetInfo(virStoragePoolPtr pool, virStoragePoolInfoPtr info) static char * esxStoragePoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags) { - esxPrivate *priv = pool->conn->storagePrivateData; - esxVI_String *propertyNameList = NULL; - esxVI_ObjectContent *datastore = NULL; - esxVI_DatastoreHostMount *hostMount = NULL; - esxVI_DynamicProperty *dynamicProperty = NULL; - esxVI_Boolean accessible = esxVI_Boolean_Undefined; - virStoragePoolDef def; - esxVI_DatastoreInfo *info = NULL; - esxVI_NasDatastoreInfo *nasInfo = NULL; char *xml = NULL; - - virCheckFlags(0, NULL); - - memset(&def, 0, sizeof(def)); + esxPrivate *priv = pool->conn->storagePrivateData; + virStorageDriverPtr backend = NULL; if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } - - if (esxVI_String_AppendValueListToList(&propertyNameList, - "summary.accessible\0" - "summary.capacity\0" - "summary.freeSpace\0" - "info\0") < 0 || - esxVI_LookupDatastoreByName(priv->primary, pool->name, - propertyNameList, &datastore, - esxVI_Occurrence_RequiredItem) < 0 || - esxVI_GetBoolean(datastore, "summary.accessible", - &accessible, esxVI_Occurrence_RequiredItem) < 0 || - esxVI_LookupDatastoreHostMount(priv->primary, datastore->obj, - &hostMount) < 0) { goto cleanup; } - def.name = pool->name; - memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN); - - def.target.path = hostMount->mountInfo->path; - - if (accessible == esxVI_Boolean_True) { - for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; - dynamicProperty = dynamicProperty->_next) { - if (STREQ(dynamicProperty->name, "summary.capacity")) { - if (esxVI_AnyType_ExpectType(dynamicProperty->val, - esxVI_Type_Long) < 0) { - goto cleanup; - } - - def.capacity = dynamicProperty->val->int64; - } else if (STREQ(dynamicProperty->name, "summary.freeSpace")) { - if (esxVI_AnyType_ExpectType(dynamicProperty->val, - esxVI_Type_Long) < 0) { - goto cleanup; - } - - def.available = dynamicProperty->val->int64; - } - } - - def.allocation = def.capacity - def.available; - } - - for (dynamicProperty = datastore->propSet; dynamicProperty != NULL; - dynamicProperty = dynamicProperty->_next) { - if (STREQ(dynamicProperty->name, "info")) { - if (esxVI_DatastoreInfo_CastFromAnyType(dynamicProperty->val, - &info) < 0) { - goto cleanup; - } - - break; - } - } - - /* See vSphere API documentation about HostDatastoreSystem for details */ - if (esxVI_LocalDatastoreInfo_DynamicCast(info) != NULL) { - def.type = VIR_STORAGE_POOL_DIR; - } else if ((nasInfo = esxVI_NasDatastoreInfo_DynamicCast(info)) != NULL) { - if (VIR_ALLOC_N(def.source.hosts, 1) < 0) { - virReportOOMError(); - goto cleanup; - } - def.type = VIR_STORAGE_POOL_NETFS; - def.source.hosts[0].name = nasInfo->nas->remoteHost; - def.source.dir = nasInfo->nas->remotePath; - - if (STRCASEEQ(nasInfo->nas->type, "NFS")) { - def.source.format = VIR_STORAGE_POOL_NETFS_NFS; - } else if (STRCASEEQ(nasInfo->nas->type, "CIFS")) { - def.source.format = VIR_STORAGE_POOL_NETFS_CIFS; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Datastore has unexpected type '%s'"), - nasInfo->nas->type); - goto cleanup; - } - } else if (esxVI_VmfsDatastoreInfo_DynamicCast(info) != NULL) { - def.type = VIR_STORAGE_POOL_FS; - /* - * FIXME: I'm not sure how to represent the source and target of a - * VMFS based datastore in libvirt terms - */ - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("DatastoreInfo has unexpected type")); + if (esxStorageGetBackendDriver(pool->conn, pool->name, &backend) < 0) { goto cleanup; } - xml = virStoragePoolDefFormat(&def); + xml = backend->poolGetXMLDesc(pool, flags); cleanup: - esxVI_String_Free(&propertyNameList); - esxVI_ObjectContent_Free(&datastore); - esxVI_DatastoreHostMount_Free(&hostMount); - esxVI_DatastoreInfo_Free(&info); return xml; + } @@ -622,33 +407,25 @@ esxStoragePoolNumberOfStorageVolumes(virStoragePoolPtr pool) { bool success = false; esxPrivate *priv = pool->conn->storagePrivateData; - esxVI_HostDatastoreBrowserSearchResults *searchResultsList = NULL; - esxVI_HostDatastoreBrowserSearchResults *searchResults = NULL; - esxVI_FileInfo *fileInfo = NULL; + virStorageDriverPtr backend = NULL; int count = 0; if (esxVI_EnsureSession(priv->primary) < 0) { - return -1; + goto cleanup; } - if (esxVI_LookupDatastoreContentByDatastoreName(priv->primary, pool->name, - &searchResultsList) < 0) { + if (esxStorageGetBackendDriver(pool->conn, pool->name, &backend) < 0) { goto cleanup; } - /* Interpret search result */ - for (searchResults = searchResultsList; searchResults != NULL; - searchResults = searchResults->_next) { - for (fileInfo = searchResults->file; fileInfo != NULL; - fileInfo = fileInfo->_next) { - ++count; - } + count = backend->poolNumOfVolumes(pool); + if (count < 0) { + goto cleanup; } success = true; cleanup: - esxVI_HostDatastoreBrowserSearchResults_Free(&searchResultsList); return success ? count : -1; } @@ -659,87 +436,25 @@ static int esxStoragePoolListStorageVolumes(virStoragePoolPtr pool, char **const names, int maxnames) { - bool success = false; + int result = -1; esxPrivate *priv = pool->conn->storagePrivateData; - esxVI_HostDatastoreBrowserSearchResults *searchResultsList = NULL; - esxVI_HostDatastoreBrowserSearchResults *searchResults = NULL; - esxVI_FileInfo *fileInfo = NULL; - char *directoryAndFileName = NULL; - size_t length; - int count = 0; - int i; - - if (names == NULL || maxnames < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument")); - return -1; - } - - if (maxnames == 0) { - return 0; - } + virStorageDriverPtr backend = NULL; if (esxVI_EnsureSession(priv->primary) < 0) { - return -1; - } - - if (esxVI_LookupDatastoreContentByDatastoreName(priv->primary, pool->name, - &searchResultsList) < 0) { goto cleanup; } - /* Interpret search result */ - for (searchResults = searchResultsList; searchResults != NULL; - searchResults = searchResults->_next) { - VIR_FREE(directoryAndFileName); - - if (esxUtil_ParseDatastorePath(searchResults->folderPath, NULL, NULL, - &directoryAndFileName) < 0) { - goto cleanup; - } - - /* Strip trailing separators */ - length = strlen(directoryAndFileName); - - while (length > 0 && directoryAndFileName[length - 1] == '/') { - directoryAndFileName[length - 1] = '\0'; - --length; - } - - /* Build volume names */ - for (fileInfo = searchResults->file; fileInfo != NULL; - fileInfo = fileInfo->_next) { - if (length < 1) { - names[count] = strdup(fileInfo->path); - - if (names[count] == NULL) { - virReportOOMError(); - goto cleanup; - } - } else if (virAsprintf(&names[count], "%s/%s", directoryAndFileName, - fileInfo->path) < 0) { - virReportOOMError(); - goto cleanup; - } - - ++count; - } + if (esxStorageGetBackendDriver(pool->conn, pool->name, &backend) < 0 || + backend->poolListVolumes(pool, names, maxnames) < 0) { + goto cleanup; } - success = true; + result = 0; cleanup: - if (! success) { - for (i = 0; i < count; ++i) { - VIR_FREE(names[i]); - } - count = -1; - } - - esxVI_HostDatastoreBrowserSearchResults_Free(&searchResultsList); - VIR_FREE(directoryAndFileName); + return result; - return count; } @@ -749,30 +464,24 @@ esxStorageVolumeLookupByName(virStoragePoolPtr pool, const char *name) { virStorageVolPtr volume = NULL; esxPrivate *priv = pool->conn->storagePrivateData; - char *datastorePath = NULL; - char *key = NULL; + virStorageDriverPtr backend = NULL; - if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } + virCheckNonNullArgGoto(name, cleanup); - if (virAsprintf(&datastorePath, "[%s] %s", pool->name, name) < 0) { - virReportOOMError(); + if (esxVI_EnsureSession(priv->primary) < 0) { goto cleanup; } - if (esxVI_LookupStorageVolumeKeyByDatastorePath(priv->primary, - datastorePath, &key) < 0) { + if (esxStorageGetBackendDriver(pool->conn, pool->name, &backend) < 0) { goto cleanup; } - volume = virGetStorageVol(pool->conn, pool->name, name, key); + volume = backend->volLookupByName(pool, name); cleanup: - VIR_FREE(datastorePath); - VIR_FREE(key); return volume; + } @@ -782,32 +491,33 @@ esxStorageVolumeLookupByPath(virConnectPtr conn, const char *path) { virStorageVolPtr volume = NULL; esxPrivate *priv = conn->storagePrivateData; - char *datastoreName = NULL; - char *directoryAndFileName = NULL; - char *key = NULL; - if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } + virCheckNonNullArgGoto(path, cleanup); - if (esxUtil_ParseDatastorePath(path, &datastoreName, NULL, - &directoryAndFileName) < 0) { + if (esxVI_EnsureSession(priv->primary) < 0) { goto cleanup; } - if (esxVI_LookupStorageVolumeKeyByDatastorePath(priv->primary, path, - &key) < 0) { - goto cleanup; + /* FIXME: calling backend blindly may set unwanted error codes + * + * VMFS Datastore path follows cannonical format i.e.: + * [<datastore_name>] <file_path> + * WHEREAS + * iSCSI LUNs device path follows normal linux path convention + */ + if (STRPREFIX(path, "[")) { + volume = backendDrv[VMFS]->volLookupByPath(conn, path); + } else if (STRPREFIX(path, "/")) { + volume = backendDrv[ISCSI]->volLookupByPath(conn, path); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected volume path format: %s"), path); } - volume = virGetStorageVol(conn, datastoreName, directoryAndFileName, key); - - cleanup: - VIR_FREE(datastoreName); - VIR_FREE(directoryAndFileName); - VIR_FREE(key); +cleanup: return volume; + } @@ -817,140 +527,29 @@ esxStorageVolumeLookupByKey(virConnectPtr conn, const char *key) { virStorageVolPtr volume = NULL; esxPrivate *priv = conn->storagePrivateData; - esxVI_String *propertyNameList = NULL; - esxVI_ObjectContent *datastoreList = NULL; - esxVI_ObjectContent *datastore = NULL; - char *datastoreName = NULL; - esxVI_HostDatastoreBrowserSearchResults *searchResultsList = NULL; - esxVI_HostDatastoreBrowserSearchResults *searchResults = NULL; - char *directoryAndFileName = NULL; - size_t length; - char *datastorePath = NULL; - char *volumeName = NULL; - esxVI_FileInfo *fileInfo = NULL; - char *uuid_string = NULL; - char key_candidate[VIR_UUID_STRING_BUFLEN] = ""; - - if (STRPREFIX(key, "[")) { - /* Key is probably a datastore path */ - return esxStorageVolumeLookupByPath(conn, key); - } + int i = 0; - if (!priv->primary->hasQueryVirtualDiskUuid) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("QueryVirtualDiskUuid not available, cannot lookup storage " - "volume by UUID")); - return NULL; - } + virCheckNonNullArgGoto(key, cleanup); if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } - - /* Lookup all datastores */ - if (esxVI_String_AppendValueToList(&propertyNameList, "summary.name") < 0 || - esxVI_LookupDatastoreList(priv->primary, propertyNameList, - &datastoreList) < 0) { goto cleanup; } - for (datastore = datastoreList; datastore != NULL; - datastore = datastore->_next) { - datastoreName = NULL; - - if (esxVI_GetStringValue(datastore, "summary.name", &datastoreName, - esxVI_Occurrence_RequiredItem) < 0) { - goto cleanup; - } - - /* Lookup datastore content */ - esxVI_HostDatastoreBrowserSearchResults_Free(&searchResultsList); - - if (esxVI_LookupDatastoreContentByDatastoreName - (priv->primary, datastoreName, &searchResultsList) < 0) { - goto cleanup; - } - - /* Interpret search result */ - for (searchResults = searchResultsList; searchResults != NULL; - searchResults = searchResults->_next) { - VIR_FREE(directoryAndFileName); - - if (esxUtil_ParseDatastorePath(searchResults->folderPath, NULL, - NULL, &directoryAndFileName) < 0) { - goto cleanup; - } - - /* Strip trailing separators */ - length = strlen(directoryAndFileName); - - while (length > 0 && directoryAndFileName[length - 1] == '/') { - directoryAndFileName[length - 1] = '\0'; - --length; - } - - /* Build datastore path and query the UUID */ - for (fileInfo = searchResults->file; fileInfo != NULL; - fileInfo = fileInfo->_next) { - VIR_FREE(datastorePath); - - if (length < 1) { - if (virAsprintf(&volumeName, "%s", - fileInfo->path) < 0) { - virReportOOMError(); - goto cleanup; - } - } else if (virAsprintf(&volumeName, "%s/%s", - directoryAndFileName, - fileInfo->path) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (virAsprintf(&datastorePath, "[%s] %s", datastoreName, - volumeName) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (esxVI_VmDiskFileInfo_DynamicCast(fileInfo) == NULL) { - /* Only a VirtualDisk has a UUID */ - continue; - } - - VIR_FREE(uuid_string); - - if (esxVI_QueryVirtualDiskUuid - (priv->primary, datastorePath, - priv->primary->datacenter->_reference, - &uuid_string) < 0) { - goto cleanup; - } - - if (esxUtil_ReformatUuid(uuid_string, key_candidate) < 0) { - goto cleanup; - } - - if (STREQ(key, key_candidate)) { - /* Found matching UUID */ - volume = virGetStorageVol(conn, datastoreName, - volumeName, key); - goto cleanup; - } - } + /* lookup by key operation is supported using connection + * pointer, so poke all supported backend drivers to perform + * the desired operation + */ + for (i = 0; i < LAST_DRIVER; ++i) { + volume = backendDrv[i]->volLookupByKey(conn, key); + if (volume != NULL) { + break; } } - cleanup: - esxVI_String_Free(&propertyNameList); - esxVI_ObjectContent_Free(&datastoreList); - esxVI_HostDatastoreBrowserSearchResults_Free(&searchResultsList); - VIR_FREE(directoryAndFileName); - VIR_FREE(datastorePath); - VIR_FREE(volumeName); - VIR_FREE(uuid_string); +cleanup: return volume; + } @@ -961,224 +560,24 @@ esxStorageVolumeCreateXML(virStoragePoolPtr pool, const char *xmldesc, { virStorageVolPtr volume = NULL; esxPrivate *priv = pool->conn->storagePrivateData; - virStoragePoolDef poolDef; - virStorageVolDefPtr def = NULL; - char *tmp; - char *unescapedDatastorePath = NULL; - char *unescapedDirectoryName = NULL; - char *unescapedDirectoryAndFileName = NULL; - char *directoryName = NULL; - char *fileName = NULL; - char *datastorePathWithoutFileName = NULL; - char *datastorePath = NULL; - esxVI_FileInfo *fileInfo = NULL; - esxVI_FileBackedVirtualDiskSpec *virtualDiskSpec = NULL; - esxVI_ManagedObjectReference *task = NULL; - esxVI_TaskInfoState taskInfoState; - char *taskInfoErrorMessage = NULL; - char *uuid_string = NULL; - char *key = NULL; - - virCheckFlags(0, NULL); - - memset(&poolDef, 0, sizeof(poolDef)); - - if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } - - if (esxStoragePoolLookupType(priv->primary, pool->name, &poolDef.type) < 0) { - return NULL; - } - - /* Parse config */ - def = virStorageVolDefParseString(&poolDef, xmldesc); + virStorageDriverPtr backend = NULL; - if (def == NULL) { - goto cleanup; - } - - if (def->type != VIR_STORAGE_VOL_FILE) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Creating non-file volumes is not supported")); - goto cleanup; - } - - /* Validate config */ - tmp = strrchr(def->name, '/'); - - if (tmp == NULL || *def->name == '/' || tmp[1] == '\0') { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Volume name '%s' doesn't have expected format " - "'<directory>/<file>'"), def->name); - goto cleanup; - } + virCheckNonNullArgGoto(xmldesc, cleanup); - if (! virFileHasSuffix(def->name, ".vmdk")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Volume name '%s' has unsupported suffix, expecting '.vmdk'"), - def->name); + if (esxVI_EnsureSession(priv->primary) < 0) { goto cleanup; } - if (virAsprintf(&unescapedDatastorePath, "[%s] %s", pool->name, - def->name) < 0) { - virReportOOMError(); + if (esxStorageGetBackendDriver(pool->conn, pool->name, &backend) < 0) { goto cleanup; } - if (def->target.format == VIR_STORAGE_FILE_VMDK) { - /* Parse and escape datastore path */ - if (esxUtil_ParseDatastorePath(unescapedDatastorePath, NULL, - &unescapedDirectoryName, - &unescapedDirectoryAndFileName) < 0) { - goto cleanup; - } - - directoryName = esxUtil_EscapeDatastoreItem(unescapedDirectoryName); - - if (directoryName == NULL) { - goto cleanup; - } - - fileName = esxUtil_EscapeDatastoreItem(unescapedDirectoryAndFileName + - strlen(unescapedDirectoryName) + 1); - - if (fileName == NULL) { - goto cleanup; - } - - if (virAsprintf(&datastorePathWithoutFileName, "[%s] %s", pool->name, - directoryName) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (virAsprintf(&datastorePath, "[%s] %s/%s", pool->name, directoryName, - fileName) < 0) { - virReportOOMError(); - goto cleanup; - } - - /* Create directory, if it doesn't exist yet */ - if (esxVI_LookupFileInfoByDatastorePath - (priv->primary, datastorePathWithoutFileName, true, &fileInfo, - esxVI_Occurrence_OptionalItem) < 0) { - goto cleanup; - } - - if (fileInfo == NULL) { - if (esxVI_MakeDirectory(priv->primary, datastorePathWithoutFileName, - priv->primary->datacenter->_reference, - esxVI_Boolean_True) < 0) { - goto cleanup; - } - } - - /* Create VirtualDisk */ - if (esxVI_FileBackedVirtualDiskSpec_Alloc(&virtualDiskSpec) < 0 || - esxVI_Long_Alloc(&virtualDiskSpec->capacityKb) < 0) { - goto cleanup; - } - - /* From the vSphere API documentation about VirtualDiskType ... */ - if (def->allocation == def->capacity) { - /* - * "A preallocated disk has all space allocated at creation time - * and the space is zeroed on demand as the space is used." - */ - virtualDiskSpec->diskType = (char *)"preallocated"; - } else if (def->allocation == 0) { - /* - * "Space required for thin-provisioned virtual disk is allocated - * and zeroed on demand as the space is used." - */ - virtualDiskSpec->diskType = (char *)"thin"; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unsupported capacity-to-allocation relation")); - goto cleanup; - } - - /* - * FIXME: The adapter type is a required parameter, but there is no - * way to let the user specify it in the volume XML config. Therefore, - * default to 'busLogic' here. - */ - virtualDiskSpec->adapterType = (char *)"busLogic"; - - virtualDiskSpec->capacityKb->value = - VIR_DIV_UP(def->capacity, 1024); /* Scale from byte to kilobyte */ - - if (esxVI_CreateVirtualDisk_Task - (priv->primary, datastorePath, priv->primary->datacenter->_reference, - esxVI_VirtualDiskSpec_DynamicCast(virtualDiskSpec), &task) < 0 || - esxVI_WaitForTaskCompletion(priv->primary, task, NULL, - esxVI_Occurrence_None, - priv->parsedUri->autoAnswer, - &taskInfoState, - &taskInfoErrorMessage) < 0) { - goto cleanup; - } - - if (taskInfoState != esxVI_TaskInfoState_Success) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not create volume: %s"), - taskInfoErrorMessage); - goto cleanup; - } - - if (priv->primary->hasQueryVirtualDiskUuid) { - if (VIR_ALLOC_N(key, VIR_UUID_STRING_BUFLEN) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (esxVI_QueryVirtualDiskUuid(priv->primary, datastorePath, - priv->primary->datacenter->_reference, - &uuid_string) < 0) { - goto cleanup; - } - - if (esxUtil_ReformatUuid(uuid_string, key) < 0) { - goto cleanup; - } - } else { - /* Fall back to the path as key */ - if (esxVI_String_DeepCopyValue(&key, datastorePath) < 0) { - goto cleanup; - } - } - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Creation of %s volumes is not supported"), - virStorageFileFormatTypeToString(def->target.format)); - goto cleanup; - } - - volume = virGetStorageVol(pool->conn, pool->name, def->name, key); + volume = backend->volCreateXML(pool, xmldesc, flags); cleanup: - if (virtualDiskSpec != NULL) { - virtualDiskSpec->diskType = NULL; - virtualDiskSpec->adapterType = NULL; - } - - virStorageVolDefFree(def); - VIR_FREE(unescapedDatastorePath); - VIR_FREE(unescapedDirectoryName); - VIR_FREE(unescapedDirectoryAndFileName); - VIR_FREE(directoryName); - VIR_FREE(fileName); - VIR_FREE(datastorePathWithoutFileName); - VIR_FREE(datastorePath); - esxVI_FileInfo_Free(&fileInfo); - esxVI_FileBackedVirtualDiskSpec_Free(&virtualDiskSpec); - esxVI_ManagedObjectReference_Free(&task); - VIR_FREE(taskInfoErrorMessage); - VIR_FREE(uuid_string); - VIR_FREE(key); return volume; + } @@ -1189,193 +588,24 @@ esxStorageVolumeCreateXMLFrom(virStoragePoolPtr pool, const char *xmldesc, { virStorageVolPtr volume = NULL; esxPrivate *priv = pool->conn->storagePrivateData; - virStoragePoolDef poolDef; - char *sourceDatastorePath = NULL; - virStorageVolDefPtr def = NULL; - char *tmp; - char *unescapedDatastorePath = NULL; - char *unescapedDirectoryName = NULL; - char *unescapedDirectoryAndFileName = NULL; - char *directoryName = NULL; - char *fileName = NULL; - char *datastorePathWithoutFileName = NULL; - char *datastorePath = NULL; - esxVI_FileInfo *fileInfo = NULL; - esxVI_ManagedObjectReference *task = NULL; - esxVI_TaskInfoState taskInfoState; - char *taskInfoErrorMessage = NULL; - char *uuid_string = NULL; - char *key = NULL; - - virCheckFlags(0, NULL); - - memset(&poolDef, 0, sizeof(poolDef)); - - if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } - - if (esxStoragePoolLookupType(priv->primary, pool->name, &poolDef.type) < 0) { - return NULL; - } - - if (virAsprintf(&sourceDatastorePath, "[%s] %s", sourceVolume->pool, - sourceVolume->name) < 0) { - virReportOOMError(); - goto cleanup; - } - - /* Parse config */ - def = virStorageVolDefParseString(&poolDef, xmldesc); - - if (def == NULL) { - goto cleanup; - } - - if (def->type != VIR_STORAGE_VOL_FILE) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Creating non-file volumes is not supported")); - goto cleanup; - } - - /* Validate config */ - tmp = strrchr(def->name, '/'); + virStorageDriverPtr backend = NULL; - if (tmp == NULL || *def->name == '/' || tmp[1] == '\0') { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Volume name '%s' doesn't have expected format " - "'<directory>/<file>'"), def->name); - goto cleanup; - } + virCheckNonNullArgGoto(xmldesc, cleanup); - if (! virFileHasSuffix(def->name, ".vmdk")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Volume name '%s' has unsupported suffix, expecting '.vmdk'"), - def->name); + if (esxVI_EnsureSession(priv->primary) < 0) { goto cleanup; } - if (virAsprintf(&unescapedDatastorePath, "[%s] %s", pool->name, - def->name) < 0) { - virReportOOMError(); + if (esxStorageGetBackendDriver(pool->conn, pool->name, &backend) < 0) { goto cleanup; } - if (def->target.format == VIR_STORAGE_FILE_VMDK) { - /* Parse and escape datastore path */ - if (esxUtil_ParseDatastorePath(unescapedDatastorePath, NULL, - &unescapedDirectoryName, - &unescapedDirectoryAndFileName) < 0) { - goto cleanup; - } - - directoryName = esxUtil_EscapeDatastoreItem(unescapedDirectoryName); - - if (directoryName == NULL) { - goto cleanup; - } - - fileName = esxUtil_EscapeDatastoreItem(unescapedDirectoryAndFileName + - strlen(unescapedDirectoryName) + 1); - - if (fileName == NULL) { - goto cleanup; - } - - if (virAsprintf(&datastorePathWithoutFileName, "[%s] %s", pool->name, - directoryName) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (virAsprintf(&datastorePath, "[%s] %s/%s", pool->name, directoryName, - fileName) < 0) { - virReportOOMError(); - goto cleanup; - } - - /* Create directory, if it doesn't exist yet */ - if (esxVI_LookupFileInfoByDatastorePath - (priv->primary, datastorePathWithoutFileName, true, &fileInfo, - esxVI_Occurrence_OptionalItem) < 0) { - goto cleanup; - } - - if (fileInfo == NULL) { - if (esxVI_MakeDirectory(priv->primary, datastorePathWithoutFileName, - priv->primary->datacenter->_reference, - esxVI_Boolean_True) < 0) { - goto cleanup; - } - } - - /* Copy VirtualDisk */ - if (esxVI_CopyVirtualDisk_Task(priv->primary, sourceDatastorePath, - priv->primary->datacenter->_reference, - datastorePath, - priv->primary->datacenter->_reference, - NULL, esxVI_Boolean_False, &task) < 0 || - esxVI_WaitForTaskCompletion(priv->primary, task, NULL, - esxVI_Occurrence_None, - priv->parsedUri->autoAnswer, - &taskInfoState, - &taskInfoErrorMessage) < 0) { - goto cleanup; - } - - if (taskInfoState != esxVI_TaskInfoState_Success) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not copy volume: %s"), - taskInfoErrorMessage); - goto cleanup; - } - - if (priv->primary->hasQueryVirtualDiskUuid) { - if (VIR_ALLOC_N(key, VIR_UUID_STRING_BUFLEN) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (esxVI_QueryVirtualDiskUuid(priv->primary, datastorePath, - priv->primary->datacenter->_reference, - &uuid_string) < 0) { - goto cleanup; - } - - if (esxUtil_ReformatUuid(uuid_string, key) < 0) { - goto cleanup; - } - } else { - /* Fall back to the path as key */ - if (esxVI_String_DeepCopyValue(&key, datastorePath) < 0) { - goto cleanup; - } - } - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Creation of %s volumes is not supported"), - virStorageFileFormatTypeToString(def->target.format)); - goto cleanup; - } - - volume = virGetStorageVol(pool->conn, pool->name, def->name, key); + volume = backend->volCreateXMLFrom(pool, xmldesc, sourceVolume, flags); cleanup: - VIR_FREE(sourceDatastorePath); - virStorageVolDefFree(def); - VIR_FREE(unescapedDatastorePath); - VIR_FREE(unescapedDirectoryName); - VIR_FREE(unescapedDirectoryAndFileName); - VIR_FREE(directoryName); - VIR_FREE(fileName); - VIR_FREE(datastorePathWithoutFileName); - VIR_FREE(datastorePath); - esxVI_FileInfo_Free(&fileInfo); - esxVI_ManagedObjectReference_Free(&task); - VIR_FREE(taskInfoErrorMessage); - VIR_FREE(uuid_string); - VIR_FREE(key); return volume; + } @@ -1383,48 +613,19 @@ esxStorageVolumeCreateXMLFrom(virStoragePoolPtr pool, const char *xmldesc, static int esxStorageVolumeDelete(virStorageVolPtr volume, unsigned int flags) { - int result = -1; esxPrivate *priv = volume->conn->storagePrivateData; - char *datastorePath = NULL; - esxVI_ManagedObjectReference *task = NULL; - esxVI_TaskInfoState taskInfoState; - char *taskInfoErrorMessage = NULL; - - virCheckFlags(0, -1); + virStorageDriverPtr backend = NULL; if (esxVI_EnsureSession(priv->primary) < 0) { return -1; } - if (virAsprintf(&datastorePath, "[%s] %s", volume->pool, volume->name) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (esxVI_DeleteVirtualDisk_Task(priv->primary, datastorePath, - priv->primary->datacenter->_reference, - &task) < 0 || - esxVI_WaitForTaskCompletion(priv->primary, task, NULL, - esxVI_Occurrence_None, - priv->parsedUri->autoAnswer, - &taskInfoState, &taskInfoErrorMessage) < 0) { - goto cleanup; - } - - if (taskInfoState != esxVI_TaskInfoState_Success) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not delete volume: %s"), - taskInfoErrorMessage); - goto cleanup; + if (esxStorageGetBackendDriver(volume->conn, volume->pool, &backend) < 0 || + backend->volDelete(volume , flags) < 0) { + return -1; } - result = 0; - - cleanup: - VIR_FREE(datastorePath); - esxVI_ManagedObjectReference_Free(&task); - VIR_FREE(taskInfoErrorMessage); - - return result; + return 0; } @@ -1432,97 +633,39 @@ esxStorageVolumeDelete(virStorageVolPtr volume, unsigned int flags) static int esxStorageVolumeWipe(virStorageVolPtr volume, unsigned int flags) { - int result = -1; esxPrivate *priv = volume->conn->storagePrivateData; - char *datastorePath = NULL; - esxVI_ManagedObjectReference *task = NULL; - esxVI_TaskInfoState taskInfoState; - char *taskInfoErrorMessage = NULL; - - virCheckFlags(0, -1); + virStorageDriverPtr backend = NULL; if (esxVI_EnsureSession(priv->primary) < 0) { return -1; } - if (virAsprintf(&datastorePath, "[%s] %s", volume->pool, volume->name) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (esxVI_ZeroFillVirtualDisk_Task(priv->primary, datastorePath, - priv->primary->datacenter->_reference, - &task) < 0 || - esxVI_WaitForTaskCompletion(priv->primary, task, NULL, - esxVI_Occurrence_None, - priv->parsedUri->autoAnswer, - &taskInfoState, &taskInfoErrorMessage) < 0) { - goto cleanup; - } - - if (taskInfoState != esxVI_TaskInfoState_Success) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not wipe volume: %s"), - taskInfoErrorMessage); - goto cleanup; + if (esxStorageGetBackendDriver(volume->conn, volume->pool, &backend) < 0 || + backend->volDelete(volume , flags) < 0) { + return -1; } - result = 0; - - cleanup: - VIR_FREE(datastorePath); - esxVI_ManagedObjectReference_Free(&task); - VIR_FREE(taskInfoErrorMessage); - - return result; + return 0; } - static int esxStorageVolumeGetInfo(virStorageVolPtr volume, virStorageVolInfoPtr info) { - int result = -1; esxPrivate *priv = volume->conn->storagePrivateData; - char *datastorePath = NULL; - esxVI_FileInfo *fileInfo = NULL; - esxVI_VmDiskFileInfo *vmDiskFileInfo = NULL; - - memset(info, 0, sizeof(*info)); + virStorageDriverPtr backend = NULL; if (esxVI_EnsureSession(priv->primary) < 0) { return -1; } - if (virAsprintf(&datastorePath, "[%s] %s", volume->pool, volume->name) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (esxVI_LookupFileInfoByDatastorePath(priv->primary, datastorePath, - false, &fileInfo, - esxVI_Occurrence_RequiredItem) < 0) { - goto cleanup; - } - - vmDiskFileInfo = esxVI_VmDiskFileInfo_DynamicCast(fileInfo); - - info->type = VIR_STORAGE_VOL_FILE; - - if (vmDiskFileInfo != NULL) { - info->capacity = vmDiskFileInfo->capacityKb->value * 1024; /* Scale from kilobyte to byte */ - info->allocation = vmDiskFileInfo->fileSize->value; - } else { - info->capacity = fileInfo->fileSize->value; - info->allocation = fileInfo->fileSize->value; + if (esxStorageGetBackendDriver(volume->conn, volume->pool, &backend) < 0 || + backend->volGetInfo(volume , info) < 0) { + return -1; } - result = 0; - - cleanup: - VIR_FREE(datastorePath); - esxVI_FileInfo_Free(&fileInfo); + return 0; - return result; } @@ -1530,84 +673,24 @@ esxStorageVolumeGetInfo(virStorageVolPtr volume, virStorageVolInfoPtr info) static char * esxStorageVolumeGetXMLDesc(virStorageVolPtr volume, unsigned int flags) { - esxPrivate *priv = volume->conn->storagePrivateData; - virStoragePoolDef pool; - char *datastorePath = NULL; - esxVI_FileInfo *fileInfo = NULL; - esxVI_VmDiskFileInfo *vmDiskFileInfo = NULL; - esxVI_IsoImageFileInfo *isoImageFileInfo = NULL; - esxVI_FloppyImageFileInfo *floppyImageFileInfo = NULL; - virStorageVolDef def; char *xml = NULL; - - virCheckFlags(0, NULL); - - memset(&pool, 0, sizeof(pool)); - memset(&def, 0, sizeof(def)); + esxPrivate *priv = volume->conn->storagePrivateData; + virStorageDriverPtr backend = NULL; if (esxVI_EnsureSession(priv->primary) < 0) { - return NULL; - } - - if (esxStoragePoolLookupType(priv->primary, volume->pool, &pool.type) < 0) { - return NULL; - } - - /* Lookup file info */ - if (virAsprintf(&datastorePath, "[%s] %s", volume->pool, volume->name) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (esxVI_LookupFileInfoByDatastorePath(priv->primary, datastorePath, - false, &fileInfo, - esxVI_Occurrence_RequiredItem) < 0) { goto cleanup; } - vmDiskFileInfo = esxVI_VmDiskFileInfo_DynamicCast(fileInfo); - isoImageFileInfo = esxVI_IsoImageFileInfo_DynamicCast(fileInfo); - floppyImageFileInfo = esxVI_FloppyImageFileInfo_DynamicCast(fileInfo); - - def.name = volume->name; - - if (esxVI_LookupStorageVolumeKeyByDatastorePath(priv->primary, datastorePath, - &def.key) < 0) { - goto cleanup; - } - - def.type = VIR_STORAGE_VOL_FILE; - def.target.path = datastorePath; - - if (vmDiskFileInfo != NULL) { - def.capacity = vmDiskFileInfo->capacityKb->value * 1024; /* Scale from kilobyte to byte */ - def.allocation = vmDiskFileInfo->fileSize->value; - - def.target.format = VIR_STORAGE_FILE_VMDK; - } else if (isoImageFileInfo != NULL) { - def.capacity = fileInfo->fileSize->value; - def.allocation = fileInfo->fileSize->value; - - def.target.format = VIR_STORAGE_FILE_ISO; - } else if (floppyImageFileInfo != NULL) { - def.capacity = fileInfo->fileSize->value; - def.allocation = fileInfo->fileSize->value; - - def.target.format = VIR_STORAGE_FILE_RAW; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("File '%s' has unknown type"), datastorePath); + if (esxStorageGetBackendDriver(volume->conn, volume->pool, &backend) < 0) { goto cleanup; } - xml = virStorageVolDefFormat(&pool, &def); + xml = backend->volGetXMLDesc(volume, flags); - cleanup: - VIR_FREE(datastorePath); - esxVI_FileInfo_Free(&fileInfo); - VIR_FREE(def.key); + cleanup: return xml; + } @@ -1615,14 +698,24 @@ esxStorageVolumeGetXMLDesc(virStorageVolPtr volume, unsigned int flags) static char * esxStorageVolumeGetPath(virStorageVolPtr volume) { - char *path; + char *path = NULL; + esxPrivate *priv = volume->conn->storagePrivateData; + virStorageDriverPtr backend = NULL; - if (virAsprintf(&path, "[%s] %s", volume->pool, volume->name) < 0) { - virReportOOMError(); - return NULL; + if (esxVI_EnsureSession(priv->primary) < 0) { + goto cleanup; } + if (esxStorageGetBackendDriver(volume->conn, volume->pool, &backend) < 0) { + goto cleanup; + } + + path = backend->volGetPath(volume); + + cleanup: + return path; + } diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index 65e1d9a..0e647d4 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -3011,7 +3011,7 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name, if (*datastore == NULL && occurrence != esxVI_Occurrence_OptionalItem) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("Could not find datastore with name '%s'"), name); + _("Could not find datastore with name '%s'"), name); goto cleanup; } @@ -3118,7 +3118,8 @@ esxVI_LookupDatastoreByAbsolutePath(esxVI_Context *ctx, int esxVI_LookupDatastoreHostMount(esxVI_Context *ctx, esxVI_ManagedObjectReference *datastore, - esxVI_DatastoreHostMount **hostMount) + esxVI_DatastoreHostMount **hostMount, + esxVI_Occurrence occurrence) { int result = -1; esxVI_String *propertyNameList = NULL; @@ -3166,9 +3167,9 @@ esxVI_LookupDatastoreHostMount(esxVI_Context *ctx, break; } - if (*hostMount == NULL) { + if (*hostMount == NULL && occurrence != esxVI_Occurrence_OptionalItem) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Could not lookup datastore host mount")); + _("Could not lookup datastore host mount")); goto cleanup; } @@ -4883,5 +4884,337 @@ esxVI_LookupManagedObjectHelper(esxVI_Context *ctx, } +int +esxVI_LookupHostInternetScsiHbaStaticTargetByName( + esxVI_Context *ctx, + const char *name, + esxVI_HostInternetScsiHbaStaticTarget **ret, + esxVI_Occurrence occurrence) +{ + int result = -1; + esxVI_HostInternetScsiHba *hostInternetScsiHba = NULL; + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + + if (esxVI_LookupHostInternetScsiHba(ctx, &hostInternetScsiHba) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to obtain hostInternetScsiHba")); + goto cleanup; + } + + if (hostInternetScsiHba == NULL) { + /* iSCSI adapter may not be enabled for this host */ + return 0; + } + + if (hostInternetScsiHba->configuredStaticTarget) { + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + if (STREQ(target->iScsiName, name)) { + break; + } + } + } + + if (target == NULL) { + if (occurrence == esxVI_Occurrence_RequiredItem) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find storage pool with name: %s"), name); + } + goto cleanup; + } + + if (esxVI_HostInternetScsiHbaStaticTarget_DeepCopy( + ret, (esxVI_HostInternetScsiHbaStaticTarget *)target) < 0) { + goto cleanup; + } + + result = 0; + + cleanup: + + esxVI_HostInternetScsiHba_Free(&hostInternetScsiHba); + + return result; +} + + +int +esxVI_LookupHostInternetScsiHba( + esxVI_Context *ctx, + esxVI_HostInternetScsiHba **hostInternetScsiHba) +{ + int result = -1; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_ObjectContent *hostSystem = NULL; + esxVI_String *propertyNameList = NULL; + esxVI_HostHostBusAdapter *hostHostBusAdapterList = NULL; + esxVI_HostHostBusAdapter *hostHostBusAdapter = NULL; + + if (esxVI_String_AppendValueToList(&propertyNameList, + "config.storageDevice.hostBusAdapter\0") < 0 || + esxVI_LookupHostSystemProperties(ctx, propertyNameList, + &hostSystem) < 0) { + goto cleanup; + } + + for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, + "config.storageDevice.hostBusAdapter")) { + if (esxVI_HostHostBusAdapter_CastListFromAnyType( + dynamicProperty->val, &hostHostBusAdapterList) < 0 || + hostHostBusAdapterList == NULL) { + goto cleanup; + } + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + /* See vSphere API documentation about HostInternetScsiHba for details */ + for (hostHostBusAdapter = hostHostBusAdapterList; hostHostBusAdapter != NULL; + hostHostBusAdapter = hostHostBusAdapter->_next) { + esxVI_HostInternetScsiHba *candidate= + esxVI_HostInternetScsiHba_DynamicCast(hostHostBusAdapter); + + if (candidate) { + if (esxVI_HostInternetScsiHba_DeepCopy(hostInternetScsiHba, + candidate) < 0) { + goto cleanup; + } + break; + } + } + + result = 0; + +cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&hostSystem); + esxVI_HostHostBusAdapter_Free(&hostHostBusAdapterList); + + return result; +} + +int +esxVI_LookupScsiLunList(esxVI_Context *ctx, + esxVI_ScsiLun **ret) +{ + int result = -1; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_ObjectContent *hostSystem = NULL; + esxVI_String *propertyNameList = NULL; + esxVI_ScsiLun *scsiLunList = NULL; + + if (esxVI_String_AppendValueToList(&propertyNameList, + "config.storageDevice.scsiLun\0") < 0 || + esxVI_LookupHostSystemProperties( + ctx, propertyNameList, &hostSystem) < 0) { + goto cleanup; + } + + for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, + "config.storageDevice.scsiLun")) { + if (esxVI_ScsiLun_CastListFromAnyType(dynamicProperty->val, + &scsiLunList) < 0) { + goto cleanup; + } + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + if (scsiLunList == NULL) { + goto cleanup; + } + + /** + * FIXME: deep list copy operation fails with error: + * " libvir: ESX Driver error : + * internal error Call to esxVI_HostDevice_Free for + * unexpected type 'HostScsiDisk' " + * HostScsiDisk extends ScsiLun + */ + *ret = scsiLunList; + scsiLunList = NULL; /* prevent double free */ + + result = 0; + +cleanup: + + esxVI_ScsiLun_Free(&scsiLunList); + + return result; + +} + +int +esxVI_LookupHostScsiTopologyLunListByTargetName( + esxVI_Context *ctx, const char *name, esxVI_HostScsiTopologyLun **ret) +{ + int result = -1; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_ObjectContent *hostSystem = NULL; + esxVI_String *propertyNameList = NULL; + esxVI_HostScsiTopologyInterface *hostScsiInterfaceList = NULL; + const esxVI_HostScsiTopologyInterface *hostScsiInterface = NULL; + const esxVI_HostScsiTopologyTarget *hostScsiTopologyTarget = NULL; + bool found = false; + + if (esxVI_String_AppendValueToList(&propertyNameList, + "config.storageDevice.scsiTopology.adapter\0") < 0 || + esxVI_LookupHostSystemProperties( + ctx, propertyNameList, &hostSystem) < 0) { + goto cleanup; + } + + for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, + "config.storageDevice.scsiTopology.adapter")) { + esxVI_HostScsiTopologyInterface_Free(&hostScsiInterfaceList); + + if ((esxVI_HostScsiTopologyInterface_CastListFromAnyType + (dynamicProperty->val, &hostScsiInterfaceList) < 0)) { + goto cleanup; + } + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + if (hostScsiInterfaceList == NULL) { + /* iSCSI adapter may not be enabled */ + return 0; + } + + /* See vSphere API documentation about HostScsiTopologyInterface */ + for (hostScsiInterface = hostScsiInterfaceList; + hostScsiInterface != NULL && !found; + hostScsiInterface = hostScsiInterface->_next) { + for (hostScsiTopologyTarget = hostScsiInterface->target; + hostScsiTopologyTarget != NULL; + hostScsiTopologyTarget = hostScsiTopologyTarget->_next) { + const esxVI_HostInternetScsiTargetTransport *candidate = + esxVI_HostInternetScsiTargetTransport_DynamicCast( + hostScsiTopologyTarget->transport); + + if (candidate && STREQ(candidate->iScsiName, name)) { + found = true; + break; + } + } + } + + if (!found || hostScsiTopologyTarget == NULL) { + goto cleanup; + } + + if (esxVI_HostScsiTopologyLun_DeepCopyList( + ret, hostScsiTopologyTarget->lun) < 0) { + goto cleanup; + } + + if (*ret == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Target not found")); + goto cleanup; + } + + result = 0; + + cleanup: + + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&hostSystem); + esxVI_HostScsiTopologyInterface_Free(&hostScsiInterfaceList); + + return result; +} + + +int +esxVI_LookupStoragePoolNameByScsiLunKey(esxVI_Context *ctx, + const char *key, + char **poolName) +{ + int result = -1; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_ObjectContent *hostSystem = NULL; + esxVI_String *propertyNameList = NULL; + esxVI_HostScsiTopologyInterface *hostScsiInterfaceList = NULL; + const esxVI_HostScsiTopologyInterface *hostScsiInterface = NULL; + const esxVI_HostScsiTopologyTarget *hostScsiTopologyTarget = NULL; + bool found = false; + + if (esxVI_String_AppendValueToList(&propertyNameList, + "config.storageDevice.scsiTopology.adapter\0") < 0 || + esxVI_LookupHostSystemProperties( + ctx, propertyNameList, &hostSystem) < 0) { + goto cleanup; + } + + for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, + "config.storageDevice.scsiTopology.adapter")) { + esxVI_HostScsiTopologyInterface_Free(&hostScsiInterfaceList); + + if ((esxVI_HostScsiTopologyInterface_CastListFromAnyType + (dynamicProperty->val, &hostScsiInterfaceList) < 0)) { + goto cleanup; + } + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + if (hostScsiInterfaceList == NULL) { + /* iSCSI adapter may not be enabled */ + return 0; + } + + /* See vSphere API documentation about HostScsiTopologyInterface */ + for (hostScsiInterface = hostScsiInterfaceList; + hostScsiInterface != NULL && !found; + hostScsiInterface = hostScsiInterface->_next) { + for (hostScsiTopologyTarget = hostScsiInterface->target; + hostScsiTopologyTarget != NULL; + hostScsiTopologyTarget = hostScsiTopologyTarget->_next) { + const esxVI_HostInternetScsiTargetTransport *candidate = + esxVI_HostInternetScsiTargetTransport_DynamicCast( + hostScsiTopologyTarget->transport); + + if (candidate) { + /* iterate hostScsiTopologyLun list to find matching key */ + const esxVI_HostScsiTopologyLun *hostScsiTopologyLun = + hostScsiTopologyTarget->lun; + for (; hostScsiTopologyLun != NULL; + hostScsiTopologyLun = hostScsiTopologyLun->_next) { + if (STREQ(hostScsiTopologyLun->scsiLun, key)) { + *poolName = strdup(candidate->iScsiName); + + if (*poolName == NULL) { + virReportOOMError(); + goto cleanup; + } + } + } + break; + } + } + } + + result = 0; + + cleanup: + esxVI_ObjectContent_Free(&hostSystem); + esxVI_String_Free(&propertyNameList); + esxVI_HostScsiTopologyInterface_Free(&hostScsiInterfaceList); + + return result; +} #include "esx_vi.generated.c" diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h index 12394e7..6149c2d 100644 --- a/src/esx/esx_vi.h +++ b/src/esx/esx_vi.h @@ -438,7 +438,8 @@ int esxVI_LookupDatastoreByAbsolutePath(esxVI_Context *ctx, int esxVI_LookupDatastoreHostMount(esxVI_Context *ctx, esxVI_ManagedObjectReference *datastore, - esxVI_DatastoreHostMount **hostMount); + esxVI_DatastoreHostMount **hostMount, + esxVI_Occurrence occurrence); int esxVI_LookupTaskInfoByTask(esxVI_Context *ctx, esxVI_ManagedObjectReference *task, @@ -524,6 +525,24 @@ int esxVI_ParseHostCpuIdInfo(esxVI_ParsedHostCpuIdInfo *parsedHostCpuIdInfo, int esxVI_ProductVersionToDefaultVirtualHWVersion(esxVI_ProductVersion productVersion); +int esxVI_LookupHostInternetScsiHbaStaticTargetByName(esxVI_Context *ctx, + const char *name, esxVI_HostInternetScsiHbaStaticTarget **ret, + esxVI_Occurrence occurrence); + +int esxVI_LookupHostInternetScsiHba( + esxVI_Context *ctx, + esxVI_HostInternetScsiHba **hostInternetScsiHba); + +int esxVI_LookupScsiLunList(esxVI_Context *ctx, esxVI_ScsiLun **ret); + +int esxVI_LookupHostScsiTopologyLunListByTargetName( + esxVI_Context *ctx, const char *name, esxVI_HostScsiTopologyLun **ret); + +int +esxVI_LookupStoragePoolNameByScsiLunKey(esxVI_Context *ctx, + const char *key, + char **poolName); + # include "esx_vi.generated.h" #endif /* __ESX_VI_H__ */ diff --git a/src/esx/esx_vi_generator.input b/src/esx/esx_vi_generator.input index c4a3e56..6e50be5 100644 --- a/src/esx/esx_vi_generator.input +++ b/src/esx/esx_vi_generator.input @@ -58,6 +58,14 @@ enum AutoStartWaitHeartbeatSetting end +enum FibreChannelPortType + fabric + loop + pointToPoint + unknown +end + + enum ManagedEntityStatus gray green @@ -128,6 +136,11 @@ enum VirtualMachinePowerState end +enum vStorageSupport + vStorageUnknown +end + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Objects # @@ -263,6 +276,13 @@ object HostAutoStartManagerConfig end +object HostBlockAdapterTargetTransport extends HostTargetTransport +end + +object HostBlockHba extends HostHostBusAdapter +end + + object HostConfigManager ManagedObjectReference cpuScheduler o ManagedObjectReference datastoreSystem o @@ -310,6 +330,32 @@ object HostDatastoreBrowserSearchSpec end +object HostDevice + String deviceName r + String deviceType r +end + + +object HostDiskDimensionsLba + Int blockSize r + Long block r +end + + +object HostFibreChannelHba extends HostHostBusAdapter + Long nodeWorldWideName r + FibreChannelPortType portType r + Long portWorldWideName r + Long speed r +end + + +object HostFibreChannelTargetTransport extends HostTargetTransport + Long nodeWorldWideName r + Long portWorldWideName r +end + + object HostFileSystemVolume String type r String name r @@ -317,11 +363,171 @@ object HostFileSystemVolume end +object HostHostBusAdapter + Int bus r + String device r + String driver o + String key o + String model r + String pci o + String status r +end + + +object HostInternetScsiTargetTransport extends HostTargetTransport + String iScsiName r + String iScsiAlias r + String address ol + +end + + +object HostInternetScsiHba extends HostHostBusAdapter + HostInternetScsiHbaAuthenticationCapabilities authenticationCapabilities r + HostInternetScsiHbaAuthenticationProperties authenticationProperties r + HostInternetScsiHbaDiscoveryCapabilities discoveryCapabilities r + HostInternetScsiHbaDiscoveryProperties discoveryProperties r + HostInternetScsiHbaIPCapabilities ipCapabilities r + HostInternetScsiHbaIPProperties ipProperties r + String iScsiName r + Boolean isSoftwareBased r + HostInternetScsiHbaParamValue advancedOptions ol + HostInternetScsiHbaSendTarget configuredSendTarget ol + HostInternetScsiHbaStaticTarget configuredStaticTarget ol + Int currentSpeedMb o + HostInternetScsiHbaDigestCapabilities digestCapabilities o + HostInternetScsiHbaDigestProperties digestProperties o + String iScsiAlias o + Int maxSpeedMb o + OptionDef supportedAdvancedOptions ol +end + + + +object HostInternetScsiHbaAuthenticationCapabilities + Boolean chapAuthSettable r + Boolean krb5AuthSettable r + Boolean spkmAuthSettable r + Boolean srpAuthSettable r + Boolean mutualChapSettable o + Boolean targetChapSettable o + Boolean targetMutualChapSettable o +end + + +object HostInternetScsiHbaAuthenticationProperties + Boolean chapAuthEnabled r + String chapAuthenticationType o + Boolean chapInherited o + String chapName o + String chapSecret o + String mutualChapAuthenticationType o + Boolean mutualChapInherited o + String mutualChapName o + String mutualChapSecret o +end + + +object HostInternetScsiHbaDigestCapabilities + Boolean dataDigestSettable o + Boolean headerDigestSettable o + Boolean targetDataDigestSettable o + Boolean targetHeaderDigestSettable o +end + + +object HostInternetScsiHbaDigestProperties + Boolean dataDigestInherited o + String dataDigestType o + Boolean headerDigestInherited o + String headerDigestType o +end + + +object HostInternetScsiHbaDiscoveryCapabilities + Boolean iSnsDiscoverySettable r + Boolean sendTargetsDiscoverySettable r + Boolean slpDiscoverySettable r + Boolean staticTargetDiscoverySettable r +end + + +object HostInternetScsiHbaDiscoveryProperties + Boolean iSnsDiscoveryEnabled r + Boolean sendTargetsDiscoveryEnabled r + Boolean slpDiscoveryEnabled r + Boolean staticTargetDiscoveryEnabled r + String iSnsDiscoveryMethod o + String iSnsHost o + String slpDiscoveryMethod o + String slpHost o +end + + +object HostInternetScsiHbaIPCapabilities + Boolean addressSettable r + Boolean alternateDnsServerAddressSettable r + Boolean defaultGatewaySettable r + Boolean ipConfigurationMethodSettable r + Boolean primaryDnsServerAddressSettable r + Boolean subnetMaskSettable r + Boolean arpRedirectSettable o + Boolean hostNameAsTargetAddress o + Boolean ipv6Supported o + Boolean mtuSettable o + Boolean nameAliasSettable o +end + + +object HostInternetScsiHbaIPProperties + Boolean dhcpConfigurationEnabled r + String address o + String alternateDnsServerAddress o + Boolean arpRedirectEnabled o + String defaultGateway o + String ipv6Address o + String ipv6DefaultGateway o + Boolean jumboFramesEnabled o + String mac o + Int mtu o + String primaryDnsServerAddress o + String subnetMask o +end + + +object HostInternetScsiHbaParamValue extends OptionValue + Boolean isInherited o +end + + +object HostInternetScsiHbaSendTarget + String address r + HostInternetScsiHbaParamValue advancedOptions ol + HostInternetScsiHbaAuthenticationProperties authenticationProperties o + HostInternetScsiHbaDigestProperties digestProperties o + String parent o + Int port o + OptionDef supportedAdvancedOptions ol +end + + +object HostInternetScsiHbaStaticTarget + String address r + String iScsiName r + HostInternetScsiHbaParamValue advancedOptions ol + HostInternetScsiHbaAuthenticationProperties authenticationProperties o + HostInternetScsiHbaDigestProperties digestProperties o + String parent o + Int port o + OptionDef supportedAdvancedOptions ol +end + + + object HostIpConfig Boolean dhcp r String ipAddress o String subnetMask o -end object HostMountInfo @@ -416,6 +622,19 @@ object HostPortGroupSpec Int vlanId r String vswitchName r HostNetworkPolicy policy r + + +object HostParallelScsiHba extends HostHostBusAdapter +end + + +object HostParallelScsiTargetTransport extends HostTargetTransport +end + + +object HostScsiDisk extends ScsiLun + HostDiskDimensionsLba capacity r + String devicePath r end @@ -425,6 +644,37 @@ object HostScsiDiskPartition end +object HostScsiTopology + HostScsiTopologyInterface adapater ol +end + + +object HostScsiTopologyInterface + String adapter r + String key r + HostScsiTopologyTarget target ol +end + + +object HostScsiTopologyLun + String key r + Int lun r + String scsiLun r +end + + +object HostScsiTopologyTarget + String key r + Int target r + HostScsiTopologyLun lun ol + HostTargetTransport transport o +end + + +object HostTargetTransport +end + + object HostVirtualSwitch String name r String key r @@ -467,7 +717,6 @@ object HostVirtualSwitchSpec HostVirtualSwitchBridge bridge o HostNetworkPolicy policy o Int mtu o -end object HostVmfsVolume extends HostFileSystemVolume @@ -527,11 +776,22 @@ object ObjectUpdate end +object OptionDef extends ElementDescription + OptionType optionType r +end + + object OptionType Boolean valueIsReadonly o end +object OptionValue + String key r + AnyType value r +end + + object PerfCounterInfo Int key r ElementDescription nameInfo r @@ -664,6 +924,45 @@ object SelectionSpec end +object ScsiLun extends HostDevice + String lunType r + String operationalState rl + String uuid r + ScsiLunDurableName alternateName ol + String canonicalName o + ScsiLunCapabilities capabilities o + ScsiLunDescriptor descriptor ol + String displayName o + ScsiLunDurableName durableName o + String key o + String model o + Int queueDepth o + String revision o + Int scsiLevel o + String serialNumber o + Byte standardInquiry ol + String vendor o +end + + +object ScsiLunCapabilities + Boolean updateDisplayNameSupported r +end + + +object ScsiLunDescriptor + String id r + String quality r +end + + +object ScsiLunDurableName + String namespace r + Byte namespaceId r + Byte data ol +end + + object ServiceContent ManagedObjectReference rootFolder r ManagedObjectReference propertyCollector r @@ -1135,6 +1434,11 @@ end method RemoveVirtualSwitch ManagedObjectReference _this r String vswitchName r + + +method RescanHba + ManagedObjectReference _this r + String hbaDevice r end diff --git a/src/esx/esx_vi_generator.py b/src/esx/esx_vi_generator.py index b49db70..7c0c1f2 100755 --- a/src/esx/esx_vi_generator.py +++ b/src/esx/esx_vi_generator.py @@ -1520,6 +1520,21 @@ additional_object_features = { "AutoStartDefaults" : Object.FEATURE__AN Object.FEATURE__ANY_TYPE, "HostDatastoreBrowserSearchResults" : Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, + "HostHostBusAdapter" : Object.FEATURE__LIST | + Object.FEATURE__ANY_TYPE, + "HostInternetScsiHba" : Object.FEATURE__DYNAMIC_CAST | + Object.FEATURE__DEEP_COPY, + "HostInternetScsiTargetTransport" : Object.FEATURE__DYNAMIC_CAST, + "HostScsiDisk" : Object.FEATURE__LIST | + Object.FEATURE__ANY_TYPE | + Object.FEATURE__DYNAMIC_CAST, + "HostScsiTopologyInterface" : Object.FEATURE__LIST | + Object.FEATURE__ANY_TYPE, + "HostScsiTopologyLun" : Object.FEATURE__ANY_TYPE | + Object.FEATURE__LIST | + Object.FEATURE__DEEP_COPY, + "HostScsiTopologyTarget" : Object.FEATURE__ANY_TYPE | + Object.FEATURE__LIST, "HostPortGroup" : Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, "HostVirtualSwitch" : Object.FEATURE__DEEP_COPY | @@ -1531,6 +1546,10 @@ additional_object_features = { "AutoStartDefaults" : Object.FEATURE__AN Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, "ResourcePoolResourceUsage" : Object.FEATURE__ANY_TYPE, + "ScsiLun" : Object.FEATURE__LIST | + Object.FEATURE__ANY_TYPE | + Object.FEATURE__DEEP_COPY, + "ScsiLunDurableName" : Object.FEATURE__LIST, "ServiceContent" : Object.FEATURE__DESERIALIZE, "SharesInfo" : Object.FEATURE__ANY_TYPE, "TaskInfo" : Object.FEATURE__LIST | -- 1.7.9.5

2012/8/20 Ata E Husain Bohra <ata.husain@hotmail.com>:
The patch refactors the current ESX storage driver due to following reasons:
1. Given most of the public APIs exposed by the storage driver in Libvirt remains same, ESX storage driver should not implement logic specific for only one supported format (current implementation only supports VMFS). 2. Decoupling interface from specific storage implementation gives us an extensible design to hook implementation for other supported storage formats.
This patch refactors the current driver to implement it as a facade pattern i.e. the driver exposes all the public libvirt APIs, but uses backend drivers to get the required task done. The backend drivers provide implementation specific to the type of storage device.
File changes: ------------------ esx_storage_driver.c ----> esx_storage_driver.c (base storage driver) | |---> esx_storage_backend_vmfs.c (VMFS backend) | |---> esx_storage_backend_iscsi.c (iSCSI backend)
The patch adds the backend driver to support iSCSI format storage pools and volumes for ESX host. The mapping of ESX iSCSI specifics to Libvirt is as follows:
1. ESX static iSCSI target <------> Libvirt Storage Pools 2. ESX iSCSI LUNs <------> Libvirt Storage Volumes.
The above understanding is based on http://libvirt.org/storage.html.
The operation supported on iSCSI pools includes:
1. List storage pools & volumes. 2. Get xml descriptor operaion on pools & volumes. 3. Lookup operation on pools & volumes by name, uuid and path (if applicable).
iSCSI pools does not support operations such as: Create / remove pools and volumes ---
Sorry, that it took me so long to start reviewing your work.
diff --git a/src/esx/esx_storage_backend_iscsi.c b/src/esx/esx_storage_backend_iscsi.c new file mode 100644 index 0000000..4f79a0f --- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.c @@ -0,0 +1,794 @@ +/* + * esx_storage_backend_iscsi.c: ESX storage backend for iSCSI handling + * + * Copyright (C) 2007-2008, 2010-2012 Red Hat, Inc.
I think this is wrong. This is all new code, isn't it? So the copyright line should read as this Copyright (C) 2012 Ata E Husain Bohra <your email address>
+#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h>
You're not using anything from sys/stat.h, so this include can be removed.
+static int +esxStorageBackendISCSINumberOfStoragePools(virConnectPtr conn) +{ + int count = 0; + esxPrivate *priv = conn->storagePrivateData; + esxVI_HostInternetScsiHba *hostInternetScsiHba = NULL; + bool success = false; + + if (esxVI_LookupHostInternetScsiHba( + priv->primary, &hostInternetScsiHba) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to obtain iSCSI adapter")); + goto cleanup; + } + + if (hostInternetScsiHba == NULL) { + /* iSCSI adapter may not be enabled for this host */ + return 0; + }
This logic suggests that there can only be at most one HostInternetScsiHba per ESX server. Is that really true?
+ if (hostInternetScsiHba->configuredStaticTarget) { + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + ++count; + } + }
The if around the for loop is not necessary ans can be removed.
+static int +esxStorageBackendISCSIListStoragePools(virConnectPtr conn, + char **const names, + const int maxnames) +{
+ if (hostInternetScsiHba->configuredStaticTarget) { + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL && count < maxnames; + target = target->_next, ++count) { + names[count] = strdup(target->iScsiName); + + if (names[count] == NULL) { + virReportOOMError(); + goto cleanup; + } + } + }
The if around the for loop is not necessary ans can be removed.
+ +static virStoragePoolPtr +esxStorageBackendISCSIPoolLookupByName(virConnectPtr conn, + const char *name) +{ + esxPrivate *priv = conn->storagePrivateData; + esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + virStoragePoolPtr pool = NULL; + + if (esxVI_LookupHostInternetScsiHbaStaticTargetByName( + priv->primary, name, &target, esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + /** + * HostInternetScsiHbaStaticTarget does not provide a uuid field, + * but iSsiName (or widely known as IQN) is unique across the multiple
Typo in 'iSsiName'.
+ * hosts, using it to compute key + */ + + md5_buffer(target->iScsiName, strlen(target->iScsiName), md5);
esxVI_LookupHostInternetScsiHbaStaticTargetByName has an occurrence parameter that is set to esxVI_Occurrence_OptionalItem. This means that esxVI_LookupHostInternetScsiHbaStaticTargetByName is allowed to return target as NULL. But you're blindly dereferencing it here. I think occurrence should be passed as esxVI_Occurrence_RequiredItem here instead.
+static virStoragePoolPtr +esxStorageBackendISCSIPoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{
+ if (hostInternetScsiHba->configuredStaticTarget) { + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + md5_buffer(target->iScsiName, strlen(target->iScsiName), md5); + + if (memcmp(uuid, md5, VIR_UUID_STRING_BUFLEN) == 0) { + break; + } + } + }
Again, the if around the for is not necessary here.
+ if (target == NULL) { + /* pool not found */ + goto cleanup; + }
Error reporting is missing here.
+static int +esxStorageBackendISCSIPoolGetInfo(virStoragePoolPtr pool ATTRIBUTE_UNUSED, + virStoragePoolInfoPtr info) +{ + /* these fields are not valid for iSCSI pool */ + info->allocation = info->capacity = info->available = 0; + info->state = esxVI_Boolean_True;
This is wrong. info->state should be VIR_STORAGE_POOL_RUNNING or another value from the virStoragePoolState if the state can be determined in more detail.
+static char * +esxStorageBackendISCSIPoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags) +{
+ if (hostInternetScsiHba->configuredStaticTarget) { + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + if (STREQ(target->iScsiName, pool->name)) { + break; + } + } + }
Again, the if around the for is not necessary here.
+ if (target == NULL) { + goto cleanup; + }
Error reporting is missing here.
+static int +esxStorageBackendISCSIPoolListStorageVolumes(virStoragePoolPtr pool, + char **const names, + int maxnames) +{
+ if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + /* O^2 but still faster than hash given N is not that large */ + for (scsiLun = scsiLunList; scsiLun != NULL && count < maxnames; + scsiLun = scsiLun->_next) { + for (hostScsiTopologyLun = hostScsiTopologyLunList; + hostScsiTopologyLun != NULL && count < maxnames; + hostScsiTopologyLun = hostScsiTopologyLun->_next) { + if (STREQ(hostScsiTopologyLun->scsiLun, scsiLun->key)) { + names[count] = strdup(scsiLun->deviceName);
What does a typical device name look like? Is it easily distinguishable from the volume names of the VMFS backend driver? Can you give some examples?
+static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByName(virStoragePoolPtr pool, + const char *name) +{
+ for (scsiLun = scsiLunList; scsiLun != NULL; + scsiLun = scsiLun->_next) { + if (STREQ(scsiLun->deviceName, name)) { + /** + * ScsiLun provides an UUID field that is unique accross + * multiple servers. But this field length is ~55 characters + * compute MD5 hash to transform it to an acceptable + * libvirt format + */ + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string);
I'm not sure if this is the best approach. What does a typical ScsiLun look like? Can you give some examples?
+static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByPath(virConnectPtr conn, const char *path) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = conn->storagePrivateData; + char *poolName = NULL; + esxVI_ScsiLun *scsiLunList = NULL; + const esxVI_ScsiLun *scsiLun = NULL; + const esxVI_HostScsiDisk *hostScsiDisk = NULL;
Remove the const here...
+ /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + + if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + for (scsiLun = scsiLunList ; scsiLun != NULL; + scsiLun = scsiLun->_next) { + hostScsiDisk = + esxVI_HostScsiDisk_DynamicCast((esxVI_ScsiLun *)scsiLun);
.. so that the (esxVI_ScsiLun *) cast is no longer necessary.
+ if (hostScsiDisk != NULL && + STREQ(hostScsiDisk->devicePath, path)) { + /* Found matching device */ + if (esxVI_LookupStoragePoolNameByScsiLunKey( + priv->primary, hostScsiDisk->key, &poolName) < 0) { + goto cleanup; + } + + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string); + + volume = virGetStorageVol(conn, poolName, path, uuid_string);
Passing the path is not correct I think, assuming that the volume name and path are not the same for iSCSI.
+virStorageDriver esxStorageBackendISCSIDrv = { + .name = "ESX ISCSI backend", + .open = NULL, /* 0.10.0 */ + .close = NULL, /* 0.10.0 */ + .numOfPools = esxStorageBackendISCSINumberOfStoragePools, /* 0.10.0 */ + .listPools = esxStorageBackendISCSIListStoragePools, /* 0.10.0 */ + .poolLookupByName = esxStorageBackendISCSIPoolLookupByName, /* 0.10.0 */ + .poolLookupByUUID = esxStorageBackendISCSIPoolLookupByUUID, /* 0.10.0 */ + .poolRefresh = esxStorageBackendISCSIPoolRefresh, /* 0.10.0 */ + .poolGetInfo = esxStorageBackendISCSIPoolGetInfo, /* 0.10.0 */ + .poolGetXMLDesc = esxStorageBackendISCSIPoolGetXMLDesc, /* 0.10.0 */ + .poolNumOfVolumes = esxStorageBackendISCSIPoolNumberOfStorageVolumes, /* 0.10.0 */ + .poolListVolumes = esxStorageBackendISCSIPoolListStorageVolumes, /* 0.10.0 */ + .volLookupByName = esxStorageBackendISCSIVolumeLookupByName, /* 0.10.0 */ + .volLookupByKey = esxStorageBackendISCSIVolumeLookupByKey, /* 0.10.0 */ + .volLookupByPath = esxStorageBackendISCSIVolumeLookupByPath, /* 0.10.0 */ + .volCreateXML = esxStorageBackendISCSIVolumeCreateXML, /* 0.10.0 */ + .volCreateXMLFrom = esxStorageBackendISCSIVolumeCreateXMLFrom, /* 0.10.0 */ + .volGetXMLDesc = esxStorageBackendISCSIVolumeGetXMLDesc, /* 0.10.0 */ + .volDelete = esxStorageBackendISCSIVolumeDelete, /* 0.10.0 */ + .volWipe = esxStorageBackendISCSIVolumeWipe, /* 0.10.0 */ + .volGetPath = esxStorageBackendISCSIVolumeGetPath, /* 0.10.0 */ + +};
The version number here are outdated now. It should be 0.10.2.
diff --git a/src/esx/esx_storage_backend_iscsi.h b/src/esx/esx_storage_backend_iscsi.h new file mode 100644 index 0000000..ca756ac --- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.h @@ -0,0 +1,31 @@ +/* + * esx_storage_backend_iscsi.h: ESX storage backend for iSCSI handling + * + * Copyright (C) 2007-2008 Red Hat, Inc.
Again, that's not the right copyright notice.
diff --git a/src/esx/esx_storage_backend_vmfs.c b/src/esx/esx_storage_backend_vmfs.c new file mode 100644 index 0000000..6550196 --- /dev/null +++ b/src/esx/esx_storage_backend_vmfs.c @@ -0,0 +1,1471 @@ + +/* + * esx_storage_backend_vmfs.c: ESX storage backend for VMFS datastores + * + * Copyright (C) 2010-2011 Red Hat, Inc. + * Copyright (C) 2010 Matthias Bolte <matthias.bolte@googlemail.com>
Put your copyright line here in the form of Copyright (C) 2012 Ata E Husain Bohra <your email address> instead of an author list on the end of the license statement.
+#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h>
This include is unused and can be removed. This is as far as I got today with the review. I'll continue in the next days, hopefully it won't take me weeks again to get back to this :) -- Matthias Bolte http://photron.blogspot.com

Please see inline. > Date: Sun, 9 Sep 2012 19:39:34 +0200
Subject: Re: [libvirt] [PATCH] Refactor ESX storage driver and add iSCSI support From: matthias.bolte@googlemail.com To: ata.husain@hotmail.com CC: libvir-list@redhat.com
2012/8/20 Ata E Husain Bohra <ata.husain@hotmail.com>:
The patch refactors the current ESX storage driver due to following reasons:
1. Given most of the public APIs exposed by the storage driver in Libvirt remains same, ESX storage driver should not implement logic specific for only one supported format (current implementation only supports VMFS). 2. Decoupling interface from specific storage implementation gives us an extensible design to hook implementation for other supported storage formats.
This patch refactors the current driver to implement it as a facade pattern i.e. the driver exposes all the public libvirt APIs, but uses backend drivers to get the required task done. The backend drivers provide implementation specific to the type of storage device.
File changes: ------------------ esx_storage_driver.c ----> esx_storage_driver.c (base storage driver) | |---> esx_storage_backend_vmfs.c (VMFS backend) | |---> esx_storage_backend_iscsi.c (iSCSI backend)
The patch adds the backend driver to support iSCSI format storage pools and volumes for ESX host. The mapping of ESX iSCSI specifics to Libvirt is as follows:
1. ESX static iSCSI target <------> Libvirt Storage Pools 2. ESX iSCSI LUNs <------> Libvirt Storage Volumes.
The above understanding is based on http://libvirt.org/storage.html.
The operation supported on iSCSI pools includes:
1. List storage pools & volumes. 2. Get xml descriptor operaion on pools & volumes. 3. Lookup operation on pools & volumes by name, uuid and path (if applicable).
iSCSI pools does not support operations such as: Create / remove pools and volumes ---
Sorry, that it took me so long to start reviewing your work.
diff --git a/src/esx/esx_storage_backend_iscsi.c b/src/esx/esx_storage_backend_iscsi.c new file mode 100644 index 0000000..4f79a0f --- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.c @@ -0,0 +1,794 @@ +/* + * esx_storage_backend_iscsi.c: ESX storage backend for iSCSI handling + * + * Copyright (C) 2007-2008, 2010-2012 Red Hat, Inc.
I think this is wrong. This is all new code, isn't it? So the copyright line should read as this
Copyright (C) 2012 Ata E Husain Bohra <your email address>
+#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h>
You're not using anything from sys/stat.h, so this include can be removed. [AB]: Sure will remove that. > > +static int
+esxStorageBackendISCSINumberOfStoragePools(virConnectPtr conn) +{ + int count = 0; + esxPrivate *priv = conn->storagePrivateData; + esxVI_HostInternetScsiHba *hostInternetScsiHba = NULL; + bool success = false; + + if (esxVI_LookupHostInternetScsiHba( + priv->primary, &hostInternetScsiHba) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to obtain iSCSI adapter")); + goto cleanup; + } + + if (hostInternetScsiHba == NULL) { + /* iSCSI adapter may not be enabled for this host */ + return 0; + }
This logic suggests that there can only be at most one HostInternetScsiHba per ESX server. Is that really true? [AB]: The proposed solution works with software iSCSI adapter, there is only one software iSCSI adapter per ESX host. Thanks for pointing this out, I wanted to add these comments but missed it :). > > + if (hostInternetScsiHba->configuredStaticTarget) {
+ const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + ++count; + } + }
The if around the for loop is not necessary ans can be removed. [AB]: I guess I am spoiled with the coding standards followed at my workplace; we try to protect conditional loops with parenthesis even if its a single line code statements. I will make necessary correction if this is not acceptable style with Libvirt community.
+static int +esxStorageBackendISCSIListStoragePools(virConnectPtr conn, + char **const names, + const int maxnames) +{
+ if (hostInternetScsiHba->configuredStaticTarget) { + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL && count < maxnames; + target = target->_next, ++count) { + names[count] = strdup(target->iScsiName); + + if (names[count] == NULL) { + virReportOOMError(); + goto cleanup; + } + } + }
The if around the for loop is not necessary ans can be removed.
+ +static virStoragePoolPtr +esxStorageBackendISCSIPoolLookupByName(virConnectPtr conn, + const char *name) +{ + esxPrivate *priv = conn->storagePrivateData; + esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + virStoragePoolPtr pool = NULL; + + if (esxVI_LookupHostInternetScsiHbaStaticTargetByName( + priv->primary, name, &target, esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + /** + * HostInternetScsiHbaStaticTarget does not provide a uuid field, + * but iSsiName (or widely known as IQN) is unique across the multiple
Typo in 'iSsiName'.
+ * hosts, using it to compute key + */ + + md5_buffer(target->iScsiName, strlen(target->iScsiName), md5);
esxVI_LookupHostInternetScsiHbaStaticTargetByName has an occurrence parameter that is set to esxVI_Occurrence_OptionalItem. This means that esxVI_LookupHostInternetScsiHbaStaticTargetByName is allowed to return target as NULL. But you're blindly dereferencing it here. I think occurrence should be passed as esxVI_Occurrence_RequiredItem here instead. [AB]: "poolLookupByName" is also used by esx_storage_drive.c:esxStorageGetBackendDriver(), in this function we want to go over both VMFS as well iSCSI pools list to determine the matching backend driver. I think the flaw is I need to check for valid target pointer (NON-NULL) before deferencing it.
+static virStoragePoolPtr +esxStorageBackendISCSIPoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{
+ if (hostInternetScsiHba->configuredStaticTarget) { + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + md5_buffer(target->iScsiName, strlen(target->iScsiName), md5); + + if (memcmp(uuid, md5, VIR_UUID_STRING_BUFLEN) == 0) { + break; + } + } + }
Again, the if around the for is not necessary here.
+ if (target == NULL) { + /* pool not found */ + goto cleanup; + }
Error reporting is missing here.
+static int +esxStorageBackendISCSIPoolGetInfo(virStoragePoolPtr pool ATTRIBUTE_UNUSED, + virStoragePoolInfoPtr info) +{ + /* these fields are not valid for iSCSI pool */ + info->allocation = info->capacity = info->available = 0; + info->state = esxVI_Boolean_True;
This is wrong. info->state should be VIR_STORAGE_POOL_RUNNING or another value from the virStoragePoolState if the state can be determined in more detail.
+static char * +esxStorageBackendISCSIPoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags) +{
+ if (hostInternetScsiHba->configuredStaticTarget) { + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + if (STREQ(target->iScsiName, pool->name)) { + break; + } + } + }
Again, the if around the for is not necessary here.
+ if (target == NULL) { + goto cleanup; + }
Error reporting is missing here.
+static int +esxStorageBackendISCSIPoolListStorageVolumes(virStoragePoolPtr pool, + char **const names, + int maxnames) +{
+ if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + /* O^2 but still faster than hash given N is not that large */ + for (scsiLun = scsiLunList; scsiLun != NULL && count < maxnames; + scsiLun = scsiLun->_next) { + for (hostScsiTopologyLun = hostScsiTopologyLunList; + hostScsiTopologyLun != NULL && count < maxnames; + hostScsiTopologyLun = hostScsiTopologyLun->_next) { + if (STREQ(hostScsiTopologyLun->scsiLun, scsiLun->key)) { + names[count] = strdup(scsiLun->deviceName);
What does a typical device name look like? Is it easily distinguishable from the volume names of the VMFS backend driver? Can you give some examples? [AB]: Device name looks similar to a device path in linux, for instance on my sample machine device path for a iSCSI lun is "/vmfs/devices/disks/t10.F405E46494C45425F49615840723D214D476A4D2E457B416". As mentioned in one of my comments, technically we can use "scsiLun->canonicalName" as "t10.F405E46494C45425F49615840723D214D476A4D2E457B416" is nothing but a canonicalName for that iSCSI lun. Only reason I used deviceName was it is a mandatory field, whereas, canonicalName is an options field inside scsiLun data object.
+static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByName(virStoragePoolPtr pool, + const char *name) +{
+ for (scsiLun = scsiLunList; scsiLun != NULL; + scsiLun = scsiLun->_next) { + if (STREQ(scsiLun->deviceName, name)) { + /** + * ScsiLun provides an UUID field that is unique accross + * multiple servers. But this field length is ~55 characters + * compute MD5 hash to transform it to an acceptable + * libvirt format + */ + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string);
I'm not sure if this is the best approach. What does a typical ScsiLun look like? Can you give some examples? [AB]: I think you mean sample scssilun->uuid, but I am printing the compelte structure just incase along with specific mention of scsilun->uuid: (gdb) p *scsiLun $2 = {_next = 0x0, _type = esxVI_Type_HostScsiDisk, deviceName = 0x682da0 "/vmfs/devices/disks/t10.F405E46494C45425F49615840723D214D476A4D2E457B416", deviceType = 0x704af0 "disk", lunType = 0x704b70 "disk", operationalState = 0x707570, uuid = 0x6832a0 "01000000004f69514870322d414d674a2d4e754b61564952545541", alternateName = 0x618660, canonicalName = 0x683160 "t10.F405E46494C45425F49615840723D214D476A4D2E457B416", capabilities = 0x0, descriptor = 0x0, displayName = 0x0, durableName = 0x61b340, key = 0x704b10 "key-vim.host.ScsiDisk-01000000004f69514870322d414d674a2d4e754b61564952545541", model = 0x704bb0 "VIRTUAL-DISK ", queueDepth = 0x0, revision = 0x704bd0 "0 ", scsiLevel = 0x704bf0, serialNumber = 0x704c10 "unavailable", standardInquiry = 0x705590, vendor = 0x704b90 "OPNFILER"} scsilun->uuid looks like:(gdb) p scsiLun->uuid $4 = 0x6832a0 "01000000004f69514870322d414d674a2d4e754b61564952545541".
+static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByPath(virConnectPtr conn, const char *path) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = conn->storagePrivateData; + char *poolName = NULL; + esxVI_ScsiLun *scsiLunList = NULL; + const esxVI_ScsiLun *scsiLun = NULL; + const esxVI_HostScsiDisk *hostScsiDisk = NULL;
Remove the const here...[AB]: As the variable is used just to iterate the list IMHO "const" assures the reader that the memory need not to be freed. I aggree there is an extra static_cast needed to perform "dynamic_cast" operation but I thought code readibility will surpass ths cost. Please let me know if this can be accepted.
+ /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + + if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + for (scsiLun = scsiLunList ; scsiLun != NULL; + scsiLun = scsiLun->_next) { + hostScsiDisk = + esxVI_HostScsiDisk_DynamicCast((esxVI_ScsiLun *)scsiLun);
.. so that the (esxVI_ScsiLun *) cast is no longer necessary.
+ if (hostScsiDisk != NULL && + STREQ(hostScsiDisk->devicePath, path)) { + /* Found matching device */ + if (esxVI_LookupStoragePoolNameByScsiLunKey( + priv->primary, hostScsiDisk->key, &poolName) < 0) { + goto cleanup; + } + + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string); + + volume = virGetStorageVol(conn, poolName, path, uuid_string);
Passing the path is not correct I think, assuming that the volume name and path are not the same for iSCSI. [AB]: For user deviceName and devicePath should be same. HostDevice dataobjects defines deviceName as:
"deviceName xsd:string The name of the device on the host. For example, /dev/cdrom or \\serverX\device_name. "As mentioned earlier, sample deviceName looks like:"/vmfs/devices/disks/t10.F405E46494C45425F49615840723D214D476A4D2E457B416"; it is a valid ESX device path as well. I will document it to make it more clear.
+virStorageDriver esxStorageBackendISCSIDrv = { + .name = "ESX ISCSI backend", + .open = NULL, /* 0.10.0 */ + .close = NULL, /* 0.10.0 */ + .numOfPools = esxStorageBackendISCSINumberOfStoragePools, /* 0.10.0 */ + .listPools = esxStorageBackendISCSIListStoragePools, /* 0.10.0 */ + .poolLookupByName = esxStorageBackendISCSIPoolLookupByName, /* 0.10.0 */ + .poolLookupByUUID = esxStorageBackendISCSIPoolLookupByUUID, /* 0.10.0 */ + .poolRefresh = esxStorageBackendISCSIPoolRefresh, /* 0.10.0 */ + .poolGetInfo = esxStorageBackendISCSIPoolGetInfo, /* 0.10.0 */ + .poolGetXMLDesc = esxStorageBackendISCSIPoolGetXMLDesc, /* 0.10.0 */ + .poolNumOfVolumes = esxStorageBackendISCSIPoolNumberOfStorageVolumes, /* 0.10.0 */ + .poolListVolumes = esxStorageBackendISCSIPoolListStorageVolumes, /* 0.10.0 */ + .volLookupByName = esxStorageBackendISCSIVolumeLookupByName, /* 0.10.0 */ + .volLookupByKey = esxStorageBackendISCSIVolumeLookupByKey, /* 0.10.0 */ + .volLookupByPath = esxStorageBackendISCSIVolumeLookupByPath, /* 0.10.0 */ + .volCreateXML = esxStorageBackendISCSIVolumeCreateXML, /* 0.10.0 */ + .volCreateXMLFrom = esxStorageBackendISCSIVolumeCreateXMLFrom, /* 0.10.0 */ + .volGetXMLDesc = esxStorageBackendISCSIVolumeGetXMLDesc, /* 0.10.0 */ + .volDelete = esxStorageBackendISCSIVolumeDelete, /* 0.10.0 */ + .volWipe = esxStorageBackendISCSIVolumeWipe, /* 0.10.0 */ + .volGetPath = esxStorageBackendISCSIVolumeGetPath, /* 0.10.0 */ + +};
The version number here are outdated now. It should be 0.10.2.
diff --git a/src/esx/esx_storage_backend_iscsi.h b/src/esx/esx_storage_backend_iscsi.h new file mode 100644 index 0000000..ca756ac --- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.h @@ -0,0 +1,31 @@ +/* + * esx_storage_backend_iscsi.h: ESX storage backend for iSCSI handling + * + * Copyright (C) 2007-2008 Red Hat, Inc.
Again, that's not the right copyright notice.
diff --git a/src/esx/esx_storage_backend_vmfs.c b/src/esx/esx_storage_backend_vmfs.c new file mode 100644 index 0000000..6550196 --- /dev/null +++ b/src/esx/esx_storage_backend_vmfs.c @@ -0,0 +1,1471 @@ + +/* + * esx_storage_backend_vmfs.c: ESX storage backend for VMFS datastores + * + * Copyright (C) 2010-2011 Red Hat, Inc. + * Copyright (C) 2010 Matthias Bolte <matthias.bolte@googlemail.com>
Put your copyright line here in the form of [AB]: I intentionally left it "unchanged". Almost all the code written in this file is written by you, I just renamed the file and I do not want to put myself as an author just for renaming the filename :). Copyright (C) 2012 Ata E Husain Bohra <your email address>
instead of an author list on the end of the license statement.
+#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h>
This include is unused and can be removed.
This is as far as I got today with the review. I'll continue in the next days, hopefully it won't take me weeks again to get back to this :) [AB]: I highly appreciate your attention and time on my patches. I'm looking forward for your comments on above mentioned points before updating a newer version for the patch. -- Matthias Bolte http://photron.blogspot.com Thanks again! Regards,Ata

2012/9/9 Ata Bohra <ata.husain@hotmail.com>
Please see inline.
+ if (hostInternetScsiHba->configuredStaticTarget) { + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; target = target->_next) { + ++count; + } + }
The if around the for loop is not necessary ans can be removed. [AB]: I guess I am spoiled with the coding standards followed at my workplace; we try to protect conditional loops with parenthesis even if its a single line code statements. I will make necessary correction if this is not acceptable style with Libvirt community.
No that's not what i meant. I didn't mean the {} around the body of the for loop. I meant the if around the for loop: Your code has this structure if (...) { for (...) { } } I meant to simplify it to for (...) { } because the condition of the if is already tested as part of the for loop conditional part. So there is no gain in wrapping the for in an additional if here.
+ * hosts, using it to compute key + */ + + md5_buffer(target->iScsiName, strlen(target->iScsiName), md5);
esxVI_LookupHostInternetScsiHbaStaticTargetByName has an occurrence parameter that is set to esxVI_Occurrence_OptionalItem. This means that esxVI_LookupHostInternetScsiHbaStaticTargetByName is allowed to return target as NULL. But you're blindly dereferencing it here. I think occurrence should be passed as esxVI_Occurrence_RequiredItem here instead. [AB]: "poolLookupByName" is also used by esx_storage_drive.c:esxStorageGetBackendDriver(), in this function we want to go over both VMFS as well iSCSI pools list to determine the matching backend driver. I think the flaw is I need to check for valid target pointer (NON-NULL) before deferencing it.
Ah, I didn't see the whole concept. So I misunderstood how this is supposed to work. Now it makes sense that there is no error reporting in some places.
+static int +esxStorageBackendISCSIPoolListStorageVolumes(virStoragePoolPtr pool, + char **const names, + int maxnames) +{
+ if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + /* O^2 but still faster than hash given N is not that large */ + for (scsiLun = scsiLunList; scsiLun != NULL && count < maxnames; + scsiLun = scsiLun->_next) { + for (hostScsiTopologyLun = hostScsiTopologyLunList; + hostScsiTopologyLun != NULL && count < maxnames; + hostScsiTopologyLun = hostScsiTopologyLun->_next) { + if (STREQ(hostScsiTopologyLun->scsiLun, scsiLun->key)) { + names[count] = strdup(scsiLun->deviceName);
What does a typical device name look like? Is it easily distinguishable from the volume names of the VMFS backend driver? Can you give some examples? [AB]: Device name looks similar to a device path in linux, for instance on my sample machine device path for a iSCSI lun is "/vmfs/devices/disks/t10.F405E46494C45425F49615840723D214D476A4D2E457B416".
As mentioned in one of my comments, technically we can use "scsiLun->canonicalName" as "t10.F405E46494C45425F49615840723D214D476A4D2E457B416" is nothing but a canonicalName for that iSCSI lun. Only reason I used deviceName was it is a mandatory field, whereas, canonicalName is an options field inside scsiLun data object.
That's good. So the different backends can easily detect that a volume is meant for them.
+static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByName(virStoragePoolPtr pool, + const char *name) +{
+ for (scsiLun = scsiLunList; scsiLun != NULL; + scsiLun = scsiLun->_next) { + if (STREQ(scsiLun->deviceName, name)) { + /** + * ScsiLun provides an UUID field that is unique accross + * multiple servers. But this field length is ~55 characters + * compute MD5 hash to transform it to an acceptable + * libvirt format + */ + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string);
I'm not sure if this is the best approach. What does a typical ScsiLun look like? Can you give some examples?
[AB]: I think you mean sample scssilun->uuid, but I am printing the compelte structure just incase along with specific mention of scsilun->uuid:
Yes, I meant the ScsiLun's UUID.
(gdb) p *scsiLun $2 = {_next = 0x0, _type = esxVI_Type_HostScsiDisk, deviceName = 0x682da0 "/vmfs/devices/disks/t10.F405E46494C45425F49615840723D214D476A4D2E457B416", deviceType = 0x704af0 "disk", lunType = 0x704b70 "disk", operationalState = 0x707570, uuid = 0x6832a0 "01000000004f69514870322d414d674a2d4e754b61564952545541", alternateName = 0x618660, canonicalName = 0x683160 "t10.F405E46494C45425F49615840723D214D476A4D2E457B416", capabilities = 0x0, descriptor = 0x0, displayName = 0x0, durableName = 0x61b340, key = 0x704b10 "key-vim.host.ScsiDisk-01000000004f69514870322d414d674a2d4e754b61564952545541", model = 0x704bb0 "VIRTUAL-DISK ", queueDepth = 0x0, revision = 0x704bd0 "0 ", scsiLevel = 0x704bf0, serialNumber = 0x704c10 "unavailable", standardInquiry = 0x705590, vendor = 0x704b90 "OPNFILER"} scsilun->uuid looks like: (gdb) p scsiLun->uuid $4 = 0x6832a0 "01000000004f69514870322d414d674a2d4e754b61564952545541".
Okay, no I'm convinced that taking the MD5 sum of this long UUID is a good and robust approach.
+static virStorageVolPtr +esxStorageBackendISCSIVolumeLookupByPath(virConnectPtr conn, const char *path) +{ + virStorageVolPtr volume = NULL; + esxPrivate *priv = conn->storagePrivateData; + char *poolName = NULL; + esxVI_ScsiLun *scsiLunList = NULL; + const esxVI_ScsiLun *scsiLun = NULL; + const esxVI_HostScsiDisk *hostScsiDisk = NULL;
Remove the const here...
[AB]: As the variable is used just to iterate the list IMHO "const" assures the reader that the memory need not to be freed. I aggree there is an extra static_cast needed to perform "dynamic_cast" operation but I thought code readibility will surpass ths cost. Please let me know if this can be accepted.
Technically you're right. The problem I see here is that the libvirt codebase doesn't follow this more strict const handling and trying to apply it in some places with just result in additional cast to remove the const again. Therefore, I'd suggest to not apply this stricter const handling.
+ /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + unsigned char md5[MD5_DIGEST_SIZE]; + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + + if (esxVI_LookupScsiLunList(priv->primary, &scsiLunList) < 0) { + goto cleanup; + } + + for (scsiLun = scsiLunList ; scsiLun != NULL; + scsiLun = scsiLun->_next) { + hostScsiDisk = + esxVI_HostScsiDisk_DynamicCast((esxVI_ScsiLun *)scsiLun);
.. so that the (esxVI_ScsiLun *) cast is no longer necessary.
+ if (hostScsiDisk != NULL && + STREQ(hostScsiDisk->devicePath, path)) { + /* Found matching device */ + if (esxVI_LookupStoragePoolNameByScsiLunKey( + priv->primary, hostScsiDisk->key, &poolName) < 0) { + goto cleanup; + } + + md5_buffer(scsiLun->uuid, strlen(scsiLun->uuid), md5); + + virUUIDFormat(md5, uuid_string); + + volume = virGetStorageVol(conn, poolName, path, uuid_string);
Passing the path is not correct I think, assuming that the volume name and path are not the same for iSCSI.
[AB]: For user deviceName and devicePath should be same. HostDevice dataobjects defines deviceName as: "deviceName xsd:string The name of the device on the host. For example, /dev/cdrom or \\serverX\device_name. " As mentioned earlier, sample deviceName looks like: "/vmfs/devices/disks/t10.F405E46494C45425F49615840723D214D476A4D2E457B416"; it is a valid ESX device path as well.
I will document it to make it more clear.
Ah, okay. I wasn't aware of that. I don't have an iSCSI setup at hand at the moment. So I cannot test how things work and need to rely on your knowledge in this aspect. -- Matthias Bolte http://photron.blogspot.com

2012/8/20 Ata E Husain Bohra <ata.husain@hotmail.com>:
The patch refactors the current ESX storage driver due to following reasons:
1. Given most of the public APIs exposed by the storage driver in Libvirt remains same, ESX storage driver should not implement logic specific for only one supported format (current implementation only supports VMFS). 2. Decoupling interface from specific storage implementation gives us an extensible design to hook implementation for other supported storage formats.
This patch refactors the current driver to implement it as a facade pattern i.e. the driver exposes all the public libvirt APIs, but uses backend drivers to get the required task done. The backend drivers provide implementation specific to the type of storage device.
This patch is quite huge. Maybe you could split it up into two patches. The first one for adding the backend structure to the storage driver and moving the current driver code to its own backend. The second patch for adding the new iSCSI backend. -- Matthias Bolte http://photron.blogspot.com

Date: Sun, 9 Sep 2012 21:12:43 +0200 Subject: Re: [libvirt] [PATCH] Refactor ESX storage driver and add iSCSI support From: matthias.bolte@googlemail.com To: ata.husain@hotmail.com CC: libvir-list@redhat.com
This patch is quite huge. Maybe you could split it up into two patches. The first one for adding the backend structure to the storage driver and moving the current driver code to its own backend. The second patch for adding the new iSCSI backend.
At first, I was thinking of doing the same but somehow felt that presenting everything in one patch may make more sense. But your feedback suggests that breaking the patch would help better management as well as review. Will do as suggested in the next patch version. Thanks!Ata

2012/9/9 Ata Bohra <ata.husain@hotmail.com>:
Date: Sun, 9 Sep 2012 21:12:43 +0200
Subject: Re: [libvirt] [PATCH] Refactor ESX storage driver and add iSCSI support From: matthias.bolte@googlemail.com To: ata.husain@hotmail.com CC: libvir-list@redhat.com
This patch is quite huge. Maybe you could split it up into two patches. The first one for adding the backend structure to the storage driver and moving the current driver code to its own backend. The second patch for adding the new iSCSI backend.
At first, I was thinking of doing the same but somehow felt that presenting everything in one patch may make more sense. But your feedback suggests that breaking the patch would help better management as well as review. Will do as suggested in the next patch version.
Thanks!Ata
With over 4000 lines of change your patch is quite huge, that makes it harder to understand all the changes you're making. There are 3 major parts in this patch. 1. Adding the backend concept. 2. Move the current driver logic to a backend. 3. Adding a new backend. Doing 1 and 2 in one patch and 3 in a second patch will simplify the review process, because one is not forced to focus on all 3 aspects as once :) -- Matthias Bolte http://photron.blogspot.com

Finally, here's the second part of my review. 2012/8/20 Ata E Husain Bohra <ata.husain@hotmail.com>:
diff --git a/src/esx/esx_storage_backend_vmfs.h b/src/esx/esx_storage_backend_vmfs.h new file mode 100644 index 0000000..d3adf73 --- /dev/null +++ b/src/esx/esx_storage_backend_vmfs.h @@ -0,0 +1,31 @@ +/* + * esx_storage_backend_vmfs.h: ESX storage backend for VMFS datastores + * + * Copyright (C) 2007-2008 Red Hat, Inc.
Wrong copyright.
+ * Author: Ata E Husain Bohra (ata.husain@hotmail.com) + */ + +#ifndef __ESX_STORAGE_BACKEND_VMFS_H__ +# define __ESX_STORAGE_BACKEND_VMFS_H__ + +#include "driver.h"
Should be # include "driver.h" to make the syntax-check happy.
diff --git a/src/esx/esx_storage_driver.c b/src/esx/esx_storage_driver.c index 348bd62..d4e81f3 100644 --- a/src/esx/esx_storage_driver.c +++ b/src/esx/esx_storage_driver.c
-/* - * The UUID of a storage pool is the MD5 sum of it's mount path. Therefore, - * verify that UUID and MD5 sum match in size, because we rely on that. +/** + * ESX storage driver implements a facade pattern; + * the driver exposes the routines supported by Libvirt + * public interface to manage ESX storage devices. Internally + * it uses backend drivers to perform the required task. */ -verify(MD5_DIGEST_SIZE == VIR_UUID_BUFLEN); +enum { + ISCSI = 0, + VMFS, + LAST_DRIVER +};
+static virStorageDriverPtr backendDrv[LAST_DRIVER] = {NULL};
Instead of touching backendDrv in each esxStorageOpen and esxStorageClose call you should just initialize it here: static virStorageDriverPtr backendDrv[] = { &esxStorageBackendISCSIDrv, &esxStorageBackendVMFSDrv };
+esxStorageGetBackendDriver(virConnectPtr conn, const char *name, + virStorageDriverPtr *backend)
Rename the name parameter to poolName to make its meaning more clear. Almost all functions call esxStorageGetBackendDriver, so this approach slows down the dirver usage. A better appraoch would be to store a pointer to the backend with the virStoragePool and virStorageVol objects, so the overhead of calling esxStorageGetBackendDriver for each operation can be avoided. Currently virStoragePool and virStorageVol objects don't allow to store a privateData pointer, so we'll need to discuss/implement this first: https://www.redhat.com/archives/libvir-list/2012-October/msg00196.html
@@ -297,65 +256,31 @@ static virStoragePoolPtr esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid) {
- if (datastore == NULL) { - virUUIDFormat(uuid, uuid_string); - - virReportError(VIR_ERR_NO_STORAGE_POOL, - _("Could not find datastore with UUID '%s'"), - uuid_string); - - goto cleanup; - } - - if (esxVI_GetStringValue(datastore, "summary.name", &name, - esxVI_Occurrence_RequiredItem) < 0) { - goto cleanup; + if (pool == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find storage pool with uuid '%s'"), uuid);
uuid isn't a printable string here. You need to format it to a string first using virUUIDFormat as it was done befor your patch here.
@@ -1432,97 +633,39 @@ esxStorageVolumeDelete(virStorageVolPtr volume, unsigned int flags) static int esxStorageVolumeWipe(virStorageVolPtr volume, unsigned int flags) {
+ if (esxStorageGetBackendDriver(volume->conn, volume->pool, &backend) < 0 || + backend->volDelete(volume , flags) < 0) {
Copy&paste error, should be volWipe instead of volDelete.
diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index 65e1d9a..0e647d4 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -3011,7 +3011,7 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
if (*datastore == NULL && occurrence != esxVI_Occurrence_OptionalItem) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("Could not find datastore with name '%s'"), name); + _("Could not find datastore with name '%s'"), name);
Why this indentation change? This changed should be removed from the patch.
@@ -3118,7 +3118,8 @@ esxVI_LookupDatastoreByAbsolutePath(esxVI_Context *ctx, int esxVI_LookupDatastoreHostMount(esxVI_Context *ctx, esxVI_ManagedObjectReference *datastore, - esxVI_DatastoreHostMount **hostMount) + esxVI_DatastoreHostMount **hostMount, + esxVI_Occurrence occurrence) { int result = -1; esxVI_String *propertyNameList = NULL; @@ -3166,9 +3167,9 @@ esxVI_LookupDatastoreHostMount(esxVI_Context *ctx, break; }
- if (*hostMount == NULL) { + if (*hostMount == NULL && occurrence != esxVI_Occurrence_OptionalItem) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Could not lookup datastore host mount")); + _("Could not lookup datastore host mount"));
Again, unnecessary indentation change.
+int +esxVI_LookupScsiLunList(esxVI_Context *ctx, + esxVI_ScsiLun **ret) +{ + int result = -1; + esxVI_DynamicProperty *dynamicProperty = NULL; + esxVI_ObjectContent *hostSystem = NULL; + esxVI_String *propertyNameList = NULL; + esxVI_ScsiLun *scsiLunList = NULL; + + if (esxVI_String_AppendValueToList(&propertyNameList, + "config.storageDevice.scsiLun\0") < 0 || + esxVI_LookupHostSystemProperties( + ctx, propertyNameList, &hostSystem) < 0) { + goto cleanup; + } + + for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, + "config.storageDevice.scsiLun")) { + if (esxVI_ScsiLun_CastListFromAnyType(dynamicProperty->val, + &scsiLunList) < 0) { + goto cleanup; + } + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + if (scsiLunList == NULL) { + goto cleanup; + } + + /** + * FIXME: deep list copy operation fails with error: + * " libvir: ESX Driver error : + * internal error Call to esxVI_HostDevice_Free for + * unexpected type 'HostScsiDisk' " + * HostScsiDisk extends ScsiLun + */ + *ret = scsiLunList; + scsiLunList = NULL; /* prevent double free */
You should have reported this problem. There was a bug in the dynamic dispatch that resulted in dynamic dispatch errors when two types were not direct ancestors. HostDevice and HostScsiDisk are not directly related, because ScsiLun sits in between. This patch should fix this problem. Could you verify this? https://www.redhat.com/archives/libvir-list/2012-October/msg00197.html
diff --git a/src/esx/esx_vi_generator.input b/src/esx/esx_vi_generator.input index c4a3e56..6e50be5 100644
object HostIpConfig Boolean dhcp r String ipAddress o String subnetMask o -end
Don't remove this end.
@@ -416,6 +622,19 @@ object HostPortGroupSpec Int vlanId r String vswitchName r HostNetworkPolicy policy r
An end is missing here.
@@ -467,7 +717,6 @@ object HostVirtualSwitchSpec HostVirtualSwitchBridge bridge o HostNetworkPolicy policy o Int mtu o -end
Don't remove this end.
object ServiceContent ManagedObjectReference rootFolder r ManagedObjectReference propertyCollector r @@ -1135,6 +1434,11 @@ end method RemoveVirtualSwitch ManagedObjectReference _this r String vswitchName r
An end is missing here. Finally, make sure to run make syntax-check and ensure that it passes. -- Matthias Bolte http://photron.blogspot.com

Sorry for late reply! Thanks Mathias for the review comments. Please see inline.> Date: Sat, 6 Oct 2012 18:58:21 +0200
Subject: Re: [libvirt] [PATCH] Refactor ESX storage driver and add iSCSI support From: matthias.bolte@googlemail.com To: ata.husain@hotmail.com CC: libvir-list@redhat.com
Finally, here's the second part of my review.
Almost all functions call esxStorageGetBackendDriver, so this approach slows down the dirver usage. A better appraoch would be to store a pointer to the backend with the virStoragePool and virStorageVol objects, so the overhead of calling esxStorageGetBackendDriver for each operation can be avoided.
Currently virStoragePool and virStorageVol objects don't allow to store a privateData pointer, so we'll need to discuss/implement this first:
https://www.redhat.com/archives/libvir-list/2012-October/msg00196.html [AB]: As per Daniels response, it seems he is OK with the approach proposed by you. I would modify the code to do following:Modify internal _virStoragePool/Vol to store "backend driver pointer" and a freefunction. virGetStoragePool/Vol will assign the user passed value to these variables. virStoragePoolDispose/VolDispose would use the free function to cleanup. I need to ask one question, I did not completly understood the role of "freefunction"in above proposal? As I understand backend driver in this case does not perform open/close operation, so does it means they can be NULL for this particular case? >> You should have reported this problem. There was a bug in the dynamic dispatch that resulted in dynamic dispatch errors when two types were not direct ancestors. HostDevice and HostScsiDisk are not directly related, because ScsiLun sits in between.
This patch should fix this problem. Could you verify this? https://www.redhat.com/archives/libvir-list/2012-October/msg00197.html [AB] Certainly I would incorporate the changes. Thanks for the fix!
Finally, make sure to run make syntax-check and ensure that it passes. I am working on getting the next version with all proposed changes (including splittingthis massive diff into two parts). Sorry for late replies but lately not getting much timeto finish up this work. -- Matthias Bolte http://photron.blogspot.com Thanks!Ata

2012/10/15 Ata Bohra <ata.husain@hotmail.com>:
Sorry for late reply!
Thanks Mathias for the review comments. Please see inline.
Date: Sat, 6 Oct 2012 18:58:21 +0200
Subject: Re: [libvirt] [PATCH] Refactor ESX storage driver and add iSCSI support From: matthias.bolte@googlemail.com To: ata.husain@hotmail.com CC: libvir-list@redhat.com
Finally, here's the second part of my review.
Almost all functions call esxStorageGetBackendDriver, so this approach slows down the dirver usage. A better appraoch would be to store a pointer to the backend with the virStoragePool and virStorageVol objects, so the overhead of calling esxStorageGetBackendDriver for each operation can be avoided.
Currently virStoragePool and virStorageVol objects don't allow to store a privateData pointer, so we'll need to discuss/implement this first:
https://www.redhat.com/archives/libvir-list/2012-October/msg00196.html [AB]: As per Daniels response, it seems he is OK with the approach proposed by you. I would modify the code to do following: Modify internal _virStoragePool/Vol to store "backend driver pointer" and a free function. virGetStoragePool/Vol will assign the user passed value to these variables. virStoragePoolDispose/VolDispose would use the free function to cleanup.
I need to ask one question, I did not completly understood the role of "freefunction" in above proposal? As I understand backend driver in this case does not perform open/close operation, so does it means they can be NULL for this particular case?
The privateData pointer in _virStoragePool/Vol structs is meant for generic use. The free function is there to allow use cases were you need to do some cleanup on the privateData when a _virStoragePool/Vol struct is freed. This is a general concept that is used in multiple places in libvirt. You're correct, in case of the ESX storage driver the free function can be NULL because we don't need to cleanup the backend driver pointer stored there when a _virStoragePool/Vol struct is free. -- Matthias Bolte http://photron.blogspot.com
participants (3)
-
Ata Bohra
-
Ata E Husain Bohra
-
Matthias Bolte