[libvirt] [PATCH 00/11] Allow creation of vHBA by parent_wwnn/wwpn or fabric_name

https://bugzilla.redhat.com/show_bug.cgi?id=1349696 Lots of details in the bz, but essentially the problem is that providing a "parent" scsi_hostX value has drawbacks on reboots because what was scsi_hostX could turn into scsi_hostY on subsequent reboots. So add the ability to use the parent wwnn/wwpn or fabric_wwn as 'search' criteria in order to create either non persistent vHBA's via nodedev or persistent vHBA's via storage pools. NB: Documentation of this "process" is on the wiki: http://wiki.libvirt.org/page/NPIV_in_libvirt and would need to be adjusted once/if the changes are accepted. John Ferlan (11): nodedev: Fix crash in libvirtd on vHBA creation path nodedev: Create helpers to search for vport capable nodedevs nodedev: Add ability to find a vport capable vHBA nodedev: Add the ability to create vHBA by parent wwnn/wwpn or fabric_wwn conf: Add more fchost search fields for storage pool vHBA creation iscsi: Clean up createVport exit paths iscsi: Change order of checks in createVport iscsi: Converge more createVport checks util: Remove need for extra VIR_FREE's in virGetFCHostNameByWWN util: Introduce virGetFCHostNameByFabricWWN iscsi: Add parent wwnn/wwpn or fabric capability for createVport docs/schemas/basictypes.rng | 15 +++ docs/schemas/nodedev.rng | 15 +++ src/conf/node_device_conf.c | 234 +++++++++++++++++++++++++++++++---- src/conf/node_device_conf.h | 17 +++ src/conf/storage_conf.c | 21 +++- src/conf/storage_conf.h | 3 + src/libvirt_private.syms | 4 + src/node_device/node_device_driver.c | 28 ++++- src/storage/storage_backend_scsi.c | 86 +++++++------ src/util/virutil.c | 113 ++++++++++++----- src/util/virutil.h | 4 + 11 files changed, 439 insertions(+), 101 deletions(-) -- 2.7.4

Providing XML such as: <device> <name>vhba</name> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device> would crash libvirt because the '<parent>' isn't a required field, but for vHBA creation it's expected (day 1 issue - see commit id '81d0ffbc'). The nodedev.rng added in commit id '2c22a68c' has this as an optional field. NB: On normal udev discovery if a parent field wasn't found, it would be set to "computer" by udevSetParent, so this is a somewhat unique path. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/node_device_conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 1cd0baf..bf5b22f 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -118,7 +118,7 @@ virNodeDeviceObjPtr virNodeDeviceFindByName(virNodeDeviceObjListPtr devs, for (i = 0; i < devs->count; i++) { virNodeDeviceObjLock(devs->objs[i]); - if (STREQ(devs->objs[i]->def->name, name)) + if (STREQ_NULLABLE(devs->objs[i]->def->name, name)) return devs->objs[i]; virNodeDeviceObjUnlock(devs->objs[i]); } -- 2.7.4

On Fri, Nov 18, 2016 at 09:26:27AM -0500, John Ferlan wrote:
Providing XML such as:
<device> <name>vhba</name> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device>
would crash libvirt because the '<parent>' isn't a required field, but for vHBA creation it's expected (day 1 issue - see commit id '81d0ffbc'). The nodedev.rng added in commit id '2c22a68c' has this as an optional field. NB: On normal udev discovery if a parent field wasn't found, it would be set to "computer" by udevSetParent, so this is a somewhat unique path.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/node_device_conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 1cd0baf..bf5b22f 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -118,7 +118,7 @@ virNodeDeviceObjPtr virNodeDeviceFindByName(virNodeDeviceObjListPtr devs,
for (i = 0; i < devs->count; i++) { virNodeDeviceObjLock(devs->objs[i]); - if (STREQ(devs->objs[i]->def->name, name)) + if (STREQ_NULLABLE(devs->objs[i]->def->name, name))
Is this needed after patch 3/11? I presume we do not store objects with NULL names in devs. In that case the callers should avoid calling this with NULL name - what is the point of iterating over the array in that case? Jan
return devs->objs[i]; virNodeDeviceObjUnlock(devs->objs[i]); }

On 01/02/2017 09:16 AM, Ján Tomko wrote:
On Fri, Nov 18, 2016 at 09:26:27AM -0500, John Ferlan wrote:
Providing XML such as:
<device> <name>vhba</name> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device>
would crash libvirt because the '<parent>' isn't a required field, but for vHBA creation it's expected (day 1 issue - see commit id '81d0ffbc'). The nodedev.rng added in commit id '2c22a68c' has this as an optional field. NB: On normal udev discovery if a parent field wasn't found, it would be set to "computer" by udevSetParent, so this is a somewhat unique path.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/node_device_conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 1cd0baf..bf5b22f 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -118,7 +118,7 @@ virNodeDeviceObjPtr virNodeDeviceFindByName(virNodeDeviceObjListPtr devs,
for (i = 0; i < devs->count; i++) { virNodeDeviceObjLock(devs->objs[i]); - if (STREQ(devs->objs[i]->def->name, name)) + if (STREQ_NULLABLE(devs->objs[i]->def->name, name))
Is this needed after patch 3/11?
Doesn't seem so - perhaps a bit of paranoia kicked in... Cannot remember for sure since it's been so long...
I presume we do not store objects with NULL names in devs. In that case the callers should avoid calling this with NULL name - what is the point of iterating over the array in that case?
Good assumption as objs[i]->def->name is the key for node devices (they don't have uuids). Good question - I have a faint recollection of tripping across this no parent path based on some historical comment somewhere that said one doesn't have to provide the parent in the nodedev-create XML... So I went back, tried again with just this patch applied the create fails with: error: Failed to create node device from vhba_nodedev.xml error: internal error: Could not find parent device for 'new device' Once patch 3 is applied the creation occurs "as advertised" (that is if you don't supply a parent the code will find "a" parent that this vport capable. Whether this ever actually worked I don't recall and didn't chase. In any case, I'll drop this patch (for now) and can post a followup that would handle a NULL 'name' better (just return NULL immediately rather than going through the whole loop needlessly). John
Jan
return devs->objs[i]; virNodeDeviceObjUnlock(devs->objs[i]); }

Extract out code from virNodeDeviceGetParentHost into helpers - it's going to be reused in upcoming patches to search on more fields Create virNodeDeviceFindVPORTCapDef in order to return a virNodeDevCapsDefPtr of the VPORT_OPS and virNodeDeviceFindFCParentHost to use the function and generate an error message if the device doesn't have the capability. Also clean up the processing in virNodeDeviceGetParentHost to remove need for goto's. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/node_device_conf.c | 83 +++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index bf5b22f..5396681 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -92,6 +92,30 @@ int virNodeDeviceHasCap(const virNodeDeviceObj *dev, const char *cap) } +/* virNodeDeviceFindVPORTCapDef: + * @dev: Pointer to current device + * + * Search the device object 'caps' array for vport_ops capability. + * + * Returns: + * Pointer to the caps or NULL if not found + */ +static virNodeDevCapsDefPtr +virNodeDeviceFindVPORTCapDef(const virNodeDeviceObj *dev) +{ + virNodeDevCapsDefPtr caps = dev->def->caps; + + while (caps) { + if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST && + (caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS)) + break; + + caps = caps->next; + } + return caps; +} + + virNodeDeviceObjPtr virNodeDeviceFindBySysfsPath(virNodeDeviceObjListPtr devs, const char *sysfs_path) @@ -1752,6 +1776,35 @@ virNodeDeviceGetWWNs(virNodeDeviceDefPtr def, /* * Return the NPIV dev's parent device name */ +/* virNodeDeviceFindFCParentHost: + * @parent: Pointer to node device object + * @parent_host: Pointer to return parent host number + * + * Search the capabilities for the device to find the FC capabilities + * in order to set the parent_host value. + * + * Returns: + * 0 on success with parent_host set, -1 otherwise; + */ +static int +virNodeDeviceFindFCParentHost(virNodeDeviceObjPtr parent, + int *parent_host) +{ + virNodeDevCapsDefPtr cap = virNodeDeviceFindVPORTCapDef(parent); + + if (!cap) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Parent device %s is not capable " + "of vport operations"), + parent->def->name); + return -1; + } + + *parent_host = cap->data.scsi_host.host; + return 0; +} + + int virNodeDeviceGetParentHost(virNodeDeviceObjListPtr devs, const char *dev_name, @@ -1759,41 +1812,19 @@ virNodeDeviceGetParentHost(virNodeDeviceObjListPtr devs, int *parent_host) { virNodeDeviceObjPtr parent = NULL; - virNodeDevCapsDefPtr cap = NULL; - int ret = 0; + int ret; - parent = virNodeDeviceFindByName(devs, parent_name); - if (parent == NULL) { + if (!(parent = virNodeDeviceFindByName(devs, parent_name))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not find parent device for '%s'"), dev_name); - ret = -1; - goto out; - } - - cap = parent->def->caps; - while (cap != NULL) { - if (cap->data.type == VIR_NODE_DEV_CAP_SCSI_HOST && - (cap->data.scsi_host.flags & - VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS)) { - *parent_host = cap->data.scsi_host.host; - break; - } - - cap = cap->next; + return -1; } - if (cap == NULL) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Parent device %s is not capable " - "of vport operations"), - parent->def->name); - ret = -1; - } + ret = virNodeDeviceFindFCParentHost(parent, parent_host); virNodeDeviceObjUnlock(parent); - out: return ret; } -- 2.7.4

On Fri, Nov 18, 2016 at 09:26:28AM -0500, John Ferlan wrote:
Extract out code from virNodeDeviceGetParentHost into helpers - it's going to be reused in upcoming patches to search on more fields
Create virNodeDeviceFindVPORTCapDef in order to return a virNodeDevCapsDefPtr of the VPORT_OPS and virNodeDeviceFindFCParentHost to use the function and generate an error message if the device doesn't have the capability.
Also clean up the processing in virNodeDeviceGetParentHost to remove need for goto's.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/node_device_conf.c | 83 +++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 26 deletions(-)
ACK Jan

If a <parent> is not supplied in the XML used to create a non-persistent vHBA, then instead of failing, let's try to find a "vports" capable node device and use that. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/node_device_conf.c | 39 ++++++++++++++++++++++++++++++++++++ src/conf/node_device_conf.h | 3 +++ src/libvirt_private.syms | 1 + src/node_device/node_device_driver.c | 15 +++++++++----- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 5396681..3aa77cf 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -151,6 +151,23 @@ virNodeDeviceObjPtr virNodeDeviceFindByName(virNodeDeviceObjListPtr devs, } +static virNodeDeviceObjPtr +virNodeDeviceFindByCap(virNodeDeviceObjListPtr devs, + const char *cap) +{ + size_t i; + + for (i = 0; i < devs->count; i++) { + virNodeDeviceObjLock(devs->objs[i]); + if (virNodeDeviceHasCap(devs->objs[i], cap)) + return devs->objs[i]; + virNodeDeviceObjUnlock(devs->objs[i]); + } + + return NULL; +} + + void virNodeDeviceDefFree(virNodeDeviceDefPtr def) { virNodeDevCapsDefPtr caps; @@ -1828,6 +1845,28 @@ virNodeDeviceGetParentHost(virNodeDeviceObjListPtr devs, return ret; } + +int +virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, + int *parent_host) +{ + virNodeDeviceObjPtr parent = NULL; + int ret; + + if (!(parent = virNodeDeviceFindByCap(devs, "vports"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not find any vport capable device")); + return -1; + } + + ret = virNodeDeviceFindFCParentHost(parent, parent_host); + + virNodeDeviceObjUnlock(parent); + + return ret; +} + + void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps) { size_t i = 0; diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 9f00500..2b2aed7 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -273,6 +273,9 @@ int virNodeDeviceGetParentHost(virNodeDeviceObjListPtr devs, const char *parent_name, int *parent_host); +int virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, + int *parent_host); + void virNodeDeviceDefFree(virNodeDeviceDefPtr def); void virNodeDeviceObjFree(virNodeDeviceObjPtr dev); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index baff82b..de14a7e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -699,6 +699,7 @@ virNodeDeviceDefParseNode; virNodeDeviceDefParseString; virNodeDeviceFindByName; virNodeDeviceFindBySysfsPath; +virNodeDeviceFindVportParentHost; virNodeDeviceGetParentHost; virNodeDeviceGetWWNs; virNodeDeviceHasCap; diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index 91bb142..0e091fe 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -584,11 +584,16 @@ nodeDeviceCreateXML(virConnectPtr conn, if (virNodeDeviceGetWWNs(def, &wwnn, &wwpn) == -1) goto cleanup; - if (virNodeDeviceGetParentHost(&driver->devs, - def->name, - def->parent, - &parent_host) == -1) { - goto cleanup; + if (def->parent) { + if (virNodeDeviceGetParentHost(&driver->devs, + def->name, + def->parent, + &parent_host) < 0) + goto cleanup; + } else { + /* Try to find "a" vport capable scsi_host when no parent supplied */ + if (virNodeDeviceFindVportParentHost(&driver->devs, &parent_host) < 0) + goto cleanup; } if (virManageVport(parent_host, -- 2.7.4

On Fri, Nov 18, 2016 at 09:26:29AM -0500, John Ferlan wrote:
If a <parent> is not supplied in the XML used to create a non-persistent vHBA, then instead of failing, let's try to find a "vports" capable node device and use that.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/node_device_conf.c | 39 ++++++++++++++++++++++++++++++++++++ src/conf/node_device_conf.h | 3 +++ src/libvirt_private.syms | 1 + src/node_device/node_device_driver.c | 15 +++++++++----- 4 files changed, 53 insertions(+), 5 deletions(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 5396681..3aa77cf 100644 + +int +virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, + int *parent_host) +{ + virNodeDeviceObjPtr parent = NULL; + int ret; + + if (!(parent = virNodeDeviceFindByCap(devs, "vports"))) {
Why do we pass the capability as a string?
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not find any vport capable device")); + return -1; + } +
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index 91bb142..0e091fe 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -584,11 +584,16 @@ nodeDeviceCreateXML(virConnectPtr conn, if (virNodeDeviceGetWWNs(def, &wwnn, &wwpn) == -1) goto cleanup;
- if (virNodeDeviceGetParentHost(&driver->devs, - def->name, - def->parent, - &parent_host) == -1) { - goto cleanup; + if (def->parent) { + if (virNodeDeviceGetParentHost(&driver->devs, + def->name, + def->parent, + &parent_host) < 0) + goto cleanup; + } else { + /* Try to find "a" vport capable scsi_host when no parent supplied */
The quotes make me nervous.
+ if (virNodeDeviceFindVportParentHost(&driver->devs, &parent_host) < 0) + goto cleanup; }
ACK Jan

On 01/02/2017 09:18 AM, Ján Tomko wrote:
On Fri, Nov 18, 2016 at 09:26:29AM -0500, John Ferlan wrote:
If a <parent> is not supplied in the XML used to create a non-persistent vHBA, then instead of failing, let's try to find a "vports" capable node device and use that.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/node_device_conf.c | 39 ++++++++++++++++++++++++++++++++++++ src/conf/node_device_conf.h | 3 +++ src/libvirt_private.syms | 1 + src/node_device/node_device_driver.c | 15 +++++++++----- 4 files changed, 53 insertions(+), 5 deletions(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 5396681..3aa77cf 100644 + +int +virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, + int *parent_host) +{ + virNodeDeviceObjPtr parent = NULL; + int ret; + + if (!(parent = virNodeDeviceFindByCap(devs, "vports"))) {
Why do we pass the capability as a string?
OK - I'll alter this to: const char *cap = virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_VPORTS); and use "cap" instead of "vports". NB: I cannot search on the virNodeDevCapType because it's actually a "subtype" of the 'scsi_host' type (as seen in virNodeDeviceHasCap) The virNodeDeviceHasCap should also alter those hardcoded STREQ values to use the virNodeDevCapTypeToString for VIR_NODE_DEV_CAP_FC_HOST and VIR_NODE_DEV_CAP_VPORTS... I'll squash the following in (sorry - inline and will mess with mail window widths, but I tested this it works): diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 42f6593..b13cb6b 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -75,14 +75,19 @@ virNodeDevCapsDefParseString(const char *xpath, int virNodeDeviceHasCap(const virNodeDeviceObj *dev, const char *cap) { virNodeDevCapsDefPtr caps = dev->def->caps; + const char *fc_host_cap = + virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_FC_HOST); + const char *vports_cap = + virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_VPORTS); + while (caps) { if (STREQ(cap, virNodeDevCapTypeToString(caps->data.type))) return 1; else if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST) - if ((STREQ(cap, "fc_host") && + if ((STREQ(cap, fc_host_cap) && (caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST)) || - (STREQ(cap, "vports") && + (STREQ(cap, vports_cap) && (caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS))) return 1; @@ -1851,9 +1856,10 @@ virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, int *parent_host) { virNodeDeviceObjPtr parent = NULL; + const char *cap = virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_VPORTS); int ret; - if (!(parent = virNodeDeviceFindByCap(devs, "vports"))) { + if (!(parent = virNodeDeviceFindByCap(devs, cap))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not find any vport capable device")); return -1;
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not find any vport capable device")); + return -1; + } +
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index 91bb142..0e091fe 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -584,11 +584,16 @@ nodeDeviceCreateXML(virConnectPtr conn, if (virNodeDeviceGetWWNs(def, &wwnn, &wwpn) == -1) goto cleanup;
- if (virNodeDeviceGetParentHost(&driver->devs, - def->name, - def->parent, - &parent_host) == -1) { - goto cleanup; + if (def->parent) { + if (virNodeDeviceGetParentHost(&driver->devs, + def->name, + def->parent, + &parent_host) < 0) + goto cleanup; + } else { + /* Try to find "a" vport capable scsi_host when no parent supplied */
The quotes make me nervous.
Removed... I think the point of them was it'll be capable, but there's no check if there's enough ports remaining for use. That'll fail differently though. John
+ if (virNodeDeviceFindVportParentHost(&driver->devs, &parent_host) < 0) + goto cleanup; }
ACK
Jan

https://bugzilla.redhat.com/show_bug.cgi?id=1349696 When creating a vHBA, the process is to feed XML to nodeDeviceCreateXML that lists the <parent> scsi_hostX to use to create the vHBA. However, between reboots, it's possible that the <parent> changes its scsi_hostX to scsi_hostY and saved XML to perform the creation will either fail or create a vHBA using the wrong parent. So add the ability to provide <parent_wwnn> and <parent_wwpn> or <parent_fabric_wwn> in order to use those values as more consistent search parameters. For some providing the wwnn/wwpn pair will provide the most specific search option, while for others providing a fabric_wwn will at least ensure usage of the same SAN. This patch will add the new fields to the nodedev.rng for input purposes only since the input XML is essentially thrown away, no need to Format the values since they'd already be printed as part of the scsi_host data block. New API virNodeDeviceGetParentHostByWWNs will take the parent_wwnn and parent_wwpn in order to search the list of devices for matching capability data fields wwnn and wwpn. New API virNodeDeviceGetParentHostByFabricWWN will take the parent_fabric_wwn in order to search the list of devices for matching capability data field fabric_wwn. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/schemas/nodedev.rng | 15 +++++ src/conf/node_device_conf.c | 120 +++++++++++++++++++++++++++++++++++ src/conf/node_device_conf.h | 14 ++++ src/libvirt_private.syms | 2 + src/node_device/node_device_driver.c | 13 ++++ 5 files changed, 164 insertions(+) diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 93a88d8..6fe49a3 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -18,6 +18,21 @@ <optional> <element name="parent"><text/></element> </optional> + <optional> + <element name="parent_wwnn"> + <ref name='wwn'/> + </element> + </optional> + <optional> + <element name="parent_wwpn"> + <ref name='wwn'/> + </element> + </optional> + <optional> + <element name="parent_fabric_wwn"> + <ref name='wwn'/> + </element> + </optional> <optional> <element name="driver"> diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 3aa77cf..cecd915 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -92,6 +92,30 @@ int virNodeDeviceHasCap(const virNodeDeviceObj *dev, const char *cap) } +/* virNodeDeviceFindFCCapDef: + * @dev: Pointer to current device + * + * Search the device object 'caps' array for fc_host capability. + * + * Returns: + * Pointer to the caps or NULL if not found + */ +static virNodeDevCapsDefPtr +virNodeDeviceFindFCCapDef(const virNodeDeviceObj *dev) +{ + virNodeDevCapsDefPtr caps = dev->def->caps; + + while (caps) { + if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST && + (caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST)) + break; + + caps = caps->next; + } + return caps; +} + + /* virNodeDeviceFindVPORTCapDef: * @dev: Pointer to current device * @@ -152,6 +176,46 @@ virNodeDeviceObjPtr virNodeDeviceFindByName(virNodeDeviceObjListPtr devs, static virNodeDeviceObjPtr +virNodeDeviceFindByWWNs(virNodeDeviceObjListPtr devs, + const char *parent_wwnn, + const char *parent_wwpn) +{ + size_t i; + + for (i = 0; i < devs->count; i++) { + virNodeDevCapsDefPtr cap; + virNodeDeviceObjLock(devs->objs[i]); + if ((cap = virNodeDeviceFindFCCapDef(devs->objs[i])) && + STREQ_NULLABLE(cap->data.scsi_host.wwnn, parent_wwnn) && + STREQ_NULLABLE(cap->data.scsi_host.wwpn, parent_wwpn)) + return devs->objs[i]; + virNodeDeviceObjUnlock(devs->objs[i]); + } + + return NULL; +} + + +static virNodeDeviceObjPtr +virNodeDeviceFindByFabricWWN(virNodeDeviceObjListPtr devs, + const char *parent_fabric_wwn) +{ + size_t i; + + for (i = 0; i < devs->count; i++) { + virNodeDevCapsDefPtr cap; + virNodeDeviceObjLock(devs->objs[i]); + if ((cap = virNodeDeviceFindFCCapDef(devs->objs[i])) && + STREQ_NULLABLE(cap->data.scsi_host.fabric_wwn, parent_fabric_wwn)) + return devs->objs[i]; + virNodeDeviceObjUnlock(devs->objs[i]); + } + + return NULL; +} + + +static virNodeDeviceObjPtr virNodeDeviceFindByCap(virNodeDeviceObjListPtr devs, const char *cap) { @@ -177,6 +241,9 @@ void virNodeDeviceDefFree(virNodeDeviceDefPtr def) VIR_FREE(def->name); VIR_FREE(def->parent); + VIR_FREE(def->parent_wwnn); + VIR_FREE(def->parent_wwpn); + VIR_FREE(def->parent_fabric_wwn); VIR_FREE(def->driver); VIR_FREE(def->sysfs_path); VIR_FREE(def->parent_sysfs_path); @@ -1652,6 +1719,10 @@ virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt, /* Extract device parent, if any */ def->parent = virXPathString("string(./parent[1])", ctxt); + def->parent_wwnn = virXPathString("string(./parent_wwnn[1])", ctxt); + def->parent_wwpn = virXPathString("string(./parent_wwpn[1])", ctxt); + def->parent_fabric_wwn = virXPathString("string(./parent_fabric_wwn[1])", + ctxt); /* Parse device capabilities */ nodes = NULL; @@ -1847,6 +1918,55 @@ virNodeDeviceGetParentHost(virNodeDeviceObjListPtr devs, int +virNodeDeviceGetParentHostByWWNs(virNodeDeviceObjListPtr devs, + const char *dev_name, + const char *parent_wwnn, + const char *parent_wwpn, + int *parent_host) +{ + virNodeDeviceObjPtr parent = NULL; + int ret; + + if (!(parent = virNodeDeviceFindByWWNs(devs, parent_wwnn, parent_wwpn))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find parent device for '%s'"), + dev_name); + return -1; + } + + ret = virNodeDeviceFindFCParentHost(parent, parent_host); + + virNodeDeviceObjUnlock(parent); + + return ret; +} + + +int +virNodeDeviceGetParentHostByFabricWWN(virNodeDeviceObjListPtr devs, + const char *dev_name, + const char *parent_fabric_wwn, + int *parent_host) +{ + virNodeDeviceObjPtr parent = NULL; + int ret; + + if (!(parent = virNodeDeviceFindByFabricWWN(devs, parent_fabric_wwn))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find parent device for '%s'"), + dev_name); + return -1; + } + + ret = virNodeDeviceFindFCParentHost(parent, parent_host); + + virNodeDeviceObjUnlock(parent); + + return ret; +} + + +int virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, int *parent_host) { diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 2b2aed7..1634483 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -200,6 +200,9 @@ struct _virNodeDeviceDef { char *sysfs_path; /* udev name/sysfs path */ char *parent; /* optional parent device name */ char *parent_sysfs_path; /* udev parent name/sysfs path */ + char *parent_wwnn; /* optional parent wwnn */ + char *parent_wwpn; /* optional parent wwpn */ + char *parent_fabric_wwn; /* optional parent fabric_wwn */ char *driver; /* optional driver name */ virNodeDevCapsDefPtr caps; /* optional device capabilities */ }; @@ -273,6 +276,17 @@ int virNodeDeviceGetParentHost(virNodeDeviceObjListPtr devs, const char *parent_name, int *parent_host); +int virNodeDeviceGetParentHostByWWNs(virNodeDeviceObjListPtr devs, + const char *dev_name, + const char *parent_wwnn, + const char *parent_wwpn, + int *parent_host); + +int virNodeDeviceGetParentHostByFabricWWN(virNodeDeviceObjListPtr devs, + const char *dev_name, + const char *parent_fabric_wwn, + int *parent_host); + int virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, int *parent_host); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index de14a7e..5673bda 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -701,6 +701,8 @@ virNodeDeviceFindByName; virNodeDeviceFindBySysfsPath; virNodeDeviceFindVportParentHost; virNodeDeviceGetParentHost; +virNodeDeviceGetParentHostByFabricWWN; +virNodeDeviceGetParentHostByWWNs; virNodeDeviceGetWWNs; virNodeDeviceHasCap; virNodeDeviceObjListExport; diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index 0e091fe..b7bec65 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -590,6 +590,19 @@ nodeDeviceCreateXML(virConnectPtr conn, def->parent, &parent_host) < 0) goto cleanup; + } else if (def->parent_wwnn && def->parent_wwpn) { + if (virNodeDeviceGetParentHostByWWNs(&driver->devs, + def->name, + def->parent_wwnn, + def->parent_wwpn, + &parent_host) < 0) + goto cleanup; + } else if (def->parent_fabric_wwn) { + if (virNodeDeviceGetParentHostByFabricWWN(&driver->devs, + def->name, + def->parent_fabric_wwn, + &parent_host) < 0) + goto cleanup; } else { /* Try to find "a" vport capable scsi_host when no parent supplied */ if (virNodeDeviceFindVportParentHost(&driver->devs, &parent_host) < 0) -- 2.7.4

John, I have a concern regarding usage of the parent_fabric_name. As far I have been told there are a lot of fc_host attributes in Linux that are optional and left to the low-level driver to decide if implemented. fabric_name apparently happens to be one of these attributes and I know an architecture that has a driver (zfcp) that does not provide it. Up until this patch the fabric_name was collected on the host during detection of the fc_host capability and provided during a nodedev dumpxml. I would like to propose making the existence of fabric_name option in the detection of the fc_host capability. In doing so it would also be required make the fabric_name xml tag optional in the xml of the nodedev dump. I am aware that this is a creating a slightly incompatible output but how else could that be fixed than making the tag optional? I can send a patch for this if you agree to the solution. The implications regarding the search by parent_fabric_wwn should be obvious. On 11/18/2016 03:26 PM, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1349696
When creating a vHBA, the process is to feed XML to nodeDeviceCreateXML that lists the <parent> scsi_hostX to use to create the vHBA. However, between reboots, it's possible that the <parent> changes its scsi_hostX to scsi_hostY and saved XML to perform the creation will either fail or create a vHBA using the wrong parent.
So add the ability to provide <parent_wwnn> and <parent_wwpn> or <parent_fabric_wwn> in order to use those values as more consistent search parameters. For some providing the wwnn/wwpn pair will provide the most specific search option, while for others providing a fabric_wwn will at least ensure usage of the same SAN.
This patch will add the new fields to the nodedev.rng for input purposes only since the input XML is essentially thrown away, no need to Format the values since they'd already be printed as part of the scsi_host data block.
New API virNodeDeviceGetParentHostByWWNs will take the parent_wwnn and parent_wwpn in order to search the list of devices for matching capability data fields wwnn and wwpn.
New API virNodeDeviceGetParentHostByFabricWWN will take the parent_fabric_wwn in order to search the list of devices for matching capability data field fabric_wwn.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/schemas/nodedev.rng | 15 +++++ src/conf/node_device_conf.c | 120 +++++++++++++++++++++++++++++++++++ src/conf/node_device_conf.h | 14 ++++ src/libvirt_private.syms | 2 + src/node_device/node_device_driver.c | 13 ++++ 5 files changed, 164 insertions(+)
diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 93a88d8..6fe49a3 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -18,6 +18,21 @@ <optional> <element name="parent"><text/></element> </optional> + <optional> + <element name="parent_wwnn"> + <ref name='wwn'/> + </element> + </optional> + <optional> + <element name="parent_wwpn"> + <ref name='wwn'/> + </element> + </optional> + <optional> + <element name="parent_fabric_wwn"> + <ref name='wwn'/> + </element> + </optional>
<optional> <element name="driver"> diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 3aa77cf..cecd915 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -92,6 +92,30 @@ int virNodeDeviceHasCap(const virNodeDeviceObj *dev, const char *cap) }
+/* virNodeDeviceFindFCCapDef: + * @dev: Pointer to current device + * + * Search the device object 'caps' array for fc_host capability. + * + * Returns: + * Pointer to the caps or NULL if not found + */ +static virNodeDevCapsDefPtr +virNodeDeviceFindFCCapDef(const virNodeDeviceObj *dev) +{ + virNodeDevCapsDefPtr caps = dev->def->caps; + + while (caps) { + if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST && + (caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST)) + break; + + caps = caps->next; + } + return caps; +} + + /* virNodeDeviceFindVPORTCapDef: * @dev: Pointer to current device * @@ -152,6 +176,46 @@ virNodeDeviceObjPtr virNodeDeviceFindByName(virNodeDeviceObjListPtr devs,
static virNodeDeviceObjPtr +virNodeDeviceFindByWWNs(virNodeDeviceObjListPtr devs, + const char *parent_wwnn, + const char *parent_wwpn) +{ + size_t i; + + for (i = 0; i < devs->count; i++) { + virNodeDevCapsDefPtr cap; + virNodeDeviceObjLock(devs->objs[i]); + if ((cap = virNodeDeviceFindFCCapDef(devs->objs[i])) && + STREQ_NULLABLE(cap->data.scsi_host.wwnn, parent_wwnn) && + STREQ_NULLABLE(cap->data.scsi_host.wwpn, parent_wwpn)) + return devs->objs[i]; + virNodeDeviceObjUnlock(devs->objs[i]); + } + + return NULL; +} + + +static virNodeDeviceObjPtr +virNodeDeviceFindByFabricWWN(virNodeDeviceObjListPtr devs, + const char *parent_fabric_wwn) +{ + size_t i; + + for (i = 0; i < devs->count; i++) { + virNodeDevCapsDefPtr cap; + virNodeDeviceObjLock(devs->objs[i]); + if ((cap = virNodeDeviceFindFCCapDef(devs->objs[i])) && + STREQ_NULLABLE(cap->data.scsi_host.fabric_wwn, parent_fabric_wwn)) + return devs->objs[i]; + virNodeDeviceObjUnlock(devs->objs[i]); + } + + return NULL; +} + + +static virNodeDeviceObjPtr virNodeDeviceFindByCap(virNodeDeviceObjListPtr devs, const char *cap) { @@ -177,6 +241,9 @@ void virNodeDeviceDefFree(virNodeDeviceDefPtr def)
VIR_FREE(def->name); VIR_FREE(def->parent); + VIR_FREE(def->parent_wwnn); + VIR_FREE(def->parent_wwpn); + VIR_FREE(def->parent_fabric_wwn); VIR_FREE(def->driver); VIR_FREE(def->sysfs_path); VIR_FREE(def->parent_sysfs_path); @@ -1652,6 +1719,10 @@ virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt,
/* Extract device parent, if any */ def->parent = virXPathString("string(./parent[1])", ctxt); + def->parent_wwnn = virXPathString("string(./parent_wwnn[1])", ctxt); + def->parent_wwpn = virXPathString("string(./parent_wwpn[1])", ctxt); + def->parent_fabric_wwn = virXPathString("string(./parent_fabric_wwn[1])", + ctxt);
/* Parse device capabilities */ nodes = NULL; @@ -1847,6 +1918,55 @@ virNodeDeviceGetParentHost(virNodeDeviceObjListPtr devs,
int +virNodeDeviceGetParentHostByWWNs(virNodeDeviceObjListPtr devs, + const char *dev_name, + const char *parent_wwnn, + const char *parent_wwpn, + int *parent_host) +{ + virNodeDeviceObjPtr parent = NULL; + int ret; + + if (!(parent = virNodeDeviceFindByWWNs(devs, parent_wwnn, parent_wwpn))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find parent device for '%s'"), + dev_name); + return -1; + } + + ret = virNodeDeviceFindFCParentHost(parent, parent_host); + + virNodeDeviceObjUnlock(parent); + + return ret; +} + + +int +virNodeDeviceGetParentHostByFabricWWN(virNodeDeviceObjListPtr devs, + const char *dev_name, + const char *parent_fabric_wwn, + int *parent_host) +{ + virNodeDeviceObjPtr parent = NULL; + int ret; + + if (!(parent = virNodeDeviceFindByFabricWWN(devs, parent_fabric_wwn))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find parent device for '%s'"), + dev_name); + return -1; + } + + ret = virNodeDeviceFindFCParentHost(parent, parent_host); + + virNodeDeviceObjUnlock(parent); + + return ret; +} + + +int virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, int *parent_host) { diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 2b2aed7..1634483 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -200,6 +200,9 @@ struct _virNodeDeviceDef { char *sysfs_path; /* udev name/sysfs path */ char *parent; /* optional parent device name */ char *parent_sysfs_path; /* udev parent name/sysfs path */ + char *parent_wwnn; /* optional parent wwnn */ + char *parent_wwpn; /* optional parent wwpn */ + char *parent_fabric_wwn; /* optional parent fabric_wwn */ char *driver; /* optional driver name */ virNodeDevCapsDefPtr caps; /* optional device capabilities */ }; @@ -273,6 +276,17 @@ int virNodeDeviceGetParentHost(virNodeDeviceObjListPtr devs, const char *parent_name, int *parent_host);
+int virNodeDeviceGetParentHostByWWNs(virNodeDeviceObjListPtr devs, + const char *dev_name, + const char *parent_wwnn, + const char *parent_wwpn, + int *parent_host); + +int virNodeDeviceGetParentHostByFabricWWN(virNodeDeviceObjListPtr devs, + const char *dev_name, + const char *parent_fabric_wwn, + int *parent_host); + int virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs, int *parent_host);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index de14a7e..5673bda 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -701,6 +701,8 @@ virNodeDeviceFindByName; virNodeDeviceFindBySysfsPath; virNodeDeviceFindVportParentHost; virNodeDeviceGetParentHost; +virNodeDeviceGetParentHostByFabricWWN; +virNodeDeviceGetParentHostByWWNs; virNodeDeviceGetWWNs; virNodeDeviceHasCap; virNodeDeviceObjListExport; diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index 0e091fe..b7bec65 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -590,6 +590,19 @@ nodeDeviceCreateXML(virConnectPtr conn, def->parent, &parent_host) < 0) goto cleanup; + } else if (def->parent_wwnn && def->parent_wwpn) { + if (virNodeDeviceGetParentHostByWWNs(&driver->devs, + def->name, + def->parent_wwnn, + def->parent_wwpn, + &parent_host) < 0) + goto cleanup; + } else if (def->parent_fabric_wwn) { + if (virNodeDeviceGetParentHostByFabricWWN(&driver->devs, + def->name, + def->parent_fabric_wwn, + &parent_host) < 0) + goto cleanup; } else { /* Try to find "a" vport capable scsi_host when no parent supplied */ if (virNodeDeviceFindVportParentHost(&driver->devs, &parent_host) < 0)
-- Mit freundlichen Grüßen/Kind regards Boris Fiuczynski IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Martina Köderitz Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

On 12/13/2016 10:58 AM, Boris Fiuczynski wrote:
John, I have a concern regarding usage of the parent_fabric_name. As far I have been told there are a lot of fc_host attributes in Linux that are optional and left to the low-level driver to decide if implemented. fabric_name apparently happens to be one of these attributes and I know an architecture that has a driver (zfcp) that does not provide it.
Up until this patch the fabric_name was collected on the host during detection of the fc_host capability and provided during a nodedev dumpxml.
How does this patch change that? All this patch does is allow one to provide a "parent_wwnn/parent_wwpn" or "parent_fabric_name" for the input XML to be passed to nodedev-create. So rather than just: <device> <parent>scsi_host3</parent> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device> One could provide: <device> <name>vhba</name> <parent_wwnn>20000000c9848140</parent_wwnn> <parent_wwpn>10000000c9848140</parent_wwpn> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device> or <device> <name>vhba</name> <parent_fabric_wwn>2002000573de9a81</parent_fabric_wwn> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device> for the 'virsh nodedev-create $FILE' command.
I would like to propose making the existence of fabric_name option in the detection of the fc_host capability. In doing so it would also be required make the fabric_name xml tag optional in the xml of the nodedev dump. I am aware that this is a creating a slightly incompatible output but how else could that be fixed than making the tag optional? I can send a patch for this if you agree to the solution.
The biggest hurdle I face with NPIV/vHBA is that there are many different options. I keep learning as I go on this stuff. It does seem from what you're indicating that the fabric_name file just doesn't exist. Although without having such a system available to me I have no way of knowing for sure. In any case, if "fabric_name" is not read, then 'scsi_host.fabric_wwn' is left blank (see nodeDeviceSysfsGetSCSIHostCaps). There isn't an error path that would cause a failure (just a debug message). If it is NULL, then since the Format code uses virBufferEscapeString which shouldn't format the XML for it. So given that, I suppose it's reasonable to provide an "<optional>" tag for the RNG file (although similarly wwnn/wwpn are also then optional). That seems reasonable (but unrelated to this code). Caveat - there is a udev bug/issue (https://bugzilla.redhat.com/show_bug.cgi?id=1319544) where the scsi_host for the vHBA is created before the files we look at have valid data. It's one of those where the QE is doing repeated create/destroy and udev hasn't really finished updating things before it sends the create event.
The implications regarding the search by parent_fabric_wwn should be obvious.
For the purpose of this patch set though, parent_fabric_wwn is a 'read only' type value. Unless I messed up in the code, if it's not available to the HBA/scsi_host on the host, then regardless of what's provided in parent_fabric_wwn the parent won't be found by fabric_wwn/fabric_name. I suppose I could add checks in virNodeDeviceDefParseXML that the provided string "looks like" a WWN (eg if virValidateWWN call) as a crude form of XML validation. John [...]

On 12/13/2016 08:30 PM, John Ferlan wrote:
On 12/13/2016 10:58 AM, Boris Fiuczynski wrote:
John, I have a concern regarding usage of the parent_fabric_name. As far I have been told there are a lot of fc_host attributes in Linux that are optional and left to the low-level driver to decide if implemented. fabric_name apparently happens to be one of these attributes and I know an architecture that has a driver (zfcp) that does not provide it.
Up until this patch the fabric_name was collected on the host during detection of the fc_host capability and provided during a nodedev dumpxml.
How does this patch change that? All this patch does is allow one to provide a "parent_wwnn/parent_wwpn" or "parent_fabric_name" for the input XML to be passed to nodedev-create. So rather than just:
<device> <parent>scsi_host3</parent> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device>
One could provide:
<device> <name>vhba</name> <parent_wwnn>20000000c9848140</parent_wwnn> <parent_wwpn>10000000c9848140</parent_wwpn> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device>
or
<device> <name>vhba</name> <parent_fabric_wwn>2002000573de9a81</parent_fabric_wwn> <capability type='scsi_host'> <capability type='fc_host'> </capability> </capability> </device>
for the 'virsh nodedev-create $FILE' command.
I would like to propose making the existence of fabric_name option in the detection of the fc_host capability. In doing so it would also be required make the fabric_name xml tag optional in the xml of the nodedev dump. I am aware that this is a creating a slightly incompatible output but how else could that be fixed than making the tag optional? I can send a patch for this if you agree to the solution.
The biggest hurdle I face with NPIV/vHBA is that there are many different options. I keep learning as I go on this stuff. It does seem from what you're indicating that the fabric_name file just doesn't exist. Although without having such a system available to me I have no way of knowing for sure.
In any case, if "fabric_name" is not read, then 'scsi_host.fabric_wwn' is left blank (see nodeDeviceSysfsGetSCSIHostCaps). There isn't an error path that would cause a failure (just a debug message). If it is NULL, then since the Format code uses virBufferEscapeString which shouldn't format the XML for it. So given that, I suppose it's reasonable to provide an "<optional>" tag for the RNG file (although similarly wwnn/wwpn are also then optional). That seems reasonable (but unrelated to this code).
In virReadFCHost the method virFileReadAll is used to read the file fabric_name. This method triggers virReportSystemError(errno, _("Failed to open file '%s'"), path); if the file cannot be opened. As a result virReadFCHost returns NULL into nodeDeviceSysfsGetSCSIHostCaps which causes cleanup to be called resulting in the removal of the fc_host capability. I will post a patch for fixing this issue.
Caveat - there is a udev bug/issue (https://bugzilla.redhat.com/show_bug.cgi?id=1319544) where the scsi_host for the vHBA is created before the files we look at have valid data. It's one of those where the QE is doing repeated create/destroy and udev hasn't really finished updating things before it sends the create event.
The implications regarding the search by parent_fabric_wwn should be obvious.
For the purpose of this patch set though, parent_fabric_wwn is a 'read only' type value. Unless I messed up in the code, if it's not available to the HBA/scsi_host on the host, then regardless of what's provided in parent_fabric_wwn the parent won't be found by fabric_wwn/fabric_name. I suppose I could add checks in virNodeDeviceDefParseXML that the provided string "looks like" a WWN (eg if virValidateWWN call) as a crude form of XML validation.
My point is that your patch starts using the optional fabric_name as (optional) input parameter in the API. It seems that the wwnn/wwpn pair provides the most specific search option anyway and always works where as the fabric_wwn might work only for some. Anyway, you are right, that it can be done if it is all optional and the fix for the fc_host capability is mostly independent of this series especially since the fabric_wwn tag is only written out and never read in from xml anyway!
John
[...]
-- Mit freundlichen Grüßen/Kind regards Boris Fiuczynski IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Martina Köderitz Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

On Fri, Nov 18, 2016 at 09:26:30AM -0500, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1349696
When creating a vHBA, the process is to feed XML to nodeDeviceCreateXML that lists the <parent> scsi_hostX to use to create the vHBA. However, between reboots, it's possible that the <parent> changes its scsi_hostX to scsi_hostY and saved XML to perform the creation will either fail or create a vHBA using the wrong parent.
So add the ability to provide <parent_wwnn> and <parent_wwpn> or <parent_fabric_wwn> in order to use those values as more consistent
These are all flattening the XML and duplicating the "parent" in their name. Can we no longer put subelements in <parent> after we've used its contents as text?
search parameters. For some providing the wwnn/wwpn pair will provide the most specific search option, while for others providing a fabric_wwn will at least ensure usage of the same SAN.
This patch will add the new fields to the nodedev.rng for input purposes only since the input XML is essentially thrown away, no need to Format the values since they'd already be printed as part of the scsi_host data block.
New API virNodeDeviceGetParentHostByWWNs will take the parent_wwnn and parent_wwpn in order to search the list of devices for matching capability data fields wwnn and wwpn.
New API virNodeDeviceGetParentHostByFabricWWN will take the parent_fabric_wwn in order to search the list of devices for matching capability data field fabric_wwn.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/schemas/nodedev.rng | 15 +++++ src/conf/node_device_conf.c | 120 +++++++++++++++++++++++++++++++++++ src/conf/node_device_conf.h | 14 ++++ src/libvirt_private.syms | 2 + src/node_device/node_device_driver.c | 13 ++++ 5 files changed, 164 insertions(+)
diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 93a88d8..6fe49a3 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -18,6 +18,21 @@ <optional> <element name="parent"><text/></element> </optional>
+ <optional> + <element name="parent_wwnn"> + <ref name='wwn'/> + </element> + </optional> + <optional> + <element name="parent_wwpn"> + <ref name='wwn'/> + </element> + </optional>
This allows either of {nn,pn} to be present, but the code expects both. Please put them in a single <optional> block if omitting either does not make sense.
+ <optional> + <element name="parent_fabric_wwn"> + <ref name='wwn'/> + </element> + </optional>
<optional> <element name="driver">
@@ -1652,6 +1719,10 @@ virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt,
/* Extract device parent, if any */ def->parent = virXPathString("string(./parent[1])", ctxt); + def->parent_wwnn = virXPathString("string(./parent_wwnn[1])", ctxt); + def->parent_wwpn = virXPathString("string(./parent_wwpn[1])", ctxt);
Should we error out if only one was provided?
+ def->parent_fabric_wwn = virXPathString("string(./parent_fabric_wwn[1])", + ctxt);
/* Parse device capabilities */ nodes = NULL;
Weak ACK, I'd rather see the XML nested (if possible). Jan

On 01/02/2017 09:25 AM, Ján Tomko wrote:
On Fri, Nov 18, 2016 at 09:26:30AM -0500, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1349696
When creating a vHBA, the process is to feed XML to nodeDeviceCreateXML that lists the <parent> scsi_hostX to use to create the vHBA. However, between reboots, it's possible that the <parent> changes its scsi_hostX to scsi_hostY and saved XML to perform the creation will either fail or create a vHBA using the wrong parent.
So add the ability to provide <parent_wwnn> and <parent_wwpn> or <parent_fabric_wwn> in order to use those values as more consistent
These are all flattening the XML and duplicating the "parent" in their name.
Can we no longer put subelements in <parent> after we've used its contents as text?
I took the easy way out, but it seems the desire is one of the following: <parent>scsi_hostN</parent> <parent wwnn='' wwpn=''/> <parent fabric_name=''/> This appears to be "doable" (again apologies in advance for the cut-n-paste and email reader issues... I can repost the entire series, but figured I'd at least take a stab first to make sure we're on the same page): diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index f76204e..4666c9c 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -16,20 +16,7 @@ <element name="path"><text/></element> </optional> <optional> - <element name="parent"><text/></element> - </optional> - <optional> - <element name="parent_wwnn"> - <ref name='wwn'/> - </element> - <element name="parent_wwpn"> - <ref name='wwn'/> - </element> - </optional> - <optional> - <element name="parent_fabric_wwn"> - <ref name='wwn'/> - </element> + <ref name="parent"/> </optional> <optional> @@ -44,6 +31,28 @@ </element> </define> + <element name='parent'> + <optional> + <attribute name='wwnn'> + <ref name='wwn'/> + </attribute> + <attribute name='wwpn'> + <ref name='wwn'/> + </attribute> + </optional> + <optional> + <attribute name='fabric_wwn'> + <ref name='wwn'/> + </attribute> + </optional> + <choice> + <text/> + <empty/> + </choice> + </element> + </define> + <define name='capability'> <element name="capability"> <choice> diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index ad7b699..4d3268f 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -1724,15 +1724,15 @@ virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt, /* Extract device parent, if any */ def->parent = virXPathString("string(./parent[1])", ctxt); - def->parent_wwnn = virXPathString("string(./parent_wwnn[1])", ctxt); - def->parent_wwpn = virXPathString("string(./parent_wwpn[1])", ctxt); + def->parent_wwnn = virXPathString("string(./parent[1]/@wwnn)", ctxt); + def->parent_wwpn = virXPathString("string(./parent[1]/@wwpn)", ctxt); if ((def->parent_wwnn && !def->parent_wwpn) || (!def->parent_wwnn && def->parent_wwpn)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("must supply both wwnn and wwpn for parent")); goto error; } - def->parent_fabric_wwn = virXPathString("string(./parent_fabric_wwn[1])", + def->parent_fabric_wwn = virXPathString("string(./parent[1]/@fabric_wwn)", ctxt); /* Parse device capabilities */
search parameters. For some providing the wwnn/wwpn pair will provide the most specific search option, while for others providing a fabric_wwn will at least ensure usage of the same SAN.
This patch will add the new fields to the nodedev.rng for input purposes only since the input XML is essentially thrown away, no need to Format the values since they'd already be printed as part of the scsi_host data block.
New API virNodeDeviceGetParentHostByWWNs will take the parent_wwnn and parent_wwpn in order to search the list of devices for matching capability data fields wwnn and wwpn.
New API virNodeDeviceGetParentHostByFabricWWN will take the parent_fabric_wwn in order to search the list of devices for matching capability data field fabric_wwn.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/schemas/nodedev.rng | 15 +++++ src/conf/node_device_conf.c | 120 +++++++++++++++++++++++++++++++++++ src/conf/node_device_conf.h | 14 ++++ src/libvirt_private.syms | 2 + src/node_device/node_device_driver.c | 13 ++++ 5 files changed, 164 insertions(+)
diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 93a88d8..6fe49a3 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -18,6 +18,21 @@ <optional> <element name="parent"><text/></element> </optional>
+ <optional> + <element name="parent_wwnn"> + <ref name='wwn'/> + </element> + </optional> + <optional> + <element name="parent_wwpn"> + <ref name='wwn'/> + </element> + </optional>
This allows either of {nn,pn} to be present, but the code expects both. Please put them in a single <optional> block if omitting either does not make sense.
OK
+ <optional> + <element name="parent_fabric_wwn"> + <ref name='wwn'/> + </element> + </optional>
<optional> <element name="driver">
@@ -1652,6 +1719,10 @@ virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt,
/* Extract device parent, if any */ def->parent = virXPathString("string(./parent[1])", ctxt); + def->parent_wwnn = virXPathString("string(./parent_wwnn[1])", ctxt); + def->parent_wwpn = virXPathString("string(./parent_wwpn[1])", ctxt);
Should we error out if only one was provided?
Sure - I can add that - I did it in a separate local commit before making the above change... John
+ def->parent_fabric_wwn = virXPathString("string(./parent_fabric_wwn[1])", + ctxt);
/* Parse device capabilities */ nodes = NULL;
Weak ACK, I'd rather see the XML nested (if possible).
Jan

Add new fields to the fchost structure to allow creation of a vHBA via the storage pool when a parent_wwnn/parent_wwpn or parent_fabric_wwn is supplied in the storage pool XML. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/schemas/basictypes.rng | 15 +++++++++++++++ src/conf/storage_conf.c | 21 +++++++++++++++++++-- src/conf/storage_conf.h | 3 +++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng index 1b4f980..cc560e6 100644 --- a/docs/schemas/basictypes.rng +++ b/docs/schemas/basictypes.rng @@ -427,6 +427,21 @@ <ref name="virYesNo"/> </attribute> </optional> + <optional> + <attribute name='parent_wwnn'> + <ref name='wwn'/> + </attribute> + </optional> + <optional> + <attribute name='parent_wwpn'> + <ref name='wwn'/> + </attribute> + </optional> + <optional> + <attribute name='parent_fabric_wwn'> + <ref name='wwn'/> + </attribute> + </optional> <attribute name='wwnn'> <ref name='wwn'/> </attribute> diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 7e7bb72..25fb983 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -335,6 +335,9 @@ virStoragePoolSourceAdapterClear(virStoragePoolSourceAdapterPtr adapter) VIR_FREE(adapter->data.fchost.wwnn); VIR_FREE(adapter->data.fchost.wwpn); VIR_FREE(adapter->data.fchost.parent); + VIR_FREE(adapter->data.fchost.parent_wwnn); + VIR_FREE(adapter->data.fchost.parent_wwpn); + VIR_FREE(adapter->data.fchost.parent_fabric_wwn); } else if (adapter->type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { VIR_FREE(adapter->data.scsi_host.name); @@ -591,10 +594,17 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt, } } - source->adapter.data.fchost.wwnn = - virXPathString("string(./adapter/@wwnn)", ctxt); + source->adapter.data.fchost.parent_wwnn = + virXPathString("string(./adapter/@parent_wwnn)", ctxt); + source->adapter.data.fchost.parent_wwpn = + virXPathString("string(./adapter/@parent_wwpn)", ctxt); + source->adapter.data.fchost.parent_fabric_wwn = + virXPathString("string(./adapter/@parent_fabric_wwn)", ctxt); + source->adapter.data.fchost.wwpn = virXPathString("string(./adapter/@wwpn)", ctxt); + source->adapter.data.fchost.wwnn = + virXPathString("string(./adapter/@wwnn)", ctxt); } else if (source->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { @@ -1100,6 +1110,13 @@ virStoragePoolSourceFormat(virBufferPtr buf, if (src->adapter.data.fchost.managed) virBufferAsprintf(buf, " managed='%s'", virTristateBoolTypeToString(src->adapter.data.fchost.managed)); + virBufferEscapeString(buf, " parent_wwnn='%s'", + src->adapter.data.fchost.parent_wwnn); + virBufferEscapeString(buf, " parent_wwpn='%s'", + src->adapter.data.fchost.parent_wwpn); + virBufferEscapeString(buf, " parent_fabric_wwn='%s'", + src->adapter.data.fchost.parent_fabric_wwn); + virBufferAsprintf(buf, " wwnn='%s' wwpn='%s'/>\n", src->adapter.data.fchost.wwnn, src->adapter.data.fchost.wwpn); diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index 185ae5e..b35471d 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -193,6 +193,9 @@ struct _virStoragePoolSourceAdapter { } scsi_host; struct { char *parent; + char *parent_wwnn; + char *parent_wwpn; + char *parent_fabric_wwn; char *wwnn; char *wwpn; int managed; /* enum virTristateSwitch */ -- 2.7.4

Use the ret = -1, goto cleanup, etc. rather than current hodgepodge. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 99504f4..cf93fdc 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -697,6 +697,7 @@ createVport(virConnectPtr conn, char *parent_hoststr = NULL; virStoragePoolFCRefreshInfoPtr cbdata = NULL; virThread thread; + int ret = -1; if (adapter->type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) return 0; @@ -725,17 +726,14 @@ createVport(virConnectPtr conn, */ if ((name = virGetFCHostNameByWWN(NULL, adapter->data.fchost.wwnn, adapter->data.fchost.wwpn))) { - int retval = 0; - /* If a parent was provided, let's make sure the 'name' we've * retrieved has the same parent */ if (adapter->data.fchost.parent && - !checkVhbaSCSIHostParent(conn, name, adapter->data.fchost.parent)) - retval = -1; + checkVhbaSCSIHostParent(conn, name, adapter->data.fchost.parent)) + ret = 0; - VIR_FREE(name); - return retval; + goto cleanup; } if (!adapter->data.fchost.parent) { @@ -743,13 +741,11 @@ createVport(virConnectPtr conn, virReportError(VIR_ERR_XML_ERROR, "%s", _("'parent' for vHBA not specified, and " "cannot find one on this host")); - return -1; + goto cleanup; } - if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) { - VIR_FREE(parent_hoststr); - return -1; - } + if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) + goto cleanup; /* NOTE: * We do not save the parent_hoststr in adapter->data.fchost.parent @@ -759,7 +755,6 @@ createVport(virConnectPtr conn, * parent. Besides we have a way to determine the parent based on * the 'name' field. */ - VIR_FREE(parent_hoststr); } /* Since we're creating the vHBA, then we need to manage removing it @@ -771,13 +766,13 @@ createVport(virConnectPtr conn, adapter->data.fchost.managed = VIR_TRISTATE_BOOL_YES; if (configFile) { if (virStoragePoolSaveConfig(configFile, pool->def) < 0) - return -1; + goto cleanup; } } if (virManageVport(parent_host, adapter->data.fchost.wwpn, adapter->data.fchost.wwnn, VPORT_CREATE) < 0) - return -1; + goto cleanup; virFileWaitForDevices(); @@ -790,8 +785,7 @@ createVport(virConnectPtr conn, adapter->data.fchost.wwpn))) { if (VIR_ALLOC(cbdata) == 0) { memcpy(cbdata->pool_uuid, pool->def->uuid, VIR_UUID_BUFLEN); - cbdata->fchost_name = name; - name = NULL; + VIR_STEAL_PTR(cbdata->fchost_name, name); if (virThreadCreate(&thread, false, virStoragePoolFCRefreshThread, cbdata) < 0) { @@ -800,10 +794,14 @@ createVport(virConnectPtr conn, virStoragePoolFCRefreshDataFree(cbdata); } } - VIR_FREE(name); } - return 0; + ret = 0; + + cleanup: + VIR_FREE(name); + VIR_FREE(parent_hoststr); + return ret; } static int -- 2.7.4

s/iscsi/scsi/ in the commit summary On Fri, Nov 18, 2016 at 09:26:32AM -0500, John Ferlan wrote:
Use the ret = -1, goto cleanup, etc. rather than current hodgepodge.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-)
ACK Jan

Move the check for an already existing vHBA to the top of the function. No sense in first decoding a provided parent if the next thing we're going to do is fail if a provided wwnn/wwpn already exists. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index cf93fdc..9863880 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -706,20 +706,6 @@ createVport(virConnectPtr conn, conn, NULLSTR(configFile), NULLSTR(adapter->data.fchost.parent), adapter->data.fchost.wwnn, adapter->data.fchost.wwpn); - /* If a parent was provided, then let's make sure it's vhost capable */ - if (adapter->data.fchost.parent) { - if (virGetSCSIHostNumber(adapter->data.fchost.parent, &parent_host) < 0) - return -1; - - if (!virIsCapableFCHost(NULL, parent_host)) { - virReportError(VIR_ERR_XML_ERROR, - _("parent '%s' specified for vHBA " - "is not vport capable"), - adapter->data.fchost.parent); - return -1; - } - } - /* If we find an existing HBA/vHBA within the fc_host sysfs * using the wwnn/wwpn, then a nodedev is already created for * this pool and we don't have to create the vHBA @@ -736,6 +722,20 @@ createVport(virConnectPtr conn, goto cleanup; } + /* If a parent was provided, then let's make sure it's vhost capable */ + if (adapter->data.fchost.parent) { + if (virGetSCSIHostNumber(adapter->data.fchost.parent, &parent_host) < 0) + return -1; + + if (!virIsCapableFCHost(NULL, parent_host)) { + virReportError(VIR_ERR_XML_ERROR, + _("parent '%s' specified for vHBA " + "is not vport capable"), + adapter->data.fchost.parent); + return -1; + } + } + if (!adapter->data.fchost.parent) { if (!(parent_hoststr = virFindFCHostCapableVport(NULL))) { virReportError(VIR_ERR_XML_ERROR, "%s", -- 2.7.4

On 11/18/2016 09:26 AM, John Ferlan wrote:
Move the check for an already existing vHBA to the top of the function. No sense in first decoding a provided parent if the next thing we're going to do is fail if a provided wwnn/wwpn already exists.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index cf93fdc..9863880 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -706,20 +706,6 @@ createVport(virConnectPtr conn, conn, NULLSTR(configFile), NULLSTR(adapter->data.fchost.parent), adapter->data.fchost.wwnn, adapter->data.fchost.wwpn);
- /* If a parent was provided, then let's make sure it's vhost capable */ - if (adapter->data.fchost.parent) { - if (virGetSCSIHostNumber(adapter->data.fchost.parent, &parent_host) < 0) - return -1; - - if (!virIsCapableFCHost(NULL, parent_host)) { - virReportError(VIR_ERR_XML_ERROR, - _("parent '%s' specified for vHBA " - "is not vport capable"), - adapter->data.fchost.parent); - return -1; - } - } - /* If we find an existing HBA/vHBA within the fc_host sysfs * using the wwnn/wwpn, then a nodedev is already created for * this pool and we don't have to create the vHBA @@ -736,6 +722,20 @@ createVport(virConnectPtr conn, goto cleanup; }
+ /* If a parent was provided, then let's make sure it's vhost capable */ + if (adapter->data.fchost.parent) { + if (virGetSCSIHostNumber(adapter->data.fchost.parent, &parent_host) < 0) + return -1; + + if (!virIsCapableFCHost(NULL, parent_host)) { + virReportError(VIR_ERR_XML_ERROR, + _("parent '%s' specified for vHBA " + "is not vport capable"), + adapter->data.fchost.parent); + return -1; + } + } + if (!adapter->data.fchost.parent) {
This could be an else now. - Eric
if (!(parent_hoststr = virFindFCHostCapableVport(NULL))) { virReportError(VIR_ERR_XML_ERROR, "%s",

On Fri, Nov 18, 2016 at 09:26:33AM -0500, John Ferlan wrote:
Move the check for an already existing vHBA to the top of the function. No sense in first decoding a provided parent if the next thing we're going to do is fail if a provided wwnn/wwpn already exists.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
@@ -736,6 +722,20 @@ createVport(virConnectPtr conn, goto cleanup; }
+ /* If a parent was provided, then let's make sure it's vhost capable */ + if (adapter->data.fchost.parent) { + if (virGetSCSIHostNumber(adapter->data.fchost.parent, &parent_host) < 0) + return -1; + + if (!virIsCapableFCHost(NULL, parent_host)) { + virReportError(VIR_ERR_XML_ERROR, + _("parent '%s' specified for vHBA " + "is not vport capable"), + adapter->data.fchost.parent); + return -1;
These returns leak name, but the following patch fixes that. ACK Jan

Remove duplicated code - make one path through Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 9863880..df48b1a 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -722,39 +722,26 @@ createVport(virConnectPtr conn, goto cleanup; } - /* If a parent was provided, then let's make sure it's vhost capable */ if (adapter->data.fchost.parent) { - if (virGetSCSIHostNumber(adapter->data.fchost.parent, &parent_host) < 0) - return -1; - - if (!virIsCapableFCHost(NULL, parent_host)) { - virReportError(VIR_ERR_XML_ERROR, - _("parent '%s' specified for vHBA " - "is not vport capable"), - adapter->data.fchost.parent); - return -1; - } - } - - if (!adapter->data.fchost.parent) { + if (VIR_STRDUP(parent_hoststr, adapter->data.fchost.parent) < 0) + goto cleanup; + } else { if (!(parent_hoststr = virFindFCHostCapableVport(NULL))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'parent' for vHBA not specified, and " "cannot find one on this host")); goto cleanup; } + } - if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) - goto cleanup; + if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) + goto cleanup; - /* NOTE: - * We do not save the parent_hoststr in adapter->data.fchost.parent - * since we could be writing out the 'def' to the saved XML config. - * If we wrote out the name in the XML, then future starts would - * always use the same parent rather than finding the "best available" - * parent. Besides we have a way to determine the parent based on - * the 'name' field. - */ + if (adapter->data.fchost.parent && !virIsCapableFCHost(NULL, parent_host)) { + virReportError(VIR_ERR_XML_ERROR, + _("parent '%s' specified for vHBA is not vport capable"), + parent_hoststr); + goto cleanup; } /* Since we're creating the vHBA, then we need to manage removing it -- 2.7.4

On Fri, Nov 18, 2016 at 09:26:34AM -0500, John Ferlan wrote:
Remove duplicated code - make one path through
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-)
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 9863880..df48b1a 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -722,39 +722,26 @@ createVport(virConnectPtr conn,
- if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) - goto cleanup; + if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) + goto cleanup;
- /* NOTE: - * We do not save the parent_hoststr in adapter->data.fchost.parent - * since we could be writing out the 'def' to the saved XML config. - * If we wrote out the name in the XML, then future starts would - * always use the same parent rather than finding the "best available" - * parent. Besides we have a way to determine the parent based on - * the 'name' field. - */
Don't we need to preserve this note for future generations?
+ if (adapter->data.fchost.parent && !virIsCapableFCHost(NULL, parent_host)) { + virReportError(VIR_ERR_XML_ERROR, + _("parent '%s' specified for vHBA is not vport capable"), + parent_hoststr); + goto cleanup; }
ACK Jan

On 01/02/2017 09:30 AM, Ján Tomko wrote:
On Fri, Nov 18, 2016 at 09:26:34AM -0500, John Ferlan wrote:
Remove duplicated code - make one path through
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-)
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index 9863880..df48b1a 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -722,39 +722,26 @@ createVport(virConnectPtr conn,
- if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) - goto cleanup; + if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) + goto cleanup;
- /* NOTE: - * We do not save the parent_hoststr in adapter->data.fchost.parent - * since we could be writing out the 'def' to the saved XML config. - * If we wrote out the name in the XML, then future starts would - * always use the same parent rather than finding the "best available" - * parent. Besides we have a way to determine the parent based on - * the 'name' field. - */
Don't we need to preserve this note for future generations?
Sure... Cannot recall what caused me to remove it - over zealous perhaps John
+ if (adapter->data.fchost.parent && !virIsCapableFCHost(NULL, parent_host)) { + virReportError(VIR_ERR_XML_ERROR, + _("parent '%s' specified for vHBA is not vport capable"), + parent_hoststr); + goto cleanup; }
ACK
Jan

Rather than extraneous VIR_FREE's depending on where we are in the code, move them to the top of the loop and in the cleanup path. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/util/virutil.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/util/virutil.c b/src/util/virutil.c index 844c947..a135819 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -2205,43 +2205,34 @@ virGetFCHostNameByWWN(const char *sysfs_prefix, } while (0) while (virDirRead(dir, &entry, prefix) > 0) { + VIR_FREE(wwnn_buf); + VIR_FREE(wwnn_path); + VIR_FREE(wwpn_buf); + VIR_FREE(wwpn_path); + if (virAsprintf(&wwnn_path, "%s/%s/node_name", prefix, entry->d_name) < 0) goto cleanup; - if (!virFileExists(wwnn_path)) { - VIR_FREE(wwnn_path); + if (!virFileExists(wwnn_path)) continue; - } READ_WWN(wwnn_path, wwnn_buf); - if (STRNEQ(wwnn, p)) { - VIR_FREE(wwnn_buf); - VIR_FREE(wwnn_path); + if (STRNEQ(wwnn, p)) continue; - } if (virAsprintf(&wwpn_path, "%s/%s/port_name", prefix, entry->d_name) < 0) goto cleanup; - if (!virFileExists(wwpn_path)) { - VIR_FREE(wwnn_buf); - VIR_FREE(wwnn_path); - VIR_FREE(wwpn_path); + if (!virFileExists(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); + if (STRNEQ(wwpn, p)) continue; - } ignore_value(VIR_STRDUP(ret, entry->d_name)); break; -- 2.7.4

On Fri, Nov 18, 2016 at 09:26:35AM -0500, John Ferlan wrote:
Rather than extraneous VIR_FREE's depending on where we are in the code, move them to the top of the loop and in the cleanup path.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/util/virutil.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-)
ACK Jan

Create a utility routine in order to read the scsi_host fabric_name files looking for a match to a passed fabric_name Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virutil.c | 86 ++++++++++++++++++++++++++++++++++++++++-------- src/util/virutil.h | 4 +++ 3 files changed, 78 insertions(+), 13 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 5673bda..3921897 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2620,6 +2620,7 @@ virGetDeviceID; virGetDeviceUnprivSGIO; virGetEnvAllowSUID; virGetEnvBlockSUID; +virGetFCHostNameByFabricWWN; virGetFCHostNameByWWN; virGetGroupID; virGetGroupList; diff --git a/src/util/virutil.c b/src/util/virutil.c index a135819..fb72f2d 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -2166,6 +2166,18 @@ virManageVport(const int parent_host, return ret; } +# 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) + /* virGetFCHostNameByWWN: * * Iterate over the sysfs tree to get FC host name (e.g. host5) @@ -2192,18 +2204,6 @@ virGetFCHostNameByWWN(const char *sysfs_prefix, if (virDirOpen(&dir, prefix) < 0) 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 (virDirRead(dir, &entry, prefix) > 0) { VIR_FREE(wwnn_buf); VIR_FREE(wwnn_path); @@ -2239,7 +2239,6 @@ virGetFCHostNameByWWN(const char *sysfs_prefix, } cleanup: -# undef READ_WWN VIR_DIR_CLOSE(dir); VIR_FREE(wwnn_path); VIR_FREE(wwpn_path); @@ -2248,6 +2247,67 @@ virGetFCHostNameByWWN(const char *sysfs_prefix, return ret; } +/* virGetFCHostNameByFabricWWN: + * + * Iterate over the sysfs tree to get FC host name (e.g. host5) + * by the provided "fabric_wwn". This would find a host on a SAN. + * + * Returns the FC host name which must be freed by the caller, + * or NULL on failure. + */ +char * +virGetFCHostNameByFabricWWN(const char *sysfs_prefix, + const char *fabric_wwn) +{ + const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH; + struct dirent *entry = NULL; + DIR *dir = NULL; + char *fabric_wwn_path = NULL; + char *fabric_wwn_buf = NULL; + char *vport_create_path = NULL; + char *p; + char *ret = NULL; + + if (virDirOpen(&dir, prefix) < 0) + return NULL; + + while (virDirRead(dir, &entry, prefix) > 0) { + VIR_FREE(fabric_wwn_path); + VIR_FREE(fabric_wwn_buf); + VIR_FREE(vport_create_path); + + if (virAsprintf(&fabric_wwn_path, "%s/%s/fabric_name", prefix, + entry->d_name) < 0) + goto cleanup; + + /* Existing vHBA's will have the same fabric_name, but won't + * have the vport_create file - so we check for both */ + if (virAsprintf(&vport_create_path, "%s/%s/vport_create", prefix, + entry->d_name) < 0) + goto cleanup; + + if (!virFileExists(fabric_wwn_path) || + !virFileExists(vport_create_path)) + continue; + + READ_WWN(fabric_wwn_path, fabric_wwn_buf); + + if (STRNEQ(fabric_wwn, p)) + continue; + + ignore_value(VIR_STRDUP(ret, entry->d_name)); + break; + } + + cleanup: + VIR_DIR_CLOSE(dir); + VIR_FREE(fabric_wwn_path); + VIR_FREE(fabric_wwn_buf); + VIR_FREE(vport_create_path); + return ret; +} +# undef READ_WWN + # define PORT_STATE_ONLINE "Online" /* virFindFCHostCapableVport: diff --git a/src/util/virutil.h b/src/util/virutil.h index 8c0d83c..3fbd7b0 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -206,6 +206,10 @@ char *virGetFCHostNameByWWN(const char *sysfs_prefix, const char *wwpn) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +char *virGetFCHostNameByFabricWWN(const char *sysfs_prefix, + const char *fabric_wwn) + ATTRIBUTE_NONNULL(2); + char *virFindFCHostCapableVport(const char *sysfs_prefix); int virParseOwnershipIds(const char *label, uid_t *uidPtr, gid_t *gidPtr); -- 2.7.4

On Fri, Nov 18, 2016 at 09:26:36AM -0500, John Ferlan wrote:
Create a utility routine in order to read the scsi_host fabric_name files looking for a match to a passed fabric_name
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virutil.c | 86 ++++++++++++++++++++++++++++++++++++++++-------- src/util/virutil.h | 4 +++ 3 files changed, 78 insertions(+), 13 deletions(-)
diff --git a/src/util/virutil.c b/src/util/virutil.c index a135819..fb72f2d 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -2166,6 +2166,18 @@ virManageVport(const int parent_host, return ret; }
+# define READ_WWN(wwn_path, buf) \
This macro either jumps to a label or alters a third variable. I don't think its scope should extend one function.
+ do { \
+ if (virFileReadAll(wwn_path, 1024, &buf) < 0) \ + goto cleanup; \
These two lines can be split out and with the STR{,N}EQ included, this macro can be COMPARE_WWN, removing the need for changing p. Or turned into a more complex function like virUSBSysReadFile.
+ if ((p = strchr(buf, '\n'))) \ + *p = '\0'; \ + if (STRPREFIX(buf, "0x")) \ + p = buf + strlen("0x"); \ + else \ + p = buf; \ + } while (0) + /* virGetFCHostNameByWWN: * * Iterate over the sysfs tree to get FC host name (e.g. host5)
The rest looks good to me. Jan

On 01/02/2017 09:51 AM, Ján Tomko wrote:
On Fri, Nov 18, 2016 at 09:26:36AM -0500, John Ferlan wrote:
Create a utility routine in order to read the scsi_host fabric_name files looking for a match to a passed fabric_name
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virutil.c | 86 ++++++++++++++++++++++++++++++++++++++++-------- src/util/virutil.h | 4 +++ 3 files changed, 78 insertions(+), 13 deletions(-)
diff --git a/src/util/virutil.c b/src/util/virutil.c index a135819..fb72f2d 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -2166,6 +2166,18 @@ virManageVport(const int parent_host, return ret; }
+# define READ_WWN(wwn_path, buf) \
This macro either jumps to a label or alters a third variable. I don't think its scope should extend one function.
+ do { \
+ if (virFileReadAll(wwn_path, 1024, &buf) < 0) \ + goto cleanup; \
These two lines can be split out and with the STR{,N}EQ included, this macro can be COMPARE_WWN, removing the need for changing p. Or turned into a more complex function like virUSBSysReadFile.
Not quite sure why it's felt 'p' wouldn't need changing. On my test system the "port_name", "node_name", and "fabric_wwn" files each have "0x######\n", but the stored/XML format will be ###### Maybe it's just late and I'm overthinking it.
+ if ((p = strchr(buf, '\n'))) \ + *p = '\0'; \ + if (STRPREFIX(buf, "0x")) \ + p = buf + strlen("0x"); \ + else \ + p = buf; \ + } while (0) + /* virGetFCHostNameByWWN: * * Iterate over the sysfs tree to get FC host name (e.g. host5)
The rest looks good to me.
So I went with a function: /* virReadCompareWWN * @prefix: path to the wwn file * @d_name: name of the current directory * @f_name: file name to read * * Read/compare the on-disk file with the passed wwn value. * * Returns: * -1 : Error * 0 : No match * 1 : Match */ static int virReadCompareWWN(const char *prefix, const char *d_name, const char *f_name, const char *wwn) { char *path; char *buf = NULL; char *p; int ret = -1; if (virAsprintf(&path, "%s/%s/%s", prefix, d_name, f_name) < 0) return -1; if (!virFileExists(path)) { ret = 0; goto cleanup; } if (virFileReadAll(path, 1024, &buf) < 0) goto cleanup; if ((p = strchr(buf, '\n'))) *p = '\0'; if (STRPREFIX(buf, "0x")) p = buf + strlen("0x"); else p = buf; if (STRNEQ(wwn, p)) ret = 0; else ret = 1; cleanup: VIR_FREE(path); VIR_FREE(buf); return ret; } It should be obvious what the callers do, but again I can post an updated series as long as you're generally fine with this. Tks - John

https://bugzilla.redhat.com/show_bug.cgi?id=1349696 As it turns out using only the 'parent' to achieve the goal of a consistent vHBA parent has issues with reboots where the scsi_hostX parent could change to scsi_hostY causing either failure to create the vHBA or usage of the wrong HBA for our vHBA. Thus add the ability to search for the "parent" by the parent wwnn/ wwpn values or just a fabric_name if someone only cares to ensure usage of the same SAN for the vHBA. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index df48b1a..4f13d5c 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -695,6 +695,7 @@ createVport(virConnectPtr conn, unsigned int parent_host; char *name = NULL; char *parent_hoststr = NULL; + bool skip_capable_check = false; virStoragePoolFCRefreshInfoPtr cbdata = NULL; virThread thread; int ret = -1; @@ -725,6 +726,23 @@ createVport(virConnectPtr conn, if (adapter->data.fchost.parent) { if (VIR_STRDUP(parent_hoststr, adapter->data.fchost.parent) < 0) goto cleanup; + } else if (adapter->data.fchost.parent_wwnn && + adapter->data.fchost.parent_wwpn) { + if (!(parent_hoststr = + virGetFCHostNameByWWN(NULL, adapter->data.fchost.parent_wwnn, + adapter->data.fchost.parent_wwpn))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("cannot find parent using provided wwnn/wwpn")); + goto cleanup; + } + } else if (adapter->data.fchost.parent_fabric_wwn) { + if (!(parent_hoststr = + virGetFCHostNameByFabricWWN(NULL, + adapter->data.fchost.parent_fabric_wwn))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("cannot find parent using provided fabric_wwn")); + goto cleanup; + } } else { if (!(parent_hoststr = virFindFCHostCapableVport(NULL))) { virReportError(VIR_ERR_XML_ERROR, "%s", @@ -732,14 +750,15 @@ createVport(virConnectPtr conn, "cannot find one on this host")); goto cleanup; } + skip_capable_check = true; } if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) goto cleanup; - if (adapter->data.fchost.parent && !virIsCapableFCHost(NULL, parent_host)) { + if (!skip_capable_check && !virIsCapableFCHost(NULL, parent_host)) { virReportError(VIR_ERR_XML_ERROR, - _("parent '%s' specified for vHBA is not vport capable"), + _("parent '%s' is not vport capable"), parent_hoststr); goto cleanup; } -- 2.7.4

On Fri, Nov 18, 2016 at 09:26:37AM -0500, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1349696
As it turns out using only the 'parent' to achieve the goal of a consistent vHBA parent has issues with reboots where the scsi_hostX parent could change to scsi_hostY causing either failure to create the vHBA or usage of the wrong HBA for our vHBA.
Thus add the ability to search for the "parent" by the parent wwnn/ wwpn values or just a fabric_name if someone only cares to ensure usage of the same SAN for the vHBA.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
@@ -732,14 +750,15 @@ createVport(virConnectPtr conn, "cannot find one on this host")); goto cleanup; } + skip_capable_check = true; }
if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) goto cleanup;
- if (adapter->data.fchost.parent && !virIsCapableFCHost(NULL, parent_host)) { + if (!skip_capable_check && !virIsCapableFCHost(NULL, parent_host)) { virReportError(VIR_ERR_XML_ERROR, - _("parent '%s' specified for vHBA is not vport capable"), + _("parent '%s' is not vport capable"),
Why the error message change? ACK to the rest. Jan

On 01/02/2017 09:52 AM, Ján Tomko wrote:
On Fri, Nov 18, 2016 at 09:26:37AM -0500, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1349696
As it turns out using only the 'parent' to achieve the goal of a consistent vHBA parent has issues with reboots where the scsi_hostX parent could change to scsi_hostY causing either failure to create the vHBA or usage of the wrong HBA for our vHBA.
Thus add the ability to search for the "parent" by the parent wwnn/ wwpn values or just a fabric_name if someone only cares to ensure usage of the same SAN for the vHBA.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend_scsi.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
@@ -732,14 +750,15 @@ createVport(virConnectPtr conn, "cannot find one on this host")); goto cleanup; } + skip_capable_check = true; }
if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) goto cleanup;
- if (adapter->data.fchost.parent && !virIsCapableFCHost(NULL, parent_host)) { + if (!skip_capable_check && !virIsCapableFCHost(NULL, parent_host)) { virReportError(VIR_ERR_XML_ERROR, - _("parent '%s' specified for vHBA is not vport capable"), + _("parent '%s' is not vport capable"),
Why the error message change?
Dunno... I'll restore it.. John
ACK to the rest.
Jan

ping? Tks - John On 11/18/2016 09:26 AM, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1349696
Lots of details in the bz, but essentially the problem is that providing a "parent" scsi_hostX value has drawbacks on reboots because what was scsi_hostX could turn into scsi_hostY on subsequent reboots.
So add the ability to use the parent wwnn/wwpn or fabric_wwn as 'search' criteria in order to create either non persistent vHBA's via nodedev or persistent vHBA's via storage pools.
NB: Documentation of this "process" is on the wiki:
http://wiki.libvirt.org/page/NPIV_in_libvirt
and would need to be adjusted once/if the changes are accepted.
John Ferlan (11): nodedev: Fix crash in libvirtd on vHBA creation path nodedev: Create helpers to search for vport capable nodedevs nodedev: Add ability to find a vport capable vHBA nodedev: Add the ability to create vHBA by parent wwnn/wwpn or fabric_wwn conf: Add more fchost search fields for storage pool vHBA creation iscsi: Clean up createVport exit paths iscsi: Change order of checks in createVport iscsi: Converge more createVport checks util: Remove need for extra VIR_FREE's in virGetFCHostNameByWWN util: Introduce virGetFCHostNameByFabricWWN iscsi: Add parent wwnn/wwpn or fabric capability for createVport
docs/schemas/basictypes.rng | 15 +++ docs/schemas/nodedev.rng | 15 +++ src/conf/node_device_conf.c | 234 +++++++++++++++++++++++++++++++---- src/conf/node_device_conf.h | 17 +++ src/conf/storage_conf.c | 21 +++- src/conf/storage_conf.h | 3 + src/libvirt_private.syms | 4 + src/node_device/node_device_driver.c | 28 ++++- src/storage/storage_backend_scsi.c | 86 +++++++------ src/util/virutil.c | 113 ++++++++++++----- src/util/virutil.h | 4 + 11 files changed, 439 insertions(+), 101 deletions(-)

On 12/03/2016 07:09 AM, John Ferlan wrote:
ping?
Sorry, I seem to have lost your initial cover letter so responding here...
On 11/18/2016 09:26 AM, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1349696
Lots of details in the bz, but essentially the problem is that providing a "parent" scsi_hostX value has drawbacks on reboots because what was scsi_hostX could turn into scsi_hostY on subsequent reboots.
So add the ability to use the parent wwnn/wwpn or fabric_wwn as 'search' criteria in order to create either non persistent vHBA's via nodedev or persistent vHBA's via storage pools.
I've not looked at all the patches in detail, but I have been giving them a fair bit of testing and haven't noticed any problems thus far. Once question I do have is related to volume detection in vport-based pools. Currently when there are multiple paths to a LUN only the active ones are displayed in the pool. Inactive paths are ignored since saferead() fails in src/storage/storage_backend.c:virStorageBackendDetectBlockVolFormatFD(). Without the inactive paths, there would be no path to the LUN if it was trespassed. I made the below hack to virStorageBackendUpdateVolTargetInfo() to show the inactive paths too, and was able to pass those to the guest allowing it to handle multipathing. Is there a reason the inactive paths are ignored? Regards, Jim Index: libvirt-2.5.0/src/storage/storage_backend.c =================================================================== --- libvirt-2.5.0.orig/src/storage/storage_backend.c +++ libvirt-2.5.0/src/storage/storage_backend.c @@ -1941,8 +1941,11 @@ virStorageBackendUpdateVolTargetInfo(vir if (withBlockVolFormat) { if ((ret = virStorageBackendDetectBlockVolFormatFD(target, fd, - readflags)) < 0) + readflags)) < 0) { + if (ret == -2) + ret = 0; goto cleanup; + } } cleanup:

On 12/12/2016 07:50 PM, Jim Fehlig wrote:
On 12/03/2016 07:09 AM, John Ferlan wrote:
ping?
Sorry, I seem to have lost your initial cover letter so responding here...
On 11/18/2016 09:26 AM, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1349696
Lots of details in the bz, but essentially the problem is that providing a "parent" scsi_hostX value has drawbacks on reboots because what was scsi_hostX could turn into scsi_hostY on subsequent reboots.
So add the ability to use the parent wwnn/wwpn or fabric_wwn as 'search' criteria in order to create either non persistent vHBA's via nodedev or persistent vHBA's via storage pools.
I've not looked at all the patches in detail, but I have been giving them a fair bit of testing and haven't noticed any problems thus far.
Thanks - that's good to know...
Once question I do have is related to volume detection in vport-based pools. Currently when there are multiple paths to a LUN only the active ones are displayed in the pool. Inactive paths are ignored since saferead() fails in src/storage/storage_backend.c:virStorageBackendDetectBlockVolFormatFD(). Without the inactive paths, there would be no path to the LUN if it was trespassed. I made the below hack to virStorageBackendUpdateVolTargetInfo() to show the inactive paths too, and was able to pass those to the guest allowing it to handle multipathing. Is there a reason the inactive paths are ignored?
I suppose for NPIV/vHBA LUN's it perhaps makes sense to have inactive paths. Been a while since I went down that path... Seems like virStorageBackendSCSINewLun should be able to receive a -2 in order to ignore VIR_STORAGE_VOL_READ_NOERROR. There's a sequence of changes I made starting with commit id '22346003d' which allow/support a -2 return. Those lead to a linked bz in the commit message describing a problem with multipath device with 'active' and 'ghost' devices. I'd have to dig more on that to jiggle the memory. Perhaps it's a path that needs a bit more code to more properly determine details of the LUN returning -2... IOW: Something that would dig on the failed device to see if it was mpath related. John
Regards, Jim
Index: libvirt-2.5.0/src/storage/storage_backend.c =================================================================== --- libvirt-2.5.0.orig/src/storage/storage_backend.c +++ libvirt-2.5.0/src/storage/storage_backend.c @@ -1941,8 +1941,11 @@ virStorageBackendUpdateVolTargetInfo(vir
if (withBlockVolFormat) { if ((ret = virStorageBackendDetectBlockVolFormatFD(target, fd, - readflags)) < 0) + readflags)) < 0) { + if (ret == -2) + ret = 0; goto cleanup; + } }
cleanup:
participants (5)
-
Boris Fiuczynski
-
Eric Farman
-
Jim Fehlig
-
John Ferlan
-
Ján Tomko