Remove the vast majority of bootloader-related code and make the
bootloader an attribute of the platform.
Separate X86 from EFI.
Make bootDevice always represent bootloader stage2 device and create
bootLoaderDevice to represent bootloader stage1 device. Make both of
them into properties.
diff --git a/pyanaconda/platform.py b/pyanaconda/platform.py
index 3942887..b1fee83 100644
--- a/pyanaconda/platform.py
+++ b/pyanaconda/platform.py
@@ -1,7 +1,7 @@
#
# platform.py: Architecture-specific information
#
-# Copyright (C) 2009
+# Copyright (C) 2009-2011
# Red Hat, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
@@ -20,12 +20,12 @@
# Authors: Chris Lumens <clumens@redhat.com>
#
class Platform(object):
"""Platform
@@ -34,15 +34,9 @@ class Platform(object):
during installation. The intent is to eventually encapsulate all the
architecture quirks in one place to avoid lots of platform checks
throughout anaconda."""
- _bootFSTypes = ["ext3"]
- _diskLabelType = "msdos"
- _isEfi = iutil.isEfi()
_minimumSector = 0
_packages = []
- _supportsLvmBoot = False
- _supportsMdRaidBoot = False
- _minBootPartSize = 50
- _maxBootPartSize = 0
+ _bootloaderClass = bootloader.BootLoader
def __init__(self, anaconda):
"""Creates a new Platform object. This is basically an abstract class.
@@ -50,106 +44,117 @@ class Platform(object):
returned by getPlatform below. Not all subclasses need to provide
all the methods in this class."""
self.anaconda = anaconda
+ self.bootloader = self._bootloaderClass(storage=getattr(anaconda,
+ "storage",
+ None))
- def _mntDict(self):
- """Return a dictionary mapping mount points to devices."""
- ret = {}
- for device in [d for d in self.anaconda.storage.devices if d.format.mountable]:
- ret[device.format.mountpoint] = device
-
- return ret
-
+ @property
def bootDevice(self):
- """Return the device where /boot is mounted."""
- if self.__class__ is Platform:
- raise NotImplementedError("bootDevice not implemented for this platform")
+ """The device that includes the /boot filesystem."""
+ return self.bootloader.stage2_device
- mntDict = self._mntDict()
- return mntDict.get("/boot", mntDict.get("/"))
+ @property
+ def bootLoaderDevice(self):
+ """The device the bootloader will be installed into."""
+ return self.bootloader.stage1_device
+
+ @property
+ def bootFSTypes(self):
+ """A list of all valid filesystem types for the boot partition."""
+ return self.bootloader.linux_boot_device_format_types
@property
def defaultBootFSType(self):
- """Return the default filesystem type for the boot partition."""
- return self._bootFSTypes[0]
+ """The default filesystem type for the boot partition."""
+ return self.bootFSTypes[0]
@property
- def bootFSTypes(self):
- """Return a list of all valid filesystem types for the boot partition."""
- return self._bootFSTypes
-
- def bootloaderChoices(self, bl):
- """Return the default list of places to install the bootloader.
- This is returned as a dictionary of locations to (device, identifier)
- tuples. If there is no boot device, an empty dictionary is
- returned."""
- if self.__class__ is Platform:
- raise NotImplementedError("bootloaderChoices not implemented for this platform")
-
- bootDev = self.bootDevice()
- ret = {}
-
- if not bootDev:
- return ret
-
- if bootDev.type == "mdarray":
- ret["boot"] = (bootDev.name, N_("RAID Device"))
- ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
- else:
- ret["boot"] = (bootDev.name, N_("First sector of boot partition"))
- ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
+ def diskLabelTypes(self):
+ """A list of valid disklabel types for this architecture."""
+ return self.bootloader.target_device_disklabel_types
- return ret
+ @property
+ def defaultDiskLabelType(self):
+ """The default disklabel type for this architecture."""
+ return self.diskLabelTypes[0]
+
+ def diskLabelType(self, device_type):
+ """The default disklabel type for the specified device type."""
+ return self.defaultDiskLabelType
+
+ def checkDiskLabel(self, req):
+ """Check the disk containing req for the correct disklabel type.
+
+ Return a list of error strings if incorrect disklabels are found."""
+ errors = []
+ if not self.bootloader.target_device_disklabel_types:
+ return errors
- def checkBootRequest(self, req):
+ for disk in req.disks:
+ labelType = disk.format.labelType
+ labelTypes = self.bootloader.target_device_disklabel_types
+ if labelType not in labelTypes:
+ errors.append(_("%s must have a %s disk label.")
+ % (disk.name,
+ " or ".join([t.upper() for t in labelTypes])))
+ return errors
+
+ def checkBootRequest(self):
"""Perform an architecture-specific check on the boot device. Not all
platforms may need to do any checks. Returns a list of errors if
there is a problem, or [] otherwise."""
errors = []
+ req = self.bootDevice
if not req:
return [_("You have not created a bootable partition.")]
- # most arches can't have boot on RAID
- if req.type == "mdarray":
- if not self.supportsMdRaidBoot:
- errors.append(_("Bootable partitions cannot be on a RAID device."))
- elif req.type == "mdarray" and req.level != 1:
- errors.append(_("Bootable partitions can only be on RAID1 devices."))
- else:
- for p in req.parents:
- if p.type != "partition":
- errors.append(_("Bootable RAID1 set members must be partitions."))
- break
-
- # most arches can't have boot on a logical volume
- if req.type == "lvmlv" and not self.supportsLvmBoot:
- errors.append(_("Bootable partitions cannot be on a logical volume."))
+ # TODO: reimplement BootLoader._device_is_bootable(req, linux=True)
+ # such that it returns a list of error strings instead of
+ # True/False
+
+ if req.type not in self.bootloader.boot_device_types:
+ errors.append(_("The /boot filesystem cannot be on devices of "
+ "type %s") % req.type)
+ elif req.type == "mdarray":
+ raid_levels = self.bootloader.boot_device_raid_levels
+ if req.level not in raid_levels:
+ levels = ",".join(["RAID%d" % l for l in raid_levels])
+ errors.append(_("RAID sets containing the /boot filesystem "
+ "must have one of the following raid levels: "
+ "%s.") % levels)
+
+ for p in req.parents:
+ if p.type != "partition":
+ errors.append(_("RAID sets containing the /boot "
+ "filesystem may only have partitions "
+ "as member devices."))
+ break
# Make sure /boot is on a supported FS type. This prevents crazy
# things like boot on vfat.
if not req.format.bootable or
- (getattr(req.format, "mountpoint", None) == "/boot" and
- req.format.type not in self.bootFSTypes):
- errors.append(_("Bootable partitions cannot be on an %s filesystem.") % req.format.type)
+ req.format.type not in self.bootFSTypes:
+ errors.append(_("The /boot filesystem cannot be of type %s.") % req.format.type)
if req.type == "luks/dm-crypt":
# Handle encrypted boot on a partition.
- errors.append(_("Bootable partitions cannot be on an encrypted block device"))
+ errors.append(_("The /boot filesystem cannot be on an encrypted block device"))
else:
# Handle encrypted boot on more complicated devices.
for dev in filter(lambda d: d.type == "luks/dm-crypt", self.anaconda.storage.devices):
if req in self.anaconda.storage.deviceDeps(dev):
- errors.append(_("Bootable partitions cannot be on an encrypted block device"))
+ errors.append(_("The /boot filesystem cannot be on an encrypted block device"))
- def diskLabelType(self, deviceType):
- """Return the disk label type as a string."""
- return self._diskLabelType
+ def checkBootLoaderRequest(self):
+ """ Perform architecture-specific checks on the bootloader device.
- @property
- def isEfi(self):
- return self._isEfi
+ Returns a list of error strings.
+ """
+ return self.checkDiskLabel(self.bootLoaderDevice)
@property
def minimumSector(self, disk):
@@ -158,7 +163,7 @@ class Platform(object):
def setDefaultPartitioning(self):
"""Return the default platform-specific partitioning information."""
@@ -166,102 +171,32 @@ class Platform(object):
return [PartSpec(mountpoint="/boot", fstype=self.defaultBootFSType, size=500,
weight=self.weight(mountpoint="/boot"))]
- @property
- def supportsLvmBoot(self):
- """Does the platform support /boot on LVM logical volume?"""
- return self._supportsLvmBoot
-
- @property
- def supportsMdRaidBoot(self):
- """Does the platform support /boot on MD RAID?"""
- return self._supportsMdRaidBoot
-
- @property
- def minBootPartSize(self):
- return self._minBootPartSize
-
- @property
- def maxBootPartSize(self):
- return self._maxBootPartSize
-
- def validBootPartSize(self, size):
- """ Is the given size (in MB) acceptable for a boot device? """
+ def validBootLoaderPartSize(self, size):
+ """ Is the given size (in MB) acceptable for a bootloader device? """
if not isinstance(size, int) and not isinstance(size, float):
return False
- return ((not self.minBootPartSize or size >= self.minBootPartSize)
+ return ((self.bootloader.target_device_min_size is None or
+ size >= self.bootloader.target_device_min_size)
and
- (not self.maxBootPartSize or size <= self.maxBootPartSize))
+ (self.bootloader.target_device_max_size is None or
+ size <= self.bootloader.target_device_max_size))
def weight(self, fstype=None, mountpoint=None):
""" Given an fstype (as a string) or a mountpoint, return an integer
for the base sorting weight. This is used to modify the sort
algorithm for partition requests, mainly to make sure bootable
partitions and /boot are placed where they need to be."""
- return 0
-
-class EFI(Platform):
- _bootFSTypes = ["ext4", "ext3", "ext2"]
- _diskLabelType = "gpt"
- _minBootPartSize = 50
- _maxBootPartSize = 256
-
- def bootDevice(self):
- bootDev = None
-
- for part in self.anaconda.storage.partitions:
- if part.format.type == "efi" and self.validBootPartSize(part.size):
- bootDev = part
- # if we're only picking one, it might as well be the first
- break
-
- return bootDev
-
- def bootloaderChoices(self, bl):
- bootDev = self.bootDevice()
- ret = {}
-
- if not bootDev:
- return ret
-
- ret["boot"] = (bootDev.name, N_("EFI System Partition"))
- return ret
-
- def checkBootRequest(self, req):
- """ Perform architecture-specific checks on the boot device.
-
- Returns a list of error strings.
-
- NOTE: X86 does not have a separate checkBootRequest method,
- so this one must work for x86 as well as EFI.
- """
- if not req:
- return [_("You have not created a /boot/efi partition.")]
-
- errors = Platform.checkBootRequest(self, req)
-
- if req.format.mountpoint == "/boot/efi":
- if req.format.type != "efi":
- errors.append(_("/boot/efi is not EFI."))
+ if fstype in self.bootFSTypes and mountpoint == "/boot":
+ return 2000
+ else:
+ return 0
- # Don't try to check the disklabel on lv's etc, using lv for /boot
- # is already checked in the generic Platform.checkBootRequest()
- partitions = []
- if req.type == "partition":
- partitions = [ req ]
- elif req.type == "mdarray":
- partitions = filter(lambda d: d.type == "partition", req.parents)
-
- # Check that we've got a correct disk label.
- for p in partitions:
- partedDisk = p.disk.format.partedDisk
- labelType = self.diskLabelType(partedDisk.device.type)
- # Allow using gpt with x86, but not msdos with EFI
- if partedDisk.type != labelType and partedDisk.type != "gpt":
- errors.append(_("%s must have a %s disk label.")
- % (p.disk.name, labelType.upper()))
+class X86(Platform):
+ _bootloaderClass = bootloader.GRUB
def setDefaultPartitioning(self):
from storage.partspec import PartSpec
@@ -271,110 +206,37 @@ class EFI(Platform):
return ret
def weight(self, fstype=None, mountpoint=None):
- if fstype and fstype == "efi" or mountpoint and mountpoint == "/boot/efi":
+ score = Platform.weight(self, fstype=fstype, mountpoint=mountpoint)
+ if score:
+ return score
+ elif fstype == "efi" or mountpoint == "/boot/efi":
return 5000
- elif mountpoint and mountpoint == "/boot":
- return 2000
else:
return 0
-class Alpha(Platform):
- _diskLabelType = "bsd"
-
- def checkBootRequest(self, req):
- errors = Platform.checkBootRequest(self, req)
-
- if not req or req.type != "partition" or not req.disk:
- return errors
-
- disk = req.disk.format.partedDisk
-
- # Check that we're a BSD disk label
- if not disk.type == self._diskLabelType:
- errors.append(_("%s must have a bsd disk label.") % req.disk.name)
-
- # The first free space should start at the beginning of the drive and
- # span for a megabyte or more.
- free = disk.getFirstPartition()
- while free:
- if free.type & parted.PARTITION_FREESPACE:
- break
-
- free = free.nextPartition()
-
- if not free or free.geoemtry.start != 1L or free.getSize(unit="MB") < 1:
- errors.append(_("The disk %s requires at least 1MB of free space at the beginning.") % req.disk.name)
-
- return errors
-
-class IA64(EFI):
- _packages = ["elilo"]
-
- def __init__(self, anaconda):
- EFI.__init__(self, anaconda)
-
class PPC(Platform):
- _bootFSTypes = ["ext4", "ext3", "ext2"]
- _packages = ["yaboot"]
_ppcMachine = iutil.getPPCMachine()
- _supportsMdRaidBoot = True
+ _bootloaderClass = bootloader.Yaboot
bootPart = getattr(req, "partedPartition", None)
if not bootPart:
return errors
- # All of the above just checks the PPC PReP boot partitions. We still
- # need to make sure that whatever /boot is on also meets these criteria.
- if req == self.bootDevice():
- # However, this check only applies to prepboot.
- if bootPart.geometry.end * bootPart.geometry.device.sectorSize / (1024.0 * 1024) > 4096:
- errors.append(_("The boot partition must be within the first 4MB of the disk."))
+ if bootPart.geometry.end * bootPart.geometry.device.sectorSize / (1024.0 * 1024) > 10:
+ errors.append(_("The boot partition must be within the first 10MB of the disk."))
if not req or req.type != "partition" or not req.disk:
return errors
- disk = req.disk.format.partedDisk
-
- # Check that we're a Mac disk label
- if not disk.type == self._diskLabelType:
- errors.append(_("%s must have a mac disk label.") % req.disk.name)
-
- # All of the above just checks the appleboot partitions. We still
- # need to make sure that whatever /boot is on also meets these criteria.
- if req == self.bootDevice():
- try:
- req = self.anaconda.storage.mountpoints["/boot"]
- except KeyError:
- req = self.anaconda.storage.rootDevice
-
- return errors + self.checkBootRequest(req)
- else:
- return errors
+ return errors
def setDefaultPartitioning(self):
from storage.partspec import PartSpec
@@ -457,34 +274,24 @@ class NewWorldPPC(PPC):
return ret