[PATCH 1/2] Move sanityCheck code to anaconda's codebase
Vratislav Podzimek
vpodzime at redhat.com
Tue Jul 29 12:04:00 UTC 2014
This is the right place it belongs to.
Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
pyanaconda/kickstart.py | 7 +-
pyanaconda/storage_utils.py | 189 +++++++++++++++++++++++++++++++++++-
pyanaconda/ui/gui/spokes/custom.py | 8 +-
pyanaconda/ui/helpers.py | 5 +-
pyanaconda/ui/tui/spokes/storage.py | 6 +-
5 files changed, 201 insertions(+), 14 deletions(-)
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index 09f2a88..fcbfa72 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -26,6 +26,7 @@ from blivet.devicelibs import swap
from blivet.formats import getFormat
from blivet.partitioning import doPartitioning
from blivet.partitioning import growLVM
+from blivet.errors import PartitioningError
from blivet.size import Size
from blivet import udev
from blivet.platform import platform
@@ -263,7 +264,7 @@ class AutoPart(commands.autopart.F21_AutoPart):
def execute(self, storage, ksdata, instClass):
from blivet.partitioning import doAutoPartition
- from blivet.partitioning import sanityCheck
+ from pyanaconda.storage_utils import sanity_check
if not self.autopart:
return
@@ -291,7 +292,9 @@ class AutoPart(commands.autopart.F21_AutoPart):
storage.autoPartType = self.type
doAutoPartition(storage, ksdata)
- sanityCheck(storage)
+ errors = sanity_check(storage)
+ if errors:
+ raise PartitioningError("autopart failed:\n" + "\n".join(error.message for error in errors))
class Bootloader(commands.bootloader.F21_Bootloader):
def __init__(self, *args, **kwargs):
diff --git a/pyanaconda/storage_utils.py b/pyanaconda/storage_utils.py
index e6e3f70..f49cec0 100644
--- a/pyanaconda/storage_utils.py
+++ b/pyanaconda/storage_utils.py
@@ -25,7 +25,10 @@ import locale
from contextlib import contextmanager
+from blivet import arch
+from blivet import util
from blivet.size import Size
+from blivet.platform import platform as _platform
from blivet.devicefactory import DEVICE_TYPE_LVM
from blivet.devicefactory import DEVICE_TYPE_LVM_THINP
from blivet.devicefactory import DEVICE_TYPE_BTRFS
@@ -33,7 +36,10 @@ from blivet.devicefactory import DEVICE_TYPE_MD
from blivet.devicefactory import DEVICE_TYPE_PARTITION
from blivet.devicefactory import DEVICE_TYPE_DISK
-from pyanaconda.i18n import N_
+from pyanaconda.i18n import _, N_
+from pyanaconda import isys
+from pyanaconda.constants import productName
+
from pykickstart.constants import AUTOPART_TYPE_PLAIN, AUTOPART_TYPE_BTRFS
from pykickstart.constants import AUTOPART_TYPE_LVM, AUTOPART_TYPE_LVM_THINP
@@ -128,3 +134,184 @@ def ui_storage_logger():
storage_log.addFilter(storage_filter)
yield
storage_log.removeFilter(storage_filter)
+
+class SanityException(Exception):
+ pass
+
+class SanityError(SanityException):
+ pass
+
+class SanityWarning(SanityException):
+ pass
+
+class LUKSDeviceWithoutKeyError(SanityError):
+ pass
+
+def sanity_check(storage):
+ """
+ Run a series of tests to verify the storage configuration.
+
+ This function is called at the end of partitioning so that
+ we can make sure you don't have anything silly (like no /,
+ a really small /, etc).
+
+ :rtype: a list of SanityExceptions
+ :return: a list of accumulated errors and warnings
+
+ """
+
+ exns = []
+
+ checkSizes = [('/usr', 250), ('/tmp', 50), ('/var', 384),
+ ('/home', 100), ('/boot', 75)]
+ mustbeonlinuxfs = ['/', '/var', '/tmp', '/usr', '/home', '/usr/share', '/usr/lib']
+ mustbeonroot = ['/bin','/dev','/sbin','/etc','/lib','/root', '/mnt', 'lost+found', '/proc']
+
+ filesystems = storage.mountpoints
+ root = storage.fsset.rootDevice
+ swaps = storage.fsset.swapDevices
+
+ if root:
+ if root.size < 250:
+ exns.append(
+ SanityWarning(_("Your root partition is less than 250 "
+ "megabytes which is usually too small to "
+ "install %s.") % (productName,)))
+ else:
+ exns.append(
+ SanityError(_("You have not defined a root partition (/), "
+ "which is required for installation of %s "
+ "to continue.") % (productName,)))
+
+ # Prevent users from installing on s390x with (a) no /boot volume, (b) the
+ # root volume on LVM, and (c) the root volume not restricted to a single
+ # PV
+ # NOTE: There is not really a way for users to create a / volume
+ # restricted to a single PV. The backend support is there, but there are
+ # no UI hook-ups to drive that functionality, but I do not personally
+ # care. --dcantrell
+ if arch.isS390() and '/boot' not in storage.mountpoints and root:
+ if root.type == 'lvmlv' and not root.singlePV:
+ exns.append(
+ SanityError(_("This platform requires /boot on a dedicated "
+ "partition or logical volume. If you do not "
+ "want a /boot volume, you must place / on a "
+ "dedicated non-LVM partition.")))
+
+ # FIXME: put a check here for enough space on the filesystems. maybe?
+
+ for (mount, size) in checkSizes:
+ if mount in filesystems and filesystems[mount].size < size:
+ exns.append(
+ SanityWarning(_("Your %(mount)s partition is less than "
+ "%(size)s megabytes which is lower than "
+ "recommended for a normal %(productName)s "
+ "install.")
+ % {'mount': mount, 'size': size,
+ 'productName': productName}))
+
+ for (mount, device) in filesystems.items():
+ problem = filesystems[mount].checkSize()
+ if problem < 0:
+ exns.append(
+ SanityError(_("Your %(mount)s partition is too small for %(format)s formatting "
+ "(allowable size is %(minSize)s to %(maxSize)s)")
+ % {"mount": mount, "format": device.format.name,
+ "minSize": device.minSize, "maxSize": device.maxSize}))
+ elif problem > 0:
+ exns.append(
+ SanityError(_("Your %(mount)s partition is too large for %(format)s formatting "
+ "(allowable size is %(minSize)s to %(maxSize)s)")
+ % {"mount":mount, "format": device.format.name,
+ "minSize": device.minSize, "maxSize": device.maxSize}))
+
+ if storage.bootloader and not storage.bootloader.skip_bootloader:
+ stage1 = storage.bootloader.stage1_device
+ if not stage1:
+ exns.append(
+ SanityError(_("No valid bootloader target device found. "
+ "See below for details.")))
+ pe = _platform.stage1MissingError
+ if pe:
+ exns.append(SanityError(_(pe)))
+ else:
+ storage.bootloader.is_valid_stage1_device(stage1)
+ exns.extend(SanityError(msg) for msg in storage.bootloader.errors)
+ exns.extend(SanityWarning(msg) for msg in storage.bootloader.warnings)
+
+ stage2 = storage.bootloader.stage2_device
+ if stage1 and not stage2:
+ exns.append(SanityError(_("You have not created a bootable partition.")))
+ else:
+ storage.bootloader.is_valid_stage2_device(stage2)
+ exns.extend(SanityError(msg) for msg in storage.bootloader.errors)
+ exns.extend(SanityWarning(msg) for msg in storage.bootloader.warnings)
+ if not storage.bootloader.check():
+ exns.extend(SanityError(msg) for msg in storage.bootloader.errors)
+
+ #
+ # check that GPT boot disk on BIOS system has a BIOS boot partition
+ #
+ if _platform.weight(fstype="biosboot") and \
+ stage1 and stage1.isDisk and \
+ getattr(stage1.format, "labelType", None) == "gpt":
+ missing = True
+ for part in [p for p in storage.partitions if p.disk == stage1]:
+ if part.format.type == "biosboot":
+ missing = False
+ break
+
+ if missing:
+ exns.append(
+ SanityError(_("Your BIOS-based system needs a special "
+ "partition to boot from a GPT disk label. "
+ "To continue, please create a 1MiB "
+ "'biosboot' type partition.")))
+
+ if not swaps:
+ installed = util.total_memory()
+ required = Size("%s KiB" % isys.EARLY_SWAP_RAM)
+
+ if installed < required:
+ exns.append(
+ SanityError(_("You have not specified a swap partition. "
+ "%(requiredMem)s of memory is required to continue installation "
+ "without a swap partition, but you only have %(installedMem)s.")
+ % {"requiredMem": required,
+ "installedMem": installed}))
+ else:
+ exns.append(
+ SanityWarning(_("You have not specified a swap partition. "
+ "Although not strictly required in all cases, "
+ "it will significantly improve performance "
+ "for most installations.")))
+ no_uuid = [s for s in swaps if s.format.exists and not s.format.uuid]
+ if no_uuid:
+ exns.append(
+ SanityWarning(_("At least one of your swap devices does not have "
+ "a UUID, which is common in swap space created "
+ "using older versions of mkswap. These devices "
+ "will be referred to by device path in "
+ "/etc/fstab, which is not ideal since device "
+ "paths can change under a variety of "
+ "circumstances. ")))
+
+ for (mountpoint, dev) in filesystems.items():
+ if mountpoint in mustbeonroot:
+ exns.append(
+ SanityError(_("This mount point is invalid. The %s directory must "
+ "be on the / file system.") % mountpoint))
+
+ if mountpoint in mustbeonlinuxfs and (not dev.format.mountable or not dev.format.linuxNative):
+ exns.append(
+ SanityError(_("The mount point %s must be on a linux file system.") % mountpoint))
+
+ if storage.rootDevice and storage.rootDevice.format.exists:
+ e = storage.mustFormat(storage.rootDevice)
+ if e:
+ exns.append(SanityError(e))
+
+ exns += storage._verifyLUKSDevicesHaveKey()
+
+ return exns
+
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index 45ab6de..e0a78b8 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -56,8 +56,6 @@ from blivet.partitioning import doAutoPartition
from blivet.errors import StorageError
from blivet.errors import NoDisksError
from blivet.errors import NotEnoughFreeSpaceError
-from blivet.errors import SanityError
-from blivet.errors import SanityWarning
from blivet.errors import LUKSDeviceWithoutKeyError
from blivet.devicelibs import raid
from blivet.devices import LUKSDevice
@@ -66,6 +64,8 @@ from pyanaconda.storage_utils import ui_storage_logger, device_type_from_autopar
from pyanaconda.storage_utils import DEVICE_TEXT_PARTITION, DEVICE_TEXT_MAP, DEVICE_TEXT_MD
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
+from pyanaconda import storage_utils
from pyanaconda.ui.communication import hubQ
from pyanaconda.ui.gui.spokes import NormalSpoke
@@ -2167,7 +2167,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
def _do_autopart(self):
"""Helper function for on_create_clicked.
Assumes a non-final context in which at least some errors
- discovered by sanityCheck are not considered fatal because they
+ discovered by sanity_check are not considered fatal because they
will be dealt with later.
Note: There are never any non-existent devices around when this runs.
@@ -2209,7 +2209,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._storage_playground.doAutoPart = False
log.debug("finished automatic partitioning")
- exns = self._storage_playground.sanityCheck()
+ exns = storage_utils.sanity_check(self._storage_playground)
errors = [exn for exn in exns if isinstance(exn, SanityError) and not isinstance(exn, LUKSDeviceWithoutKeyError)]
warnings = [exn for exn in exns if isinstance(exn, SanityWarning)]
for error in errors:
diff --git a/pyanaconda/ui/helpers.py b/pyanaconda/ui/helpers.py
index 15597e8..e688a99 100644
--- a/pyanaconda/ui/helpers.py
+++ b/pyanaconda/ui/helpers.py
@@ -84,14 +84,13 @@ class StorageChecker(object):
target=self.checkStorage))
def checkStorage(self):
- from blivet.errors import SanityError
- from blivet.errors import SanityWarning
+ from pyanaconda.storage_utils import sanity_check, SanityError, SanityWarning
threadMgr.wait(constants.THREAD_EXECUTE_STORAGE)
hubQ.send_not_ready(self._mainSpokeClass)
hubQ.send_message(self._mainSpokeClass, _("Checking storage configuration..."))
- exns = self.storage.sanityCheck()
+ exns = sanity_check(self.storage)
errors = [exn.message for exn in exns if isinstance(exn, SanityError)]
warnings = [exn.message for exn in exns if isinstance(exn, SanityWarning)]
(StorageChecker.errors, StorageChecker.warnings) = (errors, warnings)
diff --git a/pyanaconda/ui/tui/spokes/storage.py b/pyanaconda/ui/tui/spokes/storage.py
index a0e05d9..e86f0bb 100644
--- a/pyanaconda/ui/tui/spokes/storage.py
+++ b/pyanaconda/ui/tui/spokes/storage.py
@@ -27,13 +27,11 @@ 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
+from pyanaconda.storage_utils import AUTOPART_CHOICES, sanity_check, SanityError, SanityWarning
from blivet import storageInitialize, arch
from blivet.size import Size
from blivet.errors import StorageError, DasdFormatError
-from blivet.errors import SanityError
-from blivet.errors import SanityWarning
from blivet.devices import DASDDevice, FcoeDiskDevice, iScsiDiskDevice, MultipathDevice, ZFCPDiskDevice
from blivet.devicelibs.dasd import format_dasd, make_unformatted_dasd_list
from pyanaconda.flags import flags
@@ -393,7 +391,7 @@ class StorageSpoke(NormalTUISpoke):
self._ready = True
else:
print(_("Checking storage configuration..."))
- exns = self.storage.sanityCheck()
+ exns = sanity_check(self.storage)
errors = [exn.message for exn in exns if isinstance(exn, SanityError)]
warnings = [exn.message for exn in exns if isinstance(exn, SanityWarning)]
(self.errors, self.warnings) = (errors, warnings)
--
1.9.3
More information about the anaconda-patches
mailing list