# # platform.py: Architecture-specific information # # Copyright (C) 2009-2011 # Red Hat, Inc. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Authors: Chris Lumens # import os import logging log = logging.getLogger("anaconda") import parted from pyanaconda.storage import arch from flags import flags import gettext _ = lambda x: gettext.ldgettext("anaconda", x) N_ = lambda x: x class Platform(object): """Platform A class containing platform-specific information and methods for use during installation. The intent is to eventually encapsulate all the architecture quirks in one place to avoid lots of platform checks throughout anaconda.""" _minimumSector = 0 _packages = [] # requirements for bootloader stage1 devices _boot_stage1_device_types = [] _boot_stage1_format_types = [] _boot_stage1_mountpoints = [] _boot_stage1_max_end_mb = None _boot_stage1_raid_levels = [] _boot_stage1_raid_metadata = [] _boot_stage1_raid_member_types = [] _boot_stage1_description = N_("bootloader device") _boot_raid_description = N_("RAID Device") _boot_partition_description = N_("First sector of boot partition") _boot_descriptions = {} _disklabel_types = [] _non_linux_format_types = [] def __init__(self): """Creates a new Platform object. This is basically an abstract class. You should instead use one of the platform-specific classes as returned by getPlatform below. Not all subclasses need to provide all the methods in this class.""" if flags.gpt and "gpt" in self._disklabel_types: # move GPT to the top of the list self._disklabel_types.remove("gpt") self._disklabel_types.insert(0, "gpt") @property def diskLabelTypes(self): """A list of valid disklabel types for this architecture.""" return self._disklabel_types @property def defaultDiskLabelType(self): """The default disklabel type for this architecture.""" return self.diskLabelTypes[0] @property def bootStage1ConstraintDict(self): d = {"device_types": self._boot_stage1_device_types, "format_types": self._boot_stage1_format_types, "mountpoints": self._boot_stage1_mountpoints, "max_end_mb": self._boot_stage1_max_end_mb, "raid_levels": self._boot_stage1_raid_levels, "raid_metadata": self._boot_stage1_raid_metadata, "raid_member_types": self._boot_stage1_raid_member_types, "descriptions": self._boot_descriptions} return d def requiredDiskLabelType(self, device_type): return None 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) log.debug("required disklabel type for %s (%s) is %s" % (device.name, device.partedDevice.type, labelType)) if not labelType: # otherwise, use the first supported type for this platform # that is large enough to address the whole device labelType = self.defaultDiskLabelType log.debug("default disklabel type for %s is %s" % (device.name, labelType)) for lt in self.diskLabelTypes: l = parted.freshDisk(device=device.partedDevice, ty=lt) if l.maxPartitionStartSector > device.partedDevice.length: labelType = lt log.debug("selecting %s disklabel for %s based on size" % (labelType, device.name)) break return labelType @property def minimumSector(self, disk): """Return the minimum starting sector for the provided disk.""" return self._minimumSector @property def packages (self): _packages = self._packages if flags.cmdline.get('fips', None) == '1': _packages.append('dracut-fips') return _packages def setDefaultPartitioning(self): """Return the default platform-specific partitioning information.""" from storage.partspec import PartSpec return [PartSpec(mountpoint="/boot", size=500, weight=self.weight(mountpoint="/boot"))] 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.""" if mountpoint == "/boot": return 2000 else: return 0 class X86(Platform): _boot_stage1_device_types = ["disk"] _boot_mbr_description = N_("Master Boot Record") _boot_descriptions = {"disk": _boot_mbr_description, "partition": Platform._boot_partition_description, "mdarray": Platform._boot_raid_description} _disklabel_types = ["msdos", "gpt"] # XXX hpfs, if reported by blkid/udev, will end up with a type of None _non_linux_format_types = ["vfat", "ntfs", "hpfs"] def __init__(self): super(X86, self).__init__() def setDefaultPartitioning(self): """Return the default platform-specific partitioning information.""" from storage.partspec import PartSpec ret = Platform.setDefaultPartitioning(self) ret.append(PartSpec(fstype="biosboot", size=1, weight=self.weight(fstype="biosboot"))) return ret def weight(self, fstype=None, mountpoint=None): score = Platform.weight(self, fstype=fstype, mountpoint=mountpoint) if score: return score elif fstype == "biosboot": return 5000 else: return 0 class EFI(Platform): _boot_stage1_format_types = ["efi"] _boot_stage1_device_types = ["partition"] _boot_stage1_mountpoints = ["/boot/efi"] _boot_efi_description = N_("EFI System Partition") _boot_descriptions = {"partition": _boot_efi_description, "mdarray": Platform._boot_raid_description} _disklabel_types = ["gpt"] # XXX hpfs, if reported by blkid/udev, will end up with a type of None _non_linux_format_types = ["vfat", "ntfs", "hpfs"] def setDefaultPartitioning(self): from storage.partspec import PartSpec ret = Platform.setDefaultPartitioning(self) ret.append(PartSpec(mountpoint="/boot/efi", fstype="efi", size=20, maxSize=200, grow=True, weight=self.weight(fstype="efi"))) return ret def weight(self, fstype=None, mountpoint=None): score = Platform.weight(self, fstype=fstype, mountpoint=mountpoint) if score: return score elif fstype == "efi" or mountpoint == "/boot/efi": return 5000 else: return 0 class MacEFI(EFI): _boot_stage1_format_types = ["hfs+"] _boot_efi_description = N_("Apple EFI Boot Partition") _non_linux_format_types = ["hfs+"] _packages = ["mactel-boot"] def setDefaultPartitioning(self): from storage.partspec import PartSpec ret = Platform.setDefaultPartitioning(self) ret.append(PartSpec(mountpoint="/boot/efi", fstype="hfs+", size=20, maxSize=200, grow=True, weight=self.weight(mountpoint="/boot/efi"))) return ret class PPC(Platform): _ppcMachine = arch.getPPCMachine() _boot_stage1_device_types = ["partition"] @property def ppcMachine(self): return self._ppcMachine class IPSeriesPPC(PPC): _boot_stage1_format_types = ["prepboot"] _boot_stage1_max_end_mb = 10 _boot_prep_description = N_("PReP Boot Partition") _boot_descriptions = {"partition": _boot_prep_description} _disklabel_types = ["msdos"] def setDefaultPartitioning(self): from storage.partspec import PartSpec ret = PPC.setDefaultPartitioning(self) ret.append(PartSpec(fstype="prepboot", size=4, weight=self.weight(fstype="prepboot"))) return ret def weight(self, fstype=None, mountpoint=None): score = Platform.weight(self, fstype=fstype, mountpoint=mountpoint) if score: return score elif fstype == "prepboot": return 5000 else: return 0 class NewWorldPPC(PPC): _boot_stage1_format_types = ["appleboot"] _boot_apple_description = N_("Apple Bootstrap Partition") _boot_descriptions = {"partition": _boot_apple_description} _disklabel_types = ["mac"] _non_linux_format_types = ["hfs", "hfs+"] def setDefaultPartitioning(self): from storage.partspec import PartSpec ret = Platform.setDefaultPartitioning(self) ret.append(PartSpec(fstype="appleboot", size=1, maxSize=1, weight=self.weight(fstype="appleboot"))) return ret def weight(self, fstype=None, mountpoint=None): score = Platform.weight(self, fstype=fstype, mountpoint=mountpoint) if score: return score elif fstype == "appleboot": return 5000 else: return 0 class PS3(PPC): pass class S390(Platform): _packages = ["s390utils"] _disklabel_types = ["msdos", "dasd"] _boot_stage1_device_types = ["disk", "partition"] _boot_dasd_description = N_("DASD") _boot_zfcp_description = N_("zFCP") _boot_descriptions = {"dasd": _boot_dasd_description, "zfcp": _boot_zfcp_description, "partition": Platform._boot_partition_description} def __init__(self): Platform.__init__(self) def setDefaultPartitioning(self): """Return the default platform-specific partitioning information.""" from storage.partspec import PartSpec return [PartSpec(mountpoint="/boot", size=500, weight=self.weight(mountpoint="/boot"), lv=True, singlePV=True)] def requiredDiskLabelType(self, device_type): """The required disklabel type for the specified device type.""" if device_type == parted.DEVICE_DASD: return "dasd" return super(S390, self).requiredDiskLabelType(device_type) class Sparc(Platform): _boot_stage1_format_types = [] _boot_stage1_mountpoints = [] _boot_stage1_max_end_mb = None _disklabel_types = ["sun"] @property def minimumSector(self, disk): (cylinders, heads, sectors) = disk.device.biosGeometry start = long(sectors * heads) start /= long(1024 / disk.device.sectorSize) return start+1 class ARM(Platform): _armMachine = None _boot_stage1_device_types = ["disk"] _boot_mbr_description = N_("Master Boot Record") _boot_descriptions = {"disk": _boot_mbr_description, "partition": Platform._boot_partition_description} _disklabel_types = ["msdos"] @property def armMachine(self): if not self._armMachine: self._armMachine = arch.getARMMachine() return self._armMachine def weight(self, fstype=None, mountpoint=None): """Return the ARM platform-specific weight for the / partition. On ARM images '/' must be the last partition, so we try to weight it accordingly.""" if mountpoint == "/": return -100 else: return Platform.weight(self, fstype=fstype, mountpoint=mountpoint) class omapARM(ARM): _boot_stage1_format_types = ["vfat"] _boot_stage1_device_types = ["partition"] _boot_stage1_mountpoints = ["/boot/uboot"] _boot_uboot_description = N_("U-Boot Partition") _boot_descriptions = {"partition": _boot_uboot_description} def setDefaultPartitioning(self): """Return the ARM-OMAP platform-specific partitioning information.""" from storage.partspec import PartSpec ret = [PartSpec(mountpoint="/boot/uboot", fstype="vfat", size=20, maxSize=200, grow=True, weight=self.weight(fstype="vfat", mountpoint="/boot/uboot"))] ret.append(PartSpec(mountpoint="/", fstype="ext4", size=2000, maxSize=3000, weight=self.weight(mountpoint="/"))) return ret def weight(self, fstype=None, mountpoint=None): """Return the ARM-OMAP platform-specific weights for the uboot and / partitions. On OMAP, uboot must be the first partition, and '/' must be the last partition, so we try to weight them accordingly.""" if fstype == "vfat" and mountpoint == "/boot/uboot": return 6000 elif mountpoint == "/": return -100 else: return Platform.weight(self, fstype=fstype, mountpoint=mountpoint) def getPlatform(): """Check the architecture of the system and return an instance of a Platform subclass to match. If the architecture could not be determined, raise an exception.""" if arch.isPPC(): ppcMachine = arch.getPPCMachine() if (ppcMachine == "PMac" and arch.getPPCMacGen() == "NewWorld"): return NewWorldPPC() elif ppcMachine in ["iSeries", "pSeries"]: return IPSeriesPPC() elif ppcMachine == "PS3": return PS3() else: raise SystemError, "Unsupported PPC machine type: %s" % ppcMachine elif arch.isS390(): return S390() elif arch.isSparc(): return Sparc() elif arch.isEfi(): if arch.isMactel(): return MacEFI() else: return EFI() elif arch.isX86(): return X86() elif arch.isARM(): armMachine = arch.getARMMachine() if armMachine == "omap": return omapARM() else: return ARM() else: raise SystemError, "Could not determine system architecture."