[libvirt] libvirt [PATCHv2 2/2] Add iSCSI backend storage driver for ESX.

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 | 1 + src/esx/esx_storage_backend_iscsi.c | 807 +++++++++++++++++++++++++++++++++++ src/esx/esx_storage_backend_iscsi.h | 29 ++ src/esx/esx_storage_driver.c | 8 +- src/esx/esx_vi.c | 332 ++++++++++++++ src/esx/esx_vi.h | 18 + src/esx/esx_vi_generator.input | 302 +++++++++++++ src/esx/esx_vi_generator.py | 19 + 8 files changed, 1515 insertions(+), 1 deletion(-) create mode 100644 src/esx/esx_storage_backend_iscsi.c create mode 100644 src/esx/esx_storage_backend_iscsi.h diff --git a/src/Makefile.am b/src/Makefile.am index 4026a15..1668b84 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -494,6 +494,7 @@ ESX_DRIVER_SOURCES = \ 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 \ diff --git a/src/esx/esx_storage_backend_iscsi.c b/src/esx/esx_storage_backend_iscsi.c new file mode 100644 index 0000000..fa4bf45 --- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.c @@ -0,0 +1,807 @@ +/* + * esx_storage_backend_iscsi.c: ESX storage backend for iSCSI handling + * + * Copyright (C) 2012 Ata E Husain Bohra <ata.husain@hotmail.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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> + +#include <string.h> +#include <stdio.h> +#include <unistd.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_iscsi.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; + const esxVI_HostInternetScsiHbaStaticTarget *target = NULL; + bool success = false; + + if (esxVI_LookupHostInternetScsiHba( + priv->primary, &hostInternetScsiHba) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to obtain iSCSI adapter")); + goto cleanup; + } + + /* FIXME: code looks for software iSCSI adapter only */ + 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. + */ + 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; + const esxVI_HostInternetScsiHbaStaticTarget *target = 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; + } + + /* FIXME: code looks for software iSCSI adapter only */ + 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. + */ + 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; + + /* lookup routine are used by the base driver to determine + * appropriate backend driver, lookup targetName as optional + * parameter + */ + if (esxVI_LookupHostInternetScsiHbaStaticTargetByName( + priv->primary, name, &target, esxVI_Occurrence_OptionalItem) < 0 || + target == NULL) { + goto cleanup; + } + + /** + * HostInternetScsiHbaStaticTarget does not provide a uuid field, + * but iScsiName (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, &esxStorageBackendISCSIDrv, NULL); + + 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; + } + + /* FIXME: code just looks for software iSCSI adapter */ + if (hostInternetScsiHba == NULL) { + /* iSCSI adapter may not be enabled for this host */ + return NULL; + } + + 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, error handling done by the base driver */ + goto cleanup; + } + + pool = virGetStoragePool(conn, + target->iScsiName, + md5, + &esxStorageBackendISCSIDrv, + NULL); + + 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 = VIR_STORAGE_POOL_RUNNING; + + 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; + } + + for (target = hostInternetScsiHba->configuredStaticTarget; + target != NULL; + target = target->_next) { + if (STREQ(target->iScsiName, pool->name)) { + break; + } + } + + if (target == NULL) { + /* pool not found */ + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find storage pool with name '%s'"), pool->name); + 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, + &esxStorageBackendISCSIDrv, NULL); + 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; + 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(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, + &esxStorageBackendISCSIDrv, NULL); + 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, + &esxStorageBackendISCSIDrv, + NULL); + 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, /* 1.0.0 */ + .close = NULL, /* 1.0.0 */ + .numOfPools = esxStorageBackendISCSINumberOfStoragePools, /* 1.0.0 */ + .listPools = esxStorageBackendISCSIListStoragePools, /* 1.0.0 */ + .poolLookupByName = esxStorageBackendISCSIPoolLookupByName, /* 1.0.0 */ + .poolLookupByUUID = esxStorageBackendISCSIPoolLookupByUUID, /* 1.0.0 */ + .poolRefresh = esxStorageBackendISCSIPoolRefresh, /* 1.0.0 */ + .poolGetInfo = esxStorageBackendISCSIPoolGetInfo, /* 1.0.0 */ + .poolGetXMLDesc = esxStorageBackendISCSIPoolGetXMLDesc, /* 1.0.0 */ + .poolNumOfVolumes = esxStorageBackendISCSIPoolNumberOfStorageVolumes, /* 1.0.0 */ + .poolListVolumes = esxStorageBackendISCSIPoolListStorageVolumes, /* 1.0.0 */ + .volLookupByName = esxStorageBackendISCSIVolumeLookupByName, /* 1.0.0 */ + .volLookupByKey = esxStorageBackendISCSIVolumeLookupByKey, /* 1.0.0 */ + .volLookupByPath = esxStorageBackendISCSIVolumeLookupByPath, /* 1.0.0 */ + .volCreateXML = esxStorageBackendISCSIVolumeCreateXML, /* 1.0.0 */ + .volCreateXMLFrom = esxStorageBackendISCSIVolumeCreateXMLFrom, /* 1.0.0 */ + .volGetXMLDesc = esxStorageBackendISCSIVolumeGetXMLDesc, /* 1.0.0 */ + .volDelete = esxStorageBackendISCSIVolumeDelete, /* 1.0.0 */ + .volWipe = esxStorageBackendISCSIVolumeWipe, /* 1.0.0 */ + .volGetPath = esxStorageBackendISCSIVolumeGetPath, /* 1.0.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..f78da46 --- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.h @@ -0,0 +1,29 @@ +/* + * esx_storage_backend_iscsi.h: ESX storage backend for iSCSI handling + * + * Copyright (C) 2012 Ata E Husain Bohra <ata.husain@hotmail.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/>. + * + */ + +#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_driver.c b/src/esx/esx_storage_driver.c index 5530ea7..6952c69 100644 --- a/src/esx/esx_storage_driver.c +++ b/src/esx/esx_storage_driver.c @@ -31,6 +31,7 @@ #include "esx_private.h" #include "esx_storage_driver.h" #include "esx_storage_backend_vmfs.h" +#include "esx_storage_backend_iscsi.h" #define VIR_FROM_THIS VIR_FROM_ESX @@ -42,11 +43,13 @@ */ enum { VMFS = 0, + ISCSI, LAST_DRIVER }; static virStorageDriverPtr backendDrv[] = { - &esxStorageBackendVMFSDrv + &esxStorageBackendVMFSDrv, + &esxStorageBackendISCSIDrv }; static virDrvOpenStatus @@ -465,9 +468,12 @@ esxStorageVolumeLookupByPath(virConnectPtr conn, const char *path) * 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); diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index 9fb2c11..12100d7 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -4875,5 +4875,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; + } + + 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 || + esxVI_ScsiLun_DeepCopyList(ret, scsiLunList) < 0) { + goto cleanup; + } + + 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; + } + } + } + /* hostScsiTopologyLun iteration done, terminate loop */ + 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 d7895a0..8e56044 100644 --- a/src/esx/esx_vi.h +++ b/src/esx/esx_vi.h @@ -528,6 +528,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..21f5b10 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,12 @@ enum VirtualMachinePowerState end +enum vStorageSupport + vStorageUnknown +end + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Objects # @@ -262,6 +276,13 @@ object HostAutoStartManagerConfig AutoStartPowerInfo powerInfo ol end +object HostBlockAdapterTargetTransport extends HostTargetTransport +end + + +object HostBlockHba extends HostHostBusAdapter +end + object HostConfigManager ManagedObjectReference cpuScheduler o @@ -309,6 +330,31 @@ object HostDatastoreBrowserSearchSpec Boolean sortFoldersFirst o 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 @@ -323,6 +369,164 @@ object HostIpConfig String subnetMask o 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 HostMountInfo String path o @@ -418,12 +622,55 @@ object HostPortGroupSpec HostNetworkPolicy policy r end +object HostParallelScsiHba extends HostHostBusAdapter +end + + +object HostParallelScsiTargetTransport extends HostTargetTransport +end + + +object HostScsiDisk extends ScsiLun + HostDiskDimensionsLba capacity r + String devicePath r +end + object HostScsiDiskPartition String diskName r Int partition r 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 @@ -526,12 +773,22 @@ object ObjectUpdate MissingProperty missingSet i 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 +921,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 @@ -1138,6 +1434,12 @@ method RemoveVirtualSwitch end +method RescanHba + ManagedObjectReference _this r + String hbaDevice r +end + + method RetrieveProperties returns ObjectContent ol ManagedObjectReference _this:propertyCollector r PropertyFilterSpec specSet rl diff --git a/src/esx/esx_vi_generator.py b/src/esx/esx_vi_generator.py index 2883ac0..af4e7e8 100755 --- a/src/esx/esx_vi_generator.py +++ b/src/esx/esx_vi_generator.py @@ -1532,6 +1532,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 | @@ -1543,6 +1558,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/11/10 Ata E Husain Bohra <ata.husain@hotmail.com>:
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.
I don't have time for this one today anymore. I'll definitely come back to it later this week. -- Matthias Bolte http://photron.blogspot.com

2012/11/10 Ata E Husain Bohra <ata.husain@hotmail.com>:
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 | 1 + src/esx/esx_storage_backend_iscsi.c | 807 +++++++++++++++++++++++++++++++++++ src/esx/esx_storage_backend_iscsi.h | 29 ++ src/esx/esx_storage_driver.c | 8 +- src/esx/esx_vi.c | 332 ++++++++++++++ src/esx/esx_vi.h | 18 + src/esx/esx_vi_generator.input | 302 +++++++++++++ src/esx/esx_vi_generator.py | 19 + 8 files changed, 1515 insertions(+), 1 deletion(-) create mode 100644 src/esx/esx_storage_backend_iscsi.c create mode 100644 src/esx/esx_storage_backend_iscsi.h
--- /dev/null +++ b/src/esx/esx_storage_backend_iscsi.c
+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 */
This comment is wrong here, because a hash wouldn't help here. In order to get the storage volume list this 2-dimensional list need to be iterated. There is no way to do this faster, except adding a cache but then you get the problem of ehen to invalidate it. So this 2 level for loop is totally fine and the comment should be removed, because it is misleading.
+ 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 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 ||
No need to look up the hostStorageSystem here. It is already available here: ctx->hostSystem->configManager->storageSystem
+ 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 virStorageVolPtr +esxStorageBackendISCSIVolumeCreateXML(virStoragePoolPtr pool ATTRIBUTE_UNUSED, + const char *xmldesc ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(0, NULL);
A VIR_ERR_NO_SUPPORT error should be reported here.
+ /* 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);
A VIR_ERR_NO_SUPPORT error should be reported here.
+ /* not supported operation for iSCSI pools */ + return NULL; +}
+static int +esxStorageBackendISCSIVolumeDelete(virStorageVolPtr volume ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(0, -1);
A VIR_ERR_NO_SUPPORT error should be reported here.
+ /* unsupported operation for iSCSI volume */ + return 1;
Should return -1;
+} + + + +static int +esxStorageBackendISCSIVolumeWipe(virStorageVolPtr volume ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(0, -1);
A VIR_ERR_NO_SUPPORT error should be reported here.
+ /* unsupported operation for iSCSI volume */ + return 1;
Should return -1;
+} + + + +static char* +esxStorageBackendISCSIVolumeGetPath(virStorageVolPtr volume) +{ + char *path; + + if (virAsprintf(&path, "%s", volume->name) < 0) { + virReportOOMError(); + return NULL; + }
Can be simplified to strdup.
+ return path; + +}
diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index 9fb2c11..12100d7 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c
+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 || + esxVI_ScsiLun_DeepCopyList(ret, scsiLunList) < 0) { + goto cleanup; + } + + result = 0; + +cleanup: + + esxVI_ScsiLun_Free(&scsiLunList);
Needs to free propertyNameList and hostSystem too.
+ return result; + +}
diff --git a/src/esx/esx_vi_generator.input b/src/esx/esx_vi_generator.input index c4a3e56..21f5b10 100644 --- a/src/esx/esx_vi_generator.input +++ b/src/esx/esx_vi_generator.input
@@ -128,6 +136,12 @@ enum VirtualMachinePowerState end
+enum vStorageSupport + vStorageUnknown +end
This enum is not used anywhere and can be removed. ACK. No major problems left. I fixed the minor things I mentioned, made make syntax-check pass and pushed the result. Thanks! -- Matthias Bolte http://photron.blogspot.com
participants (2)
-
Ata E Husain Bohra
-
Matthias Bolte