diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py index 4ae4a8451..a849e7ac3 100644 --- a/blivet/devices/disk.py +++ b/blivet/devices/disk.py @@ -498,7 +498,12 @@ def dracut_setup_args(self): from ..zfcp import has_auto_lun_scan # zFCP auto LUN scan needs only the device ID - if has_auto_lun_scan(self.hba_id): + # If the user explicitly over-specified with a full path configuration + # respect this choice and emit a full path specification nonetheless. + errorlevel = util.run_program(["lszdev", "zfcp-lun", "--configured", + "%s:%s:%s" % (self.hba_id, self.wwpn, + self.fcp_lun)]) + if has_auto_lun_scan(self.hba_id) and errorlevel != 0: 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,)]) @@ -525,67 +530,19 @@ def __init__(self, device, **kwargs): :type format: :class:`~.formats.DeviceFormat` or a subclass of it :keyword str wwn: the disk's WWN :keyword busid: bus ID - :keyword opts: options - :type opts: dict with option name keys and option value values """ self.busid = kwargs.pop('busid') - self.opts = kwargs.pop('opts') DiskDevice.__init__(self, device, **kwargs) @property def description(self): return "DASD device %s" % self.busid - def get_opts(self): - return ["%s=%s" % (k, v) for k, v in self.opts.items() if v == '1'] - def dracut_setup_args(self): - conf = "/etc/dasd.conf" - line = None - if os.path.isfile(conf): - f = open(conf) - # grab the first line that starts with our bus_id - for l in f.readlines(): - if l.startswith(self.busid): - line = l.rstrip() - break - - f.close() - - # See if we got a line. If not, grab our get_opts - if not line: - line = self.busid - for devopt in self.get_opts(): - line += " %s" % devopt - - # Create a translation mapping from dasd.conf format to module format - translate = {'use_diag': 'diag', - 'readonly': 'ro', - 'erplog': 'erplog', - 'failfast': 'failfast'} - - # this is a really awkward way of determining if the - # feature found is actually desired (1, not 0), plus - # translating that feature into the actual kernel module - # value - opts = [] - parts = line.split() - for chunk in parts[1:]: - try: - feat, val = chunk.split('=') - if int(val): - opts.append(translate[feat]) - except (ValueError, KeyError): - # If we don't know what the feature is (feat not in translate - # or if we get a val that doesn't cleanly convert to an int - # we can't do anything with it. - log.warning("failed to parse dasd feature %s", chunk) - - if opts: - return set(["rd.dasd=%s(%s)" % (self.busid, - ":".join(opts))]) - else: - return set(["rd.dasd=%s" % self.busid]) + devspec = util.capture_output(["/lib/s390-tools/zdev-to-dasd_mod.dasd", + "persistent", self.busid]).strip() + # strip to remove trailing newline, which must not appear in zipl BLS + return set(["rd.dasd=%s" % devspec]) NVMeController = namedtuple("NVMeController", ["name", "serial", "nvme_ver", "id", "subsysnqn", diff --git a/blivet/populator/helpers/disk.py b/blivet/populator/helpers/disk.py index aa10c8693..8936d4de2 100644 --- a/blivet/populator/helpers/disk.py +++ b/blivet/populator/helpers/disk.py @@ -204,9 +204,6 @@ def match(cls, data): def _get_kwargs(self): kwargs = super(DASDDevicePopulator, self)._get_kwargs() kwargs["busid"] = udev.device_get_dasd_bus_id(self.data) - kwargs["opts"] = {} - for attr in ['readonly', 'use_diag', 'erplog', 'failfast']: - kwargs["opts"][attr] = udev.device_get_dasd_flag(self.data, attr) log.info("%s is a dasd device", udev.device_get_name(self.data)) return kwargs diff --git a/blivet/zfcp.py b/blivet/zfcp.py index a2b7facb2..a33eb48b3 100644 --- a/blivet/zfcp.py +++ b/blivet/zfcp.py @@ -45,7 +45,6 @@ def logged_write_line_to_file(fn, value): zfcpsysfs = "/sys/bus/ccw/drivers/zfcp" scsidevsysfs = "/sys/bus/scsi/devices" -zfcpconf = "/etc/zfcp.conf" def _is_lun_scan_allowed(): @@ -104,8 +103,6 @@ def __init__(self, devnum): if not self.devnum: raise ValueError(_("You have not specified a device number 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 str(self.devnum) @@ -113,20 +110,6 @@ def _to_string(self): def __str__(self): return self._to_string() - 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(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(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. @@ -134,10 +117,8 @@ def _set_zfcp_device_online(self): """ try: - with open(self._device_online_path) as f: - devonline = f.readline().strip() - if devonline != "1": - logged_write_line_to_file(self._device_online_path, "1") + util.run_program(["chzdev", "--enable", "zfcp-host", self.devnum, + "--yes", "--no-root-update", "--force"]) except OSError as e: raise ValueError(_("Could not set zFCP device %(devnum)s " "online (%(e)s).") @@ -150,7 +131,8 @@ def _set_zfcp_device_offline(self): """ try: - logged_write_line_to_file(self._device_online_path, "0") + util.run_program(["chzdev", "--disable", "zfcp-host", self.devnum, + "--yes", "--no-root-update", "--force"]) except OSError as e: raise ValueError(_("Could not set zFCP device %(devnum)s " "offline (%(e)s).") @@ -163,6 +145,7 @@ def _is_associated_with_fcp(self, fcphbasysfs, fcpwwpnsysfs, fcplunsysfs): :returns: True or False """ + @abstractmethod def online_device(self): """Initialize the device and make its storage block device(s) ready to use. @@ -170,10 +153,6 @@ def online_device(self): :raises: ValueError if the device cannot be initialized """ - self._free_device() - 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.""" @@ -238,51 +217,15 @@ def online_device(self): :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) - - # Activating using devnum, WWPN, and LUN despite available zFCP auto LUN scan should still - # be possible as this method was used as a workaround until the support for zFCP auto LUN - # scan devices has been implemented. Just log a warning message and continue. - if has_auto_lun_scan(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): - # older zfcp sysfs interface - try: - logged_write_line_to_file(portadd, self.wwpn) - udev.settle() - except OSError as e: - raise ValueError(_("Could not add WWPN %(wwpn)s to zFCP " - "device %(devnum)s (%(e)s).") - % {'wwpn': self.wwpn, - 'devnum': self.devnum, - 'e': e}) - else: - # newer zfcp sysfs interface with auto port scan - raise ValueError(_("WWPN %(wwpn)s not found at zFCP device " - "%(devnum)s.") % {'wwpn': self.wwpn, - 'devnum': self.devnum}) - else: - if os.path.exists(portadd): - # older zfcp sysfs interface - log.info("WWPN %(wwpn)s at zFCP device %(devnum)s already " - "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) - udev.settle() + util.run_program(["chzdev", "--enable", "zfcp-lun", + "%s:%s:%s" % (self.devnum, self.wwpn, self.fcplun), + "--yes", "--no-root-update", "--force"]) except OSError as e: raise ValueError(_("Could not add LUN %(fcplun)s to WWPN " "%(wwpn)s on zFCP device %(devnum)s " @@ -296,51 +239,23 @@ def online_device(self): 'wwpn': self.wwpn, 'devnum': self.devnum}) - # check the state of the LUN - fail = "0" - try: - f = open(failed, "r") - fail = f.readline().strip() - f.close() - except OSError as e: - raise ValueError(_("Could not read failed attribute of LUN " - "%(fcplun)s at WWPN %(wwpn)s on zFCP device " - "%(devnum)s (%(e)s).") - % {'fcplun': self.fcplun, - 'wwpn': self.wwpn, - 'devnum': self.devnum, - 'e': e}) - if fail != "0": - self.offline_device() - raise ValueError(_("Failed LUN %(fcplun)s at WWPN %(wwpn)s on " - "zFCP device %(devnum)s removed again.") - % {'fcplun': self.fcplun, - 'wwpn': self.wwpn, - 'devnum': self.devnum}) + # Activating using devnum, WWPN, and LUN despite available zFCP auto LUN scan should still + # be possible as this method was used as a workaround until the support for zFCP auto LUN + # scan devices has been implemented. Just log a warning message and continue. + if has_auto_lun_scan(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) return True def offline_device(self): """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) - portdir = "%s/%s/%s" % (zfcpsysfs, self.devnum, self.wwpn) - devdir = "%s/%s" % (zfcpsysfs, self.devnum) - - try: - self.offline_scsi_device() - except OSError as e: - raise ValueError(_("Could not correctly delete SCSI device of " - "zFCP %(devnum)s %(wwpn)s %(fcplun)s " - "(%(e)s).") - % {'devnum': self.devnum, 'wwpn': self.wwpn, - 'fcplun': self.fcplun, 'e': e}) - # remove the LUN try: - logged_write_line_to_file(unitremove, self.fcplun) + util.run_program(["chzdev", "--disable", "zfcp-lun", + "%s:%s:%s" % (self.devnum, self.wwpn, self.fcplun), + "--yes", "--no-root-update", "--force"]) except OSError as e: raise ValueError(_("Could not remove LUN %(fcplun)s at WWPN " "%(wwpn)s on zFCP device %(devnum)s " @@ -348,45 +263,6 @@ def offline_device(self): % {'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): - if lun.startswith("0x") and \ - os.path.isdir(os.path.join(portdir, lun)): - log.info("Not removing WWPN %s at zFCP device %s since port still has other LUNs, e.g. %s.", - self.wwpn, self.devnum, lun) - return True - - try: - logged_write_line_to_file(portremove, self.wwpn) - except OSError as e: - raise ValueError(_("Could not remove WWPN %(wwpn)s on zFCP " - "device %(devnum)s (%(e)s).") - % {'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): - if port.startswith("0x") and \ - os.path.isdir(os.path.join(devdir, port)): - log.info("Not setting zFCP device %s offline since it still has other ports, e.g. %s.", - self.devnum, port) - return True - else: - # newer zfcp sysfs interface with auto port scan - luns = glob.glob("%s/0x????????????????/0x????????????????" - % (devdir,)) - if len(luns) != 0: - log.info("Not setting zFCP device %s offline since it still has other LUNs, e.g. %s.", - self.devnum, luns[0]) - return True - - # no other WWPNs/LUNs exists for this device number, it's safe to bring it offline - self._set_zfcp_device_offline() - return True @@ -408,7 +284,7 @@ def online_device(self): :raises: ValueError if the device cannot be initialized """ - super().online_device() + self._set_zfcp_device_online() if not has_auto_lun_scan(self.devnum): raise ValueError(_("zFCP device %s cannot use auto LUN scan.") % self) @@ -446,18 +322,22 @@ class zFCP: """ ZFCP utility class. - This class will automatically online to ZFCP drives configured in - /tmp/fcpconfig when the startup() method gets called. It can also be - used to manually configure ZFCP devices through the add_fcp() method. + This class is used to manually configure ZFCP devices through the + add_fcp() method, which is used by the anaconda GUI or by kickstart. - As this class needs to make sure that /tmp/fcpconfig configured + As this class needs to make sure that configured drives are only onlined once and as it keeps a global list of all ZFCP devices it is implemented as a Singleton. + + In particular, this class does not create objects for any other method + that enables ZFCP devices such as rd.zfcp= or any device auto + configuration. These methods make zfcp-attached SCSI disk block devices + available, which ZFCPDiskDevice [devices/disk.py] can directly + discover. """ def __init__(self): self.fcpdevs = set() - self.has_read_config = False self.down = True # So that users can write zfcp() to get the singleton instance @@ -468,46 +348,6 @@ def __deepcopy__(self, memo_dict): # pylint: disable=unused-argument return self - def read_config(self): - try: - f = open(zfcpconf, "r") - except OSError: - log.info("no %s; not configuring zfcp", zfcpconf) - return - - lines = [x.strip().lower() for x in f.readlines()] - f.close() - - for line in lines: - if line.startswith("#") or line == '': - continue - - fields = line.split() - - # zFCP auto LUN scan available - if len(fields) == 1: - devnum = fields[0] - wwpn = None - fcplun = None - elif len(fields) == 3: - devnum = fields[0] - wwpn = fields[1] - fcplun = fields[2] - elif len(fields) == 5: - # support old syntax of: - # devno scsiid wwpn scsilun fcplun - devnum = fields[0] - wwpn = fields[2] - fcplun = fields[4] - else: - log.warning("Invalid line found in %s: %s", zfcpconf, line) - continue - - try: - self.add_fcp(devnum, wwpn, fcplun) - except ValueError as e: - log.warning("%s", str(e)) - def add_fcp(self, devnum, wwpn=None, fcplun=None): if wwpn and fcplun: d = ZFCPDeviceFullPath(devnum, wwpn, fcplun) @@ -533,11 +373,6 @@ def startup(self): if not self.down: return self.down = False - if not self.has_read_config: - self.read_config() - self.has_read_config = True - # read_config calls add_fcp which calls online_device already - return if len(self.fcpdevs) == 0: return @@ -548,16 +383,7 @@ def startup(self): log.warning("%s", str(e)) def write(self, root): - if len(self.fcpdevs) == 0: - return - f = open(root + zfcpconf, "w") - for d in self.fcpdevs: - f.write("%s\n" % (d,)) - f.close() - - f = open(root + "/etc/modprobe.conf", "a") - f.write("alias scsi_hostadapter zfcp\n") - f.close() + pass # Create ZFCP singleton diff --git a/python-blivet.spec b/python-blivet.spec index 80b6f90f2..277974f30 100644 --- a/python-blivet.spec +++ b/python-blivet.spec @@ -25,6 +25,7 @@ Patch0: 0001-remove-btrfs-plugin.patch %global libblockdevver 3.2.0 %global libbytesizever 0.3 %global pyudevver 0.18 +%global s390utilscorever 2.31.0 BuildArch: noarch @@ -73,6 +74,7 @@ Recommends: libblockdev-nvme >= %{libblockdevver} Recommends: libblockdev-part >= %{libblockdevver} Recommends: libblockdev-swap >= %{libblockdevver} Recommends: libblockdev-s390 >= %{libblockdevver} +Recommends: s390utils-core >= %{s390utilscorever} Requires: python3-bytesize >= %{libbytesizever} Requires: util-linux >= %{utillinuxver}