[PATCH 2/2] Move code related to finding existing installations to anaconda
Vratislav Podzimek
vpodzime at redhat.com
Fri Jan 23 14:14:49 UTC 2015
Only anaconda uses this code that previously lived in blivet and it is
installer-specific. Thus it should live in anaconda's codebase not blivet's.
Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
anaconda | 4 +-
pyanaconda/rescue.py | 6 +-
pyanaconda/storage_utils.py | 218 ++++++++++++++++++++++++++++++++
pyanaconda/ui/gui/spokes/custom.py | 2 +-
pyanaconda/ui/gui/spokes/lib/refresh.py | 5 +-
pyanaconda/ui/gui/spokes/storage.py | 2 +
pyanaconda/ui/tui/spokes/storage.py | 3 +-
7 files changed, 231 insertions(+), 9 deletions(-)
diff --git a/anaconda b/anaconda
index cd28667..72a86e6 100755
--- a/anaconda
+++ b/anaconda
@@ -1289,7 +1289,7 @@ if __name__ == "__main__":
signal.signal(signal.SIGUSR2, lambda signum, frame: anaconda.dumpState())
atexit.register(exitHandler, ksdata.reboot, anaconda.storage)
- from blivet import storageInitialize
+ from pyanaconda.storage_utils import storageInitializeAndPopulate
from pyanaconda.packaging import payloadMgr
from pyanaconda.timezone import time_initialize
@@ -1299,7 +1299,7 @@ if __name__ == "__main__":
cleanPStore()
if not flags.dirInstall:
- threadMgr.add(AnacondaThread(name=constants.THREAD_STORAGE, target=storageInitialize,
+ threadMgr.add(AnacondaThread(name=constants.THREAD_STORAGE, target=storageInitializeAndPopulate,
args=(anaconda.storage, ksdata, anaconda.protected)))
threadMgr.add(AnacondaThread(name=constants.THREAD_TIME_INIT, target=time_initialize,
args=(ksdata.timezone, anaconda.storage, anaconda.bootloader)))
diff --git a/pyanaconda/rescue.py b/pyanaconda/rescue.py
index 6702e2b..ed17a18 100644
--- a/pyanaconda/rescue.py
+++ b/pyanaconda/rescue.py
@@ -35,6 +35,7 @@ from pyanaconda.flags import flags
from pyanaconda.installinterfacebase import InstallInterfaceBase
from pyanaconda.i18n import _
from pyanaconda.kickstart import runPostScripts
+from pyanaconda.storage_utils import storageInitializeAndPopulate
from blivet import mountExistingSystem
from blivet.errors import StorageError
@@ -252,6 +253,7 @@ def _unlock_devices(intf, storage):
def doRescue(intf, rescue_mount, ksdata):
import blivet
+ from pyanaconda.storage_utils import findExistingInstallations
# XXX: hook the exception handler wrapper that turns off snack first
orig_hook = sys.excepthook
@@ -305,9 +307,9 @@ def doRescue(intf, rescue_mount, ksdata):
break
sto = blivet.Blivet(ksdata=ksdata)
- blivet.storageInitialize(sto, ksdata, [])
+ storageInitializeAndPopulate(sto, ksdata, [])
_unlock_devices(intf, sto)
- roots = blivet.findExistingInstallations(sto.devicetree)
+ roots = findExistingInstallations(sto.devicetree)
if not roots:
root = None
diff --git a/pyanaconda/storage_utils.py b/pyanaconda/storage_utils.py
index d0906d3..aeabaa5 100644
--- a/pyanaconda/storage_utils.py
+++ b/pyanaconda/storage_utils.py
@@ -22,11 +22,15 @@
import re
import locale
+import os
+import shlex
from contextlib import contextmanager
from blivet import arch
from blivet import util
+from blivet import storageInitialize
+from blivet import Root, BlkidTab, CryptTab
from blivet.size import Size
from blivet.platform import platform as _platform
from blivet.devicefactory import DEVICE_TYPE_LVM
@@ -35,10 +39,12 @@ from blivet.devicefactory import DEVICE_TYPE_BTRFS
from blivet.devicefactory import DEVICE_TYPE_MD
from blivet.devicefactory import DEVICE_TYPE_PARTITION
from blivet.devicefactory import DEVICE_TYPE_DISK
+from blivet.storage_log import log_exception_info
from pyanaconda.i18n import _, N_
from pyanaconda import isys
from pyanaconda.constants import productName
+from pyanaconda.iutil import getTargetPhysicalRoot, getSysroot
from pykickstart.constants import AUTOPART_TYPE_PLAIN, AUTOPART_TYPE_BTRFS
from pykickstart.constants import AUTOPART_TYPE_LVM, AUTOPART_TYPE_LVM_THINP
@@ -380,3 +386,215 @@ def bound_size(size, device, old_size):
size = min_size
return size
+
+def releaseFromRedhatRelease(fn):
+ """
+ Attempt to identify the installation of a Linux distribution via
+ /etc/redhat-release. This file must already have been verified to exist
+ and be readable.
+
+ :param fn: an open filehandle on /etc/redhat-release
+ :type fn: filehandle
+ :returns: The distribution's name and version, or None for either or both
+ if they cannot be determined
+ :rtype: (string, string)
+ """
+ relName = None
+ relVer = None
+
+ with open(fn) as f:
+ try:
+ relstr = f.readline().strip()
+ except (IOError, AttributeError):
+ relstr = ""
+
+ # get the release name and version
+ # assumes that form is something
+ # like "Red Hat Linux release 6.2 (Zoot)"
+ (product, sep, version) = relstr.partition(" release ")
+ if sep:
+ relName = product
+ relVer = version.split()[0]
+
+ return (relName, relVer)
+
+def releaseFromOsRelease(fn):
+ """
+ Attempt to identify the installation of a Linux distribution via
+ /etc/os-release. This file must already have been verified to exist
+ and be readable.
+
+ :param fn: an open filehandle on /etc/os-release
+ :type fn: filehandle
+ :returns: The distribution's name and version, or None for either or both
+ if they cannot be determined
+ :rtype: (string, string)
+ """
+ relName = None
+ relVer = None
+
+ with open(fn, "r") as f:
+ parser = shlex.shlex(f)
+
+ while True:
+ key = parser.get_token()
+ if key == parser.eof:
+ break
+ elif key == "NAME":
+ # Throw away the "=".
+ parser.get_token()
+ relName = parser.get_token().strip("'\"")
+ elif key == "VERSION_ID":
+ # Throw away the "=".
+ parser.get_token()
+ relVer = parser.get_token().strip("'\"")
+
+ return (relName, relVer)
+
+def getReleaseString():
+ """
+ Attempt to identify the installation of a Linux distribution by checking
+ a previously mounted filesystem for several files. The filesystem must
+ be mounted under the target physical root.
+
+ :returns: The machine's arch, distribution name, and distribution version
+ or None for any parts that cannot be determined
+ :rtype: (string, string, string)
+ """
+ relName = None
+ relVer = None
+
+ try:
+ relArch = util.capture_output(["arch"], root=getSysroot()).strip()
+ except OSError:
+ relArch = None
+
+ filename = "%s/etc/redhat-release" % getSysroot()
+ if os.access(filename, os.R_OK):
+ (relName, relVer) = releaseFromRedhatRelease(filename)
+ else:
+ filename = "%s/etc/os-release" % getSysroot()
+ if os.access(filename, os.R_OK):
+ (relName, relVer) = releaseFromOsRelease(filename)
+
+ return (relArch, relName, relVer)
+
+def parseFSTab(devicetree, chroot=None):
+ """ parse /etc/fstab and return a tuple of a mount dict and swap list """
+ if not chroot or not os.path.isdir(chroot):
+ chroot = getSysroot()
+
+ mounts = {}
+ swaps = []
+ path = "%s/etc/fstab" % chroot
+ if not os.access(path, os.R_OK):
+ # XXX should we raise an exception instead?
+ log.info("cannot open %s for read", path)
+ return (mounts, swaps)
+
+ blkidTab = BlkidTab(chroot=chroot)
+ try:
+ blkidTab.parse()
+ log.debug("blkid.tab devs: %s", list(blkidTab.devices.keys()))
+ except Exception: # pylint: disable=broad-except
+ log_exception_info(log.info, "error parsing blkid.tab")
+ blkidTab = None
+
+ cryptTab = CryptTab(devicetree, blkidTab=blkidTab, chroot=chroot)
+ try:
+ cryptTab.parse(chroot=chroot)
+ log.debug("crypttab maps: %s", list(cryptTab.mappings.keys()))
+ except Exception: # pylint: disable=broad-except
+ log_exception_info(log.info, "error parsing crypttab")
+ cryptTab = None
+
+ with open(path) as f:
+ log.debug("parsing %s", path)
+ for line in f.readlines():
+
+ (line, _pound, _comment) = line.partition("#")
+ fields = line.split(None, 4)
+
+ if len(fields) < 5:
+ continue
+
+ (devspec, mountpoint, fstype, options, _rest) = fields
+
+ # find device in the tree
+ device = devicetree.resolveDevice(devspec,
+ cryptTab=cryptTab,
+ blkidTab=blkidTab,
+ options=options)
+
+ if device is None:
+ continue
+
+ if fstype != "swap":
+ mounts[mountpoint] = device
+ else:
+ swaps.append(device)
+
+ return (mounts, swaps)
+
+def findExistingInstallations(devicetree):
+ if not os.path.exists(getTargetPhysicalRoot()):
+ util.makedirs(getTargetPhysicalRoot())
+
+ roots = []
+ for device in devicetree.leaves:
+ if not device.format.linuxNative or not device.format.mountable or \
+ not device.controllable:
+ continue
+
+ try:
+ device.setup()
+ except Exception: # pylint: disable=broad-except
+ log_exception_info(log.warning, "setup of %s failed", [device.name])
+ continue
+
+ options = device.format.options + ",ro"
+ try:
+ device.format.mount(options=options, mountpoint=getSysroot())
+ except Exception: # pylint: disable=broad-except
+ log_exception_info(log.warning, "mount of %s as %s failed", [device.name, device.format.type])
+ device.teardown()
+ continue
+
+ if not os.access(getSysroot() + "/etc/fstab", os.R_OK):
+ device.teardown(recursive=True)
+ continue
+
+ try:
+ (architecture, product, version) = getReleaseString()
+ except ValueError:
+ name = _("Linux on %s") % device.name
+ else:
+ # I'd like to make this finer grained, but it'd be very difficult
+ # to translate.
+ if not product or not version or not architecture:
+ name = _("Unknown Linux")
+ elif "linux" in product.lower():
+ name = _("%(product)s %(version)s for %(arch)s") % \
+ {"product": product, "version": version, "arch": architecture}
+ else:
+ name = _("%(product)s Linux %(version)s for %(arch)s") % \
+ {"product": product, "version": version, "arch": architecture}
+
+ (mounts, swaps) = parseFSTab(devicetree, chroot=getSysroot())
+ device.teardown()
+ if not mounts and not swaps:
+ # empty /etc/fstab. weird, but I've seen it happen.
+ continue
+ roots.append(Root(mounts=mounts, swaps=swaps, name=name))
+
+ return roots
+
+def populate_storage_roots(storage):
+ try:
+ storage.roots = findExistingInstallations(storage.devicetree)
+ except Exception: # pylint: disable=broad-except
+ log_exception_info(log.info, "failure detecting existing installations")
+
+def storageInitializeAndPopulate(storage, ksdata, protected):
+ storageInitialize(storage, ksdata, protected)
+ populate_storage_roots(storage)
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index 29528b8..58cea8d 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -54,7 +54,6 @@ from blivet.devicefactory import DEVICE_TYPE_MD
from blivet.devicefactory import DEVICE_TYPE_DISK
from blivet.devicefactory import DEVICE_TYPE_LVM_THINP
from blivet.devicefactory import SIZE_POLICY_AUTO
-from blivet import findExistingInstallations
from pyanaconda.autopart import doAutoPartition
from blivet.errors import StorageError
from blivet.errors import NoDisksError
@@ -67,6 +66,7 @@ from pyanaconda.storage_utils import DEVICE_TEXT_PARTITION, DEVICE_TEXT_MAP, DEV
from pyanaconda.storage_utils import PARTITION_ONLY_FORMAT_TYPES, MOUNTPOINT_DESCRIPTIONS
from pyanaconda.storage_utils import NAMED_DEVICE_TYPES, CONTAINER_DEVICE_TYPES
from pyanaconda.storage_utils import SanityError, SanityWarning, LUKSDeviceWithoutKeyError
+from pyanaconda.storage_utils import findExistingInstallations
from pyanaconda import storage_utils
from pyanaconda.ui.communication import hubQ
diff --git a/pyanaconda/ui/gui/spokes/lib/refresh.py b/pyanaconda/ui/gui/spokes/lib/refresh.py
index 396c38f..6daee45 100644
--- a/pyanaconda/ui/gui/spokes/lib/refresh.py
+++ b/pyanaconda/ui/gui/spokes/lib/refresh.py
@@ -24,8 +24,7 @@ from gi.repository import GLib
from pyanaconda.threads import threadMgr, AnacondaThread
from pyanaconda.ui.gui import GUIObject
from pyanaconda import constants
-
-from blivet import storageInitialize
+from pyanaconda.storage_utils import storageInitializeAndPopulate
__all__ = ["RefreshDialog"]
@@ -89,7 +88,7 @@ class RefreshDialog(GUIObject):
self._notebook.set_current_page(1)
# And now to fire up the storage reinitialization.
- threadMgr.add(AnacondaThread(name=constants.THREAD_STORAGE, target=storageInitialize,
+ threadMgr.add(AnacondaThread(name=constants.THREAD_STORAGE, target=storageInitializeAndPopulate,
args=(self.storage, self.data, self.storage.devicetree.protectedDevNames)))
self._elapsed = 0
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py
index d19a20b..e394bf4 100644
--- a/pyanaconda/ui/gui/spokes/storage.py
+++ b/pyanaconda/ui/gui/spokes/storage.py
@@ -68,6 +68,7 @@ from pyanaconda.i18n import _, C_, CN_, P_
from pyanaconda import constants, iutil, isys
from pyanaconda.bootloader import BootLoaderError
from pyanaconda.autopart import swap_suggestion
+from pyanaconda.storage_utils import populate_storage_roots
from pykickstart.constants import CLEARPART_TYPE_NONE, AUTOPART_TYPE_LVM
from pykickstart.errors import KickstartValueError
@@ -333,6 +334,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
self.data.ignoredisk.onlyuse = []
self.storage.config.update(self.data)
self.storage.reset()
+ populate_storage_roots(self.storage)
self.disks = getDisks(self.storage.devicetree)
# now set ksdata back to the user's specified config
applyDiskSelection(self.storage, self.data, self.selected_disks)
diff --git a/pyanaconda/ui/tui/spokes/storage.py b/pyanaconda/ui/tui/spokes/storage.py
index 1c7b600..36c3562 100644
--- a/pyanaconda/ui/tui/spokes/storage.py
+++ b/pyanaconda/ui/tui/spokes/storage.py
@@ -27,7 +27,7 @@ from pyanaconda.ui.categories.system import SystemCategory
from pyanaconda.ui.tui.spokes import NormalTUISpoke
from pyanaconda.ui.tui.simpleline import TextWidget, CheckboxWidget
from pyanaconda.ui.tui.tuiobject import YesNoDialog
-from pyanaconda.storage_utils import AUTOPART_CHOICES, sanity_check, SanityError, SanityWarning
+from pyanaconda.storage_utils import AUTOPART_CHOICES, sanity_check, SanityError, SanityWarning, populate_storage_roots
from blivet import arch
from blivet.size import Size
@@ -378,6 +378,7 @@ class StorageSpoke(NormalTUISpoke):
self.storage.config.update(self.data)
self.storage.autoPartType = self.data.autopart.type
self.storage.reset()
+ populate_storage_roots(self.storage)
# now set ksdata back to the user's specified config
applyDiskSelection(self.storage, self.data, self.selected_disks)
except BootLoaderError as e:
--
2.1.0
More information about the anaconda-patches
mailing list