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