From f0dd65d2f4b29a145abda356d180822b1d89d06e Mon Sep 17 00:00:00 2001
From: "Samantha N. Bueno" <sbueno+anaconda@redhat.com>
Date: Wed, 23 Mar 2016 22:49:49 -0400
Subject: [PATCH 3/7] Move StorageDiscoveryConfig and installer-specific
 functions to osinstall

Begin the process of isolating installer-specific code by moving a good
chunk of it to osinstall. Eventually this will all get moved to anaconda,
but this is an interim step.

disk_images, exclusive_disks, and ignored_disks are two attrs from
StorageDiscoveryConfig that were removed, since they are not specific to
the installer. These are now in Blivet class.
---
 blivet/blivet.py    | 263 -------------------------------------------------
 blivet/osinstall.py | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 279 insertions(+), 263 deletions(-)

diff --git a/blivet/blivet.py b/blivet/blivet.py
index 97fbd68..9e216d4 100644
--- a/blivet/blivet.py
+++ b/blivet/blivet.py
@@ -260,31 +260,6 @@ class Blivet(object, metaclass=SynchronizedMeta):
         self.update_bootloader_disk_list()
 
     @property
-    def unused_devices(self):
-        used_devices = []
-        for root in self.roots:
-            for device in list(root.mounts.values()) + root.swaps:
-                if device not in self.devices:
-                    continue
-
-                used_devices.extend(device.ancestors)
-
-        for new in [d for d in self.devicetree.leaves if not d.format.exists]:
-            if new.format.mountable and not new.format.mountpoint:
-                continue
-
-            used_devices.extend(new.ancestors)
-
-        for device in self.partitions:
-            if getattr(device, "is_logical", False):
-                extended = device.disk.format.extended_partition.path
-                used_devices.append(self.devicetree.get_device_by_path(extended))
-
-        used = set(used_devices)
-        _all = set(self.devices)
-        return list(_all.difference(used))
-
-    @property
     def devices(self):
         """ A list of all the devices in the device tree. """
         devices = self.devicetree.devices
@@ -466,100 +441,6 @@ class Blivet(object, metaclass=SynchronizedMeta):
         swaps.sort(key=lambda d: d.name)
         return swaps
 
-    def should_clear(self, device, **kwargs):
-        """ Return True if a clearpart settings say a device should be cleared.
-
-            :param device: the device (required)
-            :type device: :class:`~.devices.StorageDevice`
-            :keyword clear_part_type: overrides :attr:`self.config.clear_part_type`
-            :type clear_part_type: int
-            :keyword clear_part_disks: overrides
-                                     :attr:`self.config.clear_part_disks`
-            :type clear_part_disks: list
-            :keyword clear_part_devices: overrides
-                                       :attr:`self.config.clear_part_devices`
-            :type clear_part_devices: list
-            :returns: whether or not clear_partitions should remove this device
-            :rtype: bool
-        """
-        clear_part_type = kwargs.get("clear_part_type", self.config.clear_part_type)
-        clear_part_disks = kwargs.get("clear_part_disks",
-                                      self.config.clear_part_disks)
-        clear_part_devices = kwargs.get("clear_part_devices",
-                                        self.config.clear_part_devices)
-
-        for disk in device.disks:
-            # this will not include disks with hidden formats like multipath
-            # and firmware raid member disks
-            if clear_part_disks and disk.name not in clear_part_disks:
-                return False
-
-        if not self.config.clear_non_existent:
-            if (device.is_disk and not device.format.exists) or \
-               (not device.is_disk and not device.exists):
-                return False
-
-        # the only devices we want to clear when clear_part_type is
-        # CLEARPART_TYPE_NONE are uninitialized disks, or disks with no
-        # partitions, in clear_part_disks, and then only when we have been asked
-        # to initialize disks as needed
-        if clear_part_type in [CLEARPART_TYPE_NONE, None]:
-            if not self.config.initialize_disks or not device.is_disk:
-                return False
-
-            if not empty_device(device):
-                return False
-
-        if isinstance(device, PartitionDevice):
-            # Never clear the special first partition on a Mac disk label, as
-            # that holds the partition table itself.
-            # Something similar for the third partition on a Sun disklabel.
-            if device.is_magic:
-                return False
-
-            # We don't want to fool with extended partitions, freespace, &c
-            if not device.is_primary and not device.is_logical:
-                return False
-
-            if clear_part_type == CLEARPART_TYPE_LINUX and \
-               not device.format.linux_native and \
-               not device.get_flag(parted.PARTITION_LVM) and \
-               not device.get_flag(parted.PARTITION_RAID) and \
-               not device.get_flag(parted.PARTITION_SWAP):
-                return False
-        elif device.is_disk:
-            if device.partitioned and clear_part_type != CLEARPART_TYPE_ALL:
-                # if clear_part_type is not CLEARPART_TYPE_ALL but we'll still be
-                # removing every partition from the disk, return True since we
-                # will want to be able to create a new disklabel on this disk
-                if not empty_device(device):
-                    return False
-
-            # Never clear disks with hidden formats
-            if device.format.hidden:
-                return False
-
-            # When clear_part_type is CLEARPART_TYPE_LINUX and a disk has non-
-            # linux whole-disk formatting, do not clear it. The exception is
-            # the case of an uninitialized disk when we've been asked to
-            # initialize disks as needed
-            if (clear_part_type == CLEARPART_TYPE_LINUX and
-                not ((self.config.initialize_disks and
-                      empty_device(device)) or
-                     (not device.partitioned and device.format.linux_native))):
-                return False
-
-        # Don't clear devices holding install media.
-        descendants = self.devicetree.get_dependent_devices(device)
-        if device.protected or any(d.protected for d in descendants):
-            return False
-
-        if clear_part_type == CLEARPART_TYPE_LIST and \
-           device.name not in clear_part_devices:
-            return False
-
-        return True
-
     def recursive_remove(self, device):
         """ Remove a device after removing its dependent devices.
 
@@ -571,42 +452,6 @@ class Blivet(object, metaclass=SynchronizedMeta):
         """
         self.devicetree.recursive_remove(device)
 
-    def clear_partitions(self):
-        """ Clear partitions and dependent devices from disks.
-
-            This is also where zerombr is handled.
-        """
-        # Sort partitions by descending partition number to minimize confusing
-        # things like multiple "destroy sda5" actions due to parted renumbering
-        # partitions. This can still happen through the UI but it makes sense to
-        # avoid it where possible.
-        partitions = sorted(self.partitions,
-                            key=lambda p: p.parted_partition.number,
-                            reverse=True)
-        for part in partitions:
-            log.debug("clearpart: looking at %s", part.name)
-            if not self.should_clear(part):
-                continue
-
-            self.recursive_remove(part)
-            log.debug("partitions: %s", [p.getDeviceNodeName() for p in part.parted_partition.disk.partitions])
-
-        # now remove any empty extended partitions
-        self.remove_empty_extended_partitions()
-
-        # ensure all disks have appropriate disklabels
-        for disk in self.disks:
-            zerombr = (self.config.zero_mbr and disk.format.type is None)
-            should_clear = self.should_clear(disk)
-            if should_clear:
-                self.recursive_remove(disk)
-
-            if zerombr or should_clear:
-                log.debug("clearpart: initializing %s", disk.name)
-                self.initialize_disk(disk)
-
-        self.update_bootloader_disk_list()
-
     def initialize_disk(self, disk):
         """ (Re)initialize a disk by creating a disklabel on it.
 
@@ -1135,44 +980,6 @@ class Blivet(object, metaclass=SynchronizedMeta):
         for action in actions:
             self.devicetree.actions.add(action)
 
-    def format_by_default(self, device):
-        """Return whether the device should be reformatted by default."""
-        formatlist = ['/boot', '/var', '/tmp', '/usr']
-        exceptlist = ['/home', '/usr/local', '/opt', '/var/www']
-
-        if not device.format.linux_native:
-            return False
-
-        if device.format.mountable:
-            if not device.format.mountpoint:
-                return False
-
-            if device.format.mountpoint == "/" or \
-               device.format.mountpoint in formatlist:
-                return True
-
-            for p in formatlist:
-                if device.format.mountpoint.startswith(p):
-                    for q in exceptlist:
-                        if device.format.mountpoint.startswith(q):
-                            return False
-                    return True
-        elif device.format.type == "swap":
-            return True
-
-        # be safe for anything else and default to off
-        return False
-
-    def must_format(self, device):
-        """ Return a string explaining why the device must be reformatted.
-
-            Return None if the device need not be reformatted.
-        """
-        if device.format.mountable and device.format.mountpoint == "/":
-            return _("You must create a new filesystem on the root device.")
-
-        return None
-
     def safe_device_name(self, name):
         """ Convert a device name to something safe and return that.
 
@@ -1377,25 +1184,6 @@ class Blivet(object, metaclass=SynchronizedMeta):
                 fields = [dasd.busid] + dasd.get_opts()
                 f.write("%s\n" % " ".join(fields),)
 
-    def turn_on_swap(self):
-        self.fsset.turn_on_swap(root_path=get_sysroot())
-
-    def mount_filesystems(self, read_only=None, skip_root=False):
-        self.fsset.mount_filesystems(root_path=get_sysroot(),
-                                     read_only=read_only, skip_root=skip_root)
-
-    def umount_filesystems(self, swapoff=True):
-        self.fsset.umount_filesystems(swapoff=swapoff)
-
-    def parse_fstab(self, chroot=None):
-        self.fsset.parse_fstab(chroot=chroot)
-
-    def mk_dev_root(self):
-        self.fsset.mk_dev_root()
-
-    def create_swap_file(self, device, size):
-        self.fsset.create_swap_file(device, size)
-
     @property
     def bootloader(self):
         if self._bootloader is None and flags.installer_mode:
@@ -1523,23 +1311,6 @@ class Blivet(object, metaclass=SynchronizedMeta):
     def root_device(self):
         return self.fsset.root_device
 
-    def make_mtab(self):
-        path = "/etc/mtab"
-        target = "/proc/self/mounts"
-        path = os.path.normpath("%s/%s" % (get_sysroot(), path))
-
-        if os.path.islink(path):
-            # return early if the mtab symlink is already how we like it
-            current_target = os.path.normpath(os.path.dirname(path) +
-                                              "/" + os.readlink(path))
-            if current_target == target:
-                return
-
-        if os.path.exists(path):
-            os.unlink(path)
-
-        os.symlink(target, path)
-
     def compare_disks(self, first, second):
         if not isinstance(first, str):
             first = first.name
@@ -1829,37 +1600,3 @@ class Blivet(object, metaclass=SynchronizedMeta):
         self._free_space_snapshot = self.get_free_space()
 
         return self._free_space_snapshot
-
-    def add_fstab_swap(self, device):
-        """
-        Add swap device to the list of swaps that should appear in the fstab.
-
-        :param device: swap device that should be added to the list
-        :type device: blivet.devices.StorageDevice instance holding a swap format
-
-        """
-
-        self.fsset.add_fstab_swap(device)
-
-    def remove_fstab_swap(self, device):
-        """
-        Remove swap device from the list of swaps that should appear in the fstab.
-
-        :param device: swap device that should be removed from the list
-        :type device: blivet.devices.StorageDevice instance holding a swap format
-
-        """
-
-        self.fsset.remove_fstab_swap(device)
-
-    def set_fstab_swaps(self, devices):
-        """
-        Set swap devices that should appear in the fstab.
-
-        :param devices: iterable providing devices that should appear in the fstab
-        :type devices: iterable providing blivet.devices.StorageDevice instances holding
-                       a swap format
-
-        """
-
-        self.fsset.set_fstab_swaps(devices)
diff --git a/blivet/osinstall.py b/blivet/osinstall.py
index 936945b..6f08d3f 100644
--- a/blivet/osinstall.py
+++ b/blivet/osinstall.py
@@ -1093,6 +1093,285 @@ class CryptTab(object):
         return self.mappings.get(key, default)
 
 
+class InstallerStorageConfig(Blivet):
+    def __init__(self, ksdata=None):
+        Blivet.__init__(self, ksdata=ksdata)
+
+    def empty_device(self, device):
+        empty = True
+        if device.partitioned:
+            partitions = device.children
+            empty = all([p.is_magic for p in partitions])
+        else:
+            empty = (device.format.type is None)
+
+        return empty
+
+    @property
+    def unused_devices(self):
+        used_devices = []
+        for root in self.roots:
+            for device in list(root.mounts.values()) + root.swaps:
+                if device not in self.devices:
+                    continue
+
+                used_devices.extend(device.ancestors)
+
+        for new in [d for d in self.devicetree.leaves if not d.format.exists]:
+            if new.format.mountable and not new.format.mountpoint:
+                continue
+
+            used_devices.extend(new.ancestors)
+
+        for device in self.partitions:
+            if getattr(device, "is_logical", False):
+                extended = device.disk.format.extended_partition.path
+                used_devices.append(self.devicetree.get_device_by_path(extended))
+
+        used = set(used_devices)
+        _all = set(self.devices)
+        return list(_all.difference(used))
+
+    def should_clear(self, device, **kwargs):
+        """ Return True if a clearpart settings say a device should be cleared.
+
+            :param device: the device (required)
+            :type device: :class:`~.devices.StorageDevice`
+            :keyword clear_part_type: overrides :attr:`self.config.clear_part_type`
+            :type clear_part_type: int
+            :keyword clear_part_disks: overrides
+                                     :attr:`self.config.clear_part_disks`
+            :type clear_part_disks: list
+            :keyword clear_part_devices: overrides
+                                       :attr:`self.config.clear_part_devices`
+            :type clear_part_devices: list
+            :returns: whether or not clear_partitions should remove this device
+            :rtype: bool
+        """
+        clear_part_type = kwargs.get("clear_part_type", self.config.clear_part_type)
+        clear_part_disks = kwargs.get("clear_part_disks",
+                                      self.config.clear_part_disks)
+        clear_part_devices = kwargs.get("clear_part_devices",
+                                        self.config.clear_part_devices)
+
+        for disk in device.disks:
+            # this will not include disks with hidden formats like multipath
+            # and firmware raid member disks
+            if clear_part_disks and disk.name not in clear_part_disks:
+                return False
+
+        if not self.config.clear_non_existent:
+            if (device.is_disk and not device.format.exists) or \
+               (not device.is_disk and not device.exists):
+                return False
+
+        # the only devices we want to clear when clear_part_type is
+        # CLEARPART_TYPE_NONE are uninitialized disks, or disks with no
+        # partitions, in clear_part_disks, and then only when we have been asked
+        # to initialize disks as needed
+        if clear_part_type in [CLEARPART_TYPE_NONE, None]:
+            if not self.config.initialize_disks or not device.is_disk:
+                return False
+
+            if not empty_device(device):
+                return False
+
+        if isinstance(device, PartitionDevice):
+            # Never clear the special first partition on a Mac disk label, as
+            # that holds the partition table itself.
+            # Something similar for the third partition on a Sun disklabel.
+            if device.is_magic:
+                return False
+
+            # We don't want to fool with extended partitions, freespace, &c
+            if not device.is_primary and not device.is_logical:
+                return False
+
+            if clear_part_type == CLEARPART_TYPE_LINUX and \
+               not device.format.linux_native and \
+               not device.get_flag(parted.PARTITION_LVM) and \
+               not device.get_flag(parted.PARTITION_RAID) and \
+               not device.get_flag(parted.PARTITION_SWAP):
+                return False
+        elif device.is_disk:
+            if device.partitioned and clear_part_type != CLEARPART_TYPE_ALL:
+                # if clear_part_type is not CLEARPART_TYPE_ALL but we'll still be
+                # removing every partition from the disk, return True since we
+                # will want to be able to create a new disklabel on this disk
+                if not empty_device(device):
+                    return False
+
+            # Never clear disks with hidden formats
+            if device.format.hidden:
+                return False
+
+            # When clear_part_type is CLEARPART_TYPE_LINUX and a disk has non-
+            # linux whole-disk formatting, do not clear it. The exception is
+            # the case of an uninitialized disk when we've been asked to
+            # initialize disks as needed
+            if (clear_part_type == CLEARPART_TYPE_LINUX and
+                not ((self.config.initialize_disks and
+                      empty_device(device)) or
+                     (not device.partitioned and device.format.linux_native))):
+                return False
+
+        # Don't clear devices holding install media.
+        descendants = self.devicetree.get_dependent_devices(device)
+        if device.protected or any(d.protected for d in descendants):
+            return False
+
+        if clear_part_type == CLEARPART_TYPE_LIST and \
+           device.name not in clear_part_devices:
+            return False
+
+        return True
+
+    def clear_partitions(self):
+        """ Clear partitions and dependent devices from disks.
+
+            This is also where zerombr is handled.
+        """
+        # Sort partitions by descending partition number to minimize confusing
+        # things like multiple "destroy sda5" actions due to parted renumbering
+        # partitions. This can still happen through the UI but it makes sense to
+        # avoid it where possible.
+        partitions = sorted(self.partitions,
+                            key=lambda p: p.parted_partition.number,
+                            reverse=True)
+        for part in partitions:
+            log.debug("clearpart: looking at %s", part.name)
+            if not self.should_clear(part):
+                continue
+
+            self.recursive_remove(part)
+            log.debug("partitions: %s", [p.getDeviceNodeName() for p in part.parted_partition.disk.partitions])
+
+        # now remove any empty extended partitions
+        self.remove_empty_extended_partitions()
+
+        # ensure all disks have appropriate disklabels
+        for disk in self.disks:
+            zerombr = (self.config.zero_mbr and disk.format.type is None)
+            should_clear = self.should_clear(disk)
+            if should_clear:
+                self.recursive_remove(disk)
+
+            if zerombr or should_clear:
+                log.debug("clearpart: initializing %s", disk.name)
+                self.initialize_disk(disk)
+
+        self.update_bootloader_disk_list()
+
+    def format_by_default(self, device):
+        """Return whether the device should be reformatted by default."""
+        formatlist = ['/boot', '/var', '/tmp', '/usr']
+        exceptlist = ['/home', '/usr/local', '/opt', '/var/www']
+
+        if not device.format.linux_native:
+            return False
+
+        if device.format.mountable:
+            if not device.format.mountpoint:
+                return False
+
+            if device.format.mountpoint == "/" or \
+               device.format.mountpoint in formatlist:
+                return True
+
+            for p in formatlist:
+                if device.format.mountpoint.startswith(p):
+                    for q in exceptlist:
+                        if device.format.mountpoint.startswith(q):
+                            return False
+                    return True
+        elif device.format.type == "swap":
+            return True
+
+        # be safe for anything else and default to off
+        return False
+
+    def must_format(self, device):
+        """ Return a string explaining why the device must be reformatted.
+
+            Return None if the device need not be reformatted.
+        """
+        if device.format.mountable and device.format.mountpoint == "/":
+            return _("You must create a new filesystem on the root device.")
+
+        return None
+
+    def turn_on_swap(self):
+        self.fsset.turn_on_swap(root_path=get_sysroot())
+
+    def mount_filesystems(self, read_only=None, skip_root=False):
+        self.fsset.mount_filesystems(root_path=get_sysroot(),
+                                     read_only=read_only, skip_root=skip_root)
+
+    def umount_filesystems(self, swapoff=True):
+        self.fsset.umount_filesystems(swapoff=swapoff)
+
+    def parse_fstab(self, chroot=None):
+        self.fsset.parse_fstab(chroot=chroot)
+
+    def mk_dev_root(self):
+        self.fsset.mk_dev_root()
+
+    def create_swap_file(self, device, size):
+        self.fsset.create_swap_file(device, size)
+
+    def make_mtab(self):
+        path = "/etc/mtab"
+        target = "/proc/self/mounts"
+        path = os.path.normpath("%s/%s" % (get_sysroot(), path))
+
+        if os.path.islink(path):
+            # return early if the mtab symlink is already how we like it
+            current_target = os.path.normpath(os.path.dirname(path) +
+                                              "/" + os.readlink(path))
+            if current_target == target:
+                return
+
+        if os.path.exists(path):
+            os.unlink(path)
+
+        os.symlink(target, path)
+
+    def add_fstab_swap(self, device):
+        """
+        Add swap device to the list of swaps that should appear in the fstab.
+
+        :param device: swap device that should be added to the list
+        :type device: blivet.devices.StorageDevice instance holding a swap format
+
+        """
+
+        self.fsset.add_fstab_swap(device)
+
+    def remove_fstab_swap(self, device):
+        """
+        Remove swap device from the list of swaps that should appear in the fstab.
+
+        :param device: swap device that should be removed from the list
+        :type device: blivet.devices.StorageDevice instance holding a swap format
+
+        """
+
+        self.fsset.remove_fstab_swap(device)
+
+    def set_fstab_swaps(self, devices):
+        """
+        Set swap devices that should appear in the fstab.
+
+        :param devices: iterable providing devices that should appear in the fstab
+        :type devices: iterable providing blivet.devices.StorageDevice instances holding
+                       a swap format
+
+        """
+
+        self.fsset.set_fstab_swaps(devices)
+
+
+
 def get_containing_device(path, devicetree):
     """ Return the device that a path resides on. """
     if not os.path.exists(path):
-- 
2.4.3

