diff options
author | David Lehman <dlehman@redhat.com> | 2013-01-14 17:48:37 -0600 |
---|---|---|
committer | David Lehman <dlehman@redhat.com> | 2013-01-28 13:15:31 -0600 |
commit | 9040049d8d232eae3f0f51ffe442dbe49d273bce (patch) | |
tree | 5966597dd00948b4eae345a9dc8b509ebb0dab00 | |
parent | e6c6261e1d7e912103ef1618e4a84c5f70abb00a (diff) | |
download | anaconda-9040049d8d232eae3f0f51ffe442dbe49d273bce.tar.gz anaconda-9040049d8d232eae3f0f51ffe442dbe49d273bce.tar.xz anaconda-9040049d8d232eae3f0f51ffe442dbe49d273bce.zip |
Remove the storage module and replace it with blivet.
88 files changed, 131 insertions, 24605 deletions
@@ -409,8 +409,8 @@ def setupDisplay(anaconda, opts, addon_paths=None): from pyanaconda.ui.tui.spokes.askvnc import AskVNCSpoke from pykickstart.constants import DISPLAY_MODE_TEXT from pyanaconda import network - from pyanaconda.storage import arch - from pyanaconda.storage import util + from blivet import arch + import blivet.util graphical_failed = 0 vncS = vnc.VncServer() # The vnc Server object. @@ -458,7 +458,7 @@ def setupDisplay(anaconda, opts, addon_paths=None): vncS.vncconnectport = anaconda.ksdata.vnc.port # disable VNC over text question when not enough memory is available - if util.total_memory() < isys.MIN_GUI_RAM: + if blivet.util.total_memory() < isys.MIN_GUI_RAM: flags.vncquestion = False # disable VNC question if text mode is requested and this is a ks install @@ -930,8 +930,8 @@ if __name__ == "__main__": from pyanaconda.threads import initThreading, threadMgr, AnacondaThread initThreading() - from pyanaconda import storage - storage.enable_installer_mode() + import blivet + blivet.enable_installer_mode() # now start the interface setupDisplay(anaconda, opts, addon_paths) @@ -977,7 +977,7 @@ if __name__ == "__main__": # this is lame, but make things match what we expect (#443408) opts.lang = opts.lang.replace(".utf8", ".UTF-8") - from pyanaconda.storage import storageInitialize + from blivet import storageInitialize from pyanaconda.packaging import payloadInitialize from pyanaconda.network import networkInitialize, wait_for_connecting_NM_thread diff --git a/anaconda.spec.in b/anaconda.spec.in index aafa8acb8..072badb79 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -19,41 +19,26 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) # Versions of required components (done so we make sure the buildrequires # match the requires versions of things). -%define dmver 1.02.17-6 %define gettextver 0.11 -%define genisoimagever 1.1.9-4 %define gconfversion 2.28.1 %define intltoolver 0.31.2-3 %define libnlver 1.0 -%define libselinuxver 1.6 %define pykickstartver 1.99.22 -%define rpmpythonver 4.2-0.61 %define yumver 3.4.3-32 %define partedver 1.8.1 %define pypartedver 2.5-2 %define pythonpyblockver 0.45 -%define e2fsver 1.41.0 %define nmver 1:0.7.1-3.git20090414 %define dbusver 1.2.3 -%define createrepover 0.4.7 %define yumutilsver 1.1.11-3 -%define iscsiver 6.2.0.870-3 -%define pythoncryptsetupver 0.1.1 %define mehver 0.21-1 %define sckeyboardver 1.3.1 -%define libblkidver 2.17.1-1 -%define fcoeutilsver 1.0.12-3.20100323git %define firewalldver 0.2.9-1 %define pythonurlgrabberver 3.9.1-5 %define utillinuxver 2.15.1 -%define syslinuxver 3.73 %define dracutver 024-16 BuildRequires: audit-libs-devel -BuildRequires: bzip2-devel -BuildRequires: device-mapper-devel >= %{dmver} -BuildRequires: e2fsprogs-devel >= %{e2fsver} -BuildRequires: elfutils-devel BuildRequires: gettext >= %{gettextver} BuildRequires: gtk3-devel BuildRequires: gtk-doc @@ -61,30 +46,19 @@ BuildRequires: gobject-introspection-devel BuildRequires: glade-devel BuildRequires: pygobject3 BuildRequires: intltool >= %{intltoolver} -BuildRequires: libarchive-devel BuildRequires: libX11-devel BuildRequires: libXt-devel BuildRequires: libXxf86misc-devel -BuildRequires: libblkid-devel >= %{libblkidver} -BuildRequires: libcurl-devel BuildRequires: libgnomekbd-devel BuildRequires: libnl-devel >= %{libnlver} -BuildRequires: libselinux-devel >= %{libselinuxver} -BuildRequires: libsepol-devel BuildRequires: libxklavier-devel -BuildRequires: libxml2-python BuildRequires: pango-devel BuildRequires: pykickstart >= %{pykickstartver} BuildRequires: python-devel -BuildRequires: python-pyblock >= %{pythonpyblockver} BuildRequires: python-urlgrabber >= %{pythonurlgrabberver} BuildRequires: python-nose -BuildRequires: rpm-devel -BuildRequires: rpm-python >= %{rpmpythonver} BuildRequires: systemd -BuildRequires: xmlto BuildRequires: yum >= %{yumver} -BuildRequires: zlib-devel BuildRequires: NetworkManager-devel >= %{nmver} BuildRequires: NetworkManager-glib-devel >= %{nmver} BuildRequires: dbus-devel >= %{dbusver} @@ -92,41 +66,26 @@ BuildRequires: dbus-python %ifarch %livearches BuildRequires: desktop-file-utils %endif -BuildRequires: iscsi-initiator-utils-devel >= %{iscsiver} %ifarch s390 s390x BuildRequires: s390utils-devel %endif Requires: anaconda-widgets = %{version}-%{release} +Requires: python-blivet Requires: gnome-icon-theme-symbolic Requires: python-meh >= %{mehver} -Requires: policycoreutils +Requires: libselinux-python Requires: rpm-python >= %{rpmpythonver} Requires: parted >= %{partedver} Requires: pyparted >= %{pypartedver} Requires: yum >= %{yumver} -Requires: libxml2-python Requires: python-urlgrabber >= %{pythonurlgrabberver} Requires: system-logos Requires: pykickstart >= %{pykickstartver} -Requires: device-mapper >= %{dmver} -Requires: device-mapper-libs >= %{dmver} -Requires: dosfstools -Requires: e2fsprogs >= %{e2fsver} -Requires: gzip -Requires: libarchive Requires: python-babel -%ifarch %{ix86} x86_64 ia64 -Requires: dmidecode -%endif -Requires: python-pyblock >= %{pythonpyblockver} Requires: libuser-python Requires: authconfig Requires: firewalld >= %{firewalldver} -Requires: cryptsetup-luks -Requires: python-cryptsetup >= %{pythoncryptsetupver} -Requires: mdadm -Requires: lvm2 Requires: util-linux >= %{utillinuxver} Requires: dbus-python Requires: python-pwquality @@ -140,18 +99,7 @@ Requires: libgnomekbd Requires: usermode Requires: zenity %endif -Requires: createrepo >= %{createrepover} -Requires: squashfs-tools -%if ! 0%{?rhel} -Requires: hfsplus-tools -%endif -Requires: genisoimage >= %{genisoimagever} Requires: GConf2 >= %{gconfversion} -%ifarch %{ix86} x86_64 -Requires: syslinux >= %{syslinuxver} -Requires: makebootfat -Requires: device-mapper -%endif %ifarch s390 s390x Requires: openssh %endif @@ -162,21 +110,11 @@ Requires: nm-connection-editor Requires: dhclient Requires: anaconda-yum-plugins Requires: libselinux-python >= %{libselinuxver} -%ifnarch s390 s390x -Requires: fcoe-utils >= %{fcoeutilsver} -%endif -Requires: device-mapper-multipath Requires: kbd Requires: chrony Requires: rdate Requires: rsync Requires: hostname -%ifarch %{sparc} -Requires: elftoaout piggyback -%endif -%ifarch x86_64 -Requires: mactel-boot -%endif Obsoletes: anaconda-images <= 10 Provides: anaconda-images = %{version}-%{release} Obsoletes: anaconda-runtime < %{version}-%{release} diff --git a/configure.ac b/configure.ac index 3b633fd37..81c334d20 100644 --- a/configure.ac +++ b/configure.ac @@ -111,10 +111,7 @@ PKG_CHECK_MODULES([GDK], [gdk-3.0]) PKG_CHECK_MODULES([NETWORKMANAGER], [NetworkManager >= 0.7.1]) PKG_CHECK_MODULES([LIBNL], [libnl-1 >= 1.0]) PKG_CHECK_MODULES([LIBNM_GLIB], [libnm-glib >= 0.7.1 libnm-util >= 0.7.1]) -PKG_CHECK_MODULES([DEVMAPPER], [devmapper >= 1.02.17]) PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.19.5]) -PKG_CHECK_MODULES([RPM], [rpm >= 4.8.0]) -PKG_CHECK_MODULES([LIBARCHIVE], [libarchive >= 2.7.902a]) # Set $RPM_OPT_FLAGS if we don't have it if test -z $RPM_OPT_FLAGS ; then @@ -123,32 +120,12 @@ else CFLAGS="$CFLAGS $RPM_OPT_FLAGS" fi -# SELinux support can be enabled or disabled -AC_ARG_ENABLE(selinux, - AC_HELP_STRING([--enable-selinux], - [enable SELinux support (default is yes)]), - [selinux=$enableval], - [selinux=yes]) -if test x$selinux = xyes ; then - AC_CHECK_LIB([selinux], [matchpathcon], [], - [AC_MSG_FAILURE([*** libselinux not usable.])]) - AC_CHECK_LIB([selinux], [lsetfilecon], [], - [AC_MSG_FAILURE([*** libselinux not usable.])]) - - selinux_libs="-lselinux -laudit" - AC_SUBST(SELINUX_CFLAGS, [-DUSESELINUX=1]) - AC_SUBST(SELINUX_LIBS, [$selinux_libs]) -fi - # NFS support can, in theory, be enabled or disabled AC_ARG_ENABLE(nfs, AC_HELP_STRING([--enable-nfs], [enable NFS support (default is yes)]), [nfs=$enableval], [nfs=yes]) -if test x$selinux = xyes ; then - AC_SUBST(NFS_CFLAGS, [-DHAVE_NFS]) -fi # IPv6 support can be enabled or disabled AC_ARG_ENABLE(ipv6, @@ -255,9 +232,6 @@ AC_CONFIG_FILES([Makefile pyanaconda/Makefile pyanaconda/isys/Makefile pyanaconda/packaging/Makefile - pyanaconda/storage/Makefile - pyanaconda/storage/devicelibs/Makefile - pyanaconda/storage/formats/Makefile pyanaconda/ui/Makefile pyanaconda/ui/lib/Makefile pyanaconda/ui/gui/categories/Makefile @@ -274,8 +248,6 @@ AC_CONFIG_FILES([Makefile tests/Makefile tests/mock/Makefile tests/kickstart_test/Makefile - tests/storage_test/Makefile - tests/storage_test/devicelibs_test/Makefile tests/pylint/Makefile tests/regex/Makefile tests/pyanaconda_test/Makefile diff --git a/po/POTFILES.in b/po/POTFILES.in index 1d7e2643a..172d48aca 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -12,7 +12,6 @@ pyanaconda/installinterfacebase.py pyanaconda/iutil.py pyanaconda/kickstart.py pyanaconda/network.py -pyanaconda/platform.py pyanaconda/product.py pyanaconda/rescue.py pyanaconda/vnc.py @@ -27,31 +26,6 @@ pyanaconda/packaging/livepayload.py pyanaconda/packaging/tarpayload.py pyanaconda/packaging/yumpayload.py -# Storage module source files -pyanaconda/storage/__init__.py -pyanaconda/storage/dasd.py -pyanaconda/storage/deviceaction.py -pyanaconda/storage/devicelibs/crypto.py -pyanaconda/storage/devicelibs/dm.py -pyanaconda/storage/devicelibs/lvm.py -pyanaconda/storage/devicelibs/mdraid.py -pyanaconda/storage/devicelibs/swap.py -pyanaconda/storage/devices.py -pyanaconda/storage/devicetree.py -pyanaconda/storage/fcoe.py -pyanaconda/storage/formats/__init__.py -pyanaconda/storage/formats/disklabel.py -pyanaconda/storage/formats/dmraid.py -pyanaconda/storage/formats/fs.py -pyanaconda/storage/formats/luks.py -pyanaconda/storage/formats/lvmpv.py -pyanaconda/storage/formats/mdraid.py -pyanaconda/storage/formats/multipath.py -pyanaconda/storage/formats/swap.py -pyanaconda/storage/iscsi.py -pyanaconda/storage/partitioning.py -pyanaconda/storage/zfcp.py - # Interfaces pyanaconda/ui/common.py pyanaconda/ui/__init__.py diff --git a/pyanaconda/Makefile.am b/pyanaconda/Makefile.am index d106f7857..09b2b8664 100644 --- a/pyanaconda/Makefile.am +++ b/pyanaconda/Makefile.am @@ -17,7 +17,7 @@ # # Author: Martin Sivak <msivak@redhat.com> -SUBDIRS = installclasses isys storage ui packaging +SUBDIRS = installclasses isys ui packaging MAINTAINERCLEANFILES = Makefile.in diff --git a/pyanaconda/__init__.py b/pyanaconda/__init__.py index 4767080d8..aa3839442 100644 --- a/pyanaconda/__init__.py +++ b/pyanaconda/__init__.py @@ -155,8 +155,8 @@ class Anaconda(object): @property def storage(self): if not self._storage: - import storage - self._storage = storage.Storage(data=self.ksdata) + import blivet + self._storage = blivet.Blivet(ksdata=self.ksdata) return self._storage diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py index 4d7d38fdf..7e7707179 100644 --- a/pyanaconda/bootloader.py +++ b/pyanaconda/bootloader.py @@ -28,15 +28,15 @@ import struct from parted import PARTITION_BIOS_GRUB from pyanaconda import iutil -from pyanaconda.storage.devicelibs import mdraid +from blivet.devicelibs import mdraid from pyanaconda.isys import sync, getMacAddress from pyanaconda.product import productName from pyanaconda.flags import flags from pyanaconda.constants import * -from pyanaconda.storage.errors import StorageError -from pyanaconda.storage.fcoe import fcoe +from blivet.errors import StorageError +from blivet.fcoe import fcoe import pyanaconda.network -from pyanaconda.storage import platform +from blivet import platform import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -756,7 +756,7 @@ class BootLoader(object): Keyword Arguments: - storage - a pyanaconda.storage.Storage instance + storage - a blivet.Storage instance All other arguments are expected to have a dracutSetupArgs() method. @@ -774,7 +774,7 @@ class BootLoader(object): # # storage - from pyanaconda.storage.devices import NetworkStorageDevice + from blivet.devices import NetworkStorageDevice dracut_devices = [storage.rootDevice] if self.stage2_device != storage.rootDevice: dracut_devices.append(self.stage2_device) diff --git a/pyanaconda/exception.py b/pyanaconda/exception.py index 291adbcfb..f053c0d7b 100644 --- a/pyanaconda/exception.py +++ b/pyanaconda/exception.py @@ -32,7 +32,7 @@ import signal import time from flags import flags import kickstart -import storage.errors +import blivet.errors from pyanaconda.constants import ROOT_PATH from gi.repository import GLib @@ -73,7 +73,7 @@ class AnacondaExceptionHandler(ExceptionHandler): obj) return False - if issubclass(ty, storage.errors.StorageError) and value.hardware_fault: + if issubclass(ty, blivet.errors.StorageError) and value.hardware_fault: hw_error_msg = _("The installation was stopped due to what " "seems to be a problem with your hardware. " "The exact error message is:\n\n%s.\n\n " diff --git a/pyanaconda/image.py b/pyanaconda/image.py index 678608221..c77587b5d 100644 --- a/pyanaconda/image.py +++ b/pyanaconda/image.py @@ -19,12 +19,12 @@ import isys import os, os.path, stat, sys -from pyanaconda.storage import util from constants import * from errors import * -import pyanaconda.storage.arch +import blivet.util +import blivet.arch import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -32,7 +32,7 @@ _ = lambda x: gettext.ldgettext("anaconda", x) import logging log = logging.getLogger("anaconda") -_arch = pyanaconda.storage.arch.getArch() +_arch = blivet.arch.getArch() def findFirstIsoImage(path): """ @@ -62,12 +62,12 @@ def findFirstIsoImage(path): log.debug("mounting %s on /mnt/install/cdimage", what) try: - util.mount(what, "/mnt/install/cdimage", fstype="iso9660", options="ro") + blivet.util.mount(what, "/mnt/install/cdimage", fstype="iso9660", options="ro") except OSError: continue if not os.access("/mnt/install/cdimage/.discinfo", os.R_OK): - util.umount("/mnt/install/cdimage") + blivet.util.umount("/mnt/install/cdimage") continue log.debug("Reading .discinfo") @@ -81,14 +81,14 @@ def findFirstIsoImage(path): if discArch != arch: log.warning("findFirstIsoImage: architectures mismatch: %s, %s" % (discArch, arch)) - util.umount("/mnt/install/cdimage") + blivet.util.umount("/mnt/install/cdimage") continue # If there's no repodata, there's no point in trying to # install from it. if not os.access("/mnt/install/cdimage/repodata", os.R_OK): log.warning("%s doesn't have repodata, skipping" %(what,)) - util.umount("/mnt/install/cdimage") + blivet.util.umount("/mnt/install/cdimage") continue # warn user if images appears to be wrong size @@ -99,7 +99,7 @@ def findFirstIsoImage(path): raise exn log.info("Found disc at %s" % fn) - util.umount("/mnt/install/cdimage") + blivet.util.umount("/mnt/install/cdimage") return fn return None @@ -152,7 +152,7 @@ def mountImageDirectory(method, storage): while True: try: - util.mount(url, ISO_DIR, fstype="nfs", options=method.options) + blivet.util.mount(url, ISO_DIR, fstype="nfs", options=method.options) except OSError as e: log.error("couldn't mount ISO source directory: %s" % e) exn = MediaMountError(str(e)) @@ -175,7 +175,7 @@ def mountImage(isodir, tree): image = os.path.normpath("%s/%s" % (isodir, image)) try: - util.mount(image, tree, fstype = 'iso9660', options="ro") + blivet.util.mount(image, tree, fstype = 'iso9660', options="ro") except OSError: exn = MissingImageError() if errorHandler.cb(exn) == ERROR_RAISE: @@ -214,7 +214,7 @@ def potentialHdisoSources(devicetree): def umountImage(tree): if os.path.ismount(tree): - util.umount(tree) + blivet.util.umount(tree) def unmountCD(dev): if not dev: diff --git a/pyanaconda/install.py b/pyanaconda/install.py index 6e3ee9916..9a55a9cfb 100644 --- a/pyanaconda/install.py +++ b/pyanaconda/install.py @@ -21,7 +21,7 @@ # from pyanaconda.constants import ROOT_PATH -from pyanaconda.storage import turnOnFilesystems +from blivet import turnOnFilesystems from pyanaconda.bootloader import writeBootLoader from pyanaconda.progress import progress_report from pyanaconda.users import createLuserConf, getPassAlgo, Users diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py index 888d0810c..753de20a2 100644 --- a/pyanaconda/installclass.py +++ b/pyanaconda/installclass.py @@ -30,9 +30,9 @@ import types from constants import * from product import * -from storage.partspec import * -from storage.devicelibs import swap -from storage.platform import platform +from blivet.partspec import * +from blivet.devicelibs import swap +from blivet.platform import platform import gettext _ = lambda x: gettext.ldgettext("anaconda", x) diff --git a/pyanaconda/isys/__init__.py b/pyanaconda/isys/__init__.py index 7d73836ff..7f4bcb4c8 100755 --- a/pyanaconda/isys/__init__.py +++ b/pyanaconda/isys/__init__.py @@ -37,7 +37,7 @@ import stat import posix import sys from pyanaconda import iutil -from pyanaconda.storage import arch +import blivet.arch import re import struct import dbus @@ -71,7 +71,7 @@ NM_DEVICE_TYPE_ETHERNET = 1 DBUS_PROPS_IFACE = "org.freedesktop.DBus.Properties" -if arch.getArch() in ("sparc", "ppc64"): +if blivet.arch.getArch() in ("sparc", "ppc64"): MIN_RAM = 768 * 1024 GUI_INSTALL_EXTRA_RAM = 512 * 1024 else: @@ -255,7 +255,7 @@ def isPaeAvailable(): return isPAE isPAE = False - if not arch.isX86(): + if not blivet.arch.isX86(): return isPAE f = open("/proc/cpuinfo", "r") diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 0d5da2893..a93c552c9 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -19,17 +19,18 @@ # from pyanaconda.errors import ScriptError, errorHandler -from storage.deviceaction import * -from storage.devices import LUKSDevice -from storage.devicelibs.lvm import getPossiblePhysicalExtents -from storage.devicelibs.mpath import MultipathConfigWriter, MultipathTopology -from storage.devicelibs import swap -from storage.formats import getFormat -from storage.partitioning import doPartitioning -from storage.partitioning import growLVM -import storage.iscsi -import storage.fcoe -import storage.zfcp +from blivet.deviceaction import * +from blivet.devices import LUKSDevice +from blivet.devicelibs.lvm import getPossiblePhysicalExtents +from blivet.devicelibs.mpath import MultipathConfigWriter, MultipathTopology +from blivet.devicelibs import swap +from blivet.formats import getFormat +from blivet.partitioning import doPartitioning +from blivet.partitioning import growLVM +import blivet.iscsi +import blivet.fcoe +import blivet.zfcp +import blivet.arch import glob import iutil @@ -43,7 +44,6 @@ import shlex import sys import urlgrabber import pykickstart.commands as commands -from pyanaconda.storage import arch from pyanaconda import keyboard from pyanaconda import ntp from pyanaconda import timezone @@ -244,7 +244,7 @@ class Authconfig(commands.authconfig.FC3_Authconfig): class AutoPart(commands.autopart.F18_AutoPart): def execute(self, storage, ksdata, instClass): - from pyanaconda.storage.partitioning import doAutoPartition + from blivet.partitioning import doAutoPartition if not self.autopart: return @@ -451,7 +451,7 @@ class Fcoe(commands.fcoe.F13_Fcoe): if fc.nic not in isys.getDeviceProperties(): raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent nic %s in fcoe command" % fc.nic) - storage.fcoe.fcoe().addSan(nic=fc.nic, dcb=fc.dcb, auto_vlan=True) + blivet.fcoe.fcoe().addSan(nic=fc.nic, dcb=fc.dcb, auto_vlan=True) return fc @@ -546,7 +546,7 @@ class Iscsi(commands.iscsi.F17_Iscsi): if tg.iface not in active_ifaces: raise KickstartValueError, formatErrorMsg(self.lineno, msg="network interface %s required by iscsi %s target is not up" % (tg.iface, tg.target)) - mode = storage.iscsi.iscsi().mode + mode = blivet.iscsi.iscsi().mode if mode == "none": if tg.iface: storage.iscsi.iscsi().create_interfaces(active_ifaces) @@ -555,7 +555,7 @@ class Iscsi(commands.iscsi.F17_Iscsi): raise KickstartValueError, formatErrorMsg(self.lineno, msg="iscsi --iface must be specified (binding used) either for all targets or for none") try: - storage.iscsi.iscsi().addTarget(tg.ipaddr, tg.port, tg.user, + blivet.iscsi.iscsi().addTarget(tg.ipaddr, tg.port, tg.user, tg.password, tg.user_in, tg.password_in, target=tg.target, @@ -573,7 +573,7 @@ class IscsiName(commands.iscsiname.FC6_IscsiName): def parse(self, args): retval = commands.iscsiname.FC6_IscsiName.parse(self, args) - storage.iscsi.iscsi().initiator = self.iscsiname + blivet.iscsi.iscsi().initiator = self.iscsiname return retval class Lang(commands.lang.FC3_Lang): @@ -849,7 +849,7 @@ class PartitionData(commands.partition.F18_PartData): if self.onPart: ksdata.onPart[kwargs["name"]] = self.onPart elif self.mountpoint == "/boot/efi": - if arch.isMactel(): + if blivet.arch.isMactel(): type = "hfs+" else: type = "EFI System Partition" @@ -1288,7 +1288,7 @@ class ZFCP(commands.zfcp.F14_ZFCP): def parse(self, args): fcp = commands.zfcp.F14_ZFCP.parse(self, args) try: - storage.zfcp.ZFCP().addFCP(fcp.devnum, fcp.wwpn, fcp.fcplun) + blivet.zfcp.ZFCP().addFCP(fcp.devnum, fcp.wwpn, fcp.fcplun) except ValueError as e: log.warning(str(e)) @@ -1471,9 +1471,9 @@ def parseKickstart(f): # We need this so all the /dev/disk/* stuff is set up before parsing. udev_trigger(subsystem="block", action="change") # So that drives onlined by these can be used in the ks file - storage.iscsi.iscsi().startup() - storage.fcoe.fcoe().startup() - storage.zfcp.ZFCP().startup() + blivet.iscsi.iscsi().startup() + blivet.fcoe.fcoe().startup() + blivet.zfcp.ZFCP().startup() # Note we do NOT call dasd.startup() here, that does not online drives, but # only checks if they need formatting, which requires zerombr to be known detect_multipaths() diff --git a/pyanaconda/network.py b/pyanaconda/network.py index 1f2bf1961..1c6762555 100644 --- a/pyanaconda/network.py +++ b/pyanaconda/network.py @@ -39,8 +39,8 @@ import re from flags import flags from simpleconfig import IfcfgFile import urlgrabber.grabber -from pyanaconda.storage.devices import FcoeDiskDevice, iScsiDiskDevice -from pyanaconda.storage import arch +from blivet.devices import FcoeDiskDevice, iScsiDiskDevice +import blivet.arch import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -272,7 +272,7 @@ class NetworkDevice(IfcfgFile): def __str__(self): s = "" keys = self.info.keys() - if arch.isS390() and ("HWADDR" in keys): + if blivet.arch.isS390() and ("HWADDR" in keys): keys.remove("HWADDR") # make sure we include autoneg in the ethtool line if 'ETHTOOL_OPTS' in keys: @@ -597,7 +597,7 @@ def dracutBootArguments(ifcfg, storage_ipaddr, hostname=None): nettype = ifcfg.get("NETTYPE") subchannels = ifcfg.get("SUBCHANNELS") - if arch.isS390() and nettype and subchannels: + if blivet.arch.isS390() and nettype and subchannels: znet = "rd.znet=%s,%s" % (nettype, subchannels) options = ifcfg.get("OPTIONS").strip("'\"") if options: diff --git a/pyanaconda/packaging/__init__.py b/pyanaconda/packaging/__init__.py index 75ea61f93..fa4be2272 100644 --- a/pyanaconda/packaging/__init__.py +++ b/pyanaconda/packaging/__init__.py @@ -52,10 +52,10 @@ import logging log = logging.getLogger("packaging") from pyanaconda.errors import * -from pyanaconda.storage.errors import StorageError -from pyanaconda.storage import util -from pyanaconda.storage import arch -from pyanaconda.storage.platform import platform +from blivet.errors import StorageError +import blivet.util +import blivet.arch +from blivet.platform import platform #from pyanaconda.progress import progress from pyanaconda.product import productName, productVersion @@ -129,7 +129,7 @@ def get_mount_device(mountpoint): break if mount_device and re.match(r'/dev/loop\d+$', mount_device): - from pyanaconda.storage.devicelibs import loop + from blivet.devicelibs import loop loop_name = os.path.basename(mount_device) mount_device = loop.get_backing_file(loop_name) log.debug("found backing file %s for loop device %s" % (mount_device, @@ -484,7 +484,7 @@ class Payload(object): return else: try: - util.umount(realMountpoint) + blivet.util.umount(realMountpoint) except Exception as e: log.error(str(e)) log.info("umount failed -- mounting on top of it") @@ -510,7 +510,7 @@ class Payload(object): else: log.debug("%s already has something mounted on it" % mountpoint) try: - util.umount(mountpoint) + blivet.util.umount(mountpoint) except OSError as e: log.error(str(e)) log.info("umount failed -- mounting on top of it") @@ -519,7 +519,7 @@ class Payload(object): url = "%s:%s" % (server, path) try: - util.mount(url, mountpoint, fstype="nfs", options=options) + blivet.util.mount(url, mountpoint, fstype="nfs", options=options) except OSError as e: raise PayloadSetupError(str(e)) @@ -655,7 +655,7 @@ class PackagePayload(Payload): kernels.insert(0, "kernel-PAE") # most ARM systems use platform-specific kernels - if arch.isARM(): + if blivet.arch.isARM(): if platform.armMachine is not None: kernels = ["kernel-%s" % platform.armMachine] @@ -717,7 +717,7 @@ def write_txmbrs(payload, filename): if __name__ == "__main__": import os import sys - import pyanaconda.storage as _storage + import blivet from pykickstart.version import makeVersion from pyanaconda.packaging.yumpayload import YumPayload @@ -732,7 +732,7 @@ if __name__ == "__main__": #ksdata.method.url = "http://dl.fedoraproject.org/pub/fedora/linux/development/17/x86_64/os/" # set up storage - storage = _storage.Storage(data=ksdata) + storage = blivet.Blivet(ksdata=ksdata) storage.reset() # set up the payload diff --git a/pyanaconda/packaging/livepayload.py b/pyanaconda/packaging/livepayload.py index 26c573ff9..73fc3326a 100644 --- a/pyanaconda/packaging/livepayload.py +++ b/pyanaconda/packaging/livepayload.py @@ -46,8 +46,8 @@ log = logging.getLogger("anaconda") from pyanaconda.errors import * from pyanaconda import progress -from pyanaconda.storage.size import Size -from pyanaconda.storage import util +from blivet.size import Size +import blivet.util from pyanaconda.threads import threadMgr, AnacondaThread import gettext @@ -64,7 +64,7 @@ class LiveImagePayload(ImagePayload): exn = PayloadSetupError("%s is not a valid block device" % (self.data.method.partition,)) if errorHandler.cb(exn) == ERROR_RAISE: raise exn - util.mount(osimg.path, INSTALL_TREE, fstype="auto", options="ro") + blivet.util.mount(osimg.path, INSTALL_TREE, fstype="auto", options="ro") def preInstall(self, packages=None, groups=None): """ Perform pre-installation tasks. """ @@ -128,7 +128,7 @@ class LiveImagePayload(ImagePayload): def postInstall(self): """ Perform post-installation tasks. """ progress.send_message(_("Performing post-install setup tasks")) - util.umount(INSTALL_TREE) + blivet.util.umount(INSTALL_TREE) super(LiveImagePayload, self).postInstall() self._recreateInitrds() diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py index 43feebe63..83461b1a5 100644 --- a/pyanaconda/packaging/yumpayload.py +++ b/pyanaconda/packaging/yumpayload.py @@ -73,9 +73,9 @@ from pyanaconda.flags import flags from pyanaconda import iutil from pyanaconda.iutil import ProxyString, ProxyStringError from pyanaconda.network import hasActiveNetDev -from pyanaconda.storage.size import Size -from pyanaconda.storage import util -from pyanaconda.storage import arch +from blivet.size import Size +import blivet.util +import blivet.arch from pyanaconda.image import opticalInstallMedia from pyanaconda.image import mountImage @@ -137,7 +137,7 @@ class YumPayload(PackagePayload): get_mount_device(INSTALL_TREE) == self.install_device.path: self.install_device.teardown(recursive=True) else: - util.umount(INSTALL_TREE) + blivet.util.umount(INSTALL_TREE) if os.path.ismount(ISO_DIR) and not flags.testing: if self.install_device and \ @@ -150,7 +150,7 @@ class YumPayload(PackagePayload): # Commenting out the below is a hack for F18. FIXME #else: # # NFS - # util.umount(ISO_DIR) + # blivet.util.umount(ISO_DIR) self.install_device = None @@ -835,7 +835,7 @@ reposdir=%s if mountpoint and os.path.ismount(mountpoint): try: - util.umount(mountpoint, removeDir=False) + blivet.util.umount(mountpoint) except SystemError as e: log.error("failed to unmount nfs repo %s: %s" % (mountpoint, e)) @@ -1502,7 +1502,7 @@ class RPMCallback(object): self.total_actions = 0 self.completed_actions = None # will be set to 0 when starting tx - self.base_arch = arch.getArch() + self.base_arch = blivet.arch.getArch() def _get_txmbr(self, key): """ Return a (name, TransactionMember) tuple from cb key. """ diff --git a/pyanaconda/rescue.py b/pyanaconda/rescue.py index 9b3be6d52..c6750bf39 100644 --- a/pyanaconda/rescue.py +++ b/pyanaconda/rescue.py @@ -28,8 +28,8 @@ from flags import flags import sys import os import isys -from storage import mountExistingSystem -from storage.errors import StorageError +from blivet import mountExistingSystem +from blivet.errors import StorageError from installinterfacebase import InstallInterfaceBase import iutil import shutil @@ -192,7 +192,7 @@ def runShell(screen = None, msg=""): screen.finish() def doRescue(rescue_mount, ksdata): - import storage + import blivet for file in [ "services", "protocols", "group", "joe", "man.config", "nsswitch.conf", "selinux", "mke2fs.conf" ]: @@ -242,9 +242,9 @@ def doRescue(rescue_mount, ksdata): break - sto = storage.Storage(ksdata) - storage.storageInitialize(sto, ksdata, []) - roots = storage.findExistingInstallations(sto.devicetree) + sto = blivet.Blivet(ksdata=ksdata) + blivet.storageInitialize(sto, ksdata, []) + roots = blivet.findExistingInstallations(sto.devicetree) if not roots: root = None diff --git a/pyanaconda/storage/Makefile.am b/pyanaconda/storage/Makefile.am deleted file mode 100644 index 7347694e7..000000000 --- a/pyanaconda/storage/Makefile.am +++ /dev/null @@ -1,26 +0,0 @@ -# storage/Makefile.am for anaconda -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# Author: David Cantrell <dcantrell@redhat.com> - -pkgpyexecdir = $(pyexecdir)/py$(PACKAGE_NAME) -storagedir = $(pkgpyexecdir)/storage -storage_PYTHON = *.py - -SUBDIRS = devicelibs formats - -MAINTAINERCLEANFILES = Makefile.in diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py deleted file mode 100644 index bc973e3cd..000000000 --- a/pyanaconda/storage/__init__.py +++ /dev/null @@ -1,3533 +0,0 @@ -# __init__.py -# Entry point for anaconda's storage configuration module. -# -# Copyright (C) 2009 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> -# - -## -## Default stub values for installer-specific stuff that gets set up in -## enable_installer_mode. -## -isys = None -ROOT_PATH = '/' -shortProductName = '' -bootLoaderError = Exception - -import os -import time -import stat -import errno -import sys -import statvfs -import copy - -import nss.nss -import parted - -from pykickstart.constants import * - -from storage_log import log_method_call -from errors import * -from devices import * -from devicetree import DeviceTree -from deviceaction import * -from formats import getFormat -from formats import get_device_format_class -from formats import get_default_filesystem_type -from devicelibs.dm import name_from_dm_node -from devicelibs.crypto import generateBackupPassphrase -from devicelibs.mpath import MultipathConfigWriter -from devicelibs.edd import get_edd_dict -from devicelibs.mdraid import get_member_space -from devicelibs.mdraid import raidLevelString -from devicelibs.lvm import get_pv_space -from .partitioning import SameSizeSet -from .partitioning import TotalSizeSet -from .partitioning import doPartitioning -from udev import * -import iscsi -import fcoe -import zfcp -import dasd -import util -import arch -from flags import flags -from platform import platform as _platform - -import shelve -import contextlib - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -def enable_installer_mode(): - global isys - global ROOT_PATH - global shortProductName - global get_bootloader - global BootLoaderError - - from pyanaconda import isys - from pyanaconda.constants import ROOT_PATH - from pyanaconda.constants import shortProductName - from pyanaconda.bootloader import get_bootloader - from pyanaconda.bootloader import BootLoaderError - - flags.installer_mode = True - -DEVICE_TYPE_LVM = 0 -DEVICE_TYPE_MD = 1 -DEVICE_TYPE_PARTITION = 2 -DEVICE_TYPE_BTRFS = 3 -DEVICE_TYPE_DISK = 4 - -def getDeviceType(device): - device_types = {"partition": DEVICE_TYPE_PARTITION, - "lvmlv": DEVICE_TYPE_LVM, - "btrfs subvolume": DEVICE_TYPE_BTRFS, - "btrfs volume": DEVICE_TYPE_BTRFS, - "mdarray": DEVICE_TYPE_MD} - - use_dev = device - if isinstance(device, LUKSDevice): - use_dev = device.slave - - if use_dev.isDisk: - device_type = DEVICE_TYPE_DISK - else: - device_type = device_types.get(use_dev.type) - - return device_type - -def getRAIDLevel(device): - # TODO: move this into StorageDevice - use_dev = device - if isinstance(device, LUKSDevice): - use_dev = device.slave - - # TODO: lvm and perhaps pulling raid level from md pvs - raid_level = None - if hasattr(use_dev, "level"): - raid_level = raidLevelString(use_dev.level) - elif hasattr(use_dev, "dataLevel"): - raid_level = use_dev.dataLevel or "single" - elif hasattr(use_dev, "volume"): - raid_level = use_dev.volume.dataLevel or "single" - - return raid_level - -def storageInitialize(storage, ksdata, protected): - from pyanaconda.flags import flags as anaconda_flags - flags.update_from_anaconda_flags(anaconda_flags) - - storage.shutdown() - - # touch /dev/.in_sysinit so that /lib/udev/rules.d/65-md-incremental.rules - # does not mess with any mdraid sets - open("/dev/.in_sysinit", "w") - - # XXX I don't understand why I have to do this, but this is needed to - # populate the udev db - util.run_program(["udevadm", "control", "--property=ANACONDA=1"]) - udev_trigger(subsystem="block", action="change") - - # Before we set up the storage system, we need to know which disks to - # ignore, etc. Luckily that's all in the kickstart data. - storage.config.update(ksdata) - - lvm.lvm_vg_blacklist = [] - - # Set up the protected partitions list now. - if protected: - storage.config.protectedDevSpecs.extend(protected) - storage.reset() - - if not flags.live_install and not storage.protectedDevices: - raise UnknownSourceDeviceError(protected) - else: - storage.reset() - - # kickstart uses all the disks - if flags.automated_install: - if not ksdata.ignoredisk.onlyuse: - ksdata.ignoredisk.onlyuse = [d.name for d in storage.disks \ - if d.name not in ksdata.ignoredisk.ignoredisk] - log.debug("onlyuse is now: %s" % (",".join(ksdata.ignoredisk.onlyuse))) - -def turnOnFilesystems(storage): - if not flags.installer_mode: - return - - if (flags.live_install and not flags.image_install and not storage.fsset.active): - # turn off any swaps that we didn't turn on - # needed for live installs - util.run_program(["swapoff", "-a"]) - storage.devicetree.teardownAll() - - try: - storage.doIt() - except FSResizeError as e: - if os.path.exists("/tmp/resize.out"): - details = open("/tmp/resize.out", "r").read() - else: - details = e.args[1] - - if errorHandler.cb(e, e.args[0], details=details) == ERROR_RAISE: - raise - except Exception as e: - raise - - storage.turnOnSwap() - # FIXME: For livecd, skipRoot needs to be True. - storage.mountFilesystems(raiseErrors=False, - readOnly=False, - skipRoot=False) - writeEscrowPackets(storage) - -def writeEscrowPackets(storage): - escrowDevices = filter(lambda d: d.format.type == "luks" and \ - d.format.escrow_cert, - storage.devices) - - if not escrowDevices: - return - - log.debug("escrow: writeEscrowPackets start") - - nss.nss.nss_init_nodb() # Does nothing if NSS is already initialized - - backupPassphrase = generateBackupPassphrase() - try: - for device in escrowDevices: - log.debug("escrow: device %s: %s" % - (repr(device.path), repr(device.format.type))) - device.format.escrow(ROOT_PATH + "/root", - backupPassphrase) - - except (IOError, RuntimeError) as e: - # TODO: real error handling - log.error("failed to store encryption key: %s" % e) - - log.debug("escrow: writeEscrowPackets done") - - -def undoEncryption(storage): - for device in storage.devicetree.getDevicesByType("luks/dm-crypt"): - if device.exists: - continue - - slave = device.slave - format = device.format - - # set any devices that depended on the luks device to now depend on - # the former slave device - for child in storage.devicetree.getChildren(device): - child.parents.remove(device) - device.removeChild() - child.parents.append(slave) - - storage.devicetree.registerAction(ActionDestroyFormat(device)) - storage.devicetree.registerAction(ActionDestroyDevice(device)) - storage.devicetree.registerAction(ActionDestroyFormat(slave)) - storage.devicetree.registerAction(ActionCreateFormat(slave, format)) - -class StorageDiscoveryConfig(object): - def __init__(self): - # storage configuration variables - self.ignoreDiskInteractive = False - self.ignoredDisks = [] - self.exclusiveDisks = [] - self.clearPartType = None - self.clearPartDisks = [] - self.clearPartDevices = [] - self.initializeDisks = False - self.protectedDevSpecs = [] - self.diskImages = {} - self.mpathFriendlyNames = True - - # Whether clearPartitions removes scheduled/non-existent devices and - # disklabels depends on this flag. - self.clearNonExistent = False - - def update(self, ksdata): - self.ignoredDisks = ksdata.ignoredisk.ignoredisk[:] - self.exclusiveDisks = ksdata.ignoredisk.onlyuse[:] - self.clearPartType = ksdata.clearpart.type - self.clearPartDisks = ksdata.clearpart.drives[:] - self.clearPartDevices = ksdata.clearpart.devices[:] - self.initializeDisks = ksdata.clearpart.initAll - self.zeroMbr = ksdata.zerombr.zerombr - -class Storage(object): - def __init__(self, data=None): - """ Create a Storage instance. - - Keyword Arguments: - - data - a pykickstart Handler instance - """ - self.data = data - self._bootloader = None - - self.config = StorageDiscoveryConfig() - - # storage configuration variables - self.doAutoPart = False - self.clearPartChoice = None - self.encryptedAutoPart = False - self.autoPartType = AUTOPART_TYPE_LVM - self.encryptionPassphrase = None - self.encryptionCipher = None - self.escrowCertificates = {} - self.autoPartEscrowCert = None - self.autoPartAddBackupPassphrase = False - self.encryptionRetrofit = False - self.autoPartitionRequests = [] - self.eddDict = {} - - self.__luksDevs = {} - self.size_sets = [] - - self.iscsi = iscsi.iscsi() - self.fcoe = fcoe.fcoe() - self.zfcp = zfcp.ZFCP() - self.dasd = dasd.DASD() - - self._nextID = 0 - self.defaultFSType = get_default_filesystem_type() - self._dumpFile = "/tmp/storage.state" - - # these will both be empty until our reset method gets called - self.devicetree = DeviceTree(conf=self.config, - passphrase=self.encryptionPassphrase, - luksDict=self.__luksDevs, - iscsi=self.iscsi, - dasd=self.dasd) - self.fsset = FSSet(self.devicetree) - self.roots = [] - self.services = set() - - def doIt(self): - self.devicetree.processActions() - self.doEncryptionPassphraseRetrofits() - - # now set the boot partition's flag - if self.bootloader: - if self.bootloader.stage2_bootable: - boot = self.bootDevice - else: - boot = self.bootLoaderDevice - - if boot.type == "mdarray": - bootDevs = boot.parents - else: - bootDevs = [boot] - - for dev in bootDevs: - if hasattr(dev, "bootable"): - # Dos labels can only have one partition marked as active - # and unmarking ie the windows partition is not a good idea - skip = False - if dev.disk.format.partedDisk.type == "msdos": - for p in dev.disk.format.partedDisk.partitions: - if p.type == parted.PARTITION_NORMAL and \ - p.getFlag(parted.PARTITION_BOOT): - skip = True - break - - # GPT labeled disks should only have bootable set on the - # EFI system partition (parted sets the EFI System GUID on - # GPT partitions with the boot flag) - if dev.disk.format.labelType == "gpt" and \ - dev.format.type != "efi": - skip = True - - if skip: - log.info("not setting boot flag on %s" % dev.name) - continue - # hfs+ partitions on gpt can't be marked bootable via - # parted - if dev.disk.format.partedDisk.type == "gpt" and \ - dev.format.type == "hfs+": - log.info("not setting boot flag on hfs+ partition" - " %s" % dev.name) - continue - log.info("setting boot flag on %s" % dev.name) - dev.bootable = True - - # Set the boot partition's name on disk labels that support it - if dev.partedPartition.disk.supportsFeature(parted.DISK_TYPE_PARTITION_NAME): - ped_partition = dev.partedPartition.getPedPartition() - ped_partition.set_name(dev.format.name) - - dev.disk.setup() - dev.disk.format.commitToDisk() - - self.dumpState("final") - - @property - def nextID(self): - id = self._nextID - self._nextID += 1 - return id - - def shutdown(self): - try: - self.devicetree.teardownAll() - except Exception as e: - log.error("failure tearing down device tree: %s" % e) - - def reset(self, cleanupOnly=False): - """ Reset storage configuration to reflect actual system state. - - This should rescan from scratch but not clobber user-obtained - information like passphrases, iscsi config, &c - - """ - # save passphrases for luks devices so we don't have to reprompt - self.encryptionPassphrase = None - for device in self.devices: - if device.format.type == "luks" and device.format.exists: - self.__luksDevs[device.format.uuid] = device.format._LUKS__passphrase - - if self.data: - self.config.update(self.data) - - if not flags.image_install: - self.iscsi.startup() - self.fcoe.startup() - self.zfcp.startup() - self.dasd.startup(None, - self.config.exclusiveDisks, - self.config.initializeDisks) - clearPartType = self.config.clearPartType # save this before overriding it - if self.dasd: - # Reset the internal dasd list (823534) - self.dasd.clear_device_list() - - self.devicetree.reset(conf=self.config, - passphrase=self.encryptionPassphrase, - luksDict=self.__luksDevs, - iscsi=self.iscsi, - dasd=self.dasd) - self.devicetree.populate(cleanupOnly=cleanupOnly) - self.config.clearPartType = clearPartType # set it back - self.fsset = FSSet(self.devicetree) - self.eddDict = get_edd_dict(self.partitioned) - if self.bootloader: - # clear out bootloader attributes that refer to devices that are - # no longer in the tree - self.bootloader.stage1_disk = None - self.bootloader.stage1_device = None - self.bootloader.stage2_device = None - - self.roots = findExistingInstallations(self.devicetree) - - self.dumpState("initial") - - self.updateBootLoaderDiskList() - - @property - def unusedDevices(self): - used_devices = [] - for root in self.roots: - for device in root.mounts.values() + root.swaps: - if device not in self.devices: - continue - - used_devices.extend(device.ancestors) - - for new in [d for d in self.devicetree.leaves if not d.format.exists]: - if new in self.swaps or getattr(new.format, "mountpoint", None): - used_devices.extend(new.ancestors) - - for device in self.partitions: - if getattr(device, "isLogical", False): - extended = device.disk.format.extendedPartition.path - used_devices.append(self.devicetree.getDeviceByPath(extended)) - - used = set(used_devices) - _all = set(self.devices) - return list(_all.difference(used)) - - @property - def devices(self): - """ A list of all the devices in the device tree. """ - devices = self.devicetree.devices - devices.sort(key=lambda d: d.name) - return devices - - @property - def disks(self): - """ A list of the disks in the device tree. - - Ignored disks are not included, as are disks with no media present. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - disks = [] - for device in self.devicetree.devices: - if device.isDisk: - if not device.mediaPresent: - log.info("Skipping disk: %s: No media present" % device.name) - continue - disks.append(device) - disks.sort(key=lambda d: d.name, cmp=self.compareDisks) - return disks - - @property - def partitioned(self): - """ A list of the partitioned devices in the device tree. - - Ignored devices are not included, nor disks with no media present. - - Devices of types for which partitioning is not supported are also - not included. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - partitioned = [] - for device in self.devicetree.devices: - if not device.partitioned: - continue - - if not device.mediaPresent: - log.info("Skipping device: %s: No media present" % device.name) - continue - - partitioned.append(device) - - partitioned.sort(key=lambda d: d.name) - return partitioned - - @property - def partitions(self): - """ A list of the partitions in the device tree. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - partitions = self.devicetree.getDevicesByInstance(PartitionDevice) - partitions.sort(key=lambda d: d.name) - return partitions - - @property - def vgs(self): - """ A list of the LVM Volume Groups in the device tree. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - vgs = self.devicetree.getDevicesByType("lvmvg") - vgs.sort(key=lambda d: d.name) - return vgs - - @property - def lvs(self): - """ A list of the LVM Logical Volumes in the device tree. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - lvs = self.devicetree.getDevicesByType("lvmlv") - lvs.sort(key=lambda d: d.name) - return lvs - - @property - def pvs(self): - """ A list of the LVM Physical Volumes in the device tree. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - devices = self.devicetree.devices - pvs = [d for d in devices if d.format.type == "lvmpv"] - pvs.sort(key=lambda d: d.name) - return pvs - - def unusedPVs(self, vg=None): - unused = [] - for pv in self.pvs: - used = False - for _vg in self.vgs: - if _vg.dependsOn(pv) and _vg != vg: - used = True - break - elif _vg == vg: - break - if not used: - unused.append(pv) - return unused - - @property - def mdarrays(self): - """ A list of the MD arrays in the device tree. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - arrays = self.devicetree.getDevicesByType("mdarray") - arrays.sort(key=lambda d: d.name) - return arrays - - @property - def mdcontainers(self): - """ A list of the MD containers in the device tree. """ - arrays = self.devicetree.getDevicesByType("mdcontainer") - arrays.sort(key=lambda d: d.name) - return arrays - - @property - def mdmembers(self): - """ A list of the MD member devices in the device tree. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - devices = self.devicetree.devices - members = [d for d in devices if d.format.type == "mdmember"] - members.sort(key=lambda d: d.name) - return members - - def unusedMDMembers(self, array=None): - unused = [] - for member in self.mdmembers: - used = False - for _array in self.mdarrays + self.mdcontainers: - if _array.dependsOn(member) and _array != array: - used = True - break - elif _array == array: - break - if not used: - unused.append(member) - return unused - - @property - def btrfsVolumes(self): - return sorted(self.devicetree.getDevicesByType("btrfs volume"), - key=lambda d: d.name) - - @property - def swaps(self): - """ A list of the swap devices in the device tree. - - This is based on the current state of the device tree and - does not necessarily reflect the actual on-disk state of the - system's disks. - """ - devices = self.devicetree.devices - swaps = [d for d in devices if d.format.type == "swap"] - swaps.sort(key=lambda d: d.name) - return swaps - - @property - def protectedDevices(self): - devices = self.devicetree.devices - protected = [d for d in devices if d.protected] - protected.sort(key=lambda d: d.name) - return protected - - @property - def liveImage(self): - """ The OS image used by live installs. """ - return None - - def shouldClear(self, device, **kwargs): - clearPartType = kwargs.get("clearPartType", self.config.clearPartType) - clearPartDisks = kwargs.get("clearPartDisks", - self.config.clearPartDisks) - clearPartDevices = kwargs.get("clearPartDevices", - self.config.clearPartDevices) - - def empty_disk(device): - empty = True - if device.partitioned: - partitions = self.devicetree.getChildren(device) - empty = all([p.isMagic for p in partitions]) - else: - empty = (device.format.type is None) - - return empty - - for disk in device.disks: - # this will not include disks with hidden formats like multipath - # and firmware raid member disks - if clearPartDisks and disk.name not in clearPartDisks: - return False - - if not self.config.clearNonExistent: - if (device.isDisk and not device.format.exists) or \ - (not device.isDisk and not device.exists): - return False - - # the only devices we want to clear when clearPartType is - # CLEARPART_TYPE_NONE are uninitialized disks, or disks with no - # partitions, in clearPartDisks, and then only when we have been asked - # to initialize disks as needed - if clearPartType in [CLEARPART_TYPE_NONE, None]: - if not self.config.initializeDisks or not device.isDisk: - return False - - if not empty_disk(device): - return False - - if isinstance(device, PartitionDevice): - # Never clear the special first partition on a Mac disk label, as - # that holds the partition table itself. - # Something similar for the third partition on a Sun disklabel. - if device.isMagic: - return False - - # We don't want to fool with extended partitions, freespace, &c - if not device.isPrimary and not device.isLogical: - return False - - if clearPartType == CLEARPART_TYPE_LINUX and \ - not device.format.linuxNative and \ - not device.getFlag(parted.PARTITION_LVM) and \ - not device.getFlag(parted.PARTITION_RAID) and \ - not device.getFlag(parted.PARTITION_SWAP): - return False - elif device.isDisk: - if device.partitioned and clearPartType != CLEARPART_TYPE_ALL: - # if clearPartType is not CLEARPART_TYPE_ALL but we'll still be - # removing every partition from the disk, return True since we - # will want to be able to create a new disklabel on this disk - if not empty_disk(device): - return False - - # Never clear disks with hidden formats - if device.format.hidden: - return False - - # When clearPartType is CLEARPART_TYPE_LINUX and a disk has non- - # linux whole-disk formatting, do not clear it. The exception is - # the case of an uninitialized disk when we've been asked to - # initialize disks as needed - if clearPartType == CLEARPART_TYPE_LINUX and \ - not (self.config.initializeDisks and - device.format.type is None) and \ - not device.partitioned and not device.format.linuxNative: - return False - - # Don't clear devices holding install media. - if device.protected: - return False - - if clearPartType == CLEARPART_TYPE_LIST and \ - device.name not in clearPartDevices: - return False - - return True - - def recursiveRemove(self, device): - log.debug("removing %s" % device.name) - - # XXX is there any argument for not removing incomplete devices? - # -- maybe some RAID devices - devices = self.deviceDeps(device) - while devices: - log.debug("devices to remove: %s" % ([d.name for d in devices],)) - leaves = [d for d in devices if d.isleaf] - log.debug("leaves to remove: %s" % ([d.name for d in leaves],)) - for leaf in leaves: - self.destroyDevice(leaf) - devices.remove(leaf) - - if device.isDisk: - self.devicetree.registerAction(ActionDestroyFormat(device)) - else: - self.destroyDevice(device) - - def clearPartitions(self): - """ Clear partitions and dependent devices from disks. - - Arguments: - - None - - NOTES: - - - Needs some error handling - - """ - # 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 - # avoid it where possible. - partitions = sorted(self.partitions, - key=lambda p: p.partedPartition.number, - reverse=True) - for part in partitions: - log.debug("clearpart: looking at %s" % part.name) - if not self.shouldClear(part): - continue - - self.recursiveRemove(part) - log.debug("partitions: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions]) - - # now remove any empty extended partitions - self.removeEmptyExtendedPartitions() - - # ensure all disks have appropriate disklabels - for disk in self.disks: - if not self.shouldClear(disk): - continue - - log.debug("clearpart: initializing %s" % disk.name) - self.initializeDisk(disk) - - self.updateBootLoaderDiskList() - - def initializeDisk(self, disk): - """ (Re)initialize a disk by creating a disklabel on it. - - The disk should not contain any partitions except perhaps for a - magic partitions on mac and sun disklabels. - """ - # first, remove magic mac/sun partitions from the parted Disk - if disk.partitioned: - magic_partitions = {"mac": 1, "sun": 3} - if disk.format.labelType in magic_partitions: - number = magic_partitions[disk.format.labelType] - # remove the magic partition - for part in disk.format.partitions: - if part.disk == disk and part.partedPartition.number == number: - log.debug("removing %s" % part.name) - # We can't schedule the magic partition for removal - # because parted will not allow us to remove it from the - # disk. Still, we need it out of the devicetree. - self.devicetree._removeDevice(part, moddisk=False) - - if disk.partitioned and disk.format.partitions: - raise ValueError("cannot initialize a disk that has partitions") - - # remove existing formatting from the disk - destroy_action = ActionDestroyFormat(disk) - self.devicetree.registerAction(destroy_action) - - labelType = _platform.bestDiskLabelType(disk) - - # create a new disklabel on the disk - newLabel = getFormat("disklabel", device=disk.path, - labelType=labelType) - create_action = ActionCreateFormat(disk, format=newLabel) - self.devicetree.registerAction(create_action) - - def removeEmptyExtendedPartitions(self): - for disk in self.partitioned: - log.debug("checking whether disk %s has an empty extended" % disk.name) - extended = disk.format.extendedPartition - logical_parts = disk.format.logicalPartitions - log.debug("extended is %s ; logicals is %s" % (extended, [p.getDeviceNodeName() for p in logical_parts])) - if extended and not logical_parts: - log.debug("removing empty extended partition from %s" % disk.name) - extended_name = devicePathToName(extended.getDeviceNodeName()) - extended = self.devicetree.getDeviceByName(extended_name) - self.destroyDevice(extended) - - def getFreeSpace(self, disks=None, clearPartType=None): - """ Return a dict with free space info for each disk. - - The dict values are 2-tuples: (disk_free, fs_free). fs_free is - space available by shrinking filesystems. disk_free is space not - allocated to any partition. - - disks and clearPartType allow specifying a set of disks other than - self.disks and a clearPartType value other than - self.config.clearPartType. - """ - from size import Size - if disks is None: - disks = self.disks - - if clearPartType is None: - clearPartType = self.config.clearPartType - - free = {} - for disk in disks: - should_clear = self.shouldClear(disk, clearPartType=clearPartType, - clearPartDisks=[disk.name]) - if should_clear: - free[disk.name] = (Size(spec="%f mb" % disk.size), 0) - continue - - disk_free = 0 - fs_free = 0 - if disk.partitioned: - disk_free = disk.format.free - for partition in [p for p in self.partitions if p.disk == disk]: - # only check actual filesystems since lvm &c require a bunch of - # operations to translate free filesystem space into free disk - # space - should_clear = self.shouldClear(partition, - clearPartType=clearPartType, - clearPartDisks=[disk.name]) - if should_clear: - disk_free += partition.size - elif hasattr(partition.format, "free"): - fs_free += partition.format.free - elif hasattr(disk.format, "free"): - fs_free = disk.format.free - elif disk.format.type is None: - disk_free = disk.size - - free[disk.name] = (Size(spec="%f mb" % disk_free), - Size(spec="%f mb" % fs_free)) - - return free - - @property - def names(self): - return self.devicetree.names - - def exceptionDisks(self): - """ Return a list of removable devices to save exceptions to. - - FIXME: This raises the problem that the device tree can be - in a state that does not reflect that actual current - state of the system at any given point. - - We need a way to provide direct scanning of disks, - partitions, and filesystems without relying on the - larger objects' correctness. - - Also, we need to find devices that have just been made - available for the purpose of storing the exception - report. - """ - # When a usb is connected from before the start of the installation, - # it is not correctly detected. - udev_trigger(subsystem="block", action="change") - self.reset() - - dests = [] - - for disk in self.disks: - if not disk.removable and \ - disk.format is not None and \ - disk.format.mountable: - dests.append([disk.path, disk.name]) - - for part in self.partitions: - if not part.disk.removable: - continue - - elif part.partedPartition.active and \ - not part.partedPartition.getFlag(parted.PARTITION_RAID) and \ - not part.partedPartition.getFlag(parted.PARTITION_LVM) and \ - part.format is not None and part.format.mountable: - dests.append([part.path, part.name]) - - return dests - - def deviceImmutable(self, device, ignoreProtected=False): - """ Return any reason the device cannot be modified/removed. - - Return False if the device can be removed. - - Devices that cannot be removed include: - - - protected partitions - - devices that are part of an md array or lvm vg - - extended partition containing logical partitions that - meet any of the above criteria - - """ - if not isinstance(device, Device): - raise ValueError("arg1 (%s) must be a Device instance" % device) - - if not ignoreProtected and device.protected and \ - not getattr(device.format, "inconsistentVG", False): - return _("This partition is holding the data for the hard " - "drive install.") - elif isinstance(device, PartitionDevice) and device.isProtected: - # LDL formatted DASDs always have one partition, you'd have to - # reformat the DASD in CDL mode to get rid of it - return _("You cannot delete a partition of a LDL formatted " - "DASD.") - elif device.format.type == "mdmember": - for array in self.mdarrays + self.mdcontainers: - if array.dependsOn(device): - if array.minor is not None: - return _("This device is part of the RAID " - "device %s.") % (array.path,) - else: - return _("This device is part of a RAID device.") - elif device.format.type == "lvmpv": - if device.format.inconsistentVG: - return _("This device is part of an inconsistent LVM " - "Volume Group.") - for vg in self.vgs: - if vg.dependsOn(device): - if vg.name is not None: - return _("This device is part of the LVM " - "volume group '%s'.") % (vg.name,) - else: - return _("This device is part of a LVM volume " - "group.") - elif device.format.type == "luks": - try: - luksdev = self.devicetree.getChildren(device)[0] - except IndexError: - pass - else: - return self.deviceImmutable(luksdev) - elif isinstance(device, PartitionDevice) and device.isExtended: - reasons = {} - for dep in self.deviceDeps(device): - reason = self.deviceImmutable(dep) - if reason: - reasons[dep.path] = reason - if reasons: - msg = _("This device is an extended partition which " - "contains logical partitions that cannot be " - "deleted:\n\n") - for dev in reasons: - msg += "%s: %s" % (dev, reasons[dev]) - return msg - - return False - - def deviceDeps(self, device): - return self.devicetree.getDependentDevices(device) - - def newPartition(self, *args, **kwargs): - """ Return a new PartitionDevice instance for configuring. """ - if kwargs.has_key("fmt_type"): - kwargs["format"] = getFormat(kwargs.pop("fmt_type"), - mountpoint=kwargs.pop("mountpoint", - None), - **kwargs.pop("fmt_args", {})) - - if kwargs.has_key("name"): - name = kwargs.pop("name") - else: - name = "req%d" % self.nextID - - if "weight" not in kwargs: - fmt = kwargs.get("format") - if fmt: - mountpoint = getattr(fmt, "mountpoint", None) - - kwargs["weight"] = _platform.weight(mountpoint=mountpoint, - fstype=fmt.type) - - - return PartitionDevice(name, *args, **kwargs) - - def newMDArray(self, *args, **kwargs): - """ Return a new MDRaidArrayDevice instance for configuring. """ - if kwargs.has_key("fmt_type"): - kwargs["format"] = getFormat(kwargs.pop("fmt_type"), - mountpoint=kwargs.pop("mountpoint", - None), - **kwargs.pop("fmt_args", {})) - - if kwargs.has_key("name"): - name = kwargs.pop("name") - safe_name = self.safeDeviceName(name) - if safe_name != name: - log.warning("using '%s' instead of specified name '%s'" - % (safe_name, name)) - name = safe_name - else: - swap = getattr(kwargs.get("format"), "type", None) == "swap" - mountpoint = getattr(kwargs.get("format"), "mountpoint", None) - name = self.suggestDeviceName(prefix=shortProductName, - swap=swap, - mountpoint=mountpoint) - - return MDRaidArrayDevice(name, *args, **kwargs) - - def newVG(self, *args, **kwargs): - """ Return a new LVMVolumeGroupDevice instance. """ - pvs = kwargs.pop("parents", []) - for pv in pvs: - if pv not in self.devices: - raise ValueError("pv is not in the device tree") - - if kwargs.has_key("name"): - name = kwargs.pop("name") - safe_name = self.safeDeviceName(name) - if safe_name != name: - log.warning("using '%s' instead of specified name '%s'" - % (safe_name, name)) - name = safe_name - else: - hostname = "" - if self.data and self.data.network.hostname is not None: - hostname = self.data.network.hostname - - name = self.suggestContainerName(hostname=hostname) - - if name in self.names: - raise ValueError("name already in use") - - return LVMVolumeGroupDevice(name, pvs, *args, **kwargs) - - def newLV(self, *args, **kwargs): - """ Return a new LVMLogicalVolumeDevice instance. """ - vg = kwargs.get("parents", [None])[0] - mountpoint = kwargs.pop("mountpoint", None) - if kwargs.has_key("fmt_type"): - kwargs["format"] = getFormat(kwargs.pop("fmt_type"), - mountpoint=mountpoint, - **kwargs.pop("fmt_args", {})) - - if kwargs.has_key("name"): - name = kwargs.pop("name") - # make sure the specified name is sensible - safe_vg_name = self.safeDeviceName(vg.name) - full_name = "%s-%s" % (safe_vg_name, name) - safe_name = self.safeDeviceName(full_name) - if safe_name != full_name: - new_name = safe_name[len(safe_vg_name)+1:] - log.warning("using '%s' instead of specified name '%s'" - % (new_name, name)) - name = new_name - else: - if kwargs.get("format") and kwargs["format"].type == "swap": - swap = True - else: - swap = False - name = self.suggestDeviceName(parent=vg, - swap=swap, - mountpoint=mountpoint) - - if "%s-%s" % (vg.name, name) in self.names: - raise ValueError("name already in use") - - return LVMLogicalVolumeDevice(name, *args, **kwargs) - - def newBTRFS(self, *args, **kwargs): - """ Return a new BTRFSVolumeDevice or BRFSSubVolumeDevice. """ - log.debug("newBTRFS: args = %s ; kwargs = %s" % (args, kwargs)) - name = kwargs.pop("name", None) - if args: - name = args[0] - - mountpoint = kwargs.pop("mountpoint", None) - - fmt_args = kwargs.pop("fmt_args", {}) - fmt_args.update({"mountpoint": mountpoint}) - - if kwargs.pop("subvol", False): - dev_class = BTRFSSubVolumeDevice - # make sure there's a valid parent device - parents = kwargs.get("parents", []) - if not parents or len(parents) != 1 or \ - not isinstance(parents[0], BTRFSVolumeDevice): - raise ValueError("new btrfs subvols require a parent volume") - - # set up the subvol name, using mountpoint if necessary - if not name: - # for btrfs this only needs to ensure the subvol name is not - # already in use within the parent volume - name = self.suggestDeviceName(mountpoint=mountpoint) - fmt_args["mountopts"] = "subvol=%s" % name - kwargs.pop("metaDataLevel", None) - kwargs.pop("dataLevel", None) - else: - dev_class = BTRFSVolumeDevice - # set up the volume label, using hostname if necessary - if not name: - hostname = "" - if self.data and self.data.network.hostname is not None: - hostname = self.data.network.hostname - - name = self.suggestContainerName(hostname=hostname) - if "label" not in fmt_args: - fmt_args["label"] = name - - # discard fmt_type since it's btrfs always - kwargs.pop("fmt_type", None) - - # this is to avoid auto-scheduled format create actions - device = dev_class(name, **kwargs) - device.format = getFormat("btrfs", **fmt_args) - return device - - def newBTRFSSubVolume(self, *args, **kwargs): - kwargs["subvol"] = True - return self.newBTRFS(*args, **kwargs) - - def createDevice(self, device): - """ Schedule creation of a device. - - TODO: We could do some things here like assign the next - available raid minor if one isn't already set. - """ - self.devicetree.registerAction(ActionCreateDevice(device)) - if device.format.type: - self.devicetree.registerAction(ActionCreateFormat(device)) - - def destroyDevice(self, device): - """ Schedule destruction of a device. """ - if device.format.exists and device.format.type: - # schedule destruction of any formatting while we're at it - self.devicetree.registerAction(ActionDestroyFormat(device)) - - action = ActionDestroyDevice(device) - self.devicetree.registerAction(action) - - def formatDevice(self, device, format): - """ Schedule formatting of a device. """ - self.devicetree.registerAction(ActionDestroyFormat(device)) - self.devicetree.registerAction(ActionCreateFormat(device, format)) - - def resetDevice(self, device): - """ Cancel all scheduled actions and reset formatting. """ - actions = self.devicetree.findActions(device=device) - for action in reversed(actions): - self.devicetree.cancelAction(action) - - # make sure any random overridden attributes are reset - device.format = copy.copy(device.originalFormat) - - def resizeDevice(self, device, new_size): - classes = [] - if device.resizable: - classes.append(ActionResizeDevice) - - if device.format.resizable: - classes.append(ActionResizeFormat) - - if not classes: - raise ValueError("device cannot be resized") - - # if this is a shrink, schedule the format resize first - if new_size < device.size: - classes.reverse() - - for action_class in classes: - self.devicetree.registerAction(action_class(device, new_size)) - - def formatByDefault(self, device): - """Return whether the device should be reformatted by default.""" - formatlist = ['/boot', '/var', '/tmp', '/usr'] - exceptlist = ['/home', '/usr/local', '/opt', '/var/www'] - - if not device.format.linuxNative: - return False - - if device.format.mountable: - if not device.format.mountpoint: - return False - - if device.format.mountpoint == "/" or \ - device.format.mountpoint in formatlist: - return True - - for p in formatlist: - if device.format.mountpoint.startswith(p): - for q in exceptlist: - if device.format.mountpoint.startswith(q): - return False - return True - elif device.format.type == "swap": - return True - - # be safe for anything else and default to off - return False - - def mustFormat(self, device): - """ Return a string explaining why the device must be reformatted. - - Return None if the device need not be reformatted. - """ - if device.format.mountable and device.format.mountpoint == "/": - return _("You must create a new filesystem on the root device.") - - return None - - def extendedPartitionsSupported(self): - """ Return whether any disks support extended partitions.""" - for disk in self.partitioned: - if disk.format.partedDisk.supportsFeature(parted.DISK_TYPE_EXTENDED): - return True - return False - - def safeDeviceName(self, name): - """ Convert a device name to something safe and return that. - - LVM limits lv names to 128 characters. I don't know the limits for - the other various device types, so I'm going to pick a number so - that we don't have to have an entire fucking library to determine - device name limits. - """ - max_len = 96 # No, you don't need longer names than this. Really. - tmp = name.strip() - tmp = tmp.replace("/", "_") - tmp = re.sub("[^0-9a-zA-Z._-]", "", tmp) - tmp = tmp.lstrip("_") - - if len(tmp) > max_len: - tmp = tmp[:max_len] - - return tmp - - def suggestContainerName(self, hostname=None, prefix=""): - """ Return a reasonable, unused device name. """ - if not prefix: - prefix = shortProductName - - # try to create a device name incorporating the hostname - if hostname not in (None, "", 'localhost', 'localhost.localdomain'): - template = "%s_%s" % (prefix, hostname.split('.')[0].lower()) - template = self.safeDeviceName(template) - else: - template = prefix - - if flags.image_install: - template = "%s_image" % template - - names = self.names - name = template - if name in names: - name = None - for i in range(100): - tmpname = "%s%02d" % (template, i,) - if tmpname not in names: - name = tmpname - break - - if not name: - log.error("failed to create device name based on prefix " - "'%s' and hostname '%s'" % (prefix, hostname)) - raise RuntimeError("unable to find suitable device name") - - return name - - def suggestDeviceName(self, parent=None, swap=None, - mountpoint=None, prefix=""): - """ Return a suitable, unused name for a new logical volume. """ - body = "" - if mountpoint: - if mountpoint == "/": - body = "root" - else: - body = mountpoint[1:].replace("/", "_") - elif swap: - body = "swap" - - if prefix: - body = "_" + body - - template = self.safeDeviceName(prefix + body) - names = self.names - name = template - def full_name(name, parent): - full = "" - if parent: - full = "%s-" % parent.name - full += name - return full - - # also include names of any lvs in the parent for the case of the - # temporary vg in the lvm dialogs, which can contain lvs that are - # not yet in the devicetree and therefore not in self.names - if full_name(name, parent) in names or not body: - for i in range(100): - name = "%s%02d" % (template, i) - if full_name(name, parent) not in names: - break - else: - name = "" - - if not name: - log.error("failed to create device name based on parent '%s', " - "prefix '%s', mountpoint '%s', swap '%s'" - % (parent.name, prefix, mountpoint, swap)) - raise RuntimeError("unable to find suitable device name") - - return name - - def savePassphrase(self, device): - """ Save a device's LUKS passphrase in case of reset. """ - passphrase = device.format._LUKS__passphrase - self.__luksDevs[device.format.uuid] = passphrase - self.devicetree._DeviceTree__luksDevs[device.format.uuid] = passphrase - self.devicetree._DeviceTree__passphrases.append(passphrase) - - def doEncryptionPassphraseRetrofits(self): - """ Add the global passphrase to all preexisting LUKS devices. - - This establishes a common passphrase for all encrypted devices - in the system so that users only have to enter one passphrase - during system boot. - """ - if not self.encryptionRetrofit: - return - - for device in self.devices: - if device.format.type == "luks" and \ - device.format._LUKS__passphrase != self.encryptionPassphrase: - log.info("adding new passphrase to preexisting encrypted " - "device %s" % device.path) - try: - device.format.addPassphrase(self.encryptionPassphrase) - except CryptoError: - log.error("failed to add new passphrase to existing " - "device %s" % device.path) - - def setupDiskImages(self): - self.devicetree.setDiskImages(self.config.diskImages) - self.devicetree.setupDiskImages() - - @property - def fileSystemFreeSpace(self): - mountpoints = ["/", "/usr"] - free = 0 - btrfs_volumes = [] - for mountpoint in mountpoints: - device = self.mountpoints.get(mountpoint) - if not device: - continue - - # don't count the size of btrfs volumes repeatedly when multiple - # subvolumes are present - if isinstance(device, BTRFSSubVolumeDevice): - if device.volume in btrfs_volumes: - continue - else: - btrfs_volumes.append(device.volume) - - if device.format.exists: - free += device.format.free - else: - free += device.size - - return free - - def sanityCheck(self): - """ 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). Returns (errors, warnings) where - each is a list of strings. - """ - warnings = [] - errors = [] - - if not flags.installer_mode: - return (errors, warnings) - - 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 = self.mountpoints - root = self.fsset.rootDevice - swaps = self.fsset.swapDevices - try: - boot = self.bootDevice - except (DeviceError, AttributeError): - boot = None - - if not root: - errors.append(_("You have not defined a root partition (/), " - "which is required for installation of %s " - "to continue.") % (productName,)) - - if root and root.size < 250: - warnings.append(_("Your root partition is less than 250 " - "megabytes which is usually too small to " - "install %s.") % (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 \ - not self.mountpoints.has_key('/boot') and \ - root.type == 'lvmlv' and not root.singlePV: - errors.append(_("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: - warnings.append(_("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: - errors.append(_("Your %(mount)s partition is too small for %(format)s formatting " - "(allowable size is %(minSize)d MB to %(maxSize)d MB)") - % {"mount": mount, "format": device.format.name, - "minSize": device.minSize, "maxSize": device.maxSize}) - elif problem > 0: - errors.append(_("Your %(mount)s partition is too large for %(format)s formatting " - "(allowable size is %(minSize)d MB to %(maxSize)d MB)") - % {"mount":mount, "format": device.format.name, - "minSize": device.minSize, "maxSize": device.maxSize}) - - # FIXME: this does not work, but probably should - """ - usb_disks = [] - firewire_disks = [] - #for disk in self.disks: - if isys.driveUsesModule(disk.name, ["usb-storage", "ub"]): - usb_disks.append(disk) - elif isys.driveUsesModule(disk.name, ["sbp2", "firewire-sbp2"]): - firewire_disks.append(disk) - - uses_usb = False - uses_firewire = False - for device in filesystems.values(): - for disk in usb_disks: - if device.dependsOn(disk): - uses_usb = True - break - - for disk in firewire_disks: - if device.dependsOn(disk): - uses_firewire = True - break - - if uses_usb: - warnings.append(_("Installing on a USB device. This may " - "or may not produce a working system.")) - if uses_firewire: - warnings.append(_("Installing on a FireWire device. This may " - "or may not produce a working system.")) - """ - - if self.bootloader and not self.bootloader.skip_bootloader: - stage1 = self.bootloader.stage1_device - if not stage1: - errors.append(_("you have not created a bootloader stage1 " - "target device")) - else: - self.bootloader.is_valid_stage1_device(stage1) - errors.extend(self.bootloader.errors) - warnings.extend(self.bootloader.warnings) - - stage2 = self.bootloader.stage2_device - if not stage2: - errors.append(_("You have not created a bootable partition.")) - else: - self.bootloader.is_valid_stage2_device(stage2) - errors.extend(self.bootloader.errors) - warnings.extend(self.bootloader.warnings) - if not self.bootloader.check(): - errors.extend(self.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 self.partitions if p.disk == stage1]: - if part.format.type == "biosboot": - missing = False - break - - if missing: - errors.append(_("Your BIOS-based system needs a special " - "partition to boot with %s's new " - "disk label format (GPT). To continue, " - "please create a 1MB 'BIOS Boot' type " - "partition.") % productName) - - if not swaps: - from pyanaconda.storage.size import Size - - installed = Size(spec="%s kb" % util.total_memory()) - required = Size(spec="%s kb" % isys.EARLY_SWAP_RAM) - - if installed < required: - errors.append(_("You have not specified a swap partition. " - "%(requiredMem)s MB of memory is required to continue installation " - "without a swap partition, but you only have %(installedMem)s MB.") - % {"requiredMem": int(required.convertTo(spec="MB")), - "installedMem": int(installed.convertTo(spec="MB"))}) - else: - warnings.append(_("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: - warnings.append(_("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: - errors.append(_("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): - errors.append(_("The mount point %s must be on a linux file system.") % mountpoint) - - if self.rootDevice and self.rootDevice.format.exists: - e = self.mustFormat(self.rootDevice) - if e: - errors.append(e) - - return (errors, warnings) - - def isProtected(self, device): - """ Return True is the device is protected. """ - return device.protected - - def checkNoDisks(self): - """Check that there are valid disk devices.""" - if not self.disks: - raise NoDisksError() - - def dumpState(self, suffix): - """ Dump the current device list to the storage shelf. """ - key = "devices.%d.%s" % (time.time(), suffix) - with contextlib.closing(shelve.open(self._dumpFile)) as shelf: - shelf[key] = [d.dict for d in self.devices] - - @property - def packages(self): - pkgs = set() - pkgs.update(_platform.packages) - - if self.bootloader: - pkgs.update(self.bootloader.packages) - - for device in self.fsset.devices: - # this takes care of device and filesystem packages - pkgs.update(device.packages) - - return list(pkgs) - - def write(self): - if not os.path.isdir("%s/etc" % ROOT_PATH): - os.mkdir("%s/etc" % ROOT_PATH) - - self.fsset.write() - self.makeMtab() - self.iscsi.write(self) - self.fcoe.write() - self.zfcp.write() - self.dasd.write() - - def turnOnSwap(self, upgrading=None): - self.fsset.turnOnSwap(rootPath=ROOT_PATH, - upgrading=upgrading) - - def mountFilesystems(self, raiseErrors=None, readOnly=None, skipRoot=False): - self.fsset.mountFilesystems(rootPath=ROOT_PATH, - raiseErrors=raiseErrors, - readOnly=readOnly, skipRoot=skipRoot) - - def umountFilesystems(self, ignoreErrors=True, swapoff=True): - self.fsset.umountFilesystems(ignoreErrors=ignoreErrors, swapoff=swapoff) - - def parseFSTab(self, chroot=None): - self.fsset.parseFSTab(chroot=chroot) - - def mkDevRoot(self): - self.fsset.mkDevRoot() - - def createSwapFile(self, device, size): - self.fsset.createSwapFile(device, size) - - @property - def bootloader(self): - if self._bootloader is None and flags.installer_mode: - self._bootloader = get_bootloader() - - return self._bootloader - - def updateBootLoaderDiskList(self): - if not self.bootloader: - return - - boot_disks = [d for d in self.disks if d.partitioned] - boot_disks.sort(cmp=self.compareDisks, key=lambda d: d.name) - self.bootloader.set_disk_list(boot_disks) - - def setUpBootLoader(self): - """ Propagate ksdata into BootLoader. """ - if not self.bootloader or not self.data: - log.warning("either ksdata or bootloader data missing") - return - - if self.bootloader.skip_bootloader: - log.info("user specified that bootloader install be skipped") - return - - self.bootloader.stage1_disk = self.devicetree.resolveDevice(self.data.bootloader.bootDrive) - self.bootloader.stage2_device = self.bootDevice - try: - self.bootloader.set_stage1_device(self.devices) - except BootLoaderError as e: - log.debug("failed to set bootloader stage1 device: %s" % e) - - @property - def bootDisk(self): - disk = None - if self.data: - spec = self.data.bootloader.bootDrive - disk = self.devicetree.resolveDevice(spec) - return disk - - @property - def bootDevice(self): - dev = None - if self.fsset: - dev = self.mountpoints.get("/boot", self.rootDevice) - return dev - - @property - def bootLoaderDevice(self): - return getattr(self.bootloader, "stage1_device", None) - - @property - def bootFSTypes(self): - """A list of all valid filesystem types for the boot partition.""" - fstypes = [] - if self.bootloader: - fstypes = self.bootloader.stage2_format_types - return fstypes - - @property - def defaultBootFSType(self): - """The default filesystem type for the boot partition.""" - fstype = None - if self.bootloader: - fstype = self.bootFSTypes[0] - return fstype - - @property - def mountpoints(self): - return self.fsset.mountpoints - - @property - def rootDevice(self): - return self.fsset.rootDevice - - def makeMtab(self): - path = "/etc/mtab" - target = "/proc/self/mounts" - path = os.path.normpath("%s/%s" % (ROOT_PATH, path)) - - if os.path.islink(path): - # return early if the mtab symlink is already how we like it - current_target = os.path.normpath(os.path.dirname(path) + - "/" + os.readlink(path)) - if current_target == target: - return - - if os.path.exists(path): - os.unlink(path) - - os.symlink(target, path) - - def compareDisks(self, first, second): - if self.eddDict.has_key(first) and self.eddDict.has_key(second): - one = self.eddDict[first] - two = self.eddDict[second] - if (one < two): - return -1 - elif (one > two): - return 1 - - # if one is in the BIOS and the other not prefer the one in the BIOS - if self.eddDict.has_key(first): - return -1 - if self.eddDict.has_key(second): - return 1 - - if first.startswith("hd"): - type1 = 0 - elif first.startswith("sd"): - type1 = 1 - elif (first.startswith("vd") or first.startswith("xvd")): - type1 = -1 - else: - type1 = 2 - - if second.startswith("hd"): - type2 = 0 - elif second.startswith("sd"): - type2 = 1 - elif (second.startswith("vd") or second.startswith("xvd")): - type2 = -1 - else: - type2 = 2 - - if (type1 < type2): - return -1 - elif (type1 > type2): - return 1 - else: - len1 = len(first) - len2 = len(second) - - if (len1 < len2): - return -1 - elif (len1 > len2): - return 1 - else: - if (first < second): - return -1 - elif (first > second): - return 1 - - return 0 - - def getFSType(self, mountpoint=None): - """ Return the default filesystem type based on mountpoint. """ - fstype = self.defaultFSType - if not mountpoint: - # just return the default - pass - elif mountpoint.lower() in ("swap", "biosboot", "prepboot"): - fstype = mountpoint.lower() - elif mountpoint == "/boot": - fstype = self.defaultBootFSType - elif mountpoint == "/boot/efi": - if arch.isMactel(): - fstype = "hfs+" - else: - fstype = "efi" - - return fstype - - def setContainerMembers(self, container, factory, members=None, - device=None): - """ Set up and return the container's member partitions. """ - log_members = [] - if members: - log_members = [str(m) for m in members] - log_method_call(self, container=container, factory=factory, - members=log_members, device=device) - if factory.member_list is not None: - # short-circuit the logic below for partitions - return factory.member_list - - if container and container.exists: - # don't try to modify an existing container - return container.parents - - if factory.container_size_func is None: - return [] - - # set up member devices - container_size = factory.device_size - add_disks = [] - remove_disks = [] - - if members is None: - members = [] - - if container: - members = container.parents[:] - elif members: - # mdarray - container = device - - # The basis for whether we are modifying a member set versus creating - # one must be the member list, as container will be None when modifying - # the member set of an md array. - - # XXX how can we detect/handle failure to use one or more of the disks? - if members and device: - # See if we need to add/remove any disks, but only if we are - # adjusting a device. When adding a new device to a container we do - # not want to modify the container's disk set. - _disks = list(set([d for m in members for d in m.disks])) - - add_disks = [d for d in factory.disks if d not in _disks] - remove_disks = [d for d in _disks if d not in factory.disks] - elif not members: - # new container, so use the factory's disk set - add_disks = factory.disks - - # drop any new disks that don't have free space - min_free = min(500, factory.size) - add_disks = [d for d in add_disks if d.partitioned and - d.format.free >= min_free] - - base_size = max(1, getFormat(factory.member_format).minSize) - - # XXX TODO: multiple member devices per disk - - # prepare already-defined member partitions for reallocation - for member in members[:]: - if any([d in remove_disks for d in member.disks]): - if isinstance(member, LUKSDevice): - if container: - container.removeMember(member) - self.destroyDevice(member) - members.remove(member) - member = member.slave - else: - if container: - container.removeMember(member) - - members.remove(member) - - self.destroyDevice(member) - continue - - if isinstance(member, LUKSDevice): - if not factory.encrypted: - # encryption was toggled for the member devices - if container: - container.removeMember(member) - - self.destroyDevice(member) - members.remove(member) - - self.formatDevice(member.slave, - getFormat(factory.member_format)) - members.append(member.slave) - if container: - container.addMember(member.slave) - - member = member.slave - elif factory.encrypted: - # encryption was toggled for the member devices - if container: - container.removeMember(member) - - members.remove(member) - self.formatDevice(member, getFormat("luks")) - luks_member = LUKSDevice("luks-%s" % member.name, - parents=[member], - format=getFormat(factory.member_format)) - self.createDevice(luks_member) - members.append(luks_member) - if container: - container.addMember(luks_member) - - member.req_base_size = base_size - member.req_size = member.req_base_size - member.req_grow = True - - # set up new members as needed to accommodate the device - new_members = [] - for disk in add_disks: - if factory.encrypted and factory.encrypt_members: - luks_format = factory.member_format - member_format = "luks" - else: - member_format = factory.member_format - - try: - member = self.newPartition(parents=[disk], grow=True, - size=base_size, - fmt_type=member_format) - except StorageError as e: - log.error("failed to create new member partition: %s" % e) - continue - - self.createDevice(member) - if factory.encrypted and factory.encrypt_members: - fmt = getFormat(luks_format) - member = LUKSDevice("luks-%s" % member.name, - parents=[member], format=fmt) - self.createDevice(member) - - members.append(member) - new_members.append(member) - if container: - container.addMember(member) - - if container: - log.debug("using container %s with %d devices" % (container.name, - len(self.devicetree.getChildren(container)))) - container_size = factory.container_size_func(container, device) - log.debug("raw container size reported as %d" % container_size) - - log.debug("adding a %s with size %d" % (factory.set_class.__name__, - container_size)) - size_set = factory.set_class(members, container_size) - self.size_sets.append(size_set) - for member in members[:]: - if isinstance(member, LUKSDevice): - member = member.slave - - member.req_max_size = size_set.size - - try: - self.allocatePartitions() - except PartitioningError as e: - # try to clean up by destroying all newly added members before re- - # raising the exception - self.__cleanUpMemberDevices(new_members, container=container) - raise - - return members - - def allocatePartitions(self): - """ Allocate all requested partitions. """ - try: - doPartitioning(self) - except StorageError as e: - log.error("failed to allocate partitions: %s" % e) - raise - - def getDeviceFactory(self, device_type, size, **kwargs): - """ Return a suitable DeviceFactory instance for device_type. """ - disks = kwargs.get("disks", []) - raid_level = kwargs.get("raid_level") - encrypted = kwargs.get("encrypted", False) - - class_table = {DEVICE_TYPE_LVM: LVMFactory, - DEVICE_TYPE_BTRFS: BTRFSFactory, - DEVICE_TYPE_PARTITION: PartitionFactory, - DEVICE_TYPE_MD: MDFactory, - DEVICE_TYPE_DISK: DiskFactory} - - factory_class = class_table[device_type] - log.debug("instantiating %s: %r, %s, %s, %s" % (factory_class, - self, size, [d.name for d in disks], raid_level)) - return factory_class(self, size, disks, raid_level, encrypted) - - def getContainer(self, factory, device=None, name=None, existing=False): - # XXX would it be useful to implement this as a series of fallbacks - # instead of mutually exclusive branches? - container = None - if name: - container = self.devicetree.getDeviceByName(name) - if container and container not in factory.container_list: - log.debug("specified container name %s is wrong type (%s)" - % (name, container.type)) - container = None - elif device: - if hasattr(device, "vg"): - container = device.vg - elif hasattr(device, "volume"): - container = device.volume - else: - containers = [c for c in factory.container_list if not c.exists] - if containers: - container = containers[0] - - if container is None and existing: - containers = [c for c in factory.container_list if c.exists] - if containers: - containers.sort(key=lambda c: getattr(c, "freeSpace", c.size), - reverse=True) - container = containers[0] - - return container - - def __cleanUpMemberDevices(self, members, container=None): - for member in members: - if container: - container.removeMember(member) - - if isinstance(member, LUKSDevice): - self.destroyDevice(member) - member = member.slave - - if not member.isDisk: - self.destroyDevice(member) - - def newDevice(self, device_type, size, **kwargs): - """ Schedule creation of a device based on a top-down specification. - - Arguments: - - device_type an AUTOPART_TYPE constant (lvm|btrfs|plain) - size device's requested size - - Keyword arguments: - - mountpoint the device's mountpoint - fstype the device's filesystem type, or swap - label filesystem label - disks the set of disks we can allocate from - encrypted boolean - - raid_level (btrfs/md/lvm only) RAID level (string) - - name name for new device - container_name name of requested container - - device an already-defined but non-existent device - to adjust instead of creating a new device - - - Error handling: - - If device is None, meaning we're creating a device, the error - handling aims to remove all evidence of the attempt to create a - new device by removing unused container devices, reverting the - size of container devices that contain other devices, &c. - - If the device is not None, meaning we're adjusting the size of - a defined device, the error handling aims to revert the device - and any container to it previous size. - - In either case, we re-raise the exception so the caller knows - there was a failure. If we failed to clean up as described above - we raise ErrorRecoveryFailure to alert the caller that things - will likely be in an inconsistent state. - """ - log_method_call(self, device_type, size, **kwargs) - mountpoint = kwargs.get("mountpoint") - fstype = kwargs.get("fstype") - label = kwargs.get("label") - disks = kwargs.get("disks") - encrypted = kwargs.get("encrypted", self.data.autopart.encrypted) - - name = kwargs.get("name") - container_name = kwargs.get("container_name") - - device = kwargs.get("device") - - # md, btrfs - raid_level = kwargs.get("raid_level") - - # we can't do anything with existing devices - if device and device.exists: - log.info("newDevice refusing to change device %s" % device) - return - - if not fstype: - fstype = self.getFSType(mountpoint=mountpoint) - if fstype == "swap": - mountpoint = None - - if fstype == "swap" and device_type == DEVICE_TYPE_BTRFS: - device_type = DEVICE_TYPE_PARTITION - - fmt_args = {} - if label: - fmt_args["label"] = label - - factory = self.getDeviceFactory(device_type, size, **kwargs) - - if not factory.disks: - raise StorageError("no disks specified for new device") - - self.size_sets = [] # clear this since there are no growable reqs now - - container = self.getContainer(factory, device=device, - name=container_name) - - # TODO: striping, mirroring, &c - # TODO: non-partition members (pv-on-md) - - # setContainerMembers can modify these, so save them now - old_size = None - old_disks = [] - if device: - old_size = device.size - old_disks = device.disks[:] - - members = [] - if device and device.type == "mdarray": - members = device.parents[:] - - try: - parents = self.setContainerMembers(container, factory, - members=members, device=device) - except PartitioningError as e: - # If this is a new device, just clean up and get out. - if device: - # If this is a defined device, try to clean up by reallocating - # members as before and then get out. - factory.disks = device.disks - factory.size = device.size # this should work - - if members: - # If this is an md array we have to reset its member set - # here. - # If there is a container device, its member set was reset - # in the exception handler in setContainerMembers. - device.parents = members - - try: - self.setContainerMembers(container, factory, - members=members, - device=device) - except StorageError as e: - log.error("failed to revert device size: %s" % e) - raise ErrorRecoveryFailure("failed to revert container") - - raise - - # set up container - if not container and factory.new_container_attr: - if not parents: - raise StorageError("not enough free space on disks") - - log.debug("creating new container") - if container_name: - kwa = {"name": container_name} - else: - kwa = {} - try: - container = factory.new_container(parents=parents, **kwa) - except StorageError as e: - log.error("failed to create new device: %s" % e) - # Clean up by destroying the newly created member devices. - self.__cleanUpMemberDevices(parents) - raise - - self.createDevice(container) - elif container and not container.exists and \ - hasattr(container, "dataLevel"): - container.dataLevel = factory.raid_level - - if container: - parents = [container] - log.debug("%r" % container) - - # this will set the device's size if a device is passed in - size = factory.set_device_size(container, device=device) - if device: - # We are adjusting a defined device: size, disk set, encryption, - # raid level, fstype. The StorageDevice instance exists, but the - # underlying device does not. - # TODO: handle toggling of encryption for leaf device - e = None - try: - factory.post_create() - except StorageError as e: - log.error("device post-create method failed: %s" % e) - else: - if device.size <= device.format.minSize: - e = StorageError("failed to adjust device -- not enough free space in specified disks?") - - if e: - # Clean up by reverting the device to its previous size. - factory.size = old_size - factory.disks = old_disks - try: - self.setContainerMembers(container, factory, - members=members, device=device) - except StorageError as e: - # yes, we're replacing e here. - log.error("failed to revert device size: %s" % e) - raise ErrorRecoveryFailure("failed to revert device size") - - factory.set_device_size(container, device=device) - try: - factory.post_create() - except StorageError as e: - # yes, we're replacing e here. - log.error("failed to revert device size: %s" % e) - raise ErrorRecoveryFailure("failed to revert device size") - - raise(e) - elif factory.new_device_attr: - log.debug("creating new device") - if factory.encrypted and factory.encrypt_leaves: - luks_fmt_type = fstype - luks_fmt_args = fmt_args - luks_mountpoint = mountpoint - fstype = "luks" - mountpoint = None - fmt_args = {} - - def _container_post_error(): - # Clean up. If there is a container and it has other devices, - # try to revert it. If there is a container and it has no other - # devices, remove it. If there is not a container, remove all of - # the parents. - if container: - if container.kids: - factory.size = 0 - factory.disks = container.disks - try: - self.setContainerMembers(container, factory) - except StorageError as e: - log.error("failed to revert container: %s" % e) - raise ErrorRecoveryFailure("failed to revert container") - else: - self.destroyDevice(container) - self.__cleanUpMemberDevices(container.parents) - else: - self.__cleanUpMemberDevices(parents) - - if name: - kwa = {"name": name} - else: - kwa = {} - - try: - device = factory.new_device(parents=parents, - size=size, - fmt_type=fstype, - mountpoint=mountpoint, - fmt_args=fmt_args, - **kwa) - except (StorageError, ValueError) as e: - log.error("device instance creation failed: %s" % e) - _container_post_error() - raise - - self.createDevice(device) - e = None - try: - factory.post_create() - except StorageError as e: - log.error("device post-create method failed: %s" % e) - else: - if not device.size: - e = StorageError("failed to create device") - - if e: - self.destroyDevice(device) - _container_post_error() - raise StorageError(e) - - if factory.encrypted and factory.encrypt_leaves: - fmt = getFormat(luks_fmt_type, - mountpoint=luks_mountpoint, - **luks_fmt_args) - luks_device = LUKSDevice("luks-" + device.name, - parents=[device], format=fmt) - self.createDevice(luks_device) - - def copy(self): - new = copy.deepcopy(self) - # go through and re-get partedPartitions from the disks since they - # don't get deep-copied - for partition in new.partitions: - if not partition._partedPartition: - continue - - # don't ask me why, but we have to update the refs in req_disks - req_disks = [] - for disk in partition.req_disks: - req_disks.append(new.devicetree.getDeviceByID(disk.id)) - - partition.req_disks = req_disks - - p = partition.disk.format.partedDisk.getPartitionByPath(partition.path) - partition.partedPartition = p - - return new - - -def mountExistingSystem(fsset, rootDevice, - allowDirty=None, dirtyCB=None, - readOnly=None): - """ Mount filesystems specified in rootDevice's /etc/fstab file. """ - rootPath = ROOT_PATH - if dirtyCB is None: - dirtyCB = lambda l: False - - if readOnly: - readOnly = "ro" - else: - readOnly = "" - - if rootDevice.protected and os.path.ismount("/mnt/install/isodir"): - util.mount("/mnt/install/isodir", - rootPath, - fstype=rootDevice.format.type, - options="bind") - else: - rootDevice.setup() - rootDevice.format.mount(chroot=rootPath, - mountpoint="/", - options=readOnly) - - fsset.parseFSTab() - - # check for dirty filesystems - dirtyDevs = [] - for device in fsset.mountpoints.values(): - if not hasattr(device.format, "needsFSCheck"): - continue - - try: - device.setup() - except DeviceError as e: - # we'll catch this in the main loop - continue - - if device.format.needsFSCheck: - log.info("%s contains a dirty %s filesystem" % (device.path, - device.format.type)) - dirtyDevs.append(device.path) - - if dirtyDevs and (not allowDirty or dirtyCB(dirtyDevs)): - raise DirtyFSError("\n".join(dirtyDevs)) - - fsset.mountFilesystems(rootPath=ROOT_PATH, readOnly=readOnly, skipRoot=True) - - -class BlkidTab(object): - """ Dictionary-like interface to blkid.tab with device path keys """ - def __init__(self, chroot=""): - self.chroot = chroot - self.devices = {} - - def parse(self): - path = "%s/etc/blkid/blkid.tab" % self.chroot - log.debug("parsing %s" % path) - with open(path) as f: - for line in f.readlines(): - # this is pretty ugly, but an XML parser is more work than - # is justifiable for this purpose - if not line.startswith("<device "): - continue - - line = line[len("<device "):-len("</device>\n")] - (data, sep, device) = line.partition(">") - if not device: - continue - - self.devices[device] = {} - for pair in data.split(): - try: - (key, value) = pair.split("=") - except ValueError: - continue - - self.devices[device][key] = value[1:-1] # strip off quotes - - def __getitem__(self, key): - return self.devices[key] - - def get(self, key, default=None): - return self.devices.get(key, default) - - -class CryptTab(object): - """ Dictionary-like interface to crypttab entries with map name keys """ - def __init__(self, devicetree, blkidTab=None, chroot=""): - self.devicetree = devicetree - self.blkidTab = blkidTab - self.chroot = chroot - self.mappings = {} - - def parse(self, chroot=""): - """ Parse /etc/crypttab from an existing installation. """ - if not chroot or not os.path.isdir(chroot): - chroot = "" - - path = "%s/etc/crypttab" % chroot - log.debug("parsing %s" % path) - with open(path) as f: - if not self.blkidTab: - try: - self.blkidTab = BlkidTab(chroot=chroot) - self.blkidTab.parse() - except Exception: - self.blkidTab = None - - for line in f.readlines(): - (line, pound, comment) = line.partition("#") - fields = line.split() - if not 2 <= len(fields) <= 4: - continue - elif len(fields) == 2: - fields.extend(['none', '']) - elif len(fields) == 3: - fields.append('') - - (name, devspec, keyfile, options) = fields - - # resolve devspec to a device in the tree - device = self.devicetree.resolveDevice(devspec, - blkidTab=self.blkidTab) - if device: - self.mappings[name] = {"device": device, - "keyfile": keyfile, - "options": options} - - def populate(self): - """ Populate the instance based on the device tree's contents. """ - for device in self.devicetree.devices: - # XXX should we put them all in there or just the ones that - # are part of a device containing swap or a filesystem? - # - # Put them all in here -- we can filter from FSSet - if device.format.type != "luks": - continue - - key_file = device.format.keyFile - if not key_file: - key_file = "none" - - options = device.format.options - if not options: - options = "" - - self.mappings[device.format.mapName] = {"device": device, - "keyfile": key_file, - "options": options} - - def crypttab(self): - """ Write out /etc/crypttab """ - crypttab = "" - for name in self.mappings: - entry = self[name] - crypttab += "%s UUID=%s %s %s\n" % (name, - entry['device'].format.uuid, - entry['keyfile'], - entry['options']) - return crypttab - - def __getitem__(self, key): - return self.mappings[key] - - def get(self, key, default=None): - return self.mappings.get(key, default) - -def get_containing_device(path, devicetree): - """ Return the device that a path resides on. """ - if not os.path.exists(path): - return None - - st = os.stat(path) - major = os.major(st.st_dev) - minor = os.minor(st.st_dev) - link = "/sys/dev/block/%s:%s" % (major, minor) - if not os.path.exists(link): - return None - - try: - device_name = os.path.basename(os.readlink(link)) - except Exception: - return None - - if device_name.startswith("dm-"): - # have I told you lately that I love you, device-mapper? - device_name = name_from_dm_node(device_name) - - return devicetree.getDeviceByName(device_name) - - -class FSSet(object): - """ A class to represent a set of filesystems. """ - def __init__(self, devicetree): - self.devicetree = devicetree - self.cryptTab = None - self.blkidTab = None - self.origFStab = None - self.active = False - self._dev = None - self._devpts = None - self._sysfs = None - self._proc = None - self._devshm = None - self._usb = None - self._selinux = None - self.preserveLines = [] # lines we just ignore and preserve - - @property - def sysfs(self): - if not self._sysfs: - self._sysfs = NoDevice(format=getFormat("sysfs", - device="sys", - mountpoint="/sys")) - return self._sysfs - - @property - def dev(self): - if not self._dev: - self._dev = DirectoryDevice("/dev", format=getFormat("bind", - device="/dev", - mountpoint="/dev", - exists=True), - exists=True) - - return self._dev - - @property - def devpts(self): - if not self._devpts: - self._devpts = NoDevice(format=getFormat("devpts", - device="devpts", - mountpoint="/dev/pts")) - return self._devpts - - @property - def proc(self): - if not self._proc: - self._proc = NoDevice(format=getFormat("proc", - device="proc", - mountpoint="/proc")) - return self._proc - - @property - def devshm(self): - if not self._devshm: - self._devshm = NoDevice(format=getFormat("tmpfs", - device="tmpfs", - mountpoint="/dev/shm")) - return self._devshm - - @property - def usb(self): - if not self._usb: - self._usb = NoDevice(format=getFormat("usbfs", - device="usbfs", - mountpoint="/proc/bus/usb")) - return self._usb - - @property - def selinux(self): - if not self._selinux: - self._selinux = NoDevice(format=getFormat("selinuxfs", - device="selinuxfs", - mountpoint="/sys/fs/selinux")) - return self._selinux - - @property - def devices(self): - return sorted(self.devicetree.devices, key=lambda d: d.path) - - @property - def mountpoints(self): - filesystems = {} - for device in self.devices: - if device.format.mountable and device.format.mountpoint: - filesystems[device.format.mountpoint] = device - return filesystems - - def _parseOneLine(self, (devspec, mountpoint, fstype, options, dump, passno)): - # no sense in doing any legwork for a noauto entry - if "noauto" in options.split(","): - log.info("ignoring noauto entry") - raise UnrecognizedFSTabEntryError() - - # find device in the tree - device = self.devicetree.resolveDevice(devspec, - cryptTab=self.cryptTab, - blkidTab=self.blkidTab) - - if device: - # fall through to the bottom of this block - pass - elif devspec.startswith("/dev/loop"): - # FIXME: create devices.LoopDevice - log.warning("completely ignoring your loop mount") - elif ":" in devspec and fstype.startswith("nfs"): - # NFS -- preserve but otherwise ignore - device = NFSDevice(devspec, - exists=True, - format=getFormat(fstype, - exists=True, - device=devspec)) - elif devspec.startswith("/") and fstype == "swap": - # swap file - device = FileDevice(devspec, - parents=get_containing_device(devspec, self.devicetree), - format=getFormat(fstype, - device=devspec, - exists=True), - exists=True) - elif fstype == "bind" or "bind" in options: - # bind mount... set fstype so later comparison won't - # turn up false positives - fstype = "bind" - - # This is probably not going to do anything useful, so we'll - # make sure to try again from FSSet.mountFilesystems. The bind - # mount targets should be accessible by the time we try to do - # the bind mount from there. - parents = get_containing_device(devspec, self.devicetree) - device = DirectoryDevice(devspec, parents=parents, exists=True) - device.format = getFormat("bind", - device=device.path, - exists=True) - elif mountpoint in ("/proc", "/sys", "/dev/shm", "/dev/pts", - "/sys/fs/selinux", "/proc/bus/usb"): - # drop these now -- we'll recreate later - return None - else: - # nodev filesystem -- preserve or drop completely? - format = getFormat(fstype) - if devspec == "none" or \ - isinstance(format, get_device_format_class("nodev")): - device = NoDevice(format=format) - - if device is None: - log.error("failed to resolve %s (%s) from fstab" % (devspec, - fstype)) - raise UnrecognizedFSTabEntryError() - - device.setup() - fmt = getFormat(fstype, device=device.path, exists=True) - if fstype != "auto" and None in (device.format.type, fmt.type): - log.info("Unrecognized filesystem type for %s (%s)" - % (device.name, fstype)) - device.teardown() - raise UnrecognizedFSTabEntryError() - - # make sure, if we're using a device from the tree, that - # the device's format we found matches what's in the fstab - ftype = getattr(fmt, "mountType", fmt.type) - dtype = getattr(device.format, "mountType", device.format.type) - if fstype != "auto" and ftype != dtype: - log.info("fstab says %s at %s is %s" % (dtype, mountpoint, ftype)) - if fmt.testMount(): - device.format = fmt - else: - device.teardown() - raise FSTabTypeMismatchError("%s: detected as %s, fstab says %s" - % (mountpoint, dtype, ftype)) - del ftype - del dtype - - if device.format.mountable: - device.format.mountpoint = mountpoint - device.format.mountopts = options - - # is this useful? - try: - device.format.options = options - except AttributeError: - pass - - return device - - def parseFSTab(self, chroot=None): - """ parse /etc/fstab - - preconditions: - all storage devices have been scanned, including filesystems - postconditions: - - FIXME: control which exceptions we raise - - XXX do we care about bind mounts? - how about nodev mounts? - loop mounts? - """ - if not chroot or not os.path.isdir(chroot): - chroot = ROOT_PATH - - 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 - - blkidTab = BlkidTab(chroot=chroot) - try: - blkidTab.parse() - log.debug("blkid.tab devs: %s" % blkidTab.devices.keys()) - except Exception as e: - log.info("error parsing blkid.tab: %s" % e) - blkidTab = None - - cryptTab = CryptTab(self.devicetree, blkidTab=blkidTab, chroot=chroot) - try: - cryptTab.parse(chroot=chroot) - log.debug("crypttab maps: %s" % cryptTab.mappings.keys()) - except Exception as e: - log.info("error parsing crypttab: %s" % e) - cryptTab = None - - self.blkidTab = blkidTab - self.cryptTab = cryptTab - - with open(path) as f: - log.debug("parsing %s" % path) - - lines = f.readlines() - - # save the original file - self.origFStab = ''.join(lines) - - for line in lines: - # strip off comments - (line, pound, comment) = line.partition("#") - fields = line.split() - - if not 4 <= len(fields) <= 6: - continue - elif len(fields) == 4: - fields.extend([0, 0]) - elif len(fields) == 5: - fields.append(0) - - (devspec, mountpoint, fstype, options, dump, passno) = fields - - try: - device = self._parseOneLine((devspec, mountpoint, fstype, options, dump, passno)) - except UnrecognizedFSTabEntryError: - # just write the line back out as-is after upgrade - self.preserveLines.append(line) - continue - - if not device: - continue - - if device not in self.devicetree.devices: - try: - self.devicetree._addDevice(device) - except ValueError: - # just write duplicates back out post-install - self.preserveLines.append(line) - - def turnOnSwap(self, rootPath="", upgrading=None): - """ Activate the system's swap space. """ - if not flags.installer_mode: - return - - for device in self.swapDevices: - if isinstance(device, FileDevice): - # set up FileDevices' parents now that they are accessible - targetDir = "%s/%s" % (rootPath, device.path) - parent = get_containing_device(targetDir, self.devicetree) - if not parent: - log.error("cannot determine which device contains " - "directory %s" % device.path) - device.parents = [] - self.devicetree._removeDevice(device) - continue - else: - device.parents = [parent] - - while True: - try: - device.setup() - device.format.setup() - except StorageError as e: - if errorHandler.cb(e, device) == ERROR_RAISE: - raise - else: - break - - def mountFilesystems(self, rootPath="", readOnly=None, - skipRoot=False, raiseErrors=None): - """ Mount the system's filesystems. """ - if not flags.installer_mode: - return - - devices = self.mountpoints.values() + self.swapDevices - devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, - self.proc, self.selinux, self.usb]) - devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) - - for device in devices: - if not device.format.mountable or not device.format.mountpoint: - continue - - if skipRoot and device.format.mountpoint == "/": - continue - - options = device.format.options - if "noauto" in options.split(","): - continue - - if device.format.type == "bind" and device != self.dev: - # set up the DirectoryDevice's parents now that they are - # accessible - # - # -- bind formats' device and mountpoint are always both - # under the chroot. no exceptions. none, damn it. - targetDir = "%s/%s" % (rootPath, device.path) - parent = get_containing_device(targetDir, self.devicetree) - if not parent: - log.error("cannot determine which device contains " - "directory %s" % device.path) - device.parents = [] - self.devicetree._removeDevice(device) - continue - else: - device.parents = [parent] - - try: - device.setup() - except Exception as e: - if errorHandler.cb(e, device) == ERROR_RAISE: - raise - else: - continue - - if readOnly: - options = "%s,%s" % (options, readOnly) - - try: - device.format.setup(options=options, - chroot=rootPath) - except Exception as e: - log.error("error mounting %s on %s: %s" - % (device.path, device.format.mountpoint, e)) - if errorHandler.cb(e, device) == ERROR_RAISE: - raise - - self.active = True - - def umountFilesystems(self, ignoreErrors=True, swapoff=True): - """ unmount filesystems, except swap if swapoff == False """ - devices = self.mountpoints.values() + self.swapDevices - devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, - self.proc, self.usb, self.selinux]) - devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) - devices.reverse() - for device in devices: - if (not device.format.mountable) or \ - (device.format.type == "swap" and not swapoff): - continue - - device.format.teardown() - device.teardown() - - self.active = False - - def createSwapFile(self, device, size): - """ Create and activate a swap file under ROOT_PATH. """ - filename = "/SWAP" - count = 0 - basedir = os.path.normpath("%s/%s" % (ROOT_PATH, - device.format.mountpoint)) - while os.path.exists("%s/%s" % (basedir, filename)) or \ - self.devicetree.getDeviceByName(filename): - file = os.path.normpath("%s/%s" % (basedir, filename)) - count += 1 - filename = "/SWAP-%d" % count - - dev = FileDevice(filename, - size=size, - parents=[device], - format=getFormat("swap", device=filename)) - dev.create() - dev.setup() - dev.format.create() - dev.format.setup() - # nasty, nasty - self.devicetree._addDevice(dev) - - def mkDevRoot(self): - root = self.rootDevice - dev = "%s/%s" % (ROOT_PATH, root.path) - if not os.path.exists("%s/dev/root" %(ROOT_PATH,)) and os.path.exists(dev): - rdev = os.stat(dev).st_rdev - os.mknod("%s/dev/root" % (ROOT_PATH,), stat.S_IFBLK | 0600, rdev) - - @property - def swapDevices(self): - swaps = [] - for device in self.devices: - if device.format.type == "swap": - swaps.append(device) - return swaps - - @property - def rootDevice(self): - for path in ["/", ROOT_PATH]: - for device in self.devices: - try: - mountpoint = device.format.mountpoint - except AttributeError: - mountpoint = None - - if mountpoint == path: - return device - - def write(self): - """ write out all config files based on the set of filesystems """ - # /etc/fstab - fstab_path = os.path.normpath("%s/etc/fstab" % ROOT_PATH) - fstab = self.fstab() - open(fstab_path, "w").write(fstab) - - # /etc/crypttab - crypttab_path = os.path.normpath("%s/etc/crypttab" % ROOT_PATH) - crypttab = self.crypttab() - origmask = os.umask(0077) - open(crypttab_path, "w").write(crypttab) - os.umask(origmask) - - # /etc/mdadm.conf - mdadm_path = os.path.normpath("%s/etc/mdadm.conf" % ROOT_PATH) - mdadm_conf = self.mdadmConf() - if mdadm_conf: - open(mdadm_path, "w").write(mdadm_conf) - - # /etc/multipath.conf - multipath_conf = self.multipathConf() - if multipath_conf: - multipath_path = os.path.normpath("%s/etc/multipath.conf" % - ROOT_PATH) - conf_contents = multipath_conf.write(self.devicetree.mpathFriendlyNames) - f = open(multipath_path, "w") - f.write(conf_contents) - f.close() - else: - log.info("not writing out mpath configuration") - util.copy_to_system("/etc/multipath/wwids") - if self.devicetree.mpathFriendlyNames: - util.copy_to_system("/etc/multipath/bindings") - - def crypttab(self): - # if we are upgrading, do we want to update crypttab? - # gut reaction says no, but plymouth needs the names to be very - # specific for passphrase prompting - if not self.cryptTab: - self.cryptTab = CryptTab(self.devicetree) - self.cryptTab.populate() - - devices = self.mountpoints.values() + self.swapDevices - - # prune crypttab -- only mappings required by one or more entries - for name in self.cryptTab.mappings.keys(): - keep = False - mapInfo = self.cryptTab[name] - cryptoDev = mapInfo['device'] - for device in devices: - if device == cryptoDev or device.dependsOn(cryptoDev): - keep = True - break - - if not keep: - del self.cryptTab.mappings[name] - - return self.cryptTab.crypttab() - - def mdadmConf(self): - """ Return the contents of mdadm.conf. """ - arrays = self.devicetree.getDevicesByType("mdarray") - arrays.extend(self.devicetree.getDevicesByType("mdbiosraidarray")) - arrays.extend(self.devicetree.getDevicesByType("mdcontainer")) - # Sort it, this not only looks nicer, but this will also put - # containers (which get md0, md1, etc.) before their members - # (which get md127, md126, etc.). and lame as it is mdadm will not - # assemble the whole stack in one go unless listed in the proper order - # in mdadm.conf - arrays.sort(key=lambda d: d.path) - if not arrays: - return "" - - conf = "# mdadm.conf written out by anaconda\n" - conf += "MAILADDR root\n" - conf += "AUTO +imsm +1.x -all\n" - devices = self.mountpoints.values() + self.swapDevices - for array in arrays: - for device in devices: - if device == array or device.dependsOn(array): - conf += array.mdadmConfEntry - break - - return conf - - def multipathConf(self): - """ Return the contents of multipath.conf. """ - mpaths = self.devicetree.getDevicesByType("dm-multipath") - if not mpaths: - return None - mpaths.sort(key=lambda d: d.name) - config = MultipathConfigWriter() - whitelist = [] - for mpath in mpaths: - config.addMultipathDevice(mpath) - whitelist.append(mpath.name) - whitelist.extend([d.name for d in mpath.parents]) - - # blacklist everything we're not using and let the - # sysadmin sort it out. - for d in self.devicetree.devices: - if not d.name in whitelist: - config.addBlacklistDevice(d) - - return config - - def fstab (self): - format = "%-23s %-23s %-7s %-15s %d %d\n" - fstab = """ -# -# /etc/fstab -# Created by anaconda on %s -# -# Accessible filesystems, by reference, are maintained under '/dev/disk' -# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info -# -""" % time.asctime() - - devices = sorted(self.mountpoints.values(), - key=lambda d: d.format.mountpoint) - devices += self.swapDevices - netdevs = self.devicetree.getDevicesByInstance(NetworkStorageDevice) - for device in devices: - # why the hell do we put swap in the fstab, anyway? - if not device.format.mountable and device.format.type != "swap": - continue - - # Don't write out lines for optical devices, either. - if isinstance(device, OpticalDevice): - continue - - fstype = getattr(device.format, "mountType", device.format.type) - if fstype == "swap": - mountpoint = "swap" - options = device.format.options - else: - mountpoint = device.format.mountpoint - options = device.format.options - if not mountpoint: - log.warning("%s filesystem on %s has no mountpoint" % \ - (fstype, - device.path)) - continue - - options = options or "defaults" - for netdev in netdevs: - if device.dependsOn(netdev): - options = options + ",_netdev" - break - if device.encrypted: - options += ",x-systemd.device-timeout=0" - devspec = device.fstabSpec - dump = device.format.dump - if device.format.check and mountpoint == "/": - passno = 1 - elif device.format.check: - passno = 2 - else: - passno = 0 - fstab = fstab + device.fstabComment - fstab = fstab + format % (devspec, mountpoint, fstype, - options, dump, passno) - - # now, write out any lines we were unable to process because of - # unrecognized filesystems or unresolveable device specifications - for line in self.preserveLines: - fstab += line - - return fstab - - -def getReleaseString(): - relName = None - relVer = None - - try: - relArch = util.capture_output(["arch"], root=ROOT_PATH).strip() - except: - relArch = None - - filename = "%s/etc/redhat-release" % ROOT_PATH - if os.access(filename, os.R_OK): - with open(filename) 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 (relArch, relName, relVer) - -def findExistingInstallations(devicetree): - if not os.path.exists(ROOT_PATH): - util.makedirs(ROOT_PATH) - - 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 as e: - log.warning("setup of %s failed: %s" % (device.name, e)) - continue - - options = device.format.options + ",ro" - try: - device.format.mount(options=options, mountpoint=ROOT_PATH) - except Exception as e: - log.warning("mount of %s as %s failed: %s" % (device.name, - device.format.type, - e)) - device.teardown() - continue - - if not os.access(ROOT_PATH + "/etc/fstab", os.R_OK): - device.teardown(recursive=True) - continue - - try: - (arch, 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 arch: - name = _("Unknown Linux") - else: - name = _("%(product)s Linux %(version)s for %(arch)s") % \ - {"product": product, "version": version, "arch": arch} - - (mounts, swaps) = parseFSTab(devicetree, chroot=ROOT_PATH) - 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 - -class Root(object): - def __init__(self, mounts=None, swaps=None, name=None): - # mountpoint key, StorageDevice value - if not mounts: - self.mounts = {} - else: - self.mounts = mounts - - # StorageDevice - if not swaps: - self.swaps = [] - else: - self.swaps = swaps - - self.name = name # eg: "Fedora Linux 16 for x86_64", "Linux on sda2" - - if not self.name and "/" in self.mounts: - self.name = self.mounts["/"].format.uuid - - @property - def device(self): - return self.mounts.get("/") - -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 = ROOT_PATH - - 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" % blkidTab.devices.keys()) - except Exception as e: - log.info("error parsing blkid.tab: %s" % e) - blkidTab = None - - cryptTab = CryptTab(devicetree, blkidTab=blkidTab, chroot=chroot) - try: - cryptTab.parse(chroot=chroot) - log.debug("crypttab maps: %s" % cryptTab.mappings.keys()) - except Exception as e: - log.info("error parsing crypttab: %s" % e) - cryptTab = None - - with open(path) as f: - log.debug("parsing %s" % path) - for line in f.readlines(): - # strip off comments - (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) - - -class DeviceFactory(object): - type_desc = None - member_format = None # format type for member devices - new_container_attr = None # name of Storage method to create a container - new_device_attr = None # name of Storage method to create a device - container_list_attr = None # name of Storage attribute to list containers - encrypt_members = False - encrypt_leaves = True - - def __init__(self, storage, size, disks, raid_level, encrypted): - self.storage = storage # the Storage instance - self.size = size # the requested size for this device - self.disks = disks # the set of disks to allocate from - self.raid_level = raid_level - self.encrypted = encrypted - - # this is a list of member devices, used to short-circuit the logic in - # setContainerMembers for case of a partition - self.member_list = None - - # choose a size set class for member partition allocation - if raid_level is not None and raid_level.startswith("raid"): - self.set_class = SameSizeSet - else: - self.set_class = TotalSizeSet - - @property - def container_list(self): - """ A list of containers of the type used by this device. """ - if not self.container_list_attr: - return [] - - return getattr(self.storage, self.container_list_attr) - - def new_container(self, *args, **kwargs): - """ Return the newly created container for this device. """ - return getattr(self.storage, self.new_container_attr)(*args, **kwargs) - - def new_device(self, *args, **kwargs): - """ Return the newly created device. """ - return getattr(self.storage, self.new_device_attr)(*args, **kwargs) - - def post_create(self): - """ Perform actions required after device creation. """ - pass - - def container_size_func(self, container, device=None): - """ Return the total space needed for the specified container. """ - size = container.size - size += self.device_size - if device: - size -= device.size - - return size - - @property - def device_size(self): - """ The total disk space required for this device. """ - return self.size - - def set_device_size(self, container, device=None): - return self.size - -class DiskFactory(DeviceFactory): - type_desc = "disk" - # this is to protect against changes to these settings in the base class - encrypt_members = False - encrypt_leaves = True - -class PartitionFactory(DeviceFactory): - type_desc = "partition" - new_device_attr = "newPartition" - default_size = 1 - - def __init__(self, storage, size, disks, raid_level, encrypted): - super(PartitionFactory, self).__init__(storage, size, disks, raid_level, - encrypted) - self.member_list = self.disks - - def new_device(self, *args, **kwargs): - grow = True - max_size = kwargs.pop("size") - kwargs["size"] = 1 - - device = self.storage.newPartition(*args, - grow=grow, maxsize=max_size, - **kwargs) - return device - - def post_create(self): - self.storage.allocatePartitions() - - def set_device_size(self, container, device=None): - size = self.size - if device: - if size != device.size: - log.info("adjusting device size from %.2f to %.2f" - % (device.size, size)) - - base_size = max(PartitionFactory.default_size, - device.format.minSize) - size = max(base_size, size) - device.req_base_size = base_size - device.req_size = base_size - device.req_max_size = size - device.req_grow = size > base_size - - # this probably belongs somewhere else but this is our chance to - # update the disk set - device.req_disks = self.disks[:] - - return size - -class BTRFSFactory(DeviceFactory): - type_desc = "btrfs" - member_format = "btrfs" - new_container_attr = "newBTRFS" - new_device_attr = "newBTRFSSubVolume" - container_list_attr = "btrfsVolumes" - encrypt_members = True - encrypt_leaves = False - - def __init__(self, storage, size, disks, raid_level, encrypted): - super(BTRFSFactory, self).__init__(storage, size, disks, raid_level, - encrypted) - self.raid_level = raid_level or "single" - - def new_container(self, *args, **kwargs): - """ Return the newly created container for this device. """ - kwargs["dataLevel"] = self.raid_level - return getattr(self.storage, self.new_container_attr)(*args, **kwargs) - - def container_size_func(self, container, device=None): - """ Return the total space needed for the specified container. """ - if container.exists: - container_size = container.size - else: - container_size = sum([s.req_size for s in container.subvolumes]) - - if device: - size = self.device_size - else: - size = container_size + self.device_size - - return size - - @property - def device_size(self): - # until we get/need something better - if self.raid_level in ("single", "raid0"): - return self.size - elif self.raid_level in ("raid1", "raid10"): - return self.size * len(self.disks) - - def new_device(self, *args, **kwargs): - kwargs["dataLevel"] = self.raid_level - kwargs["metaDataLevel"] = self.raid_level - return super(BTRFSFactory, self).new_device(*args, **kwargs) - -class LVMFactory(DeviceFactory): - type_desc = "lvm" - member_format = "lvmpv" - new_container_attr = "newVG" - new_device_attr = "newLV" - container_list_attr = "vgs" - encrypt_members = True - encrypt_leaves = False - - @property - def device_size(self): - size_func_kwargs = {} - if self.raid_level in ("raid1", "raid10"): - size_func_kwargs["mirrored"] = True - if self.raid_level in ("raid0", "raid10"): - size_func_kwargs["striped"] = True - return get_pv_space(self.size, len(self.disks), **size_func_kwargs) - - def container_size_func(self, container, device=None): - size = sum([p.size for p in container.parents]) - size -= container.freeSpace - size += self.device_size - if device: - size -= get_pv_space(device.size, len(container.parents)) - - return size - - def set_device_size(self, container, device=None): - size = self.size - free = container.freeSpace - if device: - free += device.size - - if free < size: - log.info("adjusting device size from %.2f to %.2f so it fits " - "in container" % (size, free)) - size = free - - if device: - if size != device.size: - log.info("adjusting device size from %.2f to %.2f" - % (device.size, size)) - - device.size = size - - return size - -class MDFactory(DeviceFactory): - type_desc = "md" - member_format = "mdmember" - new_container_attr = None - new_device_attr = "newMDArray" - - @property - def container_list(self): - return [] - - @property - def device_size(self): - return get_member_space(self.size, len(self.disks), - level=self.raid_level) - - def container_size_func(self, container, device=None): - return get_member_space(self.size, len(container.parents), - level=self.raid_level) - - def new_device(self, *args, **kwargs): - kwargs["level"] = self.raid_level - kwargs["totalDevices"] = len(kwargs.get("parents")) - kwargs["memberDevices"] = len(kwargs.get("parents")) - return super(MDFactory, self).new_device(*args, **kwargs) diff --git a/pyanaconda/storage/arch.py b/pyanaconda/storage/arch.py deleted file mode 100644 index 7fc39e7f7..000000000 --- a/pyanaconda/storage/arch.py +++ /dev/null @@ -1,315 +0,0 @@ -# -# arch.py -# -# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2013 -# 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/>. -# -# Author(s): Jeremy Katz <katzj@redhat.com> -# Paul Nasrat <pnasrat@redhat.com> -# Peter Jones <pjones@redhat.com> -# Chris Lumens <clumens@redhat.com> -# Will Woods <wwoods@redhat.com> -# Dennis Gilmore <dgilmore@ausil.us> -# David Marlin <dmarlin@redhat.com> -# - -import os - -from .flags import flags - -## Get the SPARC machine variety type. -# @return The SPARC machine type, or 0 if not SPARC. -def getSparcMachine(): - if not isSparc(): - return 0 - - machine = None - - - f = open('/proc/cpuinfo', 'r') - lines = f.readlines() - f.close() - for line in lines: - if line.find('type') != -1: - machine = line.split(':')[1].strip() - return machine - - return None - -## Get the PPC machine variety type. -# @return The PPC machine type, or 0 if not PPC. -def getPPCMachine(): - if not isPPC(): - return 0 - - ppcMachine = None - machine = None - platform = None - - # ppc machine hash - ppcType = { 'Mac' : 'PMac', - 'Book' : 'PMac', - 'CHRP IBM' : 'pSeries', - 'Pegasos' : 'Pegasos', - 'Efika' : 'Efika', - 'iSeries' : 'iSeries', - 'pSeries' : 'pSeries', - 'PReP' : 'PReP', - 'CHRP' : 'pSeries', - 'Amiga' : 'APUS', - 'Gemini' : 'Gemini', - 'Shiner' : 'ANS', - 'BRIQ' : 'BRIQ', - 'Teron' : 'Teron', - 'AmigaOne' : 'Teron', - 'Maple' : 'pSeries', - 'Cell' : 'pSeries', - 'Momentum' : 'pSeries', - 'PS3' : 'PS3', - 'PowerNV' : 'pSeries' - } - - f = open('/proc/cpuinfo', 'r') - lines = f.readlines() - f.close() - for line in lines: - if line.find('machine') != -1: - machine = line.split(':')[1] - elif line.find('platform') != -1: - platform = line.split(':')[1] - - for part in (machine, platform): - if ppcMachine is None and part is not None: - for type in ppcType.items(): - if part.find(type[0]) != -1: - ppcMachine = type[1] - - if ppcMachine is None: - log.warning("Unable to find PowerPC machine type") - elif ppcMachine == 0: - log.warning("Unknown PowerPC machine type: %s" %(ppcMachine,)) - - return ppcMachine - -## Get the powermac machine ID. -# @return The powermac machine id, or 0 if not PPC. -def getPPCMacID(): - machine = None - - if not isPPC(): - return 0 - if getPPCMachine() != "PMac": - return 0 - - f = open('/proc/cpuinfo', 'r') - lines = f.readlines() - f.close() - for line in lines: - if line.find('machine') != -1: - machine = line.split(':')[1] - machine = machine.strip() - return machine - - log.warning("No Power Mac machine id") - return 0 - -## Get the powermac generation. -# @return The powermac generation, or 0 if not powermac. -def getPPCMacGen(): - # XXX: should NuBus be here? - pmacGen = ['OldWorld', 'NewWorld', 'NuBus'] - - if not isPPC(): - return 0 - if getPPCMachine() != "PMac": - return 0 - - f = open('/proc/cpuinfo', 'r') - lines = f.readlines() - f.close() - gen = None - for line in lines: - if line.find('pmac-generation') != -1: - gen = line.split(':')[1] - break - - if gen is None: - log.warning("Unable to find pmac-generation") - - for _type in pmacGen: - if _type in gen: - return type - - log.warning("Unknown Power Mac generation: %s" %(gen,)) - return 0 - -## Determine if the hardware is an iBook or PowerBook -# @return 1 if so, 0 otherwise. -def getPPCMacBook(): - if not isPPC(): - return 0 - if getPPCMachine() != "PMac": - return 0 - - f = open('/proc/cpuinfo', 'r') - buf = f.read() - f.close() - return 'book' in buf.lower() - -## Get the ARM processor variety. -# @return The ARM processor variety type, or 0 if not ARM. -def getARMMachine(): - if not isARM(): - return 0 - - if flags.arm_platform: - return flags.arm_platform - - armMachine = os.uname()[2].rpartition('.' )[2] - - if armMachine.startswith('arm'): - return None - else: - return armMachine - - -cell = None -## Determine if the hardware is the Cell platform. -# @return True if so, False otherwise. -def isCell(): - global cell - if cell is not None: - return cell - - cell = False - if not isPPC(): - return cell - - f = open('/proc/cpuinfo', 'r') - lines = f.readlines() - f.close() - - for line in lines: - if 'Cell' in line: - cell = True - - return cell - -mactel = None -## Determine if the hardware is an Intel-based Apple Mac. -# @return True if so, False otherwise. -def isMactel(): - global mactel - if mactel is not None: - return mactel - - if not isX86(): - mactel = False - elif not os.path.isfile(DMI_CHASSIS_VENDOR): - mactel = False - else: - buf = open(DMI_CHASSIS_VENDOR).read() - mactel = ("apple" in buf.lower()) - return mactel - -efi = None -## Determine if the hardware supports EFI. -# @return True if so, False otherwise. -def isEfi(): - global efi - if efi is not None: - return efi - - efi = False - # XXX need to make sure efivars is loaded... - if os.path.exists("/sys/firmware/efi"): - efi = True - - return efi - -# Architecture checking functions - -def isX86(bits=None): - arch = os.uname()[4] - - # x86 platforms include: - # i*86 - # athlon* - # x86_64 - # amd* - # ia32e - if bits is None: - if (arch.startswith('i') and arch.endswith('86')) or \ - arch.startswith('athlon') or arch.startswith('amd') or \ - arch == 'x86_64' or arch == 'ia32e': - return True - elif bits == 32: - if arch.startswith('i') and arch.endswith('86'): - return True - elif bits == 64: - if arch.startswith('athlon') or arch.startswith('amd') or \ - arch == 'x86_64' or arch == 'ia32e': - return True - - return False - -def isPPC(bits=None): - arch = os.uname()[4] - - if bits is None: - if arch == 'ppc' or arch == 'ppc64': - return True - elif bits == 32: - if arch == 'ppc': - return True - elif bits == 64: - if arch == 'ppc64': - return True - - return False - -def isS390(): - return os.uname()[4].startswith('s390') - -def isIA64(): - return os.uname()[4] == 'ia64' - -def isAlpha(): - return os.uname()[4].startswith('alpha') - -def isSparc(): - return os.uname()[4].startswith('sparc') - -def isARM(): - return os.uname()[4].startswith('arm') - -def getArch(): - if isX86(bits=32): - return 'i386' - elif isX86(bits=64): - return 'x86_64' - elif isPPC(bits=32): - return 'ppc' - elif isPPC(bits=64): - return 'ppc64' - elif isAlpha(): - return 'alpha' - elif isSparc(): - return 'sparc' - elif isARM(): - return 'arm' - else: - return os.uname()[4] diff --git a/pyanaconda/storage/dasd.py b/pyanaconda/storage/dasd.py deleted file mode 100644 index bd3d4debc..000000000 --- a/pyanaconda/storage/dasd.py +++ /dev/null @@ -1,218 +0,0 @@ -# -# dasd.py - DASD class -# -# Copyright (C) 2009, 2010 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/>. -# -# Red Hat Author(s): David Cantrell <dcantrell@redhat.com> -# - -import sys -import os -from pyanaconda.storage.errors import DasdFormatError -from pyanaconda.storage.devices import deviceNameToDiskByPath -from pyanaconda.storage import util -from pyanaconda.storage import arch -from . import ROOT_PATH -from .udev import udev_trigger - -import logging -log = logging.getLogger("storage") - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) -P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z) - -def getDasdPorts(): - """ Return comma delimited string of valid DASD ports. """ - ports = [] - - f = open("/proc/dasd/devices", "r") - lines = map(lambda x: x.strip(), f.readlines()) - f.close() - - for line in lines: - if "unknown" in line: - continue - - if "(FBA )" in line or "(ECKD)" in line: - ports.append(line.split('(')[0]) - - return ','.join(ports) - -class DASD: - """ Controlling class for DASD interaction before the storage code in - anaconda has initialized. - - The DASD class can determine if any DASD devices on the system are - unformatted and can perform a dasdfmt on them. - """ - - def __init__(self): - self._dasdlist = [] - self._devices = [] # list of DASDDevice objects - self.totalCylinders = 0 - self._completedCylinders = 0.0 - self._maxFormatJobs = 0 - self.dasdfmt = "/sbin/dasdfmt" - self.commonArgv = ["-y", "-d", "cdl", "-b", "4096"] - self.started = False - - def __call__(self): - return self - - def startup(self, intf, exclusiveDisks, zeroMbr): - """ Look for any unformatted DASDs in the system and offer the user - the option for format them with dasdfmt or exit the installer. - """ - if self.started: - return - - self.started = True - - if not arch.isS390(): - return - - # Trigger udev data about the dasd devices on the system - udev_trigger(action="change", name="dasd*") - - log.info("Checking for unformatted DASD devices:") - - for device in os.listdir("/sys/block"): - if not device.startswith("dasd"): - continue - - statusfile = "/sys/block/%s/device/status" % (device,) - if not os.path.isfile(statusfile): - continue - - f = open(statusfile, "r") - status = f.read().strip() - f.close() - - if status in ["unformatted"] and device not in exclusiveDisks: - bypath = deviceNameToDiskByPath(device) - if not bypath: - bypath = "/dev/" + device - - log.info(" %s (%s) status is %s, needs dasdfmt" % (device, - bypath, - status,)) - self._dasdlist.append((device, bypath)) - - if not len(self._dasdlist): - log.info(" no unformatted DASD devices found") - return - - askUser = True - - if zeroMbr: - askUser = False - elif not intf and not zeroMbr: - log.info(" non-interactive kickstart install without zerombr " - "command, unable to run dasdfmt, exiting installer") - sys.exit(0) - - c = len(self._dasdlist) - - if intf and askUser: - devs = '' - for dasd, bypath in self._dasdlist: - devs += "%s\n" % (bypath,) - - rc = intf.questionInitializeDASD(c, devs) - if rc == 1: - log.info(" not running dasdfmt, continuing installation") - return - - # gather total cylinder count - argv = ["-t", "-v"] + self.commonArgv - for dasd, bypath in self._dasdlist: - buf = util.capture_output([self.dasdfmt, argv, "/dev/" + dasd]) - for line in buf.splitlines(): - if line.startswith("Drive Geometry: "): - # line will look like this: - # Drive Geometry: 3339 Cylinders * 15 Heads = 50085 Tracks - cyls = long(filter(lambda s: s, line.split(' '))[2]) - self.totalCylinders += cyls - break - - # format DASDs - argv = ["-P"] + self.commonArgv - update = self._updateProgressWindow - - title = P_("Formatting DASD Device", "Formatting DASD Devices", c) - msg = P_("Preparing %d DASD device for use with Linux..." % c, - "Preparing %d DASD devices for use with Linux..." % c, c) - - if intf: - if self.totalCylinders: - pw = intf.progressWindow(title, msg, 1.0) - else: - pw = intf.progressWindow(title, msg, 100, pulse=True) - - for dasd, bypath in self._dasdlist: - log.info("Running dasdfmt on %s" % (bypath,)) - arglist = argv + ["/dev/" + dasd] - - try: - rc = util.run_program([self.dasdfmt] + arglist) - except Exception as e: - raise DasdFormatError(e, bypath) - - if rc: - raise DasdFormatError("dasdfmt failed: %s" % rc, bypath) - - if intf: - pw.pop() - - def addDASD(self, dasd): - """ Adds a DASDDevice to the internal list of DASDs. """ - if dasd: - self._devices.append(dasd) - - def clear_device_list(self): - """ Clear the device list to force re-populate on next access. """ - self._devices = [] - - def write(self): - """ Write /etc/dasd.conf to target system for all DASD devices - configured during installation. - """ - if self._devices == []: - return - - f = open(os.path.realpath(ROOT_PATH + "/etc/dasd.conf"), "w") - for dasd in self._devices: - fields = [dasd.busid] + dasd.getOpts() - f.write("%s\n" % (" ".join(fields),)) - f.close() - - def _updateProgressWindow(self, data, callback_data=None): - """ Reads progress output from dasdfmt and collects the number of - cylinders completed so the progress window can update. - """ - if not callback_data: - return - - if data == '\n': - # each newline we see in this output means one more cylinder done - self._completedCylinders += 1.0 - callback_data.set(self._completedCylinders / self.totalCylinders) - -# Create DASD singleton -DASD = DASD() - -# vim:tw=78:ts=4:et:sw=4 diff --git a/pyanaconda/storage/deviceaction.py b/pyanaconda/storage/deviceaction.py deleted file mode 100644 index c8f96f204..000000000 --- a/pyanaconda/storage/deviceaction.py +++ /dev/null @@ -1,587 +0,0 @@ -# deviceaction.py -# Device modification action classes for anaconda's storage configuration -# module. -# -# Copyright (C) 2009 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> -# - -from udev import * -import math - -from devices import StorageDevice -from devices import PartitionDevice -from devices import LVMLogicalVolumeDevice -from formats import getFormat -from errors import * -from parted import partitionFlag, PARTITION_LBA - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -from contextlib import contextmanager - -@contextmanager -def progress_report_stub(message): - yield - -try: - from pyanaconda.progress import progress_report -except ImportError: - progress_report = progress_report_stub - -# The values are just hints as to the ordering. -# Eg: fsmod and devmod ordering depends on the mod (shrink -v- grow) -ACTION_TYPE_NONE = 0 -ACTION_TYPE_DESTROY = 1000 -ACTION_TYPE_RESIZE = 500 -ACTION_TYPE_CREATE = 100 - -action_strings = {ACTION_TYPE_NONE: "None", - ACTION_TYPE_DESTROY: "Destroy", - ACTION_TYPE_RESIZE: "Resize", - ACTION_TYPE_CREATE: "Create"} - -ACTION_OBJECT_NONE = 0 -ACTION_OBJECT_FORMAT = 1 -ACTION_OBJECT_DEVICE = 2 - -object_strings = {ACTION_OBJECT_NONE: "None", - ACTION_OBJECT_FORMAT: "Format", - ACTION_OBJECT_DEVICE: "Device"} - -RESIZE_SHRINK = 88 -RESIZE_GROW = 89 - -resize_strings = {RESIZE_SHRINK: "Shrink", - RESIZE_GROW: "Grow"} - -def action_type_from_string(type_string): - if type_string is None: - return None - - for (k,v) in action_strings.items(): - if v.lower() == type_string.lower(): - return k - - return resize_type_from_string(type_string) - -def action_object_from_string(type_string): - if type_string is None: - return None - - for (k,v) in object_strings.items(): - if v.lower() == type_string.lower(): - return k - -def resize_type_from_string(type_string): - if type_string is None: - return None - - for (k,v) in resize_strings.items(): - if v.lower() == type_string.lower(): - return k - -class DeviceAction(object): - """ An action that will be carried out in the future on a Device. - - These classes represent actions to be performed on devices or - filesystems. - - The operand Device instance will be modified according to the - action, but no changes will be made to the underlying device or - filesystem until the DeviceAction instance's execute method is - called. The DeviceAction instance's cancel method should reverse - any modifications made to the Device instance's attributes. - - If the Device instance represents a pre-existing device, the - constructor should call any methods or set any attributes that the - action will eventually change. Device/DeviceFormat classes should verify - that the requested modifications are reasonable and raise an - exception if not. - - Only one action of any given type/object pair can exist for any - given device at any given time. This is enforced by the - DeviceTree. - - Basic usage: - - a = DeviceAction(dev) - a.execute() - - OR - - a = DeviceAction(dev) - a.cancel() - - - XXX should we back up the device with a deep copy for forcibly - cancelling actions? - - The downside is that we lose any checking or verification that - would get done when resetting the Device instance's attributes to - their original values. - - The upside is that we would be guaranteed to achieve a total - reversal. No chance of, eg: resizes ending up altering Device - size due to rounding or other miscalculation. -""" - type = ACTION_TYPE_NONE - obj = ACTION_OBJECT_NONE - _id = 0 - - def __init__(self, device): - if not isinstance(device, StorageDevice): - raise ValueError("arg 1 must be a StorageDevice instance") - self.device = device - - # Establish a unique id for each action instance. Making shallow or - # deep copyies of DeviceAction instances will require __copy__ and - # __deepcopy__ methods to handle incrementing the id in the copy - self.id = DeviceAction._id - DeviceAction._id += 1 - - def execute(self): - """ perform the action """ - pass - - def cancel(self): - """ cancel the action """ - pass - - @property - def isDestroy(self): - return self.type == ACTION_TYPE_DESTROY - - @property - def isCreate(self): - return self.type == ACTION_TYPE_CREATE - - @property - def isResize(self): - return self.type == ACTION_TYPE_RESIZE - - @property - def isShrink(self): - return (self.type == ACTION_TYPE_RESIZE and self.dir == RESIZE_SHRINK) - - @property - def isGrow(self): - return (self.type == ACTION_TYPE_RESIZE and self.dir == RESIZE_GROW) - - @property - def isDevice(self): - return self.obj == ACTION_OBJECT_DEVICE - - @property - def isFormat(self): - return self.obj == ACTION_OBJECT_FORMAT - - @property - def format(self): - return self.device.format - - def __str__(self): - s = "[%d] %s %s" % (self.id, action_strings[self.type], - object_strings[self.obj]) - if self.isResize: - s += " (%s)" % resize_strings[self.dir] - if self.isFormat: - s += " %s on" % self.format.desc - s += " %s %s (id %d)" % (self.device.type, self.device.name, - self.device.id) - return s - - def requires(self, action): - """ Return True if self requires action. """ - return False - - def obsoletes(self, action): - """ Return True is self obsoletes action. - - DeviceAction instances obsolete other DeviceAction instances with - lower id and same device. - """ - return (self.device.id == action.device.id and - self.type == action.type and - self.obj == action.obj and - self.id > action.id) - - -class ActionCreateDevice(DeviceAction): - """ Action representing the creation of a new device. """ - type = ACTION_TYPE_CREATE - obj = ACTION_OBJECT_DEVICE - - def __init__(self, device): - if device.exists: - raise ValueError("device already exists") - - # FIXME: assert device.fs is None - DeviceAction.__init__(self, device) - - def execute(self): - self.device.create() - - def requires(self, action): - """ Return True if self requires action. - - Device create actions require other actions when either of the - following is true: - - - this action's device depends on the other action's device - - both actions are partition create actions on the same disk - and this partition has a higher number - """ - rc = False - if self.device.dependsOn(action.device): - rc = True - elif (action.isCreate and action.isDevice and - isinstance(self.device, PartitionDevice) and - isinstance(action.device, PartitionDevice) and - self.device.disk == action.device.disk): - # create partitions in ascending numerical order - selfNum = self.device.partedPartition.number - otherNum = action.device.partedPartition.number - if selfNum > otherNum: - rc = True - elif (action.isCreate and action.isDevice and - isinstance(self.device, LVMLogicalVolumeDevice) and - isinstance(action.device, LVMLogicalVolumeDevice) and - self.device.vg == action.device.vg and - action.device.singlePV and not self.device.singlePV): - rc = True - return rc - - -class ActionDestroyDevice(DeviceAction): - """ An action representing the deletion of an existing device. """ - type = ACTION_TYPE_DESTROY - obj = ACTION_OBJECT_DEVICE - - def __init__(self, device): - # XXX should we insist that device.fs be None? - DeviceAction.__init__(self, device) - if device.exists: - device.teardown() - - def execute(self): - self.device.destroy() - - # Make sure libparted does not keep cached info for this device - # and returns it when we create a new device with the same name - if self.device.partedDevice: - try: - self.device.partedDevice.removeFromCache() - except Exception: - pass - - def requires(self, action): - """ Return True if self requires action. - - Device destroy actions require other actions when either of the - following is true: - - - the other action's device depends on this action's device - - both actions are partition create actions on the same disk - and this partition has a lower number - """ - rc = False - if action.device.dependsOn(self.device) and action.isDestroy: - rc = True - elif (action.isDestroy and action.isDevice and - isinstance(self.device, PartitionDevice) and - isinstance(action.device, PartitionDevice) and - self.device.disk == action.device.disk): - # remove partitions in descending numerical order - selfNum = self.device.partedPartition.number - otherNum = action.device.partedPartition.number - if selfNum < otherNum: - rc = True - elif (action.isDestroy and action.isFormat and - action.device.id == self.device.id): - # device destruction comes after destruction of device's format - rc = True - return rc - - def obsoletes(self, action): - """ Return True if self obsoletes action. - - - obsoletes all actions w/ lower id that act on the same device, - including self, if device does not exist - - - obsoletes all but ActionDestroyFormat actions w/ lower id on the - same device if device exists - """ - rc = False - if action.device.id == self.device.id: - if self.id >= action.id and not self.device.exists: - rc = True - elif self.id > action.id and \ - self.device.exists and \ - not (action.isDestroy and action.isFormat): - rc = True - - return rc - - -class ActionResizeDevice(DeviceAction): - """ An action representing the resizing of an existing device. """ - type = ACTION_TYPE_RESIZE - obj = ACTION_OBJECT_DEVICE - - def __init__(self, device, newsize): - if not device.resizable: - raise ValueError("device is not resizable") - - if long(math.floor(device.currentSize)) == newsize: - raise ValueError("new size same as old size") - - DeviceAction.__init__(self, device) - if newsize > long(math.floor(device.currentSize)): - self.dir = RESIZE_GROW - else: - self.dir = RESIZE_SHRINK - if device.targetSize > 0: - self.origsize = device.targetSize - else: - self.origsize = device.size - - self.device.targetSize = newsize - - def execute(self): - self.device.resize() - - def cancel(self): - self.device.targetSize = self.origsize - - def requires(self, action): - """ Return True if self requires action. - - A device resize action requires another action if: - - - the other action is a format resize on the same device and - both are shrink operations - - the other action grows a device (or format it contains) that - this action's device depends on - - the other action shrinks a device (or format it contains) - that depends on this action's device - """ - retval = False - if action.isResize: - if self.device.id == action.device.id and \ - self.dir == action.dir and \ - action.isFormat and self.isShrink: - retval = True - elif action.isGrow and self.device.dependsOn(action.device): - retval = True - elif action.isShrink and action.device.dependsOn(self.device): - retval = True - - return retval - - -class ActionCreateFormat(DeviceAction): - """ An action representing creation of a new filesystem. """ - type = ACTION_TYPE_CREATE - obj = ACTION_OBJECT_FORMAT - - def __init__(self, device, format=None): - DeviceAction.__init__(self, device) - if format: - self.origFormat = device.format - if self.device.format.exists: - self.device.format.teardown() - self.device.format = format - else: - self.origFormat = getFormat(None) - - def execute(self): - msg = _("Creating %(type)s on %(device)s") % {"type": self.device.format.type, "device": self.device.path} - with progress_report(msg): - self.device.setup() - - if isinstance(self.device, PartitionDevice): - for flag in partitionFlag.keys(): - # Keep the LBA flag on pre-existing partitions - if flag in [ PARTITION_LBA, self.format.partedFlag ]: - continue - self.device.unsetFlag(flag) - - if self.format.partedFlag is not None: - self.device.setFlag(self.format.partedFlag) - - if self.format.partedSystem is not None: - self.device.partedPartition.system = self.format.partedSystem - - self.device.disk.format.commitToDisk() - - self.device.format.create(device=self.device.path, - options=self.device.formatArgs) - # Get the UUID now that the format is created - udev_settle() - self.device.updateSysfsPath() - info = udev_get_block_device(self.device.sysfsPath) - self.device.format.uuid = udev_device_get_uuid(info) - - def cancel(self): - self.device.format = self.origFormat - - def requires(self, action): - """ Return True if self requires action. - - Format create action can require another action if: - - - this action's device depends on the other action's device - and the other action is not a device destroy action - - the other action is a create or resize of this action's - device - """ - return ((self.device.dependsOn(action.device) and - not (action.isDestroy and action.isDevice)) or - (action.isDevice and (action.isCreate or action.isResize) and - self.device.id == action.device.id)) - - def obsoletes(self, action): - """ Return True if this action obsoletes action. - - Format create actions obsolete the following actions: - - - format actions w/ lower id on this action's device, other - than those that destroy existing formats - """ - return (self.device.id == action.device.id and - self.obj == action.obj and - not (action.isDestroy and action.format.exists) and - self.id > action.id) - - -class ActionDestroyFormat(DeviceAction): - """ An action representing the removal of an existing filesystem. """ - type = ACTION_TYPE_DESTROY - obj = ACTION_OBJECT_FORMAT - - def __init__(self, device): - DeviceAction.__init__(self, device) - self.origFormat = self.device.format - if device.format.exists: - device.format.teardown() - self.device.format = None - - def execute(self): - """ wipe the filesystem signature from the device """ - self.device.setup(orig=True) - self.format.destroy() - udev_settle() - self.device.teardown() - - def cancel(self): - self.device.format = self.origFormat - - @property - def format(self): - return self.origFormat - - def requires(self, action): - """ Return True if self requires action. - - Format destroy actions require other actions when: - - - the other action's device depends on this action's device - and the other action is a destroy action - """ - return action.device.dependsOn(self.device) and action.isDestroy - - def obsoletes(self, action): - """ Return True if this action obsoletes action. - - Format destroy actions obsolete the following actions: - - - format actions w/ lower id on same device, including self if - format does not exist - - - format destroy action on a non-existent format shouldn't - obsolete a format destroy action on an existing one - """ - return (self.device.id == action.device.id and - self.obj == action.obj and - (self.id > action.id or - (self.id == action.id and not self.format.exists)) and - not (action.format.exists and not self.format.exists)) - - -class ActionResizeFormat(DeviceAction): - """ An action representing the resizing of an existing filesystem. - - XXX Do we even want to support resizing of a filesystem without - also resizing the device it resides on? - """ - type = ACTION_TYPE_RESIZE - obj = ACTION_OBJECT_FORMAT - - def __init__(self, device, newsize): - if not device.format.resizable: - raise ValueError("format is not resizable") - - if long(math.floor(device.format.currentSize)) == newsize: - raise ValueError("new size same as old size") - - DeviceAction.__init__(self, device) - if newsize > long(math.floor(device.format.currentSize)): - self.dir = RESIZE_GROW - else: - self.dir = RESIZE_SHRINK - self.origSize = self.device.format.targetSize - self.device.format.targetSize = newsize - - def execute(self): - msg = _("Resizing filesystem on %(device)s") % {"device": self.device.path} - with progress_report(msg): - self.device.setup(orig=True) - self.device.format.doResize() - - def cancel(self): - self.device.format.targetSize = self.origSize - - def requires(self, action): - """ Return True if self requires action. - - A format resize action requires another action if: - - - the other action is a device resize on the same device and - both are grow operations - - the other action shrinks a device (or format it contains) - that depends on this action's device - - the other action grows a device (or format) that this - action's device depends on - """ - retval = False - if action.isResize: - if self.device.id == action.device.id and \ - self.dir == action.dir and \ - action.isDevice and self.isGrow: - retval = True - elif action.isShrink and action.device.dependsOn(self.device): - retval = True - elif action.isGrow and self.device.dependsOn(action.device): - retval = True - - return retval diff --git a/pyanaconda/storage/devicelibs/Makefile.am b/pyanaconda/storage/devicelibs/Makefile.am deleted file mode 100644 index 86a7d5ebc..000000000 --- a/pyanaconda/storage/devicelibs/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -# storage/devicelibs/Makefile.am for anaconda -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# Author: David Cantrell <dcantrell@redhat.com> - -pkgpyexecdir = $(pyexecdir)/py$(PACKAGE_NAME) -storagedevicelibsdir = $(pkgpyexecdir)/storage/devicelibs -storagedevicelibs_PYTHON = *.py - -MAINTAINERCLEANFILES = Makefile.in diff --git a/pyanaconda/storage/devicelibs/__init__.py b/pyanaconda/storage/devicelibs/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/pyanaconda/storage/devicelibs/__init__.py +++ /dev/null diff --git a/pyanaconda/storage/devicelibs/btrfs.py b/pyanaconda/storage/devicelibs/btrfs.py deleted file mode 100644 index a6b69e332..000000000 --- a/pyanaconda/storage/devicelibs/btrfs.py +++ /dev/null @@ -1,112 +0,0 @@ -# -# btrfs.py -# btrfs functions -# -# Copyright (C) 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/>. -# -# Author(s): David Lehman <dlehman@redhat.com> -# - -import os -import re - -from .. import util -from ..errors import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -def btrfs(args, capture=False): - if capture: - exec_func = util.capture_output - else: - exec_func = util.run_program - - argv = ["btrfs"] + args - - ret = exec_func(argv) - if ret and not capture: - raise BTRFSError(ret) - return ret - -def create_volume(devices, label=None, data=None, metadata=None): - """ For now, data and metadata must be strings mkfs.btrfs understands. """ - if not devices: - raise ValueError("no devices specified") - elif any([not os.path.exists(d) for d in devices]): - raise ValueError("one or more specified devices not present") - - args = [] - if data: - args.append("--data=%s" % data) - - if metadata: - args.append("--metadata=%s" % metadata) - - if label: - args.append("--label=%s" % label) - - args.extend(devices) - - ret = util.run_program(["mkfs.btrfs"] + args) - if ret: - raise BTRFSError(ret) - - return ret - -# destroy is handled using wipefs - -# add device - -# remove device - -def create_subvolume(mountpoint, name): - if not os.path.ismount(mountpoint): - raise ValueError("volume not mounted") - - path = os.path.normpath("%s/%s" % (mountpoint, name)) - args = ["subvol", "create", path] - return btrfs(args) - -def delete_subvolume(mountpoint, name): - if not os.path.ismount(mountpoint): - raise ValueError("volume not mounted") - - path = os.path.normpath("%s/%s" % (mountpoint, name)) - args = ["subvol", "delete", path] - return btrfs(args) - -def create_snapshot(source, dest): - pass - -def scan_device(path): - return btrfs(["device", "scan", path]) - -# get a list of subvolumes from a mounted btrfs filesystem -def list_subvolumes(mountpoint): - if not os.path.ismount(mountpoint): - raise ValueError("volume not mounted") - - args = ["subvol", "list", mountpoint] - buf = btrfs(args, capture=True) - vols = [] - for group in re.findall(r'ID (\d+) gen \d+ top level \d+ path (.+)\n', buf): - vols.append({"id": int(group[0]), "path": group[1]}) - - return vols diff --git a/pyanaconda/storage/devicelibs/crypto.py b/pyanaconda/storage/devicelibs/crypto.py deleted file mode 100644 index 2a41cdd2c..000000000 --- a/pyanaconda/storage/devicelibs/crypto.py +++ /dev/null @@ -1,153 +0,0 @@ -# -# crypto.py -# -# Copyright (C) 2009 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/>. -# -# Author(s): Dave Lehman <dlehman@redhat.com> -# Martin Sivak <msivak@redhat.com> -# - -import os -from pycryptsetup import CryptSetup - -from ..errors import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -LUKS_METADATA_SIZE = 2.0 # MB - -# Keep the character set size a power of two to make sure all characters are -# equally likely -GENERATED_PASSPHRASE_CHARSET = ("0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "./") -# 20 chars * 6 bits per char = 120 "bits of security" -GENERATED_PASSPHRASE_LENGTH = 20 - -def generateBackupPassphrase(): - rnd = os.urandom(GENERATED_PASSPHRASE_LENGTH) - cs = GENERATED_PASSPHRASE_CHARSET - raw = "".join([cs[ord(c) % len(cs)] for c in rnd]) - - # Make the result easier to read - parts = [] - for i in xrange(0, len(raw), 5): - parts.append(raw[i : i + 5]) - return "-".join(parts) - -def askyes(question): - return True - -def dolog(priority, text): - pass - -def askpassphrase(text): - return None - -def is_luks(device): - cs = CryptSetup(device=device, yesDialog = askyes, logFunc = dolog, passwordDialog = askpassphrase) - return cs.isLuks() - -def luks_uuid(device): - cs = CryptSetup(device=device, yesDialog = askyes, logFunc = dolog, passwordDialog = askpassphrase) - return cs.luksUUID() - -def luks_status(name): - """True means active, False means inactive (or non-existent)""" - cs = CryptSetup(name=name, yesDialog = askyes, logFunc = dolog, passwordDialog = askpassphrase) - return cs.status() - -def luks_format(device, - passphrase=None, - cipher=None, key_size=None, key_file=None): - if not passphrase: - raise ValueError("luks_format requires passphrase") - - cs = CryptSetup(device=device, yesDialog = askyes, logFunc = dolog, passwordDialog = askpassphrase) - - #None is not considered as default value and pycryptsetup doesn't accept it - #so we need to filter out all Nones - kwargs = {} - - # Split cipher designator to cipher name and cipher mode - cipherType = None - cipherMode = None - if cipher: - cparts = cipher.split("-") - cipherType = "".join(cparts[0:1]) - cipherMode = "-".join(cparts[1:]) - - if cipherType: kwargs["cipher"] = cipherType - if cipherMode: kwargs["cipherMode"] = cipherMode - if key_size: kwargs["keysize"] = key_size - - rc = cs.luksFormat(**kwargs) - if rc: - raise CryptoError("luks_format failed for '%s'" % device) - - # activate first keyslot - cs.addKeyByVolumeKey(newPassphrase = passphrase) - if rc: - raise CryptoError("luks_add_key_by_volume_key failed for '%s'" % device) - - -def luks_open(device, name, passphrase=None, key_file=None): - if not passphrase: - raise ValueError("luks_format requires passphrase") - - cs = CryptSetup(device=device, yesDialog = askyes, logFunc = dolog, passwordDialog = askpassphrase) - - rc = cs.activate(passphrase = passphrase, name = name) - if rc<0: - raise CryptoError("luks_open failed for %s (%s) with errno %d" % (device, name, rc)) - -def luks_close(name): - cs = CryptSetup(name=name, yesDialog = askyes, logFunc = dolog, passwordDialog = askpassphrase) - rc = cs.deactivate() - - if rc: - raise CryptoError("luks_close failed for %s" % name) - -def luks_add_key(device, - new_passphrase=None, - passphrase=None, key_file=None): - - if not passphrase: - raise ValueError("luks_add_key requires passphrase") - - cs = CryptSetup(device=device, yesDialog = askyes, logFunc = dolog, passwordDialog = askpassphrase) - rc = cs.addKeyByPassphrase(passphrase = passphrase, newPassphrase = new_passphrase) - - if rc<0: - raise CryptoError("luks add key failed with errcode %d" % (rc,)) - -def luks_remove_key(device, - del_passphrase=None, - passphrase=None, key_file=None): - - if not passphrase: - raise ValueError("luks_remove_key requires passphrase") - - cs = CryptSetup(device=device, yesDialog = askyes, logFunc = dolog, passwordDialog = askpassphrase) - rc = cs.removePassphrase(passphrase = passphrase) - - if rc: - raise CryptoError("luks remove key failed with errcode %d" % (rc,)) - - - diff --git a/pyanaconda/storage/devicelibs/dm.py b/pyanaconda/storage/devicelibs/dm.py deleted file mode 100644 index e7adfec05..000000000 --- a/pyanaconda/storage/devicelibs/dm.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# dm.py -# device-mapper functions -# -# Copyright (C) 2009 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/>. -# -# Author(s): Dave Lehman <dlehman@redhat.com> -# - -import os - -import block -from .. import util -from ..errors import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -def dm_setup(args): - ret = util.run_program(["dmsetup"] + args) - if ret: - raise DMError("Failed to run dmsetup %s" % " ".join(args)) - -def dm_create_linear(map_name, device, length, uuid): - table = "0 %d linear %s 0" % (length, device) - args = ["create", map_name, "--uuid", uuid, "--table", "%s" % table] - dm_setup(args) - -def dm_remove(map_name): - args = ["remove", map_name] - dm_setup(args) - -def name_from_dm_node(dm_node): - # first, try sysfs - name_file = "/sys/class/block/%s/dm/name" % dm_node - try: - name = open(name_file).read().strip() - except IOError: - # next, try pyblock - name = block.getNameFromDmNode(dm_node) - - return name - -def dm_node_from_name(map_name): - named_path = "/dev/mapper/%s" % map_name - try: - # /dev/mapper/ nodes are usually symlinks to /dev/dm-N - node = os.path.basename(os.readlink(named_path)) - except OSError: - try: - # dm devices' names are based on the block device minor - st = os.stat(named_path) - minor = os.minor(st.st_rdev) - node = "dm-%d" % minor - except OSError: - # try pyblock - node = block.getDmNodeFromName(map_name) - - if not node: - raise DMError("dm_node_from_name(%s) has failed." % node) - - return node diff --git a/pyanaconda/storage/devicelibs/edd.py b/pyanaconda/storage/devicelibs/edd.py deleted file mode 100644 index 30e8004d3..000000000 --- a/pyanaconda/storage/devicelibs/edd.py +++ /dev/null @@ -1,231 +0,0 @@ -# -# edd.py -# BIOS EDD data parsing functions -# -# Copyright (C) 2010 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/>. -# -# Author(s): Hans de Goede <hdegoede@redhat.com> -# Ales Kozumplik <akozumpl@redhat.com> -# - -import glob -import logging -import os -import re -import struct - -log = logging.getLogger("storage") - -re_host_bus = re.compile(r'^PCI\s*(\S*)\s*channel: (\S*)\s*$') -re_interface_scsi = re.compile(r'^SCSI\s*id: (\S*)\s*lun: (\S*)\s*$') -re_interface_ata = re.compile(r'^ATA\s*device: (\S*)\s*$') - -class EddEntry(object): - """ This object merely collects what the /sys/firmware/edd/* entries can - provide. - """ - def __init__(self, sysfspath): - self.type = None - - self.ata_device = None - self.channel = None - self.mbr_signature = None - self.pci_dev = None - self.scsi_id = None - self.scsi_lun = None - self.sectors = None - - self.load(sysfspath) - - def __str__(self): - return \ - "\ttype: %(type)s, ata_device: %(ata_device)s\n" \ - "\tchannel: %(channel)s, mbr_signature: %(mbr_signature)s\n" \ - "\tpci_dev: %(pci_dev)s, scsi_id: %(scsi_id)s\n" \ - "\tscsi_lun: %(scsi_lun)s, sectors: %(sectors)s" % self.__dict__ - - def _read_file(self, filename): - contents = None - if os.path.exists(filename): - with open(filename) as f: - contents = f.read().rstrip() - return contents - - def load(self, sysfspath): - interface = self._read_file(os.path.join(sysfspath, "interface")) - if interface: - self.type = interface.split()[0] - if self.type == "SCSI": - match = re_interface_scsi.match(interface) - self.scsi_id = int(match.group(1)) - self.scsi_lun = int(match.group(2)) - elif self.type == "ATA": - match = re_interface_ata.match(interface) - self.ata_device = int(match.group(1)) - - self.mbr_signature = self._read_file( - os.path.join(sysfspath, "mbr_signature")) - sectors = self._read_file(os.path.join(sysfspath, "sectors")) - if sectors: - self.sectors = int(sectors) - hbus = self._read_file(os.path.join(sysfspath, "host_bus")) - if hbus: - match = re_host_bus.match(hbus) - if match: - self.pci_dev = match.group(1) - self.channel = int(match.group(2)) - else: - log.warning("edd: can not match host_bus: %s" % hbus) - -class EddMatcher(object): - """ This object tries to match given entry to a disk device name. - - Assuming, heuristic analysis and guessing hapens here. - """ - def __init__(self, edd_entry): - self.edd = edd_entry - - def devname_from_pci_dev(self): - name = None - if self.edd.type == "ATA" and \ - self.edd.channel is not None and \ - self.edd.ata_device is not None: - path = "/sys/devices/pci0000:00/0000:%(pci_dev)s/host%(chan)d/"\ - "target%(chan)d:0:%(dev)d/%(chan)d:0:%(dev)d:0/block" % { - 'pci_dev' : self.edd.pci_dev, - 'chan' : self.edd.channel, - 'dev' : self.edd.ata_device - } - if os.path.isdir(path): - block_entries = os.listdir(path) - if len(block_entries) == 1: - name = block_entries[0] - else: - log.warning("edd: directory does not exist: %s" % path) - elif self.edd.type == "SCSI": - pattern = "/sys/devices/pci0000:00/0000:%(pci_dev)s/virtio*/block" % \ - {'pci_dev' : self.edd.pci_dev} - matching_paths = glob.glob(pattern) - if len(matching_paths) != 1 or not os.path.exists(matching_paths[0]): - return None - block_entries = os.listdir(matching_paths[0]) - if len(block_entries) == 1: - name = block_entries[0] - return name - - def match_via_mbrsigs(self, mbr_dict): - """ Try to match the edd entry based on its mbr signature. - - This will obviously fail for a fresh drive/image, but in extreme - cases can also show false positives for randomly matching data. - """ - for (name, mbr_signature) in mbr_dict.items(): - if mbr_signature == self.edd.mbr_signature: - return name - return None - -def biosdev_to_edd_dir(biosdev): - return "/sys/firmware/edd/int13_dev%x" % biosdev - -def collect_edd_data(): - edd_data_dict = {} - # the hard drive numbering starts at 0x80 (128 decimal): - for biosdev in range(0x80, 0x80+16): - sysfspath = biosdev_to_edd_dir(biosdev) - if not os.path.exists(sysfspath): - break - edd_data_dict[biosdev] = EddEntry(sysfspath) - return edd_data_dict - -def collect_mbrs(devices): - """ Read MBR signatures from devices. - - Returns a dict mapping device names to their MBR signatures. It is not - guaranteed this will succeed, with a new disk for instance. - """ - mbr_dict = {} - for dev in devices: - try: - fd = os.open(dev.path, os.O_RDONLY) - # The signature is the unsigned integer at byte 440: - os.lseek(fd, 440, 0) - mbrsig = struct.unpack('I', os.read(fd, 4)) - os.close(fd) - except OSError as e: - log.warning("edd: error reading mbrsig from disk %s: %s" % - (dev.name, str(e))) - continue - - mbrsig_str = "0x%08x" % mbrsig - # sanity check - if mbrsig_str == '0x00000000': - log.info("edd: MBR signature on %s is zero. new disk image?" % dev.name) - continue - else: - for (dev_name, mbrsig_str_old) in mbr_dict.items(): - if mbrsig_str_old == mbrsig_str: - log.error("edd: dupicite MBR signature %s for %s and %s" % - (mbrsig_str, dev_name, dev.name)) - # this actually makes all the other data useless - return {} - # update the dictionary - mbr_dict[dev.name] = mbrsig_str - log.info("edd: collected mbr signatures: %s" % mbr_dict) - return mbr_dict - -def get_edd_dict(devices): - """ Generates the 'device name' -> 'edd number' mapping. - - The EDD kernel module that exposes /sys/firmware/edd is thoroughly - broken, the information there is incomplete and sometimes downright - wrong. So after we mine out all useful information that the files under - /sys/firmware/edd/int13_*/ can provide, we resort to heuristics and - guessing. Our first attempt is, by looking at the device type int - 'interface', attempting to map pci device number, channel number etc. to - a sysfs path, check that the path really exists, then read the device - name (e.g 'sda') from there. Should this fail we try to match contents - of 'mbr_signature' to a real MBR signature found on the existing block - devices. - """ - mbr_dict = collect_mbrs(devices) - edd_entries_dict = collect_edd_data() - global edd_dict - for (edd_number, edd_entry) in edd_entries_dict.items(): - log.debug("edd: data extracted from 0x%x:\n%s" % (edd_number, edd_entry)) - matcher = EddMatcher(edd_entry) - # first try to match through the pci dev etc. - name = matcher.devname_from_pci_dev() - # next try to compare mbr signatures - if name: - log.debug("edd: matched 0x%x to %s using pci_dev" % (edd_number, name)) - else: - name = matcher.match_via_mbrsigs(mbr_dict) - if name: - log.info("edd: matched 0x%x to %s using MBR sig" % (edd_number, name)) - - if name: - old_edd_number = edd_dict.get(name) - if old_edd_number: - log.info("edd: both edd entries 0x%x and 0x%x seem to map to %s" % - (old_edd_number, edd_number, name)) - # this means all the other data can be confused and useless - return {} - edd_dict[name] = edd_number - continue - log.error("edd: unable to match edd entry 0x%x" % edd_number) - return edd_dict - -edd_dict = {} diff --git a/pyanaconda/storage/devicelibs/loop.py b/pyanaconda/storage/devicelibs/loop.py deleted file mode 100644 index 8a4750cce..000000000 --- a/pyanaconda/storage/devicelibs/loop.py +++ /dev/null @@ -1,89 +0,0 @@ -# -# loop.py -# loop device functions -# -# Copyright (C) 2010 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/>. -# -# Author(s): David Lehman <dlehman@redhat.com> -# - -import os - -from .. import util -from ..errors import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - - -def losetup(args, capture=False): - if capture: - exec_func = util.capture_output - else: - exec_func = util.run_program - - try: - # ask losetup what this loop device's backing device is - ret = exec_func(["losetup"] + args) - except OSError as e: - raise LoopError(e.strerror) - - return ret - -def get_backing_file(name): - path = "" - sys_path = "/sys/class/block/%s/loop/backing_file" % name - if os.access(sys_path, os.R_OK): - path = open(sys_path).read().strip() - - return path - -def get_loop_name(path): - args = ["-j", path] - buf = losetup(args, capture=True) - if len(buf.splitlines()) > 1: - # there should never be more than one loop device listed - raise LoopError("multiple loops associated with %s" % path) - - name = os.path.basename(buf.split(":")[0]) - return name - -def loop_setup(path): - args = ["-f", path] - msg = None - try: - msg = losetup(args) - except LoopError as e: - msg = str(e) - - if msg: - raise LoopError("failed to set up loop for %s: %s" % (path, msg)) - -def loop_teardown(path): - args = ["-d", path] - msg = None - try: - msg = losetup(args) - except LoopError as e: - msg = str(e) - - if msg: - raise DeviceError("failed to tear down loop %s: %s" % (path, msg)) - - diff --git a/pyanaconda/storage/devicelibs/lvm.py b/pyanaconda/storage/devicelibs/lvm.py deleted file mode 100644 index 76919939b..000000000 --- a/pyanaconda/storage/devicelibs/lvm.py +++ /dev/null @@ -1,403 +0,0 @@ -# -# lvm.py -# lvm functions -# -# Copyright (C) 2009 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/>. -# -# Author(s): Dave Lehman <dlehman@redhat.com> -# - -import os -import math -import re - -import logging -log = logging.getLogger("storage") - -from .. import util -from .. import arch -from ..errors import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -MAX_LV_SLOTS = 256 - -# some of lvm's defaults that we have no way to ask it for -LVM_PE_START = 1.0 # MB -LVM_PE_SIZE = 4.0 # MB - -def has_lvm(): - if util.find_program_in_path("lvm"): - for line in open("/proc/devices").readlines(): - if "device-mapper" in line.split(): - return True - - return False - -# Start config_args handling code -# -# Theoretically we can handle all that can be handled with the LVM --config -# argument. For every time we call an lvm_cc (lvm compose config) funciton -# we regenerate the config_args with all global info. -config_args = [] # Holds the final argument list -config_args_data = { "filterRejects": [], # regular expressions to reject. - "filterAccepts": [] } # regexp to accept - -def _composeConfig(): - """lvm command accepts lvm.conf type arguments preceded by --config. """ - global config_args, config_args_data - config_args = [] - - filter_string = "" - rejects = config_args_data["filterRejects"] - for reject in rejects: - filter_string += ("\"r|/%s$|\"," % reject) - - filter_string = " filter=[%s] " % filter_string.strip(",") - - # As we add config strings we should check them all. - if filter_string == "": - # Nothing was really done. - return - - # devices_string can have (inside the brackets) "dir", "scan", - # "preferred_names", "filter", "cache_dir", "write_cache_state", - # "types", "sysfs_scan", "md_component_detection". see man lvm.conf. - devices_string = " devices {%s} " % (filter_string) # strings can be added - config_string = devices_string # more strings can be added. - config_args = ["--config", config_string] - -def lvm_cc_addFilterRejectRegexp(regexp): - """ Add a regular expression to the --config string.""" - global config_args_data - log.debug("lvm filter: adding %s to the reject list" % regexp) - config_args_data["filterRejects"].append(regexp) - - # compose config once more. - _composeConfig() - -def lvm_cc_removeFilterRejectRegexp(regexp): - """ Remove a regular expression from the --config string.""" - global config_args_data - log.debug("lvm filter: removing %s from the reject list" % regexp) - try: - config_args_data["filterRejects"].remove(regexp) - except ValueError: - log.debug("%s wasn't in the reject list" % regexp) - return - - # compose config once more. - _composeConfig() - -def lvm_cc_resetFilter(): - global config_args, config_args_data - config_args_data["filterRejects"] = [] - config_args_data["filterAccepts"] = [] - config_args = [] -# End config_args handling code. - -# Names that should not be used int the creation of VGs -lvm_vg_blacklist = [] -def blacklistVG(name): - global lvm_vg_blacklist - lvm_vg_blacklist.append(name) - -def getPossiblePhysicalExtents(floor=0): - """Returns a list of integers representing the possible values for - the physical extent of a volume group. Value is in KB. - - floor - size (in KB) of smallest PE we care about. - """ - - possiblePE = [] - curpe = 8 - while curpe <= 16384*1024: - if curpe >= floor: - possiblePE.append(curpe) - curpe = curpe * 2 - - return possiblePE - -def getMaxLVSize(): - """ Return the maximum size (in MB) of a logical volume. """ - if arch.getArch() in ("x86_64", "ppc64", "alpha", "ia64", "s390", "sparc"): #64bit architectures - return (8*1024*1024*1024*1024) #Max is 8EiB (very large number..) - else: - return (16*1024*1024) #Max is 16TiB - -def clampSize(size, pesize, roundup=None): - if roundup: - round = math.ceil - else: - round = math.floor - - return long(round(float(size)/float(pesize)) * pesize) - -def get_pv_space(size, disks, pesize=LVM_PE_SIZE, - striped=False, mirrored=False): - """ Given specs for an LV, return total PV space required. """ - # XXX default extent size should be something we can ask of lvm - # TODO: handle striped and mirrored - # this is adding one extent for the lv's metadata - space = clampSize(size, pesize, roundup=True) + \ - pesize - return space - -def lvm(args): - ret = util.run_program(["lvm"] + args) - if ret: - raise LVMError("running lvm " + " ".join(args) + " failed") - -def pvcreate(device): - # we force dataalignment=1024k since we cannot get lvm to tell us what - # the pe_start will be in advance - args = ["pvcreate"] + \ - config_args + \ - ["--dataalignment", "1024k"] + \ - [device] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("pvcreate failed for %s: %s" % (device, msg)) - -def pvresize(device, size): - args = ["pvresize"] + \ - ["--setphysicalvolumesize", ("%dm" % size)] + \ - config_args + \ - [device] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("pvresize failed for %s: %s" % (device, msg)) - -def pvremove(device): - args = ["pvremove", "--force", "--force", "--yes"] + \ - config_args + \ - [device] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("pvremove failed for %s: %s" % (device, msg)) - -def pvinfo(device): - """ - If the PV was created with '--metadacopies 0', lvm will do some - scanning of devices to determine from their metadata which VG - this PV belongs to. - - pvs -o pv_name,pv_mda_count,vg_name,vg_uuid --config \ - 'devices { scan = "/dev" filter = ["a/loop0/", "r/.*/"] }' - """ - #cfg = "'devices { scan = \"/dev\" filter = [\"a/%s/\", \"r/.*/\"] }'" - args = ["pvs", "--noheadings"] + \ - ["--units", "m"] + \ - ["-o", "pv_name,pv_mda_count,vg_name,vg_uuid"] + \ - config_args + \ - [device] - - rc = util.capture_output(["lvm"] + args) - vals = rc.split() - if not vals: - raise LVMError("pvinfo failed for %s" % device) - - # don't raise an exception if pv is not a part of any vg - pv_name = vals[0] - try: - vg_name, vg_uuid = vals[2], vals[3] - except IndexError: - vg_name, vg_uuid = "", "" - - info = {'pv_name': pv_name, - 'vg_name': vg_name, - 'vg_uuid': vg_uuid} - - return info - -def vgcreate(vg_name, pv_list, pe_size): - argv = ["vgcreate"] - if pe_size: - argv.extend(["-s", "%dm" % pe_size]) - argv.extend(config_args) - argv.append(vg_name) - argv.extend(pv_list) - - try: - lvm(argv) - except LVMError as msg: - raise LVMError("vgcreate failed for %s: %s" % (vg_name, msg)) - -def vgremove(vg_name): - args = ["vgremove", "--force"] + \ - config_args +\ - [vg_name] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("vgremove failed for %s: %s" % (vg_name, msg)) - -def vgactivate(vg_name): - args = ["vgchange", "-a", "y"] + \ - config_args + \ - [vg_name] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("vgactivate failed for %s: %s" % (vg_name, msg)) - -def vgdeactivate(vg_name): - args = ["vgchange", "-a", "n"] + \ - config_args + \ - [vg_name] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("vgdeactivate failed for %s: %s" % (vg_name, msg)) - -def vgreduce(vg_name, pv_list, rm=False): - """ Reduce a VG. - - rm -> with RemoveMissing option. - Use pv_list when rm=False, otherwise ignore pv_list and call vgreduce with - the --removemissing option. - """ - args = ["vgreduce"] - args.extend(config_args) - if rm: - args.extend(["--removemissing", "--force", vg_name]) - else: - args.extend([vg_name] + pv_list) - - try: - lvm(args) - except LVMError as msg: - raise LVMError("vgreduce failed for %s: %s" % (vg_name, msg)) - -def vginfo(vg_name): - args = ["vgs", "--noheadings", "--nosuffix"] + \ - ["--units", "m"] + \ - ["-o", "uuid,size,free,extent_size,extent_count,free_count,pv_count"] + \ - config_args + \ - [vg_name] - - buf = util.capture_output(["lvm"] + args) - info = buf.split() - if len(info) != 7: - raise LVMError(_("vginfo failed for %s" % vg_name)) - - d = {} - (d['uuid'],d['size'],d['free'],d['pe_size'], - d['pe_count'],d['pe_free'],d['pv_count']) = info - return d - -def lvs(vg_name): - args = ["lvs", "--noheadings", "--nosuffix"] + \ - ["--units", "m"] + \ - ["-o", "lv_name,lv_uuid,lv_size,lv_attr"] + \ - config_args + \ - [vg_name] - - buf = util.capture_output(["lvm"] + args) - - lvs = {} - for line in buf.splitlines(): - line = line.strip() - if not line: - continue - (name, uuid, size, attr) = line.split() - lvs[name] = {"size": size, - "uuid": uuid, - "attr": attr} - - if not lvs: - raise LVMError(_("lvs failed for %s" % vg_name)) - - return lvs - -def lvorigin(vg_name, lv_name): - args = ["lvs", "--noheadings", "-o", "origin"] + \ - config_args + \ - ["%s/%s" % (vg_name, lv_name)] - - buf = util.capture_output(["lvm"] + args) - - try: - origin = buf.splitlines()[0].strip() - except IndexError: - origin = '' - - return origin - -def lvcreate(vg_name, lv_name, size, pvs=[]): - args = ["lvcreate"] + \ - ["-L", "%dm" % size] + \ - ["-n", lv_name] + \ - config_args + \ - [vg_name] + pvs - - try: - lvm(args) - except LVMError as msg: - raise LVMError("lvcreate failed for %s/%s: %s" % (vg_name, lv_name, msg)) - -def lvremove(vg_name, lv_name): - args = ["lvremove"] + \ - config_args + \ - ["%s/%s" % (vg_name, lv_name)] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("lvremove failed for %s: %s" % (lv_name, msg)) - -def lvresize(vg_name, lv_name, size): - args = ["lvresize"] + \ - ["--force", "-L", "%dm" % size] + \ - config_args + \ - ["%s/%s" % (vg_name, lv_name)] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("lvresize failed for %s: %s" % (lv_name, msg)) - -def lvactivate(vg_name, lv_name): - # see if lvchange accepts paths of the form 'mapper/$vg-$lv' - args = ["lvchange", "-a", "y"] + \ - config_args + \ - ["%s/%s" % (vg_name, lv_name)] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("lvactivate failed for %s: %s" % (lv_name, msg)) - -def lvdeactivate(vg_name, lv_name): - args = ["lvchange", "-a", "n"] + \ - config_args + \ - ["%s/%s" % (vg_name, lv_name)] - - try: - lvm(args) - except LVMError as msg: - raise LVMError("lvdeactivate failed for %s: %s" % (lv_name, msg)) - diff --git a/pyanaconda/storage/devicelibs/mdraid.py b/pyanaconda/storage/devicelibs/mdraid.py deleted file mode 100644 index ab18641ac..000000000 --- a/pyanaconda/storage/devicelibs/mdraid.py +++ /dev/null @@ -1,280 +0,0 @@ -# -# mdraid.py -# mdraid functions -# -# Copyright (C) 2009 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/>. -# -# Author(s): Dave Lehman <dlehman@redhat.com> -# - -import os - -from .. import util -from ..errors import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -# these defaults were determined empirically -MD_SUPERBLOCK_SIZE = 2.0 # MB -MD_CHUNK_SIZE = 0.5 # MB - -# raidlevels constants -RAID10 = 10 -RAID6 = 6 -RAID5 = 5 -RAID4 = 4 -RAID1 = 1 -RAID0 = 0 - -def getRaidLevels(): - mdstat_descriptors = { - RAID10: ("[RAID10]", "[raid10]"), - RAID6: ("[RAID6]", "[raid6]"), - RAID5: ("[RAID5]", "[raid5]"), - RAID4: ("[RAID4]", "[raid4]"), - RAID1: ("[RAID1]", "[raid1]"), - RAID0: ("[RAID0]", "[raid0]"), - } - avail = [] - try: - f = open("/proc/mdstat", "r") - except IOError: - pass - else: - for l in f.readlines(): - if not l.startswith("Personalities"): - continue - - lst = l.split() - - for level in mdstat_descriptors: - for d in mdstat_descriptors[level]: - if d in lst: - avail.append(level) - break - - f.close() - - avail.sort() - return avail - -raid_levels = getRaidLevels() - -raid_descriptors = {RAID10: ("raid10", "RAID10", "10", 10), - RAID6: ("raid6", "RAID6", "6", 6), - RAID5: ("raid5", "RAID5", "5", 5), - RAID4: ("raid4", "RAID4", "4", 4), - RAID1: ("raid1", "mirror", "RAID1", "1", 1), - RAID0: ("raid0", "stripe", "RAID0", "0", 0)} - -def raidLevel(descriptor): - for level in raid_levels: - if isRaid(level, descriptor): - return level - else: - raise MDRaidError("invalid raid level descriptor %s" % descriptor) - -def raidLevelString(level): - if level in raid_descriptors.keys(): - return raid_descriptors[level][0] - else: - raise MDRaidError("invalid raid level constant %s" % level) - -def isRaid(raid, raidlevel): - """Return whether raidlevel is a valid descriptor of raid""" - if raid in raid_descriptors: - return raidlevel in raid_descriptors[raid] - else: - raise MDRaidError("invalid raid level %d" % raid) - -def get_raid_min_members(raidlevel): - """Return the minimum number of raid members required for raid level""" - raid_min_members = {RAID10: 2, - RAID6: 4, - RAID5: 3, - RAID4: 3, - RAID1: 2, - RAID0: 2} - - for raid, min_members in raid_min_members.items(): - if isRaid(raid, raidlevel): - return min_members - - raise MDRaidError("invalid raid level %d" % raidlevel) - -def get_raid_max_spares(raidlevel, nummembers): - """Return the maximum number of raid spares for raidlevel.""" - raid_max_spares = {RAID10: lambda: max(0, nummembers - get_raid_min_members(RAID10)), - RAID6: lambda: max(0, nummembers - get_raid_min_members(RAID6)), - RAID5: lambda: max(0, nummembers - get_raid_min_members(RAID5)), - RAID4: lambda: max(0, nummembers - get_raid_min_members(RAID4)), - RAID1: lambda: max(0, nummembers - get_raid_min_members(RAID1)), - RAID0: lambda: 0} - - for raid, max_spares_func in raid_max_spares.items(): - if isRaid(raid, raidlevel): - return max_spares_func() - - raise MDRaidError("invalid raid level %d" % raidlevel) - -def get_member_space(size, disks, level=None): - space = 0 # size of *each* member device - - if isinstance(level, str): - level = raidLevel(level) - - min_members = get_raid_min_members(level) - if disks < min_members: - raise MDRaidError("raid%d requires at least %d disks" - % (level, min_members)) - - if level == RAID0: - # you need the sum of the member sizes to equal your desired capacity - space = size / disks - elif level == RAID1: - # you need each member's size to equal your desired capacity - space = size - elif level in (RAID4, RAID5): - # you need the sum of all but one members' sizes to equal your desired - # capacity - space = size / (disks - 1) - elif level == RAID6: - # you need the sum of all but two members' sizes to equal your desired - # capacity - space = size / (disks - 2) - elif level == RAID10: - # you need the sum of the member sizes to equal twice your desired - # capacity - space = size / (disks / 2.0) - - space += MD_SUPERBLOCK_SIZE - - return space * disks - -def mdadm(args): - ret = util.run_program(["mdadm"] + args) - if ret: - raise MDRaidError("running mdadm " + " ".join(args) + " failed") - -def mdcreate(device, level, disks, spares=0, metadataVer=None, bitmap=False): - argv = ["--create", device, "--run", "--level=%s" % level] - raid_devs = len(disks) - spares - argv.append("--raid-devices=%d" % raid_devs) - if spares: - argv.append("--spare-devices=%d" % spares) - if metadataVer: - argv.append("--metadata=%s" % metadataVer) - if bitmap: - argv.append("--bitmap=internal") - argv.extend(disks) - - try: - mdadm(argv) - except MDRaidError as msg: - raise MDRaidError("mdcreate failed for %s: %s" % (device, msg)) - -def mddestroy(device): - args = ["--zero-superblock", device] - - try: - mdadm(args) - except MDRaidError as msg: - raise MDRaidError("mddestroy failed for %s: %s" % (device, msg)) - -def mdadd(device): - args = ["--incremental", "--quiet"] - args.append(device) - - try: - mdadm(args) - except MDRaidError as msg: - raise MDRaidError("mdadd failed for %s: %s" % (device, msg)) - -def mdactivate(device, members=[], super_minor=None, uuid=None): - if super_minor is None and not uuid: - raise MDRaidError("mdactivate requires either a uuid or a super-minor") - - if uuid: - identifier = "--uuid=%s" % uuid - else: - identifier = "" - - args = ["--assemble", device, identifier, "--run"] - args += members - - try: - mdadm(args) - except MDRaidError as msg: - raise MDRaidError("mdactivate failed for %s: %s" % (device, msg)) - -def mddeactivate(device): - args = ["--stop", device] - - try: - mdadm(args) - except MDRaidError as msg: - raise MDRaidError("mddeactivate failed for %s: %s" % (device, msg)) - -def mdexamine(device): - _vars = util.capture_output(["mdadm", - "--examine", "--brief", device]).split() - - info = {} - if len(_vars) > 1 and _vars[1].startswith("/dev/md"): - info["device"] = _vars[1] - _vars = _vars[2:] - elif len(_vars) > 1: - _vars = _vars[1:] - - for var in _vars: - (name, equals, value) = var.partition("=") - if not equals: - continue - - info[name.lower()] = value.strip() - - return info - -def md_node_from_name(name): - named_path = "/dev/md/" + name - try: - node = os.path.basename(os.readlink(named_path)) - except OSError as e: - raise MDRaidError("md_node_from_name failed: %s" % e) - else: - return node - -def name_from_md_node(node): - md_dir = "/dev/md" - name = None - # It's sad, but it's what we've got. - for link in os.listdir(md_dir): - full_path = "%s/%s" % (md_dir, link) - md_name = os.path.basename(os.readlink(full_path)) - log.debug("link: %s -> %s" % (link, os.readlink(full_path))) - if md_name == node: - name = link - break - - if not name: - raise MDRaidError("name_from_md_node(%s) failed" % node) - - log.debug("returning %s" % name) - return name diff --git a/pyanaconda/storage/devicelibs/mpath.py b/pyanaconda/storage/devicelibs/mpath.py deleted file mode 100644 index d49ab07be..000000000 --- a/pyanaconda/storage/devicelibs/mpath.py +++ /dev/null @@ -1,285 +0,0 @@ - -import re - -from ..udev import udev_device_is_disk -from .. import util -from ..flags import flags -from ..storage_log import log_method_call - -import logging -log = logging.getLogger("storage") - -def parseMultipathOutput(output): - """ - Parse output from "multipath -d" or "multipath -ll" and form a topology. - - Returns a dictionary: - {'mpatha':['sdb','sdc'], 'mpathb': ['sdd', 'sde'], ... } - - The 'multipath -d' output looks like: - create: mpathc (1ATA ST3120026AS 5M) undef ATA,ST3120026AS - size=112G features='0' hwhandler='0' wp=undef - `-+- policy='round-robin 0' prio=1 status=undef - `- 2:0:0:0 sda 8:0 undef ready running - create: mpathb (36006016092d21800703762872c60db11) undef DGC,RAID 5 - size=10G features='1 queue_if_no_path' hwhandler='1 emc' wp=undef - `-+- policy='round-robin 0' prio=2 status=undef - |- 6:0:0:0 sdb 8:16 undef ready running - `- 7:0:0:0 sdc 8:32 undef ready running - create: mpatha (36001438005deb4710000500000270000) dm-0 HP,HSV400 - size=20G features='0' hwhandler='0' wp=rw - |-+- policy='round-robin 0' prio=-1 status=active - | |- 7:0:0:1 sda 8:0 active undef running - | `- 7:0:1:1 sdb 8:16 active undef running - `-+- policy='round-robin 0' prio=-1 status=enabled - |- 7:0:2:1 sdc 8:32 active undef running - `- 7:0:3:1 sdd 8:48 active undef running - - (In anaconda, the first one there won't be included because we blacklist - "ATA" as a vendor.) - - The 'multipath -ll' output looks like (notice the missing 'create' before - 'mpatha'): - - mpatha (3600a0b800067fcc9000001694b557dd1) dm-0 IBM,1726-4xx FAStT - size=360G features='0' hwhandler='1 rdac' wp=rw - `-+- policy='round-robin 0' prio=3 status=active - |- 2:0:0:0 sda 8:0 active ready running - `- 3:0:0:0 sdb 8:16 active ready running - - """ - mpaths = {} - if output is None: - return mpaths - - name = None - devices = [] - - policy = re.compile('^[|+` -]+policy') - device = re.compile('^[|+` -]+[0-9]+:[0-9]+:[0-9]+:[0-9]+ ([a-zA-Z0-9!/]+)') - create = re.compile('^(create: )?(mpath\w+|[a-f0-9]+)') - - lines = output.split('\n') - for line in lines: - pmatch = policy.match(line) - dmatch = device.match(line) - cmatch = create.match(line) - lexemes = line.split() - if not lexemes: - break - if cmatch and cmatch.group(2): - if name and devices: - mpaths[name] = devices - name = None - devices = [] - name = cmatch.group(2) - elif lexemes[0].startswith('size='): - pass - elif pmatch: - pass - elif dmatch: - devices.append(dmatch.groups()[0].replace('!','/')) - - if name and devices: - mpaths[name] = devices - - return mpaths - -class MultipathTopology(object): - def __init__(self, devices_list): - self._devices = devices_list - self._nondisks = [] - self._singlepaths = [] - self._multipaths = [] # mpath members - self._devmap = {} - - self._build_topology() - - def _build_devmap(self): - self._devmap = {} - for dev in self._devices: - self._devmap[dev['name']] = dev - - def _build_mpath_topology(self): - with open("/etc/multipath.conf") as conf: - log.debug("/etc/multipath.conf contents:") - map(lambda line: log.debug(line.rstrip()), conf) - log.debug("(end of /etc/multipath.conf)") - self._mpath_topology = parseMultipathOutput( - util.capture_output(["multipath", "-d"])) - self._mpath_topology.update(parseMultipathOutput( - util.capture_output(["multipath", "-ll"]))) - - delete_keys = [] - for (mp, disks) in self._mpath_topology.items(): - # single device mpath is not really an mpath, eliminate them: - if len(disks) < 2: - log.info("MultipathTopology: not a multipath: %s" % disks) - delete_keys.append(mp) - continue - # some usb cardreaders use multiple lun's (for different slots) and - # report a fake disk serial which is the same for all the lun's - # (#517603). find those mpaths and eliminate them: - only_non_usbs = [d for d in disks if - self._devmap[d].get("ID_USB_DRIVER") != "usb-storage"] - if len(only_non_usbs) == 0: - log.info("DeviceToppology: found multi lun usb " - "mass storage device: %s" % disks) - delete_keys.append(mp) - map(lambda key: self._mpath_topology.pop(key), delete_keys) - - def _build_topology(self): - log_method_call(self) - self._build_devmap() - self._build_mpath_topology() - - for dev in self._devices: - name = dev['name'] - if not udev_device_is_disk(dev): - self._nondisks.append(name) - log.info("MultipathTopology: found non-disk device: %s" % name) - continue - mpath_name = self.multipath_name(name) - if mpath_name: - dev["ID_FS_TYPE"] = "multipath_member" - dev["ID_MPATH_NAME"] = mpath_name - log.info("MultipathTopology: found a multipath member of %s: %s " % - (mpath_name, name)) - continue - # it's a disk and not a multipath member (can be a coalesced - # multipath) - self._singlepaths.append(name) - log.info("MultipathTopology: found singlepath device: %s" % name) - - def devices_iter(self): - """ Generator. Yields all disk devices, mpaths members, coalesced mpath - devices and partitions. - - This property guarantees the order of the returned devices is the - same as in the device list passed to the object's constructor. - """ - for device in self._devices: - yield device - - def singlepaths_iter(self): - """ Generator. Yields only the singlepath disks. - """ - for name in self._singlepaths: - yield self._devmap[name] - - def multipath_name(self, mpath_member_name): - """ If the mpath_member_name is a member of a multipath device return - the name of the device (e.g. mpathc). - - Else return None. - """ - for (name, members) in self._mpath_topology.items(): - if mpath_member_name in members: - return name - return None - - def multipaths_iter(self): - """Generator. Yields all the multipath members, in a topology. - - Every iteration returns a list of mpath member devices forming a - multipath. - """ - for disks in self._mpath_topology.values(): - yield [self._devmap[d] for d in disks] - - -class MultipathConfigWriter: - def __init__(self): - self.blacklist_devices = [] - self.mpaths = [] - - def addBlacklistDevice(self, device): - self.blacklist_devices.append(device) - - def addMultipathDevice(self, mpath): - self.mpaths.append(mpath) - - def write(self, friendly_names): - # if you add anything here, be sure and also add it to anaconda's - # multipath.conf - ret = '' - ret += """\ -# multipath.conf written by anaconda - -defaults { - user_friendly_names %(friendly_names)s -} -blacklist { - devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" - devnode "^hd[a-z]" - devnode "^dcssblk[0-9]*" - device { - vendor "DGC" - product "LUNZ" - } - device { - vendor "IBM" - product "S/390.*" - } - # don't count normal SATA devices as multipaths - device { - vendor "ATA" - } - # don't count 3ware devices as multipaths - device { - vendor "3ware" - } - device { - vendor "AMCC" - } - # nor highpoint devices - device { - vendor "HPT" - } -""" % {'friendly_names' : "yes" if friendly_names else "no"} - for device in self.blacklist_devices: - if device.serial: - ret += '\twwid "%s"\n' % device.serial - elif device.vendor and device.model: - ret += '\tdevice {\n' - ret += '\t\tvendor %s\n' % device.vendor - ret += '\t\tproduct %s\n' % device.model - ret += '\t}\n' - if self.mpaths: - ret += '\twwid "*"\n' - ret += '}\n' - ret += 'blacklist_exceptions {\n' - for mpath in self.mpaths: - for k,v in mpath.config.items(): - if k == 'wwid': - ret += '\twwid "%s"\n' % v - ret += '}\n' - ret += 'multipaths {\n' - for mpath in self.mpaths: - ret += '\tmultipath {\n' - for k,v in mpath.config.items(): - if k == 'wwid': - ret += '\t\twwid "%s"\n' % v - else: - ret += '\t\t%s %s\n' % (k, v) - ret += '\t}\n' - ret += '}\n' - - return ret - - def writeConfig(self, friendly_names=True): - if not flags.multipath: - # not writing out a multipath.conf will effectively blacklist all - # mpath which will prevent any of them from being activated during - # install - return - - cfg = self.write(friendly_names) - with open("/etc/multipath.conf", "w+") as mpath_cfg: - mpath_cfg.write(cfg) - -def flush_mpaths(): - util.run_program(["multipath", "-F"]) - check_output = util.capture_output(["multipath", "-ll"]).strip() - if check_output: - log.error("multipath: some devices could not be flushed") diff --git a/pyanaconda/storage/devicelibs/swap.py b/pyanaconda/storage/devicelibs/swap.py deleted file mode 100644 index 4c9490a00..000000000 --- a/pyanaconda/storage/devicelibs/swap.py +++ /dev/null @@ -1,157 +0,0 @@ -# swap.py -# Python module for managing swap devices. -# -# Copyright (C) 2009 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> -# - -import resource -import os - -from ..errors import * -from .. import util -from . import dm - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -def mkswap(device, label=''): - # We use -f to force since mkswap tends to refuse creation on lvs with - # a message about erasing bootbits sectors on whole disks. Bah. - argv = ["-f"] - if label: - argv.extend(["-L", label]) - argv.append(device) - - ret = util.run_program(["mkswap"] + argv) - - if ret: - raise SwapError("mkswap failed for '%s'" % device) - -def swapon(device, priority=None): - pagesize = resource.getpagesize() - buf = None - sig = None - - if pagesize > 2048: - num = pagesize - else: - num = 2048 - - try: - fd = os.open(device, os.O_RDONLY) - buf = os.read(fd, num) - except OSError: - pass - finally: - try: - os.close(fd) - except (OSError, UnboundLocalError): - pass - - if buf is not None and len(buf) == pagesize: - sig = buf[pagesize - 10:] - if sig == 'SWAP-SPACE': - raise OldSwapError - if sig == 'S1SUSPEND\x00' or sig == 'S2SUSPEND\x00': - raise SuspendError - - if sig != 'SWAPSPACE2': - raise UnknownSwapError - - argv = [] - if isinstance(priority, int) and 0 <= priority <= 32767: - argv.extend(["-p", "%d" % priority]) - argv.append(device) - - rc = util.run_program(["swapon"] + argv) - - if rc: - raise SwapError("swapon failed for '%s'" % device) - -def swapoff(device): - rc = util.run_program(["swapoff", device]) - - if rc: - raise SwapError("swapoff failed for '%s'" % device) - -def swapstatus(device): - alt_dev = None - if device.startswith("/dev/mapper/"): - # get the real device node for device-mapper devices since the ones - # with meaningful names are just symlinks - try: - alt_dev = "/dev/%s" % dm.dm_node_from_name(device.split("/")[-1]) - except DMError: - alt_dev = None - - lines = open("/proc/swaps").readlines() - status = False - for line in lines: - if not line.strip(): - continue - - swap_dev = line.split()[0] - if swap_dev in [device, alt_dev]: - status = True - break - - return status - -def swapSuggestion(quiet=False, hibernation=False): - """ - Suggest the size of the swap partition that will be created. - - @param quiet: log size information - @param hibernation: calculate swap size big enough for hibernation - @return: calculated swap size - - """ - - mem = util.total_memory()/1024 - mem = ((mem/16)+1)*16 - if not quiet: - log.info("Detected %sM of memory", mem) - - #chart suggested in the discussion with other teams - if mem < 2048: - swap = 2 * mem - - elif 2048 <= mem < 8192: - swap = mem - - elif 8192 <= mem < 65536: - swap = mem / 2 - - else: - swap = 4096 - - if hibernation: - if mem <= 65536: - swap = mem + swap - else: - log.info("Ignoring --hibernation option on systems with 64 GB of RAM or more") - - if not quiet: - log.info("Swap attempt of %sM", swap) - - return swap - diff --git a/pyanaconda/storage/devices.py b/pyanaconda/storage/devices.py deleted file mode 100644 index 1a4b4d071..000000000 --- a/pyanaconda/storage/devices.py +++ /dev/null @@ -1,4190 +0,0 @@ -# devices.py -# Device classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 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> -# - - -""" - Device classes for use by anaconda. - - This is the hierarchy of device objects that anaconda will use for - managing storage devices in the system. These classes will - individually make use of external support modules as needed to - perform operations specific to the type of device they represent. - - TODO: - - see how to do network devices (NetworkManager may help) - - perhaps just a wrapper here - - document return values of all methods/functions - - find out what other kinds of wild and crazy devices we need to - represent here (iseries? xen? more mainframe? mac? ps?) - - PReP - - this is a prime candidate for a PseudoDevice - - DASD - - ZFCP - - XEN - - What specifications do we allow? new existing - partitions - usage + + - filesystem, partition type are implicit - mountpoint + + - size - exact + - - range + - - resize - + - format - + - encryption + + - - disk - exact + - - set + - - how will we specify this? - partition w/ multiple parents cannot otherwise occur - primary + - - - mdraid sets - filesystem (*) + + - mountpoint + + - size? - format - + - encryption + + - - level + ? - device minor + ? - member devices + ? - spares + ? - name? - bitmap? (boolean) + - - - volume groups - name + - - member pvs + + - pesize + ? - - logical volumes - filesystem + + - mountpoint + + - size - exact + ? - format - + - encryption + + - - name + ? - vgname + ? - - -""" - -import os -import math -import copy -import pprint -import tempfile - -# device backend modules -from devicelibs import mdraid -from devicelibs import lvm -from devicelibs import dm -from devicelibs import loop -from devicelibs import btrfs -from devicelibs import crypto -import parted -import _ped -import block - -from errors import * -import util -import arch -from flags import flags -from storage_log import log_method_call -from udev import * -from formats import get_device_format_class, getFormat, DeviceFormat - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) -P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z) - -import logging -log = logging.getLogger("storage") - -def get_device_majors(): - majors = {} - for line in open("/proc/devices").readlines(): - try: - (major, device) = line.split() - except ValueError: - continue - try: - majors[int(major)] = device - except ValueError: - continue - return majors -device_majors = get_device_majors() - - -def devicePathToName(devicePath): - if devicePath.startswith("/dev/"): - name = devicePath[5:] - else: - name = devicePath - - if name.startswith("mapper/"): - name = name[7:] - - if name.startswith("md/"): - name = name[3:] - - return name - - -def deviceNameToDiskByPath(deviceName=None): - if not deviceName: - return "" - - ret = None - for dev in udev_get_block_devices(): - if udev_device_get_name(dev) == deviceName: - ret = udev_device_get_by_path(dev) - break - - if ret: - return ret - raise DeviceNotFoundError(deviceName) - -class Device(object): - """ A generic device. - - Device instances know which devices they depend upon (parents - attribute). They do not know which devices depend upon them, but - they do know whether or not they have any dependent devices - (isleaf attribute). - - A Device's setup method should set up all parent devices as well - as the device itself. It should not run the resident format's - setup method. - - Which Device types rely on their parents' formats being active? - DMCryptDevice - - A Device's teardown method should accept the keyword argument - recursive, which takes a boolean value and indicates whether or - not to recursively close parent devices. - - A Device's create method should create all parent devices as well - as the device itself. It should also run the Device's setup method - after creating the device. The create method should not create a - device's resident format. - - Which device type rely on their parents' formats to be created - before they can be created/assembled? - VolumeGroup - DMCryptDevice - - A Device's destroy method should destroy any resident format - before destroying the device itself. - - """ - - # This is a counter for generating unique ids for Devices. - _id = 0 - - _type = "device" - _packages = [] - _services = [] - - def __init__(self, name, parents=None): - """ Create a Device instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - parents -- a list of required Device instances - - """ - self._name = name - if parents is None: - parents = [] - elif not isinstance(parents, list): - raise ValueError("parents must be a list of Device instances") - self.parents = parents - self.kids = 0 - - # Set this instance's id and increment the counter. - self.id = Device._id - Device._id += 1 - - for parent in self.parents: - parent.addChild() - - def __deepcopy__(self, memo): - """ Create a deep copy of a Device instance. - - We can't do copy.deepcopy on parted objects, which is okay. - For these parted objects, we just do a shallow copy. - """ - new = self.__class__.__new__(self.__class__) - memo[id(self)] = new - dont_copy_attrs = ('_raidSet',) - shallow_copy_attrs = ('_partedDevice', '_partedPartition') - for (attr, value) in self.__dict__.items(): - if attr in dont_copy_attrs: - setattr(new, attr, value) - elif attr in shallow_copy_attrs: - setattr(new, attr, copy.copy(value)) - else: - setattr(new, attr, copy.deepcopy(value, memo)) - - return new - - def __repr__(self): - s = ("%(type)s instance (%(id)s) --\n" - " name = %(name)s status = %(status)s" - " kids = %(kids)s id = %(dev_id)s\n" - " parents = %(parents)s\n" % - {"type": self.__class__.__name__, "id": "%#x" % id(self), - "name": self.name, "kids": self.kids, "status": self.status, - "dev_id": self.id, - "parents": pprint.pformat([str(p) for p in self.parents])}) - return s - - def __str__(self): - s = "%s %s (%d)" % (self.type, self.name, self.id) - return s - - @property - def dict(self): - d = {"type": self.type, "name": self.name, - "parents": [p.name for p in self.parents]} - return d - - def removeChild(self): - log_method_call(self, name=self.name, kids=self.kids) - self.kids -= 1 - - def addChild(self): - log_method_call(self, name=self.name, kids=self.kids) - self.kids += 1 - - def setup(self): - """ Open, or set up, a device. """ - raise NotImplementedError("setup method not defined for Device") - - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - raise NotImplementedError("teardown method not defined for Device") - - def create(self): - """ Create the device. """ - raise NotImplementedError("create method not defined for Device") - - def destroy(self): - """ Destroy the device. """ - raise NotImplementedError("destroy method not defined for Device") - - def setupParents(self, orig=False): - """ Run setup method of all parent devices. """ - log_method_call(self, name=self.name, orig=orig, kids=self.kids) - for parent in self.parents: - parent.setup(orig=orig) - - def teardownParents(self, recursive=None): - """ Run teardown method of all parent devices. """ - for parent in self.parents: - parent.teardown(recursive=recursive) - - def dependsOn(self, dep): - """ Return True if this device depends on dep. """ - # XXX does a device depend on itself? - if dep in self.parents: - return True - - for parent in self.parents: - if parent.dependsOn(dep): - return True - - return False - - def dracutSetupArgs(self): - return set() - - @property - def status(self): - """ This device's status. - - For now, this should return a boolean: - True the device is open and ready for use - False the device is not open - """ - return False - - @property - def name(self): - """ This device's name. """ - return self._name - - @property - def isleaf(self): - """ True if this device has no children. """ - return self.kids == 0 - - @property - def typeDescription(self): - """ String describing the device type. """ - return self._type - - @property - def type(self): - """ Device type. """ - return self._type - - @property - def ancestors(self): - l = set([self]) - for p in [d for d in self.parents if d not in l]: - l.update(set(p.ancestors)) - return list(l) - - @property - def packages(self): - """ List of packages required to manage devices of this type. - - This list includes the packages required by its parent devices. - """ - packages = self._packages - for parent in self.parents: - for package in parent.packages: - if package not in packages: - packages.append(package) - - return packages - - @property - def services(self): - """ List of services required to manage devices of this type. - - This list includes the services required by its parent devices." - """ - services = self._services - for parent in self.parents: - for service in parent.services: - if service not in services: - services.append(service) - - return services - - @property - def mediaPresent(self): - return True - - -class NetworkStorageDevice(object): - """ Virtual base class for network backed storage devices """ - - def __init__(self, host_address=None, nic=None): - """ Create a NetworkStorage Device instance. Note this class is only - to be used as a baseclass and then only with multiple inheritance. - The only correct use is: - class MyStorageDevice(StorageDevice, NetworkStorageDevice): - - The sole purpose of this class is to: - 1) Be able to check if a StorageDevice is network backed - (using isinstance). - 2) To be able to get the host address of the host (server) backing - the storage *or* the NIC through which the storage is connected - - Arguments: - - host_address -- host address of the backing server - nic -- nic to which the storage is bound - """ - self.host_address = host_address - self.nic = nic - - -class StorageDevice(Device): - """ A generic storage device. - - A fully qualified path to the device node can be obtained via the - path attribute, although it is not guaranteed to be useful, or - even present, unless the StorageDevice's setup method has been - run. - - StorageDevice instances can optionally contain a filesystem, - represented by an FS instance. A StorageDevice's create method - should create a filesystem if one has been specified. - """ - _type = "storage" - _devDir = "/dev" - sysfsBlockDir = "class/block" - _resizable = False - _partitionable = False - _isDisk = False - - def __init__(self, name, format=None, uuid=None, - size=None, major=None, minor=None, - sysfsPath='', parents=None, exists=False, serial=None, - vendor="", model="", bus=""): - """ Create a StorageDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - size -- the device's size (units/format TBD) - major -- the device major - minor -- the device minor - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - uuid -- universally unique identifier - parents -- a list of required Device instances - serial -- the ID_SERIAL_SHORT for this device - vendor -- the manufacturer of this Device - model -- manufacturer's device model string - bus -- the interconnect this device uses - - """ - # allow specification of individual parents - if isinstance(parents, Device): - parents = [parents] - - self.exists = exists - Device.__init__(self, name, parents=parents) - - self.uuid = uuid - self._format = None - self._size = util.numeric_type(size) - self.major = util.numeric_type(major) - self.minor = util.numeric_type(minor) - self.sysfsPath = sysfsPath - self._serial = serial - self._vendor = vendor - self._model = model - self.bus = bus - - self.protected = False - self.controllable = not flags.testing - - self.format = format - self.originalFormat = copy.copy(self.format) - self.fstabComment = "" - self._targetSize = self._size - - self._partedDevice = None - - if self.exists and flags.testing and not self._size: - def read_int_from_sys(path): - return int(open(path).readline().strip()) - - device_root = "/sys/class/block/%s" % self.name - if os.path.exists("%s/queue" % device_root): - sector_size = read_int_from_sys("%s/queue/logical_block_size" - % device_root) - size = read_int_from_sys("%s/size" % device_root) - self._size = (size * sector_size) / (1024.0 * 1024.0) - - def __str__(self): - exist = "existing" - if not self.exists: - exist = "non-existent" - s = "%s %dMB %s" % (exist, self.size, super(StorageDevice, self).__str__()) - if self.format.type: - s += " with %s" % self.format - - return s - - @property - def packages(self): - """ List of packages required to manage devices of this type. - - This list includes the packages required by this device's - format type as well those required by all of its parent - devices. - """ - packages = super(StorageDevice, self).packages - packages.extend(self.format.packages) - for parent in self.parents: - for package in parent.format.packages: - if package not in packages: - packages.append(package) - - return packages - - @property - def services(self): - """ List of services required to manage devices of this type. - - This list includes the services required by this device's - format type as well those required by all of its parent - devices. - """ - services = super(StorageDevice, self).services - services.extend(self.format.services) - for parent in self.parents: - for service in parent.format.services: - if service not in services: - services.append(service) - - return services - - @property - def disks(self): - """ A list of all disks this device depends on, including itself. """ - _disks = [] - for parent in self.parents: - for disk in parent.disks: - if disk not in _disks: - _disks.append(disk) - - if self.isDisk and not self.format.hidden: - _disks.append(self) - - return _disks - - @property - def encrypted(self): - """ True if this device, or any it requires, is encrypted. """ - crypted = False - for parent in self.parents: - if parent.encrypted: - crypted = True - break - - if not crypted and isinstance(self, DMCryptDevice): - crypted = True - - return crypted - - @property - def partedDevice(self): - if self.exists and self.status and not self._partedDevice: - log.debug("looking up parted Device: %s" % self.path) - - # We aren't guaranteed to be able to get a device. In - # particular, built-in USB flash readers show up as devices but - # do not always have any media present, so parted won't be able - # to find a device. - try: - self._partedDevice = parted.Device(path=self.path) - except (_ped.IOException, _ped.DeviceException): - pass - - return self._partedDevice - - def _getTargetSize(self): - return self._targetSize - - def _setTargetSize(self, newsize): - self._targetSize = newsize - - targetSize = property(lambda s: s._getTargetSize(), - lambda s, v: s._setTargetSize(v), - doc="Target size of this device") - - def __repr__(self): - s = Device.__repr__(self) - s += (" uuid = %(uuid)s size = %(size)s\n" - " format = %(format)s\n" - " major = %(major)s minor = %(minor)s exists = %(exists)s" - " protected = %(protected)s\n" - " sysfs path = %(sysfs)s partedDevice = %(partedDevice)s\n" - " target size = %(targetSize)s path = %(path)s\n" - " format args = %(formatArgs)s originalFormat = %(origFmt)s" % - {"uuid": self.uuid, "format": self.format, "size": self.size, - "major": self.major, "minor": self.minor, "exists": self.exists, - "sysfs": self.sysfsPath, "partedDevice": self.partedDevice, - "targetSize": self.targetSize, "path": self.path, - "protected": self.protected, - "formatArgs": self.formatArgs, "origFmt": self.originalFormat.type}) - return s - - @property - def dict(self): - d = super(StorageDevice, self).dict - d.update({"uuid": self.uuid, "size": self.size, - "format": self.format.dict, "removable": self.removable, - "major": self.major, "minor": self.minor, - "exists": self.exists, "sysfs": self.sysfsPath, - "targetSize": self.targetSize, "path": self.path}) - return d - - @property - def path(self): - """ Device node representing this device. """ - return "%s/%s" % (self._devDir, self.name) - - def updateSysfsPath(self): - """ Update this device's sysfs path. """ - log_method_call(self, self.name, status=self.status) - sysfsName = self.name.replace("/", "!") - path = os.path.join("/sys", self.sysfsBlockDir, sysfsName) - self.sysfsPath = os.path.realpath(path)[4:] - log.debug("%s sysfsPath set to %s" % (self.name, self.sysfsPath)) - - @property - def formatArgs(self): - """ Device-specific arguments to format creation program. """ - return [] - - @property - def resizable(self): - """ Can this type of device be resized? """ - return (self._resizable and self.exists and self.format.resizable) - - def notifyKernel(self): - """ Send a 'change' uevent to the kernel for this device. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - log.debug("not sending change uevent for non-existent device") - return - - if not self.status: - log.debug("not sending change uevent for inactive device") - return - - path = os.path.normpath("/sys/%s" % self.sysfsPath) - try: - util.notify_kernel(path, action="change") - except (ValueError, IOError) as e: - log.warning("failed to notify kernel of change: %s" % e) - - @property - def fstabSpec(self): - spec = self.path - if self.format and self.format.uuid: - spec = "UUID=%s" % self.format.uuid - return spec - - def resize(self): - """ Resize the device. - - New size should already be set. - """ - raise NotImplementedError("resize method not defined for StorageDevice") - - # - # setup - # - def _preSetup(self, orig=False): - """ Preparation and pre-condition checking for device setup. - - Return True if setup should proceed or False if not. - """ - if not self.exists: - raise DeviceError("device has not been created", self.name) - - if self.status or not self.controllable: - return False - - self.setupParents(orig=orig) - return True - - def _setup(self, orig=False): - """ Perform device-specific setup operations. """ - pass - - def setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - if not self._preSetup(orig=orig): - return - - self._setup(orig=orig) - self._postSetup() - - def _postSetup(self): - """ Perform post-setup operations. """ - udev_settle() - # we always probe since the device may not be set up when we want - # information about it - self._size = self.currentSize - - # - # teardown - # - def _preTeardown(self, recursive=None): - """ Preparation and pre-condition checking for device teardown. - - Return True if teardown should proceed or False if not. - """ - if not self.exists and not recursive: - raise DeviceError("device has not been created", self.name) - - if not self.status or not self.controllable: - return False - - if self.originalFormat.exists: - self.originalFormat.teardown() - self.format.cacheMajorminor() - if self.format.exists: - self.format.teardown() - udev_settle() - return True - - def _teardown(self, recursive=None): - """ Perform device-specific teardown operations. """ - pass - - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - if not self._preTeardown(recursive=recursive): - return - - self._teardown(recursive=recursive) - self._postTeardown(recursive=recursive) - - def _postTeardown(self, recursive=None): - """ Perform post-teardown operations. """ - if recursive: - self.teardownParents(recursive=recursive) - - # - # create - # - def _preCreate(self): - """ Preparation and pre-condition checking for device creation. """ - if self.exists: - raise DeviceError("device has already been created", self.name) - - self.setupParents() - - def _create(self): - """ Perform device-specific create operations. """ - pass - - def create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - self._preCreate() - try: - self._create() - except Exception as e: - raise DeviceCreateError(str(e), self.name) - else: - self._postCreate() - - def _postCreate(self): - """ Perform post-create operations. """ - self.exists = True - self.setup() - self.updateSysfsPath() - udev_settle() - - # - # destroy - # - def _preDestroy(self): - """ Preparation and precondition checking for device destruction. """ - if not self.exists: - raise DeviceError("device has not been created", self.name) - - if not self.isleaf: - raise DeviceError("Cannot destroy non-leaf device", self.name) - - self.teardown() - - def _destroy(self): - """ Perform device-specific destruction operations. """ - pass - - def destroy(self): - """ Destroy the device. """ - log_method_call(self, self.name, status=self.status) - self._preDestroy() - self._destroy() - self._postDestroy() - - def _postDestroy(self): - """ Perform post-destruction operations. """ - self.exists = False - - def setupParents(self, orig=False): - """ Run setup method of all parent devices. """ - log_method_call(self, name=self.name, orig=orig, kids=self.kids) - for parent in self.parents: - parent.setup(orig=orig) - if orig: - _format = parent.originalFormat - else: - _format = parent.format - - # set up the formatting, if present - if _format.type and _format.exists: - _format.setup() - - def _getSize(self): - """ Get the device's size in MB, accounting for pending changes. """ - if self.exists and not self.mediaPresent: - return 0 - - if self.exists and self.partedDevice: - self._size = self.currentSize - - size = self._size - if self.exists and self.resizable and self.targetSize != size: - size = self.targetSize - - return size - - def _setSize(self, newsize): - """ Set the device's size to a new value. """ - if newsize > self.maxSize: - raise DeviceError("device cannot be larger than %s MB" % - (self.maxSize,), self.name) - self._size = newsize - - size = property(lambda x: x._getSize(), - lambda x, y: x._setSize(y), - doc="The device's size in MB, accounting for pending changes") - - @property - def currentSize(self): - """ The device's actual size. """ - size = 0 - if self.exists and self.partedDevice: - size = self.partedDevice.getSize() - elif self.exists: - size = self._size - return size - - @property - def minSize(self): - """ The minimum size this device can be. """ - if self.format.minSize: - return self.format.minSize - else: - return self.size - - @property - def maxSize(self): - """ The maximum size this device can be. """ - if self.format.maxSize > self.currentSize: - return self.currentSize - else: - return self.format.maxSize - - @property - def status(self): - """ This device's status. - - For now, this should return a boolean: - True the device is open and ready for use - False the device is not open - """ - if not self.exists: - return False - return os.access(self.path, os.W_OK) - - def _setFormat(self, format): - """ Set the Device's format. """ - if not format: - format = getFormat(None, device=self.path, exists=self.exists) - log_method_call(self, self.name, type=format.type, - current=getattr(self._format, "type", None)) - if self._format and self._format.status: - # FIXME: self.format.status doesn't mean much - raise DeviceError("cannot replace active format", self.name) - - self._format = format - self._format.device = self.path - - def _getFormat(self): - return self._format - - format = property(lambda d: d._getFormat(), - lambda d,f: d._setFormat(f), - doc="The device's formatting.") - - def preCommitFixup(self, *args, **kwargs): - """ Do any necessary pre-commit fixups.""" - pass - - @property - def removable(self): - devpath = os.path.normpath("/sys/%s" % self.sysfsPath) - remfile = os.path.normpath("%s/removable" % devpath) - return (self.sysfsPath and os.path.exists(devpath) and - os.access(remfile, os.R_OK) and - open(remfile).readline().strip() == "1") - - @property - def isDisk(self): - return self._isDisk - - @property - def partitionable(self): - return self._partitionable - - @property - def partitioned(self): - return self.format.type == "disklabel" and self.partitionable - - @property - def serial(self): - return self._serial - - @property - def model(self): - if not self._model: - self._model = getattr(self.partedDevice, "model", "") - return self._model - - @property - def vendor(self): - return self._vendor - - @property - def growable(self): - """ True if this device or it's component devices are growable. """ - grow = getattr(self, "req_grow", False) - if not grow: - for parent in self.parents: - grow = parent.growable - if grow: - break - return grow - - def checkSize(self): - """ Check to make sure the size of the device is allowed by the - format used. - - Returns: - 0 - ok - 1 - Too large - -1 - Too small - """ - if self.format.maxSize and self.size > self.format.maxSize: - return 1 - elif self.format.minSize and self.size < self.format.minSize: - return -1 - return 0 - -class DiskDevice(StorageDevice): - """ A disk """ - _type = "disk" - _partitionable = True - _isDisk = True - - def __init__(self, name, format=None, - size=None, major=None, minor=None, sysfsPath='', - parents=None, serial=None, vendor="", model="", bus="", - exists=True): - """ Create a DiskDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - size -- the device's size (units/format TBD) - major -- the device major - minor -- the device minor - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - removable -- whether or not this is a removable device - serial -- the ID_SERIAL_SHORT for this device - vendor -- the manufacturer of this Device - model -- manufacturer's device model string - bus -- the interconnect this device uses - - - DiskDevices always exist. - """ - StorageDevice.__init__(self, name, format=format, size=size, - major=major, minor=minor, exists=exists, - sysfsPath=sysfsPath, parents=parents, - serial=serial, model=model, - vendor=vendor, bus=bus) - - def __repr__(self): - s = StorageDevice.__repr__(self) - s += (" removable = %(removable)s partedDevice = %(partedDevice)r" % - {"removable": self.removable, "partedDevice": self.partedDevice}) - return s - - @property - def mediaPresent(self): - if flags.testing: - return True - - if not self.partedDevice: - return False - - # Some drivers (cpqarray <blegh>) make block device nodes for - # controllers with no disks attached and then report a 0 size, - # treat this as no media present - return self.partedDevice.getSize() != 0 - - @property - def description(self): - return self.model - - @property - def size(self): - """ The disk's size in MB """ - return super(DiskDevice, self).size - #size = property(StorageDevice._getSize) - - def _preDestroy(self): - """ Destroy the device. """ - log_method_call(self, self.name, status=self.status) - if not self.mediaPresent: - raise DeviceError("cannot destroy disk with no media", self.name) - - StorageDevice._preDestroy(self) - - -class PartitionDevice(StorageDevice): - """ A disk partition. - - On types and flags... - - We don't need to deal with numerical partition types at all. The - only type we are concerned with is primary/logical/extended. Usage - specification is accomplished through the use of flags, which we - will set according to the partition's format. - """ - _type = "partition" - _resizable = True - defaultSize = 500 - - def __init__(self, name, format=None, - size=None, grow=False, maxsize=None, - major=None, minor=None, bootable=None, - sysfsPath='', parents=None, exists=False, - partType=None, primary=False, weight=0): - """ Create a PartitionDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - exists -- indicates whether this is an existing device - format -- the device's format (DeviceFormat instance) - - For existing partitions: - - parents -- the disk that contains this partition - major -- the device major - minor -- the device minor - sysfsPath -- sysfs device path - - For new partitions: - - partType -- primary,extended,&c (as parted constant) - grow -- whether or not to grow the partition - maxsize -- max size for growable partitions (in MB) - size -- the device's size (in MB) - bootable -- whether the partition is bootable - parents -- a list of potential containing disks - weight -- an initial sorting weight to assign - """ - self.req_disks = [] - self.req_partType = None - self.req_primary = None - self.req_grow = None - self.req_bootable = None - self.req_size = 0 - self.req_base_size = 0 - self.req_max_size = 0 - self.req_base_weight = 0 - - self._bootable = False - - StorageDevice.__init__(self, name, format=format, size=size, - major=major, minor=minor, exists=exists, - sysfsPath=sysfsPath, parents=parents) - - if not exists: - # this is a request, not a partition -- it has no parents - self.req_disks = self.parents[:] - for dev in self.parents: - dev.removeChild() - self.parents = [] - - # FIXME: Validate partType, but only if this is a new partition - # Otherwise, overwrite it with the partition's type. - self._partType = None - self.partedFlags = {} - self._partedPartition = None - self._origPath = None - self._currentSize = 0 - - # FIXME: Validate size, but only if this is a new partition. - # For existing partitions we will get the size from - # parted. - - if self.exists and not flags.testing: - log.debug("looking up parted Partition: %s" % self.path) - self._partedPartition = self.disk.format.partedDisk.getPartitionByPath(self.path) - if not self._partedPartition: - raise DeviceError("cannot find parted partition instance", self.name) - - self._origPath = self.path - # collect information about the partition from parted - self.probe() - if self.getFlag(parted.PARTITION_PREP): - # the only way to identify a PPC PReP Boot partition is to - # check the partition type/flags, so do it here. - self.format = getFormat("prepboot", device=self.path, exists=True) - elif self.getFlag(parted.PARTITION_BIOS_GRUB): - # the only way to identify a BIOS Boot partition is to - # check the partition type/flags, so do it here. - self.format = getFormat("biosboot", device=self.path, exists=True) - else: - # XXX It might be worthwhile to create a shit-simple - # PartitionRequest class and pass one to this constructor - # for new partitions. - if not self._size: - # default size for new partition requests - self._size = self.defaultSize - self.req_name = name - self.req_partType = partType - self.req_primary = primary - self.req_max_size = util.numeric_type(maxsize) - self.req_grow = grow - self.req_bootable = bootable - - # req_size may be manipulated in the course of partitioning - self.req_size = self._size - - # req_base_size will always remain constant - self.req_base_size = self._size - - self.req_base_weight = weight - - def __repr__(self): - s = StorageDevice.__repr__(self) - s += (" grow = %(grow)s max size = %(maxsize)s bootable = %(bootable)s\n" - " part type = %(partType)s primary = %(primary)s\n" - " partedPartition = %(partedPart)s\n" - " disk = %(disk)s\n" % - {"grow": self.req_grow, "maxsize": self.req_max_size, - "bootable": self.bootable, "partType": self.partType, - "primary": self.req_primary, - "partedPart": self.partedPartition, "disk": self.disk}) - - if self.partedPartition: - s += (" start = %(start)s end = %(end)s length = %(length)s\n" - " flags = %(flags)s" % - {"length": self.partedPartition.geometry.length, - "start": self.partedPartition.geometry.start, - "end": self.partedPartition.geometry.end, - "flags": self.partedPartition.getFlagsAsString()}) - - return s - - @property - def dict(self): - d = super(PartitionDevice, self).dict - d.update({"type": self.partType}) - if not self.exists: - d.update({"grow": self.req_grow, "maxsize": self.req_max_size, - "bootable": self.bootable, - "primary": self.req_primary}) - - if self.partedPartition: - d.update({"length": self.partedPartition.geometry.length, - "start": self.partedPartition.geometry.start, - "end": self.partedPartition.geometry.end, - "flags": self.partedPartition.getFlagsAsString()}) - return d - - def _setTargetSize(self, newsize): - if newsize != self.currentSize: - # change this partition's geometry in-memory so that other - # partitioning operations can complete (e.g., autopart) - self._targetSize = newsize - disk = self.disk.format.partedDisk - - # resize the partition's geometry in memory - (constraint, geometry) = self._computeResize(self.partedPartition) - disk.setPartitionGeometry(partition=self.partedPartition, - constraint=constraint, - start=geometry.start, end=geometry.end) - - @property - def path(self): - if not self.parents: - devDir = StorageDevice._devDir - else: - devDir = self.parents[0]._devDir - - return "%s/%s" % (devDir, self.name) - - @property - def partType(self): - """ Get the partition's type (as parted constant). """ - try: - ptype = self.partedPartition.type - except AttributeError: - ptype = self._partType - - if not self.exists and ptype is None: - ptype = self.req_partType - - return ptype - - @property - def isExtended(self): - return (self.partType is not None and - self.partType & parted.PARTITION_EXTENDED) - - @property - def isLogical(self): - return (self.partType is not None and - self.partType & parted.PARTITION_LOGICAL) - - @property - def isPrimary(self): - return (self.partType is not None and - self.partType == parted.PARTITION_NORMAL) - - @property - def isProtected(self): - return (self.partType is not None and - self.partType & parted.PARTITION_PROTECTED) - - @property - def fstabSpec(self): - spec = self.path - if self.disk and self.disk.type == 'dasd': - spec = deviceNameToDiskByPath(self.name) - elif self.format and self.format.uuid: - spec = "UUID=%s" % self.format.uuid - return spec - - def _getPartedPartition(self): - return self._partedPartition - - def _setPartedPartition(self, partition): - """ Set this PartitionDevice's parted Partition instance. """ - log_method_call(self, self.name) - if partition is None: - path = None - elif isinstance(partition, parted.Partition): - path = partition.path - else: - raise ValueError("partition must be a parted.Partition instance") - - log.debug("device %s new partedPartition %s" % (self.name, partition)) - self._partedPartition = partition - self.updateName() - - partedPartition = property(lambda d: d._getPartedPartition(), - lambda d,p: d._setPartedPartition(p)) - - def preCommitFixup(self, *args, **kwargs): - """ Re-get self.partedPartition from the original disklabel. """ - log_method_call(self, self.name) - if not self.exists: - return - - # find the correct partition on the original parted.Disk since the - # name/number we're now using may no longer match - _disklabel = self.disk.originalFormat - - if self.isExtended: - # getPartitionBySector doesn't work on extended partitions - _partition = _disklabel.extendedPartition - log.debug("extended lookup found partition %s" - % devicePathToName(getattr(_partition, "path", None))) - else: - # lookup the partition by sector to avoid the renumbering - # nonsense entirely - _sector = self.partedPartition.geometry.start - _partition = _disklabel.partedDisk.getPartitionBySector(_sector) - log.debug("sector-based lookup found partition %s" - % devicePathToName(getattr(_partition, "path", None))) - - self.partedPartition = _partition - - def _getWeight(self): - return self.req_base_weight - - def _setWeight(self, weight): - self.req_base_weight = weight - - weight = property(lambda d: d._getWeight(), - lambda d,w: d._setWeight(w)) - - def updateSysfsPath(self): - """ Update this device's sysfs path. """ - log_method_call(self, self.name, status=self.status) - if not self.parents: - self.sysfsPath = '' - - elif isinstance(self.parents[0], DMDevice): - dm_node = dm.dm_node_from_name(self.name) - path = os.path.join("/sys", self.sysfsBlockDir, dm_node) - self.sysfsPath = os.path.realpath(path)[4:] - elif isinstance(self.parents[0], MDRaidArrayDevice): - md_node = mdraid.md_node_from_name(self.name) - path = os.path.join("/sys", self.sysfsBlockDir, md_node) - self.sysfsPath = os.path.realpath(path)[4:] - else: - StorageDevice.updateSysfsPath(self) - - def updateName(self): - if self.partedPartition is None: - self._name = self.req_name - else: - self._name = \ - devicePathToName(self.partedPartition.getDeviceNodeName()) - - def dependsOn(self, dep): - """ Return True if this device depends on dep. """ - if isinstance(dep, PartitionDevice) and dep.isExtended and \ - self.isLogical and self.disk == dep.disk: - return True - - return Device.dependsOn(self, dep) - - def _setFormat(self, format): - """ Set the Device's format. """ - log_method_call(self, self.name) - StorageDevice._setFormat(self, format) - - def _setBootable(self, bootable): - """ Set the bootable flag for this partition. """ - if self.partedPartition: - if arch.isS390(): - return - if self.flagAvailable(parted.PARTITION_BOOT): - if bootable: - self.setFlag(parted.PARTITION_BOOT) - else: - self.unsetFlag(parted.PARTITION_BOOT) - else: - raise DeviceError("boot flag not available for this partition", self.name) - - self._bootable = bootable - else: - self.req_bootable = bootable - - def _getBootable(self): - return self._bootable or self.req_bootable - - bootable = property(_getBootable, _setBootable) - - def flagAvailable(self, flag): - if not self.partedPartition: - return - - return self.partedPartition.isFlagAvailable(flag) - - def getFlag(self, flag): - log_method_call(self, path=self.path, flag=flag) - if not self.partedPartition or not self.flagAvailable(flag): - return - - return self.partedPartition.getFlag(flag) - - def setFlag(self, flag): - log_method_call(self, path=self.path, flag=flag) - if not self.partedPartition or not self.flagAvailable(flag): - return - - self.partedPartition.setFlag(flag) - - def unsetFlag(self, flag): - log_method_call(self, path=self.path, flag=flag) - if not self.partedPartition or not self.flagAvailable(flag): - return - - self.partedPartition.unsetFlag(flag) - - @property - def isMagic(self): - if not self.disk: - return False - - number = getattr(self.partedPartition, "number", -1) - magic = self.disk.format.magicPartitionNumber - return (number == magic) - - def probe(self): - """ Probe for any missing information about this device. - - size, partition type, flags - """ - log_method_call(self, self.name, exists=self.exists) - if not self.exists: - return - - # this is in MB - self._size = self.partedPartition.getSize() - self._currentSize = self._size - self.targetSize = self._size - - self._partType = self.partedPartition.type - - self._bootable = self.getFlag(parted.PARTITION_BOOT) - - def _create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - self.disk.format.addPartition(self.partedPartition) - - try: - self.disk.format.commit() - except DiskLabelCommitError: - part = self.disk.format.partedDisk.getPartitionByPath(self.path) - self.disk.format.removePartition(part) - raise - - def _postCreate(self): - if self.isExtended: - partition = self.disk.format.extendedPartition - else: - start = self.partedPartition.geometry.start - partition = self.disk.format.partedDisk.getPartitionBySector(start) - - log.debug("post-commit partition path is %s" % getattr(partition, - "path", None)) - self.partedPartition = partition - if not self.isExtended: - # Ensure old metadata which lived in freespace so did not get - # explictly destroyed by a destroyformat action gets wiped - DeviceFormat(device=self.path, exists=True).destroy() - - StorageDevice._postCreate(self) - self._currentSize = self.partedPartition.getSize() - - def _computeResize(self, partition): - log_method_call(self, self.name, status=self.status) - - # compute new size for partition - currentGeom = partition.geometry - currentDev = currentGeom.device - newLen = long(self.targetSize * 1024 * 1024) / currentDev.sectorSize - newGeometry = parted.Geometry(device=currentDev, - start=currentGeom.start, - length=newLen) - # and align the end sector - newGeometry.end = self.disk.format.endAlignment.alignDown(newGeometry, - newGeometry.end) - constraint = parted.Constraint(exactGeom=newGeometry) - - return (constraint, newGeometry) - - def resize(self): - """ Resize the device. - - self.targetSize must be set to the new size. - """ - log_method_call(self, self.name, status=self.status) - self._preDestroy() - if self.targetSize != self.currentSize: - # partedDisk has been restored to _origPartedDisk, so - # recalculate resize geometry because we may have new - # partitions on the disk, which could change constraints - partedDisk = self.disk.format.partedDisk - partition = partedDisk.getPartitionByPath(self.path) - (constraint, geometry) = self._computeResize(partition) - - partedDisk.setPartitionGeometry(partition=partition, - constraint=constraint, - start=geometry.start, - end=geometry.end) - - self.disk.format.commit() - self._currentSize = partition.getSize() - - def _preDestroy(self): - StorageDevice._preDestroy(self) - if not self.sysfsPath: - return - - self.setupParents(orig=True) - - def _destroy(self): - """ Destroy the device. """ - log_method_call(self, self.name, status=self.status) - # we should have already set self.partedPartition to point to the - # partition on the original disklabel - self.disk.originalFormat.removePartition(self.partedPartition) - try: - self.disk.originalFormat.commit() - except DiskLabelCommitError: - self.disk.originalFormat.addPartition(self.partedPartition) - self.partedPartition = self.disk.originalFormat.partedDisk.getPartitionByPath(self.path) - raise - - if self.disk.format.exists and \ - self.disk.format.partedDisk != self.disk.originalFormat.partedDisk: - # If the new/current disklabel is the same as the original one, we - # have to duplicate the removal on the other copy of the DiskLabel. - part = self.disk.format.partedDisk.getPartitionByPath(self.path) - self.disk.format.removePartition(part) - self.disk.format.commit() - - def deactivate(self): - """ - This is never called. For instructional purposes only. - - We do not want multipath partitions disappearing upon their teardown(). - """ - if self.parents[0].type == 'dm-multipath': - devmap = block.getMap(major=self.major, minor=self.minor) - if devmap: - try: - block.removeDeviceMap(devmap) - except Exception as e: - raise DeviceTeardownError("failed to tear down device-mapper partition %s: %s" % (self.name, e)) - udev_settle() - - def _getSize(self): - """ Get the device's size. """ - size = self._size - if self.partedPartition: - # this defaults to MB - size = self.partedPartition.getSize() - return size - - def _setSize(self, newsize): - """ Set the device's size (for resize, not creation). - - Arguments: - - newsize -- the new size (in MB) - - """ - log_method_call(self, self.name, - status=self.status, size=self._size, newsize=newsize) - if not self.exists: - raise DeviceError("device does not exist", self.name) - - if newsize > self.disk.size: - raise ValueError("partition size would exceed disk size") - - # this defaults to MB - maxAvailableSize = self.partedPartition.getMaxAvailableSize() - - if newsize > maxAvailableSize: - raise ValueError("new size is greater than available space") - - # now convert the size to sectors and update the geometry - geometry = self.partedPartition.geometry - physicalSectorSize = geometry.device.physicalSectorSize - - new_length = (newsize * (1024 * 1024)) / physicalSectorSize - geometry.length = new_length - - def _getDisk(self): - """ The disk that contains this partition.""" - try: - disk = self.parents[0] - except IndexError: - disk = None - return disk - - def _setDisk(self, disk): - """Change the parent. - - Setting up a disk is not trivial. It has the potential to change - the underlying object. If necessary we must also change this object. - """ - log_method_call(self, self.name, old=getattr(self.disk, "name", None), - new=getattr(disk, "name", None)) - if self.disk: - self.disk.removeChild() - - if disk: - self.parents = [disk] - disk.addChild() - else: - self.parents = [] - - disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d)) - - @property - def maxSize(self): - """ The maximum size this partition can be. """ - # XXX Only allow growth up to the amount of free space following this - # partition on disk. We don't care about leading free space -- - # a filesystem cannot be relocated, so if you want to use space - # before and after your partition, remove it and create a new one. - sector = self.partedPartition.geometry.end + 1 - maxPartSize = self.size - try: - partition = self.partedPartition.disk.getPartitionBySector(sector) - except _ped.PartitionException: - pass - else: - if partition.type == parted.PARTITION_FREESPACE: - maxPartSize = self.size + math.floor(partition.getSize()) - - return min(self.format.maxSize, maxPartSize) - - @property - def currentSize(self): - """ The device's actual size. """ - if self.exists: - return self._currentSize - else: - return 0 - - @property - def resizable(self): - """ Can this type of device be resized? """ - return super(PartitionDevice, self).resizable and \ - self.disk.type != 'dasd' - - def checkSize(self): - """ Check to make sure the size of the device is allowed by the - format used. - - Returns: - 0 - ok - 1 - Too large - -1 - Too small - """ - if self.format.maxSize and self.size > self.format.maxSize: - return 1 - elif (self.format.minSize and - (not self.req_grow and - self.size < self.format.minSize) or - (self.req_grow and self.req_max_size and - self.req_max_size < self.format.minSize)): - return -1 - return 0 - -class DMDevice(StorageDevice): - """ A device-mapper device """ - _type = "dm" - _devDir = "/dev/mapper" - - def __init__(self, name, format=None, size=None, dmUuid=None, - target=None, exists=False, parents=None, sysfsPath=''): - """ Create a DMDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - target -- the device-mapper target type (string) - size -- the device's size (units/format TBD) - dmUuid -- the device's device-mapper UUID - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - exists -- indicates whether this is an existing device - """ - StorageDevice.__init__(self, name, format=format, size=size, - exists=exists, - parents=parents, sysfsPath=sysfsPath) - self.target = target - self.dmUuid = dmUuid - - def __repr__(self): - s = StorageDevice.__repr__(self) - s += (" target = %(target)s dmUuid = %(dmUuid)s" % - {"target": self.target, "dmUuid": self.dmUuid}) - return s - - @property - def dict(self): - d = super(DMDevice, self).dict - d.update({"target": self.target, "dmUuid": self.dmUuid}) - return d - - @property - def fstabSpec(self): - """ Return the device specifier for use in /etc/fstab. """ - return self.path - - @property - def mapName(self): - """ This device's device-mapper map name """ - return self.name - - @property - def status(self): - _status = False - for map in block.dm.maps(): - if map.name == self.mapName: - _status = map.live_table and not map.suspended - break - - return _status - - def updateSysfsPath(self): - """ Update this device's sysfs path. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - if self.status: - dm_node = self.getDMNode() - path = os.path.join("/sys", self.sysfsBlockDir, dm_node) - self.sysfsPath = os.path.realpath(path)[4:] - else: - self.sysfsPath = '' - - #def getTargetType(self): - # return dm.getDmTarget(name=self.name) - - def getDMNode(self): - """ Return the dm-X (eg: dm-0) device node for this device. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - return dm.dm_node_from_name(self.name) - - def setupPartitions(self): - log_method_call(self, name=self.name, kids=self.kids) - rc = util.run_program(["kpartx", "-a", "-s", self.path]) - if rc: - raise DMError("partition activation failed for '%s'" % self.name) - udev_settle() - - def teardownPartitions(self): - log_method_call(self, name=self.name, kids=self.kids) - rc = util.run_program(["kpartx", "-d", "-s", self.path]) - if rc: - raise DMError("partition deactivation failed for '%s'" % self.name) - udev_settle() - - def _setName(self, name): - """ Set the device's map name. """ - log_method_call(self, self.name, status=self.status) - if self.status: - raise DeviceError("cannot rename active device", self.name) - - self._name = name - #self.sysfsPath = "/dev/disk/by-id/dm-name-%s" % self.name - - name = property(lambda d: d._name, - lambda d,n: d._setName(n)) - - @property - def slave(self): - """ This device's backing device. """ - return self.parents[0] - - -class DMLinearDevice(DMDevice): - _type = "dm-linear" - _partitionable = True - _isDisk = True - - def __init__(self, name, format=None, size=None, dmUuid=None, - exists=False, parents=None, sysfsPath=''): - """ Create a DMLinearDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - size -- the device's size (units/format TBD) - dmUuid -- the device's device-mapper UUID - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - exists -- indicates whether this is an existing device - """ - if not parents: - raise ValueError("DMLinearDevice requires a backing block device") - - DMDevice.__init__(self, name, format=format, size=size, - parents=parents, sysfsPath=sysfsPath, - exists=exists, target="linear", dmUuid=dmUuid) - - def _setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - slave_length = self.slave.partedDevice.length - dm.dm_create_linear(self.name, self.slave.path, slave_length, - self.dmUuid) - - def _postSetup(self): - StorageDevice._postSetup(self) - self.setupPartitions() - udev_settle() - - def _teardown(self, recursive=False): - self.teardownPartitions() - udev_settle() - dm.dm_remove(self.name) - udev_settle() - - def deactivate(self, recursive=False): - StorageDevice.teardown(self, recursive=recursive) - - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - if not self._preTeardown(recursive=recursive): - return - - log.debug("not tearing down dm-linear device %s" % self.name) - - @property - def description(self): - return self.model - - -class DMCryptDevice(DMDevice): - """ A dm-crypt device """ - _type = "dm-crypt" - - def __init__(self, name, format=None, size=None, uuid=None, - exists=False, sysfsPath='', parents=None): - """ Create a DMCryptDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - size -- the device's size (units/format TBD) - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - exists -- indicates whether this is an existing device - """ - DMDevice.__init__(self, name, format=format, size=size, - parents=parents, sysfsPath=sysfsPath, - exists=exists, target="crypt") - -class LUKSDevice(DMCryptDevice): - """ A mapped LUKS device. """ - _type = "luks/dm-crypt" - _packages = ["cryptsetup-luks"] - - def __init__(self, name, format=None, size=None, uuid=None, - exists=False, sysfsPath='', parents=None): - """ Create a LUKSDevice instance. - - Arguments: - - name -- the device name - - Keyword Arguments: - - size -- the device's size in MB - uuid -- the device's UUID - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - exists -- indicates whether this is an existing device - """ - DMCryptDevice.__init__(self, name, format=format, size=size, - parents=parents, sysfsPath=sysfsPath, - uuid=None, exists=exists) - - @property - def size(self): - if not self.exists or not self.partedDevice: - size = float(self.slave.size) - crypto.LUKS_METADATA_SIZE - else: - size = self.partedDevice.getSize() - return size - - def _postCreate(self): - self._name = self.slave.format.mapName - StorageDevice._postCreate(self) - - def _postTeardown(self, recursive=False): - if not recursive: - # this is handled by StorageDevice._postTeardown if recursive - # is True - self.teardownParents(recursive=recursive) - - StorageDevice._postTeardown(self, recursive=recursive) - - def dracutSetupArgs(self): - return set(["rd.luks.uuid=luks-%s" % self.slave.format.uuid]) - -class LVMVolumeGroupDevice(DMDevice): - """ An LVM Volume Group - - XXX Maybe this should inherit from StorageDevice instead of - DMDevice since there's no actual device. - """ - _type = "lvmvg" - _packages = ["lvm2"] - - def __init__(self, name, parents=None, size=None, free=None, - peSize=None, peCount=None, peFree=None, pvCount=None, - uuid=None, exists=False, sysfsPath=''): - """ Create a LVMVolumeGroupDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - parents -- a list of physical volumes (StorageDevice) - - Keyword Arguments: - - peSize -- physical extent size (in MB) - exists -- indicates whether this is an existing device - sysfsPath -- sysfs device path - - For existing VG's only: - - size -- the VG's size (in MB) - free -- amount of free space in the VG - peFree -- number of free extents - peCount -- total number of extents - pvCount -- number of PVs in this VG - uuid -- the VG's UUID - - """ - self.pvClass = get_device_format_class("lvmpv") - if not self.pvClass: - raise StorageError("cannot find 'lvmpv' class") - - if isinstance(parents, list): - for dev in parents: - if not isinstance(dev.format, self.pvClass): - raise ValueError("constructor requires a list of PVs") - elif not isinstance(parents.format, self.pvClass): - raise ValueError("constructor requires a list of PVs") - - DMDevice.__init__(self, name, parents=parents, - exists=exists, sysfsPath=sysfsPath) - - self.uuid = uuid - self.free = util.numeric_type(free) - self.peSize = util.numeric_type(peSize) - self.peCount = util.numeric_type(peCount) - self.peFree = util.numeric_type(peFree) - self.pvCount = util.numeric_type(pvCount) - self.lv_names = [] - self.lv_uuids = [] - self.lv_sizes = [] - self.lv_attr = [] - self.hasDuplicate = False - self.reserved_percent = 0 - self.reserved_space = 0 - - # circular references, here I come - self._lvs = [] - - # TODO: validate peSize if given - if not self.peSize: - self.peSize = lvm.LVM_PE_SIZE # MB - - if not self.exists: - self.pvCount = len(self.parents) - - # Some snapshots don't have a proper LV as an origin (--vorigin). - # They still occupy space in the VG. - self.voriginSnapshots = {} - - def __repr__(self): - s = DMDevice.__repr__(self) - s += (" free = %(free)s PE Size = %(peSize)s PE Count = %(peCount)s\n" - " PE Free = %(peFree)s PV Count = %(pvCount)s\n" - " LV Names = %(lv_names)s modified = %(modified)s\n" - " extents = %(extents)s free space = %(freeSpace)s\n" - " free extents = %(freeExtents)s" - " reserved percent = %(rpct)s reserved space = %(res)s\n" - " PVs = %(pvs)s\n" - " LVs = %(lvs)s" % - {"free": self.free, "peSize": self.peSize, "peCount": self.peCount, - "peFree": self.peFree, "pvCount": self.pvCount, - "lv_names": self.lv_names, "modified": self.isModified, - "extents": self.extents, "freeSpace": self.freeSpace, - "freeExtents": self.freeExtents, - "rpct": self.reserved_percent, "res": self.reserved_space, - "pvs": pprint.pformat([str(p) for p in self.pvs]), - "lvs": pprint.pformat([str(l) for l in self.lvs])}) - return s - - @property - def dict(self): - d = super(LVMVolumeGroupDevice, self).dict - d.update({"free": self.free, "peSize": self.peSize, - "peCount": self.peCount, "peFree": self.peFree, - "pvCount": self.pvCount, "extents": self.extents, - "freeSpace": self.freeSpace, - "freeExtents": self.freeExtents, - "lv_names": self.lv_names, - "lv_uuids": self.lv_uuids, - "lv_sizes": self.lv_sizes, - "lv_attr": self.lv_attr, - "reserved_percent": self.reserved_percent, - "reserved_space": self.reserved_space, - "lvNames": [lv.name for lv in self.lvs]}) - return d - - @property - def mapName(self): - """ This device's device-mapper map name """ - # Thank you lvm for this lovely hack. - return self.name.replace("-","--") - - @property - def path(self): - """ Device node representing this device. """ - return "%s/%s" % (self._devDir, self.mapName) - - def updateSysfsPath(self): - """ Update this device's sysfs path. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - self.sysfsPath = '' - - @property - def status(self): - """ The device's status (True means active). """ - if not self.exists: - return False - - # certainly if any of this VG's LVs are active then so are we - for lv in self.lvs: - if lv.status: - return True - - # if any of our PVs are not active then we cannot be - for pv in self.pvs: - if not pv.status: - return False - - # if we are missing some of our PVs we cannot be active - if not self.complete: - return False - - return True - - def _addDevice(self, device): - """ Add a new physical volume device to the volume group. - - XXX This is for use by device probing routines and is not - intended for modification of the VG. - """ - log_method_call(self, - self.name, - device=device.name, - status=self.status) - if not self.exists: - raise DeviceError("device does not exist", self.name) - - if not isinstance(device.format, self.pvClass): - raise ValueError("addDevice requires a PV arg") - - if self.uuid and device.format.vgUuid != self.uuid: - # this means there is another vg with the same name on the system - # set hasDuplicate which will make complete return False - # and let devicetree._handleInconsistencies() further handle this. - # Note we still add the device to our parents for use by - # devicetree._handleInconsistencies() - self.hasDuplicate = True - - if device in self.pvs: - raise ValueError("device is already a member of this VG") - - self.parents.append(device) - device.addChild() - - # now see if the VG can be activated - if self.complete: - self.setup() - - def _removeDevice(self, device): - """ Remove a physical volume from the volume group. - - This is for cases like clearing of preexisting partitions. - """ - log_method_call(self, - self.name, - device=device.name, - status=self.status) - try: - self.parents.remove(device) - except ValueError: - raise ValueError("cannot remove non-member PV device from VG") - - device.removeChild() - - def _preSetup(self, orig=False): - if self.exists and not self.complete: - raise DeviceError("cannot activate VG with missing PV(s)", self.name) - return StorageDevice._preSetup(self, orig=orig) - - def _teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - lvm.vgdeactivate(self.name) - - def _create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - pv_list = [pv.path for pv in self.parents] - lvm.vgcreate(self.name, pv_list, self.peSize) - - def _preDestroy(self): - StorageDevice._preDestroy(self) - # set up the pvs since lvm needs access to them to do the vgremove - self.setupParents(orig=True) - - def _destroy(self): - """ Destroy the device. """ - log_method_call(self, self.name, status=self.status) - if not self.complete: - for pv in self.pvs: - # Remove the PVs from the ignore filter so we can wipe them. - lvm.lvm_cc_removeFilterRejectRegexp(pv.name) - - # Don't run vgremove or vgreduce since there may be another VG with - # the same name that we want to keep/use. - return - - lvm.vgreduce(self.name, [], rm=True) - lvm.vgdeactivate(self.name) - lvm.vgremove(self.name) - - def reduce(self, pv_list): - """ Remove the listed PVs from the VG. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - lvm.vgreduce(self.name, pv_list) - # XXX do we need to notify the kernel? - - def _addLogVol(self, lv): - """ Add an LV to this VG. """ - if lv in self._lvs: - raise ValueError("lv is already part of this vg") - - # verify we have the space, then add it - # do not verify for growing vg (because of ks) - if not lv.exists and not self.growable and lv.size > self.freeSpace: - raise DeviceError("new lv is too large to fit in free space", self.name) - - log.debug("Adding %s/%dMB to %s" % (lv.name, lv.size, self.name)) - self._lvs.append(lv) - - def _removeLogVol(self, lv): - """ Remove an LV from this VG. """ - if lv not in self.lvs: - raise ValueError("specified lv is not part of this vg") - - self._lvs.remove(lv) - - def _addPV(self, pv): - """ Add a PV to this VG. """ - if pv in self.pvs: - raise ValueError("pv is already part of this vg") - - # for the time being we will not allow vgextend - if self.exists: - raise DeviceError("cannot add pv to existing vg", self.name) - - self.parents.append(pv) - pv.addChild() - - # and update our pv count - self.pvCount = len(self.parents) - - def addMember(self, member): - self._addPV(member) - - def _removePV(self, pv): - """ Remove an PV from this VG. """ - if not pv in self.pvs: - raise ValueError("specified pv is not part of this vg") - - # for the time being we will not allow vgreduce - if self.exists: - raise DeviceError("cannot remove pv from existing vg", self.name) - - self.parents.remove(pv) - pv.removeChild() - - # and update our pv count - self.pvCount = len(self.parents) - - def removeMember(self, member): - self._removePV(member) - - # We can't rely on lvm to tell us about our size, free space, &c - # since we could have modifications queued, unless the VG and all of - # its PVs already exist. - # - # -- liblvm may contain support for in-memory devices - - @property - def isModified(self): - """ Return True if the VG has changes queued that LVM is unaware of. """ - modified = True - if self.exists and not filter(lambda d: not d.exists, self.pvs): - modified = False - - return modified - - @property - def snapshotSpace(self): - """ Total space used by snapshots in this volume group. """ - used = 0 - for lv in self.lvs: - used += self.align(lv.snapshotSpace, roundup=True) - - for (vname, vsize) in self.voriginSnapshots.items(): - used += self.align(vsize, roundup=True) - - return used - - @property - def reservedSpace(self): - """ Reserved space in this VG, in MB """ - reserved = 0 - if self.reserved_percent > 0: - reserved = self.reserved_percent * 0.01 * self.size - elif self.reserved_space > 0: - reserved = self.reserved_space - - return self.align(reserved, roundup=True) - - @property - def size(self): - """ The size of this VG """ - # TODO: just ask lvm if isModified returns False - - # sum up the sizes of the PVs and align to pesize - size = 0 - for pv in self.pvs: - size += max(0, self.align(pv.size - pv.format.peStart)) - - return size - - @property - def extents(self): - """ Number of extents in this VG """ - # TODO: just ask lvm if isModified returns False - - return self.size / self.peSize - - @property - def freeSpace(self): - """ The amount of free space in this VG (in MB). """ - # TODO: just ask lvm if isModified returns False - - # total the sizes of any LVs - log.debug("%s size is %dMB" % (self.name, self.size)) - used = sum(lv.vgSpaceUsed for lv in self.lvs) + self.snapshotSpace - used += self.reservedSpace - free = self.size - used - log.debug("vg %s has %dMB free" % (self.name, free)) - return free - - @property - def freeExtents(self): - """ The number of free extents in this VG. """ - # TODO: just ask lvm if isModified returns False - return self.freeSpace / self.peSize - - def align(self, size, roundup=None): - """ Align a size to a multiple of physical extent size. """ - size = util.numeric_type(size) - - if roundup: - round = math.ceil - else: - round = math.floor - - # we want Kbytes as a float for our math - size *= 1024.0 - pesize = self.peSize * 1024.0 - return long((round(size / pesize) * pesize) / 1024) - - @property - def pvs(self): - """ A list of this VG's PVs """ - return self.parents[:] # we don't want folks changing our list - - @property - def lvs(self): - """ A list of this VG's LVs """ - return self._lvs[:] # we don't want folks changing our list - - @property - def complete(self): - """Check if the vg has all its pvs in the system - Return True if complete. - """ - # vgs with duplicate names are overcomplete, which is not what we want - if self.hasDuplicate: - return False - - return len(self.pvs) == self.pvCount or not self.exists - - -class LVMLogicalVolumeDevice(DMDevice): - """ An LVM Logical Volume """ - _type = "lvmlv" - _resizable = True - _packages = ["lvm2"] - - def __init__(self, name, parents=None, size=None, uuid=None, - stripes=1, logSize=0, snapshotSpace=0, - format=None, exists=False, sysfsPath='', - grow=None, maxsize=None, percent=None, - singlePV=False): - """ Create a LVMLogicalVolumeDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - vgdev -- volume group (LVMVolumeGroupDevice instance) - - Keyword Arguments: - - size -- the device's size (in MB) - uuid -- the device's UUID - stripes -- number of copies in the vg (>1 for mirrored lvs) - logSize -- size of log volume (for mirrored lvs) - snapshotSpace -- sum of sizes of snapshots of this lv - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - exists -- indicates whether this is an existing device - singlePV -- if true, maps this lv to a single pv - - For new (non-existent) LVs only: - - grow -- whether to grow this LV - maxsize -- maximum size for growable LV (in MB) - percent -- percent of VG space to take - - """ - if isinstance(parents, list): - if len(parents) != 1: - raise ValueError("constructor requires a single LVMVolumeGroupDevice instance") - elif not isinstance(parents[0], LVMVolumeGroupDevice): - raise ValueError("constructor requires a LVMVolumeGroupDevice instance") - elif not isinstance(parents, LVMVolumeGroupDevice): - raise ValueError("constructor requires a LVMVolumeGroupDevice instance") - DMDevice.__init__(self, name, size=size, format=format, - sysfsPath=sysfsPath, parents=parents, - exists=exists) - - self.singlePVerr = ("%(mountpoint)s is restricted to a single " - "physical volume on this platform. No physical " - "volumes available in volume group %(vgname)s " - "with %(size)d MB of available space." % - {'mountpoint': getattr(self.format, "mountpoint", - "A proposed logical volume"), - 'vgname': self.vg.name, - 'size': self.size}) - - self.uuid = uuid - self.snapshotSpace = snapshotSpace - self.stripes = stripes - self.logSize = logSize - self.singlePV = singlePV - - self.req_grow = None - self.req_max_size = 0 - self.req_size = 0 - self.req_percent = 0 - - if not self.exists: - self.req_grow = grow - self.req_max_size = util.numeric_type(maxsize) - # XXX should we enforce that req_size be pe-aligned? - self.req_size = self._size - self.req_percent = util.numeric_type(percent) - - if self.singlePV: - # make sure there is at least one PV that can hold this LV - validpvs = filter(lambda x: float(x.size) >= self.req_size, - self.vg.pvs) - if not validpvs: - for dev in self.parents: - dev.removeChild() - raise SinglePhysicalVolumeError(self.singlePVerr) - - # here we go with the circular references - self.vg._addLogVol(self) - - def __repr__(self): - s = DMDevice.__repr__(self) - s += (" VG device = %(vgdev)r\n" - " percent = %(percent)s\n" - " mirrored = %(mirrored)s stripes = %(stripes)d" - " snapshot total = %(snapshots)dMB\n" - " VG space used = %(vgspace)dMB" % - {"vgdev": self.vg, "percent": self.req_percent, - "mirrored": self.mirrored, "stripes": self.stripes, - "snapshots": self.snapshotSpace, "vgspace": self.vgSpaceUsed }) - return s - - @property - def dict(self): - d = super(LVMLogicalVolumeDevice, self).dict - if self.exists: - d.update({"mirrored": self.mirrored, "stripes": self.stripes, - "snapshots": self.snapshotSpace, - "vgspace": self.vgSpaceUsed}) - else: - d.update({"percent": self.req_percent}) - - return d - - @property - def mirrored(self): - return self.stripes > 1 - - def _setSize(self, size): - size = self.vg.align(util.numeric_type(size)) - log.debug("trying to set lv %s size to %dMB" % (self.name, size)) - if size <= self.vg.freeSpace + self.vgSpaceUsed: - self._size = size - self.targetSize = size - else: - log.debug("failed to set size: %dMB short" % (size - (self.vg.freeSpace + self.vgSpaceUsed),)) - raise ValueError("not enough free space in volume group") - - size = property(StorageDevice._getSize, _setSize) - - @property - def vgSpaceUsed(self): - """ Space occupied by this LV, not including snapshots. """ - return (self.vg.align(self.size, roundup=True) * self.stripes - + self.logSize) - - @property - def vg(self): - """ This Logical Volume's Volume Group. """ - return self.parents[0] - - @property - def mapName(self): - """ This device's device-mapper map name """ - # Thank you lvm for this lovely hack. - return "%s-%s" % (self.vg.mapName, self._name.replace("-","--")) - - @property - def path(self): - """ Device node representing this device. """ - return "%s/%s" % (self._devDir, self.mapName) - - def getDMNode(self): - """ Return the dm-X (eg: dm-0) device node for this device. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - return dm.dm_node_from_name(self.mapName) - - @property - def name(self): - """ This device's name. """ - return "%s-%s" % (self.vg.name, self._name) - - @property - def lvname(self): - """ The LV's name (not including VG name). """ - return self._name - - @property - def complete(self): - """ Test if vg exits and if it has all pvs. """ - return self.vg.complete - - def setupParents(self, orig=False): - # parent is a vg, which has no formatting (or device for that matter) - Device.setupParents(self, orig=orig) - - def _setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - lvm.lvactivate(self.vg.name, self._name) - - def _teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - lvm.lvdeactivate(self.vg.name, self._name) - - def _postTeardown(self, recursive=False): - try: - # It's likely that teardown of a VG will fail due to other - # LVs being active (filesystems mounted, &c), so don't let - # it bring everything down. - StorageDevice._postTeardown(self, recursive=recursive) - except StorageError: - if recursive: - log.debug("vg %s teardown failed; continuing" % self.vg.name) - else: - raise - - def _create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - # should we use --zero for safety's sake? - if self.singlePV: - lvm.lvcreate(self.vg.name, self._name, self.size, - pvs=self._getSinglePV()) - else: - lvm.lvcreate(self.vg.name, self._name, self.size) - - def _preDestroy(self): - StorageDevice._preDestroy(self) - # set up the vg's pvs so lvm can remove the lv - self.vg.setupParents(orig=True) - - def _destroy(self): - """ Destroy the device. """ - log_method_call(self, self.name, status=self.status) - lvm.lvremove(self.vg.name, self._name) - - def _getSinglePV(self): - validpvs = filter(lambda x: float(x.size) >= self.size, self.vg.pvs) - - if not validpvs: - raise SinglePhysicalVolumeError(self.singlePVerr) - - return [validpvs[0].path] - - def resize(self): - log_method_call(self, self.name, status=self.status) - self._preDestroy() - - # Setup VG parents (in case they are dmraid partitions for example) - self.vg.setupParents(orig=True) - - if self.originalFormat.exists: - self.originalFormat.teardown() - if self.format.exists: - self.format.teardown() - - udev_settle() - lvm.lvresize(self.vg.name, self._name, self.size) - - def dracutSetupArgs(self): - # Note no mapName usage here, this is a lvm cmdline name, which - # is different (ofcourse) - return set(["rd.lvm.lv=%s/%s" % (self.vg.name, self._name)]) - - def checkSize(self): - """ Check to make sure the size of the device is allowed by the - format used. - - Returns: - 0 - ok - 1 - Too large - -1 - Too small - """ - if self.format.maxSize and self.size > self.format.maxSize: - return 1 - elif (self.format.minSize and - (not self.req_grow and - self.size < self.format.minSize) or - (self.req_grow and self.req_max_size and - self.req_max_size < self.format.minSize)): - return -1 - return 0 - -class MDRaidArrayDevice(StorageDevice): - """ An mdraid (Linux RAID) device. """ - _type = "mdarray" - _packages = ["mdadm"] - _devDir = "/dev/md" - - def __init__(self, name, level=None, major=None, minor=None, size=None, - memberDevices=None, totalDevices=None, - uuid=None, format=None, exists=False, metadataVersion=None, - parents=None, sysfsPath=''): - """ Create a MDRaidArrayDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - level -- the device's RAID level (a string, eg: '1' or 'raid1') - metadataVersion -- the version of the device's md metadata - parents -- list of member devices (StorageDevice instances) - size -- the device's size (units/format TBD) - uuid -- the device's UUID - minor -- the device minor - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - exists -- indicates whether this is an existing device - """ - StorageDevice.__init__(self, name, format=format, exists=exists, - major=major, minor=minor, size=size, - parents=parents, sysfsPath=sysfsPath) - - self.level = level - if level == "container": - self._type = "mdcontainer" - elif level is not None: - self.level = mdraid.raidLevel(level) - - # For new arrays check if we have enough members - if (not exists and parents and - len(parents) < mdraid.get_raid_min_members(self.level)): - for dev in self.parents: - dev.removeChild() - raise DeviceError, P_("A RAID%(raidLevel)d set requires at least %(minMembers)d member", - "A RAID%(raidLevel)d set requires at least %(minMembers)d members", - mdraid.get_raid_min_members(self.level)) % \ - {"raidLevel": self.level, "minMembers": mdraid.get_raid_min_members(self.level)} - - self.uuid = uuid - self._totalDevices = util.numeric_type(totalDevices) - self._memberDevices = util.numeric_type(memberDevices) - - self.chunkSize = 512.0 / 1024.0 # chunk size in MB - - if not self.exists and not isinstance(metadataVersion, str): - self.metadataVersion = "default" - else: - self.metadataVersion = metadataVersion - - # bitmaps are not meaningful on raid0 according to mdadm-3.0.3 - self.createBitmap = self.level != 0 - - # For container members probe size now, as we cannot determine it - # when teared down. - if self.parents and self.parents[0].type == "mdcontainer": - self._size = self.currentSize - self._type = "mdbiosraidarray" - - self.formatClass = get_device_format_class("mdmember") - if not self.formatClass: - for dev in self.parents: - dev.removeChild() - raise DeviceError("cannot find class for 'mdmember'", self.name) - - if self.exists and self.uuid and not flags.testing: - # this is a hack to work around mdadm's insistence on giving - # really high minors to arrays it has no config entry for - open("/etc/mdadm.conf", "a").write("ARRAY %s UUID=%s\n" - % (self.path, self.uuid)) - - @property - def rawArraySize(self): - """ Calculate the raw array size without taking into account space - reserved for metadata or chunkSize alignment. - - This is used to calculate the superBlockSize for v1.1 and v1.2 - metadata. - - Returns the raw size in MB - """ - smallestMemberSize = self.smallestMember.size - if self.level == mdraid.RAID0: - size = self.memberDevices * smallestMemberSize - elif self.level == mdraid.RAID1: - size = smallestMemberSize - elif self.level == mdraid.RAID4: - size = (self.memberDevices - 1) * smallestMemberSize - elif self.level == mdraid.RAID5: - size = (self.memberDevices - 1) * smallestMemberSize - elif self.level == mdraid.RAID6: - size = (self.memberDevices - 2) * smallestMemberSize - elif self.level == mdraid.RAID10: - size = (self.memberDevices / 2.0) * smallestMemberSize - else: - size = smallestMemberSize - log.error("unknown RAID level %s" % (self.level)) - log.debug("raw RAID %s size == %s" % (self.level, size)) - return size - - @property - def superBlockSize(self): - """ mdadm has different amounts of space reserved for its use depending - on the metadata type and size of the array. - - 0.9 use 2.0 MB - 1.0 use 2.0 MB - 1.1 or 1.2 use the formula lifted from mdadm/super1.c to calculate it - based on the array size. - """ - # mdadm 3.2.4 made a major change in the amount of space used for 1.1 and 1.2 - # in order to reserve space for reshaping. See commit 508a7f16 in the - # upstream mdadm repository. - if self.metadataVersion not in ["default", "1.1", "1.2"]: - headroom = 2.0 - else: - array_size = self.rawArraySize - # MDADM: We try to leave 0.1% at the start for reshape - # MDADM: operations, but limit this to 128Meg (0.1% of 10Gig) - # MDADM: which is plenty for efficient reshapes - # NOTE: In the mdadm code this is in 512b sectors. Converted to use MB - headroom = 128 - while headroom << 10 > array_size: - headroom >>= 1 - log.info("Using %sMB superBlockSize" % (headroom)) - return headroom - - @property - def smallestMember(self): - try: - smallest = sorted(self.devices, key=lambda d: d.size)[0] - except IndexError: - smallest = None - return smallest - - @property - def size(self): - if not self.devices: - return 0 - - # For container members return probed size, as we cannot determine it - # when teared down. - if self.type == "mdbiosraidarray": - return self._size - - size = 0 - smallestMemberSize = self.smallestMember.size - self.superBlockSize - if not self.exists or not self.partedDevice: - if self.level == mdraid.RAID0: - size = self.memberDevices * smallestMemberSize - size -= size % self.chunkSize - elif self.level == mdraid.RAID1: - size = smallestMemberSize - elif self.level == mdraid.RAID4: - size = (self.memberDevices - 1) * smallestMemberSize - size -= size % self.chunkSize - elif self.level == mdraid.RAID5: - size = (self.memberDevices - 1) * smallestMemberSize - size -= size % self.chunkSize - elif self.level == mdraid.RAID6: - size = (self.memberDevices - 2) * smallestMemberSize - size -= size % self.chunkSize - elif self.level == mdraid.RAID10: - size = (self.memberDevices / 2.0) * smallestMemberSize - size -= size % self.chunkSize - log.debug("non-existent RAID %s size == %s" % (self.level, size)) - else: - size = self.partedDevice.getSize() - log.debug("existing RAID %s size == %s" % (self.level, size)) - - return size - - @property - def description(self): - if self.level == mdraid.RAID0: - levelstr = "stripe" - elif self.level == mdraid.RAID1: - levelstr = "mirror" - else: - levelstr = "raid%s" % self.level - - if self.type == "mdcontainer": - return "BIOS RAID container" - elif self.type == "mdbiosraidarray": - return "BIOS RAID set (%s)" % levelstr - else: - return "MDRAID set (%s)" % levelstr - - def __repr__(self): - s = StorageDevice.__repr__(self) - s += (" level = %(level)s spares = %(spares)s\n" - " members = %(memberDevices)s\n" - " total devices = %(totalDevices)s" - " metadata version = %(metadataVersion)s" % - {"level": self.level, "spares": self.spares, - "memberDevices": self.memberDevices, - "totalDevices": self.totalDevices, - "metadataVersion": self.metadataVersion}) - return s - - @property - def dict(self): - d = super(MDRaidArrayDevice, self).dict - d.update({"level": self.level, - "spares": self.spares, "memberDevices": self.memberDevices, - "totalDevices": self.totalDevices, - "metadataVersion": self.metadataVersion}) - return d - - @property - def mdadmConfEntry(self): - """ This array's mdadm.conf entry. """ - if self.level is None or self.memberDevices is None or not self.uuid: - raise DeviceError("array is not fully defined", self.name) - - # containers and the sets within must only have a UUID= parameter - if self.type == "mdcontainer" or self.type == "mdbiosraidarray": - fmt = "ARRAY %s UUID=%s\n" - return fmt % (self.path, self.uuid) - - fmt = "ARRAY %s level=raid%d num-devices=%d UUID=%s\n" - return fmt % (self.path, self.level, self.memberDevices, self.uuid) - - @property - def totalDevices(self): - """ Total number of devices in the array, including spares. """ - count = len(self.parents) - if not self.exists: - count = self._totalDevices - return count - - def _getMemberDevices(self): - return self._memberDevices - - def _setMemberDevices(self, number): - if not isinstance(number, int): - raise ValueError("memberDevices is an integer") - - if number > self.totalDevices: - raise ValueError("memberDevices cannot be greater than totalDevices") - self._memberDevices = number - - memberDevices = property(_getMemberDevices, _setMemberDevices, - doc="number of member devices") - - def _getSpares(self): - spares = 0 - if self.memberDevices is not None: - if self.totalDevices is not None and \ - self.totalDevices > self.memberDevices: - spares = self.totalDevices - self.memberDevices - elif self.totalDevices is None: - spares = self.memberDevices - self._totalDevices = self.memberDevices - return spares - - def _setSpares(self, spares): - # FIXME: this is too simple to be right - if self.totalDevices > spares: - self.memberDevices = self.totalDevices - spares - - spares = property(_getSpares, _setSpares) - - def updateSysfsPath(self): - """ Update this device's sysfs path. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - # We don't use self.status here because self.status requires a valid - # sysfs path to function correctly. - if os.path.exists(self.path): - md_node = mdraid.md_node_from_name(self.name) - self.sysfsPath = "/devices/virtual/block/%s" % md_node - else: - self.sysfsPath = '' - - def _addDevice(self, device): - """ Add a new member device to the array. - - XXX This is for use when probing devices, not for modification - of arrays. - """ - log_method_call(self, - self.name, - device=device.name, - status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - if not isinstance(device.format, self.formatClass): - raise ValueError("invalid device format for mdraid member") - - if self.uuid and device.format.mdUuid != self.uuid: - raise ValueError("cannot add member with non-matching UUID") - - if device in self.devices: - raise ValueError("device is already a member of this array") - - # we added it, so now set up the relations - self.devices.append(device) - device.addChild() - - device.setup() - udev_settle() - - if self.spares > 0: - # mdadm doesn't like it when you try to incrementally add spares - return - - try: - mdraid.mdadd(device.path) - # mdadd causes udev events - udev_settle() - except MDRaidError as e: - log.warning("failed to add member %s to md array %s: %s" - % (device.path, self.path, e)) - - if self.status: - # we always probe since the device may not be set up when we want - # information about it - self._size = self.currentSize - - def _removeDevice(self, device): - """ Remove a component device from the array. - - XXX This is for use by clearpart, not for reconfiguration. - """ - log_method_call(self, - self.name, - device=device.name, - status=self.status) - - if device not in self.devices: - raise ValueError("cannot remove non-member device from array") - - self.devices.remove(device) - device.removeChild() - - def addMember(self, member): - if member in self.parents: - raise ValueError("member is already part of this array") - - # for the time being we will not allow adding members to existing arrays - if self.exists: - raise DeviceError("cannot add member to existing array", self.name) - - self.parents.append(member) - member.addChild() - self.memberDevices += 1 - - def removeMember(self, member): - self._removeDevice(member) - self.memberDevices -= 1 - - @property - def status(self): - """ This device's status. - - For now, this should return a boolean: - True the device is open and ready for use - False the device is not open - """ - # check the status in sysfs - status = False - if not self.exists: - return status - - state_file = "/sys/%s/md/array_state" % self.sysfsPath - if os.access(state_file, os.R_OK): - state = open(state_file).read().strip() - if state in ("clean", "active", "active-idle", "readonly", "read-auto"): - status = True - # mdcontainers have state inactive when started (clear if stopped) - if self.type == "mdcontainer" and state == "inactive": - status = True - - return status - - @property - def degraded(self): - """ Return True if the array is running in degraded mode. """ - rc = False - degraded_file = "/sys/%s/md/degraded" % self.sysfsPath - if os.access(degraded_file, os.R_OK): - val = open(degraded_file).read().strip() - if val == "1": - rc = True - - return rc - - @property - def complete(self): - if self.type == "mdbiosraidarray": - members = len(self.parents[0].parents) - else: - members = len(self.parents) - - return (self.memberDevices <= members) or not self.exists - - @property - def devices(self): - """ Return a list of this array's member device instances. """ - return self.parents - - def _postSetup(self): - super(MDRaidArrayDevice, self)._postSetup() - self.updateSysfsPath() - - def _setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - disks = [] - for member in self.devices: - member.setup(orig=orig) - disks.append(member.path) - - mdraid.mdactivate(self.path, - members=disks, - uuid=self.uuid) - - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - # we don't really care about the return value of _preTeardown here. - # see comment just above mddeactivate call - self._preTeardown(recursive=recursive) - - # Since BIOS RAID sets (containers in mdraid terminology) never change - # there is no need to stop them and later restart them. Not stopping - # (and thus also not starting) them also works around bug 523334 - if self.type == "mdcontainer" or self.type == "mdbiosraidarray": - return - - # We don't really care what the array's state is. If the device - # file exists, we want to deactivate it. mdraid has too many - # states. - if self.exists and os.path.exists(self.path): - mdraid.mddeactivate(self.path) - - self._postTeardown(recursive=recursive) - - def preCommitFixup(self, *args, **kwargs): - """ Determine create parameters for this set """ - mountpoints = kwargs.pop("mountpoints") - log_method_call(self, self.name, mountpoints) - - if "/boot" in mountpoints: - bootmountpoint = "/boot" - else: - bootmountpoint = "/" - - # If we are used to boot from we cannot use 1.1 metadata - if getattr(self.format, "mountpoint", None) == bootmountpoint or \ - getattr(self.format, "mountpoint", None) == "/boot/efi" or \ - self.format.type == "prepboot": - self.metadataVersion = "1.0" - - # Bitmaps are not useful for swap and small partitions - if self.size < 1000 or self.format.type == "swap": - self.createBitmap = False - - def _postCreate(self): - # this is critical since our status method requires a valid sysfs path - md_node = mdraid.md_node_from_name(self.name) - self.sysfsPath = "/devices/virtual/block/%s" % md_node - self.exists = True # I think we can remove this. - - StorageDevice._postCreate(self) - - # update our uuid attribute with the new array's UUID - info = udev_get_block_device(self.sysfsPath) - self.uuid = udev_device_get_md_uuid(info) - for member in self.devices: - member.mdUuid = self.uuid - - def _create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - disks = [disk.path for disk in self.devices] - spares = len(self.devices) - self.memberDevices - mdraid.mdcreate(self.path, - self.level, - disks, - spares, - metadataVer=self.metadataVersion, - bitmap=self.createBitmap) - - @property - def formatArgs(self): - formatArgs = [] - if self.format.type == "ext2": - if self.level == mdraid.RAID5: - formatArgs = ['-R', - 'stride=%d' % ((self.memberDevices - 1) * 16)] - if self.level == mdraid.RAID4: - formatArgs = ['-R', - 'stride=%d' % ((self.memberDevices - 1) * 16)] - elif self.level == mdraid.RAID0: - formatArgs = ['-R', - 'stride=%d' % (self.memberDevices * 16)] - - @property - def mediaPresent(self): - # Containers should not get any format handling done - # (the device node does not allow read / write calls) - if self.type == "mdcontainer": - return False - # BIOS RAID sets should show as present even when teared down - elif self.type == "mdbiosraidarray": - return True - elif flags.testing: - return True - else: - return self.partedDevice is not None - - @property - def model(self): - return self.description - - @property - def partitionable(self): - return self.type == "mdbiosraidarray" - - @property - def isDisk(self): - return self.type == "mdbiosraidarray" - - def dracutSetupArgs(self): - return set(["rd.md.uuid=%s" % self.uuid]) - -class DMRaidArrayDevice(DMDevice): - """ A dmraid (device-mapper RAID) device """ - _type = "dm-raid array" - _packages = ["dmraid"] - _partitionable = True - _isDisk = True - - def __init__(self, name, raidSet=None, format=None, - size=None, parents=None, sysfsPath=''): - """ Create a DMRaidArrayDevice instance. - - Arguments: - - name -- the dmraid name also the device node's basename - - Keyword Arguments: - - raidSet -- the RaidSet object from block - parents -- a list of the member devices - sysfsPath -- sysfs device path - size -- the device's size - format -- a DeviceFormat instance - """ - if isinstance(parents, list): - for parent in parents: - if not parent.format or parent.format.type != "dmraidmember": - raise ValueError("parent devices must contain dmraidmember format") - DMDevice.__init__(self, name, format=format, size=size, - parents=parents, sysfsPath=sysfsPath, exists=True) - - self.formatClass = get_device_format_class("dmraidmember") - if not self.formatClass: - raise StorageError("cannot find class for 'dmraidmember'") - - self._raidSet = raidSet - - @property - def raidSet(self): - return self._raidSet - - def _addDevice(self, device): - """ Add a new member device to the array. - - XXX This is for use when probing devices, not for modification - of arrays. - """ - log_method_call(self, self.name, device=device.name, status=self.status) - - if not self.exists: - raise DeviceError("device has not been created", self.name) - - if not isinstance(device.format, self.formatClass): - raise ValueError("invalid device format for dmraid member") - - if device in self.members: - raise ValueError("device is already a member of this array") - - # we added it, so now set up the relations - self.devices.append(device) - device.addChild() - - @property - def members(self): - return self.parents - - @property - def devices(self): - """ Return a list of this array's member device instances. """ - return self.parents - - def deactivate(self): - """ Deactivate the raid set. """ - log_method_call(self, self.name, status=self.status) - # This call already checks if the set is not active. - self._raidSet.deactivate() - - def activate(self): - """ Activate the raid set. """ - log_method_call(self, self.name, status=self.status) - # This call already checks if the set is active. - self._raidSet.activate(mknod=True) - udev_settle() - - def _setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - self.activate() - - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - if not self._preTeardown(recursive=recursive): - return - - log.debug("not tearing down dmraid device %s" % self.name) - - @property - def description(self): - return "BIOS RAID set (%s)" % self._raidSet.rs.set_type - - @property - def model(self): - return self.description - - def dracutSetupArgs(self): - return set(["rd.dm.uuid=%s" % self.name]) - -class MultipathDevice(DMDevice): - """ A multipath device """ - _type = "dm-multipath" - _packages = ["device-mapper-multipath"] - _services = ["multipathd"] - _partitionable = True - _isDisk = True - - def __init__(self, name, info, format=None, size=None, - parents=None, sysfsPath=''): - """ Create a MultipathDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - info -- the udev info for this device - - Keyword Arguments: - - sysfsPath -- sysfs device path - size -- the device's size - format -- a DeviceFormat instance - parents -- a list of the backing devices (Device instances) - """ - - self._info = info - self.setupIdentity() - DMDevice.__init__(self, name, format=format, size=size, - parents=parents, sysfsPath=sysfsPath, - exists=True) - - self.config = { - 'wwid' : self.identity, - 'mode' : '0600', - 'uid' : '0', - 'gid' : '0', - } - - def setupIdentity(self): - """ Adds identifying remarks to MultipathDevice object. - - May be overridden by a sub-class for e.g. RDAC handling. - """ - self._identity = self._info.get("ID_SERIAL_RAW", self._info.get("ID_SERIAL_SHORT")) - - @property - def identity(self): - """ Get identity set with setupIdentityFromInfo() - - May be overridden by a sub-class for e.g. RDAC handling. - """ - if not hasattr(self, "_identity"): - raise RuntimeError, "setupIdentityFromInfo() has not been called." - return self._identity - - @property - def wwid(self): - identity = self.identity - ret = [] - while identity: - ret.append(identity[:2]) - identity = identity[2:] - return ":".join(ret) - - @property - def model(self): - if not self.parents: - return "" - return self.parents[0].model - - @property - def vendor(self): - if not self.parents: - return "" - return self.parents[0].vendor - - @property - def description(self): - return "WWID %s" % (self.wwid,) - - def addParent(self, parent): - """ Add a parent device to the mpath. """ - log_method_call(self, self.name, status=self.status) - if self.status: - self.teardown() - self.parents.append(parent) - self.setup() - else: - self.parents.append(parent) - - def deactivate(self): - """ - This is never called, included just for documentation. - - If we called this during teardown(), we wouldn't be able to get parted - object because /dev/mapper/mpathX wouldn't exist. - """ - if self.exists and os.path.exists(self.path): - #self.teardownPartitions() - #rc = util.run_program(["multipath", '-f', self.name]) - #if rc: - # raise MPathError("multipath deactivation failed for '%s'" % - # self.name) - bdev = block.getDevice(self.name) - devmap = block.getMap(major=bdev[0], minor=bdev[1]) - if devmap.open_count: - return - try: - block.removeDeviceMap(devmap) - except Exception as e: - raise MPathError("failed to tear down multipath device %s: %s" - % (self.name, e)) - - def _setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - udev_settle() - rc = util.run_program(["multipath", self.name]) - if rc: - raise MPathError("multipath activation failed for '%s'" % - self.name, hardware_fault=True) - - def _postSetup(self): - StorageDevice._postSetup(self) - self.setupPartitions() - udev_settle() - -class NoDevice(StorageDevice): - """ A nodev device for nodev filesystems like tmpfs. """ - _type = "nodev" - - def __init__(self, format=None): - """ Create a NoDevice instance. - - Arguments: - - Keyword Arguments: - - format -- a DeviceFormat instance - """ - if format: - name = format.type - else: - name = "none" - - StorageDevice.__init__(self, name, format=format, exists=True) - - @property - def path(self): - """ Device node representing this device. """ - return self.name - - def setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - - def teardown(self, recursive=False): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - # just make sure the format is unmounted - self._preTeardown(recursive=recursive) - - def create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - - def destroy(self): - """ Destroy the device. """ - log_method_call(self, self.name, status=self.status) - self._preDestroy() - - -class FileDevice(StorageDevice): - """ A file on a filesystem. - - This exists because of swap files. - """ - _type = "file" - _devDir = "" - - def __init__(self, path, format=None, size=None, - exists=False, parents=None): - """ Create a FileDevice instance. - - Arguments: - - path -- full path to the file - - Keyword Arguments: - - format -- a DeviceFormat instance - size -- the file size (units TBD) - parents -- a list of required devices (Device instances) - exists -- indicates whether this is an existing device - """ - if not path.startswith("/"): - raise ValueError("FileDevice requires an absolute path") - - StorageDevice.__init__(self, path, format=format, size=size, - exists=exists, parents=parents) - - @property - def fstabSpec(self): - return self.name - - @property - def path(self): - root = "" - try: - status = self.parents[0].format.status - except (AttributeError, IndexError): - # either this device has no parents or something is wrong with - # the first one - status = (os.access(self.name, os.R_OK) and - self.parents in ([], None)) - else: - # this is the actual active mountpoint - root = self.parents[0].format._mountpoint - # trim the mountpoint down to the chroot since we already have - # the otherwise fully-qualified path - mountpoint = self.parents[0].format.mountpoint - while mountpoint.endswith("/"): - mountpoint = mountpoint[:-1] - if mountpoint: - root = root[:-len(mountpoint)] - - return os.path.normpath("%s%s" % (root, self.name)) - - def _preSetup(self, orig=False): - if self.format and self.format.exists and not self.format.status: - self.format.device = self.path - - return StorageDevice._preSetup(self, orig=orig) - - def _preTeardown(self, recursive=None): - if self.format and self.format.exists and not self.format.status: - self.format.device = self.path - - return StorageDevice._preTeardown(self, recursive=recursive) - - def _create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - fd = os.open(self.path, os.O_WRONLY|os.O_CREAT|os.O_TRUNC) - buf = "\0" * 1024 * 1024 - for n in range(self.size): - os.write(fd, buf) - os.close(fd) - - def _destroy(self): - """ Destroy the device. """ - log_method_call(self, self.name, status=self.status) - os.unlink(self.path) - - -class SparseFileDevice(FileDevice): - """A sparse file on a filesystem. - This exists for sparse disk images.""" - _type = "sparse file" - def _create(self): - """Create a sparse file.""" - log_method_call(self, self.name, status=self.status) - fd = os.open(self.path, os.O_WRONLY|os.O_CREAT|os.O_TRUNC) - os.ftruncate(fd, 1024*1024*self.size) - os.close(fd) - - -class DirectoryDevice(FileDevice): - """ A directory on a filesystem. - - This exists because of bind mounts. - """ - _type = "directory" - - def _create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - util.makedirs(self.path) - - -class LoopDevice(StorageDevice): - """ A loop device. """ - _type = "loop" - - def __init__(self, name=None, format=None, size=None, sysfsPath=None, - exists=False, parents=None): - """ Create a LoopDevice instance. - - Arguments: - - name -- the device's name - - Keyword Arguments: - - format -- a DeviceFormat instance - size -- the device's size in MB - parents -- a list of required devices (Device instances) - exists -- indicates whether this is an existing device - - - Loop devices always exist. - """ - if not parents: - raise ValueError("LoopDevice requires a backing device") - - if not name: - # set up a temporary name until we've activated the loop device - name = "tmploop%d" % Device._id - - StorageDevice.__init__(self, name, format=format, size=size, - exists=True, parents=parents) - - def updateName(self): - """ Update this device's name. """ - if not self.slave.status: - # if the backing device is inactive, so are we - return self.name - - if self.name.startswith("loop"): - # if our name is loopN we must already be active - return self.name - - name = loop.get_loop_name(self.slave.path) - if name.startswith("loop"): - self._name = name - - return self.name - - @property - def status(self): - return (self.slave.status and - self.name.startswith("loop") and - loop.get_loop_name(self.slave.path) == self.name) - - @property - def size(self): - return self.slave.size - - def _setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - loop.loop_setup(self.slave.path) - - def _postSetup(self): - StorageDevice._postSetup(self) - self.updateName() - self.updateSysfsPath() - - def _teardown(self, recursive=False): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - loop.loop_teardown(self.path) - - def _postTeardown(self, recursive=False): - StorageDevice._postTeardown(self, recursive=recursive) - self._name = "tmploop%d" % self.id - self.sysfsPath = '' - - @property - def slave(self): - return self.parents[0] - - -class iScsiDiskDevice(DiskDevice, NetworkStorageDevice): - """ An iSCSI disk. """ - _type = "iscsi" - _packages = ["iscsi-initiator-utils", "dracut-network"] - - def __init__(self, device, **kwargs): - self.node = kwargs.pop("node") - self.ibft = kwargs.pop("ibft") - self.nic = kwargs.pop("nic") - self.initiator = kwargs.pop("initiator") - - if self.node is None: - # qla4xxx partial offload - name = kwargs.pop("fw_name") - address = kwargs.pop("fw_address") - port = kwargs.pop("fw_port") - DiskDevice.__init__(self, device, **kwargs) - NetworkStorageDevice.__init__(self, - host_address=address, - nic=self.nic) - log.debug("created new iscsi disk %s %s:%s using fw initiator %s" - % (name, address, port, self.initiator)) - else: - DiskDevice.__init__(self, device, **kwargs) - NetworkStorageDevice.__init__(self, host_address=self.node.address, - nic=self.nic) - log.debug("created new iscsi disk %s %s:%d via %s:%s" % (self.node.name, - self.node.address, - self.node.port, - self.node.iface, - self.nic)) - - def dracutSetupArgs(self): - if self.ibft: - return set(["iscsi_firmware"]) - - # qla4xxx partial offload - if self.node is None: - return set() - - address = self.node.address - # surround ipv6 addresses with [] - if ":" in address: - address = "[%s]" % address - - netroot="netroot=iscsi:" - auth = self.node.getAuth() - if auth: - netroot += "%s:%s" % (auth.username, auth.password) - if len(auth.reverse_username) or len(auth.reverse_password): - netroot += ":%s:%s" % (auth.reverse_username, - auth.reverse_password) - - iface_spec = "" - if self.nic != "default": - iface_spec = ":%s:%s" % (self.node.iface, self.nic) - netroot += "@%s::%d%s::%s" % (address, - self.node.port, - iface_spec, - self.node.name) - - initiator = "iscsi_initiator=%s" % self.initiator - - return set([netroot, initiator]) - -class FcoeDiskDevice(DiskDevice, NetworkStorageDevice): - """ An FCoE disk. """ - _type = "fcoe" - _packages = ["fcoe-utils", "dracut-network"] - - def __init__(self, device, **kwargs): - self.nic = kwargs.pop("nic") - self.identifier = kwargs.pop("identifier") - DiskDevice.__init__(self, device, **kwargs) - NetworkStorageDevice.__init__(self, nic=self.nic) - log.debug("created new fcoe disk %s (%s) @ %s" % - (device, self.identifier, self.nic)) - - def dracutSetupArgs(self): - dcb = True - - from .fcoe import fcoe - for nic, dcb, auto_vlan in fcoe().nics: - if nic == self.nic: - break - - if dcb: - dcbOpt = "dcb" - else: - dcbOpt = "nodcb" - - return set(["fcoe=edd:%s" % dcbOpt]) - - -class OpticalDevice(StorageDevice): - """ An optical drive, eg: cdrom, dvd+r, &c. - - XXX Is this useful? - """ - _type = "cdrom" - - def __init__(self, name, major=None, minor=None, exists=False, - format=None, parents=None, sysfsPath='', vendor="", - model=""): - StorageDevice.__init__(self, name, format=format, - major=major, minor=minor, exists=True, - parents=parents, sysfsPath=sysfsPath, - vendor=vendor, model=model) - - @property - def mediaPresent(self): - """ Return a boolean indicating whether or not the device contains - media. - """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - try: - fd = os.open(self.path, os.O_RDONLY) - except OSError as e: - # errno 123 = No medium found - if e.errno == 123: - return False - else: - return True - else: - os.close(fd) - return True - - def eject(self): - """ Eject the drawer. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created", self.name) - - #try to umount and close device before ejecting - self.teardown() - - try: - util.run_program(["eject", self.name]) - except OSError as e: - log.warning("error ejecting cdrom %s: %s" % (self.name, e)) - - -class ZFCPDiskDevice(DiskDevice): - """ A mainframe ZFCP disk. """ - _type = "zfcp" - - def __init__(self, device, **kwargs): - self.hba_id = kwargs.pop("hba_id") - self.wwpn = kwargs.pop("wwpn") - self.fcp_lun = kwargs.pop("fcp_lun") - DiskDevice.__init__(self, device, **kwargs) - - def __repr__(self): - s = DiskDevice.__repr__(self) - s += (" hba_id = %(hba_id)s wwpn = %(wwpn)s fcp_lun = %(fcp_lun)s" % - {"hba_id": self.hba_id, - "wwpn": self.wwpn, - "fcp_lun": self.fcp_lun}) - return s - - @property - def description(self): - return "FCP device %(device)s with WWPN %(wwpn)s and LUN %(lun)s" \ - % {'device': self.hba_id, - 'wwpn': self.wwpn, - 'lun': self.fcp_lun} - - def dracutSetupArgs(self): - return set(["rd.zfcp=%s,%s,%s" % (self.hba_id, self.wwpn, self.fcp_lun,)]) - -class DASDDevice(DiskDevice): - """ A mainframe DASD. """ - _type = "dasd" - - def __init__(self, device, **kwargs): - self.busid = kwargs.pop('busid') - self.opts = kwargs.pop('opts') - self.dasd = kwargs.pop('dasd') - DiskDevice.__init__(self, device, **kwargs) - - if self.dasd: - self.dasd.addDASD(self) - - @property - def description(self): - return "DASD device %s" % self.busid - - def getOpts(self): - return ["%s=%s" % (k, v) for k, v in self.opts.items() if v == '1'] - - def dracutSetupArgs(self): - conf = "/etc/dasd.conf" - line = None - if os.path.isfile(conf): - f = open(conf) - # grab the first line that starts with our busID - line = [line for line in f.readlines() - if line.startswith(self.busid)][:1] - f.close() - - # See if we got a line. If not, grab our getOpts - if not line: - line = self.busid - for devopt in self.getOpts(): - line += " %s" % devopt - - # Create a translation mapping from dasd.conf format to module format - translate = {'use_diag': 'diag', - 'readonly': 'ro', - 'erplog': 'erplog', - 'failfast': 'failfast'} - - # this is a really awkward way of determining if the - # feature found is actually desired (1, not 0), plus - # translating that feature into the actual kernel module - # value - opts = [] - parts = line.split() - for chunk in parts[1:]: - try: - feat, val = chunk.split('=') - if int(val): - opts.append(translate[feat]) - except: - # If we don't know what the feature is (feat not in translate - # or if we get a val that doesn't cleanly convert to an int - # we can't do anything with it. - log.warning("failed to parse dasd feature %s" % chunk) - - if opts: - return set(["rd.dasd=%s(%s)" % (self.busid, - ":".join(opts))]) - else: - return set(["rd.dasd=%s" % self.busid]) - -class NFSDevice(StorageDevice, NetworkStorageDevice): - """ An NFS device """ - _type = "nfs" - _packages = ["dracut-network"] - - def __init__(self, device, format=None, parents=None): - # we could make host/ip, path, &c but will anything use it? - StorageDevice.__init__(self, device, format=format, parents=parents) - NetworkStorageDevice.__init__(self, device.split(":")[0]) - - @property - def path(self): - """ Device node representing this device. """ - return self.name - - def setup(self, orig=False): - """ Open, or set up, a device. """ - log_method_call(self, self.name, orig=orig, status=self.status, - controllable=self.controllable) - - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status, - controllable=self.controllable) - - def create(self): - """ Create the device. """ - log_method_call(self, self.name, status=self.status) - self._preCreate() - - def destroy(self): - """ Destroy the device. """ - log_method_call(self, self.name, status=self.status) - - -class BTRFSDevice(StorageDevice): - """ Base class for BTRFS volume and sub-volume devices. """ - _type = "btrfs" - _packages = ["btrfs-progs"] - - def __init__(self, *args, **kwargs): - """ Passing None or no name means auto-generate one like btrfs.%d """ - if not args or not args[0]: - args = ("btrfs.%d" % Device._id,) - - self.req_size = kwargs.pop("size", None) - super(BTRFSDevice, self).__init__(*args, **kwargs) - - def updateSysfsPath(self): - """ Update this device's sysfs path. """ - log_method_call(self, self.name, status=self.status) - self.parents[0].updateSysfsPath() - self.sysfsPath = self.parents[0].sysfsPath - log.debug("%s sysfsPath set to %s" % (self.name, self.sysfsPath)) - - def _postCreate(self): - super(BTRFSDevice, self)._postCreate() - self.format.exists = True - self.format.device = self.path - - def _preDestroy(self): - """ Preparation and precondition checking for device destruction. """ - super(BTRFSDevice, self)._preDestroy() - self.setupParents(orig=True) - - def _getSize(self): - size = sum([d.size for d in self.parents]) - return size - - def _setSize(self, size): - raise RuntimeError("cannot directly set size of btrfs volume") - - @property - def status(self): - return not any([not d.status for d in self.parents]) - - @property - def _temp_dir_prefix(self): - return "btrfs-tmp.%s" % self.id - - def _do_temp_mount(self, orig=False): - if self.format.status or not self.exists: - return - - tmpdir = tempfile.mkdtemp(prefix=self._temp_dir_prefix) - if orig: - fmt = self.originalFormat - else: - fmt = self.format - - fmt.mount(mountpoint=tmpdir) - - def _undo_temp_mount(self): - if getattr(self.format, "_mountpoint", None): - fmt = self.format - elif getattr(self.originalFormat, "_mountpoint", None): - fmt = self.originalFormat - else: - return - - mountpoint = fmt._mountpoint - - if os.path.basename(mountpoint).startswith(self._temp_dir_prefix): - fmt.unmount() - os.rmdir(mountpoint) - - @property - def path(self): - return self.parents[0].path - - -class BTRFSVolumeDevice(BTRFSDevice): - _type = "btrfs volume" - - def __init__(self, *args, **kwargs): - self.dataLevel = kwargs.pop("dataLevel", None) - self.metaDataLevel = kwargs.pop("metaDataLevel", None) - - super(BTRFSVolumeDevice, self).__init__(*args, **kwargs) - - self.subvolumes = [] - - for parent in self.parents: - if parent.format.type != "btrfs": - raise ValueError("member device %s is not BTRFS" % parent.name) - - if parent.format.exists and self.exists and \ - parent.format.volUUID != self.uuid: - raise ValueError("BTRFS member device %s UUID %s does not " - "match volume UUID %s" % (parent.name, - parent.format.volUUID, self.uuid)) - - if self.parents and not self.format.type: - label = getattr(self.parents[0].format, "label", None) - self.format = getFormat("btrfs", - exists=self.exists, - label=label, - volUUID=self.uuid, - device=self.path) - self.originalFormat = copy.copy(self.format) - - def _setFormat(self, format): - """ Set the Device's format. """ - super(BTRFSVolumeDevice, self)._setFormat(format) - self._name = "btrfs.%d" % self.id - label = getattr(self.format, "label", None) - if label: - self._name = label - - def _getSize(self): - size = sum([d.size for d in self.parents]) - if self.dataLevel in ("raid1", "raid10"): - size /= len(self.parents) - - return size - - def _addDevice(self, device): - """ Add a new device to this volume. - - XXX This is for use by device probing routines and is not - intended for modification of the volume. - """ - log_method_call(self, - self.name, - device=device.name, - status=self.status) - if not self.exists: - raise DeviceError("device does not exist", self.name) - - if device.format.type != "btrfs": - raise ValueError("addDevice requires a btrfs device as sole arg") - - if device.format.volUUID != self.uuid: - raise ValueError("device UUID does not match the volume UUID") - - if device in self.parents: - raise ValueError("device is already a member of this volume") - - self.parents.append(device) - device.addChild() - - def _removeDevice(self, device): - """ Remove a device from the volume. - - This is for cases like clearing of preexisting partitions. - """ - log_method_call(self, - self.name, - device=device.name, - status=self.status) - try: - self.parents.remove(device) - except ValueError: - raise ValueError("cannot remove non-member device from volume") - - device.removeChild() - - def addMember(self, member): - if member in self.parents: - raise ValueError("member is already part of this volume") - - # for the time being we will not allow adding members to existing vols - if self.exists: - raise DeviceError("cannot add member to existing volume", self.name) - - self.parents.append(member) - member.addChild() - - def removeMember(self, member): - if member not in self.parents: - raise ValueError("member is not part of this volume") - - if self.exists: - raise DeviceError("cannot remove member from an existing volume") - - self.parents.remove(member) - member.removeChild() - - def _addSubVolume(self, vol): - if vol.name in [v.name for v in self.subvolumes]: - raise ValueError("subvolume %s already exists" % vol.name) - - self.subvolumes.append(vol) - - def _removeSubVolume(self, name): - if name not in [v.name for v in self.subvolumes]: - raise ValueError("cannot remove non-existent subvolume %s" % name) - - names = [v.name for v in self.subvolumes] - self.subvolumes.pop(names.index(name)) - - def listSubVolumes(self): - subvols = [] - self.setup(orig=True) - try: - self._do_temp_mount(orig=True) - except FSError as e: - log.debug("btrfs temp mount failed: %s" % e) - return subvols - - try: - subvols = btrfs.list_subvolumes(self.originalFormat._mountpoint) - except BTRFSError as e: - log.debug("failed to list subvolumes: %s" % e) - finally: - self._undo_temp_mount() - - return subvols - - def createSubVolumes(self): - self._do_temp_mount() - for name, subvol in self.subvolumes: - if subvol.exists: - continue - subvol.create(mountpoint=self._temp_dir_prefix) - self._undo_temp_mount() - - def removeSubVolume(self, name): - raise NotImplementedError() - - def _create(self): - log_method_call(self, self.name, status=self.status) - btrfs.create_volume(devices=[d.path for d in self.parents], - label=self.format.label, - data=self.dataLevel, - metadata=self.metaDataLevel) - - def _destroy(self): - log_method_call(self, self.name, status=self.status) - for device in self.parents: - device.setup(orig=True) - DeviceFormat(device=device.path, exists=True).destroy() - -class BTRFSSubVolumeDevice(BTRFSDevice): - """ A btrfs subvolume pseudo-device. """ - _type = "btrfs subvolume" - - def __init__(self, *args, **kwargs): - self.vol_id = kwargs.pop("vol_id", None) - super(BTRFSSubVolumeDevice, self).__init__(*args, **kwargs) - - self.volume._addSubVolume(self) - - @property - def volume(self): - return self.parents[0] - - def setupParents(self, orig=False): - """ Run setup method of all parent devices. """ - log_method_call(self, name=self.name, orig=orig, kids=self.kids) - self.volume.setup(orig=orig) - - def _create(self): - log_method_call(self, self.name, status=self.status) - self.volume._do_temp_mount() - mountpoint = self.volume.format._mountpoint - if not mountpoint: - raise RuntimeError("btrfs subvol create requires mounted volume") - - btrfs.create_subvolume(mountpoint, self.name) - self.volume._undo_temp_mount() - - def _destroy(self): - log_method_call(self, self.name, status=self.status) - self.volume._do_temp_mount(orig=True) - mountpoint = self.volume.originalFormat._mountpoint - if not mountpoint: - raise RuntimeError("btrfs subvol destroy requires mounted volume") - btrfs.delete_subvolume(mountpoint, self.name) - self.volume._undo_temp_mount() diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py deleted file mode 100644 index f0d49f727..000000000 --- a/pyanaconda/storage/devicetree.py +++ /dev/null @@ -1,2205 +0,0 @@ -# devicetree.py -# Device management for anaconda's storage configuration module. -# -# Copyright (C) 2009, 2010, 2011 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> -# - -import os -import stat -import block -import re -import shutil -import pprint -import copy - -from errors import * -from devices import * -from deviceaction import * -from pykickstart.constants import * -import formats -import devicelibs.mdraid -import devicelibs.dm -import devicelibs.lvm -import devicelibs.mpath -import devicelibs.loop -import devicelibs.edd -from udev import * -import util -from platform import platform -import tsort -from flags import flags -from storage_log import log_method_call, log_method_return -import parted -import _ped - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -class DeviceTree(object): - """ A quasi-tree that represents the devices in the system. - - The tree contains a list of device instances, which does not - necessarily reflect the actual state of the system's devices. - DeviceActions are used to perform modifications to the tree, - except when initially populating the tree. - - DeviceAction instances are registered, possibly causing the - addition or removal of Device instances to/from the tree. The - DeviceActions are all reversible up to the time their execute - method has been called. - - Only one action of any given type/object pair should exist for - any given device at any given time. - - DeviceAction instances can only be registered for leaf devices, - except for resize actions. - """ - - def __init__(self, conf=None, passphrase=None, luksDict=None, - iscsi=None, dasd=None): - self.reset(conf, passphrase, luksDict, iscsi, dasd) - - def reset(self, conf=None, passphrase=None, luksDict=None, - iscsi=None, dasd=None): - # internal data members - self._devices = [] - self._actions = [] - - # a list of all device names we encounter - self.names = [] - - self._hidden = [] - - # indicates whether or not the tree has been fully populated - self.populated = False - - self.exclusiveDisks = getattr(conf, "exclusiveDisks", []) - self.iscsi = iscsi - self.dasd = dasd - self.mpathFriendlyNames = getattr(conf, "mpathFriendlyNames", True) - - self.diskImages = {} - images = getattr(conf, "diskImages", {}) - if images: - # this will overwrite self.exclusiveDisks - self.setDiskImages(images) - - # protected device specs as provided by the user - self.protectedDevSpecs = getattr(conf, "protectedDevSpecs", []) - self.liveBackingDevice = None - - # names of protected devices at the time of tree population - self.protectedDevNames = [] - - self.unusedRaidMembers = [] - - self.__multipaths = {} - self.__multipathConfigWriter = devicelibs.mpath.MultipathConfigWriter() - - self.__passphrases = [] - if passphrase: - self.__passphrases.append(passphrase) - - self.__luksDevs = {} - if luksDict and isinstance(luksDict, dict): - self.__luksDevs = luksDict - self.__passphrases.extend([p for p in luksDict.values() if p]) - - self._ignoredDisks = [] - for disk in getattr(conf, "ignoredDisks", []): - self.addIgnoredDisk(disk) - devicelibs.lvm.lvm_cc_resetFilter() - - self._cleanup = False - - def setDiskImages(self, images): - """ Set the disk images and reflect them in exclusiveDisks. """ - self.diskImages = images - # disk image files are automatically exclusive - self.exclusiveDisks = self.diskImages.keys() - - def addIgnoredDisk(self, disk): - self._ignoredDisks.append(disk) - devicelibs.lvm.lvm_cc_addFilterRejectRegexp(disk) - - def pruneActions(self): - """ Remove redundant/obsolete actions from the action list. """ - for action in reversed(self._actions[:]): - if action not in self._actions: - log.debug("action %d already pruned" % action.id) - continue - - for obsolete in self._actions[:]: - if action.obsoletes(obsolete): - log.info("removing obsolete action %d (%d)" - % (obsolete.id, action.id)) - self._actions.remove(obsolete) - - def sortActions(self): - """ Sort actions based on dependencies. """ - if not self._actions: - return - - edges = [] - - # collect all ordering requirements for the actions - for action in self._actions: - action_idx = self._actions.index(action) - children = [] - for _action in self._actions: - if _action == action: - continue - - # create edges based on both action type and dependencies. - if action.type > _action.type or _action.requires(action): - children.append(_action) - - for child in children: - child_idx = self._actions.index(child) - edges.append((action_idx, child_idx)) - - # create a graph reflecting the ordering information we have - graph = tsort.create_graph(range(len(self._actions)), edges) - - # perform a topological sort based on the graph's contents - order = tsort.tsort(graph) - - # now replace self._actions with a sorted version of the same list - actions = [] - for idx in order: - actions.append(self._actions[idx]) - self._actions = actions - - def processActions(self, dryRun=None): - """ Execute all registered actions. """ - log.info("resetting parted disks...") - for device in self.devices: - if device.partitioned: - device.format.resetPartedDisk() - if device.originalFormat.type == "disklabel" and \ - device.originalFormat != device.format: - device.originalFormat.resetPartedDisk() - - # Call preCommitFixup on all devices - mpoints = [getattr(d.format, 'mountpoint', "") for d in self.devices] - for device in self.devices: - device.preCommitFixup(mountpoints=mpoints) - - # Also call preCommitFixup on any devices we're going to - # destroy (these are already removed from the tree) - for action in self._actions: - if isinstance(action, ActionDestroyDevice): - action.device.preCommitFixup(mountpoints=mpoints) - - # setup actions to create any extended partitions we added - # - # XXX At this point there can be duplicate partition paths in the - # tree (eg: non-existent sda6 and previous sda6 that will become - # sda5 in the course of partitioning), so we access the list - # directly here. - for device in self._devices: - if isinstance(device, PartitionDevice) and \ - device.isExtended and not device.exists: - # don't properly register the action since the device is - # already in the tree - self._actions.append(ActionCreateDevice(device)) - - for action in self._actions: - log.debug("action: %s" % action) - - log.info("pruning action queue...") - self.pruneActions() - - log.info("sorting actions...") - self.sortActions() - for action in self._actions: - log.debug("action: %s" % action) - - for action in self._actions: - log.info("executing action: %s" % action) - if not dryRun: - try: - action.execute() - except DiskLabelCommitError: - # it's likely that a previous format destroy action - # triggered setup of an lvm or md device. - self.teardownAll() - action.execute() - - udev_settle() - for device in self._devices: - # make sure we catch any renumbering parted does - if device.exists and isinstance(device, PartitionDevice): - device.updateName() - device.format.device = device.path - - def _addDevice(self, newdev): - """ Add a device to the tree. - - Raise ValueError if the device's identifier is already - in the list. - """ - if newdev.uuid and newdev.uuid in [d.uuid for d in self._devices] and \ - not isinstance(newdev, NoDevice): - raise ValueError("device is already in tree") - - # make sure this device's parent devices are in the tree already - for parent in newdev.parents: - if parent not in self._devices: - raise DeviceTreeError("parent device not in tree") - - self._devices.append(newdev) - - # don't include "req%d" partition names - if ((newdev.type != "partition" or - not newdev.name.startswith("req")) and - newdev.type != "btrfs volume" and - newdev.name not in self.names): - self.names.append(newdev.name) - log.info("added %s %s (id %d) to device tree" % (newdev.type, - newdev.name, - newdev.id)) - - def _removeDevice(self, dev, force=None, moddisk=True): - """ Remove a device from the tree. - - Only leaves may be removed. - """ - if dev not in self._devices: - raise ValueError("Device '%s' not in tree" % dev.name) - - if not dev.isleaf and not force: - log.debug("%s has %d kids" % (dev.name, dev.kids)) - raise ValueError("Cannot remove non-leaf device '%s'" % dev.name) - - # if this is a partition we need to remove it from the parted.Disk - if moddisk and isinstance(dev, PartitionDevice) and \ - dev.disk is not None: - # if this partition hasn't been allocated it could not have - # a disk attribute - if dev.partedPartition.type == parted.PARTITION_EXTENDED and \ - len(dev.disk.format.logicalPartitions) > 0: - raise ValueError("Cannot remove extended partition %s. " - "Logical partitions present." % dev.name) - - dev.disk.format.removePartition(dev.partedPartition) - - # adjust all other PartitionDevice instances belonging to the - # same disk so the device name matches the potentially altered - # name of the parted.Partition - for device in self._devices: - if isinstance(device, PartitionDevice) and \ - device.disk == dev.disk: - device.updateName() - elif hasattr(dev, "vg"): - dev.vg._removeLogVol(dev) - elif hasattr(dev, "volume"): - dev.volume._removeSubVolume(dev.name) - - self._devices.remove(dev) - if dev.name in self.names and getattr(dev, "complete", True): - self.names.remove(dev.name) - log.info("removed %s %s (id %d) from device tree" % (dev.type, - dev.name, - dev.id)) - - for parent in dev.parents: - # Will this cause issues with garbage collection? - # Do we care about garbage collection? At all? - parent.removeChild() - - def registerAction(self, action): - """ Register an action to be performed at a later time. - - Modifications to the Device instance are handled before we - get here. - """ - if not (action.isCreate and action.isDevice) and \ - action.device not in self._devices: - raise DeviceTreeError("device is not in the tree") - elif (action.isCreate and action.isDevice): - if action.device in self._devices: - raise DeviceTreeError("device is already in the tree") - - if action.isCreate and action.isDevice: - self._addDevice(action.device) - elif action.isDestroy and action.isDevice: - self._removeDevice(action.device) - elif action.isCreate and action.isFormat: - if isinstance(action.device.format, formats.fs.FS) and \ - action.device.format.mountpoint in self.filesystems: - raise DeviceTreeError("mountpoint already in use") - - log.info("registered action: %s" % action) - self._actions.append(action) - - def cancelAction(self, action): - """ Cancel a registered action. - - This will unregister the action and do any required - modifications to the device list. - - Actions all operate on a Device, so we can use the devices - to determine dependencies. - """ - if action.isCreate and action.isDevice: - # remove the device from the tree - self._removeDevice(action.device) - elif action.isDestroy and action.isDevice: - # add the device back into the tree - self._addDevice(action.device) - elif action.isFormat and \ - (action.isCreate or action.isResize): - action.cancel() - - self._actions.remove(action) - - def findActions(self, device=None, type=None, object=None, path=None, - devid=None): - """ Find all actions that match all specified parameters. - - Keyword arguments: - - device -- device to match (Device, or None to match any) - type -- action type to match (string, or None to match any) - object -- operand type to match (string, or None to match any) - path -- device path to match (string, or None to match any) - - """ - if device is None and type is None and object is None and \ - path is None and devid is None: - return self._actions[:] - - # convert the string arguments to the types used in actions - _type = action_type_from_string(type) - _object = action_object_from_string(object) - - actions = [] - for action in self._actions: - if device is not None and action.device != device: - continue - - if _type is not None and action.type != _type: - continue - - if _object is not None and action.obj != _object: - continue - - if path is not None and action.device.path != path: - continue - - if devid is not None and action.device.id != devid: - continue - - actions.append(action) - - return actions - - def getDependentDevices(self, dep): - """ Return a list of devices that depend on dep. - - The list includes both direct and indirect dependents. - """ - dependents = [] - - # special handling for extended partitions since the logical - # partitions and their deps effectively depend on the extended - logicals = [] - if isinstance(dep, PartitionDevice) and dep.partType and \ - dep.isExtended: - # collect all of the logicals on the same disk - for part in self.getDevicesByInstance(PartitionDevice): - if part.partType and part.isLogical and part.disk == dep.disk: - logicals.append(part) - - incomplete = [d for d in self._devices - if not getattr(d, "complete", True)] - for device in self.devices + incomplete: - if device.dependsOn(dep): - dependents.append(device) - else: - for logical in logicals: - if device.dependsOn(logical): - dependents.append(device) - break - - return dependents - - def isIgnored(self, info): - """ Return True if info is a device we should ignore. - - Arguments: - - info -- a dict representing a udev db entry - - TODO: - - - filtering of SAN/FC devices - - filtering by driver? - - """ - sysfs_path = udev_device_get_sysfs_path(info) - name = udev_device_get_name(info) - if not sysfs_path: - return None - - if name in self._ignoredDisks: - log.debug("device '%s' in ignoredDisks" % name) - return True - - # Special handling for mdraid external metadata sets (mdraid BIOSRAID): - # 1) The containers are intermediate devices which will never be - # in exclusiveDisks - # 2) Sets get added to exclusive disks with their dmraid set name by - # the filter ui. Note that making the ui use md names instead is not - # possible as the md names are simpy md# and we cannot predict the # - if udev_device_is_md(info) and \ - udev_device_get_md_level(info) == "container": - return False - - if udev_device_get_md_container(info) and \ - udev_device_is_md(info) and \ - udev_device_get_md_name(info): - md_name = udev_device_get_md_name(info) - # mdadm may have appended _<digit>+ if the current hostname - # does not match the one in the array metadata - alt_name = re.sub("_\d+$", "", md_name) - raw_pattern = "isw_[a-z]*_%s" - for i in range(0, len(self.exclusiveDisks)): - if re.match(raw_pattern % md_name, self.exclusiveDisks[i]) or \ - re.match(raw_pattern % alt_name, self.exclusiveDisks[i]): - self.exclusiveDisks[i] = name - return False - - # never ignore mapped disk images. if you don't want to use them, - # don't specify them in the first place - if udev_device_is_dm_anaconda(info) or udev_device_is_dm_livecd(info): - return False - - # Ignore loop and ram devices, we normally already skip these in - # udev.py: enumerate_block_devices(), but we can still end up trying - # to add them to the tree when they are slaves of other devices, this - # happens for example with the livecd - if name.startswith("ram"): - return True - - if name.startswith("loop"): - # ignore loop devices unless they're backed by a file - return (not devicelibs.loop.get_backing_file(name)) - - # We want exclusiveDisks to operate on anything that could be - # considered a directly usable disk, ie: fwraid array, mpath, or disk. - # - # Unfortunately, since so many things are represented as disks by - # udev/sysfs, we have to define what is a disk in terms of what is - # not a disk. - if udev_device_is_disk(info) and \ - not udev_device_is_dm_partition(info) and \ - not udev_device_is_dm_lvm(info) and \ - not udev_device_is_dm_crypt(info) and \ - not (udev_device_is_md(info) and - not udev_device_get_md_container(info)): - if self.exclusiveDisks and name not in self.exclusiveDisks: - log.debug("device '%s' not in exclusiveDisks" % name) - self.addIgnoredDisk(name) - return True - - # Ignore any readonly disks - if (udev_device_is_disk(info) and not - (udev_device_is_cdrom(info) or - udev_device_is_partition(info) or - udev_device_is_dm_partition(info) or - udev_device_is_dm_lvm(info) or - udev_device_is_dm_crypt(info) or - (udev_device_is_md(info) and not - udev_device_get_md_container(info)))): - if util.get_sysfs_attr(info["sysfs_path"], 'ro') == '1': - log.debug("Ignoring read only device %s" % name) - self.addIgnoredDisk(name) - return True - - # FIXME: check for virtual devices whose slaves are on the ignore list - - def addUdevLVDevice(self, info): - name = udev_device_get_name(info) - log_method_call(self, name=name) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - - # initiate detection of all PVs and hope that it leads to us having - # the VG and LVs in the tree - for pv_name in os.listdir("/sys" + sysfs_path + "/slaves"): - link = os.readlink("/sys" + sysfs_path + "/slaves/" + pv_name) - pv_sysfs_path = os.path.normpath(sysfs_path + '/slaves/' + link) - pv_info = udev_get_block_device(pv_sysfs_path) - self.addUdevDevice(pv_info) - - vg_name = udev_device_get_lv_vg_name(info) - device = self.getDeviceByName(vg_name) - if not device: - log.error("failed to find vg '%s' after scanning pvs" % vg_name) - - # Don't return the device like we do in the other addUdevFooDevice - # methods. The device we have here is a vg, not an lv. - - def addUdevDMDevice(self, info): - name = udev_device_get_name(info) - log_method_call(self, name=name) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - device = None - - for dmdev in self.devices: - if not isinstance(dmdev, DMDevice): - continue - - try: - # there is a device in the tree already with the same - # major/minor as this one but with a different name - # XXX this is kind of racy - if dmdev.getDMNode() == os.path.basename(sysfs_path): - # XXX should we take the name already in use? - device = dmdev - break - except DMError: - # This is a little lame, but the VG device is a DMDevice - # and it won't have a dm node. At any rate, this is not - # important enough to crash the install. - log.debug("failed to find dm node for %s" % dmdev.name) - continue - - cleanup_luks = udev_device_is_dm_luks(info) and self._cleanup - slave_dev = None - slave_info = None - if device is None: - # we couldn't find it, so create it - # first, get a list of the slave devs and look them up - dir = os.path.normpath("/sys/%s/slaves" % sysfs_path) - slave_names = os.listdir(dir) - for slave_name in slave_names: - # if it's a dm-X name, resolve it to a map name first - if slave_name.startswith("dm-"): - dev_name = dm.name_from_dm_node(slave_name) - else: - dev_name = slave_name.replace("!", "/") # handles cciss - slave_dev = self.getDeviceByName(dev_name) - path = os.path.normpath("%s/%s" % (dir, slave_name)) - new_info = udev_get_block_device(os.path.realpath(path)[4:]) - if not slave_dev: - # we haven't scanned the slave yet, so do it now - if new_info: - self.addUdevDevice(new_info) - slave_dev = self.getDeviceByName(dev_name) - if slave_dev is None: - # if the current slave is still not in - # the tree, something has gone wrong - log.error("failure scanning device %s: could not add slave %s" % (name, dev_name)) - return - - if cleanup_luks: - slave_info = new_info - - # try to get the device again now that we've got all the slaves - device = self.getDeviceByName(name) - - if device is None and udev_device_is_dm_partition(info): - diskname = udev_device_get_dm_partition_disk(info) - disk = self.getDeviceByName(diskname) - return self.addUdevPartitionDevice(info, disk=disk) - - # if this is a luks device whose map name is not what we expect, - # fix up the map name and see if that sorts us out - if device is None and cleanup_luks and slave_info and slave_dev: - slave_dev.format.mapName = name - self.handleUdevLUKSFormat(slave_info, slave_dev) - - # try once more to get the device - device = self.getDeviceByName(name) - - # create a device for the livecd OS image(s) - if device is None and udev_device_is_dm_livecd(info): - device = DMDevice(name, dmUuid=info.get('DM_UUID'), - sysfsPath=sysfs_path, exists=True, - parents=[slave_dev]) - device.protected = True - device.controllable = False - self._addDevice(device) - - # if we get here, we found all of the slave devices and - # something must be wrong -- if all of the slaves are in - # the tree, this device should be as well - if device is None: - devicelibs.lvm.lvm_cc_addFilterRejectRegexp(name) - log.warning("ignoring dm device %s" % name) - - return device - - def addUdevMDDevice(self, info): - name = udev_device_get_md_name(info) - log_method_call(self, name=name) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - device = None - - slaves = [] - dir = os.path.normpath("/sys/%s/slaves" % sysfs_path) - slave_names = os.listdir(dir) - for slave_name in slave_names: - # if it's a dm-X name, resolve it to a map name - if slave_name.startswith("dm-"): - dev_name = dm.name_from_dm_node(slave_name) - else: - dev_name = slave_name - slave_dev = self.getDeviceByName(dev_name) - if slave_dev: - slaves.append(slave_dev) - else: - # we haven't scanned the slave yet, so do it now - path = os.path.normpath("%s/%s" % (dir, slave_name)) - new_info = udev_get_block_device(os.path.realpath(path)[4:]) - if new_info: - self.addUdevDevice(new_info) - if self.getDeviceByName(dev_name) is None: - # if the current slave is still not in - # the tree, something has gone wrong - log.error("failure scanning device %s: could not add slave %s" % (name, dev_name)) - return - - # try to get the device again now that we've got all the slaves - device = self.getDeviceByName(name) - - if device is None: - device = self.getDeviceByUuid(info.get("MD_UUID")) - if device: - raise DeviceTreeError("MD RAID device %s already in " - "devicetree as %s" % (name, device.name)) - - # if we get here, we found all of the slave devices and - # something must be wrong -- if all of the slaves we in - # the tree, this device should be as well - if device is None: - if name is None: - name = udev_device_get_name(info) - - log.error("failed to scan md array %s" % name) - try: - devicelibs.mdraid.mddeactivate("/dev/" + name) - except MDRaidError: - log.error("failed to stop broken md array %s" % name) - - return device - - def addUdevPartitionDevice(self, info, disk=None): - name = udev_device_get_name(info) - log_method_call(self, name=name) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - device = None - - if name.startswith("md"): - name = devicelibs.mdraid.name_from_md_node(name) - device = self.getDeviceByName(name) - if device: - return device - - if disk is None: - disk_name = os.path.basename(os.path.dirname(sysfs_path)) - disk_name = disk_name.replace('!','/') - if disk_name.startswith("md"): - disk_name = devicelibs.mdraid.name_from_md_node(disk_name) - - disk = self.getDeviceByName(disk_name) - - if disk is None: - # create a device instance for the disk - new_info = udev_get_block_device(os.path.dirname(sysfs_path)) - if new_info: - self.addUdevDevice(new_info) - disk = self.getDeviceByName(disk_name) - - if disk is None: - # if the current device is still not in - # the tree, something has gone wrong - log.error("failure scanning device %s" % disk_name) - devicelibs.lvm.lvm_cc_addFilterRejectRegexp(name) - return - - # Sun disklabels have a partition that spans the entire disk as - # partition 3. It does not appear in the partition list. Fantastic. - is_sun_magic = (getattr(disk.format, "labelType", None) == "sun" and - udev_device_get_minor(info) == 3) - - # Check that the disk has partitions. If it does not, we must have - # reinitialized the disklabel. - # - # Also ignore partitions on devices we do not support partitioning - # of, like logical volumes. - if ((not getattr(disk.format, "partitions", None) and not is_sun_magic) - or not disk.partitionable): - # When we got here because the disk does not have a disklabel - # format (ie a biosraid member), or because it is not - # partitionable we want LVM to ignore this partition too - if disk.format.type != "disklabel" or not disk.partitionable: - devicelibs.lvm.lvm_cc_addFilterRejectRegexp(name) - log.debug("ignoring partition %s" % name) - return - - try: - device = PartitionDevice(name, sysfsPath=sysfs_path, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - exists=True, parents=[disk]) - except DeviceError as e: - # corner case sometime the kernel accepts a partition table - # which gets rejected by parted, in this case we will - # prompt to re-initialize the disk, so simply skip the - # faulty partitions. - log.error("Failed to instantiate PartitionDevice: %s" % e) - return - - self._addDevice(device) - return device - - def addUdevDiskDevice(self, info): - name = udev_device_get_name(info) - log_method_call(self, name=name) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - serial = udev_device_get_serial(info) - bus = udev_device_get_bus(info) - - # udev doesn't always provide a vendor. - vendor = udev_device_get_vendor(info) - if not vendor: - vendor = "" - - device = None - - kwargs = { "serial": serial, "vendor": vendor, "bus": bus } - if udev_device_is_iscsi(info): - diskType = iScsiDiskDevice - initiator = udev_device_get_iscsi_initiator(info) - target = udev_device_get_iscsi_name(info) - address = udev_device_get_iscsi_address(info) - port = udev_device_get_iscsi_port(info) - nic = udev_device_get_iscsi_nic(info) - kwargs["initiator"] = initiator - if initiator == self.iscsi.initiator: - node = self.iscsi.getNode(target, address, port, nic) - kwargs["node"] = node - kwargs["ibft"] = node in self.iscsi.ibftNodes - kwargs["nic"] = self.iscsi.ifaces.get(node.iface, node.iface) - log.info("%s is an iscsi disk" % name) - else: - # qla4xxx partial offload - kwargs["node"] = None - kwargs["ibft"] = False - kwargs["nic"] = "offload:not_accessible_via_iscsiadm" - kwargs["fw_address"] = address - kwargs["fw_port"] = port - kwargs["fw_name"] = name - elif udev_device_is_fcoe(info): - diskType = FcoeDiskDevice - kwargs["nic"] = udev_device_get_fcoe_nic(info) - kwargs["identifier"] = udev_device_get_fcoe_identifier(info) - log.info("%s is an fcoe disk" % name) - elif udev_device_get_md_container(info): - name = udev_device_get_md_name(info) - diskType = MDRaidArrayDevice - parentPath = udev_device_get_md_container(info) - parentName = devicePathToName(parentPath) - container = self.getDeviceByName(parentName) - if not container: - parentSysName = devicelibs.mdraid.md_node_from_name(parentName) - container_sysfs = "/class/block/" + parentSysName - container_info = udev_get_block_device(container_sysfs) - if not container_info: - log.error("failed to find md container %s at %s" - % (parentName, container_sysfs)) - return - - self.addUdevDevice(container_info) - container = self.getDeviceByName(parentName) - if not container: - log.error("failed to scan md container %s" % parentName) - return - - kwargs["parents"] = [container] - kwargs["level"] = udev_device_get_md_level(info) - kwargs["memberDevices"] = int(udev_device_get_md_devices(info)) - kwargs["uuid"] = udev_device_get_md_uuid(info) - kwargs["exists"] = True - del kwargs["serial"] - del kwargs["vendor"] - del kwargs["bus"] - elif udev_device_is_dasd(info): - diskType = DASDDevice - kwargs["dasd"] = self.dasd - kwargs["busid"] = udev_device_get_dasd_bus_id(info) - kwargs["opts"] = {} - - for attr in ['readonly', 'use_diag', 'erplog', 'failfast']: - kwargs["opts"][attr] = udev_device_get_dasd_flag(info, attr) - - log.info("%s is a dasd device" % name) - elif udev_device_is_zfcp(info): - diskType = ZFCPDiskDevice - - for attr in ['hba_id', 'wwpn', 'fcp_lun']: - kwargs[attr] = udev_device_get_zfcp_attribute(info, attr=attr) - - log.info("%s is a zfcp device" % name) - else: - diskType = DiskDevice - log.info("%s is a disk" % name) - - device = diskType(name, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - sysfsPath=sysfs_path, **kwargs) - self._addDevice(device) - return device - - def addUdevOpticalDevice(self, info): - log_method_call(self) - # XXX should this be RemovableDevice instead? - # - # Looks like if it has ID_INSTANCE=0:1 we can ignore it. - device = OpticalDevice(udev_device_get_name(info), - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - sysfsPath=udev_device_get_sysfs_path(info), - vendor=udev_device_get_vendor(info), - model=udev_device_get_model(info)) - self._addDevice(device) - return device - - def addUdevLoopDevice(self, info): - name = udev_device_get_name(info) - log_method_call(self, name=name) - sysfs_path = udev_device_get_sysfs_path(info) - sys_file = "/sys/%s/loop/backing_file" % sysfs_path - backing_file = open(sys_file).read().strip() - file_device = self.getDeviceByName(backing_file) - if not file_device: - file_device = FileDevice(backing_file, exists=True) - self._addDevice(file_device) - device = LoopDevice(name, - parents=[file_device], - sysfsPath=sysfs_path, - exists=True) - if not self._cleanup or file_device not in self.diskImages.values(): - # don't allow manipulation of loop devices other than those - # associated with disk images, and then only during cleanup - file_device.controllable = False - device.controllable = False - self._addDevice(device) - return device - - def addUdevDevice(self, info): - name = udev_device_get_name(info) - log_method_call(self, name=name, info=pprint.pformat(info)) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - - # make sure we note the name of every device we see - if name not in self.names: - self.names.append(name) - - if self.isIgnored(info): - log.info("ignoring %s (%s)" % (name, sysfs_path)) - if name not in self._ignoredDisks: - self.addIgnoredDisk(name) - - if udev_device_is_multipath_member(info): - # last time we are seeing this mpath member is now, so make sure - # LVM ignores its partitions too else a duplicate VG name could - # harm us later during partition creation: - if udev_device_is_dm(info): - path = "/dev/mapper/%s" % name - else: - path = "/dev/%s" % name - log.debug("adding partitions on %s to the lvm ignore list" % path) - partitions_paths = [] - try: - partitions_paths = [p.path - for p in parted.Disk(device=parted.Device(path=path)).partitions] - except (_ped.IOException, _ped.DeviceException, _ped.DiskLabelException) as e: - log.error("Parted error scanning partitions on %s:" % path) - log.error(str(e)) - # slice off the "/dev/" part, lvm filter cares only about the rest - partitions_paths = [p[5:] for p in partitions_paths] - map(lvm.lvm_cc_addFilterRejectRegexp, partitions_paths) - return - - log.info("scanning %s (%s)..." % (name, sysfs_path)) - device = self.getDeviceByName(name) - - # - # The first step is to either look up or create the device - # - if device: - # we successfully looked up the device. skip to format handling. - # first, grab the parted.Device while it's active - _unused = device.partedDevice - elif udev_device_is_loop(info): - log.info("%s is a loop device" % name) - device = self.addUdevLoopDevice(info) - elif udev_device_is_multipath_member(info): - device = self.addUdevDiskDevice(info) - elif udev_device_is_dm(info) and udev_device_is_dm_mpath(info): - log.info("%s is a multipath device" % name) - device = self.addUdevDMDevice(info) - elif udev_device_is_dm_lvm(info): - log.info("%s is an lvm logical volume" % name) - device = self.addUdevLVDevice(info) - elif udev_device_is_dm(info): - log.info("%s is a device-mapper device" % name) - device = self.addUdevDMDevice(info) - elif udev_device_is_md(info) and not udev_device_get_md_container(info): - log.info("%s is an md device" % name) - if uuid: - # try to find the device by uuid - device = self.getDeviceByUuid(uuid) - - if device is None: - device = self.addUdevMDDevice(info) - elif udev_device_is_cdrom(info): - log.info("%s is a cdrom" % name) - device = self.addUdevOpticalDevice(info) - elif udev_device_is_biosraid_member(info) and udev_device_is_disk(info): - log.info("%s is part of a biosraid" % name) - device = DiskDevice(name, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - sysfsPath=sysfs_path, exists=True) - self._addDevice(device) - elif udev_device_is_disk(info): - device = self.addUdevDiskDevice(info) - elif udev_device_is_partition(info): - log.info("%s is a partition" % name) - device = self.addUdevPartitionDevice(info) - else: - log.error("Unknown block device type for: %s" % name) - return - - # If this device is protected, mark it as such now. Once the tree - # has been populated, devices' protected attribute is how we will - # identify protected devices. - if device and device.name in self.protectedDevNames: - device.protected = True - # if this is the live backing device we want to mark its parents - # as protected also - if device.name == self.liveBackingDevice: - for parent in device.parents: - parent.protected = True - - # Don't try to do format handling on drives without media or - # if we didn't end up with a device somehow. - if not device or not device.mediaPresent: - log.debug("no device or no media present") - return - - # now handle the device's formatting - self.handleUdevDeviceFormat(info, device) - log.info("got device: %r" % device) - if device.format.type: - log.info("got format: %r" % device.format) - device.originalFormat = copy.copy(device.format) - - def handleUdevDiskLabelFormat(self, info, device): - disklabel_type = info.get("ID_PART_TABLE_TYPE") - log_method_call(self, device=device.name, label_type=disklabel_type) - # if there is no disklabel on the device - if disklabel_type is None and \ - getFormat(udev_device_get_format(info)).type is not None: - log.debug("device %s does not contain a disklabel" % device.name) - return - - if device.partitioned: - # this device is already set up - log.debug("disklabel format on %s already set up" % device.name) - return - - try: - device.setup() - except Exception as e: - log.debug("setup of %s failed: %s" % (device.name, e)) - log.warning("aborting disklabel handler for %s" % device.name) - return - - # special handling for unsupported partitioned devices - if not device.partitionable: - try: - format = getFormat("disklabel", - device=device.path, - labelType=disklabel_type, - exists=True) - except InvalidDiskLabelError: - log.warning("disklabel detected but not usable on %s" - % device.name) - pass - return - - # we're going to pass the "best" disklabel type into the DiskLabel - # constructor, but it only has meaning for non-existent disklabels. - labelType = platform.bestDiskLabelType(device) - - try: - format = getFormat("disklabel", - device=device.path, - labelType=labelType, - exists=True) - except InvalidDiskLabelError: - log.info("no usable disklabel on %s" % device.name) - return - else: - device.format = format - - def handleUdevLUKSFormat(self, info, device): - log_method_call(self, name=device.name, type=device.format.type) - if not device.format.uuid: - log.info("luks device %s has no uuid" % device.path) - return - - # look up or create the mapped device - if not self.getDeviceByName(device.format.mapName): - passphrase = self.__luksDevs.get(device.format.uuid) - if device.format.configured: - pass - elif passphrase: - device.format.passphrase = passphrase - elif device.format.uuid in self.__luksDevs: - log.info("skipping previously-skipped luks device %s" - % device.name) - elif self._cleanup or flags.testing: - # if we're only building the devicetree so that we can - # tear down all of the devices we don't need a passphrase - if device.format.status: - # this makes device.configured return True - device.format.passphrase = 'yabbadabbadoo' - else: - # Try each known passphrase. - for passphrase in self.__passphrases: - device.format.passphrase = passphrase - try: - device.format.setup() - except CryptoError: - device.format.passphrase = None - else: - break - - luks_device = LUKSDevice(device.format.mapName, - parents=[device], - exists=True) - try: - luks_device.setup() - except (LUKSError, CryptoError, DeviceError) as e: - log.info("setup of %s failed: %s" % (device.format.mapName, - e)) - device.removeChild() - else: - self._addDevice(luks_device) - else: - log.warning("luks device %s already in the tree" - % device.format.mapName) - - def handleVgLvs(self, vg_device): - """ Handle setup of the LV's in the vg_device - return True if an LV was setup - return False if there was an error, or no more LV's to setup - """ - ret = False - vg_name = vg_device.name - lv_names = vg_device.lv_names - lv_uuids = vg_device.lv_uuids - lv_sizes = vg_device.lv_sizes - lv_attr = vg_device.lv_attr - - if not vg_device.complete: - log.warning("Skipping LVs for incomplete VG %s" % vg_name) - return False - - if not lv_names: - log.debug("no LVs listed for VG %s" % vg_name) - return False - - def lv_attr_cmp(a, b): - """ Sort so that mirror images come first and snapshots last. """ - mirror_chars = "Iil" - snapshot_chars = "Ss" - if a[0] in mirror_chars and b[0] not in mirror_chars: - return -1 - elif a[0] not in mirror_chars and b[0] in mirror_chars: - return 1 - elif a[0] not in snapshot_chars and b[0] in snapshot_chars: - return -1 - elif a[0] in snapshot_chars and b[0] not in snapshot_chars: - return 1 - else: - return 0 - - # make a list of indices with mirror volumes up front and snapshots at - # the end - indices = range(len(lv_names)) - indices.sort(key=lambda i: lv_attr[i], cmp=lv_attr_cmp) - mirrors = {} - for index in indices: - lv_name = lv_names[index] - name = "%s-%s" % (vg_name, lv_name) - if lv_attr[index][0] in 'Ss': - log.info("found lvm snapshot volume '%s'" % name) - origin_name = devicelibs.lvm.lvorigin(vg_name, lv_name) - if not origin_name: - log.error("lvm snapshot '%s-%s' has unknown origin" - % (vg_name, lv_name)) - continue - - origin = self.getDeviceByName("%s-%s" % (vg_name, - origin_name)) - if not origin: - if origin_name.endswith("_vorigin]"): - log.info("snapshot volume '%s' has vorigin" % name) - vg_device.voriginSnapshots[lv_name] = lv_sizes[index] - else: - log.warning("snapshot lv '%s' origin lv '%s-%s' " - "not found" % (name, - vg_name, origin_name)) - continue - - log.debug("adding %dMB to %s snapshot total" - % (lv_sizes[index], origin.name)) - origin.snapshotSpace += lv_sizes[index] - continue - elif lv_attr[index][0] == 'v': - # skip vorigins - continue - elif lv_attr[index][0] in 'Ii': - # mirror image - lv_name = re.sub(r'_mimage.+', '', lv_name[1:-1]) - name = "%s-%s" % (vg_name, lv_name) - if name not in mirrors: - mirrors[name] = {"stripes": 0, "log": 0} - - mirrors[name]["stripes"] += 1 - elif lv_attr[index][0] == 'l': - # log volume - lv_name = re.sub(r'_mlog.*', '', lv_name[1:-1]) - name = "%s-%s" % (vg_name, lv_name) - if name not in mirrors: - mirrors[name] = {"stripes": 0, "log": 0} - - mirrors[name]["log"] = lv_sizes[index] - - lv_dev = self.getDeviceByName(name) - if lv_dev is None: - lv_uuid = lv_uuids[index] - lv_size = lv_sizes[index] - lv_device = LVMLogicalVolumeDevice(lv_name, - vg_device, - uuid=lv_uuid, - size=lv_size, - exists=True) - self._addDevice(lv_device) - lv_device.setup() - ret = True - - for name, mirror in mirrors.items(): - lv_dev = self.getDeviceByName(name) - lv_dev.stripes = mirror["stripes"] - lv_dev.logSize = mirror["log"] - log.debug("set %s stripes to %d, log size to %dMB, total size %dMB" - % (lv_dev.name, lv_dev.stripes, lv_dev.logSize, - lv_dev.vgSpaceUsed)) - - return ret - - def handleUdevLVMPVFormat(self, info, device): - log_method_call(self, name=device.name, type=device.format.type) - # lookup/create the VG and LVs - try: - vg_name = udev_device_get_vg_name(info) - vg_uuid = udev_device_get_vg_uuid(info) - except KeyError: - # no vg name means no vg -- we're done with this pv - return - - vg_device = self.getDeviceByUuid(vg_uuid) - if vg_device: - vg_device._addDevice(device) - else: - try: - vg_size = udev_device_get_vg_size(info) - vg_free = udev_device_get_vg_free(info) - pe_size = udev_device_get_vg_extent_size(info) - pe_count = udev_device_get_vg_extent_count(info) - pe_free = udev_device_get_vg_free_extents(info) - pv_count = udev_device_get_vg_pv_count(info) - except (KeyError, ValueError) as e: - log.warning("invalid data for %s: %s" % (device.name, e)) - return - - vg_device = LVMVolumeGroupDevice(vg_name, - device, - uuid=vg_uuid, - size=vg_size, - free=vg_free, - peSize=pe_size, - peCount=pe_count, - peFree=pe_free, - pvCount=pv_count, - exists=True) - self._addDevice(vg_device) - - # Now we add any lv info found in this pv to the vg_device, we - # do this for all pvs as pvs only contain lv info for lvs which they - # contain themselves - try: - lv_names = udev_device_get_lv_names(info) - lv_uuids = udev_device_get_lv_uuids(info) - lv_sizes = udev_device_get_lv_sizes(info) - lv_attr = udev_device_get_lv_attr(info) - except KeyError as e: - log.warning("invalid data for %s: %s" % (device.name, e)) - return - - for i in range(len(lv_names)): - # Skip empty and already added lvs - if not lv_names[i] or lv_names[i] in vg_device.lv_names: - continue - - vg_device.lv_names.append(lv_names[i]) - vg_device.lv_uuids.append(lv_uuids[i]) - vg_device.lv_sizes.append(lv_sizes[i]) - vg_device.lv_attr.append(lv_attr[i]) - - name = "%s-%s" % (vg_name, lv_names[i]) - if name not in self.names: - self.names.append(name) - - def handleUdevMDMemberFormat(self, info, device): - log_method_call(self, name=device.name, type=device.format.type) - # either look up or create the array device - name = udev_device_get_name(info) - sysfs_path = udev_device_get_sysfs_path(info) - - md_array = self.getDeviceByUuid(device.format.mdUuid) - if device.format.mdUuid and md_array: - md_array._addDevice(device) - else: - # create the array with just this one member - try: - # level is reported as, eg: "raid1" - md_level = udev_device_get_md_level(info) - md_devices = int(udev_device_get_md_devices(info)) - md_uuid = udev_device_get_md_uuid(info) - except (KeyError, ValueError) as e: - log.warning("invalid data for %s: %s" % (name, e)) - return - - md_name = None - md_metadata = None - - # check the list of devices udev knows about to see if the array - # this device belongs to is already active - for dev in udev_get_block_devices(): - if not udev_device_is_md(dev): - continue - - try: - dev_uuid = udev_device_get_md_uuid(dev) - dev_level = udev_device_get_md_level(dev) - except KeyError: - continue - - if dev_uuid is None or dev_level is None: - continue - - if dev_uuid == md_uuid and dev_level == md_level: - md_name = udev_device_get_md_name(dev) - md_metadata = dev.get("MD_METADATA") - break - - md_info = devicelibs.mdraid.mdexamine(device.path) - if not md_metadata: - md_metadata = md_info.get("metadata", "0.90") - - if not md_name: - md_path = md_info.get("device", "") - if md_path: - md_name = devicePathToName(md_path) - if re.match(r'md\d+$', md_name): - # md0 -> 0 - md_name = md_name[2:] - - if md_name: - array = self.getDeviceByName(md_name) - if array and array.uuid != md_uuid: - log.error("found multiple devices with the name %s" - % md_name) - - log.info("using name %s for md array containing member %s" - % (md_name, device.name)) - try: - md_array = MDRaidArrayDevice(md_name, - level=md_level, - memberDevices=md_devices, - uuid=md_uuid, - metadataVersion=md_metadata, - exists=True) - except ValueError as e: - log.error("failed to create md array: %s" % e) - return - - md_array.updateSysfsPath() - md_array._addDevice(device) - self._addDevice(md_array) - - def handleMultipathMemberFormat(self, info, device): - log_method_call(self, name=device.name, type=device.format.type) - - name = udev_device_get_multipath_name(info) - if self.__multipaths.has_key(name): - mp = self.__multipaths[name] - mp.addParent(device) - else: - mp = MultipathDevice(name, info, parents=[device]) - self.__multipaths[name] = mp - - def handleUdevDMRaidMemberFormat(self, info, device): - log_method_call(self, name=device.name, type=device.format.type) - name = udev_device_get_name(info) - sysfs_path = udev_device_get_sysfs_path(info) - uuid = udev_device_get_uuid(info) - major = udev_device_get_major(info) - minor = udev_device_get_minor(info) - - def _all_ignored(rss): - retval = True - for rs in rss: - if rs.name not in self._ignoredDisks: - retval = False - break - return retval - - # Have we already created the DMRaidArrayDevice? - rss = block.getRaidSetFromRelatedMem(uuid=uuid, name=name, - major=major, minor=minor) - if len(rss) == 0: - # we ignore the device in the hope that all the devices - # from this set will be ignored. - self.unusedRaidMembers.append(device.name) - self.addIgnoredDisk(device.name) - return - - # We ignore the device if all the rss are in self._ignoredDisks - if _all_ignored(rss): - self.addIgnoredDisk(device.name) - return - - for rs in rss: - dm_array = self.getDeviceByName(rs.name) - if dm_array is not None: - # We add the new device. - dm_array._addDevice(device) - else: - # Activate the Raid set. - rs.activate(mknod=True) - dm_array = DMRaidArrayDevice(rs.name, - raidSet=rs, - parents=[device]) - - self._addDevice(dm_array) - - # Wait for udev to scan the just created nodes, to avoid a race - # with the udev_get_block_device() call below. - udev_settle() - - # Get the DMRaidArrayDevice a DiskLabel format *now*, in case - # its partitions get scanned before it does. - dm_array.updateSysfsPath() - dm_array_info = udev_get_block_device(dm_array.sysfsPath) - self.handleUdevDiskLabelFormat(dm_array_info, dm_array) - - # Use the rs's object on the device. - # pyblock can return the memebers of a set and the - # device has the attribute to hold it. But ATM we - # are not really using it. Commenting this out until - # we really need it. - #device.format.raidmem = block.getMemFromRaidSet(dm_array, - # major=major, minor=minor, uuid=uuid, name=name) - - def handleBTRFSFormat(self, info, device): - log_method_call(self, name=device.name) - name = udev_device_get_name(info) - sysfs_path = udev_device_get_sysfs_path(info) - uuid = udev_device_get_uuid(info) - - btrfs_dev = None - for d in self.devices: - if isinstance(d, BTRFSVolumeDevice) and d.uuid == uuid: - btrfs_dev = d - break - - if btrfs_dev: - log.info("found btrfs volume %s" % btrfs_dev.name) - btrfs_dev._addDevice(device) - else: - label = udev_device_get_label(info) - log.info("creating btrfs volume btrfs.%s" % label) - btrfs_dev = BTRFSVolumeDevice(label, parents=[device], uuid=uuid, - exists=True) - self._addDevice(btrfs_dev) - - if not btrfs_dev.subvolumes: - for subvol_dict in btrfs_dev.listSubVolumes(): - vol_id = subvol_dict["id"] - vol_path = subvol_dict["path"] - if vol_path in [sv.name for sv in btrfs_dev.subvolumes]: - continue - fmt = getFormat("btrfs", device=btrfs_dev.path, exists=True, - mountopts="subvol=%s" % vol_path) - subvol = BTRFSSubVolumeDevice(vol_path, - vol_id=vol_id, - format=fmt, - parents=[btrfs_dev], - exists=True) - self._addDevice(subvol) - - def handleUdevDeviceFormat(self, info, device): - log_method_call(self, name=getattr(device, "name", None)) - name = udev_device_get_name(info) - sysfs_path = udev_device_get_sysfs_path(info) - uuid = udev_device_get_uuid(info) - label = udev_device_get_label(info) - format_type = udev_device_get_format(info) - serial = udev_device_get_serial(info) - - # Now, if the device is a disk, see if there is a usable disklabel. - # If not, see if the user would like to create one. - # XXX ignore disklabels on multipath or biosraid member disks - if not udev_device_is_biosraid_member(info) and \ - not udev_device_is_multipath_member(info): - self.handleUdevDiskLabelFormat(info, device) - if device.partitioned or self.isIgnored(info) or \ - (not device.partitionable and - device.format.type == "disklabel"): - # If the device has a disklabel, or the user chose not to - # create one, we are finished with this device. Otherwise - # it must have some non-disklabel formatting, in which case - # we fall through to handle that. - return - - format = None - if (not device) or (not format_type) or device.format.type: - # this device has no formatting or it has already been set up - # FIXME: this probably needs something special for disklabels - log.debug("no type or existing type for %s, bailing" % (name,)) - return - - # set up the common arguments for the format constructor - args = [format_type] - kwargs = {"uuid": uuid, - "label": label, - "device": device.path, - "serial": serial, - "exists": True} - - # set up type-specific arguments for the format constructor - if format_type == "multipath_member": - kwargs["multipath_members"] = self.getDevicesBySerial(serial) - elif format_type == "crypto_LUKS": - # luks/dmcrypt - kwargs["name"] = "luks-%s" % uuid - elif format_type in formats.mdraid.MDRaidMember._udevTypes: - # mdraid - try: - kwargs["mdUuid"] = udev_device_get_md_uuid(info) - except KeyError: - log.warning("mdraid member %s has no md uuid" % name) - kwargs["biosraid"] = udev_device_is_biosraid_member(info) - elif format_type == "LVM2_member": - # lvm - try: - kwargs["vgName"] = udev_device_get_vg_name(info) - except KeyError as e: - log.warning("PV %s has no vg_name" % name) - try: - kwargs["vgUuid"] = udev_device_get_vg_uuid(info) - except KeyError: - log.warning("PV %s has no vg_uuid" % name) - try: - kwargs["peStart"] = udev_device_get_pv_pe_start(info) - except KeyError: - log.warning("PV %s has no pe_start" % name) - elif format_type == "vfat": - # efi magic - if isinstance(device, PartitionDevice) and device.bootable: - efi = formats.getFormat("efi") - if efi.minSize <= device.size <= efi.maxSize: - args[0] = "efi" - elif format_type == "hfs": - # apple bootstrap magic - if isinstance(device, PartitionDevice) and device.bootable: - apple = formats.getFormat("appleboot") - if apple.minSize <= device.size <= apple.maxSize: - args[0] = "appleboot" - elif format_type == "btrfs": - # the format's uuid attr will contain the UUID_SUB, while the - # overarching volume UUID will be stored as volUUID - kwargs["uuid"] = info["ID_FS_UUID_SUB"] - kwargs["volUUID"] = uuid - - try: - log.info("type detected on '%s' is '%s'" % (name, format_type,)) - device.format = formats.getFormat(*args, **kwargs) - except FSError: - log.warning("type '%s' on '%s' invalid, assuming no format" % - (format_type, name,)) - device.format = formats.DeviceFormat() - return - - # - # now do any special handling required for the device's format - # - if device.format.type == "luks": - self.handleUdevLUKSFormat(info, device) - elif device.format.type == "mdmember": - self.handleUdevMDMemberFormat(info, device) - elif device.format.type == "dmraidmember": - self.handleUdevDMRaidMemberFormat(info, device) - elif device.format.type == "lvmpv": - self.handleUdevLVMPVFormat(info, device) - elif device.format.type == "multipath_member": - self.handleMultipathMemberFormat(info, device) - elif device.format.type == "btrfs": - self.handleBTRFSFormat(info, device) - - def updateDeviceFormat(self, device): - log.info("updating format of device: %s" % device) - try: - util.notify_kernel("/sys%s" % device.sysfsPath) - except (ValueError, IOError) as e: - log.warning("failed to notify kernel of change: %s" % e) - - udev_settle() - info = udev_get_device(device.sysfsPath) - self.handleUdevDeviceFormat(info, device) - if device.format.type: - log.info("got format: %s" % device.format) - - def _handleInconsistencies(self): - for vg in [d for d in self.devices if d.type == "lvmvg"]: - if vg.complete: - continue - - # Make sure lvm doesn't get confused by PVs that belong to - # incomplete VGs. We will remove the PVs from the blacklist when/if - # the time comes to remove the incomplete VG and its PVs. - for pv in vg.pvs: - devicelibs.lvm.lvm_cc_addFilterRejectRegexp(pv.name) - - def hide(self, device): - for d in self.getChildren(device): - self.hide(d) - - log.info("hiding device %s %s (id %d)" % (device.type, - device.name, - device.id)) - - for action in reversed(self._actions): - if not action.device.dependsOn(device) and action.device != device: - continue - - log.debug("cancelling action: %s" % action) - try: - action.cancel() - except Exception: - log.warning("failed to cancel action while hiding %s: %s" - % (device.name, action)) - finally: - self._actions.remove(action) - - # XXX modifications that do not require actions, like setting a - # mountpoint, will not be reversed here - - # we're intentionally not modifying self.names here - self._devices.remove(device) - self._hidden.append(device) - lvm.lvm_cc_addFilterRejectRegexp(device.name) - for parent in device.parents: - parent.removeChild() - - def unhide(self, device): - # the hidden list should be in leaves-first order - for hidden in reversed(self._hidden): - if hidden == device or hidden.dependsOn(device): - log.info("unhiding device %s %s (id %d)" % (hidden.type, - hidden.name, - hidden.id)) - self._hidden.remove(hidden) - self._devices.append(hidden) - lvm.lvm_cc_removeFilterRejectRegexp(hidden.name) - for parent in device.parents: - parent.addChild() - - def _setupLvs(self): - ret = False - - for device in self.getDevicesByType("lvmvg"): - if self.handleVgLvs(device): - ret = True - - return ret - - def setupDiskImages(self): - """ Set up devices to represent the disk image files. """ - for (name, path) in self.diskImages.items(): - log.info("setting up disk image file '%s' as '%s'" % (path, name)) - try: - filedev = FileDevice(path, exists=True) - filedev.setup() - log.debug("%s" % filedev) - - loop_name = devicelibs.loop.get_loop_name(filedev.path) - loop_sysfs = None - if loop_name: - loop_sysfs = "/class/block/%s" % loop_name - loopdev = LoopDevice(name=loop_name, - parents=[filedev], - sysfsPath=loop_sysfs, - exists=True) - loopdev.setup() - log.debug("%s" % loopdev) - dmdev = DMLinearDevice(name, - dmUuid="ANACONDA-%s" % name, - parents=[loopdev], - exists=True) - dmdev.setup() - dmdev.updateSysfsPath() - log.debug("%s" % dmdev) - except (ValueError, DeviceError) as e: - log.error("failed to set up disk image: %s" % e) - else: - self._addDevice(filedev) - self._addDevice(loopdev) - self._addDevice(dmdev) - info = udev_get_block_device(dmdev.sysfsPath) - self.addUdevDevice(info) - - def backupConfigs(self, restore=False): - """ Create a backup copies of some storage config files. """ - configs = ["/etc/mdadm.conf", "/etc/multipath.conf"] - for cfg in configs: - if restore: - src = cfg + ".anacbak" - dst = cfg - func = os.rename - op = "restore from backup" - else: - src = cfg - dst = cfg + ".anacbak" - func = shutil.copy2 - op = "create backup copy" - - if os.access(dst, os.W_OK): - try: - os.unlink(dst) - except OSError as e: - msg = str(e) - log.info("failed to remove %s: %s" % (dst, msg)) - - if os.access(src, os.W_OK): - # copy the config to a backup with extension ".anacbak" - try: - func(src, dst) - except (IOError, OSError) as e: - msg = str(e) - log.error("failed to %s of %s: %s" % (op, cfg, msg)) - elif restore and os.access(cfg, os.W_OK): - # remove the config since we created it - log.info("removing anaconda-created %s" % cfg) - try: - os.unlink(cfg) - except OSError as e: - msg = str(e) - log.error("failed to remove %s: %s" % (cfg, msg)) - else: - # don't try to backup non-existent configs - log.info("not going to %s of non-existent %s" % (op, cfg)) - - def restoreConfigs(self): - self.backupConfigs(restore=True) - - def populate(self, cleanupOnly=False): - """ Locate all storage devices. """ - self.backupConfigs() - if cleanupOnly: - self._cleanup = True - - try: - self._populate() - except Exception: - raise - finally: - self.restoreConfigs() - - def _populate(self): - log.info("DeviceTree.populate: ignoredDisks is %s ; exclusiveDisks is %s" - % (self._ignoredDisks, self.exclusiveDisks)) - - self.setupDiskImages() - - # mark the tree as unpopulated so exception handlers can tell the - # exception originated while finding storage devices - self.populated = False - - # resolve the protected device specs to device names - for spec in self.protectedDevSpecs: - name = udev_resolve_devspec(spec) - log.debug("protected device spec %s resolved to %s" % (spec, name)) - if name: - self.protectedDevNames.append(name) - - # FIXME: the backing dev for the live image can't be used as an - # install target. note that this is a little bit of a hack - # since we're assuming that /run/initramfs/live will exist - for mnt in open("/proc/mounts").readlines(): - if " /run/initramfs/live " not in mnt: - continue - - live_device_name = mnt.split()[0].split("/")[-1] - log.info("%s looks to be the live device; marking as protected" - % (live_device_name,)) - self.protectedDevNames.append(live_device_name) - self.liveBackingDevice = live_device_name - break - - old_devices = {} - - if os.access("/etc/multipath.conf", os.W_OK): - self.__multipathConfigWriter.writeConfig(self.mpathFriendlyNames) - self.topology = devicelibs.mpath.MultipathTopology(udev_get_block_devices()) - log.info("devices to scan: %s" % - [d['name'] for d in self.topology.devices_iter()]) - for dev in self.topology.devices_iter(): - # avoid the problems caused by encountering multipath devices in - # this loop by simply skipping all dm devices here - if dev['name'].startswith("dm-"): - log.debug("Skipping a device mapper drive (%s) for now" % dev['name']) - continue - - old_devices[dev['name']] = dev - self.addUdevDevice(dev) - - # Having found all the disks, we can now find all the multipaths built - # upon them. - whitelist = [] - mpaths = self.__multipaths.values() - mpaths.sort(key=lambda d: d.name) - for mp in mpaths: - log.info("adding mpath device %s" % mp.name) - mp.setup() - mp.updateSysfsPath() - mp_info = udev_get_block_device(mp.sysfsPath) - if mp_info is None or self.isIgnored(mp_info): - mp.teardown() - continue - - whitelist.append(mp.name) - for p in mp.parents: - whitelist.append(p.name) - self.__multipathConfigWriter.addMultipathDevice(mp) - self._addDevice(mp) - self.addUdevDevice(mp_info) - for d in self.devices: - if not d.name in whitelist: - self.__multipathConfigWriter.addBlacklistDevice(d) - self.__multipathConfigWriter.writeConfig(self.mpathFriendlyNames) - else: - log.info("Skipping multipath detection due to running as non-root.") - - # Now, loop and scan for devices that have appeared since the two above - # blocks or since previous iterations. - while True: - devices = [] - new_devices = udev_get_block_devices() - - for new_device in new_devices: - if not old_devices.has_key(new_device['name']): - old_devices[new_device['name']] = new_device - devices.append(new_device) - - if len(devices) == 0: - # nothing is changing -- time to setup lvm lvs and scan them - # we delay this till all other devices are scanned so that - # 1) the lvm filter for ignored disks is completely setup - # 2) we have checked all devs for duplicate vg names - if self._setupLvs(): - # remove any logical volume devices from old_devices so - # they will be re-scanned to get their formatting handled - for (old_name, old_device) in old_devices.items(): - if udev_device_is_dm_lvm(old_device): - del old_devices[old_name] - continue - # nothing is changing -- we are finished building devices - break - - log.info("devices to scan: %s" % [d['name'] for d in devices]) - for dev in devices: - self.addUdevDevice(dev) - - self.populated = True - - # After having the complete tree we make sure that the system - # inconsistencies are ignored or resolved. - self._handleInconsistencies() - - self.teardownAll() - - def teardownAll(self): - """ Run teardown methods on all devices. """ - for device in self.leaves: - if device.protected: - continue - - try: - device.teardown(recursive=True) - except StorageError as e: - log.info("teardown of %s failed: %s" % (device.name, e)) - - def setupAll(self): - """ Run setup methods on all devices. """ - for device in self.leaves: - try: - device.setup() - except DeviceError as (msg, name): - log.error("setup of %s failed: %s" % (device.name, msg)) - - def getDeviceBySysfsPath(self, path): - if not path: - return None - - found = None - for device in self._devices: - if device.sysfsPath == path: - found = device - break - - log_method_return(self, found) - return found - - def getDeviceByUuid(self, uuid): - if not uuid: - return None - - found = None - for device in self._devices: - if device.uuid == uuid: - found = device - break - elif device.format.uuid == uuid: - found = device - break - - log_method_return(self, found) - return found - - def getDevicesBySerial(self, serial): - devices = [] - for device in self._devices: - if not hasattr(device, "serial"): - log.warning("device %s has no serial attr" % device.name) - continue - if device.serial == serial: - devices.append(device) - - log_method_return(self, devices) - return devices - - def getDeviceByLabel(self, label): - if not label: - return None - - found = None - for device in self._devices: - _label = getattr(device.format, "label", None) - if not _label: - continue - - if _label == label: - found = device - break - - log_method_return(self, found) - return found - - def getDeviceByName(self, name): - log_method_call(self, name=name) - if not name: - log_method_return(self, None) - return None - - found = None - for device in self._devices: - if not getattr(device, "complete", True): - continue - - if device.name == name: - found = device - break - elif (device.type == "lvmlv" or device.type == "lvmvg") and \ - device.name == name.replace("--","-"): - found = device - break - - log_method_return(self, str(found)) - return found - - def getDeviceByPath(self, path, preferLeaves=True): - log_method_call(self, path=path) - if not path: - log_method_return(self, None) - return None - - found = None - leaf = None - other = None - for device in self._devices: - if not getattr(device, "complete", True): - continue - - if (device.path == path or - ((device.type == "lvmlv" or device.type == "lvmvg") and - device.path == path.replace("--","-"))): - if device.isleaf and not leaf: - leaf = device - elif not other: - other = device - - if preferLeaves: - all_devs = [leaf, other] - else: - all_devs = [other, leaf] - all_devs = [d for d in all_devs if d] - if all_devs: - found = all_devs[0] - - log_method_return(self, str(found)) - return found - - def getDevicesByType(self, device_type): - # TODO: expand this to catch device format types - return [d for d in self._devices if d.type == device_type] - - def getDevicesByInstance(self, device_class): - return [d for d in self._devices if isinstance(d, device_class)] - - def getDeviceByID(self, id_num): - for device in self._devices: - if device.id == id_num: - return device - - @property - def devices(self): - """ List of device instances """ - devices = [] - for device in self._devices: - if not getattr(device, "complete", True): - continue - - if device.uuid and device.uuid in [d.uuid for d in devices] and \ - not isinstance(device, NoDevice): - raise DeviceTreeError("duplicate uuids in device tree") - - devices.append(device) - - return devices - - @property - def filesystems(self): - """ List of filesystems. """ - #""" Dict with mountpoint keys and filesystem values. """ - filesystems = [] - for dev in self.leaves: - if dev.format and getattr(dev.format, 'mountpoint', None): - filesystems.append(dev.format) - - return filesystems - - @property - def uuids(self): - """ Dict with uuid keys and Device values. """ - uuids = {} - for dev in self._devices: - try: - uuid = dev.uuid - except AttributeError: - uuid = None - - if uuid: - uuids[uuid] = dev - - try: - uuid = dev.format.uuid - except AttributeError: - uuid = None - - if uuid: - uuids[uuid] = dev - - return uuids - - @property - def labels(self): - """ Dict with label keys and Device values. - - FIXME: duplicate labels are a possibility - """ - labels = {} - for dev in self._devices: - # don't include btrfs member devices - if getattr(dev.format, "label", None) and \ - (dev.format.type != "btrfs" or isinstance(dev, BTRFSDevice)): - labels[dev.format.label] = dev - - return labels - - @property - def leaves(self): - """ List of all devices upon which no other devices exist. """ - leaves = [d for d in self._devices if d.isleaf] - return leaves - - def getChildren(self, device): - """ Return a list of a device's children. """ - return [c for c in self._devices if device in c.parents] - - def resolveDevice(self, devspec, blkidTab=None, cryptTab=None, options=None): - # find device in the tree - device = None - if devspec.startswith("UUID="): - # device-by-uuid - uuid = devspec.partition("=")[2] - if ((uuid.startswith('"') and uuid.endswith('"')) or - (uuid.startswith("'") and uuid.endswith("'"))): - uuid = uuid[1:-1] - device = self.uuids.get(uuid) - elif devspec.startswith("LABEL="): - # device-by-label - label = devspec.partition("=")[2] - if ((label.startswith('"') and label.endswith('"')) or - (label.startswith("'") and label.endswith("'"))): - label = label[1:-1] - device = self.labels.get(label) - elif re.match(r'(0x)?[A-Za-z0-9]{2}(p\d+)?$', devspec): - # BIOS drive number - spec = int(devspec, 16) - for (edd_name, edd_number) in devicelibs.edd.edd_dict.items(): - if edd_number == spec: - device = self.getDeviceByName(edd_name) - break - else: - if not devspec.startswith("/dev/"): - device = self.getDeviceByName(devspec) - if not device: - devspec = "/dev/" + devspec - - if not device: - if devspec.startswith("/dev/disk/"): - devspec = os.path.realpath(devspec) - - if devspec.startswith("/dev/dm-"): - dm_name = devicelibs.dm.name_from_dm_node(devspec[5:]) - if dm_name: - devspec = "/dev/mapper/" + dm_name - - # device path - device = self.getDeviceByPath(devspec) - - if device is None: - if blkidTab: - # try to use the blkid.tab to correlate the device - # path with a UUID - blkidTabEnt = blkidTab.get(devspec) - if blkidTabEnt: - log.debug("found blkid.tab entry for '%s'" % devspec) - uuid = blkidTabEnt.get("UUID") - if uuid: - device = self.getDeviceByUuid(uuid) - if device: - devstr = device.name - else: - devstr = "None" - log.debug("found device '%s' in tree" % devstr) - if device and device.format and \ - device.format.type == "luks": - map_name = device.format.mapName - log.debug("luks device; map name is '%s'" % map_name) - mapped_dev = self.getDeviceByName(map_name) - if mapped_dev: - device = mapped_dev - - if device is None and cryptTab and \ - devspec.startswith("/dev/mapper/"): - # try to use a dm-crypt mapping name to - # obtain the underlying device, possibly - # using blkid.tab - cryptTabEnt = cryptTab.get(devspec.split("/")[-1]) - if cryptTabEnt: - luks_dev = cryptTabEnt['device'] - try: - device = self.getChildren(luks_dev)[0] - except IndexError as e: - pass - elif device is None: - # dear lvm: can we please have a few more device nodes - # for each logical volume? - # three just doesn't seem like enough. - name = devspec[5:] # strip off leading "/dev/" - (vg_name, slash, lv_name) = name.partition("/") - if lv_name and not "/" in lv_name: - # looks like we may have one - lv = "%s-%s" % (vg_name, lv_name) - device = self.getDeviceByName(lv) - - # check mount options for btrfs volumes in case it's a subvol - if device and device.type == "btrfs volume" and options: - attr = None - if "subvol=" in options: - attr = "name" - val = util.get_option_value("subvol", options) - elif "subvolid=" in options: - attr = "vol_id" - val = util.get_option_value("subvolid", options) - - if attr and val: - for subvol in device.subvolumes: - if getattr(subvol, attr, None) == val: - device = subvol - break - - if device: - log.debug("resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type)) - else: - log.debug("failed to resolve '%s'" % devspec) - return device diff --git a/pyanaconda/storage/errors.py b/pyanaconda/storage/errors.py deleted file mode 100644 index aad1d00db..000000000 --- a/pyanaconda/storage/errors.py +++ /dev/null @@ -1,193 +0,0 @@ -# errors.py -# Exception classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 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 StorageError(Exception): - def __init__(self, *args, **kwargs): - self.hardware_fault = kwargs.pop("hardware_fault", False) - super(StorageError, self).__init__(*args, **kwargs) - -class NoDisksError(StorageError): - pass - -class ErrorRecoveryFailure(StorageError): - pass - -# Device -class DeviceError(StorageError): - pass - -class DeviceCreateError(DeviceError): - pass - -class DeviceDestroyError(DeviceError): - pass - -class DeviceResizeError(DeviceError): - pass - -class DeviceSetupError(DeviceError): - pass - -class DeviceTeardownError(DeviceError): - pass - -class DeviceUserDeniedFormatError(DeviceError): - pass - -# DeviceFormat -class DeviceFormatError(StorageError): - pass - -class FormatCreateError(DeviceFormatError): - pass - -class FormatDestroyError(DeviceFormatError): - pass - -class FormatSetupError(DeviceFormatError): - pass - -class FormatTeardownError(DeviceFormatError): - pass - -class DMRaidMemberError(DeviceFormatError): - pass - -class MultipathMemberError(DeviceFormatError): - pass - -class FSError(DeviceFormatError): - pass - -class FSResizeError(FSError): - pass - -class DirtyFSError(FSError): - pass - -class LUKSError(DeviceFormatError): - pass - -class MDMemberError(DeviceFormatError): - pass - -class PhysicalVolumeError(DeviceFormatError): - pass - -class SinglePhysicalVolumeError(DeviceFormatError): - pass - -class SwapSpaceError(DeviceFormatError): - pass - -class DiskLabelError(DeviceFormatError): - pass - -class InvalidDiskLabelError(DiskLabelError): - pass - -class DiskLabelCommitError(DiskLabelError): - pass - -# devicelibs -class SwapError(StorageError): - pass - -class SuspendError(SwapError): - pass - -class OldSwapError(SwapError): - pass - -class UnknownSwapError(SwapError): - pass - -class MDRaidError(StorageError): - pass - -class DMError(StorageError): - pass - -class LVMError(StorageError): - pass - -class CryptoError(StorageError): - pass - -class MPathError(StorageError): - pass - -class LoopError(StorageError): - pass - -class BTRFSError(StorageError): - pass - -# DeviceTree -class DeviceTreeError(StorageError): - pass - -class DeviceNotFoundError(StorageError): - pass - -# DeviceAction -class DeviceActionError(StorageError): - pass - -# partitioning -class PartitioningError(StorageError): - pass - -class NotEnoughFreeSpaceError(StorageError): - pass - -# udev -class UdevError(StorageError): - pass - -# fstab -class UnrecognizedFSTabEntryError(StorageError): - pass - -class FSTabTypeMismatchError(StorageError): - pass - -# dasd -class DasdFormatError(StorageError): - pass - -# size -class SizeParamsError(StorageError): - pass - -class SizeNotPositiveError(StorageError): - pass - -class SizePlacesError(StorageError): - pass - -# probing -class UnknownSourceDeviceError(StorageError): - pass - -class NoDisksError(StorageError): - pass diff --git a/pyanaconda/storage/fcoe.py b/pyanaconda/storage/fcoe.py deleted file mode 100644 index 8e8e301e6..000000000 --- a/pyanaconda/storage/fcoe.py +++ /dev/null @@ -1,163 +0,0 @@ -# -# fcoe.py - fcoe class -# -# Copyright (C) 2009 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/>. -# - -import os -from . import util -from .udev import udev_settle -#from pyanaconda import isys -from . import ROOT_PATH -import logging -import time -log = logging.getLogger("storage") - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -_fcoe_module_loaded = False - -def has_fcoe(): - global _fcoe_module_loaded - if not _fcoe_module_loaded: - util.run_program(["modprobe", "fcoe"]) - _fcoe_module_loaded = True - if "bnx2x" in util.lsmod(): - log.info("fcoe: loading bnx2fc") - util.run_program(["modprobe", "bnx2fc"]) - - return os.access("/sys/module/fcoe", os.X_OK) - -class fcoe(object): - """ FCoE utility class. - - This class will automatically discover and connect to EDD configured - FCoE SAN's when the startup() method gets called. It can also be - used to manually configure FCoE SAN's through the addSan() method. - - As this class needs to make sure certain things like starting fcoe - daemons and connecting to firmware discovered SAN's only happens once - and as it keeps a global list of all FCoE devices it is - implemented as a Singleton. - """ - - def __init__(self): - self.started = False - self.lldpadStarted = False - self.nics = [] - - # So that users can write fcoe() to get the singleton instance - def __call__(self): - return self - - def _stabilize(self): - # I have no clue how long we need to wait, this ought to do the trick - time.sleep(10) - udev_settle() - - def _startEDD(self): - rc = util.capture_output(["/usr/libexec/fcoe/fcoe_edd.sh", "-i"]) - if not rc.startswith("NIC="): - log.info("No FCoE EDD info found: %s" % rc.rstrip()) - return - - (key, val) = rc.strip().split("=", 1) - #if val not in isys.getDeviceProperties(): - # log.error("Unknown FCoE NIC found in EDD: %s, ignoring" % val) - # return - - log.info("FCoE NIC found in EDD: %s" % val) - self.addSan(val, dcb=True, auto_vlan=True) - - def startup(self): - if self.started: - return - - if not has_fcoe(): - return - - self._startEDD() - self.started = True - - def _startLldpad(self): - if self.lldpadStarted: - return - - util.run_program(["lldpad", "-d"]) - self.lldpadStarted = True - - def addSan(self, nic, dcb=False, auto_vlan=True): - if not has_fcoe(): - raise IOError, _("FCoE not available") - - log.info("Activating FCoE SAN attached to %s, dcb: %s autovlan: %s" % - (nic, dcb, auto_vlan)) - - util.run_program(["ip", "link", "set", nic, "up"]) - - if dcb: - self._startLldpad() - util.run_program(["dcbtool", "sc", nic, "dcb", "on"]) - util.run_program(["dcbtool", "sc", nic, "app:fcoe", - "e:1", "a:1", "w:1"]) - util.run_program(["fipvlan", "-c", "-s", "-f", - "'-fcoe'", nic]) - else: - if auto_vlan: - # certain network configrations require the VLAN layer module: - util.run_program(["modprobe", "8021q"]) - util.run_program(["fipvlan", '-c', '-s', '-f', - "'-fcoe'", nic]) - else: - f = open("/sys/module/libfcoe/parameters/create", "w") - f.write(nic) - f.close() - - self._stabilize() - self.nics.append((nic, dcb, auto_vlan)) - - def write(self): - if not self.nics: - return - - if not os.path.isdir(ROOT_PATH + "/etc/fcoe"): - os.makedirs(ROOT_PATH + "/etc/fcoe", 0755) - - for nic, dcb, auto_vlan in self.nics: - fd = os.open(ROOT_PATH + "/etc/fcoe/cfg-" + nic, - os.O_RDWR | os.O_CREAT) - os.write(fd, '# Created by anaconda\n') - os.write(fd, '# Enable/Disable FCoE service at the Ethernet port\n') - os.write(fd, 'FCOE_ENABLE="yes"\n') - os.write(fd, '# Indicate if DCB service is required at the Ethernet port\n') - if dcb: - os.write(fd, 'DCB_REQUIRED="yes"\n') - else: - os.write(fd, 'DCB_REQUIRED="no"\n') - os.write(fd, '# Indicate if VLAN discovery should be handled by fcoemon\n') - if auto_vlan: - os.write(fd, 'AUTO_VLAN="yes"\n') - else: - os.write(fd, 'AUTO_VLAN="no"\n') - os.close(fd) - - return - -# Create FCoE singleton -fcoe = fcoe() - -# vim:tw=78:ts=4:et:sw=4 diff --git a/pyanaconda/storage/flags.py b/pyanaconda/storage/flags.py deleted file mode 100644 index ed0e5dcd1..000000000 --- a/pyanaconda/storage/flags.py +++ /dev/null @@ -1,98 +0,0 @@ -# flags.py -# -# Copyright (C) 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 -# 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): David Lehman <dlehman@redhat.com> -# - -import shlex -import selinux - -class Flags(object): - def __init__(self): - # - # mode of operation - # - self.testing = False - self.installer_mode = False - - # - # minor modes (installer-specific) - # - self.automated_install = False - self.live_install = False - self.image_install = False - - # - # enable/disable functionality - # - self.selinux = selinux.is_selinux_enabled() - self.multipath = True - self.dmraid = True - self.ibft = False - self.noiswmd = False - - self.gfs2 = True - self.jfs = True - self.reiserfs = True - - self.arm_platform = None - - self.gpt = False - - self.boot_cmdline = {} - - self.update_from_boot_cmdline() - - def get_boot_cmdline(self): - buf = open("/proc/cmdline").read().strip() - args = shlex.split(buf) - for arg in args: - (opt, equals, val) = arg.partition("=") - self.boot_cmdline[opt] = val - - def update_from_boot_cmdline(self): - self.get_boot_cmdline() - if "nompath" in self.boot_cmdline: - self.multipath = False - - if "nodmraid" in self.boot_cmdline: - self.dmraid = False - - if "ibft" in self.boot_cmdline: - self.ibft = True - - if "noiswmd" in self.boot_cmdline: - self.noiswmd = True - - def update_from_anaconda_flags(self, anaconda_flags): - self.installer_mode = True - self.testing = anaconda_flags.testing - self.automated_install = anaconda_flags.automatedInstall - self.live_install = anaconda_flags.livecdInstall - - self.selinux = anaconda_flags.selinux - - self.gfs2 = "gfs2" in self.boot_cmdline - self.jfs = "jfs" in self.boot_cmdline - self.reiserfs = "reiserfs" in self.boot_cmdline - - self.arm_platform = anaconda_flags.armPlatform - self.gpt = anaconda_flags.gpt - - -flags = Flags() diff --git a/pyanaconda/storage/formats/Makefile.am b/pyanaconda/storage/formats/Makefile.am deleted file mode 100644 index 7ecaf075d..000000000 --- a/pyanaconda/storage/formats/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -# storage/formats/Makefile.am for anaconda -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# Author: David Cantrell <dcantrell@redhat.com> - -pkgpyexecdir = $(pyexecdir)/py$(PACKAGE_NAME) -storageformatsdir = $(pkgpyexecdir)/storage/formats -storageformats_PYTHON = *.py - -MAINTAINERCLEANFILES = Makefile.in diff --git a/pyanaconda/storage/formats/__init__.py b/pyanaconda/storage/formats/__init__.py deleted file mode 100644 index 62bfb31c4..000000000 --- a/pyanaconda/storage/formats/__init__.py +++ /dev/null @@ -1,438 +0,0 @@ -# __init__.py -# Entry point for anaconda storage formats subpackage. -# -# Copyright (C) 2009 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> -# - -import os - -from pyanaconda.product import productName -from pyanaconda.baseudev import udev_get_device -from ..util import notify_kernel -from ..util import get_sysfs_path_by_name -from ..util import run_program -from ..storage_log import log_method_call -from ..errors import * -from ..devicelibs.dm import dm_node_from_name -from ..devicelibs.mdraid import md_node_from_name -from ..udev import udev_device_get_major, udev_device_get_minor - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - - -device_formats = {} -def register_device_format(fmt_class): - if not issubclass(fmt_class, DeviceFormat): - raise ValueError("arg1 must be a subclass of DeviceFormat") - - device_formats[fmt_class._type] = fmt_class - log.debug("registered device format class %s as %s" % (fmt_class.__name__, - fmt_class._type)) - -default_fstypes = ["ext4", "ext3", "ext2"] -if productName.startswith("Red Hat Enterprise Linux"): - default_fstypes.insert(0, "xfs") - -def get_default_filesystem_type(): - for fstype in default_fstypes: - try: - supported = get_device_format_class(fstype).supported - except AttributeError: - supported = None - - if supported: - return fstype - - raise DeviceFormatError("None of %s is supported by your kernel" % ",".join(default_fstypes)) - -def getFormat(fmt_type, *args, **kwargs): - """ Return a DeviceFormat instance based on fmt_type and args. - - Given a device format type and a set of constructor arguments, - return a DeviceFormat instance. - - Return None if no suitable format class is found. - - Arguments: - - fmt_type -- the name of the format type (eg: 'ext3', 'swap') - - Keyword Arguments: - - The keyword arguments may vary according to the format type, - but here is the common set: - - device -- path to the device on which the format resides - uuid -- the UUID of the (preexisting) formatted device - exists -- whether or not the format exists on the device - - """ - fmt_class = get_device_format_class(fmt_type) - fmt = None - if fmt_class: - fmt = fmt_class(*args, **kwargs) - try: - className = fmt.__class__.__name__ - except AttributeError: - className = None - log.debug("getFormat('%s') returning %s instance" % (fmt_type, className)) - return fmt - -def collect_device_format_classes(): - """ Pick up all device format classes from this directory. - - Note: Modules must call register_device_format(FormatClass) in - order for the format class to be picked up. - """ - dir = os.path.dirname(__file__) - for module_file in os.listdir(dir): - # make sure we're not importing this module - if module_file.endswith(".py") and module_file != __file__: - mod_name = module_file[:-3] - # imputil is deprecated in python 2.6 - try: - globals()[mod_name] = __import__(mod_name, globals(), locals(), [], -1) - except ImportError: - log.error("import of device format module '%s' failed" % mod_name) - from traceback import format_exc - log.debug(format_exc()) - -def get_device_format_class(fmt_type): - """ Return an appropriate format class based on fmt_type. """ - if not device_formats: - collect_device_format_classes() - - fmt = device_formats.get(fmt_type) - if not fmt: - for fmt_class in device_formats.values(): - if fmt_type and fmt_type == fmt_class._name: - fmt = fmt_class - break - elif fmt_type in fmt_class._udevTypes: - fmt = fmt_class - break - - # default to no formatting, AKA "Unknown" - if not fmt: - fmt = DeviceFormat - - return fmt - -class DeviceFormat(object): - """ Generic device format. """ - _type = None - _name = "Unknown" - _udevTypes = [] - partedFlag = None - partedSystem = None - _formattable = False # can be formatted - _supported = False # is supported - _linuxNative = False # for clearpart - _packages = [] # required packages - _services = [] # required services - _resizable = False # can be resized - _maxSize = 0 # maximum size in MB - _minSize = 0 # minimum size in MB - _dump = False - _check = False - _hidden = False # hide devices with this formatting? - - def __init__(self, *args, **kwargs): - """ Create a DeviceFormat instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this format's UUID - exists -- indicates whether this is an existing format - - """ - self.device = kwargs.get("device") - self.uuid = kwargs.get("uuid") - self.exists = kwargs.get("exists") - self.options = kwargs.get("options") - self._majorminor = None - - # don't worry about existence if this is a DeviceFormat instance - #if self.__class__ is DeviceFormat: - # self.exists = True - - def __repr__(self): - s = ("%(classname)s instance (%(id)s) --\n" - " type = %(type)s name = %(name)s status = %(status)s\n" - " device = %(device)s uuid = %(uuid)s exists = %(exists)s\n" - " options = %(options)s supported = %(supported)s" - " formattable = %(format)s resizable = %(resize)s\n" % - {"classname": self.__class__.__name__, "id": "%#x" % id(self), - "type": self.type, "name": self.name, "status": self.status, - "device": self.device, "uuid": self.uuid, "exists": self.exists, - "options": self.options, "supported": self.supported, - "format": self.formattable, "resize": self.resizable}) - return s - - @property - def _existence_str(self): - exist = "existing" - if not self.exists: - exist = "non-existent" - return exist - - @property - def desc(self): - return str(self.type) - - def __str__(self): - return "%s %s" % (self._existence_str, self.desc) - - @property - def dict(self): - d = {"type": self.type, "name": self.name, "device": self.device, - "uuid": self.uuid, "exists": self.exists, - "options": self.options, "supported": self.supported, - "resizable": self.resizable} - return d - - def _setOptions(self, options): - self._options = options - - def _getOptions(self): - return self._options - - options = property(_getOptions, _setOptions) - - def _setDevice(self, devspec): - if devspec and not devspec.startswith("/"): - raise ValueError("device must be a fully qualified path") - self._device = devspec - - def _getDevice(self): - return self._device - - device = property(lambda f: f._getDevice(), - lambda f,d: f._setDevice(d), - doc="Full path the device this format occupies") - - @property - def name(self): - if self._name: - name = self._name - else: - name = self.type - return name - - @property - def type(self): - return self._type - - def probe(self): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - - def notifyKernel(self): - log_method_call(self, device=self.device, - type=self.type) - if not self.device: - return - - if self.device.startswith("/dev/mapper/"): - try: - name = dm_node_from_name(os.path.basename(self.device)) - except DMError: - log.warning("failed to get dm node for %s" % self.device) - return - elif self.device.startswith("/dev/md/"): - try: - name = md_node_from_name(os.path.basename(self.device)) - except MDRaidError: - log.warning("failed to get md node for %s" % self.device) - return - else: - name = self.device - - path = get_sysfs_path_by_name(name) - try: - notify_kernel(path, action="change") - except (ValueError, IOError) as e: - log.warning("failed to notify kernel of change: %s" % e) - - def cacheMajorminor(self): - """ Cache the value of self.majorminor. - - Once a device node of this format's device disappears (for instance - after a teardown), it is no longer possible to figure out the value - of self.majorminor pseudo-unique string. Call this method before - that happens for caching this. - """ - self._majorminor = None - try: - self.majorminor # this does the caching - except StorageError: - # entirely possible there's no majorminor, for instance an - # LVMVolumeGroup has got no device node and no sysfs path. In this - # case obviously, calling majorminor of this object later raises an - # exception. - pass - return self._majorminor - - def create(self, *args, **kwargs): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - # allow late specification of device path - device = kwargs.get("device") - if device: - self.device = device - - if not os.path.exists(self.device): - raise FormatCreateError("invalid device specification", self.device) - - def destroy(self, *args, **kwargs): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - try: - rc = run_program(["wipefs", "-a", self.device]) - except OSError as e: - err = str(e) - else: - err = "" - if rc: - err = str(rc) - - if err: - msg = "error wiping old signatures from %s: %s" % (self.device, err) - raise FormatDestroyError(msg) - - self.exists = False - - def setup(self, *args, **kwargs): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - - if not self.exists: - raise FormatSetupError("format has not been created") - - if self.status: - return - - # allow late specification of device path - device = kwargs.get("device") - if device: - self.device = device - - if not self.device or not os.path.exists(self.device): - raise FormatSetupError("invalid device specification") - - def teardown(self, *args, **kwargs): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - - @property - def status(self): - return (self.exists and - self.__class__ is not DeviceFormat and - isinstance(self.device, str) and - self.device and - os.path.exists(self.device)) - - @property - def formattable(self): - """ Can we create formats of this type? """ - return self._formattable - - @property - def supported(self): - """ Is this format a supported type? """ - return self._supported - - @property - def packages(self): - """ Packages required to manage formats of this type. """ - return self._packages - - @property - def services(self): - """ Services required to manage formats of this type. """ - return self._services - - @property - def resizable(self): - """ Can formats of this type be resized? """ - return self._resizable and self.exists - - @property - def linuxNative(self): - """ Is this format type native to linux? """ - return self._linuxNative - - @property - def mountable(self): - """ Is this something we can mount? """ - return False - - @property - def dump(self): - """ Whether or not this format will be dumped by dump(8). """ - return self._dump - - @property - def check(self): - """ Whether or not this format is checked on boot. """ - return self._check - - @property - def maxSize(self): - """ Maximum size (in MB) for this format type. """ - return self._maxSize - - @property - def minSize(self): - """ Minimum size (in MB) for this format type. """ - return self._minSize - - @property - def hidden(self): - """ Whether devices with this formatting should be hidden in UIs. """ - return self._hidden - - @property - def majorminor(self): - """A string suitable for using as a pseudo-unique ID in kickstart.""" - if not self._majorminor: - # If this is a device-mapper device, we have to get the DM node and - # build the sysfs path from that. - try: - device = dm_node_from_name(os.path.basename(self.device)) - except DMError: - device = self.device - - try: - sysfs_path = get_sysfs_path_by_name(device) - except RuntimeError: - raise StorageError("DeviceFormat.majorminor: " - "can not get majorminor for '%s'" % device) - dev = udev_get_device(sysfs_path[4:]) - - self._majorminor = "%03d%03d" %\ - (udev_device_get_major(dev), udev_device_get_minor(dev)) - return self._majorminor - -collect_device_format_classes() diff --git a/pyanaconda/storage/formats/biosboot.py b/pyanaconda/storage/formats/biosboot.py deleted file mode 100644 index c3aef8d2b..000000000 --- a/pyanaconda/storage/formats/biosboot.py +++ /dev/null @@ -1,60 +0,0 @@ -# biosboot.py -# Device format classes for anaconda's storage configuration module. -# -# Copyright (C) 2011 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> -# - -from parted import PARTITION_BIOS_GRUB - -from ..errors import * -from .. import platform -from . import DeviceFormat, register_device_format - -class BIOSBoot(DeviceFormat): - """ BIOS boot partition for GPT disklabels. """ - _type = "biosboot" - _name = "BIOS Boot" - _udevTypes = [] - partedFlag = PARTITION_BIOS_GRUB - _formattable = True # can be formatted - _linuxNative = True # for clearpart - _maxSize = 2 # maximum size in MB - _minSize = 0.5 # minimum size in MB - - def __init__(self, *args, **kwargs): - """ Create a BIOSBoot instance. - - Keyword Arguments: - - device -- path to the underlying device - exists -- indicates whether this is an existing format - - """ - DeviceFormat.__init__(self, *args, **kwargs) - - @property - def status(self): - return False - - @property - def supported(self): - return isinstance(platform.platform, platform.X86) - -register_device_format(BIOSBoot) - diff --git a/pyanaconda/storage/formats/disklabel.py b/pyanaconda/storage/formats/disklabel.py deleted file mode 100644 index 018adf426..000000000 --- a/pyanaconda/storage/formats/disklabel.py +++ /dev/null @@ -1,443 +0,0 @@ -# disklabel.py -# Device format classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 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> -# - -import os -import copy - -from ..storage_log import log_method_call -import parted -import _ped -from ..errors import * -from .. import arch -from ..flags import flags -from ..udev import udev_settle -from . import DeviceFormat, register_device_format - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - - -class DiskLabel(DeviceFormat): - """ Disklabel """ - _type = "disklabel" - _name = "partition table" - _formattable = True # can be formatted - _supported = False # is supported - - def __init__(self, *args, **kwargs): - """ Create a DiskLabel instance. - - Keyword Arguments: - - labelType -- type of disklabel to create - device -- path to the underlying device - exists -- indicates whether this is an existing format - - """ - log_method_call(self, *args, **kwargs) - DeviceFormat.__init__(self, *args, **kwargs) - - if not self.exists: - self._labelType = kwargs.get("labelType", "msdos") - else: - self._labelType = "" - - self._size = None - - self._partedDevice = None - self._partedDisk = None - self._origPartedDisk = None - self._alignment = None - self._endAlignment = None - - if self.partedDevice: - # set up the parted objects and raise exception on failure - self._origPartedDisk = self.partedDisk.duplicate() - - def __deepcopy__(self, memo): - """ Create a deep copy of a Disklabel instance. - - We can't do copy.deepcopy on parted objects, which is okay. - For these parted objects, we just do a shallow copy. - """ - new = self.__class__.__new__(self.__class__) - memo[id(self)] = new - shallow_copy_attrs = ('_partedDevice', '_alignment', '_endAlignment') - duplicate_attrs = ('_partedDisk', '_origPartedDisk') - for (attr, value) in self.__dict__.items(): - if attr in shallow_copy_attrs: - setattr(new, attr, copy.copy(value)) - elif attr in duplicate_attrs: - setattr(new, attr, value.duplicate()) - else: - setattr(new, attr, copy.deepcopy(value, memo)) - - return new - - def __repr__(self): - s = DeviceFormat.__repr__(self) - if flags.testing: - return s - s += (" type = %(type)s partition count = %(count)s" - " sectorSize = %(sectorSize)s\n" - " align_offset = %(offset)s align_grain = %(grain)s\n" - " partedDisk = %(disk)s\n" - " origPartedDisk = %(orig_disk)r\n" - " partedDevice = %(dev)s\n" % - {"type": self.labelType, "count": len(self.partitions), - "sectorSize": self.partedDevice.sectorSize, - "offset": self.alignment.offset, - "grain": self.alignment.grainSize, - "disk": self.partedDisk, "orig_disk": self._origPartedDisk, - "dev": self.partedDevice}) - return s - - @property - def desc(self): - return "%s %s" % (self.labelType, self.type) - - @property - def dict(self): - d = super(DiskLabel, self).dict - if flags.testing: - return d - - d.update({"labelType": self.labelType, - "partitionCount": len(self.partitions), - "sectorSize": self.partedDevice.sectorSize, - "offset": self.alignment.offset, - "grainSize": self.alignment.grainSize}) - return d - - def resetPartedDisk(self): - """ Set this instance's partedDisk to reflect the disk's contents. """ - log_method_call(self, device=self.device) - self._partedDisk = self._origPartedDisk - - def freshPartedDisk(self): - """ Return a new, empty parted.Disk instance for this device. """ - log_method_call(self, device=self.device, labelType=self._labelType) - return parted.freshDisk(device=self.partedDevice, ty=self._labelType) - - @property - def partedDisk(self): - if not self._partedDisk: - if self.exists: - try: - self._partedDisk = parted.Disk(device=self.partedDevice) - except (_ped.DiskLabelException, _ped.IOException, - NotImplementedError) as e: - raise InvalidDiskLabelError() - - if self._partedDisk.type == "loop": - # When the device has no partition table but it has a FS, - # it will be created with label type loop. Treat the - # same as if the device had no label (cause it really - # doesn't). - raise InvalidDiskLabelError() - - # here's where we correct the ctor-supplied disklabel type for - # preexisting disklabels if the passed type was wrong - self._labelType = self._partedDisk.type - else: - self._partedDisk = self.freshPartedDisk() - - # turn off cylinder alignment - if self._partedDisk.isFlagAvailable(parted.DISK_CYLINDER_ALIGNMENT): - self._partedDisk.unsetFlag(parted.DISK_CYLINDER_ALIGNMENT) - - # Set the boot flag on the GPT PMBR, this helps some BIOS systems boot - if self._partedDisk.isFlagAvailable(parted.DISK_GPT_PMBR_BOOT): - # MAC can boot as EFI or as BIOS, neither should have PMBR boot set - if arch.isEfi() or arch.isMactel(): - self._partedDisk.unsetFlag(parted.DISK_GPT_PMBR_BOOT) - log.debug("Clear pmbr_boot on %s" % (self._partedDisk,)) - else: - self._partedDisk.setFlag(parted.DISK_GPT_PMBR_BOOT) - log.debug("Set pmbr_boot on %s" % (self._partedDisk,)) - else: - log.debug("Did not change pmbr_boot on %s" % (self._partedDisk,)) - - return self._partedDisk - - @property - def partedDevice(self): - if not self._partedDevice and self.device: - if os.path.exists(self.device): - # We aren't guaranteed to be able to get a device. In - # particular, built-in USB flash readers show up as devices but - # do not always have any media present, so parted won't be able - # to find a device. - try: - self._partedDevice = parted.Device(path=self.device) - except (_ped.IOException, _ped.DeviceException) as e: - log.error("DiskLabel.partedDevice: Parted exception: %s" % e) - else: - log.info("DiskLabel.partedDevice: %s does not exist" % self.device) - - if not self._partedDevice: - log.info("DiskLabel.partedDevice returning None") - return self._partedDevice - - @property - def labelType(self): - """ The disklabel type (eg: 'gpt', 'msdos') """ - try: - lt = self.partedDisk.type - except Exception: - lt = self._labelType - return lt - - @property - def name(self): - return "%s (%s)" % (self._name, self.labelType.upper()) - - @property - def size(self): - size = self._size - if not size: - try: - size = self.partedDevice.getSize(unit="MB") - except Exception: - size = 0 - - return size - - @property - def status(self): - """ Device status. """ - return False - - def setup(self, *args, **kwargs): - """ Open, or set up, a device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise DeviceFormatError("format has not been created") - - if self.status: - return - - DeviceFormat.setup(self, *args, **kwargs) - - def teardown(self, *args, **kwargs): - """ Close, or tear down, a device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise DeviceFormatError("format has not been created") - - def create(self, *args, **kwargs): - """ Create the device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if self.exists: - raise DeviceFormatError("format already exists") - - if self.status: - raise DeviceFormatError("device exists and is active") - - DeviceFormat.create(self, *args, **kwargs) - - # We're relying on someone having called resetPartedDisk -- we - # could ensure a fresh disklabel by setting self._partedDisk to - # None right before calling self.commit(), but that might hide - # other problems. - self.commit() - self.exists = True - - def destroy(self, *args, **kwargs): - """ Wipe the disklabel from the device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise DeviceFormatError("format does not exist") - - if not os.access(self.device, os.W_OK): - raise DeviceFormatError("device path does not exist") - - self.partedDevice.clobber() - self.exists = False - - def commit(self): - """ Commit the current partition table to disk and notify the OS. """ - log_method_call(self, device=self.device, - numparts=len(self.partitions)) - try: - self.partedDisk.commit() - except parted.DiskException as msg: - raise DiskLabelCommitError(msg) - else: - udev_settle() - - def commitToDisk(self): - """ Commit the current partition table to disk. """ - log_method_call(self, device=self.device, - numparts=len(self.partitions)) - try: - self.partedDisk.commitToDevice() - except parted.DiskException as msg: - raise DiskLabelCommitError(msg) - - def addPartition(self, *args, **kwargs): - partition = kwargs.get("partition", None) - if not partition: - partition = args[0] - geometry = partition.geometry - constraint = kwargs.get("constraint", None) - if not constraint and len(args) > 1: - constraint = args[1] - elif not constraint: - constraint = parted.Constraint(exactGeom=geometry) - - new_partition = parted.Partition(disk=self.partedDisk, - type=partition.type, - geometry=geometry) - self.partedDisk.addPartition(partition=new_partition, - constraint=constraint) - - def removePartition(self, partition): - self.partedDisk.removePartition(partition) - - @property - def extendedPartition(self): - try: - extended = self.partedDisk.getExtendedPartition() - except Exception: - extended = None - return extended - - @property - def logicalPartitions(self): - try: - logicals = self.partedDisk.getLogicalPartitions() - except Exception: - logicals = [] - return logicals - - @property - def firstPartition(self): - try: - part = self.partedDisk.getFirstPartition() - except Exception: - part = None - return part - - @property - def partitions(self): - try: - parts = self.partedDisk.partitions - except Exception: - parts = [] - if flags.testing: - sys_block_root = "/sys/class/block/" - - # FIXME: /dev/mapper/foo won't work without massaging - disk_name = self.device.split("/")[-1] - - disk_root = sys_block_root + disk_name - parts = [n for n in os.listdir(disk_root) if n.startswith(disk_name)] - return parts - - @property - def alignment(self): - """ Alignment requirements for this device. """ - if not self._alignment: - try: - disklabel_alignment = self.partedDisk.partitionAlignment - except _ped.CreateException: - disklabel_alignment = parted.Alignment(offset=0, grainSize=1) - - try: - optimum_device_alignment = self.partedDevice.optimumAlignment - except _ped.CreateException: - optimum_device_alignment = None - - try: - minimum_device_alignment = self.partedDevice.minimumAlignment - except _ped.CreateException: - minimum_device_alignment = None - - try: - a = optimum_device_alignment.intersect(disklabel_alignment) - except (ArithmeticError, AttributeError): - try: - a = minimum_device_alignment.intersect(disklabel_alignment) - except (ArithmeticError, AttributeError): - a = disklabel_alignment - - self._alignment = a - - return self._alignment - - @property - def endAlignment(self): - if not self._endAlignment: - self._endAlignment = parted.Alignment( - offset = self.alignment.offset - 1, - grainSize = self.alignment.grainSize) - - return self._endAlignment - - @property - def free(self): - def read_int_from_sys(path): - return int(open(path).readline().strip()) - - try: - free = sum([f.getSize() - for f in self.partedDisk.getFreeSpacePartitions()]) - except Exception: - sys_block_root = "/sys/class/block/" - - # FIXME: /dev/mapper/foo won't work without massaging - disk_name = self.device.split("/")[-1] - - disk_root = sys_block_root + disk_name - disk_length = read_int_from_sys("%s/size" % disk_root) - sector_size = read_int_from_sys("%s/queue/logical_block_size" % disk_root) - partition_names = [n for n in os.listdir(disk_root) if n.startswith(disk_name)] - used_sectors = 0 - for partition_name in partition_names: - partition_root = sys_block_root + partition_name - partition_length = read_int_from_sys("%s/size" % partition_root) - used_sectors += partition_length - - free = ((disk_length - used_sectors) * sector_size) / (1024.0 * 1024.0) - - return free - - @property - def magicPartitionNumber(self): - """ Number of disklabel-type-specific special partition. """ - if self.labelType == "mac": - return 1 - elif self.labelType == "sun": - return 3 - else: - return 0 - -register_device_format(DiskLabel) - diff --git a/pyanaconda/storage/formats/dmraid.py b/pyanaconda/storage/formats/dmraid.py deleted file mode 100644 index f37b2d33f..000000000 --- a/pyanaconda/storage/formats/dmraid.py +++ /dev/null @@ -1,114 +0,0 @@ -# dmraid.py -# dmraid device formats -# -# Copyright (C) 2009 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> -# - -from ..storage_log import log_method_call -from ..flags import flags -from ..errors import * -from . import DeviceFormat, register_device_format - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - - -class DMRaidMember(DeviceFormat): - """ A dmraid member disk. """ - _type = "dmraidmember" - _name = "dm-raid member device" - # XXX This looks like trouble. - # - # Maybe a better approach is a RaidMember format with subclass - # for MDRaidMember, letting all *_raid_member types fall through - # to the generic RaidMember format, which is basically read-only. - # - # One problem that presents is the possibility of someone passing - # a dmraid member to the MDRaidArrayDevice constructor. - _udevTypes = ["adaptec_raid_member", "ddf_raid_member", - "hpt37x_raid_member", "hpt45x_raid_member", - "isw_raid_member", - "jmicron_raid_member", "lsi_mega_raid_member", - "nvidia_raid_member", "promise_fasttrack_raid_member", - "silicon_medley_raid_member", "via_raid_member"] - _formattable = False # can be formatted - _supported = True # is supported - _linuxNative = False # for clearpart - _packages = ["dmraid"] # required packages - _resizable = False # can be resized - _maxSize = 0 # maximum size in MB - _minSize = 0 # minimum size in MB - _hidden = True # hide devices with this formatting? - - def __init__(self, *args, **kwargs): - """ Create a DeviceFormat instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this format's UUID - exists -- indicates whether this is an existing format - - On initialization this format is like DeviceFormat - - """ - log_method_call(self, *args, **kwargs) - DeviceFormat.__init__(self, *args, **kwargs) - - # Initialize the attribute that will hold the block object. - self._raidmem = None - - def __repr__(self): - s = DeviceFormat.__repr__(self) - s += (" raidmem = %(raidmem)r" % {"raidmem": self.raidmem}) - return s - - def _getRaidmem(self): - return self._raidmem - - def _setRaidmem(self, raidmem): - self._raidmem = raidmem - - raidmem = property(lambda d: d._getRaidmem(), - lambda d,r: d._setRaidmem(r)) - - def create(self, *args, **kwargs): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - raise DMRaidMemberError("creation of dmraid members is non-sense") - - def destroy(self, *args, **kwargs): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - raise DMRaidMemberError("destruction of dmraid members is non-sense") - - -if not flags.noiswmd: - DMRaidMember._udevTypes.remove("isw_raid_member") - -# The anaconda cmdline has not been parsed yet when we're first imported, -# so we can not use flags.dmraid here -if not flags.dmraid: - DMRaidMember._udevTypes = [] - -register_device_format(DMRaidMember) - diff --git a/pyanaconda/storage/formats/fs.py b/pyanaconda/storage/formats/fs.py deleted file mode 100644 index b472e8727..000000000 --- a/pyanaconda/storage/formats/fs.py +++ /dev/null @@ -1,1433 +0,0 @@ -# filesystems.py -# Filesystem classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 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> -# David Cantrell <dcantrell@redhat.com> -# - -""" Filesystem classes for use by anaconda. - - TODO: - - bug 472127: allow creation of tmpfs filesystems (/tmp, /var/tmp, &c) -""" -import math -import os -import sys -import tempfile -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 - -import logging -log = logging.getLogger("storage") - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -try: - lost_and_found_context = selinux.matchpathcon("/lost+found", 0)[1] -except OSError: - lost_and_found_context = None - -# these are for converting to/from SI for ntfsresize -mb = 1000 * 1000.0 -mib = 1024 * 1024.0 - -fs_configs = {} - -def get_kernel_filesystems(): - fs_list = [] - for line in open("/proc/filesystems").readlines(): - fs_list.append(line.split()[-1]) - return fs_list - -global kernel_filesystems -kernel_filesystems = get_kernel_filesystems() - -def fsConfigFromFile(config_file): - """ Generate a set of attribute name/value pairs with which a - filesystem type can be defined. - - The following config file would define a filesystem identical to - the static Ext3FS class definition: - - type = ext3 - mkfs = "mke2fs" - resizefs = "resize2fs" - labelfs = "e2label" - fsck = "e2fsck" - packages = ["e2fsprogs"] - formattable = True - supported = True - resizable = True - linuxNative = True - maxSize = 8 * 1024 * 1024 - minSize = 0 - defaultFormatOptions = "-t ext3" - defaultMountOptions = "defaults" - - """ - # XXX NOTUSED - lines = open(config_file).readlines() - fs_attrs = {} - for line in lines: - (key, value) = [t.strip() for t in line.split("=")] - if not hasattr(FS, "_" + key): - print "invalid key: %s" % key - continue - - fs_attrs[key] = value - - if not fs_attrs.has_key("type"): - raise ValueError, _("filesystem configuration missing a type") - - # XXX what's the policy about multiple configs for a given type? - fs_configs[fs_attrs['type']] = fs_attrs - -class FS(DeviceFormat): - """ Filesystem class. """ - _type = "Abstract Filesystem Class" # fs type name - _mountType = None # like _type but for passing to mount - _name = None - _mkfs = "" # mkfs utility - _modules = [] # kernel modules required for support - _resizefs = "" # resize utility - _labelfs = "" # labeling utility - _fsck = "" # fs check utility - _fsckErrors = {} # fs check command error codes & msgs - _infofs = "" # fs info utility - _defaultFormatOptions = [] # default options passed to mkfs - _defaultMountOptions = ["defaults"] # default options passed to mount - _defaultLabelOptions = [] - _defaultCheckOptions = [] - _defaultInfoOptions = [] - _existingSizeFields = [] - _fsProfileSpecifier = None # mkfs option specifying fsprofile - - def __init__(self, *args, **kwargs): - """ Create a FS instance. - - Keyword Args: - - device -- path to the device containing the filesystem - mountpoint -- the filesystem's mountpoint - label -- the filesystem label - uuid -- the filesystem UUID - mountopts -- mount options for the filesystem - size -- the filesystem's size in MiB - exists -- indicates whether this is an existing filesystem - - """ - if self.__class__ is FS: - raise TypeError("FS is an abstract class.") - - DeviceFormat.__init__(self, *args, **kwargs) - self.mountpoint = kwargs.get("mountpoint") - self.mountopts = kwargs.get("mountopts") - self.label = kwargs.get("label") - self.fsprofile = kwargs.get("fsprofile") - - # filesystem size does not necessarily equal device size - self._size = kwargs.get("size", 0) - self._minInstanceSize = None # min size of this FS instance - self._mountpoint = None # the current mountpoint when mounted - if self.exists: - self._size = self._getExistingSize() - foo = self.minSize # force calculation of minimum size - - self._targetSize = self._size - - if self.supported: - self.loadModule() - - def __repr__(self): - s = DeviceFormat.__repr__(self) - s += (" mountpoint = %(mountpoint)s mountopts = %(mountopts)s\n" - " label = %(label)s size = %(size)s" - " targetSize = %(targetSize)s\n" % - {"mountpoint": self.mountpoint, "mountopts": self.mountopts, - "label": self.label, "size": self._size, - "targetSize": self.targetSize}) - return s - - @property - def desc(self): - s = "%s filesystem" % self.type - if self.mountpoint: - s += " mounted at %s" % self.mountpoint - return s - - @property - def dict(self): - d = super(FS, self).dict - d.update({"mountpoint": self.mountpoint, "size": self._size, - "label": self.label, "targetSize": self.targetSize, - "mountable": self.mountable}) - return d - - def _setTargetSize(self, newsize): - """ Set a target size for this filesystem. """ - if not self.exists: - raise FSError("filesystem has not been created") - - if newsize is None: - # unset any outstanding resize request - self._targetSize = self._size - return - - if not self.minSize <= newsize < self.maxSize: - raise ValueError("invalid target size request") - - self._targetSize = newsize - - def _getTargetSize(self): - """ Get this filesystem's target size. """ - return self._targetSize - - targetSize = property(_getTargetSize, _setTargetSize, - doc="Target size for this filesystem") - - def _getSize(self): - """ Get this filesystem's size. """ - size = self._size - if self.resizable and self.targetSize != size: - size = self.targetSize - return size - - size = property(_getSize, doc="This filesystem's size, accounting " - "for pending changes") - - def _getExistingSize(self): - """ Determine the size of this filesystem. Filesystem must - exist. Each filesystem varies, but the general procedure - is to run the filesystem dump or info utility and read - the block size and number of blocks for the filesystem - and compute megabytes from that. - - The loop that reads the output from the infofsProg is meant - to be simple, but take in to account variations in output. - The general procedure: - 1) Capture output from infofsProg. - 2) Iterate over each line of the output: - a) Trim leading and trailing whitespace. - b) Break line into fields split on ' ' - c) If line begins with any of the strings in - _existingSizeFields, start at the end of - fields and take the first one that converts - to a long. Store this in the values list. - d) Repeat until the values list length equals - the _existingSizeFields length. - 3) If the length of the values list equals the length - of _existingSizeFields, compute the size of this - filesystem by multiplying all of the values together - to get bytes, then convert to megabytes. Return - this value. - 4) If we were unable to capture all fields, return 0. - - The caller should catch exceptions from this method. Any - exception raised indicates a need to change the fields we - are looking for, the command to run and arguments, or - something else. If you catch an exception from this method, - assume the filesystem cannot be resized. - """ - size = self._size - - if self.infofsProg and self.exists and not size and \ - util.find_program_in_path(self.infofsProg): - try: - values = [] - argv = self._defaultInfoOptions + [ self.device ] - - buf = util.capture_output([self.infofsProg] + argv) - - for line in buf.splitlines(): - found = False - - line = line.strip() - tmp = line.split(' ') - tmp.reverse() - - for field in self._existingSizeFields: - if line.startswith(field): - for subfield in tmp: - try: - values.append(long(subfield)) - found = True - break - except ValueError: - continue - - if found: - break - - if len(values) == len(self._existingSizeFields): - break - - if len(values) != len(self._existingSizeFields): - return 0 - - size = 1 - for value in values: - size *= value - - # report current size as megabytes - size = math.floor(size / 1024.0 / 1024.0) - except Exception as e: - log.error("failed to obtain size of filesystem on %s: %s" - % (self.device, e)) - - return size - - @property - def currentSize(self): - """ The filesystem's current actual size. """ - size = 0 - if self.exists: - size = self._size - return float(size) - - @property - def free(self): - free = 0 - if self.exists: - if self.currentSize and self.minSize and \ - self.currentSize != self.minSize: - free = int(max(0, self.currentSize - self.minSize)) # truncate - - return free - - def _getFormatOptions(self, options=None): - argv = [] - if options and isinstance(options, list): - argv.extend(options) - argv.extend(self.defaultFormatOptions) - if self._fsProfileSpecifier and self.fsprofile: - argv.extend([self._fsProfileSpecifier, self.fsprofile]) - argv.append(self.device) - return argv - - def doFormat(self, *args, **kwargs): - """ Create the filesystem. - - Arguments: - - None - - Keyword Arguments: - - options -- list of options to pass to mkfs - - """ - log_method_call(self, type=self.mountType, device=self.device, - mountpoint=self.mountpoint) - - options = kwargs.get("options") - - if self.exists: - raise FormatCreateError("filesystem already exists", self.device) - - if not self.formattable: - return - - if not self.mkfsProg: - return - - if self.exists: - return - - if not os.path.exists(self.device): - raise FormatCreateError("device does not exist", self.device) - - argv = self._getFormatOptions(options=options) - - try: - ret = util.run_program([self.mkfsProg] + argv) - except OSError as e: - raise FormatCreateError(e, self.device) - - if ret: - raise FormatCreateError("format failed: %s" % ret.rc, self.device) - - self.exists = True - self.notifyKernel() - - if self.label: - self.writeLabel(self.label) - - @property - def resizeArgs(self): - argv = [self.device, "%d" % (self.targetSize,)] - return argv - - def doResize(self, *args, **kwargs): - """ Resize this filesystem to new size @newsize. - - Arguments: - - None - """ - - if not self.exists: - raise FSResizeError("filesystem does not exist", self.device) - - if not self.resizable: - raise FSResizeError("filesystem not resizable", self.device) - - if self.targetSize == self.currentSize: - return - - if not self.resizefsProg: - return - - if not os.path.exists(self.device): - raise FSResizeError("device does not exist", self.device) - - self.doCheck() - - # The first minimum size can be incorrect if the fs was not - # properly unmounted. After doCheck the minimum size will be correct - # so run the check one last time and bump up the size if it was too - # small. - self._minInstanceSize = None - if self.targetSize < self.minSize: - self.targetSize = self.minSize - log.info("Minimum size changed, setting targetSize on %s to %s" \ - % (self.device, self.targetSize)) - try: - ret = util.run_program([self.resizefsProg] + self.resizeArgs) - except OSError as e: - raise FSResizeError(e, self.device) - - if ret: - raise FSResizeError("resize failed: %s" % ret, self.device) - - self.doCheck() - - # XXX must be a smarter way to do this - self._size = self.targetSize - self.notifyKernel() - - def _getCheckArgs(self): - argv = [] - argv.extend(self.defaultCheckOptions) - argv.append(self.device) - return argv - - def _fsckFailed(self, rc): - return False - - def _fsckErrorMessage(self, rc): - return _("Unknown return code: %d.") % (rc,) - - def doCheck(self): - if not self.exists: - raise FSError("filesystem has not been created") - - if not self.fsckProg: - return - - if not os.path.exists(self.device): - raise FSError("device does not exist") - - try: - ret = util.run_program([self.fsckProg] + self._getCheckArgs()) - except OSError as e: - raise FSError("filesystem check failed: %s" % e) - - if self._fsckFailed(ret): - hdr = _("%(type)s filesystem check failure on %(device)s: ") % \ - {"type": self.type, "device": self.device} - - msg = self._fsckErrorMessage(ret) - raise FSError(hdr + msg) - - def loadModule(self): - """Load whatever kernel module is required to support this filesystem.""" - global kernel_filesystems - - if not self._modules or self.mountType in kernel_filesystems: - return - - for module in self._modules: - try: - rc = util.run_program(["modprobe", module]) - except OSError as e: - log.error("Could not load kernel module %s: %s" % (module, e)) - self._supported = False - return - - if rc: - log.error("Could not load kernel module %s" % module) - self._supported = False - return - - # If we successfully loaded a kernel module, for this filesystem, we - # also need to update the list of supported filesystems. - kernel_filesystems = get_kernel_filesystems() - - def testMount(self, options=None): - """ Try to mount the fs and return True if successful. """ - ret = False - - if self.status: - raise RuntimeError("filesystem is already mounted") - - # create a temp dir - prefix = "%s.%s" % (os.path.basename(self.device), self.type) - mountpoint = tempfile.mkdtemp(prefix=prefix) - - # try the mount - try: - self.mount(mountpoint=mountpoint) - except Exception as e: - log.info("test mount failed: %s" % e) - else: - self.unmount() - ret = True - finally: - os.rmdir(mountpoint) - - return ret - - def mount(self, *args, **kwargs): - """ Mount this filesystem. - - Arguments: - - None - - Keyword Arguments: - - options -- mount options (overrides all other option strings) - chroot -- prefix to apply to mountpoint - mountpoint -- mountpoint (overrides self.mountpoint) - """ - options = kwargs.get("options", "") - chroot = kwargs.get("chroot", "/") - mountpoint = kwargs.get("mountpoint") - - if not self.exists: - raise FSError("filesystem has not been created") - - if not mountpoint: - mountpoint = self.mountpoint - - if not mountpoint: - raise FSError("no mountpoint given") - - if self.status: - return - - if not isinstance(self, NoDevFS) and not os.path.exists(self.device): - raise FSError("device %s does not exist" % self.device) - - # XXX os.path.join is FUBAR: - # - # os.path.join("/mnt/foo", "/") -> "/" - # - #mountpoint = os.path.join(chroot, mountpoint) - chrootedMountpoint = os.path.normpath("%s/%s" % (chroot, mountpoint)) - util.makedirs(chrootedMountpoint) - if flags.selinux: - ret = util.reset_file_context(mountpoint, chroot) - log.info("set SELinux context for mountpoint %s to %s" \ - % (mountpoint, ret)) - - # passed in options override default options - if not options or not isinstance(options, str): - options = self.options - - if isinstance(self, BindFS): - options = "bind," + options - - try: - rc = util.mount(self.device, chrootedMountpoint, - fstype=self.mountType, - options=options) - except Exception as e: - raise FSError("mount failed: %s" % e) - - if rc: - raise FSError("mount failed: %s" % rc) - - if flags.selinux and "ro" not in options.split(","): - ret = util.reset_file_context(mountpoint, chroot) - log.info("set SELinux context for newly mounted filesystem " - "root at %s to %s" %(mountpoint, ret)) - util.set_file_context("%s/lost+found" % mountpoint, - lost_and_found_context, chroot) - - self._mountpoint = chrootedMountpoint - - def unmount(self): - """ Unmount this filesystem. """ - if not self.exists: - raise FSError("filesystem has not been created") - - if not self._mountpoint: - # not mounted - return - - if not os.path.exists(self._mountpoint): - raise FSError("mountpoint does not exist") - - rc = util.umount(self._mountpoint) - if rc: - raise FSError("umount failed") - - self._mountpoint = None - - def _getLabelArgs(self, label): - argv = [] - argv.extend(self.defaultLabelOptions) - argv.extend([self.device, label]) - return argv - - def writeLabel(self, label): - """ Create a label for this filesystem. """ - if not self.exists: - raise FSError("filesystem has not been created") - - if not self.labelfsProg: - return - - if not os.path.exists(self.device): - raise FSError("device does not exist") - - argv = self._getLabelArgs(label) - rc = util.run_program([self.labelfsProg] + argv) - if rc: - raise FSError("label failed") - - self.label = label - self.notifyKernel() - - def _getRandomUUID(self): - uuid = util.capture_output(["uuidgen"]).strip() - return uuid - - def writeRandomUUID(self): - raise NotImplementedError("FS does not implement writeRandomUUID") - - @property - def needsFSCheck(self): - return False - - @property - def mkfsProg(self): - """ Program used to create filesystems of this type. """ - return self._mkfs - - @property - def fsckProg(self): - """ Program used to check filesystems of this type. """ - return self._fsck - - @property - def resizefsProg(self): - """ Program used to resize filesystems of this type. """ - return self._resizefs - - @property - def labelfsProg(self): - """ Program used to manage labels for this filesystem type. """ - return self._labelfs - - @property - def infofsProg(self): - """ Program used to get information for this filesystem type. """ - return self._infofs - - @property - def utilsAvailable(self): - # we aren't checking for fsck because we shouldn't need it - for prog in [self.mkfsProg, self.resizefsProg, self.labelfsProg, - self.infofsProg]: - if not prog: - continue - - if not util.find_program_in_path(prog): - return False - - return True - - @property - def supported(self): - log_method_call(self, supported=self._supported) - return self._supported and self.utilsAvailable - - @property - def mountable(self): - canmount = (self.mountType in kernel_filesystems) or \ - (os.access("/sbin/mount.%s" % (self.mountType,), os.X_OK)) - modpath = os.path.realpath(os.path.join("/lib/modules", os.uname()[2])) - modname = "%s.ko" % self.mountType - - if not canmount and os.path.isdir(modpath): - for root, dirs, files in os.walk(modpath): - have = filter(lambda x: x.startswith(modname), files) - if len(have) == 1 and have[0].startswith(modname): - return True - - return canmount - - @property - def resizable(self): - """ Can formats of this filesystem type be resized? """ - return super(FS, self).resizable and self.utilsAvailable - - @property - def defaultFormatOptions(self): - """ Default options passed to mkfs for this filesystem type. """ - # return a copy to prevent modification - return self._defaultFormatOptions[:] - - @property - def defaultMountOptions(self): - """ Default options passed to mount for this filesystem type. """ - # return a copy to prevent modification - return self._defaultMountOptions[:] - - @property - def defaultLabelOptions(self): - """ Default options passed to labeler for this filesystem type. """ - # return a copy to prevent modification - return self._defaultLabelOptions[:] - - @property - def defaultCheckOptions(self): - """ Default options passed to checker for this filesystem type. """ - # return a copy to prevent modification - return self._defaultCheckOptions[:] - - def _getOptions(self): - options = ",".join(self.defaultMountOptions) - if self.mountopts: - # XXX should we clobber or append? - options = self.mountopts - return options - - def _setOptions(self, options): - self.mountopts = options - - options = property(_getOptions, _setOptions) - - @property - def mountType(self): - if not self._mountType: - self._mountType = self._type - - return self._mountType - - # These methods just wrap filesystem-specific methods in more - # generically named methods so filesystems and formatted devices - # like swap and LVM physical volumes can have a common API. - def create(self, *args, **kwargs): - if self.exists: - raise FSError("filesystem already exists") - - DeviceFormat.create(self, *args, **kwargs) - - return self.doFormat(*args, **kwargs) - - def setup(self, *args, **kwargs): - """ Mount the filesystem. - - The filesystem will be mounted at the directory indicated by - self.mountpoint. - """ - return self.mount(**kwargs) - - def teardown(self, *args, **kwargs): - return self.unmount(*args, **kwargs) - - @property - def status(self): - # FIXME check /proc/mounts or similar - if not self.exists: - return False - return self._mountpoint is not None - - def sync(self, root="/"): - pass - -class Ext2FS(FS): - """ ext2 filesystem. """ - _type = "ext2" - _mkfs = "mke2fs" - _modules = ["ext2"] - _resizefs = "resize2fs" - _labelfs = "e2label" - _fsck = "e2fsck" - _fsckErrors = {4: _("File system errors left uncorrected."), - 8: _("Operational error."), - 16: _("Usage or syntax error."), - 32: _("e2fsck cancelled by user request."), - 128: _("Shared library error.")} - _packages = ["e2fsprogs"] - _formattable = True - _supported = True - _resizable = True - _linuxNative = True - _maxSize = 8 * 1024 * 1024 - _minSize = 0 - _defaultFormatOptions = [] - _defaultMountOptions = ["defaults"] - _defaultCheckOptions = ["-f", "-p", "-C", "0"] - _dump = True - _check = True - _infofs = "dumpe2fs" - _defaultInfoOptions = ["-h"] - _existingSizeFields = ["Block count:", "Block size:"] - _fsProfileSpecifier = "-T" - partedSystem = fileSystemType["ext2"] - - def __init__(self, *args, **kwargs): - self.dirty = False - self.errors = False - super(Ext2FS, self).__init__(*args, **kwargs) - - def _fsckFailed(self, rc): - for errorCode in self._fsckErrors.keys(): - if rc & errorCode: - return True - return False - - def _fsckErrorMessage(self, rc): - msg = '' - - for errorCode in self._fsckErrors.keys(): - if rc & errorCode: - msg += "\n" + self._fsckErrors[errorCode] - - return msg.strip() - - def writeRandomUUID(self): - if not self.exists: - raise FSError("filesystem does not exist") - - err = None - try: - rc = util.run_program(["tune2fs", "-U", "random", self.device]) - except OSError as e: - err = str(e) - else: - if rc: - err = rc - - if err: - raise FSError("failed to set UUID for %s: %s" % (self.device, err)) - - @property - def minSize(self): - """ Minimum size for this filesystem in MB. """ - if self._minInstanceSize is None: - # try once in the beginning to get the minimum size for an - # existing filesystem. - size = self._minSize - blockSize = None - - if self.exists and os.path.exists(self.device): - # get block size - buf = util.capture_output([self.infofsProg, "-h", self.device]) - for line in buf.splitlines(): - if line.startswith("Block size:"): - blockSize = int(line.split(" ")[-1]) - - if line.startswith("Filesystem state:"): - self.dirty = "not clean" in line - self.errors = "with errors" in line - - if blockSize is None: - raise FSError("failed to get block size for %s filesystem " - "on %s" % (self.mountType, self.device)) - - # get minimum size according to resize2fs - buf = util.capture_output([self.resizefsProg, - "-P", self.device]) - for line in buf.splitlines(): - if "minimum size of the filesystem:" not in line: - continue - - # line will look like: - # Estimated minimum size of the filesystem: 1148649 - # - # NOTE: The minimum size reported is in blocks. Convert - # to bytes, then megabytes, and finally round up. - (text, sep, minSize) = line.partition(": ") - size = long(minSize) * blockSize - size = math.ceil(size / 1024.0 / 1024.0) - break - - if size is None: - log.warning("failed to get minimum size for %s filesystem " - "on %s" % (self.mountType, self.device)) - else: - orig_size = size - size = min(size * 1.1, size + 500, self.currentSize) - if orig_size < size: - log.debug("padding min size from %d up to %d" % (orig_size, size)) - else: - log.debug("using current size %d as min size" % size) - - self._minInstanceSize = size - - return self._minInstanceSize - - @property - def needsFSCheck(self): - return self.dirty or self.errors - - @property - def resizeArgs(self): - argv = ["-p", self.device, "%dM" % (self.targetSize,)] - return argv - -register_device_format(Ext2FS) - - -class Ext3FS(Ext2FS): - """ ext3 filesystem. """ - _type = "ext3" - _defaultFormatOptions = ["-t", "ext3"] - _modules = ["ext3"] - partedSystem = fileSystemType["ext3"] - - # It is possible for a user to specify an fsprofile that defines a blocksize - # smaller than the default of 4096 bytes and therefore to make liars of us - # with regard to this maximum filesystem size, but if they're doing such - # things they should know the implications of their chosen block size. - _maxSize = 16 * 1024 * 1024 - - @property - def needsFSCheck(self): - return self.errors - -register_device_format(Ext3FS) - - -class Ext4FS(Ext3FS): - """ ext4 filesystem. """ - _type = "ext4" - _defaultFormatOptions = ["-t", "ext4"] - _modules = ["ext4"] - partedSystem = fileSystemType["ext4"] - -register_device_format(Ext4FS) - - -class FATFS(FS): - """ FAT filesystem. """ - _type = "vfat" - _mkfs = "mkdosfs" - _modules = ["vfat"] - _labelfs = "dosfslabel" - _fsck = "dosfsck" - _fsckErrors = {1: _("Recoverable errors have been detected or dosfsck has " - "discovered an internal inconsistency."), - 2: _("Usage error.")} - _supported = True - _formattable = True - _maxSize = 1024 * 1024 - _packages = [ "dosfstools" ] - _defaultMountOptions = ["umask=0077", "shortname=winnt"] - # FIXME this should be fat32 in some cases - partedSystem = fileSystemType["fat16"] - - def _fsckFailed(self, rc): - if rc >= 1: - return True - return False - - def _fsckErrorMessage(self, rc): - return self._fsckErrors[rc] - -register_device_format(FATFS) - - -class EFIFS(FATFS): - _type = "efi" - _mountType = "vfat" - _modules = ["vfat"] - _name = "EFI System Partition" - _minSize = 50 - - @property - def supported(self): - return (isinstance(platform.platform, platform.EFI) and - self.utilsAvailable) - -register_device_format(EFIFS) - - -class BTRFS(FS): - """ btrfs filesystem """ - _type = "btrfs" - _mkfs = "mkfs.btrfs" - _modules = ["btrfs"] - _resizefs = "btrfsctl" - _formattable = True - _linuxNative = True - _maxLabelChars = 256 - _supported = True - _dump = True - _check = True - _packages = ["btrfs-progs"] - _minSize = 256 - _maxSize = 16 * 1024 * 1024 - # FIXME parted needs to be taught about btrfs so that we can set the - # partition table type correctly for btrfs partitions - # partedSystem = fileSystemType["btrfs"] - - def __init__(self, *args, **kwargs): - super(BTRFS, self).__init__(*args, **kwargs) - self.volUUID = kwargs.pop("volUUID", None) - - def create(self, *args, **kwargs): - # filesystem creation is done in storage.devicelibs.btrfs.create_volume - pass - - def destroy(self, *args, **kwargs): - # filesystem creation is done in storage.devicelibs.btrfs.delete_volume - pass - - def setup(self, *args, **kwargs): - log_method_call(self, type=self.mountType, device=self.device, - mountpoint=self.mountpoint) - if not self.mountpoint and "mountpoint" not in kwargs: - # Since btrfs vols have subvols the format setup is automatic. - # Don't try to mount it if there's no mountpoint. - return - - return self.mount(*args, **kwargs) - - def _getFormatOptions(self, options=None): - argv = [] - if options and isinstance(options, list): - argv.extend(options) - argv.extend(self.defaultFormatOptions) - if self.label: - argv.extend(["-L", self.label]) - argv.append(self.device) - return argv - - @property - def resizeArgs(self): - argv = ["-r", "%dm" % (self.targetSize,), self.device] - return argv - -register_device_format(BTRFS) - - -class GFS2(FS): - """ gfs2 filesystem. """ - _type = "gfs2" - _mkfs = "mkfs.gfs2" - _modules = ["dlm", "gfs2"] - _formattable = True - _defaultFormatOptions = ["-j", "1", "-p", "lock_nolock", "-O"] - _linuxNative = True - _supported = False - _dump = True - _check = True - _packages = ["gfs2-utils"] - # FIXME parted needs to be thaught about btrfs so that we can set the - # partition table type correctly for btrfs partitions - # partedSystem = fileSystemType["gfs2"] - - @property - def supported(self): - """ Is this filesystem a supported type? """ - supported = self._supported - if flags.gfs2: - supported = self.utilsAvailable - - return supported - -register_device_format(GFS2) - - -class JFS(FS): - """ JFS filesystem """ - _type = "jfs" - _mkfs = "mkfs.jfs" - _modules = ["jfs"] - _labelfs = "jfs_tune" - _defaultFormatOptions = ["-q"] - _defaultLabelOptions = ["-L"] - _maxLabelChars = 16 - _maxSize = 8 * 1024 * 1024 - _formattable = True - _linuxNative = True - _supported = False - _dump = True - _check = True - _infofs = "jfs_tune" - _defaultInfoOptions = ["-l"] - _existingSizeFields = ["Aggregate block size:", "Aggregate size:"] - partedSystem = fileSystemType["jfs"] - - @property - def supported(self): - """ Is this filesystem a supported type? """ - supported = self._supported - if flags.jfs: - supported = self.utilsAvailable - - return supported - -register_device_format(JFS) - - -class ReiserFS(FS): - """ reiserfs filesystem """ - _type = "reiserfs" - _mkfs = "mkreiserfs" - _resizefs = "resize_reiserfs" - _labelfs = "reiserfstune" - _modules = ["reiserfs"] - _defaultFormatOptions = ["-f", "-f"] - _defaultLabelOptions = ["-l"] - _maxLabelChars = 16 - _maxSize = 16 * 1024 * 1024 - _formattable = True - _linuxNative = True - _supported = False - _dump = True - _check = True - _packages = ["reiserfs-utils"] - _infofs = "debugreiserfs" - _defaultInfoOptions = [] - _existingSizeFields = ["Count of blocks on the device:", "Blocksize:"] - partedSystem = fileSystemType["reiserfs"] - - @property - def supported(self): - """ Is this filesystem a supported type? """ - supported = self._supported - if flags.reiserfs: - supported = self.utilsAvailable - - return supported - - @property - def resizeArgs(self): - argv = ["-s", "%dM" % (self.targetSize,), self.device] - return argv - -register_device_format(ReiserFS) - - -class XFS(FS): - """ XFS filesystem """ - _type = "xfs" - _mkfs = "mkfs.xfs" - _modules = ["xfs"] - _labelfs = "xfs_admin" - _defaultFormatOptions = ["-f"] - _defaultLabelOptions = ["-L"] - _maxLabelChars = 16 - _maxSize = 16 * 1024 * 1024 - _formattable = True - _linuxNative = True - _supported = True - _dump = True - _check = True - _packages = ["xfsprogs"] - _infofs = "xfs_db" - _defaultInfoOptions = ["-c", "\"sb 0\"", "-c", "\"p dblocks\"", - "-c", "\"p blocksize\""] - _existingSizeFields = ["dblocks =", "blocksize ="] - partedSystem = fileSystemType["xfs"] - - def _getLabelArgs(self, label): - argv = [] - argv.extend(self.defaultLabelOptions) - argv.extend([label, self.device]) - return argv - - def sync(self, root='/'): - """ Ensure that data we've written is at least in the journal. - - This is a little odd because xfs_freeze will only be - available under the install root. - """ - if not self.status or not self._mountpoint.startswith(root): - return - - try: - util.run_program(["xfs_freeze", "-f", self.mountpoint], root=root) - except OSError as e: - log.error("failed to run xfs_freeze: %s" % e) - - try: - util.run_program(["xfs_freeze", "-u", self.mountpoint], root=root) - except OSError as e: - log.error("failed to run xfs_freeze: %s" % e) - -register_device_format(XFS) - - -class HFS(FS): - _type = "hfs" - _mkfs = "hformat" - _modules = ["hfs"] - _formattable = True - partedSystem = fileSystemType["hfs"] - -register_device_format(HFS) - - -class AppleBootstrapFS(HFS): - _type = "appleboot" - _mountType = "hfs" - _name = "Apple Bootstrap" - _minSize = 800.00 / 1024.00 - _maxSize = 1 - - @property - def supported(self): - return (isinstance(platform.platform, platform.NewWorldPPC) - and self.utilsAvailable) - -register_device_format(AppleBootstrapFS) - - -class HFSPlus(FS): - _type = "hfs+" - _modules = ["hfsplus"] - _udevTypes = ["hfsplus"] - _mkfs = "mkfs.hfsplus" - _fsck = "fsck.hfsplus" - _packages = ["hfsplus-tools"] - _formattable = True - _mountType = "hfsplus" - _minSize = 1 - _maxSize = 2 * 1024 * 1024 - _check = True - partedSystem = fileSystemType["hfs+"] - -register_device_format(HFSPlus) - - -class NTFS(FS): - """ ntfs filesystem. """ - _type = "ntfs" - _resizefs = "ntfsresize" - _fsck = "ntfsresize" - _resizable = True - _minSize = 1 - _maxSize = 16 * 1024 * 1024 - _defaultMountOptions = ["defaults", "ro"] - _defaultCheckOptions = ["-c"] - _packages = ["ntfsprogs"] - _infofs = "ntfsinfo" - _defaultInfoOptions = ["-m"] - _existingSizeFields = ["Cluster Size:", "Volume Size in Clusters:"] - partedSystem = fileSystemType["ntfs"] - - @property - def mountable(self): - return False - - def _fsckFailed(self, rc): - if rc != 0: - return True - return False - - @property - def minSize(self): - """ The minimum filesystem size in megabytes. """ - if self._minInstanceSize is None: - # we try one time to determine the minimum size. - size = self._minSize - if self.exists and os.path.exists(self.device) and \ - util.find_program_in_path(self.resizefsProg): - minSize = None - buf = util.run_program([self.resizefsProg, "-m", self.device]) - for l in buf.split("\n"): - if not l.startswith("Minsize"): - continue - try: - minSize = int(l.split(":")[1].strip()) # MB - minSize *= (mb / mib) # MiB - except (IndexError, ValueError) as e: - minSize = None - log.warning("Unable to parse output for minimum size on %s: %s" %(self.device, e)) - - if minSize is None: - log.warning("Unable to discover minimum size of filesystem " - "on %s" %(self.device,)) - else: - size = min(minSize * 1.1, minSize + 500, self.currentSize) - if minSize < size: - log.debug("padding min size from %d up to %d" % (minSize, size)) - else: - log.debug("using current size %d as min size" % size) - - self._minInstanceSize = size - - return self._minInstanceSize - - @property - def resizeArgs(self): - # You must supply at least two '-f' options to ntfsresize or - # the proceed question will be presented to you. - - # FIXME: This -1 is because our partition alignment calculations plus - # converting back and forth between MiB and MB means the filesystem is - # getting resized to be slightly larger than the partition holding it. - # This hack just makes the filesystem fit. - targetSize = (mib / mb) * (self.targetSize-1) # convert MiB to MB - argv = ["-ff", "-s", "%dM" % (targetSize,), self.device] - return argv - - -register_device_format(NTFS) - - -# if this isn't going to be mountable it might as well not be here -class NFS(FS): - """ NFS filesystem. """ - _type = "nfs" - _modules = ["nfs"] - - def _deviceCheck(self, devspec): - if devspec is not None and ":" not in devspec: - raise ValueError("device must be of the form <host>:<path>") - - @property - def mountable(self): - return False - - def _setDevice(self, devspec): - self._deviceCheck(devspec) - self._device = devspec - - def _getDevice(self): - return self._device - - device = property(lambda f: f._getDevice(), - lambda f,d: f._setDevice(d), - doc="Full path the device this format occupies") - -register_device_format(NFS) - - -class NFSv4(NFS): - """ NFSv4 filesystem. """ - _type = "nfs4" - _modules = ["nfs4"] - -register_device_format(NFSv4) - - -class Iso9660FS(FS): - """ ISO9660 filesystem. """ - _type = "iso9660" - _formattable = False - _supported = True - _resizable = False - _linuxNative = False - _dump = False - _check = False - _defaultMountOptions = ["ro"] - -register_device_format(Iso9660FS) - - -class NoDevFS(FS): - """ nodev filesystem base class """ - _type = "nodev" - - def __init__(self, *args, **kwargs): - FS.__init__(self, *args, **kwargs) - self.exists = True - self.device = self.type - - def _setDevice(self, devspec): - self._device = devspec - - def _getExistingSize(self): - pass - -register_device_format(NoDevFS) - - -class DevPtsFS(NoDevFS): - """ devpts filesystem. """ - _type = "devpts" - _defaultMountOptions = ["gid=5", "mode=620"] - -register_device_format(DevPtsFS) - - -# these don't really need to be here -class ProcFS(NoDevFS): - _type = "proc" - -register_device_format(ProcFS) - - -class SysFS(NoDevFS): - _type = "sysfs" - -register_device_format(SysFS) - - -class TmpFS(NoDevFS): - _type = "tmpfs" - -register_device_format(TmpFS) - - -class BindFS(FS): - _type = "bind" - - @property - def mountable(self): - return True - - def _getExistingSize(self): - pass - -register_device_format(BindFS) - - -class SELinuxFS(NoDevFS): - _type = "selinuxfs" - - @property - def mountable(self): - return flags.selinux and super(SELinuxFS, self).mountable - -register_device_format(SELinuxFS) - - -class USBFS(NoDevFS): - _type = "usbfs" - -register_device_format(USBFS) - diff --git a/pyanaconda/storage/formats/luks.py b/pyanaconda/storage/formats/luks.py deleted file mode 100644 index 4b37cb430..000000000 --- a/pyanaconda/storage/formats/luks.py +++ /dev/null @@ -1,342 +0,0 @@ -# luks.py -# Device format classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 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> -# - - - -import os - -try: - import volume_key -except ImportError: - volume_key = None - -from ..storage_log import log_method_call -from ..errors import * -from ..devicelibs import crypto -from . import DeviceFormat, register_device_format - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - - -class LUKS(DeviceFormat): - """ A LUKS device. """ - _type = "luks" - _name = "LUKS" - _lockedName = _("Encrypted") - _udevTypes = ["crypto_LUKS"] - _formattable = True # can be formatted - _supported = False # is supported - _linuxNative = True # for clearpart - _packages = ["cryptsetup-luks"] # required packages - - def __init__(self, *args, **kwargs): - """ Create a LUKS instance. - - Keyword Arguments: - - device -- the path to the underlying device - name -- the name of the mapped device - uuid -- this device's UUID - passphrase -- device passphrase (string) - key_file -- path to a file containing a key (string) - cipher -- cipher mode string - key_size -- key size in bits - exists -- indicates whether this is an existing format - escrow_cert -- certificate to use for key escrow - add_backup_passphrase -- generate a backup passphrase? - """ - log_method_call(self, *args, **kwargs) - DeviceFormat.__init__(self, *args, **kwargs) - self.cipher = kwargs.get("cipher") - self.key_size = kwargs.get("key_size") - self.mapName = kwargs.get("name") - - if not self.exists and not self.cipher: - self.cipher = "aes-xts-plain64" - if not self.key_size: - # default to the max (512 bits) for aes-xts - self.key_size = 512 - - # FIXME: these should both be lists, but managing them will be a pain - self.__passphrase = kwargs.get("passphrase") - self._key_file = kwargs.get("key_file") - self.escrow_cert = kwargs.get("escrow_cert") - self.add_backup_passphrase = kwargs.get("add_backup_passphrase", False) - - if not self.mapName and self.exists and self.uuid: - self.mapName = "luks-%s" % self.uuid - elif not self.mapName and self.device: - self.mapName = "luks-%s" % os.path.basename(self.device) - - def __repr__(self): - s = DeviceFormat.__repr__(self) - if self.__passphrase: - passphrase = "(set)" - else: - passphrase = "(not set)" - s += (" cipher = %(cipher)s keySize = %(keySize)s" - " mapName = %(mapName)s\n" - " keyFile = %(keyFile)s passphrase = %(passphrase)s\n" - " escrowCert = %(escrowCert)s addBackup = %(backup)s" % - {"cipher": self.cipher, "keySize": self.key_size, - "mapName": self.mapName, "keyFile": self._key_file, - "passphrase": passphrase, "escrowCert": self.escrow_cert, - "backup": self.add_backup_passphrase}) - return s - - @property - def dict(self): - d = super(LUKS, self).dict - d.update({"cipher": self.cipher, "keySize": self.key_size, - "mapName": self.mapName, "hasKey": self.hasKey, - "escrowCert": self.escrow_cert, - "backup": self.add_backup_passphrase}) - return d - - @property - def name(self): - name = self._name - # for existing locked devices, show "Encrypted" instead of LUKS - if self.hasKey or not self.exists: - name = self._name - else: - name = "%s (%s)" % (self._lockedName, self._name) - return name - - def _setPassphrase(self, passphrase): - """ Set the passphrase used to access this device. """ - self.__passphrase = passphrase - - passphrase = property(fset=_setPassphrase) - - @property - def hasKey(self): - return ((self.__passphrase not in ["", None]) or - (self._key_file and os.access(self._key_file, os.R_OK))) - - @property - def configured(self): - """ To be ready we need a key or passphrase and a map name. """ - return self.hasKey and self.mapName - - @property - def status(self): - if not self.exists or not self.mapName: - return False - return os.path.exists("/dev/mapper/%s" % self.mapName) - - def probe(self): - """ Probe for any missing information about this format. - - cipher mode, key size - """ - raise NotImplementedError("probe method not defined for LUKS") - - def setup(self, *args, **kwargs): - """ Open, or set up, the format. """ - log_method_call(self, device=self.device, mapName=self.mapName, - type=self.type, status=self.status) - if not self.configured: - raise LUKSError("luks device not configured") - - if self.status: - return - - DeviceFormat.setup(self, *args, **kwargs) - crypto.luks_open(self.device, self.mapName, - passphrase=self.__passphrase, - key_file=self._key_file) - - def teardown(self, *args, **kwargs): - """ Close, or tear down, the format. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise LUKSError("format has not been created") - - if self.status: - log.debug("unmapping %s" % self.mapName) - crypto.luks_close(self.mapName) - - def create(self, *args, **kwargs): - """ Create the format. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.hasKey: - raise LUKSError("luks device has no key/passphrase") - - try: - DeviceFormat.create(self, *args, **kwargs) - crypto.luks_format(self.device, - passphrase=self.__passphrase, - key_file=self._key_file, - cipher=self.cipher, - key_size=self.key_size) - except Exception: - raise - else: - self.uuid = crypto.luks_uuid(self.device) - self.exists = True - self.mapName = "luks-%s" % self.uuid - self.notifyKernel() - - def destroy(self, *args, **kwargs): - """ Create the format. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - self.teardown() - DeviceFormat.destroy(self, *args, **kwargs) - - @property - def keyFile(self): - """ Path to key file to be used in /etc/crypttab """ - return self._key_file - - def addKeyFromFile(self, keyfile): - """ Add a new key from a file. - - Add the contents of the specified key file to an available key - slot in the LUKS header. - """ - log_method_call(self, device=self.device, - type=self.type, status=self.status, file=keyfile) - if not self.exists: - raise LUKSError("format has not been created") - - crypto.luks_add_key(self.device, - passphrase=self.__passphrase, - key_file=self._key_file, - new_key_file=keyfile) - - def addPassphrase(self, passphrase): - """ Add a new passphrase. - - Add the specified passphrase to an available key slot in the - LUKS header. - """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise LUKSError("format has not been created") - - crypto.luks_add_key(self.device, - passphrase=self.__passphrase, - key_file=self._key_file, - new_passphrase=passphrase) - - def removeKeyFromFile(self, keyfile): - """ Remove a key contained in a file. - - Remove key contained in the specified key file from the LUKS - header. - """ - log_method_call(self, device=self.device, - type=self.type, status=self.status, file=keyfile) - if not self.exists: - raise LUKSError("format has not been created") - - crypto.luks_remove_key(self.device, - passphrase=self.__passphrase, - key_file=self._key_file, - del_key_file=keyfile) - - - def removePassphrase(self, passphrase): - """ Remove the specified passphrase from the LUKS header. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise LUKSError("format has not been created") - - crypto.luks_remove_key(self.device, - passphrase=self.__passphrase, - key_file=self._key_file, - del_passphrase=passphrase) - - def _escrowVolumeIdent(self, vol): - """ Return an escrow packet filename prefix for a volume_key.Volume. """ - label = vol.label - if label is not None: - label = label.replace("/", "_") - uuid = vol.uuid - if uuid is not None: - uuid = uuid.replace("/", "_") - # uuid is never None on LUKS volumes - if label is not None and uuid is not None: - volume_ident = "%s-%s" % (label, uuid) - elif uuid is not None: - volume_ident = uuid - elif label is not None: - volume_ident = label - else: - volume_ident = "_unknown" - return volume_ident - - def escrow(self, directory, backupPassphrase): - log.debug("escrow: escrowVolume start for %s" % self.device) - if volume_key is None: - raise LUKSError("Missing key escrow support libraries") - - vol = volume_key.Volume.open(self.device) - volume_ident = self._escrowVolumeIdent(vol) - - ui = volume_key.UI() - # This callback is not expected to be used, let it always fail - ui.generic_cb = lambda unused_prompt, unused_echo: None - def known_passphrase_cb(unused_prompt, failed_attempts): - if failed_attempts == 0: - return self.__passphrase - return None - ui.passphrase_cb = known_passphrase_cb - - log.debug("escrow: getting secret") - vol.get_secret(volume_key.SECRET_DEFAULT, ui) - log.debug("escrow: creating packet") - default_packet = vol.create_packet_assymetric_from_cert_data \ - (volume_key.SECRET_DEFAULT, self.escrow_cert, ui) - log.debug("escrow: packet created") - with open("%s/%s-escrow" % (directory, volume_ident), "wb") as f: - f.write(default_packet) - log.debug("escrow: packet written") - - if self.add_backup_passphrase: - log.debug("escrow: adding backup passphrase") - vol.add_secret(volume_key.SECRET_PASSPHRASE, backupPassphrase) - log.debug("escrow: creating backup packet") - backup_passphrase_packet = \ - vol.create_packet_assymetric_from_cert_data \ - (volume_key.SECRET_PASSPHRASE, self.escrow_cert, ui) - log.debug("escrow: backup packet created") - with open("%s/%s-escrow-backup-passphrase" % - (directory, volume_ident), "wb") as f: - f.write(backup_passphrase_packet) - log.debug("escrow: backup packet written") - - log.debug("escrow: escrowVolume done for %s" % repr(self.device)) - - -register_device_format(LUKS) - diff --git a/pyanaconda/storage/formats/lvmpv.py b/pyanaconda/storage/formats/lvmpv.py deleted file mode 100644 index d804711fa..000000000 --- a/pyanaconda/storage/formats/lvmpv.py +++ /dev/null @@ -1,146 +0,0 @@ -# lvmpv.py -# Device format classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 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> -# - -import os - -from ..storage_log import log_method_call -from parted import PARTITION_LVM -from ..errors import * -from ..devicelibs import lvm -from . import DeviceFormat, register_device_format - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - - -class LVMPhysicalVolume(DeviceFormat): - """ An LVM physical volume. """ - _type = "lvmpv" - _name = "physical volume (LVM)" - _udevTypes = ["LVM2_member"] - partedFlag = PARTITION_LVM - _formattable = True # can be formatted - _supported = True # is supported - _linuxNative = True # for clearpart - _minSize = lvm.LVM_PE_SIZE * 2 # one for metadata and one for data - _packages = ["lvm2"] # required packages - - def __init__(self, *args, **kwargs): - """ Create an LVMPhysicalVolume instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this PV's uuid (not the VG uuid) - vgName -- the name of the VG this PV belongs to - vgUuid -- the UUID of the VG this PV belongs to - peStart -- offset of first physical extent - exists -- indicates whether this is an existing format - - """ - log_method_call(self, *args, **kwargs) - DeviceFormat.__init__(self, *args, **kwargs) - self.vgName = kwargs.get("vgName") - self.vgUuid = kwargs.get("vgUuid") - # liblvm may be able to tell us this at some point, even - # for not-yet-created devices - self.peStart = kwargs.get("peStart", lvm.LVM_PE_START) # in MB - - self.inconsistentVG = False - - def __repr__(self): - s = DeviceFormat.__repr__(self) - s += (" vgName = %(vgName)s vgUUID = %(vgUUID)s" - " peStart = %(peStart)s" % - {"vgName": self.vgName, "vgUUID": self.vgUuid, - "peStart": self.peStart}) - return s - - @property - def dict(self): - d = super(LVMPhysicalVolume, self).dict - d.update({"vgName": self.vgName, "vgUUID": self.vgUuid, - "peStart": self.peStart}) - return d - - def probe(self): - """ Probe for any missing information about this device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise PhysicalVolumeError("format has not been created") - - #info = lvm.pvinfo(self.device) - #self.vgName = info['vg_name'] - #self.vgUuid = info['vg_uuid'] - - def create(self, *args, **kwargs): - """ Create the format. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - - try: - DeviceFormat.create(self, *args, **kwargs) - # Consider use of -Z|--zero - # -f|--force or -y|--yes may be required - - # lvm has issues with persistence of metadata, so here comes the - # hammer... - DeviceFormat.destroy(self, *args, **kwargs) - - lvm.pvcreate(self.device) - except Exception: - raise - else: - self.exists = True - self.notifyKernel() - - def destroy(self, *args, **kwargs): - """ Destroy the format. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise PhysicalVolumeError("format has not been created") - - if self.status: - raise PhysicalVolumeError("device is active") - - # FIXME: verify path exists? - try: - lvm.pvremove(self.device) - except LVMError: - DeviceFormat.destroy(self, *args, **kwargs) - - self.exists = False - self.notifyKernel() - - @property - def status(self): - # XXX hack - return (self.exists and self.vgName and - os.path.isdir("/dev/mapper/%s" % self.vgName)) - -register_device_format(LVMPhysicalVolume) - diff --git a/pyanaconda/storage/formats/mdraid.py b/pyanaconda/storage/formats/mdraid.py deleted file mode 100644 index 9d1e26eed..000000000 --- a/pyanaconda/storage/formats/mdraid.py +++ /dev/null @@ -1,120 +0,0 @@ -# mdraid.py -# Device format classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 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> -# - -import os - -from ..storage_log import log_method_call -from parted import PARTITION_RAID -from ..errors import * -from ..devicelibs import mdraid -from . import DeviceFormat, register_device_format -from ..flags import flags - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - - -class MDRaidMember(DeviceFormat): - """ An mdraid member disk. """ - _type = "mdmember" - _name = "software RAID" - _udevTypes = ["linux_raid_member"] - partedFlag = PARTITION_RAID - _formattable = True # can be formatted - _supported = True # is supported - _linuxNative = True # for clearpart - _packages = ["mdadm"] # required packages - - def __init__(self, *args, **kwargs): - """ Create a MDRaidMember instance. - - Keyword Arguments: - - device -- path to underlying device - uuid -- this member device's uuid - mdUuid -- the uuid of the array this device belongs to - exists -- indicates whether this is an existing format - - """ - log_method_call(self, *args, **kwargs) - DeviceFormat.__init__(self, *args, **kwargs) - self.mdUuid = kwargs.get("mdUuid") - self.raidMinor = None - - #self.probe() - self.biosraid = kwargs.get("biosraid") - - def __repr__(self): - s = DeviceFormat.__repr__(self) - s += (" mdUUID = %(mdUUID)s biosraid = %(biosraid)s" % - {"mdUUID": self.mdUuid, "biosraid": self.biosraid}) - return s - - @property - def dict(self): - d = super(MDRaidMember, self).dict - d.update({"mdUUID": self.mdUuid, "biosraid": self.biosraid}) - return d - - def probe(self): - """ Probe for any missing information about this format. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise MDMemberError("format does not exist") - - info = mdraid.mdexamine(self.device) - if self.uuid is None: - self.uuid = info['uuid'] - if self.raidMinor is None: - self.raidMinor = info['mdMinor'] - - def destroy(self, *args, **kwargs): - if not self.exists: - raise MDMemberError("format does not exist") - - if not os.access(self.device, os.W_OK): - raise MDMemberError("device path does not exist") - - mdraid.mddestroy(self.device) - self.exists = False - - @property - def status(self): - # XXX hack -- we don't have a nice way to see if the array is active - return False - - @property - def hidden(self): - return (self._hidden or self.biosraid) - -# nodmraid -> Wether to use BIOS RAID or not -# Note the anaconda cmdline has not been parsed yet when we're first imported, -# so we can not use flags.dmraid here -if not flags.noiswmd and flags.dmraid: - MDRaidMember._udevTypes.append("isw_raid_member") - -register_device_format(MDRaidMember) - diff --git a/pyanaconda/storage/formats/multipath.py b/pyanaconda/storage/formats/multipath.py deleted file mode 100644 index 01d69ee3e..000000000 --- a/pyanaconda/storage/formats/multipath.py +++ /dev/null @@ -1,94 +0,0 @@ -# multipath.py -# multipath device formats -# -# Copyright (C) 2009 Red Hat, Inc. -# -# 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/>. -# -# 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): Peter Jones <pjones@redhat.com> -# - -from ..storage_log import log_method_call -from ..errors import * -from . import DeviceFormat, register_device_format - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -class MultipathMember(DeviceFormat): - """ A multipath member disk. """ - _type = "multipath_member" - _name = "multipath member device" - _udev_types = ["multipath_member"] - _formattable = False # can be formatted - _supported = True # is supported - _linuxNative = False # for clearpart - _packages = ["device-mapper-multipath"] # required packages - _resizable = False # can be resized - _maxSize = 0 # maximum size in MB - _minSize = 0 # minimum size in MB - _hidden = True # hide devices with this formatting? - - def __init__(self, *args, **kwargs): - """ Create a DeviceFormat instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this format's UUID - exists -- indicates whether this is an existing format - - On initialization this format is like DeviceFormat - - """ - log_method_call(self, *args, **kwargs) - DeviceFormat.__init__(self, *args, **kwargs) - - # Initialize the attribute that will hold the block object. - self._member = None - - def __repr__(self): - s = DeviceFormat.__repr__(self) - s += (" member = %(member)r" % {"member": self.member}) - return s - - def _getMember(self): - return self._member - - def _setMember(self, member): - self._member = member - - member = property(lambda s: s._getMember(), - lambda s,m: s._setMember(m)) - - def create(self, *args, **kwargs): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - raise MultipathMemberError("creation of multipath members is non-sense") - - def destroy(self, *args, **kwargs): - log_method_call(self, device=self.device, - type=self.type, status=self.status) - raise MultipathMemberError("destruction of multipath members is non-sense") - -register_device_format(MultipathMember) - diff --git a/pyanaconda/storage/formats/prepboot.py b/pyanaconda/storage/formats/prepboot.py deleted file mode 100644 index dd2cf8c82..000000000 --- a/pyanaconda/storage/formats/prepboot.py +++ /dev/null @@ -1,87 +0,0 @@ -# prepboot.py -# Format class for PPC PReP Boot. -# -# Copyright (C) 2009 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> -# - -from ..errors import * -from .. import platform -from . import DeviceFormat, register_device_format -from parted import PARTITION_PREP -import os -import logging -log = logging.getLogger("storage") - -class PPCPRePBoot(DeviceFormat): - """ Generic device format. """ - _type = "prepboot" - _name = "PPC PReP Boot" - _udevTypes = [] - partedFlag = PARTITION_PREP - _formattable = True # can be formatted - _linuxNative = True # for clearpart - _maxSize = 10 # maximum size in MB - _minSize = 4 # minimum size in MB - - def __init__(self, *args, **kwargs): - """ Create a PRePBoot instance. - - Keyword Arguments: - - device -- path to the underlying device - exists -- indicates whether this is an existing format - - """ - DeviceFormat.__init__(self, *args, **kwargs) - - def create(self, *args, **kwargs): - if self.exists: - raise FSError("PReP Boot format already exists") - - DeviceFormat.create(self, *args, **kwargs) - - try: - fd = os.open(self.device, os.O_RDWR) - length = os.lseek(fd, 0, os.SEEK_END) - os.lseek(fd, 0, os.SEEK_SET) - buf = '\0' * 1024 * 1024 - while length > 0: - if length >= len(buf): - os.write(fd, buf) - length -= len(buf) - else: - buf = '0' * length - os.write(fd, buf) - length = 0 - os.close(fd) - except OSError as e: - log.error("error zeroing out %s: %s" % (self.device, e)) - if fd: - os.close(fd) - - @property - def status(self): - return False - - @property - def supported(self): - return isinstance(platform.platform, platform.IPSeriesPPC) - -register_device_format(PPCPRePBoot) - diff --git a/pyanaconda/storage/formats/swap.py b/pyanaconda/storage/formats/swap.py deleted file mode 100644 index 903fe6e2c..000000000 --- a/pyanaconda/storage/formats/swap.py +++ /dev/null @@ -1,171 +0,0 @@ -# swap.py -# Device format classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 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> -# - -from parted import PARTITION_SWAP, fileSystemType -from ..storage_log import log_method_call -from ..errors import * -from ..util import numeric_type -from ..devicelibs import swap -from . import DeviceFormat, register_device_format - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - - -class SwapSpace(DeviceFormat): - """ Swap space """ - _type = "swap" - _name = None - _udevTypes = ["swap"] - partedFlag = PARTITION_SWAP - partedSystem = fileSystemType["linux-swap(v1)"] - _formattable = True # can be formatted - _supported = True # is supported - _linuxNative = True # for clearpart - - #see rhbz#744129 for details - _maxSize = 128 * 1024 - - def __init__(self, *args, **kwargs): - """ Create a SwapSpace instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this swap space's uuid - label -- this swap space's label - priority -- this swap space's priority - exists -- indicates whether this is an existing format - - """ - log_method_call(self, *args, **kwargs) - DeviceFormat.__init__(self, *args, **kwargs) - - self.priority = kwargs.get("priority") - self.label = kwargs.get("label") - - def __repr__(self): - s = DeviceFormat.__repr__(self) - s += (" priority = %(priority)s label = %(label)s" % - {"priority": self.priority, "label": self.label}) - return s - - @property - def dict(self): - d = super(SwapSpace, self).dict - d.update({"priority": self.priority, "label": self.label}) - return d - - def _setPriority(self, priority): - if priority is None: - self._priority = None - return - - if not isinstance(priority, int) or not 0 <= priority <= 32767: - raise ValueError("swap priority must be an integer between 0 and 32767") - - self._priority = priority - - def _getPriority(self): - return self._priority - - priority = property(_getPriority, _setPriority, - doc="The priority of the swap device") - - def _getOptions(self): - opts = "" - if self.priority is not None: - opts += "pri=%d" % self.priority - - return opts - - def _setOptions(self, opts): - if not opts: - self.priority = None - return - - for option in opts.split(","): - (opt, equals, arg) = option.partition("=") - if equals and opt == "pri": - try: - self.priority = int(arg) - except ValueError: - log.info("invalid value for swap priority: %s" % arg) - - options = property(_getOptions, _setOptions, - doc="The swap device's fstab options string") - - @property - def status(self): - """ Device status. """ - return self.exists and swap.swapstatus(self.device) - - def setup(self, *args, **kwargs): - """ Open, or set up, a device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise SwapSpaceError("format has not been created") - - if self.status: - return - - DeviceFormat.setup(self, *args, **kwargs) - swap.swapon(self.device, priority=self.priority) - - def teardown(self, *args, **kwargs): - """ Close, or tear down, a device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise SwapSpaceError("format has not been created") - - if self.status: - swap.swapoff(self.device) - - def create(self, *args, **kwargs): - """ Create the device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - force = kwargs.get("force") - if not force and self.exists: - raise SwapSpaceError("format already exists") - - if force: - self.teardown() - elif self.status: - raise SwapSpaceError("device exists and is active") - - try: - DeviceFormat.create(self, *args, **kwargs) - swap.mkswap(self.device, label=self.label) - except Exception: - raise - else: - self.exists = True - self.notifyKernel() - -register_device_format(SwapSpace) - diff --git a/pyanaconda/storage/iscsi.py b/pyanaconda/storage/iscsi.py deleted file mode 100644 index 8b7cbf80d..000000000 --- a/pyanaconda/storage/iscsi.py +++ /dev/null @@ -1,437 +0,0 @@ -# -# iscsi.py - iscsi class -# -# Copyright (C) 2005, 2006 IBM, Inc. All rights reserved. -# Copyright (C) 2006 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/>. -# - -from . import ROOT_PATH -from udev import udev_settle -from . import util -from .flags import flags -import os -import logging -import shutil -import time -import hashlib -import random -import itertools -log = logging.getLogger("storage") - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -has_libiscsi = True -try: - import libiscsi -except ImportError: - has_libiscsi = False - -# Note that stage2 copies all files under /sbin to /usr/sbin -ISCSID="" -INITIATOR_FILE="/etc/iscsi/initiatorname.iscsi" - -ISCSI_MODULES=['cxgb3i', 'bnx2i', 'be2iscsi'] - -def has_iscsi(): - global ISCSID - - if not os.access("/sys/module/iscsi_tcp", os.X_OK): - return False - - if not ISCSID: - location = util.find_program_in_path("iscsid") - if not location: - return False - ISCSID = location - log.info("ISCSID is %s" % (ISCSID,)) - - return True - -def randomIname(): - """Generate a random initiator name the same way as iscsi-iname""" - - s = "iqn.1994-05.com.domain:01." - m = hashlib.md5() - u = os.uname() - for i in u: - m.update(i) - dig = m.hexdigest() - - for i in range(0, 6): - s += dig[random.randrange(0, 32)] - return s - -class iscsi(object): - """ iSCSI utility class. - - This class will automatically discover and login to iBFT (or - other firmware) configured iscsi devices when the startup() method - gets called. It can also be used to manually configure iscsi devices - through the addTarget() method. - - As this class needs to make sure certain things like starting iscsid - and logging in to firmware discovered disks only happens once - and as it keeps a global list of all iSCSI devices it is implemented as - a Singleton. - """ - - def __init__(self): - # Dictionary of discovered targets containing list of (node, - # logged_in) tuples. - self.discovered_targets = {} - # This list contains nodes discovered through iBFT (or other firmware) - self.ibftNodes = [] - self._initiator = "" - self.initiatorSet = False - self.started = False - self.ifaces = {} - - if flags.ibft: - try: - initiatorname = libiscsi.get_firmware_initiator_name() - self._initiator = initiatorname - self.initiatorSet = True - except Exception: - pass - - # So that users can write iscsi() to get the singleton instance - def __call__(self): - return self - - def _getInitiator(self): - if self._initiator != "": - return self._initiator - - return randomIname() - - def _setInitiator(self, val): - if self.initiatorSet and val != self._initiator: - raise ValueError, _("Unable to change iSCSI initiator name once set") - if len(val) == 0: - raise ValueError, _("Must provide an iSCSI initiator name") - self._initiator = val - - initiator = property(_getInitiator, _setInitiator) - - def active_nodes(self, target=None): - """Nodes logged in to""" - if target: - return [node for (node, logged_in) in - self.discovered_targets.get(target, []) - if logged_in] - else: - return [node for (node, logged_in) in - itertools.chain(*self.discovered_targets.values()) - if logged_in] + self.ibftNodes - - def _getMode(self): - if not self.active_nodes(): - return "none" - if self.ifaces: - return "bind" - else: - return "default" - - mode = property(_getMode) - - def _mark_node_active(self, node, active=True): - """Mark node as one logged in to - - Returns False if not found - """ - for target_nodes in self.discovered_targets.values(): - for nodeinfo in target_nodes: - if nodeinfo[0] is node: - nodeinfo[1] = active - return True - return False - - - def _startIBFT(self): - if not flags.ibft: - return - - try: - found_nodes = libiscsi.discover_firmware() - except Exception: - log.info("iscsi: No IBFT info found."); - # an exception here means there is no ibft firmware, just return - return - - for node in found_nodes: - try: - node.login() - log.info("iscsi IBFT: logged into %s at %s:%s through %s" % ( - node.name, node.address, node.port, node.iface)) - self.ibftNodes.append(node) - except IOError as e: - log.error("Could not log into ibft iscsi target %s: %s" % - (node.name, str(e))) - pass - - self.stabilize() - - def stabilize(self): - # Wait for udev to create the devices for the just added disks - - # It is possible when we get here the events for the new devices - # are not send yet, so sleep to make sure the events are fired - time.sleep(2) - udev_settle() - - def create_interfaces(self, ifaces): - for iface in ifaces: - iscsi_iface_name = "iface%d" % len(self.ifaces) - #iscsiadm -m iface -I iface0 --op=new - util.run_program(["iscsiadm", "-m", "iface", - "-I", iscsi_iface_name, "--op=new"]) - #iscsiadm -m iface -I iface0 --op=update -n iface.net_ifacename -v eth0 - util.run_program(["iscsiadm", "-m", "iface", - "-I", iscsi_iface_name, "--op=update", - "-n", "iface.net_ifacename", "-v", iface]) - - self.ifaces[iscsi_iface_name] = iface - log.debug("created_interface %s:%s" % (iscsi_iface_name, iface)) - - def delete_interfaces(self): - if not self.ifaces: - return None - for iscsi_iface_name in self.ifaces: - #iscsiadm -m iface -I iface0 --op=delete - util.run_program(["iscsiadm", "-m", "iface", - "-I", iscsi_iface_name, "--op=delete"]) - self.ifaces = {} - - def startup(self): - if self.started: - return - - if not has_iscsi(): - return - - if self._initiator == "": - log.info("no initiator set") - return - - log.debug("Setting up %s" % (INITIATOR_FILE, )) - log.info("iSCSI initiator name %s", self.initiator) - if os.path.exists(INITIATOR_FILE): - os.unlink(INITIATOR_FILE) - if not os.path.isdir("/etc/iscsi"): - os.makedirs("/etc/iscsi", 0755) - fd = os.open(INITIATOR_FILE, os.O_RDWR | os.O_CREAT) - os.write(fd, "InitiatorName=%s\n" %(self.initiator)) - os.close(fd) - self.initiatorSet = True - - for dir in ['ifaces','isns','nodes','send_targets','slp','static']: - fulldir = "/var/lib/iscsi/%s" % (dir,) - if not os.path.isdir(fulldir): - os.makedirs(fulldir, 0755) - - log.info("iSCSI startup") - util.run_program(['modprobe', '-a'] + ISCSI_MODULES) - # iscsiuio is needed by Broadcom offload cards (bnx2i). Currently - # not present in iscsi-initiator-utils for Fedora. - try: - iscsiuio = util.find_program_in_path('iscsiuio', - raise_on_error=True) - except RuntimeError: - log.info("iscsi: iscsiuio not found.") - else: - log.debug("iscsi: iscsiuio is at %s" % iscsiuio) - util.run_program([iscsiuio]) - # run the daemon - util.run_program([ISCSID]) - time.sleep(1) - - self._startIBFT() - self.started = True - - def discover(self, ipaddr, port="3260", username=None, password=None, - r_username=None, r_password=None, intf=None): - """ - Discover iSCSI nodes on the target available for login. - - If we are logged in a node discovered for specified target - do not do the discovery again as it can corrupt credentials - stored for the node (setAuth and getAuth are using database - in /var/lib/iscsi/nodes which is filled by discovery). Just - return nodes obtained and stored in the first discovery - instead. - - Returns list of nodes user can log in. - """ - authinfo = None - - if not has_iscsi(): - raise IOError, _("iSCSI not available") - if self._initiator == "": - raise ValueError, _("No initiator name set") - - if self.active_nodes((ipaddr, port)): - log.debug("iSCSI: skipping discovery of %s:%s due to active nodes" % - (ipaddr, port)) - else: - if username or password or r_username or r_password: - # Note may raise a ValueError - authinfo = libiscsi.chapAuthInfo(username=username, - password=password, - reverse_username=r_username, - reverse_password=r_password) - self.startup() - - # Note may raise an IOError - found_nodes = libiscsi.discover_sendtargets(address=ipaddr, - port=int(port), - authinfo=authinfo) - if found_nodes is None: - return None - self.discovered_targets[(ipaddr, port)] = [] - for node in found_nodes: - self.discovered_targets[(ipaddr, port)].append([node, False]) - log.debug("discovered iSCSI node: %s" % node.name) - - # only return the nodes we are not logged into yet - return [node for (node, logged_in) in - self.discovered_targets[(ipaddr, port)] - if not logged_in] - - def log_into_node(self, node, username=None, password=None, - r_username=None, r_password=None, intf=None): - """ - Raises IOError. - """ - rc = False # assume failure - msg = "" - - if intf: - w = intf.waitWindow(_("Logging in to iSCSI node"), - _("Logging in to iSCSI node %s") % node.name) - try: - authinfo = None - if username or password or r_username or r_password: - # may raise a ValueError - authinfo = libiscsi.chapAuthInfo(username=username, - password=password, - reverse_username=r_username, - reverse_password=r_password) - node.setAuth(authinfo) - node.login() - rc = True - log.info("iSCSI: logged into %s at %s:%s through %s" % ( - node.name, node.address, node.port, node.iface)) - if not self._mark_node_active(node): - log.error("iSCSI: node not found among discovered") - except (IOError, ValueError) as e: - msg = str(e) - log.warning("iSCSI: could not log into %s: %s" % (node.name, msg)) - if intf: - w.pop() - - return (rc, msg) - - # NOTE: the same credentials are used for discovery and login - # (unlike in UI) - def addTarget(self, ipaddr, port="3260", user=None, pw=None, - user_in=None, pw_in=None, intf=None, target=None, iface=None): - found = 0 - logged_in = 0 - - found_nodes = self.discover(ipaddr, port, user, pw, user_in, pw_in, - intf) - if found_nodes == None: - raise IOError, _("No iSCSI nodes discovered") - - for node in found_nodes: - if target and target != node.name: - log.debug("iscsi: skipping logging to iscsi node '%s'" % - node.name) - continue - if iface: - node_net_iface = self.ifaces.get(node.iface, node.iface) - if iface != node_net_iface: - log.debug("iscsi: skipping logging to iscsi node '%s' via %s" % - (node.name, node_net_iface)) - continue - - found = found + 1 - - (rc, msg) = self.log_into_node(node, user, pw, user_in, pw_in, - intf) - if rc: - logged_in = logged_in +1 - - if found == 0: - raise IOError, _("No new iSCSI nodes discovered") - - if logged_in == 0: - raise IOError, _("Could not log in to any of the discovered nodes") - - self.stabilize(intf) - - def write(self, storage): - if not self.initiatorSet: - return - - # set iscsi nodes to autostart - root = storage.rootDevice - for node in self.active_nodes(): - autostart = True - disks = self.getNodeDisks(node, storage) - for disk in disks: - # nodes used for root get started by the initrd - if root.dependsOn(disk): - autostart = False - - if autostart: - node.setParameter("node.startup", "automatic") - - if not os.path.isdir(ROOT_PATH + "/etc/iscsi"): - os.makedirs(ROOT_PATH + "/etc/iscsi", 0755) - fd = os.open(ROOT_PATH + INITIATOR_FILE, os.O_RDWR | os.O_CREAT) - os.write(fd, "InitiatorName=%s\n" %(self.initiator)) - os.close(fd) - - # copy "db" files. *sigh* - if os.path.isdir(ROOT_PATH + "/var/lib/iscsi"): - shutil.rmtree(ROOT_PATH + "/var/lib/iscsi") - if os.path.isdir("/var/lib/iscsi"): - shutil.copytree("/var/lib/iscsi", ROOT_PATH + "/var/lib/iscsi", - symlinks=True) - - def getNode(self, name, address, port, iface): - for node in self.active_nodes(): - if node.name == name and node.address == address and \ - node.port == int(port) and node.iface == iface: - return node - - return None - - def getNodeDisks(self, node, storage): - nodeDisks = [] - iscsiDisks = storage.devicetree.getDevicesByType("iscsi") - for disk in iscsiDisks: - if disk.node == node: - nodeDisks.append(disk) - - return nodeDisks - -# Create iscsi singleton -iscsi = iscsi() - -# vim:tw=78:ts=4:et:sw=4 diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py deleted file mode 100644 index 243c62b80..000000000 --- a/pyanaconda/storage/partitioning.py +++ /dev/null @@ -1,1947 +0,0 @@ -# partitioning.py -# Disk partitioning functions. -# -# Copyright (C) 2009 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> -# - -import sys -import os -from operator import add, sub, gt, lt - -import parted -from pykickstart.constants import * - -from errors import * -from deviceaction import * -from devices import PartitionDevice, LUKSDevice, devicePathToName -from formats import getFormat - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -def _getCandidateDisks(storage): - """ Return a list of disks with space for a default-sized partition. """ - disks = [] - for disk in storage.partitioned: - if storage.config.clearPartDisks and \ - (disk.name not in storage.config.clearPartDisks): - continue - - part = disk.format.firstPartition - while part: - if not part.type & parted.PARTITION_FREESPACE: - part = part.nextPartition() - continue - - if part.getSize(unit="MB") > PartitionDevice.defaultSize: - disks.append(disk) - break - - part = part.nextPartition() - - return disks - -def _scheduleImplicitPartitions(storage, disks): - """ Schedule creation of a lvm/btrfs partition on each disk in disks. """ - # create a separate pv or btrfs partition for each disk with free space - devs = [] - - # only schedule the partitions if either lvm or btrfs autopart was chosen - if storage.autoPartType not in (AUTOPART_TYPE_LVM, AUTOPART_TYPE_BTRFS): - return devs - - for disk in disks: - if storage.encryptedAutoPart: - fmt_type = "luks" - fmt_args = {"passphrase": storage.encryptionPassphrase, - "cipher": storage.encryptionCipher, - "escrow_cert": storage.autoPartEscrowCert, - "add_backup_passphrase": storage.autoPartAddBackupPassphrase} - else: - if storage.autoPartType == AUTOPART_TYPE_LVM: - fmt_type = "lvmpv" - else: - fmt_type = "btrfs" - fmt_args = {} - part = storage.newPartition(fmt_type=fmt_type, - fmt_args=fmt_args, - grow=True, - parents=[disk]) - storage.createDevice(part) - devs.append(part) - - return devs - -def _schedulePartitions(storage, disks): - """ Schedule creation of autopart partitions. """ - # basis for requests with requiredSpace is the sum of the sizes of the - # two largest free regions - all_free = getFreeRegions(disks) - all_free.sort(key=lambda f: f.length, reverse=True) - if not all_free: - # this should never happen since we've already filtered the disks - # to those with at least 500MB free - log.error("no free space on disks %s" % ([d.name for d in disks],)) - return - - free = all_free[0].getSize() - if len(all_free) > 1: - free += all_free[1].getSize() - - # The boot disk must be set at this point. See if any platform-specific - # stage1 device we might allocate already exists on the boot disk. - stage1_device = None - for device in storage.devices: - if storage.bootloader.stage1_disk not in device.disks: - continue - - if storage.bootloader.is_valid_stage1_device(device): - stage1_device = device - break - - # - # First pass is for partitions only. We'll do LVs later. - # - for request in storage.autoPartitionRequests: - if (request.lv and storage.autoPartType == AUTOPART_TYPE_LVM) or \ - (request.btr and storage.autoPartType == AUTOPART_TYPE_BTRFS): - continue - - if request.requiredSpace and request.requiredSpace > free: - continue - - elif request.fstype in ("prepboot", "efi", "hfs+") and \ - (storage.bootloader.skip_bootloader or stage1_device): - # there should never be a need for more than one of these - # partitions, so skip them. - log.info("skipping unneeded stage1 %s request" % request.fstype) - log.debug(request) - - if request.fstype == "efi": - # Set the mountpoint for the existing EFI boot partition - stage1_device.format.mountpoint = "/boot/efi" - - log.debug(stage1_device) - continue - elif request.fstype == "biosboot": - is_gpt = (stage1_device and - getattr(stage1_device.format, "labelType", None) == "gpt") - has_bios_boot = (stage1_device and - any([p.format.type == "biosboot" - for p in storage.partitions - if p.disk == stage1_device])) - if (storage.bootloader.skip_bootloader or - not (stage1_device and stage1_device.isDisk and - is_gpt and not has_bios_boot)): - # there should never be a need for more than one of these - # partitions, so skip them. - log.info("skipping unneeded stage1 %s request" % request.fstype) - log.debug(request) - log.debug(stage1_device) - continue - - if request.encrypted and storage.encryptedAutoPart: - fmt_type = "luks" - fmt_args = {"passphrase": storage.encryptionPassphrase, - "cipher": storage.encryptionCipher, - "escrow_cert": storage.autoPartEscrowCert, - "add_backup_passphrase": storage.autoPartAddBackupPassphrase} - else: - fmt_type = request.fstype - fmt_args = {} - - dev = storage.newPartition(fmt_type=fmt_type, - fmt_args=fmt_args, - size=request.size, - grow=request.grow, - maxsize=request.maxSize, - mountpoint=request.mountpoint, - parents=disks, - weight=request.weight) - - # schedule the device for creation - storage.createDevice(dev) - - if request.encrypted and storage.encryptedAutoPart: - luks_fmt = getFormat(request.fstype, - device=dev.path, - mountpoint=request.mountpoint) - luks_dev = LUKSDevice("luks-%s" % dev.name, - format=luks_fmt, - size=dev.size, - parents=dev) - storage.createDevice(luks_dev) - - # make sure preexisting broken lvm/raid configs get out of the way - return - -def _scheduleVolumes(storage, devs): - """ Schedule creation of autopart lvm/btrfs volumes. """ - if not devs: - return - - if storage.autoPartType == AUTOPART_TYPE_LVM: - new_container = storage.newVG - new_volume = storage.newLV - format_name = "lvmpv" - else: - new_container = storage.newBTRFS - new_volume = storage.newBTRFS - format_name = "btrfs" - - if storage.encryptedAutoPart: - pvs = [] - for dev in devs: - pv = LUKSDevice("luks-%s" % dev.name, - format=getFormat(format_name, device=dev.path), - size=dev.size, - parents=dev) - pvs.append(pv) - storage.createDevice(pv) - else: - pvs = devs - - # create a vg containing all of the autopart pvs - container = new_container(parents=pvs) - storage.createDevice(container) - - # - # Convert storage.autoPartitionRequests into Device instances and - # schedule them for creation. - # - # Second pass, for LVs only. - for request in storage.autoPartitionRequests: - btr = storage.autoPartType == AUTOPART_TYPE_BTRFS and request.btr - lv = storage.autoPartType == AUTOPART_TYPE_LVM and request.lv - - if not btr and not lv: - continue - - # required space isn't relevant on btrfs - if lv and \ - request.requiredSpace and request.requiredSpace > container.size: - continue - - if request.fstype is None: - if btr: - # btrfs volumes can only contain btrfs filesystems - request.fstype = "btrfs" - else: - request.fstype = storage.defaultFSType - - kwargs = {"mountpoint": request.mountpoint, - "fmt_type": request.fstype} - if lv: - kwargs.update({"parents": [container], - "grow": request.grow, - "maxsize": request.maxSize, - "size": request.size, - "singlePV": request.singlePV}) - else: - kwargs.update({"parents": [container], - "size": request.size, - "subvol": True}) - - dev = new_volume(**kwargs) - - # schedule the device for creation - storage.createDevice(dev) - -def doAutoPartition(storage, data): - log.debug("doAutoPart: %s" % storage.doAutoPart) - log.debug("encryptedAutoPart: %s" % storage.encryptedAutoPart) - log.debug("autoPartType: %s" % storage.autoPartType) - log.debug("clearPartType: %s" % storage.config.clearPartType) - log.debug("clearPartDisks: %s" % storage.config.clearPartDisks) - log.debug("autoPartitionRequests:\n%s" % "".join([str(p) for p in storage.autoPartitionRequests])) - log.debug("storage.disks: %s" % [d.name for d in storage.disks]) - log.debug("storage.partitioned: %s" % [d.name for d in storage.partitioned]) - log.debug("all names: %s" % [d.name for d in storage.devices]) - log.debug("boot disk: %s" % getattr(storage.bootDisk, "name", None)) - - disks = [] - devs = [] - - if not storage.doAutoPart: - return - - if not storage.partitioned: - raise NoDisksError(_("No usable disks selected")) - - disks = _getCandidateDisks(storage) - devs = _scheduleImplicitPartitions(storage, disks) - log.debug("candidate disks: %s" % disks) - log.debug("devs: %s" % devs) - - if disks == []: - raise NotEnoughFreeSpaceError(_("Not enough free space on disks for " - "automatic partitioning")) - - _schedulePartitions(storage, disks) - - # run the autopart function to allocate and grow partitions - doPartitioning(storage) - _scheduleVolumes(storage, devs) - - # grow LVs - growLVM(storage) - - storage.setUpBootLoader() - - # now do a full check of the requests - (errors, warnings) = storage.sanityCheck() - for error in errors: - log.error(error) - for warning in warnings: - log.warning(warning) - if errors: - raise PartitioningError("\n".join(errors)) - -def partitionCompare(part1, part2): - """ More specifically defined partitions come first. - - < 1 => x < y - 0 => x == y - > 1 => x > y - """ - ret = 0 - - if part1.req_base_weight: - ret -= part1.req_base_weight - - if part2.req_base_weight: - ret += part2.req_base_weight - - # more specific disk specs to the front of the list - # req_disks being empty is equivalent to it being an infinitely long list - if part1.req_disks and not part2.req_disks: - ret -= 500 - elif not part1.req_disks and part2.req_disks: - ret += 500 - else: - ret += cmp(len(part1.req_disks), len(part2.req_disks)) * 500 - - # primary-only to the front of the list - ret -= cmp(part1.req_primary, part2.req_primary) * 200 - - # fixed size requests to the front - ret += cmp(part1.req_grow, part2.req_grow) * 100 - - # larger requests go to the front of the list - ret -= cmp(part1.req_base_size, part2.req_base_size) * 50 - - # potentially larger growable requests go to the front - if part1.req_grow and part2.req_grow: - if not part1.req_max_size and part2.req_max_size: - ret -= 25 - elif part1.req_max_size and not part2.req_max_size: - ret += 25 - else: - ret -= cmp(part1.req_max_size, part2.req_max_size) * 25 - - # give a little bump based on mountpoint - if hasattr(part1.format, "mountpoint") and \ - hasattr(part2.format, "mountpoint"): - ret += cmp(part1.format.mountpoint, part2.format.mountpoint) * 10 - - if ret > 0: - ret = 1 - elif ret < 0: - ret = -1 - - return ret - -def getNextPartitionType(disk, no_primary=None): - """ Find the type of partition to create next on a disk. - - Return a parted partition type value representing the type of the - next partition we will create on this disk. - - If there is only one free primary partition and we can create an - extended partition, we do that. - - If there are free primary slots and an extended partition we will - recommend creating a primary partition. This can be overridden - with the keyword argument no_primary. - - Arguments: - - disk -- a parted.Disk instance representing the disk - - Keyword arguments: - - no_primary -- given a choice between primary and logical - partitions, prefer logical - - """ - part_type = None - extended = disk.getExtendedPartition() - supports_extended = disk.supportsFeature(parted.DISK_TYPE_EXTENDED) - logical_count = len(disk.getLogicalPartitions()) - max_logicals = disk.getMaxLogicalPartitions() - primary_count = disk.primaryPartitionCount - - if primary_count < disk.maxPrimaryPartitionCount: - if primary_count == disk.maxPrimaryPartitionCount - 1: - # can we make an extended partition? now's our chance. - if not extended and supports_extended: - part_type = parted.PARTITION_EXTENDED - elif not extended: - # extended partitions not supported. primary or nothing. - if not no_primary: - part_type = parted.PARTITION_NORMAL - else: - # there is an extended and a free primary - if not no_primary: - part_type = parted.PARTITION_NORMAL - elif logical_count < max_logicals: - # we have an extended with logical slots, so use one. - part_type = parted.PARTITION_LOGICAL - else: - # there are two or more primary slots left. use one unless we're - # not supposed to make primaries. - if not no_primary: - part_type = parted.PARTITION_NORMAL - elif extended and logical_count < max_logicals: - part_type = parted.PARTITION_LOGICAL - elif extended and logical_count < max_logicals: - part_type = parted.PARTITION_LOGICAL - - return part_type - -def getBestFreeSpaceRegion(disk, part_type, req_size, - boot=None, best_free=None, grow=None): - """ Return the "best" free region on the specified disk. - - For non-boot partitions, we return the largest free region on the - disk. For boot partitions, we return the first region that is - large enough to hold the partition. - - Partition type (parted's PARTITION_NORMAL, PARTITION_LOGICAL) is - taken into account when locating a suitable free region. - - For locating the best region from among several disks, the keyword - argument best_free allows the specification of a current "best" - free region with which to compare the best from this disk. The - overall best region is returned. - - Arguments: - - disk -- the disk (a parted.Disk instance) - part_type -- the type of partition we want to allocate - (one of parted's partition type constants) - req_size -- the requested size of the partition (in MB) - - Keyword arguments: - - boot -- indicates whether this will be a bootable partition - (boolean) - best_free -- current best free region for this partition - grow -- indicates whether this is a growable request - - """ - log.debug("getBestFreeSpaceRegion: disk=%s part_type=%d req_size=%dMB " - "boot=%s best=%s grow=%s" % - (disk.device.path, part_type, req_size, boot, best_free, grow)) - extended = disk.getExtendedPartition() - - for _range in disk.getFreeSpaceRegions(): - if extended: - # find out if there is any overlap between this region and the - # extended partition - log.debug("looking for intersection between extended (%d-%d) and free (%d-%d)" % - (extended.geometry.start, extended.geometry.end, _range.start, _range.end)) - - # parted.Geometry.overlapsWith can handle this - try: - free_geom = extended.geometry.intersect(_range) - except ArithmeticError: - # this freespace region does not lie within the extended - # partition's geometry - free_geom = None - - if (free_geom and part_type == parted.PARTITION_NORMAL) or \ - (not free_geom and part_type == parted.PARTITION_LOGICAL): - log.debug("free region not suitable for request") - continue - - if part_type == parted.PARTITION_NORMAL: - # we're allocating a primary and the region is not within - # the extended, so we use the original region - free_geom = _range - else: - free_geom = _range - - if free_geom.start > disk.maxPartitionStartSector: - log.debug("free range start sector beyond max for new partitions") - continue - - if boot: - free_start_mb = sectorsToSize(free_geom.start, - disk.device.sectorSize) - req_end_mb = free_start_mb + req_size - if req_end_mb > 2*1024*1024: - log.debug("free range position would place boot req above 2TB") - continue - - log.debug("current free range is %d-%d (%dMB)" % (free_geom.start, - free_geom.end, - free_geom.getSize())) - free_size = free_geom.getSize() - - # For boot partitions, we want the first suitable region we find. - # For growable or extended partitions, we want the largest possible - # free region. - # For all others, we want the smallest suitable free region. - if grow or part_type == parted.PARTITION_EXTENDED: - op = gt - else: - op = lt - if req_size <= free_size: - if not best_free or op(free_geom.length, best_free.length): - best_free = free_geom - - if boot: - # if this is a bootable partition we want to - # use the first freespace region large enough - # to satisfy the request - break - - return best_free - -def sectorsToSize(sectors, sectorSize): - """ Convert length in sectors to size in MB. - - Arguments: - - sectors - sector count - sectorSize - sector size for the device, in bytes - """ - return (sectors * sectorSize) / (1024.0 * 1024.0) - -def sizeToSectors(size, sectorSize): - """ Convert size in MB to length in sectors. - - Arguments: - - size - size in MB - sectorSize - sector size for the device, in bytes - """ - return (size * 1024.0 * 1024.0) / sectorSize - -def removeNewPartitions(disks, partitions): - """ Remove newly added input partitions from input disks. - - Arguments: - - disks -- list of StorageDevice instances with DiskLabel format - partitions -- list of PartitionDevice instances - - """ - log.debug("removing all non-preexisting partitions %s from disk(s) %s" - % (["%s(id %d)" % (p.name, p.id) for p in partitions - if not p.exists], - [d.name for d in disks])) - for part in partitions: - if part.partedPartition and part.disk in disks: - if part.exists: - # we're only removing partitions that don't physically exist - continue - - if part.isExtended: - # these get removed last - continue - - part.disk.format.partedDisk.removePartition(part.partedPartition) - part.partedPartition = None - part.disk = None - - for disk in disks: - # remove empty extended so it doesn't interfere - extended = disk.format.extendedPartition - if extended and not disk.format.logicalPartitions: - log.debug("removing empty extended partition from %s" % disk.name) - disk.format.partedDisk.removePartition(extended) - -def addPartition(disklabel, free, part_type, size): - """ Return new partition after adding it to the specified disk. - - Arguments: - - disklabel -- disklabel instance to add partition to - free -- where to add the partition (parted.Geometry instance) - part_type -- partition type (parted.PARTITION_* constant) - size -- size (in MB) of the new partition - - The new partition will be aligned. - - Return value is a parted.Partition instance. - - """ - start = free.start - if not disklabel.alignment.isAligned(free, start): - start = disklabel.alignment.alignNearest(free, start) - - if disklabel.labelType == "sun" and start == 0: - start = disklabel.alignment.alignUp(free, start) - - if part_type == parted.PARTITION_LOGICAL: - # make room for logical partition's metadata - start += disklabel.alignment.grainSize - - if start != free.start: - log.debug("adjusted start sector from %d to %d" % (free.start, start)) - - if part_type == parted.PARTITION_EXTENDED: - end = free.end - length = end - start + 1 - else: - # size is in MB - length = sizeToSectors(size, disklabel.partedDevice.sectorSize) - end = start + length - 1 - - if not disklabel.endAlignment.isAligned(free, end): - end = disklabel.endAlignment.alignNearest(free, end) - log.debug("adjusted length from %d to %d" % (length, end - start + 1)) - if start > end: - raise PartitioningError(_("unable to allocate aligned partition")) - - new_geom = parted.Geometry(device=disklabel.partedDevice, - start=start, - end=end) - - max_length = disklabel.partedDisk.maxPartitionLength - if max_length and new_geom.length > max_length: - raise PartitioningError(_("requested size exceeds maximum allowed")) - - # create the partition and add it to the disk - partition = parted.Partition(disk=disklabel.partedDisk, - type=part_type, - geometry=new_geom) - constraint = parted.Constraint(exactGeom=new_geom) - disklabel.partedDisk.addPartition(partition=partition, - constraint=constraint) - return partition - -def getFreeRegions(disks): - """ Return a list of free regions on the specified disks. - - Arguments: - - disks -- list of parted.Disk instances - - Return value is a list of unaligned parted.Geometry instances. - - """ - free = [] - for disk in disks: - for f in disk.format.partedDisk.getFreeSpaceRegions(): - if f.length > 0: - free.append(f) - - return free - -def updateExtendedPartitions(storage, disks): - # XXX hack -- if we created any extended partitions we need to add - # them to the tree now - for disk in disks: - extended = disk.format.extendedPartition - if not extended: - # remove any obsolete extended partitions - for part in storage.partitions: - if part.disk == disk and part.isExtended: - if part.exists: - storage.destroyDevice(part) - else: - storage.devicetree._removeDevice(part, moddisk=False) - continue - - extendedName = devicePathToName(extended.getDeviceNodeName()) - # remove any obsolete extended partitions - for part in storage.partitions: - if part.disk == disk and part.isExtended and \ - part.partedPartition not in disk.format.partitions: - if part.exists: - storage.destroyDevice(part) - else: - storage.devicetree._removeDevice(part, moddisk=False) - - device = storage.devicetree.getDeviceByName(extendedName) - if device: - if not device.exists: - # created by us, update partedPartition - device.partedPartition = extended - continue - - # This is a little odd because normally instantiating a partition - # that does not exist means leaving self.parents empty and instead - # populating self.req_disks. In this case, we need to skip past - # that since this partition is already defined. - device = PartitionDevice(extendedName, parents=disk) - device.parents = [disk] - device.partedPartition = extended - # just add the device for now -- we'll handle actions at the last - # moment to simplify things - storage.devicetree._addDevice(device) - -def doPartitioning(storage): - """ Allocate and grow partitions. - - When this function returns without error, all PartitionDevice - instances must have their parents set to the disk they are - allocated on, and their partedPartition attribute set to the - appropriate parted.Partition instance from their containing - disk. All req_xxxx attributes must be unchanged. - - Arguments: - - storage - Main anaconda Storage instance - - Keyword/Optional Arguments: - - None - - """ - disks = storage.partitioned - if storage.config.exclusiveDisks: - disks = [d for d in disks if d.name in storage.config.exclusiveDisks] - - for disk in disks: - try: - disk.setup() - except DeviceError as (msg, name): - log.error("failed to set up disk %s: %s" % (name, msg)) - raise PartitioningError(_("disk %s inaccessible") % disk.name) - - partitions = storage.partitions[:] - for part in storage.partitions: - part.req_bootable = False - - if part.exists: - # if the partition is preexisting or part of a complex device - # then we shouldn't modify it - partitions.remove(part) - continue - - if not part.exists: - # start over with flexible-size requests - part.req_size = part.req_base_size - - try: - storage.bootDevice.req_bootable = True - except AttributeError: - # there's no stage2 device. hopefully it's temporary. - pass - - removeNewPartitions(disks, partitions) - free = getFreeRegions(disks) - try: - allocatePartitions(storage, disks, partitions, free) - growPartitions(disks, partitions, free, size_sets=storage.size_sets) - except Exception: - raise - else: - # Mark all growable requests as no longer growable. - for partition in storage.partitions: - log.debug("fixing size of %s at %.2f" % (partition, partition.size)) - partition.req_grow = False - partition.req_base_size = partition.size - partition.req_size = partition.size - finally: - # these are only valid for one allocation run - storage.size_sets = [] - - # The number and thus the name of partitions may have changed now, - # allocatePartitions() takes care of this for new partitions, but not - # for pre-existing ones, so we update the name of all partitions here - for part in storage.partitions: - # leave extended partitions as-is -- we'll handle them separately - if part.isExtended: - continue - part.updateName() - - updateExtendedPartitions(storage, disks) - - for part in [p for p in storage.partitions if not p.exists]: - problem = part.checkSize() - if problem < 0: - raise PartitioningError(_("partition is too small for %(format)s formatting " - "(allowable size is %(minSize)d MB to %(maxSize)d MB)") - % {"format": part.format.name, "minSize": part.format.minSize, - "maxSize": part.format.maxSize}) - elif problem > 0: - raise PartitioningError(_("partition is too large for %(format)s formatting " - "(allowable size is %(minSize)d MB to %(maxSize)d MB)") - % {"format": part.format.name, "minSize": part.format.minSize, - "maxSize": part.format.maxSize}) - -def allocatePartitions(storage, disks, partitions, freespace): - """ Allocate partitions based on requested features. - - Non-existing partitions are sorted according to their requested - attributes, and then allocated. - - The basic approach to sorting is that the more specifically- - defined a request is, the earlier it will be allocated. See - the function partitionCompare for details on the sorting - criteria. - - The PartitionDevice instances will have their name and parents - attributes set once they have been allocated. - """ - log.debug("allocatePartitions: disks=%s ; partitions=%s" % - ([d.name for d in disks], - ["%s(id %d)" % (p.name, p.id) for p in partitions])) - - new_partitions = [p for p in partitions if not p.exists] - new_partitions.sort(cmp=partitionCompare) - - # the following dicts all use device path strings as keys - disklabels = {} # DiskLabel instances for each disk - all_disks = {} # StorageDevice for each disk - for disk in disks: - if disk.path not in disklabels.keys(): - disklabels[disk.path] = disk.format - all_disks[disk.path] = disk - - removeNewPartitions(disks, new_partitions) - - for _part in new_partitions: - if _part.partedPartition and _part.isExtended: - # ignore new extendeds as they are implicit requests - continue - - # obtain the set of candidate disks - req_disks = [] - if _part.req_disks: - # use the requested disk set - req_disks = _part.req_disks - else: - # no disks specified means any disk will do - req_disks = disks - - # sort the disks, making sure the boot disk is first - req_disks.sort(key=lambda d: d.name, cmp=storage.compareDisks) - for disk in req_disks: - if storage.bootDisk and disk == storage.bootDisk: - boot_index = req_disks.index(disk) - req_disks.insert(0, req_disks.pop(boot_index)) - - boot = _part.req_base_weight > 1000 - - log.debug("allocating partition: %s ; id: %d ; disks: %s ;\n" - "boot: %s ; primary: %s ; size: %dMB ; grow: %s ; " - "max_size: %s" % (_part.name, _part.id, - [d.name for d in req_disks], - boot, _part.req_primary, - _part.req_size, _part.req_grow, - _part.req_max_size)) - free = None - use_disk = None - part_type = None - growth = 0 - # loop through disks - for _disk in req_disks: - disklabel = disklabels[_disk.path] - sectorSize = disklabel.partedDevice.sectorSize - best = None - current_free = free - - # for growable requests, we don't want to pass the current free - # geometry to getBestFreeRegion -- this allows us to try the - # best region from each disk and choose one based on the total - # growth it allows - if _part.req_grow: - current_free = None - - log.debug("checking freespace on %s" % _disk.name) - - new_part_type = getNextPartitionType(disklabel.partedDisk) - if new_part_type is None: - # can't allocate any more partitions on this disk - log.debug("no free partition slots on %s" % _disk.name) - continue - - if _part.req_primary and new_part_type != parted.PARTITION_NORMAL: - if (disklabel.partedDisk.primaryPartitionCount < - disklabel.partedDisk.maxPrimaryPartitionCount): - # don't fail to create a primary if there are only three - # primary partitions on the disk (#505269) - new_part_type = parted.PARTITION_NORMAL - else: - # we need a primary slot and none are free on this disk - log.debug("no primary slots available on %s" % _disk.name) - continue - - best = getBestFreeSpaceRegion(disklabel.partedDisk, - new_part_type, - _part.req_size, - best_free=current_free, - boot=boot, - grow=_part.req_grow) - - if best == free and not _part.req_primary and \ - new_part_type == parted.PARTITION_NORMAL: - # see if we can do better with a logical partition - log.debug("not enough free space for primary -- trying logical") - new_part_type = getNextPartitionType(disklabel.partedDisk, - no_primary=True) - if new_part_type: - best = getBestFreeSpaceRegion(disklabel.partedDisk, - new_part_type, - _part.req_size, - best_free=current_free, - boot=boot, - grow=_part.req_grow) - - if best and free != best: - update = True - allocated = new_partitions[:new_partitions.index(_part)+1] - if any([p.req_grow for p in allocated]): - log.debug("evaluating growth potential for new layout") - new_growth = 0 - for disk_path in disklabels.keys(): - log.debug("calculating growth for disk %s" % disk_path) - # Now we check, for growable requests, which of the two - # free regions will allow for more growth. - - # set up chunks representing the disks' layouts - temp_parts = [] - for _p in new_partitions[:new_partitions.index(_part)]: - if _p.disk.path == disk_path: - temp_parts.append(_p) - - # add the current request to the temp disk to set up - # its partedPartition attribute with a base geometry - if disk_path == _disk.path: - _part_type = new_part_type - _free = best - if new_part_type == parted.PARTITION_EXTENDED: - addPartition(disklabel, best, new_part_type, - None) - - _part_type = parted.PARTITION_LOGICAL - - _free = getBestFreeSpaceRegion(disklabel.partedDisk, - _part_type, - _part.req_size, - boot=boot, - grow=_part.req_grow) - if not _free: - log.info("not enough space after adding " - "extended partition for growth test") - if new_part_type == parted.PARTITION_EXTENDED: - e = disklabel.extendedPartition - disklabel.partedDisk.removePartition(e) - - continue - - temp_part = addPartition(disklabel, - _free, - _part_type, - _part.req_size) - _part.partedPartition = temp_part - _part.disk = _disk - temp_parts.append(_part) - - chunks = getDiskChunks(all_disks[disk_path], - temp_parts, freespace) - - # grow all growable requests - disk_growth = 0 - disk_sector_size = disklabels[disk_path].partedDevice.sectorSize - for chunk in chunks: - chunk.growRequests() - # record the growth for this layout - new_growth += chunk.growth - disk_growth += chunk.growth - for req in chunk.requests: - log.debug("request %d (%s) growth: %d (%dMB) " - "size: %dMB" % - (req.device.id, - req.device.name, - req.growth, - sectorsToSize(req.growth, - disk_sector_size), - sectorsToSize(req.growth + req.base, - disk_sector_size))) - log.debug("disk %s growth: %d (%dMB)" % - (disk_path, disk_growth, - sectorsToSize(disk_growth, - disk_sector_size))) - - disklabel.partedDisk.removePartition(temp_part) - _part.partedPartition = None - _part.disk = None - - if new_part_type == parted.PARTITION_EXTENDED: - e = disklabel.extendedPartition - disklabel.partedDisk.removePartition(e) - - log.debug("total growth: %d sectors" % new_growth) - - # update the chosen free region unless the previous - # choice yielded greater total growth - if free is not None and new_growth <= growth: - log.debug("keeping old free: %d <= %d" % (new_growth, - growth)) - update = False - else: - growth = new_growth - - if update: - # now we know we are choosing a new free space, - # so update the disk and part type - log.debug("updating use_disk to %s, type: %s" - % (_disk.name, new_part_type)) - part_type = new_part_type - use_disk = _disk - log.debug("new free: %d-%d / %dMB" % (best.start, - best.end, - best.getSize())) - log.debug("new free allows for %d sectors of growth" % - growth) - free = best - - if free and boot: - # if this is a bootable partition we want to - # use the first freespace region large enough - # to satisfy the request - log.debug("found free space for bootable request") - break - - if free is None: - raise PartitioningError(_("not enough free space on disks")) - - _disk = use_disk - disklabel = _disk.format - - # create the extended partition if needed - if part_type == parted.PARTITION_EXTENDED: - log.debug("creating extended partition") - addPartition(disklabel, free, part_type, None) - - # now the extended partition exists, so set type to logical - part_type = parted.PARTITION_LOGICAL - - # recalculate freespace - log.debug("recalculating free space") - free = getBestFreeSpaceRegion(disklabel.partedDisk, - part_type, - _part.req_size, - boot=boot, - grow=_part.req_grow) - if not free: - raise PartitioningError(_("not enough free space after " - "creating extended partition")) - - partition = addPartition(disklabel, free, part_type, _part.req_size) - log.debug("created partition %s of %dMB and added it to %s" % - (partition.getDeviceNodeName(), partition.getSize(), - disklabel.device)) - - # this one sets the name - _part.partedPartition = partition - _part.disk = _disk - - # parted modifies the partition in the process of adding it to - # the disk, so we need to grab the latest version... - _part.partedPartition = disklabel.partedDisk.getPartitionByPath(_part.path) - - -class Request(object): - """ A partition request. - - Request instances are used for calculating how much to grow - partitions. - """ - def __init__(self, device): - """ Create a Request instance. - - Arguments: - - """ - self.device = device - self.growth = 0 # growth in sectors - self.max_growth = 0 # max growth in sectors - self.done = not getattr(device, "req_grow", True) # can we grow this - # request more? - self.base = 0 # base sectors - - @property - def growable(self): - """ True if this request is growable. """ - return getattr(self.device, "req_grow", True) - - @property - def id(self): - """ The id of the Device instance this request corresponds to. """ - return self.device.id - - def __repr__(self): - s = ("%(type)s instance --\n" - "id = %(id)s name = %(name)s growable = %(growable)s\n" - "base = %(base)d growth = %(growth)d max_grow = %(max_grow)d\n" - "done = %(done)s" % - {"type": self.__class__.__name__, "id": self.id, - "name": self.device.name, "growable": self.growable, - "base": self.base, "growth": self.growth, - "max_grow": self.max_growth, "done": self.done}) - return s - - -class PartitionRequest(Request): - def __init__(self, partition): - """ Create a PartitionRequest instance. - - Arguments: - - partition -- a PartitionDevice instance - - """ - super(PartitionRequest, self).__init__(partition) - self.base = partition.partedPartition.geometry.length # base sectors - - sector_size = partition.partedPartition.disk.device.sectorSize - - if partition.req_grow: - limits = filter(lambda l: l > 0, - [sizeToSectors(partition.req_max_size, sector_size), - sizeToSectors(partition.format.maxSize, sector_size), - partition.partedPartition.disk.maxPartitionLength]) - - if limits: - max_sectors = min(limits) - self.max_growth = max_sectors - self.base - if self.max_growth <= 0: - # max size is less than or equal to base, so we're done - self.done = True - - -class LVRequest(Request): - def __init__(self, lv): - """ Create a LVRequest instance. - - Arguments: - - lv -- an LVMLogicalVolumeDevice instance - - """ - super(LVRequest, self).__init__(lv) - - # Round up to nearest pe. For growable requests this will mean that - # first growth is to fill the remainder of any unused extent. - self.base = lv.vg.align(lv.req_size, roundup=True) / lv.vg.peSize # pe - - if lv.req_grow: - limits = [l / lv.vg.peSize for l in - [lv.vg.align(lv.req_max_size), - lv.vg.align(lv.format.maxSize)] if l > 0] - - if limits: - max_units = min(limits) - self.max_growth = max_units - self.base - if self.max_growth <= 0: - # max size is less than or equal to base, so we're done - self.done = True - - -class Chunk(object): - """ A free region from which devices will be allocated """ - def __init__(self, length, requests=None): - """ Create a Chunk instance. - - Arguments: - - length -- the length of the chunk in allocation units - - - Keyword Arguments: - - requests -- list of Request instances allocated from this chunk - - """ - if not hasattr(self, "path"): - self.path = None - self.length = length - self.pool = length # free unit count - self.base = 0 # sum of growable requests' base - # sizes - self.requests = [] # list of Request instances - if isinstance(requests, list): - for req in requests: - self.addRequest(req) - - self.skip_list = [] - - def __repr__(self): - s = ("%(type)s instance --\n" - "device = %(device)s length = %(length)d size = %(size)d\n" - "remaining = %(rem)d pool = %(pool)d" % - {"type": self.__class__.__name__, "device": self.path, - "length": self.length, "size": self.lengthToSize(self.length), - "pool": self.pool, "rem": self.remaining}) - - return s - - def __str__(self): - s = "%d on %s" % (self.length, self.path) - return s - - def addRequest(self, req): - """ Add a Request to this chunk. """ - log.debug("adding request %d to chunk %s" % (req.device.id, self)) - - self.requests.append(req) - self.pool -= req.base - - if not req.done: - self.base += req.base - - def reclaim(self, request, amount): - """ Reclaim units from a request and return them to the pool. """ - log.debug("reclaim: %s %d (%d MB)" % (request, amount, self.lengthToSize(amount))) - if request.growth < amount: - log.error("tried to reclaim %d from request with %d of growth" - % (amount, request.growth)) - raise ValueError(_("cannot reclaim more than request has grown")) - - request.growth -= amount - self.pool += amount - - # put this request in the skip list so we don't try to grow it the - # next time we call growRequests to allocate the newly re-acquired pool - if request not in self.skip_list: - self.skip_list.append(request) - - @property - def growth(self): - """ Sum of growth for all requests in this chunk. """ - return sum(r.growth for r in self.requests) - - @property - def hasGrowable(self): - """ True if this chunk contains at least one growable request. """ - for req in self.requests: - if req.growable: - return True - return False - - @property - def remaining(self): - """ Number of requests still being grown in this chunk. """ - return len([d for d in self.requests if not d.done]) - - @property - def done(self): - """ True if we are finished growing all requests in this chunk. """ - return self.remaining == 0 - - def maxGrowth(self, req): - return req.max_growth - - def lengthToSize(self, length): - return length - - def sizeToLength(self, size): - return size - - def trimOverGrownRequest(self, req, base=None): - """ Enforce max growth and return extra units to the pool. """ - max_growth = self.maxGrowth(req) - if max_growth and req.growth >= max_growth: - if req.growth > max_growth: - # we've grown beyond the maximum. put some back. - extra = req.growth - max_growth - log.debug("taking back %d (%dMB) from %d (%s)" % - (extra, self.lengthToSize(extra), - req.device.id, req.device.name)) - self.pool += extra - req.growth = max_growth - - # We're done growing this request, so it no longer - # factors into the growable base used to determine - # what fraction of the pool each request gets. - if base is not None: - base -= req.base - req.done = True - - return base - - def sortRequests(self): - pass - - def growRequests(self, uniform=False): - """ Calculate growth amounts for requests in this chunk. """ - log.debug("Chunk.growRequests: %r" % self) - - self.sortRequests() - for req in self.requests: - log.debug("req: %r" % req) - - # we use this to hold the base for the next loop through the - # chunk's requests since we want the base to be the same for - # all requests in any given growth iteration - new_base = self.base - last_pool = 0 # used to track changes to the pool across iterations - while not self.done and self.pool and last_pool != self.pool: - last_pool = self.pool # to keep from getting stuck - self.base = new_base - if uniform: - growth = last_pool / self.remaining - - log.debug("%d requests and %d (%dMB) left in chunk" % - (self.remaining, self.pool, self.lengthToSize(self.pool))) - for p in self.requests: - if p.done or p in self.skip_list: - continue - - if not uniform: - # Each request is allocated free units from the pool - # based on the relative _base_ sizes of the remaining - # growable requests. - share = p.base / float(self.base) - growth = int(share * last_pool) # truncate, don't round - - p.growth += growth - self.pool -= growth - log.debug("adding %d (%dMB) to %d (%s)" % - (growth, self.lengthToSize(growth), - p.device.id, p.device.name)) - - new_base = self.trimOverGrownRequest(p, base=new_base) - log.debug("new grow amount for request %d (%s) is %d " - "units, or %dMB" % - (p.device.id, p.device.name, p.growth, - self.lengthToSize(p.growth))) - - if self.pool: - # allocate any leftovers in pool to the first partition - # that can still grow - for p in self.requests: - if p.done: - continue - - growth = self.pool - p.growth += growth - self.pool = 0 - log.debug("adding %d (%dMB) to %d (%s)" % - (growth, self.lengthToSize(growth), - p.device.id, p.device.name)) - - self.trimOverGrownRequest(p) - log.debug("new grow amount for request %d (%s) is %d " - "units, or %dMB" % - (p.device.id, p.device.name, p.growth, - self.lengthToSize(p.growth))) - - if self.pool == 0: - break - - # requests that were skipped over this time through are back on the - # table next time - self.skip_list = [] - - -class DiskChunk(Chunk): - """ A free region on disk from which partitions will be allocated """ - def __init__(self, geometry, requests=None): - """ Create a Chunk instance. - - Arguments: - - geometry -- parted.Geometry instance describing the free space - - - Keyword Arguments: - - requests -- list of Request instances allocated from this chunk - - - Note: We will limit partition growth based on disklabel - limitations for partition end sector, so a 10TB disk with an - msdos disklabel will be treated like a 2TB disk. - - """ - self.geometry = geometry # parted.Geometry - self.sectorSize = self.geometry.device.sectorSize - self.path = self.geometry.device.path - super(DiskChunk, self).__init__(self.geometry.length, requests=requests) - - def __repr__(self): - s = super(DiskChunk, self).__str__() - s += (" start = %(start)d end = %(end)d\n" - "sectorSize = %(sectorSize)d\n" % - {"start": self.geometry.start, "end": self.geometry.end, - "sectorSize": self.sectorSize}) - return s - - def __str__(self): - s = "%d (%d-%d) on %s" % (self.length, self.geometry.start, - self.geometry.end, self.path) - return s - - def addRequest(self, req): - """ Add a Request to this chunk. """ - if not isinstance(req, PartitionRequest): - raise ValueError(_("DiskChunk requests must be of type " - "PartitionRequest")) - - if not self.requests: - # when adding the first request to the chunk, adjust the pool - # size to reflect any disklabel-specific limits on end sector - max_sector = req.device.partedPartition.disk.maxPartitionStartSector - chunk_end = min(max_sector, self.geometry.end) - if chunk_end <= self.geometry.start: - # this should clearly never be possible, but if the chunk's - # start sector is beyond the maximum allowed end sector, we - # cannot continue - log.error("chunk start sector is beyond disklabel maximum") - raise PartitioningError(_("partitions allocated outside " - "disklabel limits")) - - new_pool = chunk_end - self.geometry.start + 1 - if new_pool != self.pool: - log.debug("adjusting pool to %d based on disklabel limits" - % new_pool) - self.pool = new_pool - - super(DiskChunk, self).addRequest(req) - - def maxGrowth(self, req): - req_end = req.device.partedPartition.geometry.end - req_start = req.device.partedPartition.geometry.start - - # Establish the current total number of sectors of growth for requests - # that lie before this one within this chunk. We add the total count - # to this request's end sector to obtain the end sector for this - # request, including growth of earlier requests but not including - # growth of this request. Maximum growth values are obtained using - # this end sector and various values for maximum end sector. - growth = 0 - for request in self.requests: - if request.device.partedPartition.geometry.start < req_start: - growth += request.growth - req_end += growth - - # obtain the set of possible maximum sectors-of-growth values for this - # request and use the smallest - limits = [] - - # disklabel-specific maximum sector - max_sector = req.device.partedPartition.disk.maxPartitionStartSector - limits.append(max_sector - req_end) - - # 2TB limit on bootable partitions, regardless of disklabel - if req.device.req_bootable: - limits.append(sizeToSectors(2*1024*1024, self.sectorSize) - req_end) - - # request-specific maximum (see Request.__init__, above, for details) - if req.max_growth: - limits.append(req.max_growth) - - max_growth = min(limits) - return max_growth - - def lengthToSize(self, length): - return sectorsToSize(length, self.sectorSize) - - def sizeToLength(self, size): - return sizeToSectors(size, self.sectorSize) - - def sortRequests(self): - # sort the partitions by start sector - self.requests.sort(key=lambda r: r.device.partedPartition.geometry.start) - - -class VGChunk(Chunk): - """ A free region in an LVM VG from which LVs will be allocated """ - def __init__(self, vg, requests=None): - """ Create a VGChunk instance. - - Arguments: - - vg -- an LVMVolumeGroupDevice within which this chunk resides - - - Keyword Arguments: - - requests -- list of Request instances allocated from this chunk - - """ - self.vg = vg - self.path = vg.path - usable_extents = vg.extents - (vg.reservedSpace / vg.peSize) - super(VGChunk, self).__init__(usable_extents, requests=requests) - - def addRequest(self, req): - """ Add a Request to this chunk. """ - if not isinstance(req, LVRequest): - raise ValueError(_("VGChunk requests must be of type " - "LVRequest")) - - super(VGChunk, self).addRequest(req) - - def lengthToSize(self, length): - return length * self.vg.peSize - - def sizeToLength(self, size): - return size / self.vg.peSize - - def sortRequests(self): - # sort the partitions by start sector - self.requests.sort(key=lambda r: r.device, cmp=lvCompare) - - def growRequests(self): - self.sortRequests() - - # grow the percentage-based requests - last_pool = self.pool - for req in self.requests: - if req.done or not req.device.req_percent: - continue - - growth = int(req.device.req_percent * 0.01 * self.length)# truncate - req.growth += growth - self.pool -= growth - log.debug("adding %d (%dMB) to %d (%s)" % - (growth, self.lengthToSize(growth), - req.device.id, req.device.name)) - - new_base = self.trimOverGrownRequest(req) - log.debug("new grow amount for request %d (%s) is %d " - "units, or %dMB" % - (req.device.id, req.device.name, req.growth, - self.lengthToSize(req.growth))) - - # we're done with this request, so remove its base from the - # chunk's base - if not req.done: - self.base -= req.base - req.done = True - - super(VGChunk, self).growRequests() - - -def getDiskChunks(disk, partitions, free): - """ Return a list of Chunk instances representing a disk. - - Arguments: - - disk -- a StorageDevice with a DiskLabel format - partitions -- list of PartitionDevice instances - free -- list of parted.Geometry instances representing free space - - Partitions and free regions not on the specified disk are ignored. - - """ - # list of all new partitions on this disk - disk_parts = [p for p in partitions if p.disk == disk and not p.exists] - disk_free = [f for f in free if f.device.path == disk.path] - - - chunks = [DiskChunk(f) for f in disk_free] - - for p in disk_parts: - if p.isExtended: - # handle extended partitions specially since they are - # indeed very special - continue - - for i, f in enumerate(disk_free): - if f.contains(p.partedPartition.geometry): - chunks[i].addRequest(PartitionRequest(p)) - break - - return chunks - -class TotalSizeSet(object): - """ Set of device requests with a target combined size. - - This will be handled by growing the requests until the desired combined - size has been achieved. - """ - def __init__(self, devices, size): - self.devices = [] - for device in devices: - if isinstance(device, LUKSDevice): - partition = device.slave - else: - partition = device - - self.devices.append(partition) - - self.size = size - - self.requests = [] - - self.allocated = sum([d.req_base_size for d in self.devices]) - log.debug("set.allocated = %d" % self.allocated) - - def allocate(self, amount): - log.debug("allocating %d to TotalSizeSet with %d/%d (%d needed)" - % (amount, self.allocated, self.size, self.needed)) - self.allocated += amount - - @property - def needed(self): - return self.size - self.allocated - - def deallocate(self, amount): - log.debug("deallocating %d from TotalSizeSet with %d/%d (%d needed)" - % (amount, self.allocated, self.size, self.needed)) - self.allocated -= amount - -class SameSizeSet(object): - """ Set of device requests with a common target size. """ - def __init__(self, devices, size, grow=False, max_size=None): - self.devices = [] - for device in devices: - if isinstance(device, LUKSDevice): - partition = device.slave - else: - partition = device - - self.devices.append(partition) - - self.size = int(size / len(devices)) - self.grow = grow - self.max_size = max_size - - self.requests = [] - -def manageSizeSets(size_sets, chunks): - growth_by_request = {} - requests_by_device = {} - chunks_by_request = {} - for chunk in chunks: - for request in chunk.requests: - requests_by_device[request.device] = request - chunks_by_request[request] = chunk - growth_by_request[request] = 0 - - for i in range(2): - reclaimed = dict([(chunk, 0) for chunk in chunks]) - for ss in size_sets: - if isinstance(ss, TotalSizeSet): - # TotalSizeSet members are trimmed to achieve the requested - # total size - log.debug("set: %s %d/%d" % ([d.name for d in ss.devices], - ss.allocated, ss.size)) - - for device in ss.devices: - request = requests_by_device[device] - chunk = chunks_by_request[request] - new_growth = request.growth - growth_by_request[request] - ss.allocate(chunk.lengthToSize(new_growth)) - - # decide how much to take back from each request - # We may assume that all requests have the same base size. - # We're shooting for a roughly equal distribution by trimming - # growth from the requests that have grown the most first. - requests = sorted([requests_by_device[d] for d in ss.devices], - key=lambda r: r.growth, reverse=True) - needed = ss.needed - for request in requests: - chunk = chunks_by_request[request] - log.debug("%s" % request) - log.debug("needed: %d" % ss.needed) - - if ss.needed < 0: - # it would be good to take back some from each device - # instead of taking all from the last one(s) - extra = -chunk.sizeToLength(needed) / len(ss.devices) - if extra > request.growth and i == 0: - log.debug("not reclaiming from this request") - continue - else: - extra = min(extra, request.growth) - - reclaimed[chunk] += extra - chunk.reclaim(request, extra) - ss.deallocate(chunk.lengthToSize(extra)) - - if ss.needed <= 0: - request.done = True - - elif isinstance(ss, SameSizeSet): - # SameSizeSet members all have the same size as the smallest - # member - requests = [requests_by_device[d] for d in ss.devices] - _min_growth = min([r.growth for r in requests]) - log.debug("set: %s %d" % ([d.name for d in ss.devices], ss.size)) - log.debug("min growth is %d" % _min_growth) - for request in requests: - chunk = chunks_by_request[request] - _max_growth = chunk.sizeToLength(ss.size) - request.base - log.debug("max growth for %s is %d" % (request, _max_growth)) - min_growth = max(min(_min_growth, _max_growth), 0) - if request.growth > min_growth: - extra = request.growth - min_growth - reclaimed[chunk] += extra - chunk.reclaim(request, extra) - request.done = True - elif request.growth == min_growth: - request.done = True - - # store previous growth amounts so we know how much was allocated in - # the latest growRequests call - for request in growth_by_request.keys(): - growth_by_request[request] = request.growth - - for chunk in chunks: - if reclaimed[chunk] and not chunk.done: - chunk.growRequests() - -def growPartitions(disks, partitions, free, size_sets=None): - """ Grow all growable partition requests. - - Partitions have already been allocated from chunks of free space on - the disks. This function does not modify the ordering of partitions - or the free chunks from which they are allocated. - - Free space within a given chunk is allocated to each growable - partition allocated from that chunk in an amount corresponding to - the ratio of that partition's base size to the sum of the base sizes - of all growable partitions allocated from the chunk. - - Arguments: - - disks -- a list of all usable disks (DiskDevice instances) - partitions -- a list of all partitions (PartitionDevice instances) - free -- a list of all free regions (parted.Geometry instances) - """ - log.debug("growPartitions: disks=%s, partitions=%s" % - ([d.name for d in disks], - ["%s(id %d)" % (p.name, p.id) for p in partitions])) - all_growable = [p for p in partitions if p.req_grow] - if not all_growable: - log.debug("no growable partitions") - return - - if size_sets is None: - size_sets = [] - - log.debug("growable partitions are %s" % [p.name for p in all_growable]) - - # - # collect info about each disk and the requests it contains - # - chunks = [] - for disk in disks: - sector_size = disk.format.partedDevice.sectorSize - - # list of free space regions on this disk prior to partition allocation - disk_free = [f for f in free if f.device.path == disk.path] - if not disk_free: - log.debug("no free space on %s" % disk.name) - continue - - disk_chunks = getDiskChunks(disk, partitions, disk_free) - log.debug("disk %s has %d chunks" % (disk.name, len(disk_chunks))) - chunks.extend(disk_chunks) - - # - # grow the partitions in each chunk as a group - # - for chunk in chunks: - if not chunk.hasGrowable: - # no growable partitions in this chunk - continue - - chunk.growRequests() - - # adjust set members' growth amounts as needed - manageSizeSets(size_sets, chunks) - - for disk in disks: - log.debug("growing partitions on %s" % disk.name) - for chunk in chunks: - if chunk.path != disk.path: - continue - - if not chunk.hasGrowable: - # no growable partitions in this chunk - continue - - # recalculate partition geometries - disklabel = disk.format - start = chunk.geometry.start - - # find any extended partition on this disk - extended_geometry = getattr(disklabel.extendedPartition, - "geometry", - None) # parted.Geometry - - # align start sector as needed - if not disklabel.alignment.isAligned(chunk.geometry, start): - start = disklabel.alignment.alignUp(chunk.geometry, start) - new_partitions = [] - for p in chunk.requests: - ptype = p.device.partedPartition.type - log.debug("partition %s (%d): %s" % (p.device.name, - p.device.id, ptype)) - if ptype == parted.PARTITION_EXTENDED: - continue - - # XXX since we need one metadata sector before each - # logical partition we burn one logical block to - # safely align the start of each logical partition - if ptype == parted.PARTITION_LOGICAL: - start += disklabel.alignment.grainSize - - old_geometry = p.device.partedPartition.geometry - new_length = p.base + p.growth - end = start + new_length - 1 - # align end sector as needed - if not disklabel.endAlignment.isAligned(chunk.geometry, end): - end = disklabel.endAlignment.alignDown(chunk.geometry, end) - new_geometry = parted.Geometry(device=disklabel.partedDevice, - start=start, - end=end) - log.debug("new geometry for %s: %s" % (p.device.name, - new_geometry)) - start = end + 1 - new_partition = parted.Partition(disk=disklabel.partedDisk, - type=ptype, - geometry=new_geometry) - new_partitions.append((new_partition, p.device)) - - # remove all new partitions from this chunk - removeNewPartitions([disk], [r.device for r in chunk.requests]) - log.debug("back from removeNewPartitions") - - # adjust the extended partition as needed - # we will ony resize an extended partition that we created - log.debug("extended: %s" % extended_geometry) - if extended_geometry and \ - chunk.geometry.contains(extended_geometry): - log.debug("setting up new geometry for extended on %s" % disk.name) - ext_start = 0 - for (partition, device) in new_partitions: - if partition.type != parted.PARTITION_LOGICAL: - continue - - if not ext_start or partition.geometry.start < ext_start: - # account for the logical block difference in start - # sector for the extended -v- first logical - # (partition.geometry.start is already aligned) - ext_start = partition.geometry.start - disklabel.alignment.grainSize - - new_geometry = parted.Geometry(device=disklabel.partedDevice, - start=ext_start, - end=chunk.geometry.end) - log.debug("new geometry for extended: %s" % new_geometry) - new_extended = parted.Partition(disk=disklabel.partedDisk, - type=parted.PARTITION_EXTENDED, - geometry=new_geometry) - ptypes = [p.type for (p, d) in new_partitions] - for pt_idx, ptype in enumerate(ptypes): - if ptype == parted.PARTITION_LOGICAL: - new_partitions.insert(pt_idx, (new_extended, None)) - break - - # add the partitions with their new geometries to the disk - for (partition, device) in new_partitions: - if device: - name = device.name - else: - # If there was no extended partition on this disk when - # doPartitioning was called we won't have a - # PartitionDevice instance for it. - name = partition.getDeviceNodeName() - - log.debug("setting %s new geometry: %s" % (name, - partition.geometry)) - constraint = parted.Constraint(exactGeom=partition.geometry) - disklabel.partedDisk.addPartition(partition=partition, - constraint=constraint) - path = partition.path - if device: - # set the device's name - device.partedPartition = partition - # without this, the path attr will be a basename. eek. - device.disk = disk - - # make sure we store the disk's version of the partition - newpart = disklabel.partedDisk.getPartitionByPath(path) - device.partedPartition = newpart - - -def lvCompare(lv1, lv2): - """ More specifically defined lvs come first. - - < 1 => x < y - 0 => x == y - > 1 => x > y - """ - ret = 0 - - # larger requests go to the front of the list - ret -= cmp(lv1.size, lv2.size) * 100 - - # fixed size requests to the front - ret += cmp(lv1.req_grow, lv2.req_grow) * 50 - - # potentially larger growable requests go to the front - if lv1.req_grow and lv2.req_grow: - if not lv1.req_max_size and lv2.req_max_size: - ret -= 25 - elif lv1.req_max_size and not lv2.req_max_size: - ret += 25 - else: - ret -= cmp(lv1.req_max_size, lv2.req_max_size) * 25 - - if ret > 0: - ret = 1 - elif ret < 0: - ret = -1 - - return ret - -def growLVM(storage): - """ Grow LVs according to the sizes of the PVs. """ - for vg in storage.vgs: - total_free = vg.freeSpace - if total_free < 0: - # by now we have allocated the PVs so if there isn't enough - # space in the VG we have a real problem - raise PartitioningError(_("not enough space for LVM requests")) - elif not total_free: - log.debug("vg %s has no free space" % vg.name) - continue - - log.debug("vg %s: %dMB free ; lvs: %s" % (vg.name, total_free, - [l.lvname for l in vg.lvs])) - - chunk = VGChunk(vg, requests=[LVRequest(l) for l in vg.lvs]) - chunk.growRequests() - - # now grow the lvs by the amounts we've calculated above - for req in chunk.requests: - if not req.device.req_grow: - continue - - # Base is in pe, which means potentially rounded up by as much as - # pesize-1. As a result, you can't just add the growth to the - # initial size. - req.device.size = chunk.lengthToSize(req.base + req.growth) diff --git a/pyanaconda/storage/partspec.py b/pyanaconda/storage/partspec.py deleted file mode 100644 index 97cb33ec3..000000000 --- a/pyanaconda/storage/partspec.py +++ /dev/null @@ -1,82 +0,0 @@ -# partspec.py -# -# Copyright (C) 2009 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): Chris Lumens <clumens@redhat.com> -# - -class PartSpec(object): - def __init__(self, mountpoint=None, fstype=None, size=None, maxSize=None, - grow=False, btr=False, lv=False, singlePV=False, weight=0, - requiredSpace=0, encrypted=False): - """ Create a new storage specification. These are used to specify - the default partitioning layout as an object before we have the - storage system up and running. The attributes are obvious - except for the following: - - btr -- Should this be allocated as a btrfs subvolume? If not, - it will be allocated as a partition. - lv -- Should this be allocated as a logical volume? If not, - it will be allocated as a partition. - singlePV -- Should this logical volume map to a single physical - volume in the volume group? Implies lv=True - weight -- An integer that modifies the sort algorithm for partition - requests. A larger value means the partition will end up - closer to the front of the disk. This is mainly used to - make sure /boot ends up in front, and any special (PReP, - appleboot, etc.) partitions end up in front of /boot. - This value means nothing unless lv and btr are both False. - requiredSpace -- This value is only taken into account if - lv=True, and specifies the size in MB that the - containing VG must be for this PartSpec to even - get used. The VG's size is calculated before any - other LVs are created inside it. If not enough - space exists, this PartSpec will never get turned - into an LV. - encrypted -- Should this request be encrypted? For logical volume - requests, this is satisfied if the PVs are encrypted - as in the case of encrypted LVM autopart. - """ - - self.mountpoint = mountpoint - self.fstype = fstype - self.size = size - self.maxSize = maxSize - self.grow = grow - self.lv = lv - self.btr = btr - self.singlePV = singlePV - self.weight = weight - self.requiredSpace = requiredSpace - self.encrypted = encrypted - - if self.singlePV and not self.lv: - self.lv = True - - def __str__(self): - s = ("%(type)s instance (%(id)s) -- \n" - " mountpoint = %(mountpoint)s lv = %(lv)s singlePV = %(singlePV)s" - " btrfs = %(btrfs)s\n" - " weight = %(weight)s fstype = %(fstype)s encrypted = %(enc)s\n" - " size = %(size)s maxSize = %(maxSize)s grow = %(grow)s\n" % - {"type": self.__class__.__name__, "id": "%#x" % id(self), - "mountpoint": self.mountpoint, "lv": self.lv, "btrfs": self.btr, - "singlePV": self.singlePV, "weight": self.weight, - "fstype": self.fstype, "size": self.size, "enc": self.encrypted, - "maxSize": self.maxSize, "grow": self.grow}) - - return s diff --git a/pyanaconda/storage/platform.py b/pyanaconda/storage/platform.py deleted file mode 100644 index 5221a3065..000000000 --- a/pyanaconda/storage/platform.py +++ /dev/null @@ -1,409 +0,0 @@ -# -# 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 deleted file mode 100644 index 705b93d80..000000000 --- a/pyanaconda/storage/pyudev.py +++ /dev/null @@ -1,232 +0,0 @@ -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/size.py b/pyanaconda/storage/size.py deleted file mode 100644 index 0081d5241..000000000 --- a/pyanaconda/storage/size.py +++ /dev/null @@ -1,235 +0,0 @@ -# size.py -# Python module to represent storage sizes -# -# 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): David Cantrell <dcantrell@redhat.com> - -import re - -from decimal import Decimal -from decimal import InvalidOperation - -from errors import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) -P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z) - -# Decimal prefixes for different size increments, along with the name -# and accepted abbreviation for the prefix. These prefixes are all -# for 'bytes'. -_decimalPrefix = [(1000, _("kilo"), _("k")), - (1000**2, _("mega"), _("M")), - (1000**3, _("giga"), _("G")), - (1000**4, _("tera"), _("T")), - (1000**5, _("peta"), _("P")), - (1000**6, _("exa"), _("E")), - (1000**7, _("zetta"), _("Z")), - (1000**8, _("yotta"), _("Y"))] - -# Binary prefixes for the different size increments. Same structure -# as the above list. -_binaryPrefix = [(1024, _("kibi"), _("Ki")), - (1024**2, _("mebi"), _("Mi")), - (1024**3, _("gibi"), _("Gi")), - (1024**4, _("tebi"), None), - (1024**5, _("pebi"), None), - (1024**6, _("ebi"), None), - (1024**7, _("zebi"), None), - (1024**8, _("yobi"), None)] - -_bytes = [_('b'), _('byte'), _('bytes')] -_prefixes = _decimalPrefix + _binaryPrefix - -def _makeSpecs(prefix, abbr): - """ Internal method used to generate a list of specifiers. """ - specs = [] - - if prefix: - specs.append(prefix.lower() + _("byte")) - specs.append(prefix.lower() + _("bytes")) - - if abbr: - specs.append(abbr.lower() + _("b")) - - return specs - -def _parseSpec(spec): - """ Parse string representation of size. """ - if not spec: - raise ValueError("invalid size specification", spec) - - m = re.match(r'([0-9.]+)\s*([A-Za-z]*)$', spec.strip()) - if not m: - raise ValueError("invalid size specification", spec) - - try: - size = Decimal(m.groups()[0]) - except InvalidOperation: - raise ValueError("invalid size specification", spec) - - if size < 0: - raise SizeNotPositiveError("spec= param must be >=0") - - specifier = m.groups()[1].lower() - if specifier in _bytes or not specifier: - return size - - for factor, prefix, abbr in _prefixes: - check = _makeSpecs(prefix, abbr) - - if specifier in check: - return size * factor - - raise ValueError("invalid size specification", spec) - -class Size(Decimal): - """ Common class to represent storage device and filesystem sizes. - Can handle parsing strings such as 45MB or 6.7GB to initialize - itself, or can be initialized with a numerical size in bytes. - Also generates human readable strings to a specified number of - decimal places. - """ - - def __new__(cls, bytes=None, spec=None): - """ Initialize a new Size object. Must pass either bytes or spec, - but not both. The bytes parameter is a numerical value for - the size this object represents, in bytes. The spec parameter - is a string specification of the size using any of the size - specifiers in the _decimalPrefix or _binaryPrefix lists combined - with a 'b' or 'B'. For example, to specify 640 kilobytes, you - could pass any of these parameter: - - spec="640kb" - spec="640 kb" - spec="640KB" - spec="640 KB" - spec="640 kilobytes" - - If you want to use spec to pass a bytes value, you can use the - letter 'b' or 'B' or simply leave the specifier off and bytes - will be assumed. - """ - if bytes and spec: - raise SizeParamsError("only specify one parameter") - - if bytes is not None: - if type(bytes).__name__ in ["int", "long", "float", 'Decimal'] and bytes >= 0: - self = Decimal.__new__(cls, value=bytes) - else: - raise SizeNotPositiveError("bytes= param must be >=0") - elif spec: - self = Decimal.__new__(cls, value=_parseSpec(spec)) - else: - raise SizeParamsError("missing bytes= or spec=") - - return self - - def __str__(self, context=None): - return self.humanReadable() - - def __repr__(self): - return "Size('%s')" % self - - def __add__(self, other, context=None): - return Size(bytes=Decimal.__add__(self, other, context=context)) - - # needed to make sum() work with Size arguments - def __radd__(self, other, context=None): - return Size(bytes=Decimal.__radd__(self, other, context=context)) - - def __sub__(self, other, context=None): - # subtraction is implemented using __add__ and negation, so we'll - # be getting passed a Size - return Decimal.__sub__(self, other, context=context) - - def __mul__(self, other, context=None): - return Size(bytes=Decimal.__mul__(self, other, context=context)) - - def __div__(self, other, context=None): - return Size(bytes=Decimal.__div__(self, other, context=context)) - - def _trimEnd(self, val): - """ Internal method to trim trailing zeros. """ - val = re.sub(r'(\.\d*?)0+$', '\\1', val) - while val.endswith('.'): - val = val[:-1] - - return val - - def convertTo(self, spec="b"): - """ Return the size in the units indicated by the specifier. The - specifier can be prefixes from the _decimalPrefix and - _binaryPrefix lists combined with 'b' or 'B' for abbreviations) - or 'bytes' (for prefixes like kilo or mega). The size is - returned as a Decimal. - """ - spec = spec.lower() - - if spec in _bytes: - return self - - for factor, prefix, abbr in _prefixes: - check = _makeSpecs(prefix, abbr) - - if spec in check: - return Decimal(self / Decimal(factor)) - - return None - - def humanReadable(self, places=None, max_places=2): - """ Return a string representation of this size with appropriate - size specifier and in the specified number of decimal places - (default: auto with a maximum of 2 decimal places). - """ - if places is not None and places < 0: - raise SizePlacesError("places= must be >=0 or None") - - if max_places is not None and max_places < 0: - raise SizePlacesError("max_places= must be >=0 or None") - - check = self._trimEnd("%d" % self) - - if Decimal(check) < 1000: - return "%s B" % check - - for factor, prefix, abbr in _prefixes: - newcheck = super(Size, self).__div__(Decimal(factor)) - - if newcheck < 1000: - # nice value, use this factor, prefix and abbr - break - - if places is not None: - fmt = "%%.%df" % places - retval = fmt % newcheck - else: - retval = self._trimEnd("%f" % newcheck) - - if max_places is not None: - (whole, point, fraction) = retval.partition(".") - if point and len(fraction) > max_places: - if max_places == 0: - retval = whole - else: - retval = "%s.%s" % (whole, fraction[:max_places]) - - if abbr: - return retval + " " + abbr + _("B") - else: - return retval + " " + prefix + P_("byte", "bytes", newcheck) diff --git a/pyanaconda/storage/storage_log.py b/pyanaconda/storage/storage_log.py deleted file mode 100644 index 62ad86fd2..000000000 --- a/pyanaconda/storage/storage_log.py +++ /dev/null @@ -1,46 +0,0 @@ -import inspect -import logging - -log = logging.getLogger("storage") -log.addHandler(logging.NullHandler()) - -def function_name_and_depth(): - IGNORED_FUNCS = ["function_name_and_depth", - "log_method_call", - "log_method_return"] - stack = inspect.stack() - - for i, frame in enumerate(stack): - methodname = frame[3] - if methodname not in IGNORED_FUNCS: - return (methodname, len(stack) - i) - - return ("unknown function?", 0) - -def log_method_call(d, *args, **kwargs): - classname = d.__class__.__name__ - (methodname, depth) = function_name_and_depth() - spaces = depth * ' ' - fmt = "%s%s.%s:" - fmt_args = [spaces, classname, methodname] - - for arg in args: - fmt += " %s ;" - fmt_args.append(arg) - - for k, v in kwargs.items(): - fmt += " %s: %s ;" - if "pass" in k.lower() and v: - v = "Skipped" - fmt_args.extend([k, v]) - - logging.getLogger("storage").debug(fmt % tuple(fmt_args)) - -def log_method_return(d, retval): - classname = d.__class__.__name__ - (methodname, depth) = function_name_and_depth() - spaces = depth * ' ' - fmt = "%s%s.%s returned %s" - fmt_args = (spaces, classname, methodname, retval) - logging.getLogger("storage").debug(fmt % fmt_args) - diff --git a/pyanaconda/storage/tsort.py b/pyanaconda/storage/tsort.py deleted file mode 100644 index 52a871f56..000000000 --- a/pyanaconda/storage/tsort.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/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 deleted file mode 100644 index f8ba09c9b..000000000 --- a/pyanaconda/storage/udev.py +++ /dev/null @@ -1,737 +0,0 @@ -# udev.py -# Python module for querying the udev database for device information. -# -# 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 -# 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> -# Chris Lumens <clumens@redhat.com> -# - -import os -import re - -import util -from errors 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 - - import devices as _devices - ret = None - for dev in udev_get_block_devices(): - if devspec.startswith("LABEL="): - if udev_device_get_label(dev) == devspec[6:]: - ret = dev - break - elif devspec.startswith("UUID="): - if udev_device_get_uuid(dev) == devspec[5:]: - ret = dev - break - elif udev_device_get_name(dev) == _devices.devicePathToName(devspec): - ret = dev - break - else: - spec = devspec - if not spec.startswith("/dev/"): - spec = os.path.normpath("/dev/" + spec) - - for link in dev["symlinks"]: - if spec == link: - ret = dev - break - - del _devices - if ret: - return udev_device_get_name(ret) - -def udev_resolve_glob(glob): - import fnmatch - ret = [] - - if not glob: - return ret - - for dev in udev_get_block_devices(): - name = udev_device_get_name(dev) - - if fnmatch.fnmatch(name, glob): - ret.append(name) - else: - for link in dev["symlinks"]: - if fnmatch.fnmatch(link, glob): - ret.append(name) - - return ret - -def udev_get_block_devices(): - udev_settle() - entries = [] - for path in udev_enumerate_block_devices(): - entry = udev_get_block_device(path) - if entry: - if entry["name"].startswith("md"): - # mdraid is really braindead, when a device is stopped - # it is no longer usefull in anyway (and we should not - # probe it) yet it still sticks around, see bug rh523387 - state = None - state_file = "/sys/%s/md/array_state" % entry["sysfs_path"] - if os.access(state_file, os.R_OK): - state = open(state_file).read().strip() - if state == "clear": - continue - entries.append(entry) - return entries - -def __is_blacklisted_blockdev(dev_name): - """Is this a blockdev we never want for an install?""" - if dev_name.startswith("ram") or dev_name.startswith("fd"): - return True - - if os.path.exists("/sys/class/block/%s/device/model" %(dev_name,)): - model = open("/sys/class/block/%s/device/model" %(dev_name,)).read() - for bad in ("IBM *STMF KERNEL", "SCEI Flash-5", "DGC LUNZ"): - if model.find(bad) != -1: - log.info("ignoring %s with model %s" %(dev_name, model)) - return True - - return False - -def udev_enumerate_block_devices(): - import os.path - - return filter(lambda d: not __is_blacklisted_blockdev(os.path.basename(d)), - udev_enumerate_devices(deviceClass="block")) - -def udev_get_block_device(sysfs_path): - dev = udev_get_device(sysfs_path) - if not dev or not dev.has_key("name"): - return None - else: - return dev - - -# These are functions for retrieving specific pieces of information from -# udev database entries. -def udev_device_get_name(udev_info): - """ Return the best name for a device based on the udev db data. """ - if "DM_NAME" in udev_info: - name = udev_info["DM_NAME"] - else: - name = udev_info["name"] - - return name - -def udev_device_get_format(udev_info): - """ Return a device's format type as reported by udev. """ - return udev_info.get("ID_FS_TYPE") - -def udev_device_get_uuid(udev_info): - """ Get the UUID from the device's format as reported by udev. """ - md_uuid = udev_info.get("MD_UUID", '') - uuid = udev_info.get("ID_FS_UUID", '') - # we don't want to return the array's uuid as a member's uuid - if len(uuid) > 0 and \ - re.sub(r'\W', '', md_uuid) != re.sub(r'\W', '', uuid): - return udev_info.get("ID_FS_UUID") - -def udev_device_get_label(udev_info): - """ Get the label from the device's format as reported by udev. """ - return udev_info.get("ID_FS_LABEL") - -def udev_device_is_dm(info): - """ Return True if the device is a device-mapper device. """ - return info.has_key("DM_NAME") - -def udev_device_is_md(info): - """ Return True if the device is a mdraid array device. """ - # Don't identify partitions on mdraid arrays as raid arrays - if udev_device_is_partition(info): - return False - - # The udev information keeps shifting around. Only md arrays have a - # /sys/class/block/<name>/md/ subdirectory. - md_dir = "/sys" + udev_device_get_sysfs_path(info) + "/md" - return os.path.exists(md_dir) - -def udev_device_is_cciss(info): - """ Return True if the device is a CCISS device. """ - return udev_device_get_name(info).startswith("cciss") - -def udev_device_is_dasd(info): - """ Return True if the device is a dasd device. """ - devname = info.get("DEVNAME") - if devname: - return devname.startswith("dasd") - else: - return False - -def udev_device_is_zfcp(info): - """ Return True if the device is a zfcp device. """ - if info.get("DEVTYPE") != "disk": - return False - - subsystem = "/sys" + info.get("sysfs_path") - - while True: - topdir = os.path.realpath(os.path.dirname(subsystem)) - driver = "%s/driver" % (topdir,) - - if os.path.islink(driver): - subsystemname = os.path.basename(os.readlink(subsystem)) - drivername = os.path.basename(os.readlink(driver)) - - if subsystemname == 'ccw' and drivername == 'zfcp': - return True - - newsubsystem = os.path.dirname(topdir) - - if newsubsystem == topdir: - break - - subsystem = newsubsystem + "/subsystem" - - return False - -def udev_device_get_zfcp_attribute(info, attr=None): - """ Return the value of the specified attribute of the zfcp device. """ - if not attr: - log.debug("udev_device_get_zfcp_attribute() called with attr=None") - return None - - attribute = "/sys%s/device/%s" % (info.get("sysfs_path"), attr,) - attribute = os.path.realpath(attribute) - - if not os.path.isfile(attribute): - log.warning("%s is not a valid zfcp attribute" % (attribute,)) - return None - - return open(attribute, "r").read().strip() - -def udev_device_get_dasd_bus_id(info): - """ Return the CCW bus ID of the dasd device. """ - return info.get("sysfs_path").split("/")[-3] - -def udev_device_get_dasd_flag(info, flag=None): - """ Return the specified flag for the dasd device. """ - if flag is None: - return None - - path = "/sys" + info.get("sysfs_path") + "/device/" + flag - if not os.path.isfile(path): - return None - - return open(path, 'r').read().strip() - -def udev_device_is_cdrom(info): - """ Return True if the device is an optical drive. """ - # FIXME: how can we differentiate USB drives from CD-ROM drives? - # -- USB drives also generate a sdX device. - return info.get("ID_CDROM") == "1" - -def udev_device_is_disk(info): - """ Return True is the device is a disk. """ - if udev_device_is_cdrom(info): - return False - has_range = os.path.exists("/sys/%s/range" % info['sysfs_path']) - return info.get("DEVTYPE") == "disk" or has_range - -def udev_device_is_partition(info): - has_start = os.path.exists("/sys/%s/start" % info['sysfs_path']) - return info.get("DEVTYPE") == "partition" or has_start - -def udev_device_is_loop(info): - """ Return True if the device is a configured loop device. """ - return (udev_device_get_name(info).startswith("loop") and - os.path.isdir("/sys/%s/loop" % info['sysfs_path'])) - -def udev_device_get_serial(udev_info): - """ Get the serial number/UUID from the device as reported by udev. """ - return udev_info.get("ID_SERIAL_RAW", udev_info.get("ID_SERIAL_SHORT", udev_info.get("ID_SERIAL"))) - -def udev_device_get_wwid(udev_info): - """ The WWID of a device is typically just its serial number, but with - colons in the name to make it more readable. """ - serial = udev_device_get_serial(udev_info) - return util.insert_colons(serial) if serial else "" - -def udev_device_get_vendor(udev_info): - """ Get the vendor of the device as reported by udev. """ - return udev_info.get("ID_VENDOR_FROM_DATABASE", udev_info.get("ID_VENDOR")) - -def udev_device_get_model(udev_info): - """ Get the model of the device as reported by udev. """ - return udev_info.get("ID_MODEL_FROM_DATABASE", udev_info.get("ID_MODEL")) - -def udev_device_get_bus(udev_info): - """ Get the bus a device is connected to the system by. """ - return udev_info.get("ID_BUS", "").upper() - -def udev_device_get_path(info): - return info["ID_PATH"] - -def udev_device_get_by_path(info): - if info.has_key('symlinks'): - for link in info['symlinks']: - if link.startswith('/dev/disk/by-path/'): - return link - - return udev_device_get_name(info) - -def udev_device_get_sysfs_path(info): - return info['sysfs_path'] - -def udev_device_get_major(info): - return int(info["MAJOR"]) - -def udev_device_get_minor(info): - return int(info["MINOR"]) - -def udev_device_get_md_level(info): - return info.get("MD_LEVEL") - -def udev_device_get_md_devices(info): - return int(info["MD_DEVICES"]) - -def udev_device_get_md_uuid(info): - return info["MD_UUID"] - -def udev_device_get_md_container(info): - return info.get("MD_CONTAINER") - -def udev_device_get_md_name(info): - return info.get("MD_DEVNAME") - -def udev_device_get_vg_name(info): - return info['LVM2_VG_NAME'] - -def udev_device_get_lv_vg_name(info): - return info['DM_VG_NAME'] - -def udev_device_get_vg_uuid(info): - return info['LVM2_VG_UUID'] - -def udev_device_get_vg_size(info): - # lvm's decmial precision is not configurable, so we tell it to use - # KB and convert to MB here - return float(info['LVM2_VG_SIZE']) / 1024 - -def udev_device_get_vg_free(info): - # lvm's decmial precision is not configurable, so we tell it to use - # KB and convert to MB here - return float(info['LVM2_VG_FREE']) / 1024 - -def udev_device_get_vg_extent_size(info): - # lvm's decmial precision is not configurable, so we tell it to use - # KB and convert to MB here - return float(info['LVM2_VG_EXTENT_SIZE']) / 1024 - -def udev_device_get_vg_extent_count(info): - return int(info['LVM2_VG_EXTENT_COUNT']) - -def udev_device_get_vg_free_extents(info): - return int(info['LVM2_VG_FREE_COUNT']) - -def udev_device_get_vg_pv_count(info): - return int(info['LVM2_PV_COUNT']) - -def udev_device_get_pv_pe_start(info): - # lvm's decmial precision is not configurable, so we tell it to use - # KB and convert to MB here - return float(info['LVM2_PE_START']) / 1024 - -def udev_device_get_lv_names(info): - names = info['LVM2_LV_NAME'] - if not names: - names = [] - elif not isinstance(names, list): - names = [names] - return names - -def udev_device_get_lv_uuids(info): - uuids = info['LVM2_LV_UUID'] - if not uuids: - uuids = [] - elif not isinstance(uuids, list): - uuids = [uuids] - return uuids - -def udev_device_get_lv_sizes(info): - # lvm's decmial precision is not configurable, so we tell it to use - # KB and convert to MB here - sizes = info['LVM2_LV_SIZE'] - if not sizes: - sizes = [] - elif not isinstance(sizes, list): - sizes = [sizes] - - return [float(s) / 1024 for s in sizes] - -def udev_device_get_lv_attr(info): - attr = info['LVM2_LV_ATTR'] - if not attr: - attr = [] - elif not isinstance(attr, list): - attr = [attr] - return attr - -def udev_device_dm_subsystem_match(info, subsystem): - """ Return True if the device matches a given device-mapper subsystem. """ - uuid = info.get("DM_UUID", "") - uuid_fields = uuid.split("-") - _subsystem = uuid_fields[0] - if _subsystem.lower().startswith("part") and len(uuid_fields) > 1: - # kpartx uses partN- as a subsystem prefix, which we ignore because - # we only care about the subsystem of the partitions' parent device. - _subsystem = uuid_fields[1] - - if _subsystem == uuid or not _subsystem: - return False - - return _subsystem.lower() == subsystem.lower() - -def udev_device_is_dm_lvm(info): - """ Return True if the device is an LVM logical volume. """ - return udev_device_dm_subsystem_match(info, "lvm") - -def udev_device_is_dm_crypt(info): - """ Return True if the device is a mapped dm-crypt device. """ - return udev_device_dm_subsystem_match(info, "crypt") - -def udev_device_is_dm_luks(info): - """ Return True if the device is a mapped LUKS device. """ - is_crypt = udev_device_dm_subsystem_match(info, "crypt") - try: - _type = info.get("DM_UUID", "").split("-")[1].lower() - except IndexError: - _type = "" - - return is_crypt and _type.startswith("luks") - -def udev_device_is_dm_raid(info): - """ Return True if the device is a dmraid array device. """ - return udev_device_dm_subsystem_match(info, "dmraid") - -def udev_device_is_dm_mpath(info): - """ Return True if the device is a multipath device. """ - return udev_device_dm_subsystem_match(info, "mpath") - -def udev_device_is_dm_anaconda(info): - """ Return True if the device is an anaconda disk image. """ - return udev_device_dm_subsystem_match(info, "anaconda") - -def udev_device_is_dm_livecd(info): - """ Return True if the device is a livecd OS image. """ - # return udev_device_dm_subsystem_match(info, "livecd") - return (udev_device_is_dm(info) and - udev_device_get_name(info).startswith("live")) - -def udev_device_is_biosraid_member(info): - # Note that this function does *not* identify raid sets. - # Tests to see if device is part of a dmraid set. - # dmraid and mdraid have the same ID_FS_USAGE string, ID_FS_TYPE has a - # string that describes the type of dmraid (isw_raid_member...), I don't - # want to maintain a list and mdraid's ID_FS_TYPE='linux_raid_member', so - # dmraid will be everything that is raid and not linux_raid_member - from formats.dmraid import DMRaidMember - from formats.mdraid import MDRaidMember - if info.has_key("ID_FS_TYPE") and \ - (info["ID_FS_TYPE"] in DMRaidMember._udevTypes or \ - info["ID_FS_TYPE"] in MDRaidMember._udevTypes) and \ - info["ID_FS_TYPE"] != "linux_raid_member": - return True - - return False - -def udev_device_get_dm_partition_disk(info): - return re.sub(r'\d*$', '', udev_device_get_name(info)) - -def udev_device_is_dm_partition(info): - return (udev_device_is_dm(info) and - info.get("DM_UUID", "").split("-")[0].startswith("part")) - -def udev_device_is_multipath_member(info): - """ Return True if the device is part of a multipath. """ - return info.get("ID_FS_TYPE") == "multipath_member" - -def udev_device_get_multipath_name(info): - """ Return the name of the multipath that the device is a member of. """ - if udev_device_is_multipath_member(info): - return info['ID_MPATH_NAME'] - return None - -# iscsi disks' ID_PATH form depends on the driver: -# for software iscsi: -# ip-${iscsi_address}:${iscsi_port}-iscsi-${iscsi_tgtname}-lun-${lun} -# for partial offload iscsi: -# pci-${pci_address}-ip-${iscsi_address}:${iscsi_port}-iscsi-${iscsi_tgtname}-lun-${lun} -# Note that in the case of IPV6 iscsi_address itself can contain : -# too, but iscsi_port never contains : - -def udev_device_is_sw_iscsi(info): - # software iscsi - try: - path_components = udev_device_get_path(info).split("-") - - if info["ID_BUS"] == "scsi" and len(path_components) >= 6 and \ - path_components[0] == "ip" and path_components[2] == "iscsi": - return True - except KeyError: - pass - - return False - -def udev_device_is_partoff_iscsi(info): - # partial offload iscsi - try: - path_components = udev_device_get_path(info).split("-") - - if info["ID_BUS"] == "scsi" and len(path_components) >= 8 and \ - path_components[2] == "ip" and path_components[4] == "iscsi": - return True - except KeyError: - pass - - return False - -def udev_device_is_iscsi(info): - return udev_device_is_sw_iscsi(info) or udev_device_is_partoff_iscsi(info) - -def udev_device_get_iscsi_name(info): - name_field = 3 - if udev_device_is_partoff_iscsi(info): - name_field = 5 - - path_components = udev_device_get_path(info).split("-") - - # Tricky, the name itself contains atleast 1 - char - return "-".join(path_components[name_field:len(path_components)-2]) - -def udev_device_get_iscsi_address(info): - address_field = 1 - if udev_device_is_partoff_iscsi(info): - address_field = 3 - - path_components = udev_device_get_path(info).split("-") - - # IPV6 addresses contain : within the address, so take everything - # before the last : as address - return ":".join(path_components[address_field].split(":")[:-1]) - -def udev_device_get_iscsi_port(info): - address_field = 1 - if udev_device_is_partoff_iscsi(info): - address_field = 3 - - path_components = udev_device_get_path(info).split("-") - - # IPV6 contains : within the address, the part after the last : is the port - return path_components[address_field].split(":")[-1] - -def udev_device_get_iscsi_session(info): - # '/devices/pci0000:00/0000:00:02.0/0000:09:00.0/0000:0a:01.0/0000:0e:00.2/host3/session1/target3:0:0/3:0:0:0/block/sda' - # The position of sessionX part depends on device - # (e.g. offload vs. sw; also varies for different offload devs) - session = None - match = re.match('/.*/(session\d+)', info["sysfs_path"]) - if match: - session = match.groups()[0] - else: - log.error("udev_device_get_iscsi_session: session not found in %s" % info) - return session - - -def udev_device_get_iscsi_nic(info): - iface = None - session = udev_device_get_iscsi_session(info) - if session: - iface = open("/sys/class/iscsi_session/%s/ifacename" % - session).read().strip() - return iface - -def udev_device_get_iscsi_initiator(info): - initiator = None - if udev_device_is_partoff_iscsi(info): - host = re.match('.*/(host\d+)', info["sysfs_path"]).groups()[0] - if host: - initiator_file = "/sys/class/iscsi_host/%s/initiatorname" % host - if os.access(initiator_file, os.R_OK): - initiator = open(initiator_file).read().strip() - log.debug("found offload iscsi initiatorname %s in file %s" % - (initiator, initiator_file)) - if initiator.lstrip("(").rstrip(")").lower() == "null": - initiator = None - if initiator is None: - session = udev_device_get_iscsi_session(info) - if session: - initiator = open("/sys/class/iscsi_session/%s/initiatorname" % - session).read().strip() - log.debug("found iscsi initiatorname %s" % initiator) - return initiator - - -# fcoe disks have ID_PATH in the form of: -# For FCoE directly over the NIC (so no VLAN and thus no DCB): -# pci-eth#-fc-${id} -# For FCoE over a VLAN (the DCB case) -# fc-${id} -# fcoe parts look like this: -# pci-eth#-fc-${id}-part# -# fc-${id}-part# - -# For the FCoE over VLAN case we also do some checks on the sysfs_path as -# the ID_PATH does not contain all info we need there, the sysfs_path for -# an fcoe disk over VLAN looks like this: -# /devices/virtual/net/eth4.802-fcoe/host3/rport-3:0-4/target3:0:1/3:0:1:0/block/sde -# And for a partition: -# /devices/virtual/net/eth4.802-fcoe/host3/rport-3:0-4/target3:0:1/3:0:1:0/block/sde/sde1 - -# This is completely different for Broadcom FCoE devices (bnx2fc), where we use -# the sysfs path: -# /devices/pci0000:00/0000:00:02.0/0000:09:00.0/0000:0a:01.0/0000:0e:00.0/host3/rport-3:0-2/target3:0:1/3:0:1:3/block/sdm -# and find whether the host has 'fc_host' and if it the device has a bound -# Ethernet interface. - -def _detect_broadcom_fcoe(info): - re_pci_host=re.compile('/(.*)/(host\d+)') - match = re_pci_host.match(info["sysfs_path"]) - if match: - sysfs_pci, host = match.groups() - if os.access('/sys/%s/%s/fc_host' %(sysfs_pci, host), os.X_OK) and \ - os.access('/sys/%s/net' %(sysfs_pci), os.X_OK): - return (sysfs_pci, host) - return (None, None) - -def udev_device_is_fcoe(info): - if info.get("ID_BUS") != "scsi": - return False - - path = info.get("ID_PATH", "") - path_components = path.split("-") - - if path.startswith("pci-eth") and len(path_components) >= 4 and \ - path_components[2] == "fc": - return True - - if path.startswith("fc-") and "fcoe" in info["sysfs_path"]: - return True - - if _detect_broadcom_fcoe(info) != (None, None): - return True - - return False - -def udev_device_get_fcoe_nic(info): - path = info.get("ID_PATH", "") - path_components = path.split("-") - - if path.startswith("pci-eth") and len(path_components) >= 4 and \ - path_components[2] == "fc": - return path_components[1] - - if path.startswith("fc-") and "fcoe" in info["sysfs_path"]: - return info["sysfs_path"].split("/")[4].split(".")[0] - - (sysfs_pci, host) = _detect_broadcom_fcoe(info) - if (sysfs_pci, host) != (None, None): - net_path = '/sys/%s/net' % sysfs_pci - listdir = os.listdir(net_path) - if len(listdir) > 0 : - return listdir[0] - -def udev_device_get_fcoe_identifier(info): - path = info.get("ID_PATH", "") - path_components = path.split("-") - - if path.startswith("pci-eth") and len(path_components) >= 4 and \ - path_components[2] == "fc": - return path_components[3] - - if path.startswith("fc-") and "fcoe" in info["sysfs_path"]: - return path_components[1] - - if udev_device_is_fcoe(info) and len(path_components) >= 4 and \ - path_components[2] == 'fc': - return path_components[3] diff --git a/pyanaconda/storage/util.py b/pyanaconda/storage/util.py deleted file mode 100644 index c80841bf4..000000000 --- a/pyanaconda/storage/util.py +++ /dev/null @@ -1,272 +0,0 @@ -import os -import selinux -import subprocess - -from size import Size - -import logging -log = logging.getLogger("storage") -program_log = logging.getLogger("program") - -def _run_program(argv, root='/', stdin=None, env_prune=None): - if env_prune is None: - env_prune = [] - - def chroot(): - if root and root != '/': - os.chroot(root) - - program_log.info("Running... %s" % argv) - - env = os.environ.copy() - env.update({"LC_ALL": "C", - "INSTALL_PATH": root}) - for var in env_prune: - env.pop(var, None) - - try: - proc = subprocess.Popen(argv, - stdin=stdin, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - preexec_fn=chroot, cwd=root, env=env) - - while True: - out = proc.communicate()[0] - if out: - for line in out.splitlines(): - program_log.info(line) - - if proc.returncode is not None: - break - except OSError as e: - program_log.error("Error running %s: %s" % (argv[0], e.strerror)) - raise - - program_log.debug("Return code: %d" % proc.returncode) - return (proc.returncode, out) - -def run_program(*args, **kwargs): - return _run_program(*args, **kwargs)[0] - -def capture_output(*args, **kwargs): - return _run_program(*args, **kwargs)[1] - -def mount(device, mountpoint, fstype, options=None): - if options is None: - options = "defaults" - - mountpoint = os.path.normpath(mountpoint) - if not os.path.exists(mountpoint): - makedirs(mountpoint) - - argv = ["mount", "-t", fstype, "-o", options, device, mountpoint] - try: - rc = run_program(argv) - except OSError as e: - raise - - return rc - -def umount(mountpoint): - try: - rc = run_program(["umount", mountpoint]) - except OSError as e: - raise - - return rc - -def total_memory(): - """ Return the amount of system RAM in kilobytes. """ - lines = open("/proc/meminfo").readlines() - for line in lines: - if line.startswith("MemTotal:"): - mem = long(line.split()[1]) - - return mem - -## -## sysfs functions -## -def notify_kernel(path, action="change"): - """ Signal the kernel that the specified device has changed. - - Exceptions raised: ValueError, IOError - """ - log.debug("notifying kernel of '%s' event on device %s" % (action, path)) - path = os.path.join(path, "uevent") - if not path.startswith("/sys/") or not os.access(path, os.W_OK): - log.debug("sysfs path '%s' invalid" % path) - raise ValueError("invalid sysfs path") - - f = open(path, "a") - f.write("%s\n" % action) - f.close() - -def get_sysfs_attr(path, attr): - if not attr: - log.debug("get_sysfs_attr() called with attr=None") - return None - - attribute = "/sys%s/%s" % (path, attr) - attribute = os.path.realpath(attribute) - - if not os.path.isfile(attribute) and not os.path.islink(attribute): - log.warning("%s is not a valid attribute" % (attr,)) - return None - - return open(attribute, "r").read().strip() - -def get_sysfs_path_by_name(dev_node, class_name="block"): - """ Return sysfs path for a given device. - - For a device node (e.g. /dev/vda2) get the respective sysfs path - (e.g. /sys/class/block/vda2). This also has to work for device nodes - that are in a subdirectory of /dev like '/dev/cciss/c0d0p1'. - """ - dev_name = os.path.basename(dev_node) - if dev_node.startswith("/dev/"): - dev_name = dev_node[5:].replace("/", "!") - sysfs_class_dir = "/sys/class/%s" % class_name - dev_path = os.path.join(sysfs_class_dir, dev_name) - if os.path.exists(dev_path): - return dev_path - else: - raise RuntimeError("get_sysfs_path_by_name: Could not find sysfs path " - "for '%s' (it is not at '%s')" % (dev_node, dev_path)) - -## -## SELinux functions -## -def match_path_context(path): - """ Return the default SELinux context for the given path. """ - context = None - try: - context = selinux.matchpathcon(os.path.normpath(path), 0)[1] - except OSError as e: - log.info("failed to get default SELinux context for %s: %s" % (path, e)) - - return context - -def set_file_context(path, context, root=None): - """ Set the SELinux file context of a file. - - Arguments: - - path filename string - context context string - - Keyword Arguments: - - root an optional chroot string - - Return Value: - - True if successful, False if not. - """ - if root is None: - root = '/' - - full_path = os.path.normpath("%s/%s" % (root, path)) - if context is None or not os.access(full_path, os.F_OK): - return False - - try: - rc = (selinux.lsetfilecon(full_path, context) == 0) - except OSError as e: - log.info("failed to set SELinux context for %s: %s" % (full_path, e)) - rc = False - - return rc - -def reset_file_context(path, root=None): - """ Restore the SELinux context of a file to its default value. - - Arguments: - - path filename string - - Keyword Arguments: - - root an optional chroot string - - Return Value: - - If successful, returns the file's new/default context. - """ - context = match_path_context(path) - if context: - if set_file_context(path, context, root=root): - return context - -## -## Miscellaneous -## -def find_program_in_path(prog, raise_on_error=False): - for d in os.environ["PATH"].split(os.pathsep): - full = os.path.join(d, prog) - if os.access(full, os.X_OK): - return full - - if raise_on_error: - raise RuntimeError("Unable to locate a needed executable: '%s'" % prog) - -def makedirs(path): - if not os.path.isdir(path): - os.makedirs(path, 0755) - -def copy_to_system(source): - if not os.access(source, os.R_OK): - log.info("copy_to_system: source '%s' does not exist." % source) - return False - - target = ROOT_PATH + source - target_dir = os.path.dirname(target) - log.debug("copy_to_system: '%s' -> '%s'." % (source, target)) - if not os.path.isdir(target_dir): - os.makedirs(target_dir) - shutil.copy(source, target) - return True - -def lsmod(): - """ Returns list of names of all loaded modules. """ - with open("/proc/modules") as f: - lines = f.readlines() - return [l.split()[0] for l in lines] - -def get_option_value(opt_name, options): - """ Return the value of a named option in the specified options string. """ - for opt in options.split(","): - if "=" not in opt: - continue - - name, val = opt.split("=") - if name == opt_name: - return val.strip() - -def numeric_type(num): - """ Verify that a value is given as a numeric data type. - - Return the number if the type is sensible or raise ValueError - if not. - """ - if num is None: - num = 0 - elif not (isinstance(num, int) or \ - isinstance(num, long) or \ - isinstance(num, float)): - raise ValueError("value (%s) must be either a number or None" % num) - - return num - -def insert_colons(a_string): - """ Insert colon between every second character. - - E.g. creates 'al:go:ri:th:ms' from 'algoritms'. Useful for formatting - MAC addresses and wwids for output. - """ - suffix = a_string[-2:] - if len(a_string) > 2: - return insert_colons(a_string[:-2]) + ':' + suffix - else: - return suffix diff --git a/pyanaconda/storage/zfcp.py b/pyanaconda/storage/zfcp.py deleted file mode 100644 index 23be6cd9d..000000000 --- a/pyanaconda/storage/zfcp.py +++ /dev/null @@ -1,428 +0,0 @@ -# -# zfcp.py - mainframe zfcp configuration install data -# -# Copyright (C) 2001, 2002, 2003, 2004 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/>. -# -# Author(s): Karsten Hopp <karsten@redhat.com> -# - -import string -import os -from . import ROOT_PATH -from udev import udev_settle -from . import util - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("storage") - -def loggedWriteLineToFile(fn, value): - f = open(fn, "w") - log.debug("echo %s > %s" % (value, fn)) - f.write("%s\n" % (value)) - f.close() - -zfcpsysfs = "/sys/bus/ccw/drivers/zfcp" -scsidevsysfs = "/sys/bus/scsi/devices" -zfcpconf = "/etc/zfcp.conf" - -class ZFCPDevice: - def __init__(self, devnum, wwpn, fcplun): - self.devnum = self.sanitizeDeviceInput(devnum) - self.wwpn = self.sanitizeWWPNInput(wwpn) - self.fcplun = self.sanitizeFCPLInput(fcplun) - - if not self.checkValidDevice(self.devnum): - raise ValueError, _("You have not specified a device number or the number is invalid") - if not self.checkValidWWPN(self.wwpn): - raise ValueError, _("You have not specified a worldwide port name or the name is invalid.") - if not self.checkValidFCPLun(self.fcplun): - raise ValueError, _("You have not specified a FCP LUN or the number is invalid.") - - def __str__(self): - return "%s %s %s" %(self.devnum, self.wwpn, self.fcplun) - - def sanitizeDeviceInput(self, dev): - if dev is None or dev == "": - return None - dev = dev.lower() - bus = dev[:string.rfind(dev, ".") + 1] - dev = dev[string.rfind(dev, ".") + 1:] - dev = "0" * (4 - len(dev)) + dev - if not len(bus): - return "0.0." + dev - else: - return bus + dev - - def sanitizeWWPNInput(self, id): - if id is None or id == "": - return None - id = id.lower() - if id[:2] != "0x": - return "0x" + id - return id - - # ZFCP LUNs are usually entered as 16 bit, sysfs accepts only 64 bit - # (#125632), expand with zeroes if necessary - def sanitizeFCPLInput(self, lun): - if lun is None or lun == "": - return None - lun = lun.lower() - if lun[:2] == "0x": - lun = lun[2:] - lun = "0x" + "0" * (4 - len(lun)) + lun - lun = lun + "0" * (16 - len(lun) + 2) - return lun - - def _hextest(self, hex): - try: - int(hex, 16) - return True - except TypeError: - return False - - def checkValidDevice(self, id): - if id is None or id == "": - return False - if len(id) != 8: # p.e. 0.0.0600 - return False - if id[0] not in string.digits or id[2] not in string.digits: - return False - if id[1] != "." or id[3] != ".": - return False - return self._hextest(id[4:]) - - def checkValid64BitHex(self, hex): - if hex is None or hex == "": - return False - if len(hex) != 18: - return False - return self._hextest(hex) - checkValidWWPN = checkValidFCPLun = checkValid64BitHex - - def onlineDevice(self): - online = "%s/%s/online" %(zfcpsysfs, self.devnum) - portadd = "%s/%s/port_add" %(zfcpsysfs, self.devnum) - portdir = "%s/%s/%s" %(zfcpsysfs, self.devnum, self.wwpn) - unitadd = "%s/unit_add" %(portdir) - unitdir = "%s/%s" %(portdir, self.fcplun) - failed = "%s/failed" %(unitdir) - - if not os.path.exists(online): - log.info("Freeing zFCP device %s" % (self.devnum,)) - util.run_program(["zfcp_cio_free", "-d", self.devnum]) - - if not os.path.exists(online): - raise ValueError, _( - "zFCP device %s not found, not even in device ignore list." - %(self.devnum,)) - - try: - f = open(online, "r") - devonline = f.readline().strip() - f.close() - if devonline != "1": - loggedWriteLineToFile(online, "1") - except IOError as e: - raise ValueError, _("Could not set zFCP device %(devnum)s " - "online (%(e)s).") \ - % {'devnum': self.devnum, 'e': e} - - if not os.path.exists(portdir): - if os.path.exists(portadd): - # older zfcp sysfs interface - try: - loggedWriteLineToFile(portadd, self.wwpn) - udev_settle() - except IOError as e: - raise ValueError, _("Could not add WWPN %(wwpn)s to zFCP " - "device %(devnum)s (%(e)s).") \ - % {'wwpn': self.wwpn, - 'devnum': self.devnum, - 'e': e} - else: - # newer zfcp sysfs interface with auto port scan - raise ValueError, _("WWPN %(wwpn)s not found at zFCP device " - "%(devnum)s.") % {'wwpn': self.wwpn, - 'devnum': self.devnum} - else: - if os.path.exists(portadd): - # older zfcp sysfs interface - log.info("WWPN %(wwpn)s at zFCP device %(devnum)s already " - "there." % {'wwpn': self.wwpn, - 'devnum': self.devnum}) - - if not os.path.exists(unitdir): - try: - loggedWriteLineToFile(unitadd, self.fcplun) - udev_settle() - except IOError as e: - raise ValueError, _("Could not add LUN %(fcplun)s to WWPN " - "%(wwpn)s on zFCP device %(devnum)s " - "(%(e)s).") \ - % {'fcplun': self.fcplun, 'wwpn': self.wwpn, - 'devnum': self.devnum, 'e': e} - else: - raise ValueError, _("LUN %(fcplun)s at WWPN %(wwpn)s on zFCP " - "device %(devnum)s already configured.") \ - % {'fcplun': self.fcplun, - 'wwpn': self.wwpn, - 'devnum': self.devnum} - - fail = "0" - try: - f = open(failed, "r") - fail = f.readline().strip() - f.close() - except IOError as e: - raise ValueError, _("Could not read failed attribute of LUN " - "%(fcplun)s at WWPN %(wwpn)s on zFCP device " - "%(devnum)s (%(e)s).") \ - % {'fcplun': self.fcplun, - 'wwpn': self.wwpn, - 'devnum': self.devnum, - 'e': e} - if fail != "0": - self.offlineDevice() - raise ValueError, _("Failed LUN %(fcplun)s at WWPN %(wwpn)s on " - "zFCP device %(devnum)s removed again.") \ - % {'fcplun': self.fcplun, - 'wwpn': self.wwpn, - 'devnum': self.devnum} - - return True - - def offlineSCSIDevice(self): - f = open("/proc/scsi/scsi", "r") - lines = f.readlines() - f.close() - # alternatively iterate over /sys/bus/scsi/devices/*:0:*:*/ - - for line in lines: - if not line.startswith("Host"): - continue - scsihost = string.split(line) - host = scsihost[1] - channel = "0" - id = scsihost[5] - lun = scsihost[7] - scsidev = "%s:%s:%s:%s" % (host[4:], channel, id, lun) - fcpsysfs = "%s/%s" % (scsidevsysfs, scsidev) - scsidel = "%s/%s/delete" % (scsidevsysfs, scsidev) - - f = open("%s/hba_id" %(fcpsysfs), "r") - fcphbasysfs = f.readline().strip() - f.close() - f = open("%s/wwpn" %(fcpsysfs), "r") - fcpwwpnsysfs = f.readline().strip() - f.close() - f = open("%s/fcp_lun" %(fcpsysfs), "r") - fcplunsysfs = f.readline().strip() - f.close() - - if fcphbasysfs == self.devnum \ - and fcpwwpnsysfs == self.wwpn \ - and fcplunsysfs == self.fcplun: - loggedWriteLineToFile(scsidel, "1") - udev_settle() - return - - log.warn("no scsi device found to delete for zfcp %s %s %s" - %(self.devnum, self.wwpn, self.fcplun)) - - def offlineDevice(self): - offline = "%s/%s/online" %(zfcpsysfs, self.devnum) - portadd = "%s/%s/port_add" %(zfcpsysfs, self.devnum) - portremove = "%s/%s/port_remove" %(zfcpsysfs, self.devnum) - unitremove = "%s/%s/%s/unit_remove" %(zfcpsysfs, self.devnum, self.wwpn) - portdir = "%s/%s/%s" %(zfcpsysfs, self.devnum, self.wwpn) - devdir = "%s/%s" %(zfcpsysfs, self.devnum) - - try: - self.offlineSCSIDevice() - except IOError as e: - raise ValueError, _("Could not correctly delete SCSI device of " - "zFCP %(devnum)s %(wwpn)s %(fcplun)s " - "(%(e)s).") \ - % {'devnum': self.devnum, 'wwpn': self.wwpn, - 'fcplun': self.fcplun, 'e': e} - - try: - loggedWriteLineToFile(unitremove, self.fcplun) - except IOError as e: - raise ValueError, _("Could not remove LUN %(fcplun)s at WWPN " - "%(wwpn)s on zFCP device %(devnum)s " - "(%(e)s).") \ - % {'fcplun': self.fcplun, 'wwpn': self.wwpn, - 'devnum': self.devnum, 'e': e} - - if os.path.exists(portadd): - # only try to remove ports with older zfcp sysfs interface - for lun in os.listdir(portdir): - if lun.startswith("0x") and \ - os.path.isdir(os.path.join(portdir, lun)): - log.info("Not removing WWPN %s at zFCP device %s since port still has other LUNs, e.g. %s." - %(self.wwpn, self.devnum, lun)) - return True - - try: - loggedWriteLineToFile(portremove, self.wwpn) - except IOError as e: - raise ValueError, _("Could not remove WWPN %(wwpn)s on zFCP " - "device %(devnum)s (%(e)s).") \ - % {'wwpn': self.wwpn, - 'devnum': self.devnum, 'e': e} - - if os.path.exists(portadd): - # older zfcp sysfs interface - for port in os.listdir(devdir): - if port.startswith("0x") and \ - os.path.isdir(os.path.join(devdir, port)): - log.info("Not setting zFCP device %s offline since it still has other ports, e.g. %s." - %(self.devnum, port)) - return True - else: - # newer zfcp sysfs interface with auto port scan - import glob - luns = glob.glob("%s/0x????????????????/0x????????????????" - %(devdir,)) - if len(luns) != 0: - log.info("Not setting zFCP device %s offline since it still has other LUNs, e.g. %s." - %(self.devnum, luns[0])) - return True - - try: - loggedWriteLineToFile(offline, "0") - except IOError as e: - raise ValueError, _("Could not set zFCP device %(devnum)s " - "offline (%(e)s).") \ - % {'devnum': self.devnum, 'e': e} - - return True - -class ZFCP: - """ ZFCP utility class. - - This class will automatically online to ZFCP drives configured in - /tmp/fcpconfig when the startup() method gets called. It can also be - used to manually configure ZFCP devices through the addFCP() method. - - As this class needs to make sure that /tmp/fcpconfig configured - drives are only onlined once and as it keeps a global list of all ZFCP - devices it is implemented as a Singleton. - """ - - def __init__(self): - self.intf = None - self.fcpdevs = set() - self.hasReadConfig = False - self.down = True - - # So that users can write zfcp() to get the singleton instance - def __call__(self): - return self - - def readConfig(self): - try: - f = open(zfcpconf, "r") - except IOError: - log.info("no %s; not configuring zfcp" % (zfcpconf,)) - return - - lines = map(lambda x: x.strip().lower(), f.readlines()) - f.close() - - for line in lines: - if line.startswith("#") or line == '': - continue - - fields = line.split() - - if len(fields) == 3: - devnum = fields[0] - wwpn = fields[1] - fcplun = fields[2] - elif len(fields) == 5: - # support old syntax of: - # devno scsiid wwpn scsilun fcplun - devnum = fields[0] - wwpn = fields[2] - fcplun = fields[4] - else: - log.warn("Invalid line found in %s: %s" % (zfcpconf, line,)) - continue - - try: - self.addFCP(devnum, wwpn, fcplun) - except ValueError as e: - if self.intf: - self.intf.messageWindow(_("Error"), str(e)) - else: - log.warning(str(e)) - - def addFCP(self, devnum, wwpn, fcplun): - d = ZFCPDevice(devnum, wwpn, fcplun) - if d.onlineDevice(): - self.fcpdevs.add(d) - - def shutdown(self): - if self.down: - return - self.down = True - if len(self.fcpdevs) == 0: - return - for d in self.fcpdevs: - try: - d.offlineDevice() - except ValueError as e: - log.warn(str(e)) - - def startup(self): - if not self.down: - return - self.down = False - if not self.hasReadConfig: - self.readConfig() - self.hasReadConfig = True - # readConfig calls addFCP which calls onlineDevice already - return - - if len(self.fcpdevs) == 0: - return - for d in self.fcpdevs: - try: - d.onlineDevice() - except ValueError as e: - log.warn(str(e)) - - def write(self): - if len(self.fcpdevs) == 0: - return - f = open(ROOT_PATH + zfcpconf, "w") - for d in self.fcpdevs: - f.write("%s\n" %(d,)) - f.close() - - f = open(ROOT_PATH + "/etc/modprobe.conf", "a") - f.write("alias scsi_hostadapter zfcp\n") - f.close() - -# Create ZFCP singleton -ZFCP = ZFCP() - -# vim:tw=78:ts=4:et:sw=4 diff --git a/pyanaconda/timezone.py b/pyanaconda/timezone.py index 01edeb37c..ff9e3f377 100644 --- a/pyanaconda/timezone.py +++ b/pyanaconda/timezone.py @@ -30,7 +30,7 @@ from collections import OrderedDict from pyanaconda import localization from pyanaconda import iutil -from pyanaconda.storage import arch +from blivet import arch import logging log = logging.getLogger("anaconda") diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py index 94faef082..a590b6f58 100644 --- a/pyanaconda/ui/gui/spokes/custom.py +++ b/pyanaconda/ui/gui/spokes/custom.py @@ -49,29 +49,29 @@ from pykickstart.constants import * from pyanaconda.product import productName, productVersion from pyanaconda.threads import threadMgr -from pyanaconda.storage.formats import device_formats -from pyanaconda.storage.formats import getFormat -from pyanaconda.storage.formats.fs import FS -from pyanaconda.storage.size import Size -from pyanaconda.storage import Root -from pyanaconda.storage import DEVICE_TYPE_LVM -from pyanaconda.storage import DEVICE_TYPE_BTRFS -from pyanaconda.storage import DEVICE_TYPE_PARTITION -from pyanaconda.storage import DEVICE_TYPE_MD -from pyanaconda.storage import DEVICE_TYPE_DISK -from pyanaconda.storage import getDeviceType -from pyanaconda.storage import getRAIDLevel -from pyanaconda.storage import findExistingInstallations -from pyanaconda.storage.partitioning import doPartitioning -from pyanaconda.storage.partitioning import doAutoPartition -from pyanaconda.storage.errors import StorageError -from pyanaconda.storage.errors import NoDisksError -from pyanaconda.storage.errors import NotEnoughFreeSpaceError -from pyanaconda.storage.errors import ErrorRecoveryFailure -from pyanaconda.storage.errors import CryptoError -from pyanaconda.storage.errors import MDRaidError -from pyanaconda.storage.devicelibs import mdraid -from pyanaconda.storage.devices import LUKSDevice +from blivet.formats import device_formats +from blivet.formats import getFormat +from blivet.formats.fs import FS +from blivet.size import Size +from blivet import Root +from blivet import DEVICE_TYPE_LVM +from blivet import DEVICE_TYPE_BTRFS +from blivet import DEVICE_TYPE_PARTITION +from blivet import DEVICE_TYPE_MD +from blivet import DEVICE_TYPE_DISK +from blivet import getDeviceType +from blivet import getRAIDLevel +from blivet import findExistingInstallations +from blivet.partitioning import doPartitioning +from blivet.partitioning import doAutoPartition +from blivet.errors import StorageError +from blivet.errors import NoDisksError +from blivet.errors import NotEnoughFreeSpaceError +from blivet.errors import ErrorRecoveryFailure +from blivet.errors import CryptoError +from blivet.errors import MDRaidError +from blivet.devicelibs import mdraid +from blivet.devices import LUKSDevice from pyanaconda.ui import communication from pyanaconda.ui.gui import GUIObject diff --git a/pyanaconda/ui/gui/spokes/lib/cart.py b/pyanaconda/ui/gui/spokes/lib/cart.py index 76d36494f..bdc4fafcc 100644 --- a/pyanaconda/ui/gui/spokes/lib/cart.py +++ b/pyanaconda/ui/gui/spokes/lib/cart.py @@ -23,7 +23,7 @@ from gi.repository import Gtk from pyanaconda.ui.gui import GUIObject -from pyanaconda.storage.size import Size +from blivet.size import Size import gettext diff --git a/pyanaconda/ui/gui/spokes/lib/resize.py b/pyanaconda/ui/gui/spokes/lib/resize.py index 0753331ff..ff6e0141e 100644 --- a/pyanaconda/ui/gui/spokes/lib/resize.py +++ b/pyanaconda/ui/gui/spokes/lib/resize.py @@ -24,7 +24,7 @@ from __future__ import division from gi.repository import Gtk from pyanaconda.ui.gui import GUIObject -from pyanaconda.storage.size import Size +from blivet.size import Size import gettext diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py index a91d30242..58ebf857f 100644 --- a/pyanaconda/ui/gui/spokes/storage.py +++ b/pyanaconda/ui/gui/spokes/storage.py @@ -51,9 +51,9 @@ from pyanaconda.ui.gui.categories.storage import StorageCategory from pyanaconda.ui.gui.utils import enlightbox, gtk_call_once, gtk_thread_wait from pyanaconda.kickstart import doKickstartStorage -from pyanaconda.storage.size import Size -from pyanaconda.storage.errors import StorageError -from pyanaconda.storage.platform import platform +from blivet.size import Size +from blivet.errors import StorageError +from blivet.platform import platform from pyanaconda.threads import threadMgr, AnacondaThread from pyanaconda.product import productName from pyanaconda.flags import flags diff --git a/pyanaconda/ui/gui/tools/run-spoke.py b/pyanaconda/ui/gui/tools/run-spoke.py index 92173e747..5f85907cc 100755 --- a/pyanaconda/ui/gui/tools/run-spoke.py +++ b/pyanaconda/ui/gui/tools/run-spoke.py @@ -27,7 +27,7 @@ from pyanaconda import anaconda_log anaconda_log.init() from pyanaconda.installclass import DefaultInstall -from pyanaconda.storage import Storage +from blivet import Blivet from pyanaconda.threads import initThreading from pyanaconda.packaging.yumpayload import YumPayload from pykickstart.version import makeVersion @@ -89,7 +89,7 @@ if not spokeClass: print "Running %s %s from %s" % (spokeText, spokeClass, spokeModule) ksdata = makeVersion() -storage = Storage(data=ksdata) +storage = Blivet(ksdata=ksdata) storage.reset() instclass = DefaultInstall() diff --git a/pyanaconda/ui/lib/space.py b/pyanaconda/ui/lib/space.py index c7601d371..44d1e3c78 100644 --- a/pyanaconda/ui/lib/space.py +++ b/pyanaconda/ui/lib/space.py @@ -19,7 +19,7 @@ # Red Hat Author(s): David Lehman <dlehman@redhat.com> # -from pyanaconda.storage.size import Size +from blivet.size import Size import gettext diff --git a/pyanaconda/ui/tui/spokes/storage.py b/pyanaconda/ui/tui/spokes/storage.py index 5314438b3..292ffc29c 100644 --- a/pyanaconda/ui/tui/spokes/storage.py +++ b/pyanaconda/ui/tui/spokes/storage.py @@ -26,8 +26,8 @@ from pyanaconda.ui.tui.spokes import NormalTUISpoke from pyanaconda.ui.tui.simpleline import TextWidget, CheckboxWidget from pykickstart.constants import AUTOPART_TYPE_LVM -from pyanaconda.storage.size import Size -from pyanaconda.storage.errors import StorageError +from blivet.size import Size +from blivet.errors import StorageError from pyanaconda.flags import flags from pyanaconda.kickstart import doKickstartStorage from pyanaconda.threads import threadMgr, AnacondaThread diff --git a/pyanaconda/ui/tui/tools/run-text-spoke.py b/pyanaconda/ui/tui/tools/run-text-spoke.py index bc132f885..09c91a322 100755 --- a/pyanaconda/ui/tui/tools/run-text-spoke.py +++ b/pyanaconda/ui/tui/tools/run-text-spoke.py @@ -13,7 +13,7 @@ from pyanaconda import anaconda_log anaconda_log.init() from pyanaconda.installclass import DefaultInstall -from pyanaconda.storage import Storage +from blivet import Blivet from pyanaconda.threads import initThreading from pyanaconda.packaging.yumpayload import YumPayload from pykickstart.version import makeVersion @@ -79,7 +79,7 @@ if not spokeClass: print "Running %s %s from %s" % (spokeText, spokeClass, spokeModule) ksdata = makeVersion() -storage = Storage(data=ksdata) +storage = Blivet(ksdata=ksdata) storage.reset() instclass = DefaultInstall() app = App("TEST HARNESS", yes_or_no_question = YesNoDialog) diff --git a/scripts/anaconda-cleanup b/scripts/anaconda-cleanup index b8a348737..1682f591b 100755 --- a/scripts/anaconda-cleanup +++ b/scripts/anaconda-cleanup @@ -40,9 +40,9 @@ pyanaconda.anaconda_log.init() from pyanaconda import iutil -from pyanaconda.storage import StorageDiscoveryConfig -from pyanaconda.storage.devicetree import DeviceTree -from pyanaconda.storage import devicelibs +from blivet import StorageDiscoveryConfig +from blivet.devicetree import DeviceTree +from blivet import devicelibs storage_config = StorageDiscoveryConfig() diff --git a/tests/Makefile.am b/tests/Makefile.am index df244c9ce..cb4c70115 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -17,6 +17,6 @@ # # Author: David Cantrell <dcantrell@redhat.com> -SUBDIRS = mock kickstart_test storage_test regex pyanaconda_test pylint logpicker_test +SUBDIRS = mock kickstart_test regex pyanaconda_test pylint logpicker_test MAINTAINERCLEANFILES = Makefile.in diff --git a/tests/storage_test/Makefile.am b/tests/storage_test/Makefile.am deleted file mode 100644 index 23565abb8..000000000 --- a/tests/storage_test/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -# tests/storage/Makefile.am for anaconda -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# Author: David Cantrell <dcantrell@redhat.com> - -SUBDIRS = devicelibs_test - -EXTRA_DIST = *.py - -MAINTAINERCLEANFILES = Makefile.in diff --git a/tests/storage_test/action_test.py b/tests/storage_test/action_test.py deleted file mode 100644 index b07a44b23..000000000 --- a/tests/storage_test/action_test.py +++ /dev/null @@ -1,926 +0,0 @@ -#!/usr/bin/python - -import unittest -from mock import Mock -from mock import TestCase - -from storagetestcase import StorageTestCase -import pyanaconda.storage as storage -from pyanaconda.storage.formats import getFormat - -# device classes for brevity's sake -- later on, that is -from pyanaconda.storage.devices import StorageDevice -from pyanaconda.storage.devices import DiskDevice -from pyanaconda.storage.devices import PartitionDevice -from pyanaconda.storage.devices import MDRaidArrayDevice -from pyanaconda.storage.devices import DMDevice -from pyanaconda.storage.devices import LUKSDevice -from pyanaconda.storage.devices import LVMVolumeGroupDevice -from pyanaconda.storage.devices import LVMLogicalVolumeDevice -from pyanaconda.storage.devices import FileDevice - -# action classes -from pyanaconda.storage.deviceaction import ActionCreateDevice -from pyanaconda.storage.deviceaction import ActionResizeDevice -from pyanaconda.storage.deviceaction import ActionDestroyDevice -from pyanaconda.storage.deviceaction import ActionCreateFormat -from pyanaconda.storage.deviceaction import ActionResizeFormat -from pyanaconda.storage.deviceaction import ActionMigrateFormat -from pyanaconda.storage.deviceaction import ActionDestroyFormat - -""" DeviceActionTestSuite """ - -class DeviceActionTestCase(StorageTestCase): - def setUp(self): - """ Create something like a preexisting autopart on two disks (sda,sdb). - - The other two disks (sdc,sdd) are left for individual tests to use. - """ - self.setUpAnaconda() - - for name in ["sda", "sdb", "sdc", "sdd"]: - disk = self.newDevice(device_class=DiskDevice, - name=name, size=100000) - disk.format = self.newFormat("disklabel", path=disk.path, - exists=True) - self.storage.devicetree._addDevice(disk) - - # create a layout similar to autopart as a starting point - sda = self.storage.devicetree.getDeviceByName("sda") - sdb = self.storage.devicetree.getDeviceByName("sdb") - - sda1 = self.newDevice(device_class=PartitionDevice, - exists=True, name="sda1", parents=[sda], size=500) - sda1.format = self.newFormat("ext4", mountpoint="/boot", - device_instance=sda1, - device=sda1.path, exists=True) - self.storage.devicetree._addDevice(sda1) - - sda2 = self.newDevice(device_class=PartitionDevice, - size=99500, name="sda2", parents=[sda], exists=True) - sda2.format = self.newFormat("lvmpv", device=sda2.path, exists=True) - self.storage.devicetree._addDevice(sda2) - - sdb1 = self.newDevice(device_class=PartitionDevice, - size=99999, name="sdb1", parents=[sdb], exists=True) - sdb1.format = self.newFormat("lvmpv", device=sdb1.path, exists=True) - self.storage.devicetree._addDevice(sdb1) - - vg = self.newDevice(device_class=LVMVolumeGroupDevice, - name="VolGroup", parents=[sda2, sdb1], - exists=True) - self.storage.devicetree._addDevice(vg) - - lv_root = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="lv_root", vgdev=vg, size=160000, - exists=True) - lv_root.format = self.newFormat("ext4", mountpoint="/", - device_instance=lv_root, - device=lv_root.path, exists=True) - self.storage.devicetree._addDevice(lv_root) - - lv_swap = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="lv_swap", vgdev=vg, size=4000, - exists=True) - lv_swap.format = self.newFormat("swap", device=lv_swap.path, - device_instance=lv_swap, - exists=True) - self.storage.devicetree._addDevice(lv_swap) - - def testActions(self, *args, **kwargs): - """ Verify correct management of actions. - - - action creation/registration/cancellation - - ActionCreateDevice adds device to tree - - ActionDestroyDevice removes device from tree - - ActionCreateFormat sets device.format in tree - - ActionDestroyFormat unsets device.format in tree - - cancelled action's registration side-effects reversed - - failure to register destruction of non-leaf device - - failure to register creation of device already in tree? - - failure to register destruction of device not in tree? - - - action pruning - - non-existent-device create..destroy cycles removed - - all actions on this device should get removed - - all actions pruned from to-be-destroyed devices - - resize, format, migrate, &c - - redundant resize/migrate/format actions pruned - - last one registered stays - - - action sorting - - destroy..resize..migrate..create - - creation - - leaves-last, including formatting - - destruction - - leaves-first - """ - devicetree = self.storage.devicetree - - # clear the disks - self.destroyAllDevices() - self.assertEqual(devicetree.getDevicesByType("lvmlv"), []) - self.assertEqual(devicetree.getDevicesByType("lvmvg"), []) - self.assertEqual(devicetree.getDevicesByType("partition"), []) - - sda = devicetree.getDeviceByName("sda") - self.assertNotEqual(sda, None, "failed to find disk 'sda'") - - sda1 = self.newDevice(device_class=PartitionDevice, - name="sda1", size=500, parents=[sda]) - self.scheduleCreateDevice(device=sda1) - - sda2 = self.newDevice(device_class=PartitionDevice, - name="sda2", size=100000, parents=[sda]) - self.scheduleCreateDevice(device=sda2) - format = self.newFormat("lvmpv", device=sda2.path) - self.scheduleCreateFormat(device=sda2, format=format) - - vg = self.newDevice(device_class=LVMVolumeGroupDevice, - name="vg", parents=[sda2]) - self.scheduleCreateDevice(device=vg) - - lv_root = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="lv_root", vgdev=vg, size=60000) - self.scheduleCreateDevice(device=lv_root) - format = self.newFormat("ext4", device=lv_root.path, mountpoint="/") - self.scheduleCreateFormat(device=lv_root, format=format) - - lv_swap = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="lv_swap", vgdev=vg, size=4000) - self.scheduleCreateDevice(device=lv_swap) - format = self.newFormat("swap", device=lv_swap.path) - self.scheduleCreateFormat(device=lv_swap, format=format) - - sda3 = self.newDevice(device_class=PartitionDevice, - name="sda3", parents=[sda], size=40000) - self.scheduleCreateDevice(device=sda3) - format = self.newFormat("mdmember", device=sda3.path) - self.scheduleCreateFormat(device=sda3, format=format) - - sdb = devicetree.getDeviceByName("sdb") - self.assertNotEqual(sdb, None, "failed to find disk 'sdb'") - - sdb1 = self.newDevice(device_class=PartitionDevice, - name="sdb1", parents=[sdb], size=40000) - self.scheduleCreateDevice(device=sdb1) - format = self.newFormat("mdmember", device=sdb1.path,) - self.scheduleCreateFormat(device=sdb1, format=format) - - md0 = self.newDevice(device_class=MDRaidArrayDevice, - name="md0", level="raid0", minor=0, size=80000, - memberDevices=2, totalDevices=2, - parents=[sdb1, sda3]) - self.scheduleCreateDevice(device=md0) - - format = self.newFormat("ext4", device=md0.path, mountpoint="/home") - self.scheduleCreateFormat(device=md0, format=format) - - format = self.newFormat("ext4", mountpoint="/boot", device=sda1.path) - self.scheduleCreateFormat(device=sda1, format=format) - - def testActionCreation(self, *args, **kwargs): - """ Verify correct operation of action class constructors. """ - # instantiation of device resize action for non-existent device should - # fail - # XXX resizable depends on existence, so this is covered implicitly - sdd = self.storage.devicetree.getDeviceByName("sdd") - p = self.newDevice(device_class=PartitionDevice, - name="sdd1", size=32768, parents=[sdd]) - self.failUnlessRaises(ValueError, - storage.deviceaction.ActionResizeDevice, - p, - p.size + 7232) - - # instantiation of device resize action for non-resizable device - # should fail - vg = self.storage.devicetree.getDeviceByName("VolGroup") - self.assertNotEqual(vg, None) - self.failUnlessRaises(ValueError, - storage.deviceaction.ActionResizeDevice, - vg, - vg.size + 32) - - # instantiation of format resize action for non-resizable format type - # should fail - lv_swap = self.storage.devicetree.getDeviceByName("VolGroup-lv_swap") - self.assertNotEqual(lv_swap, None) - self.failUnlessRaises(ValueError, - storage.deviceaction.ActionResizeFormat, - lv_swap, - lv_swap.size + 32) - - # instantiation of format resize action for non-existent format - # should fail - lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root") - self.assertNotEqual(lv_root, None) - lv_root.format.exists = False - self.failUnlessRaises(ValueError, - storage.deviceaction.ActionResizeFormat, - lv_root, - lv_root.size - 1000) - lv_root.format.exists = True - - # instantiation of format migrate action for non-migratable format - # type should fail - lv_swap = self.storage.devicetree.getDeviceByName("VolGroup-lv_swap") - self.assertNotEqual(lv_swap, None) - self.assertEqual(lv_swap.exists, True) - self.failUnlessRaises(ValueError, - storage.deviceaction.ActionMigrateFormat, - lv_swap) - - # instantiation of format migrate for non-existent format should fail - lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root") - self.assertNotEqual(lv_root, None) - orig_format = lv_root.format - lv_root.format = getFormat("ext3", device=lv_root.path) - self.failUnlessRaises(ValueError, - storage.deviceaction.ActionMigrateFormat, - lv_root) - lv_root.format = orig_format - - # instantiation of device create action for existing device should - # fail - lv_swap = self.storage.devicetree.getDeviceByName("VolGroup-lv_swap") - self.assertNotEqual(lv_swap, None) - self.assertEqual(lv_swap.exists, True) - self.failUnlessRaises(ValueError, - storage.deviceaction.ActionCreateDevice, - lv_swap) - - # instantiation of format destroy action for device causes device's - # format attribute to be a DeviceFormat instance - lv_swap = self.storage.devicetree.getDeviceByName("VolGroup-lv_swap") - self.assertNotEqual(lv_swap, None) - orig_format = lv_swap.format - self.assertEqual(lv_swap.format.type, "swap") - a = storage.deviceaction.ActionDestroyFormat(lv_swap) - self.assertEqual(lv_swap.format.type, None) - - # instantiation of format create action for device causes new format - # to be accessible via device's format attribute - new_format = getFormat("vfat", device=lv_swap.path) - a = storage.deviceaction.ActionCreateFormat(lv_swap, new_format) - self.assertEqual(lv_swap.format, new_format) - lv_swap.format = orig_format - - def testActionRegistration(self, *args, **kwargs): - """ Verify correct operation of action registration and cancelling. """ - # self.setUp has just been run, so we should have something like - # a preexisting autopart config in the devicetree. - - # registering a destroy action for a non-leaf device should fail - vg = self.storage.devicetree.getDeviceByName("VolGroup") - self.assertNotEqual(vg, None) - self.assertEqual(vg.isleaf, False) - a = storage.deviceaction.ActionDestroyDevice(vg) - self.failUnlessRaises(ValueError, - self.storage.devicetree.registerAction, - a) - - # registering any action other than create for a device that's not in - # the devicetree should fail - sdc = self.storage.devicetree.getDeviceByName("sdc") - self.assertNotEqual(sdc, None) - sdc1 = self.newDevice(device_class=PartitionDevice, - name="sdc1", size=100000, parents=[sdc], - exists=True) - - sdc1_format = self.newFormat("ext2", device=sdc1.path, mountpoint="/") - create_sdc1_format = ActionCreateFormat(sdc1, sdc1_format) - self.failUnlessRaises(storage.errors.DeviceTreeError, - self.storage.devicetree.registerAction, - create_sdc1_format) - - sdc1_format.exists = True - - migrate_sdc1 = ActionMigrateFormat(sdc1) - self.failUnlessRaises(storage.errors.DeviceTreeError, - self.storage.devicetree.registerAction, - migrate_sdc1) - migrate_sdc1.cancel() - - resize_sdc1_format = ActionResizeFormat(sdc1, sdc1.size - 10000) - self.failUnlessRaises(storage.errors.DeviceTreeError, - self.storage.devicetree.registerAction, - resize_sdc1_format) - - resize_sdc1 = ActionResizeDevice(sdc1, sdc1.size - 10000) - self.failUnlessRaises(storage.errors.DeviceTreeError, - self.storage.devicetree.registerAction, - resize_sdc1) - - resize_sdc1.cancel() - resize_sdc1_format.cancel() - - destroy_sdc1_format = ActionDestroyFormat(sdc1) - self.failUnlessRaises(storage.errors.DeviceTreeError, - self.storage.devicetree.registerAction, - destroy_sdc1_format) - - - destroy_sdc1 = ActionDestroyDevice(sdc1) - self.failUnlessRaises(storage.errors.DeviceTreeError, - self.storage.devicetree.registerAction, - resize_sdc1) - - # registering a device destroy action should cause the device to be - # removed from the devicetree - lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root") - self.assertNotEqual(lv_root, None) - a = ActionDestroyDevice(lv_root) - self.storage.devicetree.registerAction(a) - lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root") - self.assertEqual(lv_root, None) - self.storage.devicetree.cancelAction(a) - - # registering a device create action should cause the device to be - # added to the devicetree - sdd = self.storage.devicetree.getDeviceByName("sdd") - self.assertNotEqual(sdd, None) - sdd1 = self.storage.devicetree.getDeviceByName("sdd1") - self.assertEqual(sdd1, None) - sdd1 = self.newDevice(device_class=PartitionDevice, - name="sdd1", size=100000, parents=[sdd]) - a = ActionCreateDevice(sdd1) - self.storage.devicetree.registerAction(a) - sdd1 = self.storage.devicetree.getDeviceByName("sdd1") - self.assertNotEqual(sdd1, None) - - def testActionObsoletes(self, *args, **kwargs): - """ Verify correct operation of DeviceAction.obsoletes. """ - self.destroyAllDevices(disks=["sdc"]) - sdc = self.storage.devicetree.getDeviceByName("sdc") - self.assertNotEqual(sdc, None) - - sdc1 = self.newDevice(device_class=PartitionDevice, - name="sdc1", parents=[sdc], size=40000) - - # ActionCreateDevice - # - # - obsoletes other ActionCreateDevice instances w/ lower id and same - # device - create_device_1 = ActionCreateDevice(sdc1) - create_device_2 = ActionCreateDevice(sdc1) - self.assertEqual(create_device_2.obsoletes(create_device_1), True) - self.assertEqual(create_device_1.obsoletes(create_device_2), False) - - # ActionCreateFormat - # - # - obsoletes other ActionCreateFormat instances w/ lower id and same - # device - format_1 = self.newFormat("ext3", mountpoint="/home", device=sdc1.path) - format_2 = self.newFormat("ext3", mountpoint="/opt", device=sdc1.path) - create_format_1 = ActionCreateFormat(sdc1, format_1) - create_format_2 = ActionCreateFormat(sdc1, format_2) - self.assertEqual(create_format_2.obsoletes(create_format_1), True) - self.assertEqual(create_format_1.obsoletes(create_format_2), False) - - # ActionMigrateFormat - # - # - obsoletes other ActionMigrateFormat instances w/ lower id and same - # device - sdc1.format = self.newFormat("ext2", mountpoint="/", device=sdc1.path, - device_instance=sdc1, - exists=True) - migrate_1 = ActionMigrateFormat(sdc1) - migrate_2 = ActionMigrateFormat(sdc1) - self.assertEqual(migrate_2.obsoletes(migrate_1), True) - self.assertEqual(migrate_1.obsoletes(migrate_2), False) - - # ActionResizeFormat - # - # - obsoletes other ActionResizeFormat instances w/ lower id and same - # device - resize_format_1 = ActionResizeFormat(sdc1, sdc1.size - 1000) - resize_format_2 = ActionResizeFormat(sdc1, sdc1.size - 5000) - self.assertEqual(resize_format_2.obsoletes(resize_format_1), True) - self.assertEqual(resize_format_1.obsoletes(resize_format_2), False) - - # ActionCreateFormat - # - # - obsoletes migrate, resize format actions w/ lower id on same device - new_format = self.newFormat("ext4", mountpoint="/foo", device=sdc1.path) - create_format_3 = ActionCreateFormat(sdc1, new_format) - self.assertEqual(create_format_3.obsoletes(resize_format_1), True) - self.assertEqual(create_format_3.obsoletes(resize_format_2), True) - self.assertEqual(create_format_3.obsoletes(migrate_1), True) - self.assertEqual(create_format_3.obsoletes(migrate_2), True) - - # ActionResizeDevice - # - # - obsoletes other ActionResizeDevice instances w/ lower id and same - # device - sdc1.exists = True - sdc1.format.exists = True - resize_device_1 = ActionResizeDevice(sdc1, sdc1.size + 10000) - resize_device_2 = ActionResizeDevice(sdc1, sdc1.size - 10000) - self.assertEqual(resize_device_2.obsoletes(resize_device_1), True) - self.assertEqual(resize_device_1.obsoletes(resize_device_2), False) - sdc1.exists = False - sdc1.format.exists = False - - # ActionDestroyFormat - # - # - obsoletes all format actions w/ lower id on same device (including - # self if format does not exist) - destroy_format_1 = ActionDestroyFormat(sdc1) - self.assertEqual(destroy_format_1.obsoletes(create_format_1), True) - self.assertEqual(destroy_format_1.obsoletes(migrate_2), True) - self.assertEqual(destroy_format_1.obsoletes(resize_format_1), True) - self.assertEqual(destroy_format_1.obsoletes(destroy_format_1), True) - - # ActionDestroyDevice - # - # - obsoletes all actions w/ lower id that act on the same non-existent - # device (including self) - # sdc1 does not exist - destroy_sdc1 = ActionDestroyDevice(sdc1) - self.assertEqual(destroy_sdc1.obsoletes(create_format_2), True) - self.assertEqual(destroy_sdc1.obsoletes(migrate_1), True) - self.assertEqual(destroy_sdc1.obsoletes(resize_format_2), True) - self.assertEqual(destroy_sdc1.obsoletes(create_device_1), True) - self.assertEqual(destroy_sdc1.obsoletes(resize_device_1), True) - self.assertEqual(destroy_sdc1.obsoletes(destroy_sdc1), True) - - # ActionDestroyDevice - # - # - obsoletes all but ActionDestroyFormat actions w/ lower id on the - # same existing device - # sda1 exists - sda1 = self.storage.devicetree.getDeviceByName("sda1") - self.assertNotEqual(sda1, None) - resize_sda1_format = ActionResizeFormat(sda1, sda1.size - 50) - resize_sda1 = ActionResizeDevice(sda1, sda1.size - 50) - destroy_sda1_format = ActionDestroyFormat(sda1) - destroy_sda1 = ActionDestroyDevice(sda1) - self.assertEqual(destroy_sda1.obsoletes(resize_sda1_format), True) - self.assertEqual(destroy_sda1.obsoletes(resize_sda1), True) - self.assertEqual(destroy_sda1.obsoletes(destroy_sda1), False) - self.assertEqual(destroy_sda1.obsoletes(destroy_sda1_format), False) - - def testActionPruning(self, *args, **kwargs): - """ Verify correct functioning of action pruning. """ - self.destroyAllDevices() - - sda = self.storage.devicetree.getDeviceByName("sda") - self.assertNotEqual(sda, None, "failed to find disk 'sda'") - - sda1 = self.newDevice(device_class=PartitionDevice, - name="sda1", size=500, parents=[sda]) - self.scheduleCreateDevice(device=sda1) - - sda2 = self.newDevice(device_class=PartitionDevice, - name="sda2", size=100000, parents=[sda]) - self.scheduleCreateDevice(device=sda2) - format = self.newFormat("lvmpv", device=sda2.path) - self.scheduleCreateFormat(device=sda2, format=format) - - vg = self.newDevice(device_class=LVMVolumeGroupDevice, - name="vg", parents=[sda2]) - self.scheduleCreateDevice(device=vg) - - lv_root = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="lv_root", vgdev=vg, size=60000) - self.scheduleCreateDevice(device=lv_root) - format = self.newFormat("ext4", device=lv_root.path, mountpoint="/") - self.scheduleCreateFormat(device=lv_root, format=format) - - lv_swap = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="lv_swap", vgdev=vg, size=4000) - self.scheduleCreateDevice(device=lv_swap) - format = self.newFormat("swap", device=lv_swap.path) - self.scheduleCreateFormat(device=lv_swap, format=format) - - # we'll soon schedule destroy actions for these members and the array, - # which will test pruning. the whole mess should reduce to nothing - sda3 = self.newDevice(device_class=PartitionDevice, - name="sda3", parents=[sda], size=40000) - self.scheduleCreateDevice(device=sda3) - format = self.newFormat("mdmember", device=sda3.path) - self.scheduleCreateFormat(device=sda3, format=format) - - sdb = self.storage.devicetree.getDeviceByName("sdb") - self.assertNotEqual(sdb, None, "failed to find disk 'sdb'") - - sdb1 = self.newDevice(device_class=PartitionDevice, - name="sdb1", parents=[sdb], size=40000) - self.scheduleCreateDevice(device=sdb1) - format = self.newFormat("mdmember", device=sdb1.path,) - self.scheduleCreateFormat(device=sdb1, format=format) - - md0 = self.newDevice(device_class=MDRaidArrayDevice, - name="md0", level="raid0", minor=0, size=80000, - memberDevices=2, totalDevices=2, - parents=[sdb1, sda3]) - self.scheduleCreateDevice(device=md0) - - format = self.newFormat("ext4", device=md0.path, mountpoint="/home") - self.scheduleCreateFormat(device=md0, format=format) - - # now destroy the md and its components - self.scheduleDestroyFormat(device=md0) - self.scheduleDestroyDevice(device=md0) - self.scheduleDestroyDevice(device=sdb1) - self.scheduleDestroyDevice(device=sda3) - - format = self.newFormat("ext4", mountpoint="/boot", device=sda1.path) - self.scheduleCreateFormat(device=sda1, format=format) - - # verify the md actions are present prior to pruning - md0_actions = self.storage.devicetree.findActions(devid=md0.id) - self.assertNotEqual(len(md0_actions), 0) - - sdb1_actions = self.storage.devicetree.findActions(devid=sdb1.id) - self.assertNotEqual(len(sdb1_actions), 0) - - sda3_actions = self.storage.devicetree.findActions(devid=sda3.id) - self.assertNotEqual(len(sda3_actions), 0) - - self.storage.devicetree.pruneActions() - - # verify the md actions are gone after pruning - md0_actions = self.storage.devicetree.findActions(devid=md0.id) - self.assertEqual(len(md0_actions), 0) - - sdb1_actions = self.storage.devicetree.findActions(devid=sdb1.id) - self.assertEqual(len(sdb1_actions), 0) - - sda3_actions = self.storage.devicetree.findActions(sda3.id) - self.assertEqual(len(sda3_actions), 0) - - def testActionDependencies(self, *args, **kwargs): - """ Verify correct functioning of action dependencies. """ - # ActionResizeDevice - # an action that shrinks a device should require the action that - # shrinks the device's format - lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root") - self.assertNotEqual(lv_root, None) - shrink_format = ActionResizeFormat(lv_root, lv_root.size - 5000) - shrink_device = ActionResizeDevice(lv_root, lv_root.size - 5000) - self.assertEqual(shrink_device.requires(shrink_format), True) - self.assertEqual(shrink_format.requires(shrink_device), False) - shrink_format.cancel() - shrink_device.cancel() - - # ActionResizeDevice - # an action that grows a format should require the action that - # grows the device - orig_size = lv_root.currentSize - grow_device = ActionResizeDevice(lv_root, orig_size + 100) - grow_format = ActionResizeFormat(lv_root, orig_size + 100) - self.assertEqual(grow_format.requires(grow_device), True) - self.assertEqual(grow_device.requires(grow_format), False) - - # create something like uncommitted autopart - self.destroyAllDevices() - sda = self.storage.devicetree.getDeviceByName("sda") - sdb = self.storage.devicetree.getDeviceByName("sdb") - sda1 = self.newDevice(device_class=PartitionDevice, - name="sda1", size=500, parents=[sda]) - sda1_format = self.newFormat("ext4", mountpoint="/boot", - device=sda1.path) - self.scheduleCreateDevice(device=sda1) - self.scheduleCreateFormat(device=sda1, format=sda1_format) - - sda2 = self.newDevice(device_class=PartitionDevice, - name="sda2", size=99500, parents=[sda]) - sda2_format = self.newFormat("lvmpv", device=sda1.path) - self.scheduleCreateDevice(device=sda2) - self.scheduleCreateFormat(device=sda2, format=sda2_format) - - sdb1 = self.newDevice(device_class=PartitionDevice, - name="sdb1", size=100000, parents=[sdb]) - sdb1_format = self.newFormat("lvmpv", device=sdb1.path) - self.scheduleCreateDevice(device=sdb1) - self.scheduleCreateFormat(device=sdb1, format=sdb1_format) - - vg = self.newDevice(device_class=LVMVolumeGroupDevice, - name="VolGroup", parents=[sda2, sdb1]) - self.scheduleCreateDevice(device=vg) - - lv_root = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="lv_root", vgdev=vg, size=160000) - self.scheduleCreateDevice(device=lv_root) - format = self.newFormat("ext4", device=lv_root.path, mountpoint="/") - self.scheduleCreateFormat(device=lv_root, format=format) - - lv_swap = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="lv_swap", vgdev=vg, size=4000) - self.scheduleCreateDevice(device=lv_swap) - format = self.newFormat("swap", device=lv_swap.path) - self.scheduleCreateFormat(device=lv_swap, format=format) - - # ActionCreateDevice - # creation of an LV should require the actions that create the VG, - # its PVs, and the devices that contain the PVs - lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root") - self.assertNotEqual(lv_root, None) - actions = self.storage.devicetree.findActions(type="create", - object="device", - device=lv_root) - self.assertEqual(len(actions), 1, - "wrong number of device create actions for lv_root: " - "%d" % len(actions)) - create_lv_action = actions[0] - - vgs = [d for d in self.storage.vgs if d.name == "VolGroup"] - self.assertNotEqual(vgs, []) - vg = vgs[0] - actions = self.storage.devicetree.findActions(type="create", - object="device", - device=vg) - self.assertEqual(len(actions), 1, - "wrong number of device create actions for VolGroup") - create_vg_action = actions[0] - - self.assertEqual(create_lv_action.requires(create_vg_action), True) - - create_pv_actions = [] - pvs = [d for d in self.storage.pvs if d in vg.pvs] - self.assertNotEqual(pvs, []) - for pv in pvs: - # include device and format create actions for each pv - actions = self.storage.devicetree.findActions(type="create", - device=pv) - self.assertEqual(len(actions), 2, - "wrong number of device create actions for " - "pv %s" % pv.name) - create_pv_actions.append(actions[0]) - - for pv_action in create_pv_actions: - self.assertEqual(create_lv_action.requires(pv_action), True) - # also check that the vg create action requires the pv actions - self.assertEqual(create_vg_action.requires(pv_action), True) - - # ActionCreateDevice - # the higher numbered partition of two that are scheduled to be - # created on a single disk should require the action that creates the - # lower numbered of the two, eg: create sda2 before creating sda3 - sdc = self.storage.devicetree.getDeviceByName("sdc") - self.assertNotEqual(sdc, None) - - sdc1 = self.newDevice(device_class=PartitionDevice, - name="sdc1", parents=[sdc], size=50000) - create_sdc1 = self.scheduleCreateDevice(device=sdc1) - self.assertEqual(isinstance(create_sdc1, ActionCreateDevice), True) - - sdc2 = self.newDevice(device_class=PartitionDevice, - name="sdc2", parents=[sdc], size=50000) - create_sdc2 = self.scheduleCreateDevice(device=sdc2) - self.assertEqual(isinstance(create_sdc2, ActionCreateDevice), True) - - self.assertEqual(create_sdc2.requires(create_sdc1), True) - self.assertEqual(create_sdc1.requires(create_sdc2), False) - - # ActionCreateDevice - # actions that create partitions on two separate disks should not - # require each other, regardless of the partitions' numbers - sda1 = self.storage.devicetree.getDeviceByName("sda1") - self.assertNotEqual(sda1, None) - actions = self.storage.devicetree.findActions(type="create", - object="device", - device=sda1) - self.assertEqual(len(actions), 1, - "wrong number of create actions found for sda1") - create_sda1 = actions[0] - self.assertEqual(create_sdc2.requires(create_sda1), False) - self.assertEqual(create_sda1.requires(create_sdc1), False) - - # ActionDestroyDevice - # an action that destroys a device containing an mdmember format - # should require the action that destroys the md array it is a - # member of if an array is defined - self.destroyAllDevices(disks=["sdc", "sdd"]) - sdc = self.storage.devicetree.getDeviceByName("sdc") - self.assertNotEqual(sdc, None) - sdd = self.storage.devicetree.getDeviceByName("sdd") - self.assertNotEqual(sdd, None) - - sdc1 = self.newDevice(device_class=PartitionDevice, - name="sdc1", parents=[sdc], size=40000) - self.scheduleCreateDevice(device=sdc1) - format = self.newFormat("mdmember", device=sdc1.path) - self.scheduleCreateFormat(device=sdc1, format=format) - - sdd1 = self.newDevice(device_class=PartitionDevice, - name="sdd1", parents=[sdd], size=40000) - self.scheduleCreateDevice(device=sdd1) - format = self.newFormat("mdmember", device=sdd1.path,) - self.scheduleCreateFormat(device=sdd1, format=format) - - md0 = self.newDevice(device_class=MDRaidArrayDevice, - name="md0", level="raid0", minor=0, size=80000, - memberDevices=2, totalDevices=2, - parents=[sdc1, sdd1]) - self.scheduleCreateDevice(device=md0) - format = self.newFormat("ext4", device=md0.path, mountpoint="/home") - self.scheduleCreateFormat(device=md0, format=format) - - destroy_md0_format = self.scheduleDestroyFormat(device=md0) - destroy_md0 = self.scheduleDestroyDevice(device=md0) - destroy_members = [self.scheduleDestroyDevice(device=sdc1)] - destroy_members.append(self.scheduleDestroyDevice(device=sdd1)) - - for member in destroy_members: - # device and format destroy actions for md members should require - # both device and format destroy actions for the md array - for array in [destroy_md0_format, destroy_md0]: - self.assertEqual(member.requires(array), True) - - # ActionDestroyDevice - # when there are two actions that will each destroy a partition on the - # same disk, the action that will destroy the lower-numbered - # partition should require the action that will destroy the higher- - # numbered partition, eg: destroy sda2 before destroying sda1 - self.destroyAllDevices(disks=["sdc", "sdd"]) - sdc1 = self.newDevice(device_class=PartitionDevice, - name="sdc1", parents=[sdc], size=50000) - self.scheduleCreateDevice(device=sdc1) - - sdc2 = self.newDevice(device_class=PartitionDevice, - name="sdc2", parents=[sdc], size=40000) - self.scheduleCreateDevice(device=sdc2) - - destroy_sdc1 = self.scheduleDestroyDevice(device=sdc1) - destroy_sdc2 = self.scheduleDestroyDevice(device=sdc2) - self.assertEqual(destroy_sdc1.requires(destroy_sdc2), True) - self.assertEqual(destroy_sdc2.requires(destroy_sdc1), False) - - self.destroyAllDevices(disks=["sdc", "sdd"]) - sdc = self.storage.devicetree.getDeviceByName("sdc") - self.assertNotEqual(sdc, None) - sdd = self.storage.devicetree.getDeviceByName("sdd") - self.assertNotEqual(sdd, None) - - sdc1 = self.newDevice(device_class=PartitionDevice, - name="sdc1", parents=[sdc], size=50000) - create_pv = self.scheduleCreateDevice(device=sdc1) - format = self.newFormat("lvmpv", device=sdc1.path) - create_pv_format = self.scheduleCreateFormat(device=sdc1, format=format) - - testvg = self.newDevice(device_class=LVMVolumeGroupDevice, - name="testvg", parents=[sdc1], size=50000) - create_vg = self.scheduleCreateDevice(device=testvg) - testlv = self.newDevice(device_class=LVMLogicalVolumeDevice, - name="testlv", vgdev=testvg, size=30000) - create_lv = self.scheduleCreateDevice(device=testlv) - format = self.newFormat("ext4", device=testlv.path) - create_lv_format = self.scheduleCreateFormat(device=testlv, format=format) - - # ActionCreateFormat - # creation of a format on a non-existent device should require the - # action that creates the device - self.assertEqual(create_lv_format.requires(create_lv), True) - - # ActionCreateFormat - # an action that creates a format on a device should require an action - # that creates a device that the format's device depends on - self.assertEqual(create_lv_format.requires(create_pv), True) - self.assertEqual(create_lv_format.requires(create_vg), True) - - # ActionCreateFormat - # an action that creates a format on a device should require an action - # that creates a format on a device that the format's device depends on - self.assertEqual(create_lv_format.requires(create_pv_format), True) - - # XXX from here on, the devices are existing but not in the tree, so - # we instantiate and use actions directly - self.destroyAllDevices(disks=["sdc", "sdd"]) - sdc1 = self.newDevice(device_class=PartitionDevice, exists=True, - name="sdc1", parents=[sdc], size=50000) - sdc1.format = self.newFormat("lvmpv", device=sdc1.path, exists=True, - device_instance=sdc1) - testvg = self.newDevice(device_class=LVMVolumeGroupDevice, exists=True, - name="testvg", parents=[sdc1], size=50000) - testlv = self.newDevice(device_class=LVMLogicalVolumeDevice, - exists=True, - name="testlv", vgdev=testvg, size=30000) - testlv.format = self.newFormat("ext4", device=testlv.path, - exists=True, device_instance=testlv) - - # ActionResizeDevice - # an action that resizes a device should require an action that grows - # a device that the first action's device depends on, eg: grow - # device containing PV before resize of VG or LVs - tmp = sdc1.format - sdc1.format = None # since lvmpv format is not resizable - grow_pv = ActionResizeDevice(sdc1, sdc1.size + 10000) - grow_lv = ActionResizeDevice(testlv, testlv.size + 5000) - grow_lv_format = ActionResizeFormat(testlv, testlv.size + 5000) - - self.assertEqual(grow_lv.requires(grow_pv), True) - self.assertEqual(grow_pv.requires(grow_lv), False) - - # ActionResizeFormat - # an action that grows a format should require the action that grows - # the format's device - self.assertEqual(grow_lv_format.requires(grow_lv), True) - self.assertEqual(grow_lv.requires(grow_lv_format), False) - - # ActionResizeFormat - # an action that resizes a device's format should depend on an action - # that grows a device the first device depends on - self.assertEqual(grow_lv_format.requires(grow_pv), True) - self.assertEqual(grow_pv.requires(grow_lv_format), False) - - # ActionResizeFormat - # an action that resizes a device's format should depend on an action - # that grows a format on a device the first device depends on - # XXX resize of PV format is not allowed, so there's no real-life - # example of this to test - - grow_lv_format.cancel() - grow_lv.cancel() - grow_pv.cancel() - - # ActionResizeDevice - # an action that resizes a device should require an action that grows - # a format on a device that the first action's device depends on, eg: - # grow PV format before resize of VG or LVs - # XXX resize of PV format is not allowed, so there's no real-life - # example of this to test - - # ActionResizeDevice - # an action that resizes a device should require an action that - # shrinks a device that depends on the first action's device, eg: - # shrink LV before resizing VG or PV devices - shrink_lv = ActionResizeDevice(testlv, testlv.size - 10000) - shrink_pv = ActionResizeDevice(sdc1, sdc1.size - 5000) - - self.assertEqual(shrink_pv.requires(shrink_lv), True) - self.assertEqual(shrink_lv.requires(shrink_pv), False) - - # ActionResizeDevice - # an action that resizes a device should require an action that - # shrinks a format on a device that depends on the first action's - # device, eg: shrink LV format before resizing VG or PV devices - shrink_lv_format = ActionResizeFormat(testlv, testlv.size) - self.assertEqual(shrink_pv.requires(shrink_lv_format), True) - self.assertEqual(shrink_lv_format.requires(shrink_pv), False) - - # ActionResizeFormat - # an action that resizes a device's format should depend on an action - # that shrinks a device that depends on the first device - # XXX can't think of a real-world example of this since PVs and MD - # member devices are not resizable in anaconda - - # ActionResizeFormat - # an action that resizes a device's format should depend on an action - # that shrinks a format on a device that depends on the first device - # XXX can't think of a real-world example of this since PVs and MD - # member devices are not resizable in anaconda - - shrink_lv_format.cancel() - shrink_lv.cancel() - shrink_pv.cancel() - sdc1.format = tmp # restore pv's lvmpv format - - # ActionCreateFormat - # an action that creates a format on a device should require an action - # that resizes a device that the format's device depends on - # XXX Really? Is this always so? - - # ActionCreateFormat - # an action that creates a format on a device should require an action - # that resizes a format on a device that the format's device depends on - # XXX Same as above. - - # ActionCreateFormat - # an action that creates a format on a device should require an action - # that resizes the device that will contain the format - grow_lv = ActionResizeDevice(testlv, testlv.size + 1000) - format = self.newFormat("msdos", device=testlv.path) - format_lv = ActionCreateFormat(testlv, format) - self.assertEqual(format_lv.requires(grow_lv), True) - self.assertEqual(grow_lv.requires(format_lv), False) - - # ActionDestroyFormat - # an action that destroys a format should require an action that - # destroys a device that depends on the format's device - destroy_pv_format = ActionDestroyFormat(sdc1) - destroy_lv_format = ActionDestroyFormat(testlv) - destroy_lv = ActionDestroyDevice(testlv) - self.assertEqual(destroy_pv_format.requires(destroy_lv), True) - self.assertEqual(destroy_lv.requires(destroy_pv_format), False) - - # ActionDestroyFormat - # an action that destroys a format should require an action that - # destroys a format on a device that depends on the first format's - # device - self.assertEqual(destroy_pv_format.requires(destroy_lv_format), True) - self.assertEqual(destroy_lv_format.requires(destroy_pv_format), False) - - def testActionSorting(self, *args, **kwargs): - """ Verify correct functioning of action sorting. """ - pass - - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(DeviceActionTestCase) - - -if __name__ == "__main__": - unittest.main() - diff --git a/tests/storage_test/devicelibs_test/Makefile.am b/tests/storage_test/devicelibs_test/Makefile.am deleted file mode 100644 index eeeabbdc4..000000000 --- a/tests/storage_test/devicelibs_test/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -# tests/storage/devicelibs/Makefile.am for anaconda -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# Author: David Cantrell <dcantrell@redhat.com> - -EXTRA_DIST = *.py - -MAINTAINERCLEANFILES = Makefile.in diff --git a/tests/storage_test/devicelibs_test/baseclass.py b/tests/storage_test/devicelibs_test/baseclass.py deleted file mode 100644 index 06adef989..000000000 --- a/tests/storage_test/devicelibs_test/baseclass.py +++ /dev/null @@ -1,80 +0,0 @@ -import unittest -import os -import subprocess - - -def makeLoopDev(device_name, file_name): - proc = subprocess.Popen(["dd", "if=/dev/zero", "of=%s" % file_name, - "bs=1024", "count=102400"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - while True: - (out, err) = proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "dd failed creating the file %s" % file_name - - proc = subprocess.Popen(["losetup", device_name, file_name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - while True: - (out, err) = proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "losetup failed setting up the loop device %s" % device_name - -def removeLoopDev(device_name, file_name): - proc = subprocess.Popen(["losetup", "-d", device_name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - while True: - (out, err) = proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "losetup failed removing the loop device %s" % device_name - - os.unlink(file_name) - -def getFreeLoopDev(): - # There's a race condition here where another process could grab the loop - # device losetup gives us before we have time to set it up, but that's just - # a chance we'll have to take. - proc = subprocess.Popen(["losetup", "-f"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out = None - - while True: - (out, err) = proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - out = out.strip() - break - - if rc: - raise OSError, "losetup failed to find a free device" - - return out - -class DevicelibsTestCase(unittest.TestCase): - - _LOOP_DEVICES = ["/tmp/test-virtdev0", "/tmp/test-virtdev1"] - - def __init__(self, *args, **kwargs): - import pyanaconda.anaconda_log - pyanaconda.anaconda_log.init() - - unittest.TestCase.__init__(self, *args, **kwargs) - self._loopMap = {} - - def setUp(self): - for file in self._LOOP_DEVICES: - dev = getFreeLoopDev() - makeLoopDev(dev, file) - self._loopMap[file] = dev - - def tearDown(self): - for (file, dev) in self._loopMap.iteritems(): - removeLoopDev(dev, file) diff --git a/tests/storage_test/devicelibs_test/crypto_test.py b/tests/storage_test/devicelibs_test/crypto_test.py deleted file mode 100755 index 3e9e3a667..000000000 --- a/tests/storage_test/devicelibs_test/crypto_test.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/python -import baseclass -import unittest -from mock import acceptance - -import tempfile -import os - -class CryptoTestCase(baseclass.DevicelibsTestCase): - - def testCrypto(self): - _LOOP_DEV0 = self._loopMap[self._LOOP_DEVICES[0]] - _LOOP_DEV1 = self._loopMap[self._LOOP_DEVICES[1]] - - import storage.devicelibs.crypto as crypto - - - @acceptance - def testCrypto(self): - ## - ## is_luks - ## - # pass - self.assertEqual(crypto.is_luks(_LOOP_DEV0), -22) - self.assertEqual(crypto.is_luks("/not/existing/device"), -22) - - ## - ## luks_format - ## - # pass - self.assertEqual(crypto.luks_format(_LOOP_DEV0, passphrase="secret", cipher="aes-cbc-essiv:sha256", key_size=256), None) - - # make a key file - handle, keyfile = tempfile.mkstemp(prefix="key", text=False) - os.write(handle, "nobodyknows") - os.close(handle) - - # format with key file - self.assertEqual(crypto.luks_format(_LOOP_DEV1, key_file=keyfile), None) - - # fail - self.assertRaises(crypto.CryptoError, crypto.luks_format, "/not/existing/device", passphrase="secret", cipher="aes-cbc-essiv:sha256", key_size=256) - # no passhprase or key file - self.assertRaises(ValueError, crypto.luks_format, _LOOP_DEV1, cipher="aes-cbc-essiv:sha256", key_size=256) - - ## - ## is_luks - ## - # pass - self.assertEqual(crypto.is_luks(_LOOP_DEV0), 0) # 0 = is luks - self.assertEqual(crypto.is_luks(_LOOP_DEV1), 0) - - ## - ## luks_add_key - ## - # pass - self.assertEqual(crypto.luks_add_key(_LOOP_DEV0, new_passphrase="another-secret", passphrase="secret"), None) - - # make another key file - handle, new_keyfile = tempfile.mkstemp(prefix="key", text=False) - os.write(handle, "area51") - os.close(handle) - - # add new key file - self.assertEqual(crypto.luks_add_key(_LOOP_DEV1, new_key_file=new_keyfile, key_file=keyfile), None) - - # fail - self.assertRaises(crypto.CryptoError, crypto.luks_add_key, _LOOP_DEV0, new_passphrase="another-secret", passphrase="wrong-passphrase") - - ## - ## luks_remove_key - ## - # fail - self.assertRaises(RuntimeError, crypto.luks_remove_key, _LOOP_DEV0, del_passphrase="another-secret", passphrase="wrong-pasphrase") - - # pass - self.assertEqual(crypto.luks_remove_key(_LOOP_DEV0, del_passphrase="another-secret", passphrase="secret"), None) - - # remove key file - self.assertEqual(crypto.luks_remove_key(LOOP_DEV1, del_key_file=new_keyfile, key_file=keyfile), None) - - ## - ## luks_open - ## - # pass - self.assertEqual(crypto.luks_open(_LOOP_DEV0, "crypted", passphrase="secret"), None) - self.assertEqual(crypto.luks_open(_LOOP_DEV1, "encrypted", key_file=keyfile), None) - - # fail - self.assertRaises(crypto.CryptoError, crypto.luks_open, "/not/existing/device", "another-crypted", passphrase="secret") - self.assertRaises(crypto.CryptoError, crypto.luks_open, "/not/existing/device", "another-crypted", key_file=keyfile) - # no passhprase or key file - self.assertRaises(ValueError, crypto.luks_open, _LOOP_DEV1, "another-crypted") - - ## - ## luks_status - ## - # pass - self.assertEqual(crypto.luks_status("crypted"), True) - self.assertEqual(crypto.luks_status("encrypted"), True) - self.assertEqual(crypto.luks_status("another-crypted"), False) - - ## - ## luks_uuid - ## - # pass - uuid = crypto.luks_uuid(_LOOP_DEV0) - self.assertEqual(crypto.luks_uuid(_LOOP_DEV0), uuid) - uuid = crypto.luks_uuid(_LOOP_DEV1) - self.assertEqual(crypto.luks_uuid(_LOOP_DEV1), uuid) - - ## - ## luks_close - ## - # pass - self.assertEqual(crypto.luks_close("crypted"), None) - self.assertEqual(crypto.luks_close("encrypted"), None) - - # fail - self.assertRaises(crypto.CryptoError, crypto.luks_close, "wrong-name") - # already closed - self.assertRaises(crypto.CryptoError, crypto.luks_close, "crypted") - self.assertRaises(crypto.CryptoError, crypto.luks_close, "encrypted") - - # cleanup - os.unlink(keyfile) - os.unlink(new_keyfile) - - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(CryptoTestCase) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/storage_test/devicelibs_test/edd_test.py b/tests/storage_test/devicelibs_test/edd_test.py deleted file mode 100644 index 449395508..000000000 --- a/tests/storage_test/devicelibs_test/edd_test.py +++ /dev/null @@ -1,212 +0,0 @@ -import mock - -class EddTestCase(mock.TestCase): - def setUp(self): - self.setupModules( - ['_isys', 'logging', 'pyanaconda.anaconda_log', 'block']) - - def tearDown(self): - self.tearDownModules() - - def test_biosdev_to_edd_dir(self): - from pyanaconda.storage.devicelibs import edd - path = edd.biosdev_to_edd_dir(138) - self.assertEqual("/sys/firmware/edd/int13_dev8a", path) - - def test_collect_edd_data(self): - from pyanaconda.storage.devicelibs import edd - - # test with vda, vdb - fs = EddTestFS(self, edd).vda_vdb() - edd_dict = edd.collect_edd_data() - self.assertEqual(len(edd_dict), 2) - self.assertEqual(edd_dict[0x80].type, "SCSI") - self.assertEqual(edd_dict[0x80].scsi_id, 0) - self.assertEqual(edd_dict[0x80].scsi_lun, 0) - self.assertEqual(edd_dict[0x80].pci_dev, "00:05.0") - self.assertEqual(edd_dict[0x80].channel, 0) - self.assertEqual(edd_dict[0x80].sectors, 16777216) - self.assertEqual(edd_dict[0x81].pci_dev, "00:06.0") - - # test with sda, vda - fs = EddTestFS(self, edd).sda_vda() - edd_dict = edd.collect_edd_data() - self.assertEqual(len(edd_dict), 2) - self.assertEqual(edd_dict[0x80].type, "ATA") - self.assertEqual(edd_dict[0x80].scsi_id, None) - self.assertEqual(edd_dict[0x80].scsi_lun, None) - self.assertEqual(edd_dict[0x80].pci_dev, "00:01.1") - self.assertEqual(edd_dict[0x80].channel, 0) - self.assertEqual(edd_dict[0x80].sectors, 2097152) - self.assertEqual(edd_dict[0x80].ata_device, 0) - self.assertEqual(edd_dict[0x80].mbr_signature, "0x000ccb01") - - def test_collect_edd_data_cciss(self): - from pyanaconda.storage.devicelibs import edd - fs = EddTestFS(self, edd).sda_cciss() - edd_dict = edd.collect_edd_data() - - self.assertEqual(edd_dict[0x80].pci_dev, None) - self.assertEqual(edd_dict[0x80].channel, None) - - def test_edd_entry_str(self): - from pyanaconda.storage.devicelibs import edd - fs = EddTestFS(self, edd).sda_vda() - edd_dict = edd.collect_edd_data() - expected_output = """\ttype: ATA, ata_device: 0 -\tchannel: 0, mbr_signature: 0x000ccb01 -\tpci_dev: 00:01.1, scsi_id: None -\tscsi_lun: None, sectors: 2097152""" - self.assertEqual(str(edd_dict[0x80]), expected_output) - - def test_matcher_device_path(self): - from pyanaconda.storage.devicelibs import edd - fs = EddTestFS(self, edd).sda_vda() - edd_dict = edd.collect_edd_data() - - analyzer = edd.EddMatcher(edd_dict[0x80]) - path = analyzer.devname_from_pci_dev() - self.assertEqual(path, "sda") - - analyzer = edd.EddMatcher(edd_dict[0x81]) - path = analyzer.devname_from_pci_dev() - self.assertEqual(path, "vda") - - def test_bad_device_path(self): - from pyanaconda.storage.devicelibs import edd - fs = EddTestFS(self, edd).sda_vda_no_pcidev() - edd_dict = edd.collect_edd_data() - - analyzer = edd.EddMatcher(edd_dict[0x80]) - path = analyzer.devname_from_pci_dev() - self.assertEqual(path, None) - - def test_bad_host_bus(self): - from pyanaconda.storage.devicelibs import edd - fs = EddTestFS(self, edd).sda_vda_no_host_bus() - - edd_dict = edd.collect_edd_data() - - # 0x80 entry is basted so fail without an exception - analyzer = edd.EddMatcher(edd_dict[0x80]) - devname = analyzer.devname_from_pci_dev() - self.assertEqual(devname, None) - - # but still succeed on 0x81 - analyzer = edd.EddMatcher(edd_dict[0x81]) - devname = analyzer.devname_from_pci_dev() - self.assertEqual(devname, "vda") - - def test_get_edd_dict_1(self): - """ Test get_edd_dict()'s pci_dev matching. """ - from pyanaconda.storage.devicelibs import edd - fs = EddTestFS(self, edd).sda_vda() - self.assertEqual(edd.get_edd_dict([]), - {'sda' : 0x80, - 'vda' : 0x81}) - - def test_get_edd_dict_2(self): - """ Test get_edd_dict()'s pci_dev matching. """ - from pyanaconda.storage.devicelibs import edd - edd.collect_mbrs = mock.Mock(return_value = { - 'sda' : '0x000ccb01', - 'vda' : '0x0006aef1'}) - fs = EddTestFS(self, edd).sda_vda_missing_details() - self.assertEqual(edd.get_edd_dict([]), - {'sda' : 0x80, - 'vda' : 0x81}) - - def test_get_edd_dict_3(self): - """ Test scenario when the 0x80 and 0x81 edd directories contain the - same data and give no way to distinguish among the two devices. - """ - from pyanaconda.storage.devicelibs import edd - edd.log = mock.Mock() - edd.collect_mbrs = mock.Mock(return_value={'sda' : '0x000ccb01', - 'vda' : '0x0006aef1'}) - fs = EddTestFS(self, edd).sda_sdb_same() - self.assertEqual(edd.get_edd_dict([]), {}) - self.assertIn((('edd: both edd entries 0x80 and 0x81 seem to map to sda',), {}), - edd.log.info.call_args_list) - -class EddTestFS(object): - def __init__(self, test_case, target_module): - self.fs = mock.DiskIO() - test_case.take_over_io(self.fs, target_module) - - def sda_vda_missing_details(self): - self.fs["/sys/firmware/edd/int13_dev80"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev80/mbr_signature"] = "0x000ccb01\n" - self.fs["/sys/firmware/edd/int13_dev81"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev81/mbr_signature"] = "0x0006aef1\n" - - def sda_vda(self): - self.fs["/sys/firmware/edd/int13_dev80"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev80/host_bus"] = "PCI 00:01.1 channel: 0\n" - self.fs["/sys/firmware/edd/int13_dev80/interface"] = "ATA device: 0\n" - self.fs["/sys/firmware/edd/int13_dev80/mbr_signature"] = "0x000ccb01\n" - self.fs["/sys/firmware/edd/int13_dev80/sectors"] = "2097152\n" - - self.fs["/sys/firmware/edd/int13_dev81"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev81/host_bus"] = "PCI 00:05.0 channel: 0\n" - self.fs["/sys/firmware/edd/int13_dev81/interface"] = "SCSI id: 0 lun: 0\n" - self.fs["/sys/firmware/edd/int13_dev81/mbr_signature"] = "0x0006aef1\n" - self.fs["/sys/firmware/edd/int13_dev81/sectors"] = "16777216\n" - - self.fs["/sys/devices/pci0000:00/0000:00:01.1/host0/target0:0:0/0:0:0:0/block"] = self.fs.Dir() - self.fs["/sys/devices/pci0000:00/0000:00:01.1/host0/target0:0:0/0:0:0:0/block/sda"] = self.fs.Dir() - - self.fs["/sys/devices/pci0000:00/0000:00:05.0/virtio2/block"] = self.fs.Dir() - self.fs["/sys/devices/pci0000:00/0000:00:05.0/virtio2/block/vda"] = self.fs.Dir() - - return self.fs - - def sda_vda_no_pcidev(self): - self.sda_vda() - entries = [e for e in self.fs.fs if e.startswith("/sys/devices/pci")] - map(self.fs.os_remove, entries) - return self.fs - - def sda_vda_no_host_bus(self): - self.sda_vda() - self.fs["/sys/firmware/edd/int13_dev80/host_bus"] = "PCI 00:01.1 channel: \n" - self.fs.os_remove("/sys/firmware/edd/int13_dev80/mbr_signature") - self.fs.os_remove("/sys/firmware/edd/int13_dev81/mbr_signature") - - def sda_cciss(self): - self.fs["/sys/firmware/edd/int13_dev80"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev80/host_bus"] = "PCIX 05:00.0 channel: 0\n" - self.fs["/sys/firmware/edd/int13_dev80/interface"] = "RAID identity_tag: 0\n" - self.fs["/sys/firmware/edd/int13_dev80/mbr_signature"] = "0x000ccb01\n" - self.fs["/sys/firmware/edd/int13_dev80/sectors"] = "2097152\n" - - return self.fs - - def vda_vdb(self): - self.fs["/sys/firmware/edd/int13_dev80"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev80/host_bus"] = "PCI 00:05.0 channel: 0\n" - self.fs["/sys/firmware/edd/int13_dev80/interface"] = "SCSI id: 0 lun: 0\n" - self.fs["/sys/firmware/edd/int13_dev80/sectors"] = "16777216\n" - - self.fs["/sys/firmware/edd/int13_dev81"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev81/host_bus"] = "PCI 00:06.0 channel: 0\n" - self.fs["/sys/firmware/edd/int13_dev81/interface"] = "SCSI id: 0 lun: 0\n" - self.fs["/sys/firmware/edd/int13_dev81/sectors"] = "4194304\n" - - return self.fs - - def sda_sdb_same(self): - self.fs["/sys/firmware/edd/int13_dev80"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev80/host_bus"] = "PCI 00:01.1 channel: 0\n" - self.fs["/sys/firmware/edd/int13_dev80/interface"] = "ATA device: 0\n" - self.fs["/sys/firmware/edd/int13_dev80/mbr_signature"] = "0x000ccb01" - self.fs["/sys/firmware/edd/int13_dev80/sectors"] = "2097152\n" - - self.fs["/sys/firmware/edd/int13_dev81"] = self.fs.Dir() - self.fs["/sys/firmware/edd/int13_dev81/host_bus"] = "PCI 00:01.1 channel: 0\n" - self.fs["/sys/firmware/edd/int13_dev81/interface"] = "ATA device: 0\n" - self.fs["/sys/firmware/edd/int13_dev81/mbr_signature"] = "0x0006aef1" - self.fs["/sys/firmware/edd/int13_dev81/sectors"] = "2097152\n" - - self.fs["/sys/devices/pci0000:00/0000:00:01.1/host0/target0:0:0/0:0:0:0/block"] = self.fs.Dir() - self.fs["/sys/devices/pci0000:00/0000:00:01.1/host0/target0:0:0/0:0:0:0/block/sda"] = self.fs.Dir() diff --git a/tests/storage_test/devicelibs_test/lvm_test.py b/tests/storage_test/devicelibs_test/lvm_test.py deleted file mode 100755 index e639a2137..000000000 --- a/tests/storage_test/devicelibs_test/lvm_test.py +++ /dev/null @@ -1,239 +0,0 @@ -#!/usr/bin/python -import baseclass -import unittest -from mock import acceptance - -class LVMTestCase(baseclass.DevicelibsTestCase): - - def testLVM(self): - _LOOP_DEV0 = self._loopMap[self._LOOP_DEVICES[0]] - _LOOP_DEV1 = self._loopMap[self._LOOP_DEVICES[1]] - - import storage.devicelibs.lvm as lvm - - - @acceptance - def testLVM(self): - ## - ## pvcreate - ## - # pass - for dev, file in self._loopMap.iteritems(): - self.assertEqual(lvm.pvcreate(dev), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.pvcreate, "/not/existing/device") - - ## - ## pvresize - ## - # pass - for dev, file in self._loopMap.iteritems(): - self.assertEqual(lvm.pvresize(dev, 50), None) - self.assertEqual(lvm.pvresize(dev, 100), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.pvresize, "/not/existing/device", 50) - - ## - ## vgcreate - ## - # pass - self.assertEqual(lvm.vgcreate("test-vg", [_LOOP_DEV0, _LOOP_DEV1], 4), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.vgcreate, "another-vg", ["/not/existing/device"], 4) - # vg already exists - self.assertRaises(lvm.LVMError, lvm.vgcreate, "test-vg", [_LOOP_DEV0], 4) - # pe size must be power of 2 - self.assertRaises(lvm.LVMError, lvm.vgcreate, "another-vg", [_LOOP_DEV0], 5) - - ## - ## pvremove - ## - # fail - # cannot remove pv now with vg created - self.assertRaises(lvm.LVMError, lvm.pvremove, _LOOP_DEV0) - - ## - ## vgdeactivate - ## - # pass - self.assertEqual(lvm.vgdeactivate("test-vg"), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.vgdeactivate, "wrong-vg-name") - - ## - ## vgreduce - ## - # pass - self.assertEqual(lvm.vgreduce("test-vg", [_LOOP_DEV1]), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.vgreduce, "wrong-vg-name", [_LOOP_DEV1]) - self.assertRaises(lvm.LVMError, lvm.vgreduce, "test-vg", ["/not/existing/device"]) - - ## - ## vgactivate - ## - # pass - self.assertEqual(lvm.vgactivate("test-vg"), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.vgactivate, "wrong-vg-name") - - ## - ## pvinfo - ## - # pass - self.assertEqual(lvm.pvinfo(_LOOP_DEV0)["pv_name"], _LOOP_DEV0) - # no vg - self.assertEqual(lvm.pvinfo(_LOOP_DEV1)["pv_name"], _LOOP_DEV1) - - # fail - self.assertRaises(lvm.LVMError, lvm.pvinfo, "/not/existing/device") - - ## - ## vginfo - ## - # pass - self.assertEqual(lvm.vginfo("test-vg")["pe_size"], "4.00") - - # fail - self.assertRaises(lvm.LVMError, lvm.vginfo, "wrong-vg-name") - - ## - ## lvcreate - ## - # pass - self.assertEqual(lvm.lvcreate("test-vg", "test-lv", 10), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.lvcreate, "wrong-vg-name", "another-lv", 10) - - ## - ## lvdeactivate - ## - # pass - self.assertEqual(lvm.lvdeactivate("test-vg", "test-lv"), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.lvdeactivate, "test-vg", "wrong-lv-name") - self.assertRaises(lvm.LVMError, lvm.lvdeactivate, "wrong-vg-name", "test-lv") - self.assertRaises(lvm.LVMError, lvm.lvdeactivate, "wrong-vg-name", "wrong-lv-name") - - ## - ## lvresize - ## - # pass - self.assertEqual(lvm.lvresize("test-vg", "test-lv", 60), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.lvresize, "test-vg", "wrong-lv-name", 80) - self.assertRaises(lvm.LVMError, lvm.lvresize, "wrong-vg-name", "test-lv", 80) - self.assertRaises(lvm.LVMError, lvm.lvresize, "wrong-vg-name", "wrong-lv-name", 80) - # changing to same size - self.assertRaises(lvm.LVMError, lvm.lvresize, "test-vg", "test-lv", 60) - - ## - ## lvactivate - ## - # pass - self.assertEqual(lvm.lvactivate("test-vg", "test-lv"), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.lvactivate, "test-vg", "wrong-lv-name") - self.assertRaises(lvm.LVMError, lvm.lvactivate, "wrong-vg-name", "test-lv") - self.assertRaises(lvm.LVMError, lvm.lvactivate, "wrong-vg-name", "wrong-lv-name") - - ## - ## lvs - ## - # pass - self.assertEqual(lvm.lvs("test-vg")["test-lv"]["size"], "60.00") - - # fail - self.assertRaises(lvm.LVMError, lvm.lvs, "wrong-vg-name") - - ## - ## has_lvm - ## - # pass - self.assertEqual(lvm.has_lvm(), True) - - # fail - # TODO - - ## - ## lvremove - ## - # pass - self.assertEqual(lvm.lvdeactivate("test-vg", "test-lv"), None) # is deactivation needed? - self.assertEqual(lvm.lvremove("test-vg", "test-lv"), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.lvremove, "test-vg", "wrong-lv-name") - self.assertRaises(lvm.LVMError, lvm.lvremove, "wrong-vg-name", "test-lv") - self.assertRaises(lvm.LVMError, lvm.lvremove, "wrong-vg-name", "wrong-lv-name") - # lv already removed - self.assertRaises(lvm.LVMError, lvm.lvremove, "test-vg", "test-lv") - - ## - ## vgremove - ## - # pass - self.assertEqual(lvm.vgremove("test-vg"), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.vgremove, "wrong-vg-name") - # vg already removed - self.assertRaises(lvm.LVMError, lvm.vgremove, "test-vg") - - ## - ## pvremove - ## - # pass - for dev, file in self._loopMap.iteritems(): - self.assertEqual(lvm.pvremove(dev), None) - - # fail - self.assertRaises(lvm.LVMError, lvm.pvremove, "/not/existing/device") - # pv already removed - self.assertRaises(lvm.LVMError, lvm.pvremove, _LOOP_DEV0) - - #def testGetPossiblePhysicalExtents(self): - # pass - self.assertEqual(lvm.getPossiblePhysicalExtents(4), - filter(lambda pe: pe > 4, map(lambda power: 2**power, xrange(3, 25)))) - self.assertEqual(lvm.getPossiblePhysicalExtents(100000), - filter(lambda pe: pe > 100000, map(lambda power: 2**power, xrange(3, 25)))) - - #def testGetMaxLVSize(self): - # pass - self.assertEqual(lvm.getMaxLVSize(), 16*1024**2) - - #def testSafeLVMName(self): - # pass - self.assertEqual(lvm.safeLvmName("/strange/lv*name5"), "strange_lvname5") - - #def testClampSize(self): - # pass - self.assertEqual(lvm.clampSize(10, 4), 8L) - self.assertEqual(lvm.clampSize(10, 4, True), 12L) - - #def testVGUsedSpace(self): - # TODO - pass - - #def testVGFreeSpace(self): - # TODO - pass - - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(LVMTestCase) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/storage_test/devicelibs_test/mdraid_test.py b/tests/storage_test/devicelibs_test/mdraid_test.py deleted file mode 100755 index 9083bd162..000000000 --- a/tests/storage_test/devicelibs_test/mdraid_test.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -import baseclass -import unittest -import time -from mock import acceptance - -class MDRaidTestCase(baseclass.DevicelibsTestCase): - - def testMDRaid(self): - _LOOP_DEV0 = self._loopMap[self._LOOP_DEVICES[0]] - _LOOP_DEV1 = self._loopMap[self._LOOP_DEVICES[1]] - - import storage.devicelibs.mdraid as mdraid - - @acceptance - def testMDRaid(self): - ## - ## getRaidLevels - ## - # pass - self.assertEqual(mdraid.getRaidLevels(), mdraid.getRaidLevels()) - - ## - ## get_raid_min_members - ## - # pass - self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID0), 2) - self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID1), 2) - self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID5), 3) - self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID6), 4) - self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID10), 2) - - # fail - # unsupported raid - self.assertRaises(ValueError, mdraid.get_raid_min_members, 8) - - ## - ## get_raid_max_spares - ## - # pass - self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID0, 5), 0) - self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID1, 5), 3) - self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID5, 5), 2) - self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID6, 5), 1) - self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID10, 5), 3) - - # fail - # unsupported raid - self.assertRaises(ValueError, mdraid.get_raid_max_spares, 8, 5) - - ## - ## mdcreate - ## - # pass - self.assertEqual(mdraid.mdcreate("/dev/md0", 1, [_LOOP_DEV0, _LOOP_DEV1]), None) - # wait for raid to settle - time.sleep(2) - - # fail - self.assertRaises(mdraid.MDRaidError, mdraid.mdcreate, "/dev/md1", 1, ["/not/existing/dev0", "/not/existing/dev1"]) - - ## - ## mddeactivate - ## - # pass - self.assertEqual(mdraid.mddeactivate("/dev/md0"), None) - - # fail - self.assertRaises(mdraid.MDRaidError, mdraid.mddeactivate, "/not/existing/md") - - ## - ## mdadd - ## - # pass - # TODO - - # fail - self.assertRaises(mdraid.MDRaidError, mdraid.mdadd, "/not/existing/device") - - ## - ## mdactivate - ## - # pass - self.assertEqual(mdraid.mdactivate("/dev/md0", [_LOOP_DEV0, _LOOP_DEV1], super_minor=0), None) - # wait for raid to settle - time.sleep(2) - - # fail - self.assertRaises(mdraid.MDRaidError, mdraid.mdactivate, "/not/existing/md", super_minor=1) - # requires super_minor or uuid - self.assertRaises(ValueError, mdraid.mdactivate, "/dev/md1") - - ## - ## mddestroy - ## - # pass - # deactivate first - self.assertEqual(mdraid.mddeactivate("/dev/md0"), None) - - self.assertEqual(mdraid.mddestroy(_LOOP_DEV0), None) - self.assertEqual(mdraid.mddestroy(_LOOP_DEV1), None) - - # fail - # not a component - self.assertRaises(mdraid.MDRaidError, mdraid.mddestroy, "/dev/md0") - self.assertRaises(mdraid.MDRaidError, mdraid.mddestroy, "/not/existing/device") - - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(MDRaidTestCase) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/storage_test/devicelibs_test/mpath_test.py b/tests/storage_test/devicelibs_test/mpath_test.py deleted file mode 100755 index 5a160d981..000000000 --- a/tests/storage_test/devicelibs_test/mpath_test.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/python -import mock - -class MPathTestCase(mock.TestCase): - - # creating devices, user_friendly_names set to yes - output1 = """\ -create: mpathb (1ATA ST3120026AS 5M) undef ATA,ST3120026AS -size=112G features='0' hwhandler='0' wp=undef -`-+- policy='round-robin 0' prio=1 status=undef - `- 2:0:0:0 sda 8:0 undef ready running -create: mpatha (36006016092d21800703762872c60db11) undef DGC,RAID 5 -size=10G features='1 queue_if_no_path' hwhandler='1 emc' wp=undef -`-+- policy='round-robin 0' prio=2 status=undef - |- 6:0:0:0 sdb 8:16 undef ready running - `- 7:0:0:0 sdc 8:32 undef ready running\ -""" - - # listing existing devices, user_friendly_names set to yes - output2 = """\ -mpathb (3600a0b800067fcc9000001f34d23ff88) dm-1 IBM,1726-4xx FAStT -size=100G features='0' hwhandler='1 rdac' wp=rw -`-+- policy='round-robin 0' prio=-1 status=active - |- 1:0:0:0 sda 8:0 active undef running - `- 2:0:0:0 sdc 8:32 active undef running -mpatha (3600a0b800067fabc000067694d23fe6e) dm-0 IBM,1726-4xx FAStT -size=100G features='0' hwhandler='1 rdac' wp=rw -`-+- policy='round-robin 0' prio=-1 status=active - |- 1:0:0:1 sdb 8:16 active undef running - `- 2:0:0:1 sdd 8:48 active undef running -""" - - # creating devices, user_friendly_names set to no - output3 = """\ -create: 3600a0b800067fabc000067694d23fe6e undef IBM,1726-4xx FAStT -size=100G features='1 queue_if_no_path' hwhandler='1 rdac' wp=undef -`-+- policy='round-robin 0' prio=6 status=undef - |- 1:0:0:1 sdb 8:16 undef ready running - `- 2:0:0:1 sdd 8:48 undef ready running -create: 3600a0b800067fcc9000001f34d23ff88 undef IBM,1726-4xx FAStT -size=100G features='1 queue_if_no_path' hwhandler='1 rdac' wp=undef -`-+- policy='round-robin 0' prio=3 status=undef - |- 1:0:0:0 sda 8:0 undef ready running - `- 2:0:0:0 sdc 8:32 undef ready running\ -""" - - # listing existing devices, user_friendly_names set to no - output4 = """\ -3600a0b800067fcc9000001f34d23ff88 dm-1 IBM,1726-4xx FAStT -size=100G features='0' hwhandler='1 rdac' wp=rw -`-+- policy='round-robin 0' prio=-1 status=active - |- 1:0:0:0 sda 8:0 active undef running - `- 2:0:0:0 sdc 8:32 active undef running -3600a0b800067fabc000067694d23fe6e dm-0 IBM,1726-4xx FAStT -size=100G features='0' hwhandler='1 rdac' wp=rw -`-+- policy='round-robin 0' prio=-1 status=active - |- 1:0:0:1 sdb 8:16 active undef running - `- 2:0:0:1 sdd 8:48 active undef running -""" - - def setUp(self): - self.setupModules( - ['_isys', 'logging', 'anaconda_log', 'block']) - - def tearDown(self): - self.tearDownModules() - - def testParse(self): - from pyanaconda.storage.devicelibs import mpath - topology = mpath.parseMultipathOutput(self.output1) - self.assertEqual(topology, - {'mpatha':['sdb','sdc'], 'mpathb':['sda']}) - topology = mpath.parseMultipathOutput(self.output2) - self.assertEqual(topology, - {'mpathb':['sda','sdc'], 'mpatha':['sdb', 'sdd']}) - topology = mpath.parseMultipathOutput(self.output3) - self.assertEqual(topology, - {'3600a0b800067fabc000067694d23fe6e' : ['sdb','sdd'], - '3600a0b800067fcc9000001f34d23ff88' : ['sda', 'sdc']}) - topology = mpath.parseMultipathOutput(self.output4) - self.assertEqual(topology, - {'3600a0b800067fabc000067694d23fe6e' : ['sdb','sdd'], - '3600a0b800067fcc9000001f34d23ff88' : ['sda', 'sdc']}) - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(MPathTestCase) diff --git a/tests/storage_test/devicelibs_test/swap_test.py b/tests/storage_test/devicelibs_test/swap_test.py deleted file mode 100755 index 3808943cc..000000000 --- a/tests/storage_test/devicelibs_test/swap_test.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/python -import baseclass -import unittest -from mock import acceptance - -class SwapTestCase(baseclass.DevicelibsTestCase): - - def testSwap(self): - _LOOP_DEV0 = self._loopMap[self._LOOP_DEVICES[0]] - _LOOP_DEV1 = self._loopMap[self._LOOP_DEVICES[1]] - - import storage.devicelibs.swap as swap - - @acceptance - def testSwap(self): - ## - ## mkswap - ## - # pass - self.assertEqual(swap.mkswap(_LOOP_DEV0, "swap"), None) - - # fail - self.assertRaises(swap.SwapError, swap.mkswap, "/not/existing/device") - - ## - ## swapon - ## - # pass - self.assertEqual(swap.swapon(_LOOP_DEV0, 1), None) - - # fail - self.assertRaises(swap.SwapError, swap.swapon, "/not/existing/device") - # not a swap partition - self.assertRaises(swap.SwapError, swap.swapon, _LOOP_DEV1) - - # pass - # make another swap - self.assertEqual(swap.mkswap(_LOOP_DEV1, "another-swap"), None) - self.assertEqual(swap.swapon(_LOOP_DEV1), None) - - ## - ## swapstatus - ## - # pass - self.assertEqual(swap.swapstatus(_LOOP_DEV0), True) - self.assertEqual(swap.swapstatus(_LOOP_DEV1), True) - - # does not fail - self.assertEqual(swap.swapstatus("/not/existing/device"), False) - - ## - ## swapoff - ## - # pass - self.assertEqual(swap.swapoff(_LOOP_DEV1), None) - - # check status - self.assertEqual(swap.swapstatus(_LOOP_DEV0), True) - self.assertEqual(swap.swapstatus(_LOOP_DEV1), False) - - self.assertEqual(swap.swapoff(_LOOP_DEV0), None) - - # fail - self.assertRaises(swap.SwapError, swap.swapoff, "/not/existing/device") - # already off - self.assertRaises(swap.SwapError, swap.swapoff, _LOOP_DEV0) - - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(SwapTestCase) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/storage_test/partitioning_test.py b/tests/storage_test/partitioning_test.py deleted file mode 100644 index 0a8cc75a5..000000000 --- a/tests/storage_test/partitioning_test.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/python - -import unittest -from mock import Mock - -import parted - -import pyanaconda.anaconda_log -pyanaconda.anaconda_log.init() - -from pyanaconda.storage.partitioning import getNextPartitionType - -# disklabel-type-specific constants -# keys: disklabel type string -# values: 3-tuple of (max_primary_count, supports_extended, max_logical_count) -disklabel_types = {'dos': (4, True, 11), - 'gpt': (128, False, 0), - 'mac': (62, False, 0)} - -class PartitioningTestCase(unittest.TestCase): - def getDisk(self, disk_type, primary_count=0, - has_extended=False, logical_count=0): - """ Return a mock representing a parted.Disk. """ - disk = Mock() - - disk.type = disk_type - label_type_info = disklabel_types[disk_type] - (max_primaries, supports_extended, max_logicals) = label_type_info - - # primary partitions - disk.primaryPartitionCount = primary_count - disk.maxPrimaryPartitionCount = max_primaries - - # extended partitions - disk.supportsFeature = Mock(return_value=supports_extended) - disk.getExtendedPartition = Mock(return_value=has_extended) - - # logical partitions - disk.getMaxLogicalPartitions = Mock(return_value=max_logicals) - disk.getLogicalPartitions = Mock(return_value=[0]*logical_count) - - return disk - - def testNextPartitionType(self): - # - # DOS - # - - # empty disk, any type - disk = self.getDisk(disk_type="dos") - self.assertEqual(getNextPartitionType(disk), parted.PARTITION_NORMAL) - - # three primaries and no extended -> extended - disk = self.getDisk(disk_type="dos", primary_count=3) - self.assertEqual(getNextPartitionType(disk), parted.PARTITION_EXTENDED) - - # three primaries and an extended -> primary - disk = self.getDisk(disk_type="dos", primary_count=3, has_extended=True) - self.assertEqual(getNextPartitionType(disk), parted.PARTITION_NORMAL) - - # three primaries and an extended w/ no_primary -> logical - disk = self.getDisk(disk_type="dos", primary_count=3, has_extended=True) - self.assertEqual(getNextPartitionType(disk, no_primary=True), - parted.PARTITION_LOGICAL) - - # four primaries and an extended, available logical -> logical - disk = self.getDisk(disk_type="dos", primary_count=4, has_extended=True, - logical_count=9) - self.assertEqual(getNextPartitionType(disk), parted.PARTITION_LOGICAL) - - # four primaries and an extended, no available logical -> None - disk = self.getDisk(disk_type="dos", primary_count=4, has_extended=True, - logical_count=11) - self.assertEqual(getNextPartitionType(disk), None) - - # four primaries and no extended -> None - disk = self.getDisk(disk_type="dos", primary_count=4, - has_extended=False) - self.assertEqual(getNextPartitionType(disk), None) - - # free primary slot, extended, no free logical slot -> primary - disk = self.getDisk(disk_type="dos", primary_count=3, has_extended=True, - logical_count=11) - self.assertEqual(getNextPartitionType(disk), parted.PARTITION_NORMAL) - - # free primary slot, extended, no free logical slot w/ no_primary - # -> None - disk = self.getDisk(disk_type="dos", primary_count=3, has_extended=True, - logical_count=11) - self.assertEqual(getNextPartitionType(disk, no_primary=True), None) - - # - # GPT - # - - # empty disk, any partition type - disk = self.getDisk(disk_type="gpt") - self.assertEqual(getNextPartitionType(disk), parted.PARTITION_NORMAL) - - # no empty slots -> None - disk = self.getDisk(disk_type="gpt", primary_count=128) - self.assertEqual(getNextPartitionType(disk), None) - - # no_primary -> None - disk = self.getDisk(disk_type="gpt") - self.assertEqual(getNextPartitionType(disk, no_primary=True), None) - - # - # MAC - # - - # empty disk, any partition type - disk = self.getDisk(disk_type="mac") - self.assertEqual(getNextPartitionType(disk), parted.PARTITION_NORMAL) - - # no empty slots -> None - disk = self.getDisk(disk_type="mac", primary_count=62) - self.assertEqual(getNextPartitionType(disk), None) - - # no_primary -> None - disk = self.getDisk(disk_type="mac") - self.assertEqual(getNextPartitionType(disk, no_primary=True), None) - - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(PartitioningTestCase) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/storage_test/size_test.py b/tests/storage_test/size_test.py deleted file mode 100644 index 4f002f298..000000000 --- a/tests/storage_test/size_test.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/python -# -# tests/storage/size_tests.py -# Size test cases for the pyanaconda.storage module -# -# 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): David Cantrell <dcantrell@redhat.com> - -import unittest - -from pyanaconda import anaconda_log -anaconda_log.init() -from pyanaconda.storage.errors import * -from pyanaconda.storage.size import Size, _prefixes - -class SizeTestCase(unittest.TestCase): - def testExceptions(self): - self.assertRaises(SizeParamsError, Size) - self.assertRaises(SizeParamsError, Size, bytes=500, spec="45GB") - - self.assertRaises(SizeNotPositiveError, Size, bytes=-1) - - self.assertRaises(SizeNotPositiveError, Size, spec="0") - self.assertRaises(SizeNotPositiveError, Size, spec="-1 TB") - self.assertRaises(SizeNotPositiveError, Size, spec="-47kb") - - s = Size(bytes=500) - self.assertRaises(SizePlacesError, s.humanReadable, places=0) - - def _prefixTestHelper(self, bytes, factor, prefix, abbr): - c = bytes * factor - - s = Size(bytes=c) - self.assertEquals(s, c) - - if prefix: - u = "%sbytes" % prefix - s = Size(spec="%ld %s" % (bytes, u)) - self.assertEquals(s, c) - self.assertEquals(s.convertTo(spec=u), bytes) - - if abbr: - u = "%sb" % abbr - s = Size(spec="%ld %s" % (bytes, u)) - self.assertEquals(s, c) - self.assertEquals(s.convertTo(spec=u), bytes) - - if not prefix and not abbr: - s = Size(spec="%ld" % bytes) - self.assertEquals(s, c) - self.assertEquals(s.convertTo(), bytes) - - def testPrefixes(self): - bytes = 47L - self._prefixTestHelper(bytes, 1, None, None) - - for factor, prefix, abbr in _prefixes: - self._prefixTestHelper(bytes, factor, prefix, abbr) - - def testHumanReadable(self): - s = Size(bytes=58929971L) - self.assertEquals(s.humanReadable(), "58.9 Mb") - - s = Size(bytes=478360371L) - self.assertEquals(s.humanReadable(), "0.48 Gb") - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(SizeTestCase) - -if __name__ == "__main__": - unittest.main() diff --git a/tests/storage_test/storagetestcase.py b/tests/storage_test/storagetestcase.py deleted file mode 100644 index 8e5df1db4..000000000 --- a/tests/storage_test/storagetestcase.py +++ /dev/null @@ -1,287 +0,0 @@ -#!/usr/bin/python - -import unittest -from mock import Mock -from mock import TestCase - -import parted - -import pyanaconda.anaconda_log -pyanaconda.anaconda_log.init() - -import pyanaconda.iutil -import pyanaconda.storage as storage -from pyanaconda.storage.formats import getFormat - -# device classes for brevity's sake -- later on, that is -from pyanaconda.storage.devices import StorageDevice -from pyanaconda.storage.devices import DiskDevice -from pyanaconda.storage.devices import PartitionDevice -from pyanaconda.storage.devices import MDRaidArrayDevice -from pyanaconda.storage.devices import DMDevice -from pyanaconda.storage.devices import LUKSDevice -from pyanaconda.storage.devices import LVMVolumeGroupDevice -from pyanaconda.storage.devices import LVMLogicalVolumeDevice -from pyanaconda.storage.devices import FileDevice - - -class StorageTestCase(TestCase): - """ StorageTestCase - - This is a base class for storage test cases. It sets up imports of - the storage package, along with an Anaconda instance and a Storage - instance. There are lots of little patches to prevent various pieces - of code from trying to access filesystems and/or devices on the host - system, along with a couple of convenience methods. - - """ - def __init__(self, *args, **kwargs): - TestCase.__init__(self, *args, **kwargs) - - self.setUpAnaconda() - - def setUpAnaconda(self): - pyanaconda.iutil.execWithRedirect = Mock() - pyanaconda.iutil.execWithCapture = Mock() - pyanaconda.iutil.execWithPulseProgress = Mock() - pyanaconda.storage.udev = Mock() - - self.anaconda = Mock() - self.setUpStorage() - - def setUpStorage(self): - self.setUpDeviceLibs() - self.storage = storage.Storage(self.anaconda) - - # device status - pyanaconda.storage.devices.StorageDevice.status = False - pyanaconda.storage.devices.DMDevice.status = False - pyanaconda.storage.devices.LUKSDevice.status = False - pyanaconda.storage.devices.LVMVolumeGroupDevice.status = False - pyanaconda.storage.devices.MDRaidArrayDevice.status = False - pyanaconda.storage.devices.FileDevice.status = False - - # prevent PartitionDevice from trying to dig around in the partition's - # geometry - pyanaconda.storage.devices.PartitionDevice._setTargetSize = StorageDevice._setTargetSize - - # prevent Ext2FS from trying to run resize2fs to get a filesystem's - # minimum size - storage.formats.fs.Ext2FS.minSize = storage.formats.DeviceFormat.minSize - storage.formats.fs.FS.migratable = storage.formats.DeviceFormat.migratable - - def setUpDeviceLibs(self): - # devicelibs shouldn't be touching or looking at the host system - - # lvm is easy because all calls to /sbin/lvm are via lvm() - storage.devicelibs.lvm.lvm = Mock() - - # mdraid is easy because all calls to /sbin/mdadm are via mdadm() - storage.devicelibs.mdraid.mdadm = Mock() - - # swap - storage.devicelibs.swap.swapstatus = Mock(return_value=False) - storage.devicelibs.swap.swapon = Mock() - storage.devicelibs.swap.swapoff = Mock() - - # dm - storage.devicelibs.dm = Mock() - - # crypto/luks - storage.devicelibs.crypto.luks_status = Mock(return_value=False) - storage.devicelibs.crypto.luks_uuid = Mock() - storage.devicelibs.crypto.luks_format = Mock() - storage.devicelibs.crypto.luks_open = Mock() - storage.devicelibs.crypto.luks_close = Mock() - storage.devicelibs.crypto.luks_add_key = Mock() - storage.devicelibs.crypto.luks_remove_key = Mock() - - # this list would normally be obtained by parsing /proc/mdstat - storage.devicelibs.mdraid.raid_levels = \ - [storage.devicelibs.mdraid.RAID10, - storage.devicelibs.mdraid.RAID0, - storage.devicelibs.mdraid.RAID1, - storage.devicelibs.mdraid.RAID4, - storage.devicelibs.mdraid.RAID5, - storage.devicelibs.mdraid.RAID6] - - def newDevice(*args, **kwargs): - """ Return a new Device instance suitable for testing. """ - args = args[1:] # drop self arg - device_class = kwargs.pop("device_class") - exists = kwargs.pop("exists", False) - part_type = kwargs.pop("part_type", parted.PARTITION_NORMAL) - device = device_class(*args, **kwargs) - - if exists: - # set up mock parted.Device w/ correct size - device._partedDevice = Mock() - device._partedDevice.getSize = Mock(return_value=float(device.size)) - device._partedDevice.sectorSize = 512 - - if isinstance(device, pyanaconda.storage.devices.PartitionDevice): - #if exists: - # device.parents = device.req_disks - device.parents = device.req_disks - - partedPartition = Mock() - - if device.disk: - part_num = device.name[len(device.disk.name):].split("p")[-1] - partedPartition.number = int(part_num) - - partedPartition.type = part_type - partedPartition.path = device.path - partedPartition.getDeviceNodeName = Mock(return_value=device.name) - partedPartition.getSize = Mock(return_value=float(device.size)) - device._partedPartition = partedPartition - - device.exists = exists - device.format.exists = exists - - if isinstance(device, pyanaconda.storage.devices.PartitionDevice): - # PartitionDevice.probe sets up data needed for resize operations - device.probe() - - return device - - def newFormat(*args, **kwargs): - """ Return a new DeviceFormat instance suitable for testing. - - Keyword Arguments: - - device_instance - StorageDevice instance this format will be - created on. This is needed for setup of - resizable formats. - - All other arguments are passed directly to - pyanaconda.storage.formats.getFormat. - """ - args = args[1:] # drop self arg - exists = kwargs.pop("exists", False) - device_instance = kwargs.pop("device_instance", None) - format = getFormat(*args, **kwargs) - if isinstance(format, storage.formats.disklabel.DiskLabel): - format._partedDevice = Mock() - format._partedDisk = Mock() - - format.exists = exists - - if format.resizable and device_instance: - format._size = device_instance.currentSize - - return format - - def destroyAllDevices(self, disks=None): - """ Remove all devices from the devicetree. - - Keyword Arguments: - - disks - a list of names of disks to remove partitions from - - Note: this is largely ripped off from partitioning.clearPartitions. - - """ - partitions = self.storage.partitions - - # 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 - # avoid it where possible. - partitions.sort(key=lambda p: p.partedPartition.number, reverse=True) - for part in partitions: - if disks and part.disk.name not in disks: - continue - - devices = self.storage.deviceDeps(part) - while devices: - leaves = [d for d in devices if d.isleaf] - for leaf in leaves: - self.storage.destroyDevice(leaf) - devices.remove(leaf) - - self.storage.destroyDevice(part) - - def scheduleCreateDevice(self, *args, **kwargs): - """ Schedule an action to create the specified device. - - Verify that the device is not already in the tree and that the - act of scheduling/registering the action also adds the device to - the tree. - - Return the DeviceAction instance. - """ - device = kwargs.pop("device") - if hasattr(device, "req_disks") and \ - len(device.req_disks) == 1 and \ - not device.parents: - device.parents = device.req_disks - - devicetree = self.storage.devicetree - - self.assertEqual(devicetree.getDeviceByName(device.name), None) - action = storage.deviceaction.ActionCreateDevice(device) - devicetree.registerAction(action) - self.assertEqual(devicetree.getDeviceByName(device.name), device) - return action - - def scheduleDestroyDevice(self, *args, **kwargs): - """ Schedule an action to destroy the specified device. - - Verify that the device exists initially and that the act of - scheduling/registering the action also removes the device from - the tree. - - Return the DeviceAction instance. - """ - device = kwargs.pop("device") - devicetree = self.storage.devicetree - - self.assertEqual(devicetree.getDeviceByName(device.name), device) - action = storage.deviceaction.ActionDestroyDevice(device) - devicetree.registerAction(action) - self.assertEqual(devicetree.getDeviceByName(device.name), None) - return action - - def scheduleCreateFormat(self, *args, **kwargs): - """ Schedule an action to write a new format to a device. - - Verify that the device is already in the tree, that it is not - already set up to contain the specified format, and that the act - of registering/scheduling the action causes the new format to be - reflected in the tree. - - Return the DeviceAction instance. - """ - device = kwargs.pop("device") - format = kwargs.pop("format") - devicetree = self.storage.devicetree - - self.assertNotEqual(device.format, format) - self.assertEqual(devicetree.getDeviceByName(device.name), device) - action = storage.deviceaction.ActionCreateFormat(device, format) - devicetree.registerAction(action) - _device = devicetree.getDeviceByName(device.name) - self.assertEqual(_device.format, format) - return action - - def scheduleDestroyFormat(self, *args, **kwargs): - """ Schedule an action to remove a format from a device. - - Verify that the device is already in the tree and that the act - of registering/scheduling the action causes the new format to be - reflected in the tree. - - Return the DeviceAction instance. - """ - device = kwargs.pop("device") - devicetree = self.storage.devicetree - - self.assertEqual(devicetree.getDeviceByName(device.name), device) - action = storage.deviceaction.ActionDestroyFormat(device) - devicetree.registerAction(action) - _device = devicetree.getDeviceByName(device.name) - self.assertEqual(_device.format.type, None) - return action - - diff --git a/tests/storage_test/tsort_test.py b/tests/storage_test/tsort_test.py deleted file mode 100644 index cb96997bf..000000000 --- a/tests/storage_test/tsort_test.py +++ /dev/null @@ -1,62 +0,0 @@ - -import unittest -import pyanaconda.storage.tsort - -class TopologicalSortTestCase(unittest.TestCase): - def runTest(self): - items = [1, 2, 3, 4, 5] - edges = [(5, 4), (4, 3), (3, 2), (2, 1)] - graph = pyanaconda.storage.tsort.create_graph(items, edges) - self._tsortTest(graph) - - edges = [(5, 4), (2, 3), (1, 5)] - graph = pyanaconda.storage.tsort.create_graph(items, edges) - self._tsortTest(graph) - - edges = [(5, 4), (4, 3), (3, 2), (2, 1), (3, 5)] - graph = pyanaconda.storage.tsort.create_graph(items, edges) - self.failUnlessRaises(pyanaconda.storage.tsort.CyclicGraphError, - pyanaconda.storage.tsort.tsort, - graph) - - edges = [(5, 4), (4, 3), (3, 2), (2, 1), (2, 3)] - graph = pyanaconda.storage.tsort.create_graph(items, edges) - self.failUnlessRaises(pyanaconda.storage.tsort.CyclicGraphError, - pyanaconda.storage.tsort.tsort, - graph) - - items = ['a', 'b', 'c', 'd'] - edges = [('a', 'c'), ('c', 'b')] - graph = pyanaconda.storage.tsort.create_graph(items, edges) - self._tsortTest(graph) - - def _tsortTest(self, graph): - def check_order(order, graph): - # since multiple solutions can potentially exist, just verify - # that the ordering constraints are satisfied - for parent, child in graph['edges']: - if order.index(parent) > order.index(child): - return False - return True - - try: - order = pyanaconda.storage.tsort.tsort(graph) - except Exception as e: - self.fail(e) - - # verify output list is of the correct length - self.failIf(len(order) != len(graph['items']), - "sorted list length is incorrect") - - # verify that all ordering constraints are satisfied - self.failUnless(check_order(order, graph), - "ordering constraints not satisfied") - - -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TopologicalSortTestCase) - - -if __name__ == "__main__": - unittest.main() - diff --git a/tests/storage_test/udev_test.py b/tests/storage_test/udev_test.py deleted file mode 100644 index 5d48279b4..000000000 --- a/tests/storage_test/udev_test.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/python - -import mock -import os - -class UdevTest(mock.TestCase): - - def setUp(self): - self.setupModules(["_isys", "block", "ConfigParser"]) - self.fs = mock.DiskIO() - - import pyanaconda.storage.udev - pyanaconda.storage.udev.os = mock.Mock() - pyanaconda.storage.udev.log = mock.Mock() - pyanaconda.storage.udev.open = self.fs.open - - def tearDown(self): - self.tearDownModules() - - def udev_enumerate_devices_test(self): - import pyanaconda.storage.udev - ENUMERATE_LIST = [ - '/sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', - '/sys/devices/virtual/block/loop0', - '/sys/devices/virtual/block/loop1', - '/sys/devices/virtual/block/ram0', - '/sys/devices/virtual/block/ram1', - '/sys/devices/virtual/block/dm-0', - ] - - pyanaconda.storage.udev.global_udev.enumerate_devices = mock.Mock(return_value=ENUMERATE_LIST) - ret = pyanaconda.storage.udev.udev_enumerate_devices() - self.assertEqual(set(ret), - set(['/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', - '/devices/virtual/block/loop0', '/devices/virtual/block/loop1', - '/devices/virtual/block/ram0', '/devices/virtual/block/ram1', - '/devices/virtual/block/dm-0']) - ) - - def udev_get_device_1_test(self): - import pyanaconda.storage.udev - - class Device(object): - def __init__(self): - self.sysname = 'loop1' - self.dict = {'symlinks': ['/dev/block/7:1'], - 'SUBSYSTEM': 'block', - 'MAJOR': '7', - 'DEVPATH': '/devices/virtual/block/loop1', - 'UDISKS_PRESENTATION_NOPOLICY': '1', - 'UDEV_LOG': '3', - 'DEVNAME': '/dev/loop1', - 'DEVTYPE': 'disk', - 'DEVLINKS': '/dev/block/7:1', - 'MINOR': '1' - } - - def __getitem__(self, key): - return self.dict[key] - - def __setitem__(self, key, value): - self.dict[key] = value - - pyanaconda.storage.udev.os.path.exists.return_value = True - DEV_PATH = '/devices/virtual/block/loop1' - dev = Device() - pyanaconda.storage.udev.global_udev = mock.Mock() - pyanaconda.storage.udev.global_udev.create_device.return_value = dev - pyanaconda.storage.udev.udev_parse_uevent_file = mock.Mock(return_value=dev) - - ret = pyanaconda.storage.udev.udev_get_device(DEV_PATH) - self.assertTrue(isinstance(ret, Device)) - self.assertEqual(ret['name'], ret.sysname) - self.assertEqual(ret['sysfs_path'], DEV_PATH) - self.assertTrue(pyanaconda.storage.udev.udev_parse_uevent_file.called) - - def udev_get_device_2_test(self): - import pyanaconda.storage.udev - pyanaconda.storage.udev.os.path.exists.return_value = False - ret = pyanaconda.storage.udev.udev_get_device('') - self.assertEqual(ret, None) - - def udev_get_device_3_test(self): - import pyanaconda.storage.udev - pyanaconda.storage.udev.os.path.exists.return_value = True - pyanaconda.storage.udev.global_udev = mock.Mock() - pyanaconda.storage.udev.global_udev.create_device.return_value = None - ret = pyanaconda.storage.udev.udev_get_device('') - self.assertEqual(ret, None) - - def udev_get_devices_test(self): - import pyanaconda.storage.udev - pyanaconda.storage.udev.udev_settle = mock.Mock() - DEVS = \ - ['/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', - '/devices/virtual/block/loop0', '/devices/virtual/block/loop1', - '/devices/virtual/block/ram0', '/devices/virtual/block/ram1', - '/devices/virtual/block/dm-0'] - pyanaconda.storage.udev.udev_enumerate_devices = mock.Mock(return_value=DEVS) - pyanaconda.storage.udev.udev_get_device = lambda x: x - ret = pyanaconda.storage.udev.udev_get_devices() - self.assertEqual(ret, DEVS) - - def udev_parse_uevent_file_1_test(self): - import pyanaconda.storage.udev - pyanaconda.storage.udev.os.path.normpath = os.path.normpath - pyanaconda.storage.udev.os.access.return_value = True - - FILE_CONTENT = "MAJOR=7\nMINOR=1\nDEVNAME=loop1\nDEVTYPE=disk\n" - self.fs.open('/sys/devices/virtual/block/loop1/uevent', 'w').write(FILE_CONTENT) - dev = {'sysfs_path': '/devices/virtual/block/loop1'} - ret = pyanaconda.storage.udev.udev_parse_uevent_file(dev) - self.assertEqual(ret, - {'sysfs_path': '/devices/virtual/block/loop1', - 'DEVNAME': 'loop1', - 'DEVTYPE': 'disk', - 'MAJOR': '7', - 'MINOR': '1'}) - - def udev_parse_uevent_file_2_test(self): - import pyanaconda.storage.udev - pyanaconda.storage.udev.os.path.normpath = os.path.normpath - pyanaconda.storage.udev.os.access.return_value = False - - dev = {'sysfs_path': '/devices/virtual/block/loop1'} - ret = pyanaconda.storage.udev.udev_parse_uevent_file(dev) - self.assertEqual(ret, {'sysfs_path': '/devices/virtual/block/loop1'}) - - def udev_settle_test(self): - import pyanaconda.storage.udev - pyanaconda.storage.udev.util = mock.Mock() - pyanaconda.storage.udev.udev_settle() - self.assertTrue(pyanaconda.storage.udev.util.run_program.called) - - def udev_trigger_test(self): - import pyanaconda.storage.udev - pyanaconda.storage.udev.util = mock.Mock() - pyanaconda.storage.udev.udev_trigger() - self.assertTrue(pyanaconda.storage.udev.util.run_program.called) |