[libvirt] [PATCH 0/7 v3] Persistent vHBA support in storage driver

v2: https://www.redhat.com/archives/libvir-list/2013-February/msg00118.html v2 - v3: * 6/8 of v1 is ACKed/pushed. * Use virStringToLong_ui instead of sscanf * Fixed various nits pointed out by John This is the 3rd part to implement NPIV migration support [1]. Part 1: (pushed) https://www.redhat.com/archives/libvir-list/2013-February/msg00112.html Part 2: (pushed) https://www.redhat.com/archives/libvir-list/2013-January/msg00859.html Osier Yang (7): New XML attributes for storage pool source adapter storage: Make the adapter name be consistent with node device driver storage: Move virStorageBackendSCSIGetHostNumber into iscsi backend phyp: Prohibit fc_host adapter for phyp driver util: Add helper to get the scsi host name by iterating over sysfs storage: Add startPool and stopPool for scsi backend storage: Guess the parent if it's not specified for vHBA docs/formatstorage.html.in | 17 +- docs/schemas/storagepool.rng | 33 +++- src/conf/storage_conf.c | 132 +++++++++++++- src/conf/storage_conf.h | 23 ++- src/libvirt_private.syms | 4 + src/phyp/phyp_driver.c | 15 +- src/storage/storage_backend_iscsi.c | 39 +++- src/storage/storage_backend_scsi.c | 202 +++++++++++++++------ src/storage/storage_backend_scsi.h | 3 - src/util/virutil.c | 202 +++++++++++++++++++++ src/util/virutil.h | 11 +- .../pool-scsi-type-fc-host.xml | 15 ++ .../pool-scsi-type-scsi-host.xml | 15 ++ .../pool-scsi-type-fc-host.xml | 18 ++ .../pool-scsi-type-scsi-host.xml | 18 ++ tests/storagepoolxml2xmlout/pool-scsi.xml | 2 +- tests/storagepoolxml2xmltest.c | 2 + 17 files changed, 669 insertions(+), 82 deletions(-) create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml -- 1.8.0.1

This introduces 4 new attributes for storage pool source adapter. E.g. <adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/> Attribute 'type' can be either 'scsi_host' or 'fc_host', and defaults to 'scsi_host' if attribute 'name' is specified. I.e. It's optional for 'scsi_host' adapter, for back-compat reason. However, mandatory for 'fc_host' adapter and any new future adapter types. Attribute 'parent' is to specify the parent for the fc_host adapter. * docs/formatstorage.html.in: - Add documents for the 4 new attrs * docs/schemas/storagepool.rng: - Add RNG schema * src/conf/storage_conf.c: - Parse and format the new XMLs * src/conf/storage_conf.h: - New struct virStoragePoolSourceAdapter, replace "char *adapter" with it; - New enum virStoragePoolSourceAdapterType * src/libvirt_private.syms: - Export TypeToString and TypeFromString * src/phyp/phyp_driver.c: - Replace "adapter" with "adapter.data.name", which is member of the union of the new struct virStoragePoolSourceAdapter now. Later patch will add the checking, as "adapter.data.name" is only valid for "scsi_host" adapter. * src/storage/storage_backend_scsi.c: - Like above * tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml: * tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml: - New test for 'fc_host' and "scsi_host" adapter * tests/storagepoolxml2xmlout/pool-scsi.xml: - Change the expected output, as the 'type' defaults to 'scsi_host' if 'name" specified now * tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml: * tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml: - New test * tests/storagepoolxml2xmltest.c: - Include the test --- docs/formatstorage.html.in | 16 ++- docs/schemas/storagepool.rng | 33 +++++- src/conf/storage_conf.c | 132 +++++++++++++++++++-- src/conf/storage_conf.h | 23 +++- src/libvirt_private.syms | 2 + src/phyp/phyp_driver.c | 8 +- src/storage/storage_backend_scsi.c | 6 +- .../pool-scsi-type-fc-host.xml | 15 +++ .../pool-scsi-type-scsi-host.xml | 15 +++ .../pool-scsi-type-fc-host.xml | 18 +++ .../pool-scsi-type-scsi-host.xml | 18 +++ tests/storagepoolxml2xmlout/pool-scsi.xml | 2 +- tests/storagepoolxml2xmltest.c | 2 + 13 files changed, 266 insertions(+), 24 deletions(-) create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in index 9b68738..eff3016 100644 --- a/docs/formatstorage.html.in +++ b/docs/formatstorage.html.in @@ -88,8 +88,20 @@ <span class="since">Since 0.4.1</span></dd> <dt><code>adapter</code></dt> <dd>Provides the source for pools backed by SCSI adapters. May - only occur once. Contains a single attribute <code>name</code> - which is the SCSI adapter name (ex. "host1"). + only occur once. Attribute <code>name</code> is the SCSI adapter + name (ex. "host1"). Attribute <code>type</code> + (<span class="since">1.0.4</span>) specifies the adapter type. + Valid value are "fc_host" and "scsi_host". If omitted and + the <code>name</code> attribute is specified, then it defaults to + "scsi_host". To keep backwards compatibility, the attribute + <code>type</code> is optional for the "scsi_host" adapter, but + mandatory for the "fc_host" adapter. Attributes <code>wwnn</code> + (Word Wide Node Name) and <code>wwpn</code> (Word Wide Port Name) + (<span class="since">1.0.4</span>) are used by the "fc_host" adapter + to uniquely indentify the device in the Fibre Channel storage farbic + (the device can be either a HBA or vHBA). The optional attribute + <code>parent</code> (<span class="since">1.0.4</span>) specifies the + parent device for the "fc_host" adapter. <span class="since">Since 0.6.2</span></dd> <dt><code>host</code></dt> <dd>Provides the source for pools backed by storage from a diff --git a/docs/schemas/storagepool.rng b/docs/schemas/storagepool.rng index 0cc0406..43283d2 100644 --- a/docs/schemas/storagepool.rng +++ b/docs/schemas/storagepool.rng @@ -276,9 +276,36 @@ <define name='sourceinfoadapter'> <element name='adapter'> - <attribute name='name'> - <text/> - </attribute> + <choice> + <group> + <!-- To keep back-compat, 'type' is not mandatory for + scsi_host adapter --> + <optional> + <attribute name='type'> + <value>scsi_host</value> + </attribute> + </optional> + <attribute name='name'> + <text/> + </attribute> + </group> + <group> + <attribute name='type'> + <value>fc_host</value> + </attribute> + <optional> + <attribute name='parent'> + <text/> + </attribute> + </optional> + <attribute name='wwnn'> + <ref name='wwn'/> + </attribute> + <attribute name='wwpn'> + <ref name='wwn'/> + </attribute> + </group> + </choice> <empty/> </element> </define> diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 9134a22..deb4221 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -90,6 +90,10 @@ VIR_ENUM_IMPL(virStoragePartedFsType, "ext2", "ext2", "extended") +VIR_ENUM_IMPL(virStoragePoolSourceAdapterType, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_LAST, + "default", "scsi_host", "fc_host") + typedef const char *(*virStorageVolFormatToString)(int format); typedef int (*virStorageVolFormatFromString)(const char *format); @@ -304,6 +308,19 @@ virStorageVolDefFree(virStorageVolDefPtr def) { VIR_FREE(def); } +static void +virStoragePoolSourceAdapterClear(virStoragePoolSourceAdapter adapter) +{ + if (adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + VIR_FREE(adapter.data.fchost.wwnn); + VIR_FREE(adapter.data.fchost.wwpn); + VIR_FREE(adapter.data.fchost.parent); + } else if (adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + VIR_FREE(adapter.data.name); + } +} + void virStoragePoolSourceClear(virStoragePoolSourcePtr source) { @@ -324,7 +341,7 @@ virStoragePoolSourceClear(virStoragePoolSourcePtr source) VIR_FREE(source->devices); VIR_FREE(source->dir); VIR_FREE(source->name); - VIR_FREE(source->adapter); + virStoragePoolSourceAdapterClear(source->adapter); VIR_FREE(source->initiator.iqn); VIR_FREE(source->vendor); VIR_FREE(source->product); @@ -489,6 +506,7 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt, virStoragePoolOptionsPtr options; char *name = NULL; char *port = NULL; + char *adapter_type = NULL; int n; relnode = ctxt->node; @@ -580,7 +598,53 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt, } source->dir = virXPathString("string(./dir/@path)", ctxt); - source->adapter = virXPathString("string(./adapter/@name)", ctxt); + + if ((adapter_type = virXPathString("string(./adapter/@type)", ctxt))) { + if ((source->adapter.type = + virStoragePoolSourceAdapterTypeTypeFromString(adapter_type)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown pool adapter type '%s'"), + adapter_type); + goto cleanup; + } + + if (source->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + source->adapter.data.fchost.parent = + virXPathString("string(./adapter/@parent)", ctxt); + source->adapter.data.fchost.wwnn = + virXPathString("string(./adapter/@wwnn)", ctxt); + source->adapter.data.fchost.wwpn = + virXPathString("string(./adapter/@wwpn)", ctxt); + } else if (source->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + source->adapter.data.name = + virXPathString("string(./adapter/@name)", ctxt); + } + } else { + char *wwnn = NULL; + char *wwpn = NULL; + + wwnn = virXPathString("string(./adapter/@wwnn)", ctxt); + wwpn = virXPathString("string(./adapter/@wwpn)", ctxt); + + if (wwnn || wwpn) { + VIR_FREE(wwnn); + VIR_FREE(wwpn); + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Use of 'wwnn' and 'wwpn' attributes " + "requires the 'fc_host' adapter 'type'")); + goto cleanup; + } + + /* To keep back-compat, 'type' is not required to specify + * for scsi_host adapter. + */ + if ((source->adapter.data.name = + virXPathString("string(./adapter/@name)", ctxt))) + source->adapter.type = + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST; + } authType = virXPathString("string(./auth/@type)", ctxt); if (authType == NULL) { @@ -618,6 +682,7 @@ cleanup: VIR_FREE(port); VIR_FREE(authType); VIR_FREE(nodeset); + VIR_FREE(adapter_type); return ret; } @@ -819,11 +884,33 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) { } if (options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) { - if (!ret->source.adapter) { - virReportError(VIR_ERR_XML_ERROR, - "%s", _("missing storage pool source adapter name")); + if (!ret->source.adapter.type) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing storage pool source adapter")); goto cleanup; } + + if (ret->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + if (!ret->source.adapter.data.fchost.wwnn || + !ret->source.adapter.data.fchost.wwpn) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'wwnn' and 'wwpn' must be specified for adapter " + "type 'fchost'")); + goto cleanup; + } + + if (!virValidateWWN(ret->source.adapter.data.fchost.wwnn) || + !virValidateWWN(ret->source.adapter.data.fchost.wwpn)) + goto cleanup; + } else if (ret->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + if (!ret->source.adapter.data.name) { + virReportError(VIR_ERR_XML_ERROR, + "%s", _("missing storage pool source adapter name")); + goto cleanup; + } + } } /* If DEVICE is the only source type, then its required */ @@ -953,9 +1040,23 @@ virStoragePoolSourceFormat(virBufferPtr buf, if ((options->flags & VIR_STORAGE_POOL_SOURCE_DIR) && src->dir) virBufferAsprintf(buf," <dir path='%s'/>\n", src->dir); - if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) && - src->adapter) - virBufferAsprintf(buf," <adapter name='%s'/>\n", src->adapter); + if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER)) { + if (src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST || + src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) + virBufferAsprintf(buf, " <adapter type='%s'", + virStoragePoolSourceAdapterTypeTypeToString(src->adapter.type)); + + if (src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + virBufferEscapeString(buf, " parent='%s'", + src->adapter.data.fchost.parent); + virBufferAsprintf(buf," wwnn='%s' wwpn='%s'/>\n", + src->adapter.data.fchost.wwnn, + src->adapter.data.fchost.wwpn); + } else if (src->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + virBufferAsprintf(buf," name='%s'/>\n", src->adapter.data.name); + } + } if ((options->flags & VIR_STORAGE_POOL_SOURCE_NAME) && src->name) virBufferAsprintf(buf," <name>%s</name>\n", src->name); @@ -1856,8 +1957,19 @@ int virStoragePoolSourceFindDuplicate(virStoragePoolObjListPtr pools, matchpool = pool; break; case VIR_STORAGE_POOL_SCSI: - if (STREQ(pool->def->source.adapter, def->source.adapter)) - matchpool = pool; + if (pool->def->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + if (STREQ(pool->def->source.adapter.data.fchost.wwnn, + def->source.adapter.data.fchost.wwnn) && + STREQ(pool->def->source.adapter.data.fchost.wwpn, + def->source.adapter.data.fchost.wwpn)) + matchpool = pool; + } else if (pool->def->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST){ + if (STREQ(pool->def->source.adapter.data.name, + def->source.adapter.data.name)) + matchpool = pool; + } break; case VIR_STORAGE_POOL_ISCSI: { diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index ad16eca..60b8034 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -233,7 +233,28 @@ struct _virStoragePoolSourceDevice { } geometry; }; +enum virStoragePoolSourceAdapterType { + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_DEFAULT = 0, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_LAST, +}; +VIR_ENUM_DECL(virStoragePoolSourceAdapterType) + +typedef struct _virStoragePoolSourceAdapter virStoragePoolSourceAdapter; +struct _virStoragePoolSourceAdapter { + int type; /* enum virStoragePoolSourceAdapterType */ + + union { + char *name; + struct { + char *parent; + char *wwnn; + char *wwpn; + } fchost; + } data; +}; typedef struct _virStoragePoolSource virStoragePoolSource; typedef virStoragePoolSource *virStoragePoolSourcePtr; @@ -250,7 +271,7 @@ struct _virStoragePoolSource { char *dir; /* Or an adapter */ - char *adapter; + virStoragePoolSourceAdapter adapter; /* Or a name */ char *name; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a2c4a54..39c02ad 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -614,6 +614,8 @@ virStoragePoolObjLock; virStoragePoolObjRemove; virStoragePoolObjSaveDef; virStoragePoolObjUnlock; +virStoragePoolSourceAdapterTypeTypeFromString; +virStoragePoolSourceAdapterTypeTypeToString; virStoragePoolSourceClear; virStoragePoolSourceFindDuplicate; virStoragePoolSourceFindDuplicateDevices; diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 59cc1ca..3a97364 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -2062,7 +2062,7 @@ phypStorageVolCreateXML(virStoragePoolPtr pool, spdef->source.ndevice = 1; /*XXX source adapter not working properly, should show hdiskX */ - if ((spdef->source.adapter = + if ((spdef->source.adapter.data.name = phypGetStoragePoolDevice(pool->conn, pool->name)) == NULL) { VIR_ERROR(_("Unable to determine storage pools's source adapter.")); goto err; @@ -2284,7 +2284,7 @@ phypVolumeGetXMLDesc(virStorageVolPtr vol, unsigned int flags) pool.source.ndevice = 1; - if ((pool.source.adapter = + if ((pool.source.adapter.data.name = phypGetStoragePoolDevice(sp->conn, sp->name)) == NULL) { VIR_ERROR(_("Unable to determine storage sps's source adapter.")); goto cleanup; @@ -2524,7 +2524,7 @@ phypBuildStoragePool(virConnectPtr conn, virStoragePoolDefPtr def) managed_system, vios_id); virBufferAsprintf(&buf, "mksp -f %schild %s", def->name, - source.adapter); + source.adapter.data.name); if (system_type == HMC) virBufferAddChar(&buf, '\''); @@ -2765,7 +2765,7 @@ phypGetStoragePoolXMLDesc(virStoragePoolPtr pool, unsigned int flags) def.source.ndevice = 1; /*XXX source adapter not working properly, should show hdiskX */ - if ((def.source.adapter = + if ((def.source.adapter.data.name = phypGetStoragePoolDevice(pool->conn, pool->name)) == NULL) { VIR_ERROR(_("Unable to determine storage pools's source adapter.")); goto err; diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 90bbf59..c1c163e 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -639,7 +639,7 @@ virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, char *path; *isActive = false; - if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter) < 0) { + if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter.data.name) < 0) { virReportOOMError(); return -1; } @@ -661,9 +661,9 @@ virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, pool->def->allocation = pool->def->capacity = pool->def->available = 0; - if (sscanf(pool->def->source.adapter, "host%u", &host) != 1) { + if (sscanf(pool->def->source.adapter.data.name, "host%u", &host) != 1) { VIR_DEBUG("Failed to get host number from '%s'", - pool->def->source.adapter); + pool->def->source.adapter.data.name); retval = -1; goto out; } diff --git a/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml b/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml new file mode 100644 index 0000000..1e9826d --- /dev/null +++ b/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml @@ -0,0 +1,15 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <source> + <adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml b/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml new file mode 100644 index 0000000..4f48133 --- /dev/null +++ b/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml @@ -0,0 +1,15 @@ +<pool type="scsi"> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <source> + <adapter type='scsi_host' name="host0"/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml b/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml new file mode 100644 index 0000000..fb1079f --- /dev/null +++ b/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml @@ -0,0 +1,18 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + <adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml b/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml new file mode 100644 index 0000000..faf5342 --- /dev/null +++ b/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml @@ -0,0 +1,18 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + <adapter type='scsi_host' name='host0'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi.xml b/tests/storagepoolxml2xmlout/pool-scsi.xml index 321dc2b..faf5342 100644 --- a/tests/storagepoolxml2xmlout/pool-scsi.xml +++ b/tests/storagepoolxml2xmlout/pool-scsi.xml @@ -5,7 +5,7 @@ <allocation unit='bytes'>0</allocation> <available unit='bytes'>0</available> <source> - <adapter name='host0'/> + <adapter type='scsi_host' name='host0'/> </source> <target> <path>/dev/disk/by-path</path> diff --git a/tests/storagepoolxml2xmltest.c b/tests/storagepoolxml2xmltest.c index 8cac978..9be63c5 100644 --- a/tests/storagepoolxml2xmltest.c +++ b/tests/storagepoolxml2xmltest.c @@ -90,6 +90,8 @@ mymain(void) DO_TEST("pool-iscsi-auth"); DO_TEST("pool-netfs"); DO_TEST("pool-scsi"); + DO_TEST("pool-scsi-type-scsi-host"); + DO_TEST("pool-scsi-type-fc-host"); DO_TEST("pool-mpath"); DO_TEST("pool-iscsi-multiiqn"); DO_TEST("pool-iscsi-vendor-product"); -- 1.8.0.1

On 03/25/2013 12:43 PM, Osier Yang wrote:
This introduces 4 new attributes for storage pool source adapter. E.g.
<adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/>
Attribute 'type' can be either 'scsi_host' or 'fc_host', and defaults to 'scsi_host' if attribute 'name' is specified. I.e. It's optional for 'scsi_host' adapter, for back-compat reason. However, mandatory for 'fc_host' adapter and any new future adapter types. Attribute 'parent' is to specify the parent for the fc_host adapter.
* docs/formatstorage.html.in: - Add documents for the 4 new attrs * docs/schemas/storagepool.rng: - Add RNG schema * src/conf/storage_conf.c: - Parse and format the new XMLs * src/conf/storage_conf.h: - New struct virStoragePoolSourceAdapter, replace "char *adapter" with it; - New enum virStoragePoolSourceAdapterType * src/libvirt_private.syms: - Export TypeToString and TypeFromString * src/phyp/phyp_driver.c: - Replace "adapter" with "adapter.data.name", which is member of the union of the new struct virStoragePoolSourceAdapter now. Later patch will add the checking, as "adapter.data.name" is only valid for "scsi_host" adapter. * src/storage/storage_backend_scsi.c: - Like above * tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml: * tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml: - New test for 'fc_host' and "scsi_host" adapter * tests/storagepoolxml2xmlout/pool-scsi.xml: - Change the expected output, as the 'type' defaults to 'scsi_host' if 'name" specified now * tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml: * tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml: - New test * tests/storagepoolxml2xmltest.c: - Include the test --- docs/formatstorage.html.in | 16 ++- docs/schemas/storagepool.rng | 33 +++++- src/conf/storage_conf.c | 132 +++++++++++++++++++-- src/conf/storage_conf.h | 23 +++- src/libvirt_private.syms | 2 + src/phyp/phyp_driver.c | 8 +- src/storage/storage_backend_scsi.c | 6 +- .../pool-scsi-type-fc-host.xml | 15 +++ .../pool-scsi-type-scsi-host.xml | 15 +++ .../pool-scsi-type-fc-host.xml | 18 +++ .../pool-scsi-type-scsi-host.xml | 18 +++ tests/storagepoolxml2xmlout/pool-scsi.xml | 2 +- tests/storagepoolxml2xmltest.c | 2 + 13 files changed, 266 insertions(+), 24 deletions(-) create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml
diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in index 9b68738..eff3016 100644 --- a/docs/formatstorage.html.in +++ b/docs/formatstorage.html.in @@ -88,8 +88,20 @@ <span class="since">Since 0.4.1</span></dd> <dt><code>adapter</code></dt> <dd>Provides the source for pools backed by SCSI adapters. May - only occur once. Contains a single attribute <code>name</code> - which is the SCSI adapter name (ex. "host1"). + only occur once. Attribute <code>name</code> is the SCSI adapter + name (ex. "host1"). Attribute <code>type</code> + (<span class="since">1.0.4</span>) specifies the adapter type.
1.0.5 now right
+ Valid value are "fc_host" and "scsi_host". If omitted and
s/value/values/
+ the <code>name</code> attribute is specified, then it defaults to + "scsi_host". To keep backwards compatibility, the attribute + <code>type</code> is optional for the "scsi_host" adapter, but + mandatory for the "fc_host" adapter. Attributes <code>wwnn</code> + (Word Wide Node Name) and <code>wwpn</code> (Word Wide Port Name) + (<span class="since">1.0.4</span>) are used by the "fc_host" adapter
1.0.5
+ to uniquely indentify the device in the Fibre Channel storage farbic
s/indentify/identify s/farbic/fabric
+ (the device can be either a HBA or vHBA). The optional attribute
s/vHBA). The/vHBA). Both wwnn and wwpn must be specified. The/ The point being - although both are optional, if one is specified, then the other must be specified as well.
+ <code>parent</code> (<span class="since">1.0.4</span>) specifies the
1.0.5
+ parent device for the "fc_host" adapter.
Does it need to be said that wwwn, wwpn, and parent have no meaning for scsi_host? Also could you add a note about how "parent" is used - that is why would someone want/need to specify. One other perhaps confusing element is that "name" is only valid for "scsi_host". I think rather discussing he attribute name first, start with the type. Then add the to keep backwards compatibility, if only the attribute name is provided for the adapter element, the "type" field will be assumed/filled in. Another thought would be how someone would go about getting the wwwn/wwpn values - not sure if we are supposed to document, but it might be useful. Although thinking about it - I suppose there could be different tools/ways to get that value across different OS's.
<span class="since">Since 0.6.2</span></dd> <dt><code>host</code></dt> <dd>Provides the source for pools backed by storage from a diff --git a/docs/schemas/storagepool.rng b/docs/schemas/storagepool.rng index 0cc0406..43283d2 100644 --- a/docs/schemas/storagepool.rng +++ b/docs/schemas/storagepool.rng @@ -276,9 +276,36 @@
<define name='sourceinfoadapter'> <element name='adapter'> - <attribute name='name'> - <text/> - </attribute> + <choice> + <group> + <!-- To keep back-compat, 'type' is not mandatory for + scsi_host adapter --> + <optional> + <attribute name='type'> + <value>scsi_host</value> + </attribute> + </optional> + <attribute name='name'> + <text/> + </attribute> + </group> + <group> + <attribute name='type'> + <value>fc_host</value> + </attribute> + <optional> + <attribute name='parent'> + <text/> + </attribute> + </optional> + <attribute name='wwnn'> + <ref name='wwn'/> + </attribute> + <attribute name='wwpn'> + <ref name='wwn'/> + </attribute> + </group> + </choice> <empty/> </element> </define> diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 9134a22..deb4221 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -90,6 +90,10 @@ VIR_ENUM_IMPL(virStoragePartedFsType, "ext2", "ext2", "extended")
+VIR_ENUM_IMPL(virStoragePoolSourceAdapterType, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_LAST, + "default", "scsi_host", "fc_host") + typedef const char *(*virStorageVolFormatToString)(int format); typedef int (*virStorageVolFormatFromString)(const char *format);
@@ -304,6 +308,19 @@ virStorageVolDefFree(virStorageVolDefPtr def) { VIR_FREE(def); }
+static void +virStoragePoolSourceAdapterClear(virStoragePoolSourceAdapter adapter) +{ + if (adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + VIR_FREE(adapter.data.fchost.wwnn); + VIR_FREE(adapter.data.fchost.wwpn); + VIR_FREE(adapter.data.fchost.parent); + } else if (adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + VIR_FREE(adapter.data.name); + } +} + void virStoragePoolSourceClear(virStoragePoolSourcePtr source) { @@ -324,7 +341,7 @@ virStoragePoolSourceClear(virStoragePoolSourcePtr source) VIR_FREE(source->devices); VIR_FREE(source->dir); VIR_FREE(source->name); - VIR_FREE(source->adapter); + virStoragePoolSourceAdapterClear(source->adapter); VIR_FREE(source->initiator.iqn); VIR_FREE(source->vendor); VIR_FREE(source->product); @@ -489,6 +506,7 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt, virStoragePoolOptionsPtr options; char *name = NULL; char *port = NULL; + char *adapter_type = NULL; int n;
relnode = ctxt->node; @@ -580,7 +598,53 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt, }
source->dir = virXPathString("string(./dir/@path)", ctxt); - source->adapter = virXPathString("string(./adapter/@name)", ctxt); + + if ((adapter_type = virXPathString("string(./adapter/@type)", ctxt))) { + if ((source->adapter.type = + virStoragePoolSourceAdapterTypeTypeFromString(adapter_type)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown pool adapter type '%s'"), + adapter_type); + goto cleanup; + } + + if (source->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + source->adapter.data.fchost.parent = + virXPathString("string(./adapter/@parent)", ctxt); + source->adapter.data.fchost.wwnn = + virXPathString("string(./adapter/@wwnn)", ctxt); + source->adapter.data.fchost.wwpn = + virXPathString("string(./adapter/@wwpn)", ctxt); + } else if (source->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + source->adapter.data.name = + virXPathString("string(./adapter/@name)", ctxt); + } + } else { + char *wwnn = NULL; + char *wwpn = NULL; + + wwnn = virXPathString("string(./adapter/@wwnn)", ctxt); + wwpn = virXPathString("string(./adapter/@wwpn)", ctxt); + + if (wwnn || wwpn) { + VIR_FREE(wwnn); + VIR_FREE(wwpn); + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Use of 'wwnn' and 'wwpn' attributes " + "requires the 'fc_host' adapter 'type'")); + goto cleanup; + }
Same for parent too, right? Conversely, if 'name' is present on a fc_host type line, then it's bogus too. So the questions then become - do we care? does it need to be errored or just messaged? or can it just be quietly ignored? Not sure what the "norm" is historically... Since we don't check in the "if" portion of this, then one could easily say - this is superfluous.
+ + /* To keep back-compat, 'type' is not required to specify + * for scsi_host adapter. + */ + if ((source->adapter.data.name = + virXPathString("string(./adapter/@name)", ctxt))) + source->adapter.type = + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST; + }
authType = virXPathString("string(./auth/@type)", ctxt); if (authType == NULL) { @@ -618,6 +682,7 @@ cleanup: VIR_FREE(port); VIR_FREE(authType); VIR_FREE(nodeset); + VIR_FREE(adapter_type); return ret; }
@@ -819,11 +884,33 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) { }
if (options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) { - if (!ret->source.adapter) { - virReportError(VIR_ERR_XML_ERROR, - "%s", _("missing storage pool source adapter name")); + if (!ret->source.adapter.type) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing storage pool source adapter")); goto cleanup; } + + if (ret->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + if (!ret->source.adapter.data.fchost.wwnn || + !ret->source.adapter.data.fchost.wwpn) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'wwnn' and 'wwpn' must be specified for adapter " + "type 'fchost'")); + goto cleanup; + } + + if (!virValidateWWN(ret->source.adapter.data.fchost.wwnn) || + !virValidateWWN(ret->source.adapter.data.fchost.wwpn)) + goto cleanup;
No validation of parent? Makes me wonder at this point of the review what is it's purpose...
+ } else if (ret->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + if (!ret->source.adapter.data.name) { + virReportError(VIR_ERR_XML_ERROR, + "%s", _("missing storage pool source adapter name")); + goto cleanup; + } + } }
/* If DEVICE is the only source type, then its required */ @@ -953,9 +1040,23 @@ virStoragePoolSourceFormat(virBufferPtr buf, if ((options->flags & VIR_STORAGE_POOL_SOURCE_DIR) && src->dir) virBufferAsprintf(buf," <dir path='%s'/>\n", src->dir); - if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) && - src->adapter) - virBufferAsprintf(buf," <adapter name='%s'/>\n", src->adapter); + if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER)) { + if (src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST || + src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) + virBufferAsprintf(buf, " <adapter type='%s'", + virStoragePoolSourceAdapterTypeTypeToString(src->adapter.type)); + + if (src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + virBufferEscapeString(buf, " parent='%s'", + src->adapter.data.fchost.parent); + virBufferAsprintf(buf," wwnn='%s' wwpn='%s'/>\n", + src->adapter.data.fchost.wwnn, + src->adapter.data.fchost.wwpn); + } else if (src->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + virBufferAsprintf(buf," name='%s'/>\n", src->adapter.data.name); + } + } if ((options->flags & VIR_STORAGE_POOL_SOURCE_NAME) && src->name) virBufferAsprintf(buf," <name>%s</name>\n", src->name); @@ -1856,8 +1957,19 @@ int virStoragePoolSourceFindDuplicate(virStoragePoolObjListPtr pools, matchpool = pool; break; case VIR_STORAGE_POOL_SCSI: - if (STREQ(pool->def->source.adapter, def->source.adapter)) - matchpool = pool; + if (pool->def->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + if (STREQ(pool->def->source.adapter.data.fchost.wwnn, + def->source.adapter.data.fchost.wwnn) && + STREQ(pool->def->source.adapter.data.fchost.wwpn, + def->source.adapter.data.fchost.wwpn)) + matchpool = pool; + } else if (pool->def->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST){ + if (STREQ(pool->def->source.adapter.data.name, + def->source.adapter.data.name)) + matchpool = pool; + } break; case VIR_STORAGE_POOL_ISCSI: { diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index ad16eca..60b8034 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -233,7 +233,28 @@ struct _virStoragePoolSourceDevice { } geometry; };
+enum virStoragePoolSourceAdapterType { + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_DEFAULT = 0, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST,
+ VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_LAST, +}; +VIR_ENUM_DECL(virStoragePoolSourceAdapterType) + +typedef struct _virStoragePoolSourceAdapter virStoragePoolSourceAdapter; +struct _virStoragePoolSourceAdapter { + int type; /* enum virStoragePoolSourceAdapterType */ + + union { + char *name; + struct { + char *parent; + char *wwnn; + char *wwpn; + } fchost; + } data; +};
typedef struct _virStoragePoolSource virStoragePoolSource; typedef virStoragePoolSource *virStoragePoolSourcePtr; @@ -250,7 +271,7 @@ struct _virStoragePoolSource { char *dir;
/* Or an adapter */ - char *adapter; + virStoragePoolSourceAdapter adapter;
/* Or a name */ char *name; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a2c4a54..39c02ad 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -614,6 +614,8 @@ virStoragePoolObjLock; virStoragePoolObjRemove; virStoragePoolObjSaveDef; virStoragePoolObjUnlock; +virStoragePoolSourceAdapterTypeTypeFromString; +virStoragePoolSourceAdapterTypeTypeToString; virStoragePoolSourceClear; virStoragePoolSourceFindDuplicate; virStoragePoolSourceFindDuplicateDevices; diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 59cc1ca..3a97364 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -2062,7 +2062,7 @@ phypStorageVolCreateXML(virStoragePoolPtr pool, spdef->source.ndevice = 1;
/*XXX source adapter not working properly, should show hdiskX */ - if ((spdef->source.adapter = + if ((spdef->source.adapter.data.name =
Is it safe to go directly here without checking the type?
phypGetStoragePoolDevice(pool->conn, pool->name)) == NULL) { VIR_ERROR(_("Unable to determine storage pools's source adapter.")); goto err; @@ -2284,7 +2284,7 @@ phypVolumeGetXMLDesc(virStorageVolPtr vol, unsigned int flags)
pool.source.ndevice = 1;
- if ((pool.source.adapter = + if ((pool.source.adapter.data.name =
Same question
phypGetStoragePoolDevice(sp->conn, sp->name)) == NULL) { VIR_ERROR(_("Unable to determine storage sps's source adapter.")); goto cleanup; @@ -2524,7 +2524,7 @@ phypBuildStoragePool(virConnectPtr conn, virStoragePoolDefPtr def) managed_system, vios_id);
virBufferAsprintf(&buf, "mksp -f %schild %s", def->name, - source.adapter); + source.adapter.data.name);
Again
if (system_type == HMC) virBufferAddChar(&buf, '\''); @@ -2765,7 +2765,7 @@ phypGetStoragePoolXMLDesc(virStoragePoolPtr pool, unsigned int flags) def.source.ndevice = 1;
/*XXX source adapter not working properly, should show hdiskX */ - if ((def.source.adapter = + if ((def.source.adapter.data.name =
Again
phypGetStoragePoolDevice(pool->conn, pool->name)) == NULL) { VIR_ERROR(_("Unable to determine storage pools's source adapter.")); goto err; diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 90bbf59..c1c163e 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -639,7 +639,7 @@ virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, char *path;
*isActive = false; - if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter) < 0) { + if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter.data.name) < 0) {
This one seems OK since it's a SCSI function
virReportOOMError(); return -1; } @@ -661,9 +661,9 @@ virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
- if (sscanf(pool->def->source.adapter, "host%u", &host) != 1) { + if (sscanf(pool->def->source.adapter.data.name, "host%u", &host) != 1) { VIR_DEBUG("Failed to get host number from '%s'", - pool->def->source.adapter); + pool->def->source.adapter.data.name); retval = -1; goto out; } diff --git a/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml b/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml new file mode 100644 index 0000000..1e9826d --- /dev/null +++ b/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml @@ -0,0 +1,15 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <source> + <adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml b/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml new file mode 100644 index 0000000..4f48133 --- /dev/null +++ b/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml @@ -0,0 +1,15 @@ +<pool type="scsi"> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <source> + <adapter type='scsi_host' name="host0"/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml b/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml new file mode 100644 index 0000000..fb1079f --- /dev/null +++ b/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml @@ -0,0 +1,18 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + <adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml b/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml new file mode 100644 index 0000000..faf5342 --- /dev/null +++ b/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml @@ -0,0 +1,18 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + <adapter type='scsi_host' name='host0'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi.xml b/tests/storagepoolxml2xmlout/pool-scsi.xml index 321dc2b..faf5342 100644 --- a/tests/storagepoolxml2xmlout/pool-scsi.xml +++ b/tests/storagepoolxml2xmlout/pool-scsi.xml @@ -5,7 +5,7 @@ <allocation unit='bytes'>0</allocation> <available unit='bytes'>0</available> <source> - <adapter name='host0'/> + <adapter type='scsi_host' name='host0'/>
Should there exist a test/config which keeps that back compat option? That is should we make sure that if only the "name" is provided that the right things happen.
</source> <target> <path>/dev/disk/by-path</path> diff --git a/tests/storagepoolxml2xmltest.c b/tests/storagepoolxml2xmltest.c index 8cac978..9be63c5 100644 --- a/tests/storagepoolxml2xmltest.c +++ b/tests/storagepoolxml2xmltest.c @@ -90,6 +90,8 @@ mymain(void) DO_TEST("pool-iscsi-auth"); DO_TEST("pool-netfs"); DO_TEST("pool-scsi"); + DO_TEST("pool-scsi-type-scsi-host"); + DO_TEST("pool-scsi-type-fc-host"); DO_TEST("pool-mpath"); DO_TEST("pool-iscsi-multiiqn"); DO_TEST("pool-iscsi-vendor-product");
ACK - with suggestions handled... John

On 05/04/13 22:53, John Ferlan wrote:
On 03/25/2013 12:43 PM, Osier Yang wrote:
This introduces 4 new attributes for storage pool source adapter. E.g.
<adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/>
Attribute 'type' can be either 'scsi_host' or 'fc_host', and defaults to 'scsi_host' if attribute 'name' is specified. I.e. It's optional for 'scsi_host' adapter, for back-compat reason. However, mandatory for 'fc_host' adapter and any new future adapter types. Attribute 'parent' is to specify the parent for the fc_host adapter.
* docs/formatstorage.html.in: - Add documents for the 4 new attrs * docs/schemas/storagepool.rng: - Add RNG schema * src/conf/storage_conf.c: - Parse and format the new XMLs * src/conf/storage_conf.h: - New struct virStoragePoolSourceAdapter, replace "char *adapter" with it; - New enum virStoragePoolSourceAdapterType * src/libvirt_private.syms: - Export TypeToString and TypeFromString * src/phyp/phyp_driver.c: - Replace "adapter" with "adapter.data.name", which is member of the union of the new struct virStoragePoolSourceAdapter now. Later patch will add the checking, as "adapter.data.name" is only valid for "scsi_host" adapter. * src/storage/storage_backend_scsi.c: - Like above * tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml: * tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml: - New test for 'fc_host' and "scsi_host" adapter * tests/storagepoolxml2xmlout/pool-scsi.xml: - Change the expected output, as the 'type' defaults to 'scsi_host' if 'name" specified now * tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml: * tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml: - New test * tests/storagepoolxml2xmltest.c: - Include the test --- docs/formatstorage.html.in | 16 ++- docs/schemas/storagepool.rng | 33 +++++- src/conf/storage_conf.c | 132 +++++++++++++++++++-- src/conf/storage_conf.h | 23 +++- src/libvirt_private.syms | 2 + src/phyp/phyp_driver.c | 8 +- src/storage/storage_backend_scsi.c | 6 +- .../pool-scsi-type-fc-host.xml | 15 +++ .../pool-scsi-type-scsi-host.xml | 15 +++ .../pool-scsi-type-fc-host.xml | 18 +++ .../pool-scsi-type-scsi-host.xml | 18 +++ tests/storagepoolxml2xmlout/pool-scsi.xml | 2 +- tests/storagepoolxml2xmltest.c | 2 + 13 files changed, 266 insertions(+), 24 deletions(-) create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml
diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in index 9b68738..eff3016 100644 --- a/docs/formatstorage.html.in +++ b/docs/formatstorage.html.in @@ -88,8 +88,20 @@ <span class="since">Since 0.4.1</span></dd> <dt><code>adapter</code></dt> <dd>Provides the source for pools backed by SCSI adapters. May - only occur once. Contains a single attribute <code>name</code> - which is the SCSI adapter name (ex. "host1"). + only occur once. Attribute <code>name</code> is the SCSI adapter + name (ex. "host1"). Attribute <code>type</code> + (<span class="since">1.0.4</span>) specifies the adapter type. 1.0.5 now right
+ Valid value are "fc_host" and "scsi_host". If omitted and s/value/values/
+ the <code>name</code> attribute is specified, then it defaults to + "scsi_host". To keep backwards compatibility, the attribute + <code>type</code> is optional for the "scsi_host" adapter, but + mandatory for the "fc_host" adapter. Attributes <code>wwnn</code> + (Word Wide Node Name) and <code>wwpn</code> (Word Wide Port Name) + (<span class="since">1.0.4</span>) are used by the "fc_host" adapter 1.0.5
+ to uniquely indentify the device in the Fibre Channel storage farbic s/indentify/identify s/farbic/fabric
+ (the device can be either a HBA or vHBA). The optional attribute s/vHBA). The/vHBA). Both wwnn and wwpn must be specified. The/
The point being - although both are optional, if one is specified, then the other must be specified as well.
+ <code>parent</code> (<span class="since">1.0.4</span>) specifies the 1.0.5
+ parent device for the "fc_host" adapter. Does it need to be said that wwwn, wwpn, and parent have no meaning for scsi_host?
Yes.
Also could you add a note about how "parent" is used - that is why would someone want/need to specify.
XML example will be good. I will add when pushing.
One other perhaps confusing element is that "name" is only valid for "scsi_host". I think rather discussing he attribute name first, start with the type. Then add the to keep backwards compatibility, if only the attribute name is provided for the adapter element, the "type" field will be assumed/filled in.
Another thought would be how someone would go about getting the wwwn/wwpn values - not sure if we are supposed to document, but it might be useful. Although thinking about it - I suppose there could be different tools/ways to get that value across different OS's.
We have commands like nodedev-dumpxml, which can get the wwnn/wwpn, I can mention it. But it's only supported for Linux platform. For other platforms, I think it's just overkill for us to document here.
<span class="since">Since 0.6.2</span></dd> <dt><code>host</code></dt> <dd>Provides the source for pools backed by storage from a diff --git a/docs/schemas/storagepool.rng b/docs/schemas/storagepool.rng index 0cc0406..43283d2 100644 --- a/docs/schemas/storagepool.rng +++ b/docs/schemas/storagepool.rng @@ -276,9 +276,36 @@
<define name='sourceinfoadapter'> <element name='adapter'> - <attribute name='name'> - <text/> - </attribute> + <choice> + <group> + <!-- To keep back-compat, 'type' is not mandatory for + scsi_host adapter --> + <optional> + <attribute name='type'> + <value>scsi_host</value> + </attribute> + </optional> + <attribute name='name'> + <text/> + </attribute> + </group> + <group> + <attribute name='type'> + <value>fc_host</value> + </attribute> + <optional> + <attribute name='parent'> + <text/> + </attribute> + </optional> + <attribute name='wwnn'> + <ref name='wwn'/> + </attribute> + <attribute name='wwpn'> + <ref name='wwn'/> + </attribute> + </group> + </choice> <empty/> </element> </define> diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 9134a22..deb4221 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -90,6 +90,10 @@ VIR_ENUM_IMPL(virStoragePartedFsType, "ext2", "ext2", "extended")
+VIR_ENUM_IMPL(virStoragePoolSourceAdapterType, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_LAST, + "default", "scsi_host", "fc_host") + typedef const char *(*virStorageVolFormatToString)(int format); typedef int (*virStorageVolFormatFromString)(const char *format);
@@ -304,6 +308,19 @@ virStorageVolDefFree(virStorageVolDefPtr def) { VIR_FREE(def); }
+static void +virStoragePoolSourceAdapterClear(virStoragePoolSourceAdapter adapter) +{ + if (adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + VIR_FREE(adapter.data.fchost.wwnn); + VIR_FREE(adapter.data.fchost.wwpn); + VIR_FREE(adapter.data.fchost.parent); + } else if (adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + VIR_FREE(adapter.data.name); + } +} + void virStoragePoolSourceClear(virStoragePoolSourcePtr source) { @@ -324,7 +341,7 @@ virStoragePoolSourceClear(virStoragePoolSourcePtr source) VIR_FREE(source->devices); VIR_FREE(source->dir); VIR_FREE(source->name); - VIR_FREE(source->adapter); + virStoragePoolSourceAdapterClear(source->adapter); VIR_FREE(source->initiator.iqn); VIR_FREE(source->vendor); VIR_FREE(source->product); @@ -489,6 +506,7 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt, virStoragePoolOptionsPtr options; char *name = NULL; char *port = NULL; + char *adapter_type = NULL; int n;
relnode = ctxt->node; @@ -580,7 +598,53 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt, }
source->dir = virXPathString("string(./dir/@path)", ctxt); - source->adapter = virXPathString("string(./adapter/@name)", ctxt); + + if ((adapter_type = virXPathString("string(./adapter/@type)", ctxt))) { + if ((source->adapter.type = + virStoragePoolSourceAdapterTypeTypeFromString(adapter_type)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown pool adapter type '%s'"), + adapter_type); + goto cleanup; + } + + if (source->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + source->adapter.data.fchost.parent = + virXPathString("string(./adapter/@parent)", ctxt); + source->adapter.data.fchost.wwnn = + virXPathString("string(./adapter/@wwnn)", ctxt); + source->adapter.data.fchost.wwpn = + virXPathString("string(./adapter/@wwpn)", ctxt); + } else if (source->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + source->adapter.data.name = + virXPathString("string(./adapter/@name)", ctxt); + } + } else { + char *wwnn = NULL; + char *wwpn = NULL; + + wwnn = virXPathString("string(./adapter/@wwnn)", ctxt); + wwpn = virXPathString("string(./adapter/@wwpn)", ctxt); + + if (wwnn || wwpn) { + VIR_FREE(wwnn); + VIR_FREE(wwpn); + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Use of 'wwnn' and 'wwpn' attributes " + "requires the 'fc_host' adapter 'type'")); + goto cleanup; + }
Same for parent too, right?
Yes. I missed it. Will add.
Conversely, if 'name' is present on a fc_host type line, then it's bogus too.
So the questions then become - do we care? does it need to be errored or just messaged? or can it just be quietly ignored? Not sure what the "norm" is historically... Since we don't check in the "if" portion of this, then one could easily say - this is superfluous.
It's historically, actually we ignore a lot everywhere when parsing. My opinion is except the fatal error, such as "type" is not specified, but "wwnn/wwpn" are specified. Others can be just ignored if the required atrributes are correctly parsed. E.g. for a "fc_host" adpater, if the wwnn/wwpn are correctly parsed, we don't care about if the "name" is specified or not, it's just ignored. I agreed with doing so, because if not, the XML parsing code will be huge, and it's overkill in my opinion.
+ + /* To keep back-compat, 'type' is not required to specify + * for scsi_host adapter. + */ + if ((source->adapter.data.name = + virXPathString("string(./adapter/@name)", ctxt))) + source->adapter.type = + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST; + }
authType = virXPathString("string(./auth/@type)", ctxt); if (authType == NULL) { @@ -618,6 +682,7 @@ cleanup: VIR_FREE(port); VIR_FREE(authType); VIR_FREE(nodeset); + VIR_FREE(adapter_type); return ret; }
@@ -819,11 +884,33 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) { }
if (options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) { - if (!ret->source.adapter) { - virReportError(VIR_ERR_XML_ERROR, - "%s", _("missing storage pool source adapter name")); + if (!ret->source.adapter.type) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing storage pool source adapter")); goto cleanup; } + + if (ret->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + if (!ret->source.adapter.data.fchost.wwnn || + !ret->source.adapter.data.fchost.wwpn) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'wwnn' and 'wwpn' must be specified for adapter " + "type 'fchost'")); + goto cleanup; + } + + if (!virValidateWWN(ret->source.adapter.data.fchost.wwnn) || + !virValidateWWN(ret->source.adapter.data.fchost.wwpn)) + goto cleanup; No validation of parent? Makes me wonder at this point of the review what is it's purpose...
See the rng schema, parent is a "<text/>", so no need to validate.
+ } else if (ret->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + if (!ret->source.adapter.data.name) { + virReportError(VIR_ERR_XML_ERROR, + "%s", _("missing storage pool source adapter name")); + goto cleanup; + } + } }
/* If DEVICE is the only source type, then its required */ @@ -953,9 +1040,23 @@ virStoragePoolSourceFormat(virBufferPtr buf, if ((options->flags & VIR_STORAGE_POOL_SOURCE_DIR) && src->dir) virBufferAsprintf(buf," <dir path='%s'/>\n", src->dir); - if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) && - src->adapter) - virBufferAsprintf(buf," <adapter name='%s'/>\n", src->adapter); + if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER)) { + if (src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST || + src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) + virBufferAsprintf(buf, " <adapter type='%s'", + virStoragePoolSourceAdapterTypeTypeToString(src->adapter.type)); + + if (src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + virBufferEscapeString(buf, " parent='%s'", + src->adapter.data.fchost.parent); + virBufferAsprintf(buf," wwnn='%s' wwpn='%s'/>\n", + src->adapter.data.fchost.wwnn, + src->adapter.data.fchost.wwpn); + } else if (src->adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + virBufferAsprintf(buf," name='%s'/>\n", src->adapter.data.name); + } + } if ((options->flags & VIR_STORAGE_POOL_SOURCE_NAME) && src->name) virBufferAsprintf(buf," <name>%s</name>\n", src->name); @@ -1856,8 +1957,19 @@ int virStoragePoolSourceFindDuplicate(virStoragePoolObjListPtr pools, matchpool = pool; break; case VIR_STORAGE_POOL_SCSI: - if (STREQ(pool->def->source.adapter, def->source.adapter)) - matchpool = pool; + if (pool->def->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) { + if (STREQ(pool->def->source.adapter.data.fchost.wwnn, + def->source.adapter.data.fchost.wwnn) && + STREQ(pool->def->source.adapter.data.fchost.wwpn, + def->source.adapter.data.fchost.wwpn)) + matchpool = pool; + } else if (pool->def->source.adapter.type == + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST){ + if (STREQ(pool->def->source.adapter.data.name, + def->source.adapter.data.name)) + matchpool = pool; + } break; case VIR_STORAGE_POOL_ISCSI: { diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index ad16eca..60b8034 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -233,7 +233,28 @@ struct _virStoragePoolSourceDevice { } geometry; };
+enum virStoragePoolSourceAdapterType { + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_DEFAULT = 0, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST, + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST,
+ VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_LAST, +}; +VIR_ENUM_DECL(virStoragePoolSourceAdapterType) + +typedef struct _virStoragePoolSourceAdapter virStoragePoolSourceAdapter; +struct _virStoragePoolSourceAdapter { + int type; /* enum virStoragePoolSourceAdapterType */ + + union { + char *name; + struct { + char *parent; + char *wwnn; + char *wwpn; + } fchost; + } data; +};
typedef struct _virStoragePoolSource virStoragePoolSource; typedef virStoragePoolSource *virStoragePoolSourcePtr; @@ -250,7 +271,7 @@ struct _virStoragePoolSource { char *dir;
/* Or an adapter */ - char *adapter; + virStoragePoolSourceAdapter adapter;
/* Or a name */ char *name; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a2c4a54..39c02ad 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -614,6 +614,8 @@ virStoragePoolObjLock; virStoragePoolObjRemove; virStoragePoolObjSaveDef; virStoragePoolObjUnlock; +virStoragePoolSourceAdapterTypeTypeFromString; +virStoragePoolSourceAdapterTypeTypeToString; virStoragePoolSourceClear; virStoragePoolSourceFindDuplicate; virStoragePoolSourceFindDuplicateDevices; diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 59cc1ca..3a97364 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -2062,7 +2062,7 @@ phypStorageVolCreateXML(virStoragePoolPtr pool, spdef->source.ndevice = 1;
/*XXX source adapter not working properly, should show hdiskX */ - if ((spdef->source.adapter = + if ((spdef->source.adapter.data.name = Is it safe to go directly here without checking the type?
Same question and same reply in v1, :-) It's sorted out in later patch, this match just add the xml support, and make sure the build is not broken.
phypGetStoragePoolDevice(pool->conn, pool->name)) == NULL) { VIR_ERROR(_("Unable to determine storage pools's source adapter.")); goto err; @@ -2284,7 +2284,7 @@ phypVolumeGetXMLDesc(virStorageVolPtr vol, unsigned int flags)
pool.source.ndevice = 1;
- if ((pool.source.adapter = + if ((pool.source.adapter.data.name =
Same question
phypGetStoragePoolDevice(sp->conn, sp->name)) == NULL) { VIR_ERROR(_("Unable to determine storage sps's source adapter.")); goto cleanup; @@ -2524,7 +2524,7 @@ phypBuildStoragePool(virConnectPtr conn, virStoragePoolDefPtr def) managed_system, vios_id);
virBufferAsprintf(&buf, "mksp -f %schild %s", def->name, - source.adapter); + source.adapter.data.name);
Again
if (system_type == HMC) virBufferAddChar(&buf, '\''); @@ -2765,7 +2765,7 @@ phypGetStoragePoolXMLDesc(virStoragePoolPtr pool, unsigned int flags) def.source.ndevice = 1;
/*XXX source adapter not working properly, should show hdiskX */ - if ((def.source.adapter = + if ((def.source.adapter.data.name =
Again
phypGetStoragePoolDevice(pool->conn, pool->name)) == NULL) { VIR_ERROR(_("Unable to determine storage pools's source adapter.")); goto err; diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 90bbf59..c1c163e 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -639,7 +639,7 @@ virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, char *path;
*isActive = false; - if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter) < 0) { + if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter.data.name) < 0) {
This one seems OK since it's a SCSI function
virReportOOMError(); return -1; } @@ -661,9 +661,9 @@ virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
- if (sscanf(pool->def->source.adapter, "host%u", &host) != 1) { + if (sscanf(pool->def->source.adapter.data.name, "host%u", &host) != 1) { VIR_DEBUG("Failed to get host number from '%s'", - pool->def->source.adapter); + pool->def->source.adapter.data.name); retval = -1; goto out; } diff --git a/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml b/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml new file mode 100644 index 0000000..1e9826d --- /dev/null +++ b/tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml @@ -0,0 +1,15 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <source> + <adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml b/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml new file mode 100644 index 0000000..4f48133 --- /dev/null +++ b/tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml @@ -0,0 +1,15 @@ +<pool type="scsi"> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <source> + <adapter type='scsi_host' name="host0"/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml b/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml new file mode 100644 index 0000000..fb1079f --- /dev/null +++ b/tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml @@ -0,0 +1,18 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + <adapter type='fc_host' parent='scsi_host5' wwnn='20000000c9831b4b' wwpn='10000000c9831b4b'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml b/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml new file mode 100644 index 0000000..faf5342 --- /dev/null +++ b/tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml @@ -0,0 +1,18 @@ +<pool type='scsi'> + <name>hba0</name> + <uuid>e9392370-2917-565e-692b-d057f46512d6</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + <adapter type='scsi_host' name='host0'/> + </source> + <target> + <path>/dev/disk/by-path</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</pool> diff --git a/tests/storagepoolxml2xmlout/pool-scsi.xml b/tests/storagepoolxml2xmlout/pool-scsi.xml index 321dc2b..faf5342 100644 --- a/tests/storagepoolxml2xmlout/pool-scsi.xml +++ b/tests/storagepoolxml2xmlout/pool-scsi.xml @@ -5,7 +5,7 @@ <allocation unit='bytes'>0</allocation> <available unit='bytes'>0</available> <source> - <adapter name='host0'/> + <adapter type='scsi_host' name='host0'/>
Should there exist a test/config which keeps that back compat option? That is should we make sure that if only the "name" is provided that the right things happen.
This is the *xml2xmlout* data. Now we always output the adpater "type" when formating.
</source> <target> <path>/dev/disk/by-path</path> diff --git a/tests/storagepoolxml2xmltest.c b/tests/storagepoolxml2xmltest.c index 8cac978..9be63c5 100644 --- a/tests/storagepoolxml2xmltest.c +++ b/tests/storagepoolxml2xmltest.c @@ -90,6 +90,8 @@ mymain(void) DO_TEST("pool-iscsi-auth"); DO_TEST("pool-netfs"); DO_TEST("pool-scsi"); + DO_TEST("pool-scsi-type-scsi-host"); + DO_TEST("pool-scsi-type-fc-host"); DO_TEST("pool-mpath"); DO_TEST("pool-iscsi-multiiqn"); DO_TEST("pool-iscsi-vendor-product");
ACK - with suggestions handled...
John
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

node device driver names the HBA like "scsi_host5", but storage driver uses "host5", which could make the user confused. This changes them to be consistent. However, for back-compat reason, adapter name like "host5" is still supported. v1 - v2: * Use virStrToLong_ui instead of sscanf * No tests addition or changes, because this patch only affects the way scsi backend works for adapter, adding xml2xml tests for it is just meaningless. --- docs/formatstorage.html.in | 15 ++++++----- src/storage/storage_backend_scsi.c | 54 +++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in index eff3016..5fae933 100644 --- a/docs/formatstorage.html.in +++ b/docs/formatstorage.html.in @@ -89,13 +89,14 @@ <dt><code>adapter</code></dt> <dd>Provides the source for pools backed by SCSI adapters. May only occur once. Attribute <code>name</code> is the SCSI adapter - name (ex. "host1"). Attribute <code>type</code> - (<span class="since">1.0.4</span>) specifies the adapter type. - Valid value are "fc_host" and "scsi_host". If omitted and - the <code>name</code> attribute is specified, then it defaults to - "scsi_host". To keep backwards compatibility, the attribute - <code>type</code> is optional for the "scsi_host" adapter, but - mandatory for the "fc_host" adapter. Attributes <code>wwnn</code> + name (ex. "scsi_host1". NB, although a name such as "host1" is + still supported for backwards compatibility, it is not recommended). + Attribute <code>type</code> (<span class="since">1.0.4</span>) + specifies the adapter type. Valid value are "fc_host" and "scsi_host". + If omitted and the <code>name</code> attribute is specified, then it + defaults to "scsi_host". To keep backwards compatibility, the + attribute <code>type</code> is optional for the "scsi_host" adapter, + but mandatory for the "fc_host" adapter. Attributes <code>wwnn</code> (Word Wide Node Name) and <code>wwpn</code> (Word Wide Port Name) (<span class="since">1.0.4</span>) are used by the "fc_host" adapter to uniquely indentify the device in the Fibre Channel storage farbic diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index c1c163e..cc1ebe2 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -632,14 +632,49 @@ out: } static int +getHostNumber(const char *adapter_name, + unsigned int *result) +{ + char *host = (char *)adapter_name; + + /* Specifying adapter like 'host5' is still supported for + * back-compat reason. + */ + if (STRPREFIX(host, "scsi_host")) { + host += strlen("scsi_host"); + } else if (STRPREFIX(host, "host")) { + host += strlen("host"); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid adapter name '%s' for scsi pool"), + adapter_name); + return -1; + } + + if (result && virStrToLong_ui(host, NULL, 10, result) == -1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid adapter name '%s' for scsi pool"), + adapter_name); + return -1; + } + + return 0; +} + +static int virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, bool *isActive) { char *path; + unsigned int host; *isActive = false; - if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter.data.name) < 0) { + + if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) + return -1; + + if (virAsprintf(&path, "/sys/class/scsi_host/host%d", host) < 0) { virReportOOMError(); return -1; } @@ -656,29 +691,24 @@ static int virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool) { - int retval = 0; - uint32_t host; + int ret = -1; + unsigned int host; pool->def->allocation = pool->def->capacity = pool->def->available = 0; - if (sscanf(pool->def->source.adapter.data.name, "host%u", &host) != 1) { - VIR_DEBUG("Failed to get host number from '%s'", - pool->def->source.adapter.data.name); - retval = -1; + if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) goto out; - } VIR_DEBUG("Scanning host%u", host); - if (virStorageBackendSCSITriggerRescan(host) < 0) { - retval = -1; + if (virStorageBackendSCSITriggerRescan(host) < 0) goto out; - } virStorageBackendSCSIFindLUs(pool, host); + ret = 0; out: - return retval; + return ret; } -- 1.8.0.1

On 03/25/2013 12:43 PM, Osier Yang wrote:
node device driver names the HBA like "scsi_host5", but storage driver uses "host5", which could make the user confused. This changes them to be consistent. However, for back-compat reason, adapter name like "host5" is still supported.
v1 - v2: * Use virStrToLong_ui instead of sscanf * No tests addition or changes, because this patch only affects the way scsi backend works for adapter, adding xml2xml tests for it is just meaningless. --- docs/formatstorage.html.in | 15 ++++++----- src/storage/storage_backend_scsi.c | 54 +++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 19 deletions(-)
diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in index eff3016..5fae933 100644 --- a/docs/formatstorage.html.in +++ b/docs/formatstorage.html.in @@ -89,13 +89,14 @@ <dt><code>adapter</code></dt> <dd>Provides the source for pools backed by SCSI adapters. May only occur once. Attribute <code>name</code> is the SCSI adapter - name (ex. "host1"). Attribute <code>type</code> - (<span class="since">1.0.4</span>) specifies the adapter type. - Valid value are "fc_host" and "scsi_host". If omitted and - the <code>name</code> attribute is specified, then it defaults to - "scsi_host". To keep backwards compatibility, the attribute - <code>type</code> is optional for the "scsi_host" adapter, but - mandatory for the "fc_host" adapter. Attributes <code>wwnn</code> + name (ex. "scsi_host1". NB, although a name such as "host1" is + still supported for backwards compatibility, it is not recommended). + Attribute <code>type</code> (<span class="since">1.0.4</span>) + specifies the adapter type. Valid value are "fc_host" and "scsi_host". + If omitted and the <code>name</code> attribute is specified, then it + defaults to "scsi_host". To keep backwards compatibility, the + attribute <code>type</code> is optional for the "scsi_host" adapter, + but mandatory for the "fc_host" adapter. Attributes <code>wwnn</code> (Word Wide Node Name) and <code>wwpn</code> (Word Wide Port Name) (<span class="since">1.0.4</span>) are used by the "fc_host" adapter to uniquely indentify the device in the Fibre Channel storage farbic diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index c1c163e..cc1ebe2 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -632,14 +632,49 @@ out: }
static int +getHostNumber(const char *adapter_name, + unsigned int *result) +{ + char *host = (char *)adapter_name; + + /* Specifying adapter like 'host5' is still supported for + * back-compat reason. + */ + if (STRPREFIX(host, "scsi_host")) { + host += strlen("scsi_host"); + } else if (STRPREFIX(host, "host")) { + host += strlen("host"); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid adapter name '%s' for scsi pool"),
s/invalid/Invalid s/scsi/SCSI
+ adapter_name); + return -1; + } + + if (result && virStrToLong_ui(host, NULL, 10, result) == -1) {
Consider using ATTRIBUTE_NONNULL(2) on the argument instead... Although this is a local/static function...
+ virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid adapter name '%s' for scsi pool"),
s/invalid/Invalid s/scsi/SCSI
+ adapter_name); + return -1; + } + + return 0; +} + +static int virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, bool *isActive) { char *path; + unsigned int host;
*isActive = false; - if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter.data.name) < 0) { + + if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) + return -1; + + if (virAsprintf(&path, "/sys/class/scsi_host/host%d", host) < 0) { virReportOOMError(); return -1; } @@ -656,29 +691,24 @@ static int virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool) { - int retval = 0; - uint32_t host; + int ret = -1; + unsigned int host;
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
- if (sscanf(pool->def->source.adapter.data.name, "host%u", &host) != 1) { - VIR_DEBUG("Failed to get host number from '%s'", - pool->def->source.adapter.data.name); - retval = -1; + if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) goto out; - }
VIR_DEBUG("Scanning host%u", host);
- if (virStorageBackendSCSITriggerRescan(host) < 0) { - retval = -1; + if (virStorageBackendSCSITriggerRescan(host) < 0) goto out; - }
virStorageBackendSCSIFindLUs(pool, host);
+ ret = 0; out: - return retval; + return ret; }
ACK with cleanups

It's only used by iscsi backend. --- src/storage/storage_backend_iscsi.c | 39 ++++++++++++++++++++++++++++++++++++- src/storage/storage_backend_scsi.c | 39 ------------------------------------- src/storage/storage_backend_scsi.h | 3 --- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/src/storage/storage_backend_iscsi.c b/src/storage/storage_backend_iscsi.c index f374961..f6b76ed 100644 --- a/src/storage/storage_backend_iscsi.c +++ b/src/storage/storage_backend_iscsi.c @@ -23,6 +23,7 @@ #include <config.h> +#include <dirent.h> #include <sys/socket.h> #include <netdb.h> #include <sys/types.h> @@ -401,6 +402,42 @@ cleanup: return ret; } +static int +virStorageBackendISCSIGetHostNumber(const char *sysfs_path, + uint32_t *host) +{ + int retval = 0; + DIR *sysdir = NULL; + struct dirent *dirent = NULL; + + VIR_DEBUG("Finding host number from '%s'", sysfs_path); + + virFileWaitForDevices(); + + sysdir = opendir(sysfs_path); + + if (sysdir == NULL) { + virReportSystemError(errno, + _("Failed to opendir path '%s'"), sysfs_path); + retval = -1; + goto out; + } + + while ((dirent = readdir(sysdir))) { + if (STREQLEN(dirent->d_name, "target", strlen("target"))) { + if (sscanf(dirent->d_name, + "target%u:", host) != 1) { + VIR_DEBUG("Failed to parse target '%s'", dirent->d_name); + retval = -1; + break; + } + } + } + + closedir(sysdir); +out: + return retval; +} static int virStorageBackendISCSIFindLUs(virStoragePoolObjPtr pool, @@ -416,7 +453,7 @@ virStorageBackendISCSIFindLUs(virStoragePoolObjPtr pool, return -1; } - if (virStorageBackendSCSIGetHostNumber(sysfs_path, &host) < 0) { + if (virStorageBackendISCSIGetHostNumber(sysfs_path, &host) < 0) { virReportSystemError(errno, _("Failed to get host number for iSCSI session " "with path '%s'"), diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index cc1ebe2..1bf6c0b 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -547,45 +547,6 @@ out: return retval; } - -int -virStorageBackendSCSIGetHostNumber(const char *sysfs_path, - uint32_t *host) -{ - int retval = 0; - DIR *sysdir = NULL; - struct dirent *dirent = NULL; - - VIR_DEBUG("Finding host number from '%s'", sysfs_path); - - virFileWaitForDevices(); - - sysdir = opendir(sysfs_path); - - if (sysdir == NULL) { - virReportSystemError(errno, - _("Failed to opendir path '%s'"), sysfs_path); - retval = -1; - goto out; - } - - while ((dirent = readdir(sysdir))) { - if (STREQLEN(dirent->d_name, "target", strlen("target"))) { - if (sscanf(dirent->d_name, - "target%u:", host) != 1) { - VIR_DEBUG("Failed to parse target '%s'", dirent->d_name); - retval = -1; - break; - } - } - } - - closedir(sysdir); -out: - return retval; -} - - static int virStorageBackendSCSITriggerRescan(uint32_t host) { diff --git a/src/storage/storage_backend_scsi.h b/src/storage/storage_backend_scsi.h index 1cdd0da..0984fd5 100644 --- a/src/storage/storage_backend_scsi.h +++ b/src/storage/storage_backend_scsi.h @@ -33,9 +33,6 @@ extern virStorageBackend virStorageBackendSCSI; int -virStorageBackendSCSIGetHostNumber(const char *sysfs_path, - uint32_t *host); -int virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool, uint32_t scanhost); -- 1.8.0.1

On 03/25/2013 12:43 PM, Osier Yang wrote:
It's only used by iscsi backend. --- src/storage/storage_backend_iscsi.c | 39 ++++++++++++++++++++++++++++++++++++- src/storage/storage_backend_scsi.c | 39 ------------------------------------- src/storage/storage_backend_scsi.h | 3 --- 3 files changed, 38 insertions(+), 43 deletions(-)
diff --git a/src/storage/storage_backend_iscsi.c b/src/storage/storage_backend_iscsi.c index f374961..f6b76ed 100644 --- a/src/storage/storage_backend_iscsi.c +++ b/src/storage/storage_backend_iscsi.c @@ -23,6 +23,7 @@
#include <config.h>
+#include <dirent.h> #include <sys/socket.h> #include <netdb.h> #include <sys/types.h> @@ -401,6 +402,42 @@ cleanup: return ret; }
+static int +virStorageBackendISCSIGetHostNumber(const char *sysfs_path, + uint32_t *host) +{ + int retval = 0; + DIR *sysdir = NULL; + struct dirent *dirent = NULL; + + VIR_DEBUG("Finding host number from '%s'", sysfs_path); + + virFileWaitForDevices(); + + sysdir = opendir(sysfs_path); + + if (sysdir == NULL) { + virReportSystemError(errno, + _("Failed to opendir path '%s'"), sysfs_path); + retval = -1; + goto out; + } + + while ((dirent = readdir(sysdir))) { + if (STREQLEN(dirent->d_name, "target", strlen("target"))) { + if (sscanf(dirent->d_name, + "target%u:", host) != 1) { + VIR_DEBUG("Failed to parse target '%s'", dirent->d_name); + retval = -1; + break; + } + } + } + + closedir(sysdir); +out: + return retval; +}
static int virStorageBackendISCSIFindLUs(virStoragePoolObjPtr pool, @@ -416,7 +453,7 @@ virStorageBackendISCSIFindLUs(virStoragePoolObjPtr pool, return -1; }
- if (virStorageBackendSCSIGetHostNumber(sysfs_path, &host) < 0) { + if (virStorageBackendISCSIGetHostNumber(sysfs_path, &host) < 0) { virReportSystemError(errno, _("Failed to get host number for iSCSI session " "with path '%s'"), diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index cc1ebe2..1bf6c0b 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -547,45 +547,6 @@ out: return retval; }
- -int -virStorageBackendSCSIGetHostNumber(const char *sysfs_path, - uint32_t *host) -{ - int retval = 0; - DIR *sysdir = NULL; - struct dirent *dirent = NULL; - - VIR_DEBUG("Finding host number from '%s'", sysfs_path); - - virFileWaitForDevices(); - - sysdir = opendir(sysfs_path); - - if (sysdir == NULL) { - virReportSystemError(errno, - _("Failed to opendir path '%s'"), sysfs_path); - retval = -1; - goto out; - } - - while ((dirent = readdir(sysdir))) { - if (STREQLEN(dirent->d_name, "target", strlen("target"))) { - if (sscanf(dirent->d_name, - "target%u:", host) != 1) { - VIR_DEBUG("Failed to parse target '%s'", dirent->d_name); - retval = -1; - break; - } - } - } - - closedir(sysdir); -out: - return retval; -} - - static int virStorageBackendSCSITriggerRescan(uint32_t host) { diff --git a/src/storage/storage_backend_scsi.h b/src/storage/storage_backend_scsi.h index 1cdd0da..0984fd5 100644 --- a/src/storage/storage_backend_scsi.h +++ b/src/storage/storage_backend_scsi.h @@ -33,9 +33,6 @@ extern virStorageBackend virStorageBackendSCSI;
int -virStorageBackendSCSIGetHostNumber(const char *sysfs_path, - uint32_t *host); -int virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool, uint32_t scanhost);
ACK (and FYI I did check - the backend_scsi.c module has other usages of dirent.h) John

It's possible to support fc_host adapter for phyp driver too, but at this stage I'd like to not allow it when I'm not that clear how it works. --- src/phyp/phyp_driver.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 3a97364..a4e327e 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -2519,6 +2519,13 @@ phypBuildStoragePool(virConnectPtr conn, virStoragePoolDefPtr def) int exit_status = 0; virBuffer buf = VIR_BUFFER_INITIALIZER; + if (source.adapter.type != + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Only 'scsi_host' adapter is supported")); + goto cleanup; + } + if (system_type == HMC) virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '", managed_system, vios_id); -- 1.8.0.1

On 03/25/2013 12:43 PM, Osier Yang wrote:
It's possible to support fc_host adapter for phyp driver too, but at this stage I'd like to not allow it when I'm not that clear how it works. --- src/phyp/phyp_driver.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 3a97364..a4e327e 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -2519,6 +2519,13 @@ phypBuildStoragePool(virConnectPtr conn, virStoragePoolDefPtr def) int exit_status = 0; virBuffer buf = VIR_BUFFER_INITIALIZER;
+ if (source.adapter.type != + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) {
So if it's == 0 (eg. DEFAULT) or not defined, then what happens here? Can/will we get here in that scenario.
+ virReportError(VIR_ERR_XML_ERROR, "%s", + _("Only 'scsi_host' adapter is supported")); + goto cleanup; + } + if (system_type == HMC) virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '", managed_system, vios_id);
ACK with question/concern handled. John

On 05/04/13 23:20, John Ferlan wrote:
On 03/25/2013 12:43 PM, Osier Yang wrote:
It's possible to support fc_host adapter for phyp driver too, but at this stage I'd like to not allow it when I'm not that clear how it works. --- src/phyp/phyp_driver.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 3a97364..a4e327e 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -2519,6 +2519,13 @@ phypBuildStoragePool(virConnectPtr conn, virStoragePoolDefPtr def) int exit_status = 0; virBuffer buf = VIR_BUFFER_INITIALIZER;
+ if (source.adapter.type != + VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { So if it's == 0 (eg. DEFAULT) or not defined, then what happens here? Can/will we get here in that scenario.
It can't, see the XML parsing, the type is either SCSI_HOST or FC_HOST after the parsing.
+ virReportError(VIR_ERR_XML_ERROR, "%s", + _("Only 'scsi_host' adapter is supported")); + goto cleanup; + } + if (system_type == HMC) virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '", managed_system, vios_id);
ACK with question/concern handled.
John
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

The helper iterates over sysfs, to find out the matched scsi host name by comparing the wwnn,wwpn pair. It will be used by checkPool and refreshPool of storage scsi backend. New helper getAdapterName is introduced in storage_backend_scsi.c, which uses the new util helper virGetFCHostNameByWWN to get the fc_host adapter name. --- src/libvirt_private.syms | 1 + src/storage/storage_backend_scsi.c | 49 ++++++++++++++--- src/util/virutil.c | 109 +++++++++++++++++++++++++++++++++++++ src/util/virutil.h | 9 ++- 4 files changed, 159 insertions(+), 9 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 39c02ad..3df7632 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1854,6 +1854,7 @@ virFindFileInPath; virFormatIntDecimal; virGetDeviceID; virGetDeviceUnprivSGIO; +virGetFCHostNameByWWN; virGetGroupID; virGetGroupName; virGetHostname; diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 1bf6c0b..258c82e 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -603,6 +603,8 @@ getHostNumber(const char *adapter_name, */ if (STRPREFIX(host, "scsi_host")) { host += strlen("scsi_host"); + } else if (STRPREFIX(host, "fc_host")) { + host += strlen("fc_host"); } else if (STRPREFIX(host, "host")) { host += strlen("host"); } else { @@ -622,42 +624,74 @@ getHostNumber(const char *adapter_name, return 0; } +static char * +getAdapterName(virStoragePoolSourceAdapter adapter) +{ + char *name = NULL; + + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) + return strdup(adapter.data.name); + + if (!(name = virGetFCHostNameByWWN(NULL, + adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn))) { + virReportError(VIR_ERR_XML_ERROR, + _("Failed to find SCSI host with wwnn='%s', " + "wwpn='%s'"), adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn); + return NULL; + } + + return name; +} + static int virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, bool *isActive) { - char *path; + char *path = NULL; + char *name = NULL; unsigned int host; + int ret = -1; *isActive = false; - if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) + if (!(name = getAdapterName(pool->def->source.adapter))) return -1; + if (getHostNumber(name, &host) < 0) + goto cleanup; + if (virAsprintf(&path, "/sys/class/scsi_host/host%d", host) < 0) { virReportOOMError(); - return -1; + goto cleanup; } if (access(path, F_OK) == 0) *isActive = true; + ret = 0; +cleanup: VIR_FREE(path); - - return 0; + VIR_FREE(name); + return ret; } static int virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool) { - int ret = -1; + char *name = NULL; unsigned int host; + int ret = -1; pool->def->allocation = pool->def->capacity = pool->def->available = 0; - if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) + if (!(name = getAdapterName(pool->def->source.adapter))) + return -1; + + if (getHostNumber(name, &host) < 0) goto out; VIR_DEBUG("Scanning host%u", host); @@ -669,6 +703,7 @@ virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, ret = 0; out: + VIR_FREE(name); return ret; } diff --git a/src/util/virutil.c b/src/util/virutil.c index 557225c..a8b9962 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -26,6 +26,7 @@ #include <config.h> +#include <dirent.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> @@ -3570,6 +3571,105 @@ cleanup: VIR_FREE(operation_path); return ret; } + +/* virGetHostNameByWWN: + * + * Iterate over the sysfs tree to get SCSI host name (e.g. scsi_host5) + * by wwnn,wwpn pair. + */ +char * +virGetFCHostNameByWWN(const char *sysfs_prefix, + const char *wwnn, + const char *wwpn) +{ + const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH; + struct dirent *entry = NULL; + DIR *dir = NULL; + char *wwnn_path = NULL; + char *wwpn_path = NULL; + char *wwnn_buf = NULL; + char *wwpn_buf = NULL; + char *p; + char *ret = NULL; + + if (!(dir = opendir(prefix))) { + virReportSystemError(errno, + _("Failed to opendir path '%s'"), + prefix); + return NULL; + } + +# define READ_WWN(wwn_path, buf) \ + do { \ + if (virFileReadAll(wwn_path, 1024, &buf) < 0) \ + goto cleanup; \ + if ((p = strchr(buf, '\n'))) \ + *p = '\0'; \ + if (STRPREFIX(buf, "0x")) \ + p = buf + strlen("0x"); \ + else \ + p = buf; \ + } while (0) + + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') + continue; + + if (virAsprintf(&wwnn_path, "%s%s/node_name", prefix, + entry->d_name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!virFileExists(wwnn_path)) { + VIR_FREE(wwnn_path); + continue; + } + + READ_WWN(wwnn_path, wwnn_buf); + + if (STRNEQ(wwnn, p)) { + VIR_FREE(wwpn_buf); + VIR_FREE(wwnn_path); + continue; + } + + if (virAsprintf(&wwpn_path, "%s%s/port_name", prefix, + entry->d_name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!virFileExists(wwpn_path)) { + VIR_FREE(wwnn_buf); + VIR_FREE(wwnn_path); + VIR_FREE(wwpn_path); + continue; + } + + READ_WWN(wwpn_path, wwpn_buf); + + if (STRNEQ(wwpn, p)) { + VIR_FREE(wwnn_path); + VIR_FREE(wwpn_path); + VIR_FREE(wwnn_buf); + VIR_FREE(wwpn_buf); + continue; + } + + ret = strdup(entry->d_name); + goto cleanup; + } + +cleanup: +# undef READ_WWN + closedir(dir); + VIR_FREE(wwnn_path); + VIR_FREE(wwpn_path); + VIR_FREE(wwnn_buf); + VIR_FREE(wwpn_buf); + return ret; +} #else int virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED, @@ -3606,4 +3706,13 @@ virManageVport(const int parent_host ATTRIBUTE_UNUSED, return -1; } +char * +virGetFCHostNameByWWN(const char *sysfs_prefix ATTRIBUTE_UNUSED, + const char *wwnn ATTRIBUTE_UNUSED, + const char *wwpn ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", _("Not supported on this platform")); + return NULL; +} + #endif /* __linux__ */ diff --git a/src/util/virutil.h b/src/util/virutil.h index 47357fa..d134034 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -295,8 +295,8 @@ int virSetDeviceUnprivSGIO(const char *path, int virGetDeviceUnprivSGIO(const char *path, const char *sysfs_dir, int *unpriv_sgio); -char * virGetUnprivSGIOSysfsPath(const char *path, - const char *sysfs_dir); +char *virGetUnprivSGIOSysfsPath(const char *path, + const char *sysfs_dir); int virReadFCHost(const char *sysfs_prefix, int host, const char *entry, @@ -317,4 +317,9 @@ int virManageVport(const int parent_host, int operation) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +char *virGetFCHostNameByWWN(const char *sysfs_prefix, + const char *wwnn, + const char *wwpn) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + #endif /* __VIR_UTIL_H__ */ -- 1.8.0.1

On 03/25/2013 12:43 PM, Osier Yang wrote:
The helper iterates over sysfs, to find out the matched scsi host name by comparing the wwnn,wwpn pair. It will be used by checkPool and refreshPool of storage scsi backend. New helper getAdapterName is introduced in storage_backend_scsi.c, which uses the new util helper virGetFCHostNameByWWN to get the fc_host adapter name. --- src/libvirt_private.syms | 1 + src/storage/storage_backend_scsi.c | 49 ++++++++++++++--- src/util/virutil.c | 109 +++++++++++++++++++++++++++++++++++++ src/util/virutil.h | 9 ++- 4 files changed, 159 insertions(+), 9 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 39c02ad..3df7632 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1854,6 +1854,7 @@ virFindFileInPath; virFormatIntDecimal; virGetDeviceID; virGetDeviceUnprivSGIO; +virGetFCHostNameByWWN; virGetGroupID; virGetGroupName; virGetHostname; diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 1bf6c0b..258c82e 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -603,6 +603,8 @@ getHostNumber(const char *adapter_name, */ if (STRPREFIX(host, "scsi_host")) { host += strlen("scsi_host"); + } else if (STRPREFIX(host, "fc_host")) { + host += strlen("fc_host");
How is this related? Or even possible? The number is associated (at least so far) with the SCSI "name" parameter which isn't used for FC. Both callers below still only call here iff it's SCSI_HOST.
} else if (STRPREFIX(host, "host")) { host += strlen("host"); } else { @@ -622,42 +624,74 @@ getHostNumber(const char *adapter_name, return 0; }
+static char * +getAdapterName(virStoragePoolSourceAdapter adapter) +{ + char *name = NULL; + + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) + return strdup(adapter.data.name); + + if (!(name = virGetFCHostNameByWWN(NULL, + adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn))) { + virReportError(VIR_ERR_XML_ERROR, + _("Failed to find SCSI host with wwnn='%s', " + "wwpn='%s'"), adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn); + return NULL;
superfluous since name = NULL... :-)
+ } + + return name; +} + static int virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, bool *isActive) { - char *path; + char *path = NULL; + char *name = NULL; unsigned int host; + int ret = -1;
*isActive = false;
- if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) + if (!(name = getAdapterName(pool->def->source.adapter))) return -1;
+ if (getHostNumber(name, &host) < 0) + goto cleanup; + if (virAsprintf(&path, "/sys/class/scsi_host/host%d", host) < 0) { virReportOOMError(); - return -1; + goto cleanup; }
if (access(path, F_OK) == 0) *isActive = true;
+ ret = 0; +cleanup: VIR_FREE(path); - - return 0; + VIR_FREE(name); + return ret; }
static int virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool) { - int ret = -1; + char *name = NULL; unsigned int host; + int ret = -1;
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
- if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) + if (!(name = getAdapterName(pool->def->source.adapter))) + return -1; + + if (getHostNumber(name, &host) < 0) goto out;
VIR_DEBUG("Scanning host%u", host); @@ -669,6 +703,7 @@ virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
ret = 0; out: + VIR_FREE(name); return ret; }
diff --git a/src/util/virutil.c b/src/util/virutil.c index 557225c..a8b9962 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -26,6 +26,7 @@
#include <config.h>
+#include <dirent.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> @@ -3570,6 +3571,105 @@ cleanup: VIR_FREE(operation_path); return ret; } + +/* virGetHostNameByWWN: + * + * Iterate over the sysfs tree to get SCSI host name (e.g. scsi_host5) + * by wwnn,wwpn pair. + */ +char * +virGetFCHostNameByWWN(const char *sysfs_prefix, + const char *wwnn, + const char *wwpn) +{ + const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH; + struct dirent *entry = NULL; + DIR *dir = NULL; + char *wwnn_path = NULL; + char *wwpn_path = NULL; + char *wwnn_buf = NULL; + char *wwpn_buf = NULL; + char *p; + char *ret = NULL; + + if (!(dir = opendir(prefix))) { + virReportSystemError(errno, + _("Failed to opendir path '%s'"), + prefix); + return NULL; + } + +# define READ_WWN(wwn_path, buf) \ + do { \ + if (virFileReadAll(wwn_path, 1024, &buf) < 0) \ + goto cleanup; \ + if ((p = strchr(buf, '\n'))) \ + *p = '\0'; \ + if (STRPREFIX(buf, "0x")) \ + p = buf + strlen("0x"); \ + else \ + p = buf; \
suggestions: 1. Add another parameter to take wwnn or wwpn (ww_name?) 2. VIR_FREE(wwn_path) here 3. VIR_FREE(buf) here too 4. You could probably put the virFileExists check in this macro too - appropriately placed of course. It'd only need to free the _path generated from virAsprintf(). VIR_FREE(wwn_path); \ if (STRNEQ(ww_name, p)) { \ VIR_FREE(buf); \ continue; \ } \ VIR_FREE(buf); \ I think this works - you can double check my eyesight though. After you exit the macro, neither the virAsprintf buffer nor the virFileReadAll buffers will still be allocated. Thus reducing the need for keeping track...
+ } while (0) + + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') + continue; + + if (virAsprintf(&wwnn_path, "%s%s/node_name", prefix, + entry->d_name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!virFileExists(wwnn_path)) { + VIR_FREE(wwnn_path); + continue; + } + + READ_WWN(wwnn_path, wwnn_buf); + + if (STRNEQ(wwnn, p)) { + VIR_FREE(wwpn_buf);
In any case: s/wwpn_buf/wwnn_buf right?
+ VIR_FREE(wwnn_path); + continue; + } + + if (virAsprintf(&wwpn_path, "%s%s/port_name", prefix, + entry->d_name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!virFileExists(wwpn_path)) { + VIR_FREE(wwnn_buf); + VIR_FREE(wwnn_path); + VIR_FREE(wwpn_path); + continue; + } + + READ_WWN(wwpn_path, wwpn_buf); + + if (STRNEQ(wwpn, p)) { + VIR_FREE(wwnn_path); + VIR_FREE(wwpn_path); + VIR_FREE(wwnn_buf); + VIR_FREE(wwpn_buf); + continue; + } + + ret = strdup(entry->d_name); + goto cleanup;
s/goto cleanup/break Same difference...
+ } + +cleanup: +# undef READ_WWN + closedir(dir); + VIR_FREE(wwnn_path); + VIR_FREE(wwpn_path); + VIR_FREE(wwnn_buf); + VIR_FREE(wwpn_buf); + return ret; +} #else int virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED, @@ -3606,4 +3706,13 @@ virManageVport(const int parent_host ATTRIBUTE_UNUSED, return -1; }
+char * +virGetFCHostNameByWWN(const char *sysfs_prefix ATTRIBUTE_UNUSED, + const char *wwnn ATTRIBUTE_UNUSED, + const char *wwpn ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", _("Not supported on this platform")); + return NULL; +} + #endif /* __linux__ */ diff --git a/src/util/virutil.h b/src/util/virutil.h index 47357fa..d134034 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -295,8 +295,8 @@ int virSetDeviceUnprivSGIO(const char *path, int virGetDeviceUnprivSGIO(const char *path, const char *sysfs_dir, int *unpriv_sgio); -char * virGetUnprivSGIOSysfsPath(const char *path, - const char *sysfs_dir); +char *virGetUnprivSGIOSysfsPath(const char *path, + const char *sysfs_dir); int virReadFCHost(const char *sysfs_prefix, int host, const char *entry, @@ -317,4 +317,9 @@ int virManageVport(const int parent_host, int operation) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+char *virGetFCHostNameByWWN(const char *sysfs_prefix, + const char *wwnn, + const char *wwpn) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + #endif /* __VIR_UTIL_H__ */
ACK - with a bit of cleanup. Depending on responses and path chosen, a followup could be done. John

On 06/04/13 03:11, John Ferlan wrote: > On 03/25/2013 12:43 PM, Osier Yang wrote: >> The helper iterates over sysfs, to find out the matched scsi host >> name by comparing the wwnn,wwpn pair. It will be used by checkPool >> and refreshPool of storage scsi backend. New helper getAdapterName >> is introduced in storage_backend_scsi.c, which uses the new util >> helper virGetFCHostNameByWWN to get the fc_host adapter name. >> --- >> src/libvirt_private.syms | 1 + >> src/storage/storage_backend_scsi.c | 49 ++++++++++++++--- >> src/util/virutil.c | 109 +++++++++++++++++++++++++++++++++++++ >> src/util/virutil.h | 9 ++- >> 4 files changed, 159 insertions(+), 9 deletions(-) >> >> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms >> index 39c02ad..3df7632 100644 >> --- a/src/libvirt_private.syms >> +++ b/src/libvirt_private.syms >> @@ -1854,6 +1854,7 @@ virFindFileInPath; >> virFormatIntDecimal; >> virGetDeviceID; >> virGetDeviceUnprivSGIO; >> +virGetFCHostNameByWWN; >> virGetGroupID; >> virGetGroupName; >> virGetHostname; >> diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c >> index 1bf6c0b..258c82e 100644 >> --- a/src/storage/storage_backend_scsi.c >> +++ b/src/storage/storage_backend_scsi.c >> @@ -603,6 +603,8 @@ getHostNumber(const char *adapter_name, >> */ >> if (STRPREFIX(host, "scsi_host")) { >> host += strlen("scsi_host"); >> + } else if (STRPREFIX(host, "fc_host")) { >> + host += strlen("fc_host"); > How is this related? Or even possible? The number is associated (at > least so far) with the SCSI "name" parameter which isn't used for FC. > Both callers below still only call here iff it's SCSI_HOST. I use getHostNumber after getAdapterName, which suits for both scsi_host and fc_host. :-) > >> } else if (STRPREFIX(host, "host")) { >> host += strlen("host"); >> } else { >> @@ -622,42 +624,74 @@ getHostNumber(const char *adapter_name, >> return 0; >> } >> >> +static char * >> +getAdapterName(virStoragePoolSourceAdapter adapter) >> +{ >> + char *name = NULL; >> + >> + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) >> + return strdup(adapter.data.name); >> + >> + if (!(name = virGetFCHostNameByWWN(NULL, >> + adapter.data.fchost.wwnn, >> + adapter.data.fchost.wwpn))) { >> + virReportError(VIR_ERR_XML_ERROR, >> + _("Failed to find SCSI host with wwnn='%s', " >> + "wwpn='%s'"), adapter.data.fchost.wwnn, >> + adapter.data.fchost.wwpn); >> + return NULL; > superfluous since name = NULL... :-) Cleaned. > >> + } >> + >> + return name; >> +} >> + >> static int >> virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, >> virStoragePoolObjPtr pool, >> bool *isActive) >> { >> - char *path; >> + char *path = NULL; >> + char *name = NULL; >> unsigned int host; >> + int ret = -1; >> >> *isActive = false; >> >> - if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) >> + if (!(name = getAdapterName(pool->def->source.adapter))) >> return -1; >> >> + if (getHostNumber(name, &host) < 0) >> + goto cleanup; >> + >> if (virAsprintf(&path, "/sys/class/scsi_host/host%d", host) < 0) { >> virReportOOMError(); >> - return -1; >> + goto cleanup; >> } >> >> if (access(path, F_OK) == 0) >> *isActive = true; >> >> + ret = 0; >> +cleanup: >> VIR_FREE(path); >> - >> - return 0; >> + VIR_FREE(name); >> + return ret; >> } >> >> static int >> virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, >> virStoragePoolObjPtr pool) >> { >> - int ret = -1; >> + char *name = NULL; >> unsigned int host; >> + int ret = -1; >> >> pool->def->allocation = pool->def->capacity = pool->def->available = 0; >> >> - if (getHostNumber(pool->def->source.adapter.data.name, &host) < 0) >> + if (!(name = getAdapterName(pool->def->source.adapter))) >> + return -1; >> + >> + if (getHostNumber(name, &host) < 0) >> goto out; >> >> VIR_DEBUG("Scanning host%u", host); >> @@ -669,6 +703,7 @@ virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, >> >> ret = 0; >> out: >> + VIR_FREE(name); >> return ret; >> } >> >> diff --git a/src/util/virutil.c b/src/util/virutil.c >> index 557225c..a8b9962 100644 >> --- a/src/util/virutil.c >> +++ b/src/util/virutil.c >> @@ -26,6 +26,7 @@ >> >> #include <config.h> >> >> +#include <dirent.h> >> #include <stdio.h> >> #include <stdarg.h> >> #include <stdlib.h> >> @@ -3570,6 +3571,105 @@ cleanup: >> VIR_FREE(operation_path); >> return ret; >> } >> + >> +/* virGetHostNameByWWN: >> + * >> + * Iterate over the sysfs tree to get SCSI host name (e.g. scsi_host5) >> + * by wwnn,wwpn pair. >> + */ >> +char * >> +virGetFCHostNameByWWN(const char *sysfs_prefix, >> + const char *wwnn, >> + const char *wwpn) >> +{ >> + const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH; >> + struct dirent *entry = NULL; >> + DIR *dir = NULL; >> + char *wwnn_path = NULL; >> + char *wwpn_path = NULL; >> + char *wwnn_buf = NULL; >> + char *wwpn_buf = NULL; >> + char *p; >> + char *ret = NULL; >> + >> + if (!(dir = opendir(prefix))) { >> + virReportSystemError(errno, >> + _("Failed to opendir path '%s'"), >> + prefix); >> + return NULL; >> + } >> + >> +# define READ_WWN(wwn_path, buf) \ >> + do { \ >> + if (virFileReadAll(wwn_path, 1024, &buf) < 0) \ >> + goto cleanup; \ >> + if ((p = strchr(buf, '\n'))) \ >> + *p = '\0'; \ >> + if (STRPREFIX(buf, "0x")) \ >> + p = buf + strlen("0x"); \ >> + else \ >> + p = buf; \ > suggestions: > 1. Add another parameter to take wwnn or wwpn (ww_name?) > 2. VIR_FREE(wwn_path) here > 3. VIR_FREE(buf) here too > 4. You could probably put the virFileExists check in this macro too - > appropriately placed of course. It'd only need to free the _path > generated from virAsprintf(). > > VIR_FREE(wwn_path); \ > if (STRNEQ(ww_name, p)) { \ > VIR_FREE(buf); \ > continue; \ This will just jump out this "do...while" loop, perhaps there is a way to work around. If it is, let's do in a later patch. > } \ > VIR_FREE(buf); \ > > I think this works - you can double check my eyesight though. After you > exit the macro, neither the virAsprintf buffer nor the virFileReadAll > buffers will still be allocated. Thus reducing the need for keeping > track... > >> + } while (0) >> + >> + while ((entry = readdir(dir))) { >> + if (entry->d_name[0] == '.') >> + continue; >> + >> + if (virAsprintf(&wwnn_path, "%s%s/node_name", prefix, >> + entry->d_name) < 0) { >> + virReportOOMError(); >> + goto cleanup; >> + } >> + >> + if (!virFileExists(wwnn_path)) { >> + VIR_FREE(wwnn_path); >> + continue; >> + } >> + >> + READ_WWN(wwnn_path, wwnn_buf); >> + >> + if (STRNEQ(wwnn, p)) { >> + VIR_FREE(wwpn_buf); > In any case: > > s/wwpn_buf/wwnn_buf > > right? Right, cleaned. > >> + VIR_FREE(wwnn_path); >> + continue; >> + } >> + >> + if (virAsprintf(&wwpn_path, "%s%s/port_name", prefix, >> + entry->d_name) < 0) { >> + virReportOOMError(); >> + goto cleanup; >> + } >> + >> + if (!virFileExists(wwpn_path)) { >> + VIR_FREE(wwnn_buf); >> + VIR_FREE(wwnn_path); >> + VIR_FREE(wwpn_path); >> + continue; >> + } >> + >> + READ_WWN(wwpn_path, wwpn_buf); >> + >> + if (STRNEQ(wwpn, p)) { >> + VIR_FREE(wwnn_path); >> + VIR_FREE(wwpn_path); >> + VIR_FREE(wwnn_buf); >> + VIR_FREE(wwpn_buf); >> + continue; >> + } >> + >> + ret = strdup(entry->d_name); >> + goto cleanup; > s/goto cleanup/break > > Same difference... Cleaned. >> + } >> + >> +cleanup: >> +# undef READ_WWN >> + closedir(dir); >> + VIR_FREE(wwnn_path); >> + VIR_FREE(wwpn_path); >> + VIR_FREE(wwnn_buf); >> + VIR_FREE(wwpn_buf); >> + return ret; >> +} >> #else >> int >> virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED, >> @@ -3606,4 +3706,13 @@ virManageVport(const int parent_host ATTRIBUTE_UNUSED, >> return -1; >> } >> >> +char * >> +virGetFCHostNameByWWN(const char *sysfs_prefix ATTRIBUTE_UNUSED, >> + const char *wwnn ATTRIBUTE_UNUSED, >> + const char *wwpn ATTRIBUTE_UNUSED) >> +{ >> + virReportSystemError(ENOSYS, "%s", _("Not supported on this platform")); >> + return NULL; >> +} >> + >> #endif /* __linux__ */ >> diff --git a/src/util/virutil.h b/src/util/virutil.h >> index 47357fa..d134034 100644 >> --- a/src/util/virutil.h >> +++ b/src/util/virutil.h >> @@ -295,8 +295,8 @@ int virSetDeviceUnprivSGIO(const char *path, >> int virGetDeviceUnprivSGIO(const char *path, >> const char *sysfs_dir, >> int *unpriv_sgio); >> -char * virGetUnprivSGIOSysfsPath(const char *path, >> - const char *sysfs_dir); >> +char *virGetUnprivSGIOSysfsPath(const char *path, >> + const char *sysfs_dir); >> int virReadFCHost(const char *sysfs_prefix, >> int host, >> const char *entry, >> @@ -317,4 +317,9 @@ int virManageVport(const int parent_host, >> int operation) >> ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); >> >> +char *virGetFCHostNameByWWN(const char *sysfs_prefix, >> + const char *wwnn, >> + const char *wwpn) >> + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); >> + >> #endif /* __VIR_UTIL_H__ */ >> > ACK - with a bit of cleanup. Depending on responses and path chosen, a > followup could be done. > > John > > -- > libvir-list mailing list > libvir-list@redhat.com > https://www.redhat.com/mailman/listinfo/libvir-list

startPool creates the vHBA if it's not existed yet, stopPool destroys the vHBA. Also to support autostart, checkPool will creates the vHBA if it's not existed yet. --- src/storage/storage_backend_scsi.c | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 258c82e..0bb4e70 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -646,6 +646,36 @@ getAdapterName(virStoragePoolSourceAdapter adapter) } static int +createVport(virStoragePoolSourceAdapter adapter) +{ + unsigned int parent_host; + + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) + return 0; + + /* This filters either HBA or already created vHBA */ + if (virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn)) + return 0; + + if (!adapter.data.fchost.parent) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'parent' for vHBA must be specified")); + return -1; + } + + if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) + return -1; + + if (virManageVport(parent_host, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn, VPORT_CREATE) < 0) + return -1; + + virFileWaitForDevices(); + return 0; +} + +static int virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, bool *isActive) @@ -707,10 +737,44 @@ out: return ret; } +static int +virStorageBackendSCSIStartPool(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool) +{ + virStoragePoolSourceAdapter adapter = pool->def->source.adapter; + + return createVport(adapter); +} + +static int +virStorageBackendSCSIStopPool(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool) +{ + virStoragePoolSourceAdapter adapter = pool->def->source.adapter; + unsigned int parent_host; + + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) + return 0; + + if (!(virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn))) + return -1; + + if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) + return -1; + + if (virManageVport(parent_host, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn, VPORT_DELETE) < 0) + return -1; + + return 0; +} virStorageBackend virStorageBackendSCSI = { .type = VIR_STORAGE_POOL_SCSI, .checkPool = virStorageBackendSCSICheckPool, .refreshPool = virStorageBackendSCSIRefreshPool, + .startPool = virStorageBackendSCSIStartPool, + .stopPool = virStorageBackendSCSIStopPool, }; -- 1.8.0.1

On 03/25/2013 12:43 PM, Osier Yang wrote:
startPool creates the vHBA if it's not existed yet, stopPool destroys
s/it's not exists/it does not exist/
the vHBA. Also to support autostart, checkPool will creates the vHBA
s/creates/create
if it's not existed yet. s/it's not existed/it does not exist
--- src/storage/storage_backend_scsi.c | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 258c82e..0bb4e70 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -646,6 +646,36 @@ getAdapterName(virStoragePoolSourceAdapter adapter) }
static int +createVport(virStoragePoolSourceAdapter adapter) +{ + unsigned int parent_host; + + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) + return 0;
Since the StopPool has the same check - why put this check in here instead of StartPool?
+ + /* This filters either HBA or already created vHBA */ + if (virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn)) + return 0;
there's a memory leak here as the called function returns strdup() value. Of course this logic could be backwards too and what you meant was "if (!vir...)" probably changes the return value too... I hope it goes without saying that the leak exists below too :-)
+ + if (!adapter.data.fchost.parent) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'parent' for vHBA must be specified")); + return -1; + } + + if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) + return -1; + + if (virManageVport(parent_host, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn, VPORT_CREATE) < 0) + return -1; + + virFileWaitForDevices(); + return 0; +} + +static int virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, bool *isActive) @@ -707,10 +737,44 @@ out: return ret; }
+static int +virStorageBackendSCSIStartPool(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool) +{ + virStoragePoolSourceAdapter adapter = pool->def->source.adapter; + + return createVport(adapter); +} + +static int +virStorageBackendSCSIStopPool(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool) +{ + virStoragePoolSourceAdapter adapter = pool->def->source.adapter; + unsigned int parent_host; + + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) + return 0; + + if (!(virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn))) + return -1; + + if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) + return -1; + + if (virManageVport(parent_host, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn, VPORT_DELETE) < 0) + return -1; + + return 0;
"Consistently speaking" there could have been a deleteVport() or the "createVport()" is/was unnecessary.
+}
virStorageBackend virStorageBackendSCSI = { .type = VIR_STORAGE_POOL_SCSI,
.checkPool = virStorageBackendSCSICheckPool, .refreshPool = virStorageBackendSCSIRefreshPool, + .startPool = virStorageBackendSCSIStartPool, + .stopPool = virStorageBackendSCSIStopPool, };
I think this one needs a refactor and re-review. John

On 06/04/13 03:29, John Ferlan wrote:
On 03/25/2013 12:43 PM, Osier Yang wrote:
startPool creates the vHBA if it's not existed yet, stopPool destroys s/it's not exists/it does not exist/
Okay.
the vHBA. Also to support autostart, checkPool will creates the vHBA s/creates/create
Okay.
if it's not existed yet. s/it's not existed/it does not exist
Okay.
--- src/storage/storage_backend_scsi.c | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 258c82e..0bb4e70 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -646,6 +646,36 @@ getAdapterName(virStoragePoolSourceAdapter adapter) }
static int +createVport(virStoragePoolSourceAdapter adapter) +{ + unsigned int parent_host; + + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) + return 0; Since the StopPool has the same check - why put this check in here instead of StartPool?
Sometimes you will want to put checking inside helper to avoid the caller incorrectly use it. Though the checking of this helper is simple enough to let the caller make mistake. But I'd keep it here.
+ + /* This filters either HBA or already created vHBA */ + if (virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn)) + return 0; there's a memory leak here as the called function returns strdup() value. Of course this logic could be backwards too and what you meant was "if (!vir...)" probably changes the return value too...
Added the VIR_FREE.
I hope it goes without saying that the leak exists below too :-)
+ + if (!adapter.data.fchost.parent) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'parent' for vHBA must be specified")); + return -1; + } + + if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) + return -1; + + if (virManageVport(parent_host, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn, VPORT_CREATE) < 0) + return -1; + + virFileWaitForDevices(); + return 0; +} + +static int virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, bool *isActive) @@ -707,10 +737,44 @@ out: return ret; }
+static int +virStorageBackendSCSIStartPool(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool) +{ + virStoragePoolSourceAdapter adapter = pool->def->source.adapter; + + return createVport(adapter); +} + +static int +virStorageBackendSCSIStopPool(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool) +{ + virStoragePoolSourceAdapter adapter = pool->def->source.adapter; + unsigned int parent_host; + + if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) + return 0; + + if (!(virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn))) + return -1; + + if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) + return -1; + + if (virManageVport(parent_host, adapter.data.fchost.wwnn, + adapter.data.fchost.wwpn, VPORT_DELETE) < 0) + return -1; + + return 0; "Consistently speaking" there could have been a deleteVport() or the "createVport()" is/was unnecessary.
I added a deleteVport.
+}
virStorageBackend virStorageBackendSCSI = { .type = VIR_STORAGE_POOL_SCSI,
.checkPool = virStorageBackendSCSICheckPool, .refreshPool = virStorageBackendSCSIRefreshPool, + .startPool = virStorageBackendSCSIStartPool, + .stopPool = virStorageBackendSCSIStopPool, };
I think this one needs a refactor and re-review.
The straightforward diff is attached, and I assume you will ACK it.

This finds the parent for vHBA by iterating over all the HBA which supports vport_ops capability on the host, and return the first one which is online, not saturated (vports in use is less than max_vports). --- src/libvirt_private.syms | 1 + src/storage/storage_backend_scsi.c | 10 ++-- src/util/virutil.c | 93 ++++++++++++++++++++++++++++++++++++++ src/util/virutil.h | 2 + 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3df7632..932a448 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1850,6 +1850,7 @@ virFileStripSuffix; virFileUnlock; virFileWaitForDevices; virFileWriteStr; +virFindFCHostCapableVport; virFindFileInPath; virFormatIntDecimal; virGetDeviceID; diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 0bb4e70..b1ff127 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -658,10 +658,12 @@ createVport(virStoragePoolSourceAdapter adapter) adapter.data.fchost.wwpn)) return 0; - if (!adapter.data.fchost.parent) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("'parent' for vHBA must be specified")); - return -1; + if (!adapter.data.fchost.parent && + !(adapter.data.fchost.parent = virFindFCHostCapableVport(NULL))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'parent' for vHBA not specified, and " + "cannot find one on this host")); + return -1; } if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) diff --git a/src/util/virutil.c b/src/util/virutil.c index a8b9962..55d807e 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -3670,6 +3670,92 @@ cleanup: VIR_FREE(wwpn_buf); return ret; } + +# define PORT_STATE_ONLINE "Online" + +/* virFindFCHostCapableVport: + * + * Iterate over the sysfs and find out the first online HBA which + * supports vport, and not saturated,. + */ +char * +virFindFCHostCapableVport(const char *sysfs_prefix) +{ + const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH; + DIR *dir = NULL; + struct dirent *entry = NULL; + char *max_vports = NULL; + char *vports = NULL; + char *state = NULL; + char *ret = NULL; + + if (!(dir = opendir(prefix))) { + virReportSystemError(errno, + _("Failed to opendir path '%s'"), + prefix); + return NULL; + } + + while ((entry = readdir(dir))) { + unsigned int host; + char *p = NULL; + + if (entry->d_name[0] == '.') + continue; + + p = entry->d_name + strlen("host"); + if (virStrToLong_ui(p, NULL, 10, &host) == -1) { + VIR_DEBUG("Failed to parse host number from '%s'", + entry->d_name); + continue; + } + + if (!virIsCapableVport(NULL, host)) + continue; + + if (virReadFCHost(NULL, host, "port_state", &state) < 0) { + VIR_DEBUG("Failed to read port_state for host%d", host); + continue; + } + + /* Skip the not online FC host */ + if (!STREQ(state, PORT_STATE_ONLINE)) { + VIR_FREE(state); + continue; + } + VIR_FREE(state); + + if (virReadFCHost(NULL, host, "max_npiv_vports", &max_vports) < 0) { + VIR_DEBUG("Failed to read max_npiv_vports for host%d", host); + continue; + } + + if (virReadFCHost(NULL, host, "npiv_vports_inuse", &vports) < 0) { + VIR_DEBUG("Failed to read npiv_vports_inuse for host%d", host); + VIR_FREE(max_vports); + continue; + } + + /* Compare from the strings directly, instead of converting + * the strings to integers first + */ + if ((strlen(max_vports) >= strlen(vports)) || + ((strlen(max_vports) == strlen(vports)) && + strcmp(max_vports, vports) > 0)) { + ret = strdup(entry->d_name); + goto cleanup; + } + + VIR_FREE(max_vports); + VIR_FREE(vports); + } + +cleanup: + closedir(dir); + VIR_FREE(max_vports); + VIR_FREE(vports); + return ret; +} #else int virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED, @@ -3715,4 +3801,11 @@ virGetFCHostNameByWWN(const char *sysfs_prefix ATTRIBUTE_UNUSED, return NULL; } +char * +virFindFCHostCapableVport(const char *sysfs_prefix ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", _("Not supported on this platform")); + return NULL; +} + #endif /* __linux__ */ diff --git a/src/util/virutil.h b/src/util/virutil.h index d134034..cde4218 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -322,4 +322,6 @@ char *virGetFCHostNameByWWN(const char *sysfs_prefix, const char *wwpn) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +char *virFindFCHostCapableVport(const char *sysfs_prefix); + #endif /* __VIR_UTIL_H__ */ -- 1.8.0.1

On 03/25/2013 12:43 PM, Osier Yang wrote:
This finds the parent for vHBA by iterating over all the HBA which supports vport_ops capability on the host, and return the first one which is online, not saturated (vports in use is less than max_vports). --- src/libvirt_private.syms | 1 + src/storage/storage_backend_scsi.c | 10 ++-- src/util/virutil.c | 93 ++++++++++++++++++++++++++++++++++++++ src/util/virutil.h | 2 + 4 files changed, 102 insertions(+), 4 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3df7632..932a448 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1850,6 +1850,7 @@ virFileStripSuffix; virFileUnlock; virFileWaitForDevices; virFileWriteStr; +virFindFCHostCapableVport; virFindFileInPath; virFormatIntDecimal; virGetDeviceID; diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 0bb4e70..b1ff127 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -658,10 +658,12 @@ createVport(virStoragePoolSourceAdapter adapter) adapter.data.fchost.wwpn)) return 0;
- if (!adapter.data.fchost.parent) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("'parent' for vHBA must be specified")); - return -1; + if (!adapter.data.fchost.parent && + !(adapter.data.fchost.parent = virFindFCHostCapableVport(NULL))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'parent' for vHBA not specified, and " + "cannot find one on this host")); + return -1; }
if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) diff --git a/src/util/virutil.c b/src/util/virutil.c index a8b9962..55d807e 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -3670,6 +3670,92 @@ cleanup: VIR_FREE(wwpn_buf); return ret; } + +# define PORT_STATE_ONLINE "Online" + +/* virFindFCHostCapableVport: + * + * Iterate over the sysfs and find out the first online HBA which + * supports vport, and not saturated,. + */ +char * +virFindFCHostCapableVport(const char *sysfs_prefix) +{ + const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH; + DIR *dir = NULL; + struct dirent *entry = NULL; + char *max_vports = NULL; + char *vports = NULL; + char *state = NULL; + char *ret = NULL; + + if (!(dir = opendir(prefix))) { + virReportSystemError(errno, + _("Failed to opendir path '%s'"), + prefix); + return NULL; + } + + while ((entry = readdir(dir))) { + unsigned int host; + char *p = NULL; + + if (entry->d_name[0] == '.') + continue; + + p = entry->d_name + strlen("host");
Assumes all entries are prefixed by "host", right? Thus should anything that is not be filtered like the ".", "..", and hidden files are above?
+ if (virStrToLong_ui(p, NULL, 10, &host) == -1) { + VIR_DEBUG("Failed to parse host number from '%s'", + entry->d_name); + continue; + } + + if (!virIsCapableVport(NULL, host)) + continue; + + if (virReadFCHost(NULL, host, "port_state", &state) < 0) { + VIR_DEBUG("Failed to read port_state for host%d", host); + continue; + } + + /* Skip the not online FC host */ + if (!STREQ(state, PORT_STATE_ONLINE)) {
s/!STREQ/STRNEQ/ ??
+ VIR_FREE(state); + continue; + } + VIR_FREE(state); + + if (virReadFCHost(NULL, host, "max_npiv_vports", &max_vports) < 0) { + VIR_DEBUG("Failed to read max_npiv_vports for host%d", host); + continue; + } + + if (virReadFCHost(NULL, host, "npiv_vports_inuse", &vports) < 0) { + VIR_DEBUG("Failed to read npiv_vports_inuse for host%d", host); + VIR_FREE(max_vports); + continue; + } + + /* Compare from the strings directly, instead of converting + * the strings to integers first + */ + if ((strlen(max_vports) >= strlen(vports)) || + ((strlen(max_vports) == strlen(vports)) && + strcmp(max_vports, vports) > 0)) { + ret = strdup(entry->d_name); + goto cleanup; + } + + VIR_FREE(max_vports); + VIR_FREE(vports); + } + +cleanup: + closedir(dir); + VIR_FREE(max_vports); + VIR_FREE(vports); + return ret; +} #else int virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED, @@ -3715,4 +3801,11 @@ virGetFCHostNameByWWN(const char *sysfs_prefix ATTRIBUTE_UNUSED, return NULL; }
+char * +virFindFCHostCapableVport(const char *sysfs_prefix ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", _("Not supported on this platform")); + return NULL; +} + #endif /* __linux__ */ diff --git a/src/util/virutil.h b/src/util/virutil.h index d134034..cde4218 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -322,4 +322,6 @@ char *virGetFCHostNameByWWN(const char *sysfs_prefix, const char *wwpn) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+char *virFindFCHostCapableVport(const char *sysfs_prefix); + #endif /* __VIR_UTIL_H__ */
ACK - NOTE that I still haven't seen anywhere "fc_host" would be necessary from patch 5/7. John

On 06/04/13 04:01, John Ferlan wrote:
This finds the parent for vHBA by iterating over all the HBA which supports vport_ops capability on the host, and return the first one which is online, not saturated (vports in use is less than max_vports). --- src/libvirt_private.syms | 1 + src/storage/storage_backend_scsi.c | 10 ++-- src/util/virutil.c | 93 ++++++++++++++++++++++++++++++++++++++ src/util/virutil.h | 2 + 4 files changed, 102 insertions(+), 4 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3df7632..932a448 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1850,6 +1850,7 @@ virFileStripSuffix; virFileUnlock; virFileWaitForDevices; virFileWriteStr; +virFindFCHostCapableVport; virFindFileInPath; virFormatIntDecimal; virGetDeviceID; diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 0bb4e70..b1ff127 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -658,10 +658,12 @@ createVport(virStoragePoolSourceAdapter adapter) adapter.data.fchost.wwpn)) return 0;
- if (!adapter.data.fchost.parent) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("'parent' for vHBA must be specified")); - return -1; + if (!adapter.data.fchost.parent && + !(adapter.data.fchost.parent = virFindFCHostCapableVport(NULL))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'parent' for vHBA not specified, and " + "cannot find one on this host")); + return -1; }
if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0) diff --git a/src/util/virutil.c b/src/util/virutil.c index a8b9962..55d807e 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -3670,6 +3670,92 @@ cleanup: VIR_FREE(wwpn_buf); return ret; } + +# define PORT_STATE_ONLINE "Online" + +/* virFindFCHostCapableVport: + * + * Iterate over the sysfs and find out the first online HBA which + * supports vport, and not saturated,. + */ +char * +virFindFCHostCapableVport(const char *sysfs_prefix) +{ + const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH; + DIR *dir = NULL; + struct dirent *entry = NULL; + char *max_vports = NULL; + char *vports = NULL; + char *state = NULL; + char *ret = NULL; + + if (!(dir = opendir(prefix))) { + virReportSystemError(errno, + _("Failed to opendir path '%s'"), + prefix); + return NULL; + } + + while ((entry = readdir(dir))) { + unsigned int host; + char *p = NULL; + + if (entry->d_name[0] == '.') + continue; + + p = entry->d_name + strlen("host"); Assumes all entries are prefixed by "host", right? Thus should anything
On 03/25/2013 12:43 PM, Osier Yang wrote: that is not be filtered like the ".", "..", and hidden files are above?
Why not to filter them?
+ if (virStrToLong_ui(p, NULL, 10, &host) == -1) { + VIR_DEBUG("Failed to parse host number from '%s'", + entry->d_name); + continue; + } + + if (!virIsCapableVport(NULL, host)) + continue; + + if (virReadFCHost(NULL, host, "port_state", &state) < 0) { + VIR_DEBUG("Failed to read port_state for host%d", host); + continue; + } + + /* Skip the not online FC host */ + if (!STREQ(state, PORT_STATE_ONLINE)) { s/!STREQ/STRNEQ/ ??
Ah, right, corrected.
+ VIR_FREE(state); + continue; + } + VIR_FREE(state); + + if (virReadFCHost(NULL, host, "max_npiv_vports", &max_vports) < 0) { + VIR_DEBUG("Failed to read max_npiv_vports for host%d", host); + continue; + } + + if (virReadFCHost(NULL, host, "npiv_vports_inuse", &vports) < 0) { + VIR_DEBUG("Failed to read npiv_vports_inuse for host%d", host); + VIR_FREE(max_vports); + continue; + } + + /* Compare from the strings directly, instead of converting + * the strings to integers first + */ + if ((strlen(max_vports) >= strlen(vports)) || + ((strlen(max_vports) == strlen(vports)) && + strcmp(max_vports, vports) > 0)) { + ret = strdup(entry->d_name); + goto cleanup; + } + + VIR_FREE(max_vports); + VIR_FREE(vports); + } + +cleanup: + closedir(dir); + VIR_FREE(max_vports); + VIR_FREE(vports); + return ret; +} #else int virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED, @@ -3715,4 +3801,11 @@ virGetFCHostNameByWWN(const char *sysfs_prefix ATTRIBUTE_UNUSED, return NULL; }
+char * +virFindFCHostCapableVport(const char *sysfs_prefix ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", _("Not supported on this platform")); + return NULL; +} + #endif /* __linux__ */ diff --git a/src/util/virutil.h b/src/util/virutil.h index d134034..cde4218 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -322,4 +322,6 @@ char *virGetFCHostNameByWWN(const char *sysfs_prefix, const char *wwpn) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+char *virFindFCHostCapableVport(const char *sysfs_prefix); + #endif /* __VIR_UTIL_H__ */
ACK - NOTE that I still haven't seen anywhere "fc_host" would be necessary from patch 5/7.
John
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 03/25/2013 10:43 AM, Osier Yang wrote:
v2: https://www.redhat.com/archives/libvir-list/2013-February/msg00118.html
v2 - v3: * 6/8 of v1 is ACKed/pushed. * Use virStringToLong_ui instead of sscanf * Fixed various nits pointed out by John
This is the 3rd part to implement NPIV migration support [1].
Sorry I haven't been able to look at this yet; is it something that needs to go in now, or can it wait until after 1.0.4? -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 28/03/13 06:00, Eric Blake wrote:
On 03/25/2013 10:43 AM, Osier Yang wrote:
v2: https://www.redhat.com/archives/libvir-list/2013-February/msg00118.html
v2 - v3: * 6/8 of v1 is ACKed/pushed. * Use virStringToLong_ui instead of sscanf * Fixed various nits pointed out by John
This is the 3rd part to implement NPIV migration support [1]. Sorry I haven't been able to look at this yet; is it something that needs to go in now, or can it wait until after 1.0.4?
Though I'm always looking forward to get it in, but It can wait after 1.0.4, as there is 4th part left, which is on the way to come. :-) Osier

On 28/03/13 06:00, Eric Blake wrote:
On 03/25/2013 10:43 AM, Osier Yang wrote:
v2: https://www.redhat.com/archives/libvir-list/2013-February/msg00118.html
v2 - v3: * 6/8 of v1 is ACKed/pushed. * Use virStringToLong_ui instead of sscanf * Fixed various nits pointed out by John
This is the 3rd part to implement NPIV migration support [1]. Sorry I haven't been able to look at this yet; is it something that needs to go in now, or can it wait until after 1.0.4?
Hi, Eric. I just sent out the 4th part: https://www.redhat.com/archives/libvir-list/2013-April/msg00416.html Can you please give a reviewing of both part 3 and part 4 these days? Thanks! Osier

Pushed this set with the nits pointed out by John, with assuming there is an ACK for the diff of 6/7. Thanks for the reviewing. On 26/03/13 00:43, Osier Yang wrote:
v2: https://www.redhat.com/archives/libvir-list/2013-February/msg00118.html
v2 - v3: * 6/8 of v1 is ACKed/pushed. * Use virStringToLong_ui instead of sscanf * Fixed various nits pointed out by John
This is the 3rd part to implement NPIV migration support [1].
Part 1: (pushed) https://www.redhat.com/archives/libvir-list/2013-February/msg00112.html
Part 2: (pushed) https://www.redhat.com/archives/libvir-list/2013-January/msg00859.html
Osier Yang (7): New XML attributes for storage pool source adapter storage: Make the adapter name be consistent with node device driver storage: Move virStorageBackendSCSIGetHostNumber into iscsi backend phyp: Prohibit fc_host adapter for phyp driver util: Add helper to get the scsi host name by iterating over sysfs storage: Add startPool and stopPool for scsi backend storage: Guess the parent if it's not specified for vHBA
docs/formatstorage.html.in | 17 +- docs/schemas/storagepool.rng | 33 +++- src/conf/storage_conf.c | 132 +++++++++++++- src/conf/storage_conf.h | 23 ++- src/libvirt_private.syms | 4 + src/phyp/phyp_driver.c | 15 +- src/storage/storage_backend_iscsi.c | 39 +++- src/storage/storage_backend_scsi.c | 202 +++++++++++++++------ src/storage/storage_backend_scsi.h | 3 - src/util/virutil.c | 202 +++++++++++++++++++++ src/util/virutil.h | 11 +- .../pool-scsi-type-fc-host.xml | 15 ++ .../pool-scsi-type-scsi-host.xml | 15 ++ .../pool-scsi-type-fc-host.xml | 18 ++ .../pool-scsi-type-scsi-host.xml | 18 ++ tests/storagepoolxml2xmlout/pool-scsi.xml | 2 +- tests/storagepoolxml2xmltest.c | 2 + 17 files changed, 669 insertions(+), 82 deletions(-) create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml
participants (3)
-
Eric Blake
-
John Ferlan
-
Osier Yang