From 35643156c511c8120f2d562c1664a3c7a5a48cfb Mon Sep 17 00:00:00 2001 From: Jan Stodola Date: Thu, 28 Oct 2021 21:17:25 +0200 Subject: [PATCH 1/8] Fix removing zFCP SCSI devices Values parsed from /proc/scsi/scsi were not correctly used to assemble paths to SCSI devices. For example: /sys/bus/scsi/devices/0:0:00:00/ was incorrectly accessed instead of: /sys/bus/scsi/devices/0:0:0:0/ Switch to a more reliable way of listing the available SCSI devices. Related: rhbz#1937030 --- blivet/zfcp.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/blivet/zfcp.py b/blivet/zfcp.py index 93af5419..3747290e 100644 --- a/blivet/zfcp.py +++ b/blivet/zfcp.py @@ -20,6 +20,7 @@ # import os +import re from . import udev from . import util from .i18n import _ @@ -167,20 +168,10 @@ class ZFCPDevice: return True def offline_scsi_device(self): - f = open("/proc/scsi/scsi", "r") - lines = f.readlines() - f.close() - # alternatively iterate over /sys/bus/scsi/devices/*:0:*:*/ + # A list of existing SCSI devices in format Host:Bus:Target:Lun + scsi_devices = [f for f in os.listdir(scsidevsysfs) if re.search(r'^[0-9]+:[0-9]+:[0-9]+:[0-9]+$', f)] - for line in lines: - if not line.startswith("Host"): - continue - scsihost = line.split() - host = scsihost[1] - channel = "0" - devid = scsihost[5] - lun = scsihost[7] - scsidev = "%s:%s:%s:%s" % (host[4:], channel, devid, lun) + for scsidev in scsi_devices: fcpsysfs = "%s/%s" % (scsidevsysfs, scsidev) scsidel = "%s/%s/delete" % (scsidevsysfs, scsidev) -- 2.34.3 From 771cbf623030b1fa51ec193a2b5e2db229420a7a Mon Sep 17 00:00:00 2001 From: Jan Stodola Date: Sun, 21 Nov 2021 02:47:45 +0100 Subject: [PATCH 2/8] Refactor the ZFCPDevice class Add a new base class for zFCP devices. Move code to the new base class. Improve documentation. Related: rhbz#1937030 --- blivet/zfcp.py | 131 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 36 deletions(-) diff --git a/blivet/zfcp.py b/blivet/zfcp.py index 3747290e..4a50f65f 100644 --- a/blivet/zfcp.py +++ b/blivet/zfcp.py @@ -21,6 +21,7 @@ import os import re +from abc import ABC from . import udev from . import util from .i18n import _ @@ -46,29 +47,19 @@ zfcpsysfs = "/sys/bus/ccw/drivers/zfcp" scsidevsysfs = "/sys/bus/scsi/devices" zfcpconf = "/etc/zfcp.conf" +class ZFCPDeviceBase(ABC): + """An abstract base class for zFCP storage devices.""" -class ZFCPDevice: - """ - .. warning:: - Since this is a singleton class, calling deepcopy() on the instance - just returns ``self`` with no copy being created. - """ - - def __init__(self, devnum, wwpn, fcplun): + def __init__(self, devnum): self.devnum = blockdev.s390.sanitize_dev_input(devnum) - self.wwpn = blockdev.s390.zfcp_sanitize_wwpn_input(wwpn) - self.fcplun = blockdev.s390.zfcp_sanitize_lun_input(fcplun) - if not self.devnum: raise ValueError(_("You have not specified a device number or the number is invalid")) - if not self.wwpn: - raise ValueError(_("You have not specified a worldwide port name or the name is invalid.")) - if not self.fcplun: - raise ValueError(_("You have not specified a FCP LUN or the number is invalid.")) + + self._device_online_path = os.path.join(zfcpsysfs, self.devnum, "online") # Force str and unicode types in case any of the properties are unicode def _to_string(self): - return "%s %s %s" % (self.devnum, self.wwpn, self.fcplun) + return str(self.devnum) def __str__(self): return stringize(self._to_string()) @@ -76,33 +67,97 @@ class ZFCPDevice: def __unicode__(self): return unicodeize(self._to_string()) - def online_device(self): - online = "%s/%s/online" % (zfcpsysfs, self.devnum) - portadd = "%s/%s/port_add" % (zfcpsysfs, self.devnum) - portdir = "%s/%s/%s" % (zfcpsysfs, self.devnum, self.wwpn) - unitadd = "%s/unit_add" % (portdir) - unitdir = "%s/%s" % (portdir, self.fcplun) - failed = "%s/failed" % (unitdir) + def _free_device(self): + """Remove the device from the I/O ignore list to make it visible to the system. + + :raises: ValueError if the device cannot be removed from the I/O ignore list + """ - if not os.path.exists(online): + if not os.path.exists(self._device_online_path): log.info("Freeing zFCP device %s", self.devnum) util.run_program(["zfcp_cio_free", "-d", self.devnum]) - if not os.path.exists(online): + if not os.path.exists(self._device_online_path): raise ValueError(_("zFCP device %s not found, not even in device ignore list.") % (self.devnum,)) + def _set_zfcp_device_online(self): + """Set the zFCP device online. + + :raises: ValueError if the device cannot be set online + """ + try: - f = open(online, "r") - devonline = f.readline().strip() - f.close() + with open(self._device_online_path) as f: + devonline = f.readline().strip() if devonline != "1": - logged_write_line_to_file(online, "1") + logged_write_line_to_file(self._device_online_path, "1") except OSError as e: raise ValueError(_("Could not set zFCP device %(devnum)s " "online (%(e)s).") % {'devnum': self.devnum, 'e': e}) + def _set_zfcp_device_offline(self): + """Set the zFCP device offline. + + :raises: ValueError if the device cannot be set offline + """ + + try: + logged_write_line_to_file(self._device_online_path, "0") + except OSError as e: + raise ValueError(_("Could not set zFCP device %(devnum)s " + "offline (%(e)s).") + % {'devnum': self.devnum, 'e': e}) + + def online_device(self): + """Initialize the device and make its storage block device(s) ready to use. + + :returns: True if success + :raises: ValueError if the device cannot be initialized + """ + + self._free_device() + self._set_zfcp_device_online() + return True + + +class ZFCPDevice(ZFCPDeviceBase): + """A class for zFCP devices that are not configured in NPIV mode. Such + devices have to be specified by a device number, WWPN and LUN. + """ + + def __init__(self, devnum, wwpn, fcplun): + super().__init__(devnum) + + self.wwpn = blockdev.s390.zfcp_sanitize_wwpn_input(wwpn) + if not self.wwpn: + raise ValueError(_("You have not specified a worldwide port name or the name is invalid.")) + + self.fcplun = blockdev.s390.zfcp_sanitize_lun_input(fcplun) + if not self.fcplun: + raise ValueError(_("You have not specified a FCP LUN or the number is invalid.")) + + # Force str and unicode types in case any of the properties are unicode + def _to_string(self): + return "{} {} {}".format(self.devnum, self.wwpn, self.fcplun) + + def online_device(self): + """Initialize the device and make its storage block device(s) ready to use. + + :returns: True if success + :raises: ValueError if the device cannot be initialized + """ + + super().online_device() + + portadd = "%s/%s/port_add" % (zfcpsysfs, self.devnum) + portdir = "%s/%s/%s" % (zfcpsysfs, self.devnum, self.wwpn) + unitadd = "%s/unit_add" % (portdir) + unitdir = "%s/%s" % (portdir, self.fcplun) + failed = "%s/failed" % (unitdir) + + # create the sysfs directory for the WWPN/port if not os.path.exists(portdir): if os.path.exists(portadd): # older zfcp sysfs interface @@ -127,6 +182,7 @@ class ZFCPDevice: "there.", {'wwpn': self.wwpn, 'devnum': self.devnum}) + # create the sysfs directory for the LUN/unit if not os.path.exists(unitdir): try: logged_write_line_to_file(unitadd, self.fcplun) @@ -144,6 +200,7 @@ class ZFCPDevice: 'wwpn': self.wwpn, 'devnum': self.devnum}) + # check the state of the LUN fail = "0" try: f = open(failed, "r") @@ -168,6 +225,8 @@ class ZFCPDevice: return True def offline_scsi_device(self): + """Find SCSI devices associated to the zFCP device and remove them from the system.""" + # A list of existing SCSI devices in format Host:Bus:Target:Lun scsi_devices = [f for f in os.listdir(scsidevsysfs) if re.search(r'^[0-9]+:[0-9]+:[0-9]+:[0-9]+$', f)] @@ -196,7 +255,8 @@ class ZFCPDevice: self.devnum, self.wwpn, self.fcplun) def offline_device(self): - offline = "%s/%s/online" % (zfcpsysfs, self.devnum) + """Remove the zFCP device from the system.""" + portadd = "%s/%s/port_add" % (zfcpsysfs, self.devnum) portremove = "%s/%s/port_remove" % (zfcpsysfs, self.devnum) unitremove = "%s/%s/%s/unit_remove" % (zfcpsysfs, self.devnum, self.wwpn) @@ -212,6 +272,7 @@ class ZFCPDevice: % {'devnum': self.devnum, 'wwpn': self.wwpn, 'fcplun': self.fcplun, 'e': e}) + # remove the LUN try: logged_write_line_to_file(unitremove, self.fcplun) except OSError as e: @@ -221,6 +282,7 @@ class ZFCPDevice: % {'fcplun': self.fcplun, 'wwpn': self.wwpn, 'devnum': self.devnum, 'e': e}) + # remove the WWPN only if there are no other LUNs attached if os.path.exists(portadd): # only try to remove ports with older zfcp sysfs interface for lun in os.listdir(portdir): @@ -238,6 +300,7 @@ class ZFCPDevice: % {'wwpn': self.wwpn, 'devnum': self.devnum, 'e': e}) + # check if there are other WWPNs existing for the zFCP device number if os.path.exists(portadd): # older zfcp sysfs interface for port in os.listdir(devdir): @@ -256,12 +319,8 @@ class ZFCPDevice: self.devnum, luns[0]) return True - try: - logged_write_line_to_file(offline, "0") - except OSError as e: - raise ValueError(_("Could not set zFCP device %(devnum)s " - "offline (%(e)s).") - % {'devnum': self.devnum, 'e': e}) + # no other WWPNs/LUNs exists for this device number, it's safe to bring it offline + self._set_zfcp_device_offline() return True -- 2.34.3 From 2dc44c00f170d64458a7c89abc91cda61af8387f Mon Sep 17 00:00:00 2001 From: Jan Stodola Date: Sun, 21 Nov 2021 02:35:05 +0100 Subject: [PATCH 3/8] Move offline_scsi_device() to the base class Related: rhbz#1937030 --- blivet/zfcp.py | 74 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/blivet/zfcp.py b/blivet/zfcp.py index 4a50f65f..af8f841d 100644 --- a/blivet/zfcp.py +++ b/blivet/zfcp.py @@ -110,6 +110,15 @@ class ZFCPDeviceBase(ABC): "offline (%(e)s).") % {'devnum': self.devnum, 'e': e}) + def _is_scsi_associated_with_fcp(self, fcphbasysfs, _fcpwwpnsysfs, _fcplunsysfs): + """Decide if the SCSI device with the provided SCSI attributes + corresponds to the zFCP device. + + :returns: True or False + """ + + return fcphbasysfs == self.devnum + def online_device(self): """Initialize the device and make its storage block device(s) ready to use. @@ -121,6 +130,30 @@ class ZFCPDeviceBase(ABC): self._set_zfcp_device_online() return True + def offline_scsi_device(self): + """Find SCSI devices associated to the zFCP device and remove them from the system.""" + + # A list of existing SCSI devices in format Host:Bus:Target:Lun + scsi_devices = [f for f in os.listdir(scsidevsysfs) if re.search(r'^[0-9]+:[0-9]+:[0-9]+:[0-9]+$', f)] + + for scsidev in scsi_devices: + fcpsysfs = os.path.join(scsidevsysfs, scsidev) + + with open(os.path.join(fcpsysfs, "hba_id")) as f: + fcphbasysfs = f.readline().strip() + with open(os.path.join(fcpsysfs, "wwpn")) as f: + fcpwwpnsysfs = f.readline().strip() + with open(os.path.join(fcpsysfs, "fcp_lun")) as f: + fcplunsysfs = f.readline().strip() + + if self._is_scsi_associated_with_fcp(fcphbasysfs, fcpwwpnsysfs, fcplunsysfs): + scsidel = os.path.join(scsidevsysfs, scsidev, "delete") + logged_write_line_to_file(scsidel, "1") + udev.settle() + return + + log.warning("No scsi device found to delete for zfcp %s", self) + class ZFCPDevice(ZFCPDeviceBase): """A class for zFCP devices that are not configured in NPIV mode. Such @@ -142,6 +175,17 @@ class ZFCPDevice(ZFCPDeviceBase): def _to_string(self): return "{} {} {}".format(self.devnum, self.wwpn, self.fcplun) + def _is_scsi_associated_with_fcp(self, fcphbasysfs, fcpwwpnsysfs, fcplunsysfs): + """Decide if the SCSI device with the provided SCSI attributes + corresponds to the zFCP device. + + :returns: True or False + """ + + return (fcphbasysfs == self.devnum and + fcpwwpnsysfs == self.wwpn and + fcplunsysfs == self.fcplun) + def online_device(self): """Initialize the device and make its storage block device(s) ready to use. @@ -224,36 +268,6 @@ class ZFCPDevice(ZFCPDeviceBase): return True - def offline_scsi_device(self): - """Find SCSI devices associated to the zFCP device and remove them from the system.""" - - # A list of existing SCSI devices in format Host:Bus:Target:Lun - scsi_devices = [f for f in os.listdir(scsidevsysfs) if re.search(r'^[0-9]+:[0-9]+:[0-9]+:[0-9]+$', f)] - - for scsidev in scsi_devices: - fcpsysfs = "%s/%s" % (scsidevsysfs, scsidev) - scsidel = "%s/%s/delete" % (scsidevsysfs, scsidev) - - f = open("%s/hba_id" % (fcpsysfs), "r") - fcphbasysfs = f.readline().strip() - f.close() - f = open("%s/wwpn" % (fcpsysfs), "r") - fcpwwpnsysfs = f.readline().strip() - f.close() - f = open("%s/fcp_lun" % (fcpsysfs), "r") - fcplunsysfs = f.readline().strip() - f.close() - - if fcphbasysfs == self.devnum \ - and fcpwwpnsysfs == self.wwpn \ - and fcplunsysfs == self.fcplun: - logged_write_line_to_file(scsidel, "1") - udev.settle() - return - - log.warning("no scsi device found to delete for zfcp %s %s %s", - self.devnum, self.wwpn, self.fcplun) - def offline_device(self): """Remove the zFCP device from the system.""" -- 2.34.3 From f194c6e591c3e409f227fd10d3e9923af91ea893 Mon Sep 17 00:00:00 2001 From: Jan Stodola Date: Sat, 6 Nov 2021 21:27:52 +0100 Subject: [PATCH 4/8] Allow to delete more than one SCSI device NPIV zFCP devices can attach more than one SCSI device, so allow to delete them all. For non-NPIV devices it means possible slowdown, since all SCSI devices would now be checked. Related: rhbz#1937030 --- blivet/zfcp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blivet/zfcp.py b/blivet/zfcp.py index af8f841d..3b3f623b 100644 --- a/blivet/zfcp.py +++ b/blivet/zfcp.py @@ -136,6 +136,7 @@ class ZFCPDeviceBase(ABC): # A list of existing SCSI devices in format Host:Bus:Target:Lun scsi_devices = [f for f in os.listdir(scsidevsysfs) if re.search(r'^[0-9]+:[0-9]+:[0-9]+:[0-9]+$', f)] + scsi_device_found = False for scsidev in scsi_devices: fcpsysfs = os.path.join(scsidevsysfs, scsidev) @@ -147,12 +148,13 @@ class ZFCPDeviceBase(ABC): fcplunsysfs = f.readline().strip() if self._is_scsi_associated_with_fcp(fcphbasysfs, fcpwwpnsysfs, fcplunsysfs): + scsi_device_found = True scsidel = os.path.join(scsidevsysfs, scsidev, "delete") logged_write_line_to_file(scsidel, "1") udev.settle() - return - log.warning("No scsi device found to delete for zfcp %s", self) + if not scsi_device_found: + log.warning("No scsi device found to delete for zfcp %s", self) class ZFCPDevice(ZFCPDeviceBase): -- 2.34.3 From f6615be663434079b3f2a86be5db88b816d8a9e1 Mon Sep 17 00:00:00 2001 From: Jan Stodola Date: Sun, 21 Nov 2021 03:01:02 +0100 Subject: [PATCH 5/8] Add a function for reading the value of a kernel module parameter Related: rhbz#1937030 --- blivet/util.py | 33 +++++++++++++++++++++++++++++++++ tests/util_test.py | 11 +++++++++++ 2 files changed, 44 insertions(+) diff --git a/blivet/util.py b/blivet/util.py index af60210b..cbef65e0 100644 --- a/blivet/util.py +++ b/blivet/util.py @@ -1131,3 +1131,36 @@ def detect_virt(): return False else: return vm[0] in ('qemu', 'kvm', 'xen') + + +def natural_sort_key(device): + """ Sorting key for devices which makes sure partitions are sorted in natural + way, e.g. 'sda1, sda2, ..., sda10' and not like 'sda1, sda10, sda2, ...' + """ + if device.type == "partition" and device.parted_partition and device.disk: + part_num = getattr(device.parted_partition, "number", -1) + return [device.disk.name, part_num] + else: + return [device.name, 0] + + +def get_kernel_module_parameter(module, parameter): + """ Return the value of a given kernel module parameter + + :param str module: a kernel module + :param str parameter: a module parameter + :returns: the value of the given kernel module parameter or None + :rtype: str + """ + + value = None + + parameter_path = os.path.join("/sys/module", module, "parameters", parameter) + try: + with open(parameter_path) as f: + value = f.read().strip() + except IOError as e: + log.warning("Couldn't get the value of the parameter '%s' from the kernel module '%s': %s", + parameter, module, str(e)) + + return value diff --git a/tests/util_test.py b/tests/util_test.py index b4f82c1b..805447c7 100644 --- a/tests/util_test.py +++ b/tests/util_test.py @@ -182,3 +182,14 @@ class GetSysfsAttrTestCase(unittest.TestCase): # the unicode replacement character (U+FFFD) should be used instead model = util.get_sysfs_attr(sysfs, "model") self.assertEqual(model, "test model\ufffd") + + +class GetKernelModuleParameterTestCase(unittest.TestCase): + + def test_nonexisting_kernel_module(self): + self.assertIsNone(util.get_kernel_module_parameter("unknown_module", "unknown_parameter")) + + def test_get_kernel_module_parameter_value(self): + with mock.patch('blivet.util.open', mock.mock_open(read_data='value\n')): + value = util.get_kernel_module_parameter("module", "parameter") + self.assertEqual(value, "value") -- 2.34.3 From 17c99a2444ef750bdbf5b24665c5fd3c52e687d9 Mon Sep 17 00:00:00 2001 From: Jan Stodola Date: Sun, 21 Nov 2021 03:01:46 +0100 Subject: [PATCH 6/8] LUN and WWPN should not be used for NPIV zFCP devices Log a warning if activating a zFCP device in NPIV mode and WWPN or LUN have been provided. They are superfluous for NPIV devices. Related: rhbz#1937030 --- blivet/zfcp.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/blivet/zfcp.py b/blivet/zfcp.py index 3b3f623b..726e9364 100644 --- a/blivet/zfcp.py +++ b/blivet/zfcp.py @@ -22,6 +22,7 @@ import os import re from abc import ABC +import glob from . import udev from . import util from .i18n import _ @@ -47,6 +48,55 @@ zfcpsysfs = "/sys/bus/ccw/drivers/zfcp" scsidevsysfs = "/sys/bus/scsi/devices" zfcpconf = "/etc/zfcp.conf" + +def _is_lun_scan_allowed(): + """Return True if automatic LUN scanning is enabled by the kernel.""" + + allow_lun_scan = util.get_kernel_module_parameter("zfcp", "allow_lun_scan") + return allow_lun_scan == "Y" + + +def _is_port_in_npiv_mode(device_id): + """Return True if the device ID is configured in NPIV mode. See + https://www.ibm.com/docs/en/linux-on-systems?topic=devices-use-npiv + """ + + port_in_npiv_mode = False + port_type_path = "/sys/bus/ccw/devices/{}/host*/fc_host/host*/port_type".format(device_id) + port_type_paths = glob.glob(port_type_path) + try: + for filename in port_type_paths: + with open(filename) as f: + port_type = f.read() + if re.search(r"(^|\s)NPIV(\s|$)", port_type): + port_in_npiv_mode = True + except OSError as e: + log.warning("Couldn't read the port_type attribute of the %s device: %s", device_id, str(e)) + port_in_npiv_mode = False + + return port_in_npiv_mode + + +def is_npiv_enabled(device_id): + """Return True if the given zFCP device ID is configured and usable in + NPIV (N_Port ID Virtualization) mode. + + :returns: True or False + """ + + # LUN scanning disabled by the kernel module prevents using the device in NPIV mode + if not _is_lun_scan_allowed(): + log.warning("Automatic LUN scanning is disabled by the zfcp kernel module.") + return False + + # The port itself has to be configured in NPIV mode + if not _is_port_in_npiv_mode(device_id): + log.warning("The zFCP device %s is not configured in NPIV mode.", device_id) + return False + + return True + + class ZFCPDeviceBase(ABC): """An abstract base class for zFCP storage devices.""" @@ -203,6 +253,13 @@ class ZFCPDevice(ZFCPDeviceBase): unitdir = "%s/%s" % (portdir, self.fcplun) failed = "%s/failed" % (unitdir) + # Activating an NPIV enabled device using devnum, WWPN and LUN should still be possible + # as this method was used as a workaround until the support for NPIV enabled devices has + # been implemented. Just log a warning message and continue. + if is_npiv_enabled(self.devnum): + log.warning("zFCP device %s in NPIV mode brought online. All LUNs will be activated " + "automatically although WWPN and LUN have been provided.", self.devnum) + # create the sysfs directory for the WWPN/port if not os.path.exists(portdir): if os.path.exists(portadd): @@ -327,7 +384,6 @@ class ZFCPDevice(ZFCPDeviceBase): return True else: # newer zfcp sysfs interface with auto port scan - import glob luns = glob.glob("%s/0x????????????????/0x????????????????" % (devdir,)) if len(luns) != 0: -- 2.34.3 From a8f97bd0d74e3da9c18bd03d968f5d2f0c3ee46f Mon Sep 17 00:00:00 2001 From: Jan Stodola Date: Sat, 6 Nov 2021 21:27:52 +0100 Subject: [PATCH 7/8] Add new class for NPIV-enabled devices Related: rhbz#1937030 --- blivet/zfcp.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/blivet/zfcp.py b/blivet/zfcp.py index 726e9364..e6c0e48a 100644 --- a/blivet/zfcp.py +++ b/blivet/zfcp.py @@ -397,6 +397,44 @@ class ZFCPDevice(ZFCPDeviceBase): return True +class ZFCPNPIVDevice(ZFCPDeviceBase): + """Class for zFCP devices configured in NPIV mode. Only a zFCP device number is + needed for such devices. + """ + + def online_device(self): + """Initialize the device and make its storage block device(s) ready to use. + + :returns: True if success + :raises: ValueError if the device cannot be initialized + """ + + super().online_device() + + if not is_npiv_enabled(self.devnum): + raise ValueError(_("zFCP device %s cannot be used in NPIV mode.") % self) + + return True + + def offline_device(self): + """Remove the zFCP device from the system. + + :returns: True if success + :raises: ValueError if the device cannot be brought offline + """ + + try: + self.offline_scsi_device() + except OSError as e: + raise ValueError(_("Could not correctly delete SCSI device of " + "zFCP %(zfcpdev)s (%(e)s).") + % {'zfcpdev': self, 'e': e}) + + self._set_zfcp_device_offline() + + return True + + class zFCP: """ ZFCP utility class. @@ -439,7 +477,12 @@ class zFCP: fields = line.split() - if len(fields) == 3: + # NPIV enabled device + if len(fields) == 1: + devnum = fields[0] + wwpn = None + fcplun = None + elif len(fields) == 3: devnum = fields[0] wwpn = fields[1] fcplun = fields[2] @@ -458,8 +501,12 @@ class zFCP: except ValueError as e: log.warning("%s", str(e)) - def add_fcp(self, devnum, wwpn, fcplun): - d = ZFCPDevice(devnum, wwpn, fcplun) + def add_fcp(self, devnum, wwpn=None, fcplun=None): + if wwpn and fcplun: + d = ZFCPDevice(devnum, wwpn, fcplun) + else: + d = ZFCPNPIVDevice(devnum) + if d.online_device(): self.fcpdevs.add(d) -- 2.34.3 From 963822ff989c938e74d582216f4f7ded595eccc1 Mon Sep 17 00:00:00 2001 From: Jan Stodola Date: Sat, 20 Nov 2021 23:12:43 +0100 Subject: [PATCH 8/8] Generate correct dracut boot arguments for NPIV devices NPIV enabled devices need only the device ID. WWPNs/LUNs are discovered automatically by the kernel module. Resolves: rhbz#1937030 --- blivet/devices/disk.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py index 67a01ba6..36278507 100644 --- a/blivet/devices/disk.py +++ b/blivet/devices/disk.py @@ -577,7 +577,15 @@ class ZFCPDiskDevice(DiskDevice): 'lun': self.fcp_lun} def dracut_setup_args(self): - return set(["rd.zfcp=%s,%s,%s" % (self.hba_id, self.wwpn, self.fcp_lun,)]) + from ..zfcp import is_npiv_enabled + + # zFCP devices in NPIV mode need only the device ID + if is_npiv_enabled(self.hba_id): + dracut_args = set(["rd.zfcp=%s" % self.hba_id]) + else: + dracut_args = set(["rd.zfcp=%s,%s,%s" % (self.hba_id, self.wwpn, self.fcp_lun,)]) + + return dracut_args class DASDDevice(DiskDevice): -- 2.34.3