From 6a41ec6d718ecaf31c60bad782c13ee3556bd5a8 Mon Sep 17 00:00:00 2001
From: David Lehman <dlehman@redhat.com>
Date: Fri, 13 Jan 2012 10:35:29 -0600
Subject: [PATCH 4/6] Add a testing flag to allow limited use as non-root.

This reverts commit 7b909ceb20402d18fb8a8bdb6aad1c51c35db30c.
This reverts commit 0b30b8ec7ec3c4dbde75da70d0445194ce22e266.
---
 pyanaconda/baseudev.py                  |    5 +--
 pyanaconda/flags.py                     |    1 +
 pyanaconda/iutil.py                     |   54 +++++++++++-----------
 pyanaconda/platform.py                  |   17 +++-----
 pyanaconda/storage/devices.py           |   22 ++++++++-
 pyanaconda/storage/devicetree.py        |   74 ++++++++++++++----------------
 pyanaconda/storage/formats/disklabel.py |   48 +++++++++++++++++++-
 pyanaconda/storage/udev.py              |    8 +---
 pyanaconda/ui/gui/tools/run-spoke.py    |    1 +
 9 files changed, 137 insertions(+), 93 deletions(-)

diff --git a/pyanaconda/baseudev.py b/pyanaconda/baseudev.py
index 61799b6..3d9ee45 100644
--- a/pyanaconda/baseudev.py
+++ b/pyanaconda/baseudev.py
@@ -82,10 +82,7 @@ def udev_settle():
     # lots of disks, or with slow disks
     argv = ["settle", "--timeout=300"]
 
-    try:
-        iutil.execWithRedirect("udevadm", argv, stderr="/dev/null")
-    except RuntimeError:
-        log.info("Skipping udevadm settle call due to running as non-root.")
+    iutil.execWithRedirect("udevadm", argv, stderr="/dev/null")
 
 def udev_trigger(subsystem=None, action="add"):
     argv = ["trigger", "--action=%s" % action]
diff --git a/pyanaconda/flags.py b/pyanaconda/flags.py
index 958eb18..3066272 100644
--- a/pyanaconda/flags.py
+++ b/pyanaconda/flags.py
@@ -121,6 +121,7 @@ class Flags:
             self.__dict__['flags']['selinux'] = 0
 
         self.__dict__['flags']['nogpt'] = self.__dict__['flags']['cmdline'].has_key("nogpt")
+        self.__dict__['flags']['testing'] = self.__dict__['flags']['cmdline'].has_key("testing")
 
 global flags
 flags = Flags()
diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py
index a8bccb8..4e47420 100644
--- a/pyanaconda/iutil.py
+++ b/pyanaconda/iutil.py
@@ -84,6 +84,11 @@ class tee(threading.Thread):
 # @return The return code of command.
 def execWithRedirect(command, argv, stdin = None, stdout = None,
                      stderr = None, root = '/'):
+    if flags.testing:
+        log.info("not running command because we're testing: %s %s"
+                    (command, " ".join(argv)))
+        return 0
+
     def chroot ():
         os.chroot(root)
 
@@ -102,22 +107,16 @@ def execWithRedirect(command, argv, stdin = None, stdout = None,
         stdin = sys.stdin.fileno()
 
     if isinstance(stdout, str):
-        try:
-            stdout = os.open(stdout, os.O_RDWR|os.O_CREAT)
-            stdoutclose = lambda : os.close(stdout)
-        except OSError:
-            stdout = sys.stdout.fileno()
+        stdout = os.open(stdout, os.O_RDWR|os.O_CREAT)
+        stdoutclose = lambda : os.close(stdout)
     elif isinstance(stdout, int):
         pass
     elif stdout is None or not isinstance(stdout, file):
         stdout = sys.stdout.fileno()
 
     if isinstance(stderr, str):
-        try:
-            stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
-            stderrclose = lambda : os.close(stderr)
-        except OSError:
-            stderr = sys.stderr.fileno()
+        stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
+        stderrclose = lambda : os.close(stderr)
     elif isinstance(stderr, int):
         pass
     elif stderr is None or not isinstance(stderr, file):
@@ -177,7 +176,7 @@ def execWithRedirect(command, argv, stdin = None, stdout = None,
         stdinclose()
         stdoutclose()
         stderrclose()
-        raise RuntimeError(errstr)
+        raise RuntimeError, errstr
 
     return ret
 
@@ -189,6 +188,11 @@ def execWithRedirect(command, argv, stdin = None, stdout = None,
 # @param root The directory to chroot to before running command.
 # @return The output of command from stdout.
 def execWithCapture(command, argv, stdin = None, stderr = None, root='/'):
+    if flags.testing:
+        log.info("not running command because we're testing: %s %s"
+                    (command, " ".join(argv)))
+        return ""
+
     def chroot():
         os.chroot(root)
 
@@ -212,11 +216,8 @@ def execWithCapture(command, argv, stdin = None, stderr = None, root='/'):
         stdin = sys.stdin.fileno()
 
     if isinstance(stderr, str):
-        try:
-            stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
-            stderrclose = lambda : os.close(stderr)
-        except OSError:
-            stderr = sys.stderr.fileno()
+        stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
+        stderrclose = lambda : os.close(stderr)
     elif isinstance(stderr, int):
         pass
     elif stderr is None or not isinstance(stderr, file):
@@ -248,7 +249,7 @@ def execWithCapture(command, argv, stdin = None, stderr = None, root='/'):
     except OSError as e:
         log.error ("Error running " + command + ": " + e.strerror)
         closefds()
-        raise RuntimeError("Error running " + command + ": " + e.strerror)
+        raise RuntimeError, "Error running " + command + ": " + e.strerror
 
     closefds()
     return rc
@@ -256,6 +257,11 @@ def execWithCapture(command, argv, stdin = None, stderr = None, root='/'):
 def execWithCallback(command, argv, stdin = None, stdout = None,
                      stderr = None, echo = True, callback = None,
                      callback_data = None, root = '/'):
+    if flags.testing:
+        log.info("not running command because we're testing: %s %s"
+                    (command, " ".join(argv)))
+        return ExecProduct(0, '', '')
+
     def chroot():
         os.chroot(root)
 
@@ -279,22 +285,16 @@ def execWithCallback(command, argv, stdin = None, stdout = None,
         stdin = sys.stdin.fileno()
 
     if isinstance(stdout, str):
-        try:
-            stdout = os.open(stdout, os.O_RDWR|os.O_CREAT)
-            stdoutclose = lambda : os.close(stdout)
-        except OSError:
-            stdout = sys.stdout.fileno()
+        stdout = os.open(stdout, os.O_RDWR|os.O_CREAT)
+        stdoutclose = lambda : os.close(stdout)
     elif isinstance(stdout, int):
         pass
     elif stdout is None or not isinstance(stdout, file):
         stdout = sys.stdout.fileno()
 
     if isinstance(stderr, str):
-        try:
-            stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
-            stderrclose = lambda : os.close(stderr)
-        except OSError:
-            stderr = sys.stderr.fileno()
+        stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
+        stderrclose = lambda : os.close(stderr)
     elif isinstance(stderr, int):
         pass
     elif stderr is None or not isinstance(stderr, file):
diff --git a/pyanaconda/platform.py b/pyanaconda/platform.py
index ba0a731..bbc6082 100644
--- a/pyanaconda/platform.py
+++ b/pyanaconda/platform.py
@@ -28,9 +28,6 @@ from pyanaconda.storage.devicelibs import mdraid
 import iutil
 from flags import flags
 
-import logging
-log = logging.getLogger("anaconda")
-
 import gettext
 _ = lambda x: gettext.ldgettext("anaconda", x)
 N_ = lambda x: x
@@ -100,6 +97,9 @@ class Platform(object):
 
     def bestDiskLabelType(self, device):
         """The best disklabel type for the specified device."""
+        if flags.testing:
+            return self.defaultDiskLabelType
+
         # if there's a required type for this device type, use that
         labelType = self.requiredDiskLabelType(device.partedDevice.type)
         if not labelType:
@@ -178,14 +178,9 @@ class X86(Platform):
             return 0
 
     def blackListGPT(self):
-        try:
-            buf = iutil.execWithCapture("dmidecode",
-                                        ["-s", "chassis-manufacturer"],
-                                        stderr="/dev/tty5")
-        except (OSError, RuntimeError):
-            log.info("Skipping dmidecode call due to running as non-root.")
-            return
-
+        buf = iutil.execWithCapture("dmidecode",
+                                    ["-s", "chassis-manufacturer"],
+                                    stderr="/dev/tty5")
         if "LENOVO" in buf.splitlines() and "gpt" in self._disklabel_types:
             self._disklabel_types.remove("gpt")
 
diff --git a/pyanaconda/storage/devices.py b/pyanaconda/storage/devices.py
index 15c3e49..7802464 100644
--- a/pyanaconda/storage/devices.py
+++ b/pyanaconda/storage/devices.py
@@ -473,7 +473,7 @@ class StorageDevice(Device):
         self.bus = bus
 
         self.protected = False
-        self.controllable = True
+        self.controllable = not flags.testing
 
         self.format = format
         self.originalFormat = self.format
@@ -482,6 +482,17 @@ class StorageDevice(Device):
 
         self._partedDevice = None
 
+        if self.exists and flags.testing and not self._size:
+            def read_int_from_sys(path):
+                return int(open(path).readline().strip())
+
+            device_root = "/sys/class/block/%s" % self.name
+            if os.path.exists("%s/queue" % device_root):
+                sector_size = read_int_from_sys("%s/queue/logical_block_size"
+                                                % device_root)
+                size = read_int_from_sys("%s/size" % device_root)
+                self._size = (size * sector_size) / (1024.0 * 1024.0)
+
     def __str__(self):
         exist = "existing"
         if not self.exists:
@@ -1040,6 +1051,9 @@ class DiskDevice(StorageDevice):
 
     @property
     def mediaPresent(self):
+        if flags.testing:
+            return True
+
         if not self.partedDevice:
             return False
 
@@ -1149,7 +1163,7 @@ class PartitionDevice(StorageDevice):
         #        For existing partitions we will get the size from
         #        parted.
 
-        if self.exists:
+        if self.exists and not flags.testing:
             log.debug("looking up parted Partition: %s" % self.path)
             self._partedPartition = self.disk.format.partedDisk.getPartitionByPath(self.path)
             if not self._partedPartition:
@@ -2749,7 +2763,7 @@ class MDRaidArrayDevice(StorageDevice):
         if not self.formatClass:
             raise DeviceError("cannot find class for 'mdmember'", self.name)
 
-        if self.exists and self.uuid:
+        if self.exists and self.uuid and not flags.testing:
             # this is a hack to work around mdadm's insistence on giving
             # really high minors to arrays it has no config entry for
             open("/etc/mdadm.conf", "a").write("ARRAY %s UUID=%s\n"
@@ -3129,6 +3143,8 @@ class MDRaidArrayDevice(StorageDevice):
         # BIOS RAID sets should show as present even when teared down
         elif self.type == "mdbiosraidarray":
             return True
+        elif flags.testing:
+            return True
         else:
             return self.partedDevice is not None
 
diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py
index 5fcc063..c45175d 100644
--- a/pyanaconda/storage/devicetree.py
+++ b/pyanaconda/storage/devicetree.py
@@ -1794,47 +1794,43 @@ class DeviceTree(object):
             self.protectedDevNames.append(livetarget)
 
         cfg = self.__multipathConfigWriter.write(self.mpathFriendlyNames)
-        old_devices = {}
-
-        if os.access("/etc/multipath.conf", os.W_OK):
-            with open("/etc/multipath.conf", "w+") as mpath_cfg:
-                mpath_cfg.write(cfg)
+        with open("/etc/multipath.conf", "w+") as mpath_cfg:
+            mpath_cfg.write(cfg)
 
-            self.topology = devicelibs.mpath.MultipathTopology(udev_get_block_devices())
-            log.info("devices to scan: %s" %
-                     [d['name'] for d in self.topology.devices_iter()])
-            for dev in self.topology.devices_iter():
-                old_devices[dev['name']] = dev
-                self.addUdevDevice(dev)
-
-            # Having found all the disks, we can now find all the multipaths built
-            # upon them.
-            whitelist = []
-            mpaths = self.__multipaths.values()
-            mpaths.sort(key=lambda d: d.name)
-            for mp in mpaths:
-                log.info("adding mpath device %s" % mp.name)
-                mp.setup()
-                mp.updateSysfsPath()
-                mp_info = udev_get_block_device(mp.sysfsPath)
-                if mp_info is None or self.isIgnored(mp_info):
-                    mp.teardown()
-                    continue
+        self.topology = devicelibs.mpath.MultipathTopology(udev_get_block_devices())
+        log.info("devices to scan: %s" %
+                 [d['name'] for d in self.topology.devices_iter()])
+        old_devices = {}
+        for dev in self.topology.devices_iter():
+            old_devices[dev['name']] = dev
+            self.addUdevDevice(dev)
+
+        # Having found all the disks, we can now find all the multipaths built
+        # upon them.
+        whitelist = []
+        mpaths = self.__multipaths.values()
+        mpaths.sort(key=lambda d: d.name)
+        for mp in mpaths:
+            log.info("adding mpath device %s" % mp.name)
+            mp.setup()
+            mp.updateSysfsPath()
+            mp_info = udev_get_block_device(mp.sysfsPath)
+            if mp_info is None or self.isIgnored(mp_info):
+                mp.teardown()
+                continue
 
-                whitelist.append(mp.name)
-                for p in mp.parents:
-                    whitelist.append(p.name)
-                self.__multipathConfigWriter.addMultipathDevice(mp)
-                self._addDevice(mp)
-                self.addUdevDevice(mp_info)
-            for d in self.devices:
-                if not d.name in whitelist:
-                    self.__multipathConfigWriter.addBlacklistDevice(d)
-            cfg = self.__multipathConfigWriter.write(self.mpathFriendlyNames)
-            with open("/etc/multipath.conf", "w+") as mpath_cfg:
-                mpath_cfg.write(cfg)
-        else:
-            log.info("Skipping multipath detection due to running as non-root.")
+            whitelist.append(mp.name)
+            for p in mp.parents:
+                whitelist.append(p.name)
+            self.__multipathConfigWriter.addMultipathDevice(mp)
+            self._addDevice(mp)
+            self.addUdevDevice(mp_info)
+        for d in self.devices:
+            if not d.name in whitelist:
+                self.__multipathConfigWriter.addBlacklistDevice(d)
+        cfg = self.__multipathConfigWriter.write(self.mpathFriendlyNames)
+        with open("/etc/multipath.conf", "w+") as mpath_cfg:
+            mpath_cfg.write(cfg)
 
         # Now, loop and scan for devices that have appeared since the two above
         # blocks or since previous iterations.
diff --git a/pyanaconda/storage/formats/disklabel.py b/pyanaconda/storage/formats/disklabel.py
index dbae626..c9509cf 100644
--- a/pyanaconda/storage/formats/disklabel.py
+++ b/pyanaconda/storage/formats/disklabel.py
@@ -23,6 +23,8 @@
 import os
 import copy
 
+from pyanaconda.flags import flags
+
 from pyanaconda.anaconda_log import log_method_call
 import parted
 import _ped
@@ -60,7 +62,7 @@ class DiskLabel(DeviceFormat):
         if not self.exists:
             self._labelType = kwargs.get("labelType", "msdos")
         else:
-            self._labelType = None
+            self._labelType = ""
 
         self._size = None
 
@@ -93,6 +95,8 @@ class DiskLabel(DeviceFormat):
 
     def __repr__(self):
         s = DeviceFormat.__repr__(self)
+        if flags.testing:
+            return s
         s += ("  type = %(type)s  partition count = %(count)s"
               "  sectorSize = %(sectorSize)s\n"
               "  align_offset = %(offset)s  align_grain = %(grain)s\n"
@@ -114,6 +118,9 @@ class DiskLabel(DeviceFormat):
     @property
     def dict(self):
         d = super(DiskLabel, self).dict
+        if flags.testing:
+            return d
+
         d.update({"labelType": self.labelType,
                   "partitionCount": len(self.partitions),
                   "sectorSize": self.partedDevice.sectorSize,
@@ -182,7 +189,11 @@ class DiskLabel(DeviceFormat):
     @property
     def labelType(self):
         """ The disklabel type (eg: 'gpt', 'msdos') """
-        return self.partedDisk.type
+        try:
+            lt = self.partedDisk.type
+        except Exception:
+            lt = self._labelType
+        return lt
 
     @property
     def name(self):
@@ -325,6 +336,14 @@ class DiskLabel(DeviceFormat):
             parts = self.partedDisk.partitions
         except Exception:
             parts = []
+            if flags.testing:
+                sys_block_root = "/sys/class/block/"
+
+                # FIXME: /dev/mapper/foo won't work without massaging
+                disk_name = self.device.split("/")[-1]
+
+                disk_root = sys_block_root + disk_name
+                parts = [n for n in os.listdir(disk_root) if n.startswith(disk_name)]
         return parts
 
     @property
@@ -369,8 +388,31 @@ class DiskLabel(DeviceFormat):
 
     @property
     def free(self):
-        return sum([f.getSize()
+        def read_int_from_sys(path):
+            return int(open(path).readline().strip())
+
+        try:
+            free = sum([f.getSize()
                         for f in self.partedDisk.getFreeSpacePartitions()])
+        except Exception:
+            sys_block_root = "/sys/class/block/"
+
+            # FIXME: /dev/mapper/foo won't work without massaging
+            disk_name = self.device.split("/")[-1]
+
+            disk_root = sys_block_root + disk_name
+            disk_length = read_int_from_sys("%s/size" % disk_root)
+            sector_size = read_int_from_sys("%s/queue/logical_block_size" % disk_root)
+            partition_names = [n for n in os.listdir(disk_root) if n.startswith(disk_name)]
+            used_sectors = 0
+            for partition_name in partition_names:
+                partition_root = sys_block_root + partition_name
+                partition_length = read_int_from_sys("%s/size" % partition_root)
+                used_sectors += partition_length
+
+            free = ((disk_length - used_sectors) * sector_size) / (1024.0 * 1024.0)
+
+        return free
 
 register_device_format(DiskLabel)
 
diff --git a/pyanaconda/storage/udev.py b/pyanaconda/storage/udev.py
index 53b6faf..7464974 100644
--- a/pyanaconda/storage/udev.py
+++ b/pyanaconda/storage/udev.py
@@ -83,14 +83,10 @@ def udev_resolve_glob(glob):
 
 def udev_get_block_devices():
     # Wait for scsi adapters to be done with scanning their busses (#583143)
-    try:
-        iutil.execWithRedirect("modprobe", [ "scsi_wait_scan" ],
+    iutil.execWithRedirect("modprobe", [ "scsi_wait_scan" ],
                                stdout = "/dev/tty5", stderr="/dev/tty5")
-        iutil.execWithRedirect("rmmod", [ "scsi_wait_scan" ],
+    iutil.execWithRedirect("rmmod", [ "scsi_wait_scan" ],
                                stdout = "/dev/tty5", stderr="/dev/tty5")
-    except (OSError, RuntimeError):
-        log.info("Skipping scsi_wait_scan due to running as non-root.")
-
     udev_settle()
     entries = []
     for path in udev_enumerate_block_devices():
diff --git a/pyanaconda/ui/gui/tools/run-spoke.py b/pyanaconda/ui/gui/tools/run-spoke.py
index 1546ff1..0edf2ab 100755
--- a/pyanaconda/ui/gui/tools/run-spoke.py
+++ b/pyanaconda/ui/gui/tools/run-spoke.py
@@ -17,6 +17,7 @@ from pykickstart.version import makeVersion
 # Don't worry with fcoe, iscsi, dasd, any of that crud.
 from pyanaconda.flags import flags
 flags.imageInstall = True
+flags.testing = True
 
 # NOTE:  To run your spoke, you need to do the proper import here (may need to
 # set $PYTHONPATH as well) and set spokeClass to be the class from that import.
-- 
1.7.6

