summaryrefslogtreecommitdiffstats
path: root/pyanaconda/storage
diff options
context:
space:
mode:
Diffstat (limited to 'pyanaconda/storage')
-rw-r--r--pyanaconda/storage/__init__.py27
-rw-r--r--pyanaconda/storage/dasd.py2
-rw-r--r--pyanaconda/storage/devicetree.py8
-rw-r--r--pyanaconda/storage/fcoe.py2
-rw-r--r--pyanaconda/storage/formats/biosboot.py4
-rw-r--r--pyanaconda/storage/formats/fs.py8
-rw-r--r--pyanaconda/storage/formats/prepboot.py4
-rw-r--r--pyanaconda/storage/partitioning.py3
-rw-r--r--pyanaconda/storage/platform.py409
-rw-r--r--pyanaconda/storage/pyudev.py232
-rw-r--r--pyanaconda/storage/tsort.py105
-rw-r--r--pyanaconda/storage/udev.py69
12 files changed, 833 insertions, 40 deletions
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py
index f6cd38096..bc973e3cd 100644
--- a/pyanaconda/storage/__init__.py
+++ b/pyanaconda/storage/__init__.py
@@ -41,7 +41,6 @@ import nss.nss
import parted
from pykickstart.constants import *
-from pyanaconda import tsort
from storage_log import log_method_call
from errors import *
@@ -69,6 +68,7 @@ import dasd
import util
import arch
from flags import flags
+from platform import platform as _platform
import shelve
import contextlib
@@ -278,17 +278,14 @@ class StorageDiscoveryConfig(object):
self.zeroMbr = ksdata.zerombr.zerombr
class Storage(object):
- def __init__(self, data=None, platform=None):
+ def __init__(self, data=None):
""" Create a Storage instance.
Keyword Arguments:
data - a pykickstart Handler instance
- platform - a Platform instance
-
"""
self.data = data
- self.platform = platform
self._bootloader = None
self.config = StorageDiscoveryConfig()
@@ -784,9 +781,6 @@ class Storage(object):
- Needs some error handling
"""
- if not hasattr(self.platform, "diskLabelTypes"):
- raise StorageError("can't clear partitions without platform data")
-
# 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
@@ -842,10 +836,7 @@ class Storage(object):
destroy_action = ActionDestroyFormat(disk)
self.devicetree.registerAction(destroy_action)
- if self.platform:
- labelType = self.platform.bestDiskLabelType(disk)
- else:
- labelType = None
+ labelType = _platform.bestDiskLabelType(disk)
# create a new disklabel on the disk
newLabel = getFormat("disklabel", device=disk.path,
@@ -1049,7 +1040,7 @@ class Storage(object):
if fmt:
mountpoint = getattr(fmt, "mountpoint", None)
- kwargs["weight"] = self.platform.weight(mountpoint=mountpoint,
+ kwargs["weight"] = _platform.weight(mountpoint=mountpoint,
fstype=fmt.type)
@@ -1569,7 +1560,7 @@ class Storage(object):
#
# check that GPT boot disk on BIOS system has a BIOS boot partition
#
- if self.platform.weight(fstype="biosboot") and \
+ if _platform.weight(fstype="biosboot") and \
stage1 and stage1.isDisk and \
getattr(stage1.format, "labelType", None) == "gpt":
missing = True
@@ -1645,8 +1636,7 @@ class Storage(object):
@property
def packages(self):
pkgs = set()
- if self.platform:
- pkgs.update(self.platform.packages)
+ pkgs.update(_platform.packages)
if self.bootloader:
pkgs.update(self.bootloader.packages)
@@ -1691,9 +1681,8 @@ class Storage(object):
@property
def bootloader(self):
- if self._bootloader is None and self.platform is not None and \
- flags.installer_mode:
- self._bootloader = get_bootloader(self.platform)
+ if self._bootloader is None and flags.installer_mode:
+ self._bootloader = get_bootloader()
return self._bootloader
diff --git a/pyanaconda/storage/dasd.py b/pyanaconda/storage/dasd.py
index abf97b90f..bd3d4debc 100644
--- a/pyanaconda/storage/dasd.py
+++ b/pyanaconda/storage/dasd.py
@@ -26,7 +26,7 @@ from pyanaconda.storage.devices import deviceNameToDiskByPath
from pyanaconda.storage import util
from pyanaconda.storage import arch
from . import ROOT_PATH
-from pyanaconda.baseudev import udev_trigger
+from .udev import udev_trigger
import logging
log = logging.getLogger("storage")
diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py
index 0a5e7cdb5..f0d49f727 100644
--- a/pyanaconda/storage/devicetree.py
+++ b/pyanaconda/storage/devicetree.py
@@ -41,8 +41,8 @@ import devicelibs.loop
import devicelibs.edd
from udev import *
import util
-from pyanaconda import platform
-from pyanaconda import tsort
+from platform import platform
+import tsort
from flags import flags
from storage_log import log_method_call, log_method_return
import parted
@@ -97,8 +97,6 @@ class DeviceTree(object):
self.dasd = dasd
self.mpathFriendlyNames = getattr(conf, "mpathFriendlyNames", True)
- self.platform = platform.getPlatform()
-
self.diskImages = {}
images = getattr(conf, "diskImages", {})
if images:
@@ -1064,7 +1062,7 @@ class DeviceTree(object):
# we're going to pass the "best" disklabel type into the DiskLabel
# constructor, but it only has meaning for non-existent disklabels.
- labelType = self.platform.bestDiskLabelType(device)
+ labelType = platform.bestDiskLabelType(device)
try:
format = getFormat("disklabel",
diff --git a/pyanaconda/storage/fcoe.py b/pyanaconda/storage/fcoe.py
index 22c6258b2..8e8e301e6 100644
--- a/pyanaconda/storage/fcoe.py
+++ b/pyanaconda/storage/fcoe.py
@@ -19,7 +19,7 @@
import os
from . import util
-from pyanaconda.baseudev import udev_settle
+from .udev import udev_settle
#from pyanaconda import isys
from . import ROOT_PATH
import logging
diff --git a/pyanaconda/storage/formats/biosboot.py b/pyanaconda/storage/formats/biosboot.py
index 861b59190..c3aef8d2b 100644
--- a/pyanaconda/storage/formats/biosboot.py
+++ b/pyanaconda/storage/formats/biosboot.py
@@ -23,6 +23,7 @@
from parted import PARTITION_BIOS_GRUB
from ..errors import *
+from .. import platform
from . import DeviceFormat, register_device_format
class BIOSBoot(DeviceFormat):
@@ -53,8 +54,7 @@ class BIOSBoot(DeviceFormat):
@property
def supported(self):
- from pyanaconda import platform
- return isinstance(platform.getPlatform(), platform.X86)
+ return isinstance(platform.platform, platform.X86)
register_device_format(BIOSBoot)
diff --git a/pyanaconda/storage/formats/fs.py b/pyanaconda/storage/formats/fs.py
index 1d355c3be..b472e8727 100644
--- a/pyanaconda/storage/formats/fs.py
+++ b/pyanaconda/storage/formats/fs.py
@@ -35,6 +35,7 @@ import selinux
from ..errors import *
from . import DeviceFormat, register_device_format
from .. import util
+from .. import platform
from ..flags import flags
from parted import fileSystemType
from ..storage_log import log_method_call
@@ -975,9 +976,7 @@ class EFIFS(FATFS):
@property
def supported(self):
- from pyanaconda import platform
- p = platform.getPlatform()
- return (isinstance(p, platform.EFI) and
+ return (isinstance(platform.platform, platform.EFI) and
self.utilsAvailable)
register_device_format(EFIFS)
@@ -1210,8 +1209,7 @@ class AppleBootstrapFS(HFS):
@property
def supported(self):
- from pyanaconda import platform
- return (isinstance(platform.getPlatform(), platform.NewWorldPPC)
+ return (isinstance(platform.platform, platform.NewWorldPPC)
and self.utilsAvailable)
register_device_format(AppleBootstrapFS)
diff --git a/pyanaconda/storage/formats/prepboot.py b/pyanaconda/storage/formats/prepboot.py
index 884aa8fc4..dd2cf8c82 100644
--- a/pyanaconda/storage/formats/prepboot.py
+++ b/pyanaconda/storage/formats/prepboot.py
@@ -21,6 +21,7 @@
#
from ..errors import *
+from .. import platform
from . import DeviceFormat, register_device_format
from parted import PARTITION_PREP
import os
@@ -80,8 +81,7 @@ class PPCPRePBoot(DeviceFormat):
@property
def supported(self):
- from pyanaconda import platform
- return isinstance(platform.getPlatform(), platform.IPSeriesPPC)
+ return isinstance(platform.platform, platform.IPSeriesPPC)
register_device_format(PPCPRePBoot)
diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py
index 64aa250eb..243c62b80 100644
--- a/pyanaconda/storage/partitioning.py
+++ b/pyanaconda/storage/partitioning.py
@@ -721,9 +721,6 @@ def doPartitioning(storage):
None
"""
- if not hasattr(storage.platform, "diskLabelTypes"):
- raise StorageError(_("can't allocate partitions without platform data"))
-
disks = storage.partitioned
if storage.config.exclusiveDisks:
disks = [d for d in disks if d.name in storage.config.exclusiveDisks]
diff --git a/pyanaconda/storage/platform.py b/pyanaconda/storage/platform.py
new file mode 100644
index 000000000..5221a3065
--- /dev/null
+++ b/pyanaconda/storage/platform.py
@@ -0,0 +1,409 @@
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Authors: Chris Lumens <clumens@redhat.com>
+#
+import os
+import logging
+log = logging.getLogger("storage")
+
+import parted
+
+from . import arch
+from .flags import flags
+from .partspec import PartSpec
+
+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")
+
+ def __call__(self):
+ return self
+
+ @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.boot_cmdline.get('fips', None) == '1':
+ _packages.append('dracut-fips')
+ return _packages
+
+ def setDefaultPartitioning(self):
+ """Return the default platform-specific partitioning information."""
+ 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."""
+ 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):
+ 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):
+ 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):
+ 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):
+ 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."""
+ 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."""
+ 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."
+
+global platform
+platform = getPlatform()
diff --git a/pyanaconda/storage/pyudev.py b/pyanaconda/storage/pyudev.py
new file mode 100644
index 000000000..705b93d80
--- /dev/null
+++ b/pyanaconda/storage/pyudev.py
@@ -0,0 +1,232 @@
+from __future__ import print_function
+
+import sys
+import os
+import fnmatch
+from ctypes import *
+
+
+# XXX this one may need some tweaking...
+def find_library(name, somajor=0):
+ env = os.environ.get("LD_LIBRARY_PATH")
+ common = ["/lib64", "/lib"]
+
+ if env:
+ libdirs = env.split(":") + common
+ else:
+ libdirs = common
+
+ libdirs = filter(os.path.isdir, libdirs)
+
+ for dir in libdirs:
+ files = fnmatch.filter(os.listdir(dir), "lib%s.so.%d" % (name, somajor))
+ files = [os.path.join(dir, file) for file in files]
+
+ if files:
+ break
+
+ if files:
+ return files[0]
+ else:
+ return None
+
+# find the udev library
+name = "udev"
+somajor = 1
+libudev = find_library(name=name, somajor=somajor)
+
+if not libudev or not os.path.exists(libudev):
+ raise ImportError, "No library named %s.%d" % (name, somajor)
+
+# load the udev library
+libudev = CDLL(libudev)
+
+
+# create aliases for needed functions and set the return types where needed
+libudev_udev_new = libudev.udev_new
+libudev_udev_new.argtypes = []
+libudev_udev_new.restype = c_void_p
+libudev_udev_unref = libudev.udev_unref
+libudev_udev_unref.argtypes = [ c_void_p ]
+
+libudev_udev_device_new_from_syspath = libudev.udev_device_new_from_syspath
+libudev_udev_device_new_from_syspath.restype = c_void_p
+libudev_udev_device_new_from_syspath.argtypes = [ c_void_p, c_char_p ]
+libudev_udev_device_unref = libudev.udev_device_unref
+libudev_udev_device_unref.argtypes = [ c_void_p ]
+
+libudev_udev_device_get_syspath = libudev.udev_device_get_syspath
+libudev_udev_device_get_syspath.restype = c_char_p
+libudev_udev_device_get_syspath.argtypes = [ c_void_p ]
+libudev_udev_device_get_sysname = libudev.udev_device_get_sysname
+libudev_udev_device_get_sysname.restype = c_char_p
+libudev_udev_device_get_sysname.argtypes = [ c_void_p ]
+libudev_udev_device_get_devpath = libudev.udev_device_get_devpath
+libudev_udev_device_get_devpath.restype = c_char_p
+libudev_udev_device_get_devpath.argtypes = [ c_void_p ]
+libudev_udev_device_get_devtype = libudev.udev_device_get_devtype
+libudev_udev_device_get_devtype.restype = c_char_p
+libudev_udev_device_get_devtype.argtypes = [ c_void_p ]
+libudev_udev_device_get_devnode = libudev.udev_device_get_devnode
+libudev_udev_device_get_devnode.restype = c_char_p
+libudev_udev_device_get_devnode.argtypes = [ c_void_p ]
+libudev_udev_device_get_subsystem = libudev.udev_device_get_subsystem
+libudev_udev_device_get_subsystem.restype = c_char_p
+libudev_udev_device_get_subsystem.argtypes = [ c_void_p ]
+libudev_udev_device_get_sysnum = libudev.udev_device_get_sysnum
+libudev_udev_device_get_sysnum.restype = c_char_p
+libudev_udev_device_get_sysnum.argtypes = [ c_void_p ]
+
+libudev_udev_device_get_properties_list_entry = libudev.udev_device_get_properties_list_entry
+libudev_udev_device_get_properties_list_entry.restype = c_void_p
+libudev_udev_device_get_properties_list_entry.argtypes = [ c_void_p ]
+libudev_udev_list_entry_get_next = libudev.udev_list_entry_get_next
+libudev_udev_list_entry_get_next.restype = c_void_p
+libudev_udev_list_entry_get_next.argtypes = [ c_void_p ]
+
+libudev_udev_list_entry_get_name = libudev.udev_list_entry_get_name
+libudev_udev_list_entry_get_name.restype = c_char_p
+libudev_udev_list_entry_get_name.argtypes = [ c_void_p ]
+libudev_udev_list_entry_get_value = libudev.udev_list_entry_get_value
+libudev_udev_list_entry_get_value.restype = c_char_p
+libudev_udev_list_entry_get_value.argtypes = [ c_void_p ]
+
+libudev_udev_enumerate_new = libudev.udev_enumerate_new
+libudev_udev_enumerate_new.restype = c_void_p
+libudev_udev_enumerate_new.argtypes = [ c_void_p ]
+libudev_udev_enumerate_unref = libudev.udev_enumerate_unref
+libudev_udev_enumerate_unref.argtypes = [ c_void_p ]
+
+libudev_udev_enumerate_add_match_subsystem = libudev.udev_enumerate_add_match_subsystem
+libudev_udev_enumerate_add_match_subsystem.restype = c_int
+libudev_udev_enumerate_add_match_subsystem.argtypes = [ c_void_p, c_char_p ]
+libudev_udev_enumerate_scan_devices = libudev.udev_enumerate_scan_devices
+libudev_udev_enumerate_scan_devices.restype = c_int
+libudev_udev_enumerate_scan_devices.argtypes = [ c_void_p ]
+libudev_udev_enumerate_get_list_entry = libudev.udev_enumerate_get_list_entry
+libudev_udev_enumerate_get_list_entry.restype = c_void_p
+libudev_udev_enumerate_get_list_entry.argtypes = [ c_void_p ]
+
+libudev_udev_device_get_devlinks_list_entry = libudev.udev_device_get_devlinks_list_entry
+libudev_udev_device_get_devlinks_list_entry.restype = c_void_p
+libudev_udev_device_get_devlinks_list_entry.argtypes = [ c_void_p ]
+
+
+class UdevDevice(dict):
+
+ def __init__(self, udev, sysfs_path):
+ dict.__init__(self)
+
+ # create new udev device from syspath
+ udev_device = libudev_udev_device_new_from_syspath(udev, sysfs_path)
+ if not udev_device:
+ # device does not exist
+ return
+
+ # set syspath and sysname properties
+ self.syspath = libudev_udev_device_get_syspath(udev_device)
+ self.sysname = libudev_udev_device_get_sysname(udev_device)
+
+ # get the devlinks list
+ devlinks = []
+ devlinks_entry = libudev_udev_device_get_devlinks_list_entry(udev_device)
+
+ while devlinks_entry:
+ path = libudev_udev_list_entry_get_name(devlinks_entry)
+ devlinks.append(path)
+
+ devlinks_entry = libudev_udev_list_entry_get_next(devlinks_entry)
+
+ # add devlinks list to the dictionary
+ self["symlinks"] = devlinks
+
+ # get the first property entry
+ property_entry = libudev_udev_device_get_properties_list_entry(udev_device)
+
+ while property_entry:
+ name = libudev_udev_list_entry_get_name(property_entry)
+ value = libudev_udev_list_entry_get_value(property_entry)
+
+ # lvm outputs values for multiple lvs in one line
+ # we want to split them and make a list
+ # if the first lv's value is empty we end up with a value starting
+ # with name=, prepend a space that our split does the right thing
+ if value.startswith("%s=" % name):
+ value = " " + value
+
+ if value.count(" %s=" % name):
+ value = value.split(" %s=" % name)
+
+ self[name] = value
+
+ # get next property entry
+ property_entry = libudev_udev_list_entry_get_next(property_entry)
+
+ # set additional properties
+ self.devpath = libudev_udev_device_get_devpath(udev_device)
+ self.subsystem = libudev_udev_device_get_subsystem(udev_device)
+ self.devtype = libudev_udev_device_get_devtype(udev_device)
+ self.sysnum = libudev_udev_device_get_sysnum(udev_device)
+ self.devnode = libudev_udev_device_get_devnode(udev_device)
+
+ # cleanup
+ libudev_udev_device_unref(udev_device)
+
+
+class Udev(object):
+
+ def __init__(self):
+ self.udev = libudev_udev_new()
+
+ def create_device(self, sysfs_path):
+ return UdevDevice(self.udev, sysfs_path)
+
+ def enumerate_devices(self, subsystem=None):
+ enumerate = libudev_udev_enumerate_new(self.udev)
+
+ # add the match subsystem
+ if subsystem is not None:
+ rc = libudev_udev_enumerate_add_match_subsystem(enumerate, subsystem)
+ if not rc == 0:
+ print("error: unable to add the match subsystem", file=sys.stderr)
+ libudev_udev_enumerate_unref(enumerate)
+ return []
+
+ # scan the devices
+ rc = libudev_udev_enumerate_scan_devices(enumerate)
+ if not rc == 0:
+ print("error: unable to enumerate the devices", file=sys.stderr)
+ libudev_udev_enumerate_unref(enumerate)
+ return []
+
+ # create the list of sysfs paths
+ sysfs_paths = []
+
+ # get the first list entry
+ list_entry = libudev_udev_enumerate_get_list_entry(enumerate)
+
+ while list_entry:
+ sysfs_path = libudev_udev_list_entry_get_name(list_entry)
+ sysfs_paths.append(sysfs_path)
+
+ # get next list entry
+ list_entry = libudev_udev_list_entry_get_next(list_entry)
+
+ # cleanup
+ libudev_udev_enumerate_unref(enumerate)
+
+ return sysfs_paths
+
+ def scan_devices(self, sysfs_paths=None):
+ if sysfs_paths is None:
+ sysfs_paths = self.enumerate_devices()
+
+ for sysfs_path in sysfs_paths:
+ device = self.create_device(sysfs_path)
+
+ if device:
+ yield device
+
+ def unref(self):
+ libudev_udev_unref(self.udev)
+ self.udev = None
diff --git a/pyanaconda/storage/tsort.py b/pyanaconda/storage/tsort.py
new file mode 100644
index 000000000..52a871f56
--- /dev/null
+++ b/pyanaconda/storage/tsort.py
@@ -0,0 +1,105 @@
+#!/usr/bin/python
+# tsort.py
+# Topological sorting.
+#
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties 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, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+class CyclicGraphError(Exception):
+ pass
+
+def tsort(graph):
+ order = [] # sorted list of items
+
+ if not graph or not graph['items']:
+ return order
+
+ # determine which nodes have no incoming edges
+ roots = [n for n in graph['items'] if graph['incoming'][n] == 0]
+ if not roots:
+ raise CyclicGraphError("no root nodes")
+
+ visited = [] # list of nodes visited, for cycle detection
+ while roots:
+ # remove a root, add it to the order
+ root = roots.pop()
+ if root in visited:
+ raise CyclicGraphError("graph contains cycles")
+
+ visited.append(root)
+ i = graph['items'].index(root)
+ order.append(root)
+ # remove each edge from the root to another node
+ for (parent, child) in [e for e in graph['edges'] if e[0] == root]:
+ graph['incoming'][child] -= 1
+ graph['edges'].remove((parent, child))
+ # if destination node is now a root, add it to roots
+ if graph['incoming'][child] == 0:
+ roots.append(child)
+
+ if len(graph['items']) != len(visited):
+ raise CyclicGraphError("graph contains cycles")
+
+
+ return order
+
+def create_graph(items, edges):
+ """ Create a graph based on a list of items and a list of edges.
+
+ Arguments:
+
+ items - an iterable containing (hashable) items to sort
+ edges - an iterable containing (parent, child) edge pair tuples
+
+ Return Value:
+
+ The return value is a dictionary representing the directed graph.
+ It has three keys:
+
+ items is the same as the input argument of the same name
+ edges is the same as the input argument of the same name
+ incoming is a dict of incoming edge count hashed by item
+
+ """
+ graph = {'items': [], # the items to sort
+ 'edges': [], # partial order info: (parent, child) pairs
+ 'incoming': {}} # incoming edge count for each item
+
+ graph['items'] = items
+ graph['edges'] = edges
+ for item in items:
+ graph['incoming'][item] = 0
+
+ for (parent, child) in edges:
+ graph['incoming'][child] += 1
+
+ return graph
+
+
+if __name__ == "__main__":
+
+ items = [5, 2, 3, 4, 1]
+ edges = [(1, 2), (2, 4), (4, 5), (3, 2)]
+
+ print items
+ print edges
+
+ graph = create_graph(items, edges)
+ print tsort(graph)
+
diff --git a/pyanaconda/storage/udev.py b/pyanaconda/storage/udev.py
index cb9601b1e..f8ba09c9b 100644
--- a/pyanaconda/storage/udev.py
+++ b/pyanaconda/storage/udev.py
@@ -1,7 +1,7 @@
# udev.py
# Python module for querying the udev database for device information.
#
-# Copyright (C) 2009 Red Hat, Inc.
+# Copyright (C) 2009, 2013 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
@@ -18,6 +18,7 @@
# Red Hat, Inc.
#
# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+# Chris Lumens <clumens@redhat.com>
#
import os
@@ -25,11 +26,75 @@ import re
import util
from errors import *
-from pyanaconda.baseudev import *
+
+import pyudev
+global_udev = pyudev.Udev()
import logging
log = logging.getLogger("storage")
+def udev_enumerate_devices(deviceClass="block"):
+ devices = global_udev.enumerate_devices(subsystem=deviceClass)
+ return [path[4:] for path in devices]
+
+def udev_get_device(sysfs_path):
+ if not os.path.exists("/sys%s" % sysfs_path):
+ log.debug("%s does not exist" % sysfs_path)
+ return None
+
+ # XXX we remove the /sys part when enumerating devices,
+ # so we have to prepend it when creating the device
+ dev = global_udev.create_device("/sys" + sysfs_path)
+
+ if dev:
+ dev["name"] = dev.sysname
+ dev["sysfs_path"] = sysfs_path
+
+ # now add in the contents of the uevent file since they're handy
+ dev = udev_parse_uevent_file(dev)
+
+ return dev
+
+def udev_get_devices(deviceClass="block"):
+ udev_settle()
+ entries = []
+ for path in udev_enumerate_devices(deviceClass):
+ entry = udev_get_device(path)
+ if entry:
+ entries.append(entry)
+ return entries
+
+def udev_parse_uevent_file(dev):
+ path = os.path.normpath("/sys/%s/uevent" % dev['sysfs_path'])
+ if not os.access(path, os.R_OK):
+ return dev
+
+ with open(path) as f:
+ for line in f.readlines():
+ (key, equals, value) = line.strip().partition("=")
+ if not equals:
+ continue
+
+ dev[key] = value
+
+ return dev
+
+def udev_settle():
+ # wait maximal 300 seconds for udev to be done running blkid, lvm,
+ # mdadm etc. This large timeout is needed when running on machines with
+ # lots of disks, or with slow disks
+ util.run_program(["udevadm", "settle", "--timeout=300"])
+
+def udev_trigger(subsystem=None, action="add", name=None):
+ argv = ["trigger", "--action=%s" % action]
+ if subsystem:
+ argv.append("--subsystem-match=%s" % subsystem)
+ if name:
+ argv.append("--sysname-match=%s" % name)
+
+ util.run_program(["udevadm"] + argv)
+ udev_settle()
+
def udev_resolve_devspec(devspec):
if not devspec:
return None