summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Lehman <dlehman@redhat.com>2013-01-14 17:48:37 -0600
committerDavid Lehman <dlehman@redhat.com>2013-01-28 13:15:31 -0600
commit9040049d8d232eae3f0f51ffe442dbe49d273bce (patch)
tree5966597dd00948b4eae345a9dc8b509ebb0dab00
parente6c6261e1d7e912103ef1618e4a84c5f70abb00a (diff)
downloadanaconda-9040049d8d232eae3f0f51ffe442dbe49d273bce.tar.gz
anaconda-9040049d8d232eae3f0f51ffe442dbe49d273bce.tar.xz
anaconda-9040049d8d232eae3f0f51ffe442dbe49d273bce.zip
Remove the storage module and replace it with blivet.
-rwxr-xr-xanaconda12
-rw-r--r--anaconda.spec.in66
-rw-r--r--configure.ac28
-rw-r--r--po/POTFILES.in26
-rw-r--r--pyanaconda/Makefile.am2
-rw-r--r--pyanaconda/__init__.py4
-rw-r--r--pyanaconda/bootloader.py12
-rw-r--r--pyanaconda/exception.py4
-rw-r--r--pyanaconda/image.py22
-rw-r--r--pyanaconda/install.py2
-rw-r--r--pyanaconda/installclass.py6
-rwxr-xr-xpyanaconda/isys/__init__.py6
-rw-r--r--pyanaconda/kickstart.py44
-rw-r--r--pyanaconda/network.py8
-rw-r--r--pyanaconda/packaging/__init__.py22
-rw-r--r--pyanaconda/packaging/livepayload.py8
-rw-r--r--pyanaconda/packaging/yumpayload.py14
-rw-r--r--pyanaconda/rescue.py12
-rw-r--r--pyanaconda/storage/Makefile.am26
-rw-r--r--pyanaconda/storage/__init__.py3533
-rw-r--r--pyanaconda/storage/arch.py315
-rw-r--r--pyanaconda/storage/dasd.py218
-rw-r--r--pyanaconda/storage/deviceaction.py587
-rw-r--r--pyanaconda/storage/devicelibs/Makefile.am24
-rw-r--r--pyanaconda/storage/devicelibs/__init__.py0
-rw-r--r--pyanaconda/storage/devicelibs/btrfs.py112
-rw-r--r--pyanaconda/storage/devicelibs/crypto.py153
-rw-r--r--pyanaconda/storage/devicelibs/dm.py78
-rw-r--r--pyanaconda/storage/devicelibs/edd.py231
-rw-r--r--pyanaconda/storage/devicelibs/loop.py89
-rw-r--r--pyanaconda/storage/devicelibs/lvm.py403
-rw-r--r--pyanaconda/storage/devicelibs/mdraid.py280
-rw-r--r--pyanaconda/storage/devicelibs/mpath.py285
-rw-r--r--pyanaconda/storage/devicelibs/swap.py157
-rw-r--r--pyanaconda/storage/devices.py4190
-rw-r--r--pyanaconda/storage/devicetree.py2205
-rw-r--r--pyanaconda/storage/errors.py193
-rw-r--r--pyanaconda/storage/fcoe.py163
-rw-r--r--pyanaconda/storage/flags.py98
-rw-r--r--pyanaconda/storage/formats/Makefile.am24
-rw-r--r--pyanaconda/storage/formats/__init__.py438
-rw-r--r--pyanaconda/storage/formats/biosboot.py60
-rw-r--r--pyanaconda/storage/formats/disklabel.py443
-rw-r--r--pyanaconda/storage/formats/dmraid.py114
-rw-r--r--pyanaconda/storage/formats/fs.py1433
-rw-r--r--pyanaconda/storage/formats/luks.py342
-rw-r--r--pyanaconda/storage/formats/lvmpv.py146
-rw-r--r--pyanaconda/storage/formats/mdraid.py120
-rw-r--r--pyanaconda/storage/formats/multipath.py94
-rw-r--r--pyanaconda/storage/formats/prepboot.py87
-rw-r--r--pyanaconda/storage/formats/swap.py171
-rw-r--r--pyanaconda/storage/iscsi.py437
-rw-r--r--pyanaconda/storage/partitioning.py1947
-rw-r--r--pyanaconda/storage/partspec.py82
-rw-r--r--pyanaconda/storage/platform.py409
-rw-r--r--pyanaconda/storage/pyudev.py232
-rw-r--r--pyanaconda/storage/size.py235
-rw-r--r--pyanaconda/storage/storage_log.py46
-rw-r--r--pyanaconda/storage/tsort.py105
-rw-r--r--pyanaconda/storage/udev.py737
-rw-r--r--pyanaconda/storage/util.py272
-rw-r--r--pyanaconda/storage/zfcp.py428
-rw-r--r--pyanaconda/timezone.py2
-rw-r--r--pyanaconda/ui/gui/spokes/custom.py46
-rw-r--r--pyanaconda/ui/gui/spokes/lib/cart.py2
-rw-r--r--pyanaconda/ui/gui/spokes/lib/resize.py2
-rw-r--r--pyanaconda/ui/gui/spokes/storage.py6
-rwxr-xr-xpyanaconda/ui/gui/tools/run-spoke.py4
-rw-r--r--pyanaconda/ui/lib/space.py2
-rw-r--r--pyanaconda/ui/tui/spokes/storage.py4
-rwxr-xr-xpyanaconda/ui/tui/tools/run-text-spoke.py4
-rwxr-xr-xscripts/anaconda-cleanup6
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/storage_test/Makefile.am24
-rw-r--r--tests/storage_test/action_test.py926
-rw-r--r--tests/storage_test/devicelibs_test/Makefile.am22
-rw-r--r--tests/storage_test/devicelibs_test/baseclass.py80
-rwxr-xr-xtests/storage_test/devicelibs_test/crypto_test.py135
-rw-r--r--tests/storage_test/devicelibs_test/edd_test.py212
-rwxr-xr-xtests/storage_test/devicelibs_test/lvm_test.py239
-rwxr-xr-xtests/storage_test/devicelibs_test/mdraid_test.py114
-rwxr-xr-xtests/storage_test/devicelibs_test/mpath_test.py86
-rwxr-xr-xtests/storage_test/devicelibs_test/swap_test.py74
-rw-r--r--tests/storage_test/partitioning_test.py130
-rw-r--r--tests/storage_test/size_test.py86
-rw-r--r--tests/storage_test/storagetestcase.py287
-rw-r--r--tests/storage_test/tsort_test.py62
-rw-r--r--tests/storage_test/udev_test.py139
88 files changed, 131 insertions, 24605 deletions
diff --git a/anaconda b/anaconda
index 9ad9844d4..5cbb39dbd 100755
--- a/anaconda
+++ b/anaconda
@@ -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)