From ce4af804f42319e3dcaed1673c12b24fe6b5adcc Mon Sep 17 00:00:00 2001
From: "Samantha N. Bueno" <sbueno+anaconda@redhat.com>
Date: Thu, 20 Feb 2014 15:50:02 -0500
Subject: [PATCH] Re-write the DASD storage code. (#1001070)

This gets rid of the DASD class and instead introduces a small
number of functions to interface with and manage DASDs. This is
completely divorced from the anaconda code and much more
lightweight.

(The corresponding Fedora bug for this is 859997.)

Resolves:rhbz#1001070

---
 blivet/__init__.py        |  13 ++-
 blivet/dasd.py            | 222 ----------------------------------------------
 blivet/devicelibs/dasd.py | 123 +++++++++++++++++++++++++
 blivet/devicetree.py      |   6 +-
 4 files changed, 132 insertions(+), 232 deletions(-)
 delete mode 100644 blivet/dasd.py
 create mode 100644 blivet/devicelibs/dasd.py

diff --git a/blivet/__init__.py b/blivet/__init__.py
index 6908b2a..b56e929 100644
--- a/blivet/__init__.py
+++ b/blivet/__init__.py
@@ -77,11 +77,11 @@ import devicefactory
 from devicelibs.dm import name_from_dm_node
 from devicelibs.crypto import generateBackupPassphrase
 from devicelibs.edd import get_edd_dict
+from devicelibs.dasd import make_dasd_list, write_dasd_conf
 from udev import udev_trigger
 import iscsi
 import fcoe
 import zfcp
-import dasd
 import util
 import arch
 from flags import flags
@@ -283,6 +283,7 @@ class Blivet(object):
         self.encryptionRetrofit = False
         self.autoPartitionRequests = []
         self.eddDict = {}
+        self.dasd = []
 
         self.__luksDevs = {}
         self.size_sets = []
@@ -291,7 +292,6 @@ class Blivet(object):
         self.iscsi = iscsi.iscsi()
         self.fcoe = fcoe.fcoe()
         self.zfcp = zfcp.ZFCP()
-        self.dasd = dasd.DASD()
 
         self._nextID = 0
         self._dumpFile = "%s/storage.state" % tempfile.gettempdir()
@@ -399,12 +399,11 @@ class Blivet(object):
             self.iscsi.startup()
             self.fcoe.startup()
             self.zfcp.startup()
-            self.dasd.startup(None,
-                              self.config.exclusiveDisks,
-                              self.config.initializeDisks)
+            self.dasd = make_dasd_list(self.dasd, self.devices)
+
         if self.dasd:
             # Reset the internal dasd list (823534)
-            self.dasd.clear_device_list()
+            self.dasd = []
 
         self.devicetree.reset(conf=self.config,
                               passphrase=self.encryptionPassphrase,
@@ -1672,7 +1671,7 @@ class Blivet(object):
         self.iscsi.write(ROOT_PATH, self)
         self.fcoe.write(ROOT_PATH)
         self.zfcp.write(ROOT_PATH)
-        self.dasd.write(ROOT_PATH)
+        write_dasd_conf(self.dasd, ROOT_PATH)
 
     def turnOnSwap(self, upgrading=None):
         self.fsset.turnOnSwap(rootPath=ROOT_PATH,
diff --git a/blivet/dasd.py b/blivet/dasd.py
deleted file mode 100644
index c2ec2e4..0000000
--- a/blivet/dasd.py
+++ /dev/null
@@ -1,222 +0,0 @@
-#
-# dasd.py - DASD class
-#
-# Copyright (C) 2009, 2010  Red Hat, Inc.  All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Red Hat Author(s): David Cantrell <dcantrell@redhat.com>
-#
-
-import sys
-import os
-from .errors import DasdFormatError
-from .devices import deviceNameToDiskByPath
-from . import util
-from . import arch
-from .udev import udev_trigger
-
-import logging
-log = logging.getLogger("blivet")
-
-import gettext
-_ = lambda x: gettext.ldgettext("blivet", x)
-P_ = lambda x, y, z: gettext.ldngettext("blivet", x, y, z)
-
-def getDasdPorts():
-    """ Return comma delimited string of valid DASD ports. """
-    ports = []
-
-    f = open("/proc/dasd/devices", "r")
-    lines = map(lambda x: x.strip(), f.readlines())
-    f.close()
-
-    for line in lines:
-        if "unknown" in line:
-            continue
-
-        if "(FBA )" in line or "(ECKD)" in line:
-            ports.append(line.split('(')[0])
-
-    return ','.join(ports)
-
-class DASD:
-    """ Controlling class for DASD interaction before the storage code in
-        anaconda has initialized.
-
-        The DASD class can determine if any DASD devices on the system are
-        unformatted and can perform a dasdfmt on them.
-    """
-
-    def __init__(self):
-        self._dasdlist = []
-        self._devices = []                  # list of DASDDevice objects
-        self.totalCylinders = 0
-        self._completedCylinders = 0.0
-        self._maxFormatJobs = 0
-        self.dasdfmt = "/sbin/dasdfmt"
-        self.commonArgv = ["-y", "-d", "cdl", "-b", "4096"]
-        self.started = False
-
-    def __call__(self):
-        return self
-
-    def startup(self, intf, exclusiveDisks, zeroMbr):
-        """ Look for any unformatted DASDs in the system and offer the user
-            the option for format them with dasdfmt or exit the installer.
-        """
-        if self.started:
-            return
-
-        self.started = True
-
-        if not arch.isS390():
-            return
-
-        # Trigger udev data about the dasd devices on the system
-        udev_trigger(action="change", name="dasd*")
-
-        log.info("Checking for unformatted DASD devices:")
-
-        for device in os.listdir("/sys/block"):
-            if not device.startswith("dasd"):
-                continue
-
-            statusfile = "/sys/block/%s/device/status" % (device,)
-            if not os.path.isfile(statusfile):
-                continue
-
-            f = open(statusfile, "r")
-            status = f.read().strip()
-            f.close()
-
-            if status in ["unformatted"] and device not in exclusiveDisks:
-                bypath = deviceNameToDiskByPath(device)
-                if not bypath:
-                    bypath = "/dev/" + device
-
-                log.info("    %s (%s) status is %s, needs dasdfmt" % (device,
-                                                                      bypath,
-                                                                      status,))
-                self._dasdlist.append((device, bypath))
-
-        if not len(self._dasdlist):
-            log.info("    no unformatted DASD devices found")
-            return
-
-        askUser = True
-
-        if zeroMbr:
-            askUser = False
-        elif not intf and not zeroMbr:
-            log.info("    non-interactive kickstart install without zerombr "
-                     "command, unable to run dasdfmt, exiting installer")
-            sys.exit(0)
-
-        c = len(self._dasdlist)
-
-        if intf and askUser:
-            devs = ''
-            for dasd, bypath in self._dasdlist:
-                devs += "%s\n" % (bypath,)
-
-            rc = intf.questionInitializeDASD(c, devs)
-            if rc == 1:
-                log.info("    not running dasdfmt, continuing installation")
-                return
-
-        # gather total cylinder count
-        argv = ["-t", "-v"] + self.commonArgv
-        for dasd, bypath in self._dasdlist:
-            buf = util.capture_output([self.dasdfmt, argv, "/dev/" + dasd])
-            for line in buf.splitlines():
-                if line.startswith("Drive Geometry: "):
-                    # line will look like this:
-                    # Drive Geometry: 3339 Cylinders * 15 Heads =  50085 Tracks
-                    cyls = long(filter(lambda s: s, line.split(' '))[2])
-                    self.totalCylinders += cyls
-                    break
-
-        # format DASDs
-        argv = ["-P"] + self.commonArgv
-        update = self._updateProgressWindow
-
-        title = P_("Formatting DASD Device", "Formatting DASD Devices", c)
-        msg = P_("Preparing %d DASD device for use with Linux..." % c,
-                 "Preparing %d DASD devices for use with Linux..." % c, c)
-
-        if intf:
-            if self.totalCylinders:
-                pw = intf.progressWindow(title, msg, 1.0)
-            else:
-                pw = intf.progressWindow(title, msg, 100, pulse=True)
-
-        for dasd, bypath in self._dasdlist:
-            log.info("Running dasdfmt on %s" % (bypath,))
-            arglist = argv + ["/dev/" + dasd]
-
-            try:
-                rc = util.run_program([self.dasdfmt] + arglist)
-            except Exception as e:
-                raise DasdFormatError(e, bypath)
-
-            if rc:
-                raise DasdFormatError("dasdfmt failed: %s" % rc, bypath)
-
-        if intf:
-            pw.pop()
-
-    def addDASD(self, dasd):
-        """ Adds a DASDDevice to the internal list of DASDs. """
-        if dasd and dasd not in self._devices:
-            self._devices.append(dasd)
-
-    def removeDASD(self, dasd):
-        """ Removes a DASDDevice from the internal list of DASDs. """
-        if dasd and dasd in self._devices:
-            self._devices.remove(dasd)
-
-    def clear_device_list(self):
-        """ Clear the device list to force re-populate on next access. """
-        self._devices = []
-
-    def write(self, ROOT_PATH):
-        """ Write /etc/dasd.conf to target system for all DASD devices
-            configured during installation.
-        """
-        if self._devices == []:
-            return
-
-        f = open(os.path.realpath(ROOT_PATH + "/etc/dasd.conf"), "w")
-        for dasd in sorted(self._devices, key=lambda d: d.name):
-            fields = [dasd.busid] + dasd.getOpts()
-            f.write("%s\n" % (" ".join(fields),))
-        f.close()
-
-    def _updateProgressWindow(self, data, callback_data=None):
-        """ Reads progress output from dasdfmt and collects the number of
-            cylinders completed so the progress window can update.
-        """
-        if not callback_data:
-            return
-
-        if data == '\n':
-            # each newline we see in this output means one more cylinder done
-            self._completedCylinders += 1.0
-            callback_data.set(self._completedCylinders / self.totalCylinders)
-
-# Create DASD singleton
-DASD = DASD()
-
-# vim:tw=78:ts=4:et:sw=4
diff --git a/blivet/devicelibs/dasd.py b/blivet/devicelibs/dasd.py
new file mode 100644
index 0000000..3f7fff1
--- /dev/null
+++ b/blivet/devicelibs/dasd.py
@@ -0,0 +1,123 @@
+#
+# dasd.py - DASD functions
+#
+# Copyright (C) 2013 Red Hat, Inc.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Red Hat Author(s): Samantha N. Bueno
+#
+
+import os
+from blivet.errors import DasdFormatError
+from blivet.devices import deviceNameToDiskByPath
+from blivet import util
+from blivet import arch
+
+import logging
+log = logging.getLogger("blivet")
+
+import gettext
+_ = lambda x: gettext.ldgettext("blivet", x)
+P_ = lambda x, y, z: gettext.ldngettext("blivet", x, y, z)
+
+def get_dasd_ports():
+    """ Return comma delimited string of valid DASD ports. """
+    ports = []
+
+    with open("/proc/dasd/devices", "r") as f:
+        lines = (line.strip() for line in f.readlines())
+
+    for line in lines:
+        if "unknown" in line:
+            continue
+
+        if "(FBA )" in line or "(ECKD)" in line:
+            ports.append(line.split('(')[0])
+
+    return ','.join(ports)
+
+def format_dasd(dasd):
+    """ Run dasdfmt on a DASD. Aside from one type of device noted below, this
+        function _does not_ check if a DASD needs to be formatted, but rather,
+        assumes the list passed needs formatting.
+
+        We don't need to show or update any progress bars, since disk actions
+        will be taking place all in the progress hub, which is just one big
+        progress bar.
+    """
+    try:
+        rc = util.run_program(["/sbin/dasdfmt", "-y", "-d", "cdl", "-b", "4096", "/dev/" + dasd])
+    except Exception as err:
+        raise DasdFormatError(err)
+
+    if rc:
+        raise DasdFormatError("dasdfmt failed: %s" % rc)
+
+def make_dasd_list(dasds, disks):
+    """ Create a list of DASDs recognized by the system. """
+    if not arch.isS390():
+        return
+
+    log.info("Generating DASD list...")
+    for dev in (d for d in disks if d.type == "dasd"):
+        if dev not in dasds:
+            dasds.append(dev)
+
+    return dasds
+
+def make_unformatted_dasd_list(dasds):
+    """ Return a list of DASDS which are not formatted. """
+    unformatted = []
+
+    for dasd in dasds:
+        if dasd_needs_format(dasd):
+            unformatted.append(dasd)
+
+    return unformatted
+
+def dasd_needs_format(dasd):
+    """ Check if a DASD needs to have dasdfmt run against it or not.
+        Return True if we do need dasdfmt, False if not.
+    """
+    statusfile = "/sys/block/%s/device/status" % (dasd,)
+    if not os.path.isfile(statusfile):
+        return False
+
+    with open(statusfile, "r") as f:
+        status = f.read().strip()
+
+    if status in ["unformatted"]:
+        bypath = deviceNameToDiskByPath(dasd)
+        if not bypath:
+            bypath = "/dev/" + dasd
+
+        log.info("  %s (%s) status is %s, needs dasdfmt" % (dasd, bypath,
+                                                            status,))
+        return True
+
+    return False
+
+
+def write_dasd_conf(disks, ROOT_PATH):
+    """ Write /etc/dasd.conf to target system for all DASD devices
+        configured during installation.
+    """
+    if disks == {}:
+        return
+
+    with open(os.path.realpath(ROOT_PATH + "/etc/dasd.conf"), "w") as f:
+        for dasd in sorted(disks, key=lambda d: d.name):
+            fields = [dasd.busid] + dasd.getOpts()
+            f.write("%s\n" % " ".join(fields),)
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
index 8a79f14..fa28898 100644
--- a/blivet/devicetree.py
+++ b/blivet/devicetree.py
@@ -946,7 +946,7 @@ class DeviceTree(object):
             info["ID_FS_TYPE"] = "multipath_member"
 
         if diskType == DASDDevice:
-            self.dasd.addDASD(device)
+            self.dasd.append(device)
 
         self._addDevice(device)
         return device
@@ -1830,7 +1830,7 @@ class DeviceTree(object):
         lvm.lvm_cc_addFilterRejectRegexp(device.name)
 
         if isinstance(device, DASDDevice):
-            self.dasd.removeDASD(device)
+            self.dasd.remove(device)
 
     def unhide(self, device):
         # the hidden list should be in leaves-first order
@@ -1846,7 +1846,7 @@ class DeviceTree(object):
                     parent.addChild()
 
                 if isinstance(device, DASDDevice):
-                    self.dasd.addDASD(device)
+                    self.dasd.append(device)
 
     def setupDiskImages(self):
         """ Set up devices to represent the disk image files. """
-- 
1.8.3.1

