From: Michael Tremer Date: Sun, 24 May 2009 15:44:29 +0000 (+0200) Subject: Daily checkin. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e37957e6f3dc6a07d7170015cfc396ecb3a12cfd;p=ipfire-3.x.git Daily checkin. --- diff --git a/src/pomona/Makefile b/src/pomona/Makefile index 6c7bba017..41c9fc4f8 100644 --- a/src/pomona/Makefile +++ b/src/pomona/Makefile @@ -20,7 +20,7 @@ include Makefile.inc -SUBDIRS = po +SUBDIRS = isys po all: subdirs diff --git a/src/pomona/dispatch.py b/src/pomona/dispatch.py index 9f10fc1c3..4bf132601 100644 --- a/src/pomona/dispatch.py +++ b/src/pomona/dispatch.py @@ -7,16 +7,16 @@ from constants import * from windows import * installSteps = [ - ("welcome", welcomeWindow,), - ("experimental", experimentalWindow,), - ("console", [ "LanguageWindow", "KeyboardWindow",]), + #("welcome", welcomeWindow,), + #("experimental", experimentalWindow,), + #("console", [ "LanguageWindow", "KeyboardWindow",]), ("storage", storageInitialize,), ("partmethod", partmethodWindow,), - ("autopartition", autopartitionWindow,), - ("autopartitionexecute", doAutoPartition,), + #("autopartition", autopartitionWindow,), + #("autopartitionexecute", doAutoPartition,), ("partition", [ "PartitionWindow",]), - ("bootloader", bootloaderWindow,), - ("complete", finishedWindow,), + #("bootloader", bootloaderWindow,), + #("complete", finishedWindow,), ] diff --git a/src/pomona/isys/Makefile b/src/pomona/isys/Makefile new file mode 100644 index 000000000..680ae057b --- /dev/null +++ b/src/pomona/isys/Makefile @@ -0,0 +1,51 @@ +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2008 Michael Tremer & Christian Schmidt # +# # +# 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 3 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 . # +# # +############################################################################### + +include ../Makefile.inc + +OBJECTS = isys.o mount.o + +CFLAGS += -I$(PYTHONINCLUDE) +LDFLAGS = +LIBS = + +all: depend _isys.so + +depend: $(patsubst %.o,%.c,$(OBJECTS)) + $(CPP) -M $(CFLAGS) $(patsubst %.o,%.c,$(OBJECTS)) > depend + +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $< + +_isys.so: $(OBJECTS) + $(CC) -shared $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ $(OBJECTS) + +install: _isys.so isys.py + cp -vf _isys.so isys.py $(PYTHONLIBDIR) + +clean: + rm -vf $(OBJECTS) depend _isys.so *.py[co] + +test: _isys.so + python -c "import isys" + +ifeq (depend,$(wildcard depend)) +include depend +endif diff --git a/src/pomona/isys/isys.c b/src/pomona/isys/isys.c new file mode 100644 index 000000000..08684fac2 --- /dev/null +++ b/src/pomona/isys/isys.c @@ -0,0 +1,62 @@ + + +#include + +#include "isys.h" +#include "mount.h" + + +static PyObject * doMount(PyObject * s, PyObject * args); +static PyObject * doUMount(PyObject * s, PyObject * args); + +static PyMethodDef isysModuleMethods[] = { + { "mount", (PyCFunction) doMount, METH_VARARGS, NULL }, + { "umount", (PyCFunction) doUMount, METH_VARARGS, NULL }, +}; + +void init_isys(void) { + PyObject * m, * d; + + m = Py_InitModule("_isys", isysModuleMethods); + d = PyModule_GetDict(m); +} + +static PyObject * doMount(PyObject * s, PyObject * args) { + char *err = NULL, *fs, *device, *mntpoint, *flags = NULL; + int rc; + + if (!PyArg_ParseTuple(args, "sss|z", &fs, &device, &mntpoint, + &flags)) return NULL; + + rc = doPwMount(device, mntpoint, fs, flags, &err); + if (rc == MOUNT_ERR_ERRNO) + PyErr_SetFromErrno(PyExc_SystemError); + else if (rc) { + PyObject *tuple = PyTuple_New(2); + + PyTuple_SetItem(tuple, 0, PyInt_FromLong(rc)); + PyTuple_SetItem(tuple, 1, PyString_FromString(err)); + PyErr_SetObject(PyExc_SystemError, tuple); + } + + if (rc) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * doUMount(PyObject * s, PyObject * args) { + char * fs; + + if (!PyArg_ParseTuple(args, "s", &fs)) + return NULL; + + if (umount(fs)) { + PyErr_SetFromErrno(PyExc_SystemError); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} diff --git a/src/pomona/isys/isys.h b/src/pomona/isys/isys.h new file mode 100644 index 000000000..4757e4cd0 --- /dev/null +++ b/src/pomona/isys/isys.h @@ -0,0 +1,5 @@ +#ifndef H_ISYS +#define H_ISYS + + +#endif diff --git a/src/pomona/isys/isys.py b/src/pomona/isys/isys.py new file mode 100644 index 000000000..ffc2c976f --- /dev/null +++ b/src/pomona/isys/isys.py @@ -0,0 +1,79 @@ +#!/usr/bin/python + +import _isys + +import os +import string + +mountCount = {} + +## Mount a filesystem, similar to the mount system call. +# @param device The device to mount. If bindMount is 1, this should be an +# already mounted directory. Otherwise, it should be a device +# name. +# @param location The path to mount device on. +# @param fstype The filesystem type on device. This can be disk filesystems +# such as vfat or ext3, or pseudo filesystems such as proc or +# selinuxfs. +# @param readOnly Should this filesystem be mounted readonly? +# @param bindMount Is this a bind mount? (see the mount(8) man page) +# @param remount Are we mounting an already mounted filesystem? +# @return The return value from the mount system call. +def mount(device, location, fstype = "ext2", readOnly = 0, bindMount = 0, remount = 0, options = "defaults"): + flags = None + location = os.path.normpath(location) + opts = string.split(options) + + # We don't need to create device nodes for devices that start with '/' + # (like '/usbdevfs') and also some special fake devices like 'proc'. + # First try to make a device node and if that fails, assume we can + # mount without making a device node. If that still fails, the caller + # will have to deal with the exception. + # We note whether or not we created a node so we can clean up later. + + if mountCount.has_key(location) and mountCount[location] > 0: + mountCount[location] = mountCount[location] + 1 + return + + if readOnly or bindMount or remount: + if readOnly: + opts.append("ro") + if bindMount: + opts.append("bind") + if remount: + opts.append("remount") + + flags = ",".join(opts) + + #log.debug("isys.py:mount()- going to mount %s on %s with options %s" %(device, location, flags)) + rc = _isys.mount(fstype, device, location, flags) + + if not rc: + mountCount[location] = 1 + + return rc + +## Unmount a filesystem, similar to the umount system call. +# @param what The directory to be unmounted. This does not need to be the +# absolute path. +# @param removeDir Should the mount point be removed after being unmounted? +# @return The return value from the umount system call. +def umount(what, removeDir = 1): + what = os.path.normpath(what) + + if not os.path.isdir(what): + raise ValueError, "isys.umount() can only umount by mount point" + + if mountCount.has_key(what) and mountCount[what] > 1: + mountCount[what] = mountCount[what] - 1 + return + + rc = _isys.umount(what) + + if removeDir and os.path.isdir(what): + os.rmdir(what) + + if not rc and mountCount.has_key(what): + del mountCount[what] + + return rc diff --git a/src/pomona/isys/mount.c b/src/pomona/isys/mount.c new file mode 100644 index 000000000..6b0f17529 --- /dev/null +++ b/src/pomona/isys/mount.c @@ -0,0 +1,186 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mount.h" + +static int mkdirIfNone(char * directory); + +static int readFD(int fd, char **buf) { + char *p; + size_t size = 4096; + int s, filesize = 0; + + *buf = calloc(4096, sizeof (char)); + if (*buf == NULL) + abort(); + + do { + p = &(*buf)[filesize]; + s = read(fd, p, 4096); + if (s < 0) + break; + + filesize += s; + if (s == 0) + break; + + size += s; + *buf = realloc(*buf, size); + if (*buf == NULL) + abort(); + } while (1); + + if (filesize == 0 && s < 0) { + free(*buf); + *buf = NULL; + return -1; + } + + return filesize; +} + +int doPwMount(char *dev, char *where, char *fs, char *options, char **err) { + int rc, child, status, pipefd[2]; + char *opts = NULL, *device; + + if (mkdirChain(where)) + return MOUNT_ERR_ERRNO; + + if (strstr(fs, "nfs")) { + if (options) { + if (asprintf(&opts, "%s,nolock", options) == -1) { + fprintf(stderr, "%s: %d: %s\n", __func__, __LINE__, + strerror(errno)); + fflush(stderr); + abort(); + } + } else { + opts = strdup("nolock"); + } + device = strdup(dev); + } else { + if ((options && strstr(options, "bind") == NULL) && + strncmp(dev, "LABEL=", 6) && strncmp(dev, "UUID=", 5) && + *dev != '/') { + if (asprintf(&device, "/dev/%s", dev) == -1) { + fprintf(stderr, "%s: %d: %s\n", __func__, __LINE__, + strerror(errno)); + fflush(stderr); + abort(); + } + } else { + device = strdup(dev); + } + if (options) + opts = strdup(options); + } + + if (pipe(pipefd)) + return MOUNT_ERR_ERRNO; + + if (!(child = fork())) { + int fd; + + close(pipefd[0]); + + /* Close stdin entirely, redirect stdout to tty5, and redirect stderr + * to a pipe so we can put error messages into exceptions. We'll + * only use these messages should mount also return an error code. + */ + fd = open("/dev/tty5", O_RDONLY); + close(STDIN_FILENO); + dup2(fd, STDIN_FILENO); + close(fd); + + fd = open("/dev/tty5", O_WRONLY); + close(STDOUT_FILENO); + dup2(fd, STDOUT_FILENO); + + dup2(pipefd[1], STDERR_FILENO); + + if (opts) { + rc = execl("/bin/mount", + "/bin/mount", "-n", "-t", fs, "-o", opts, device, where, NULL); + exit(1); + } + else { + rc = execl("/bin/mount", "/bin/mount", "-n", "-t", fs, device, where, NULL); + exit(1); + } + } + + close(pipefd[1]); + + if (err != NULL) + rc = readFD(pipefd[0], err); + + close(pipefd[0]); + waitpid(child, &status, 0); + + free(opts); + free(device); + if (!WIFEXITED(status) || (WIFEXITED(status) && WEXITSTATUS(status))) + return MOUNT_ERR_OTHER; + + return 0; +} + +int mkdirChain(char * origChain) { + char * chain; + char * chptr; + + chain = alloca(strlen(origChain) + 1); + strcpy(chain, origChain); + chptr = chain; + + while ((chptr = strchr(chptr, '/'))) { + *chptr = '\0'; + if (mkdirIfNone(chain)) { + *chptr = '/'; + return MOUNT_ERR_ERRNO; + } + + *chptr = '/'; + chptr++; + } + + if (mkdirIfNone(chain)) + return MOUNT_ERR_ERRNO; + + return 0; +} + +static int mkdirIfNone(char * directory) { + int rc, mkerr; + char * chptr; + + /* If the file exists it *better* be a directory -- I'm not going to + actually check or anything */ + if (!access(directory, X_OK)) + return 0; + + /* if the path is '/' we get ENOFILE not found" from mkdir, rather + then EEXIST which is weird */ + for (chptr = directory; *chptr; chptr++) + if (*chptr != '/') + break; + if (!*chptr) + return 0; + + rc = mkdir(directory, 0755); + mkerr = errno; + + if (!rc || mkerr == EEXIST) + return 0; + + return MOUNT_ERR_ERRNO; +} diff --git a/src/pomona/isys/mount.h b/src/pomona/isys/mount.h new file mode 100644 index 000000000..b50532264 --- /dev/null +++ b/src/pomona/isys/mount.h @@ -0,0 +1,13 @@ + +#ifndef H_MOUNT +#define H_MOUNT + +#define MOUNT_ERR_ERRNO 1 +#define MOUNT_ERR_OTHER 2 + +#include /* for umount() */ + +int doPwMount(char *dev, char *where, char *fs, char *options, char **err); +int mkdirChain(char * origChain); + +#endif diff --git a/src/pomona/partition.py b/src/pomona/partition.py index 843dba894..1eaa4be3f 100644 --- a/src/pomona/partition.py +++ b/src/pomona/partition.py @@ -53,7 +53,8 @@ class PartitionWindow(object): if part.format: type = part.format.name try: - mountpoint = part.format.mountpoint + if part.format.mountpoint: + mountpoint = part.format.mountpoint except AttributeError: pass self.lb.append([" %s" % part.name, diff --git a/src/pomona/storage/devicelibs/swap.py b/src/pomona/storage/devicelibs/swap.py new file mode 100644 index 000000000..6c45d7d7f --- /dev/null +++ b/src/pomona/storage/devicelibs/swap.py @@ -0,0 +1,85 @@ +import resource + +import util +import os + +from ..errors import * + +import gettext +_ = lambda x: gettext.ldgettext("pomona", x) + +def mkswap(device, label=''): + argv = [] + if label: + argv.extend(["-L", label]) + argv.append(device) + + rc = util.execWithRedirect("mkswap", argv, + stderr = "/dev/tty5", + stdout = "/dev/tty5", + searchPath=1) + + if rc: + raise SwapError("mkswap failed for '%s'" % device) + +def swapon(device, priority=None): + pagesize = resource.getpagesize() + buf = 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 + + argv = [] + if isinstance(priority, int) and 0 <= priority <= 32767: + argv.extend(["-p", "%d" % priority]) + argv.append(device) + + rc = util.execWithRedirect("swapon", + argv, + stderr = "/dev/tty5", + stdout = "/dev/tty5", + searchPath=1) + + if rc: + raise SwapError("swapon failed for '%s'" % device) + +def swapoff(device): + rc = util.execWithRedirect("swapoff", [device], + stderr = "/dev/tty5", + stdout = "/dev/tty5", + searchPath=1) + + if rc: + raise SwapError("swapoff failed for '%s'" % device) + +def swapstatus(device): + lines = open("/proc/swaps").readlines() + status = False + for line in lines: + if not line.strip(): + continue + + if line.split()[0] == device: + status = True + break + + return status diff --git a/src/pomona/storage/formats/__init__.py b/src/pomona/storage/formats/__init__.py index b8fd9bc43..b271c00a2 100644 --- a/src/pomona/storage/formats/__init__.py +++ b/src/pomona/storage/formats/__init__.py @@ -156,7 +156,7 @@ class DeviceFormat(object): def _setDevice(self, devspec): if devspec and not devspec.startswith("/"): - raise ValueError("device must be a fully qualified path") + raise ValueError("device must be a fully qualified path: %s" % devspec) self._device = devspec def _getDevice(self): diff --git a/src/pomona/storage/formats/fs.py b/src/pomona/storage/formats/fs.py index d2e4071dd..2eace6153 100644 --- a/src/pomona/storage/formats/fs.py +++ b/src/pomona/storage/formats/fs.py @@ -3,6 +3,8 @@ import os import tempfile +import isys + import util from ..errors import * diff --git a/src/pomona/storage_old/__init__.py b/src/pomona/storage_old/__init__.py deleted file mode 100644 index 702e3bee7..000000000 --- a/src/pomona/storage_old/__init__.py +++ /dev/null @@ -1,1133 +0,0 @@ -#/usr/bin/python - -import os -import sys -import time - -sys.path.append("/usr/src/src/pomona") # XXX - -import gettext -_ = lambda x: gettext.ldgettext("pomona", x) - -from constants import * - -from devicetree import DeviceTree -from devices import * -from deviceaction import * -from devicelibs.lvm import safeLvmName -from udev import * -import formats - -def storageInitialize(installer): - storage = installer.ds.storage - - storage.shutdown() - - if installer.dispatch.dir == DISPATCH_BACK: - return - - # XXX I don't understand why I have to do this - udev_trigger(subsystem="block") - - #XXX Determine our cdrom drive/usb key here and add it to protectedPartiotions - storage.reset() - -class Storage(object): - def __init__(self, installer, ignored=[], clear=[]): - self.installer = installer - - self.doAutoPart = False - self.clearDisks = clear - self.ignoredDisks = ignored - - self.encryptedAutoPart = False - self.encryptionPassphrase = None - - self.autoPartitionRequests = [] - - self.devicetree = DeviceTree(self) - - self._nextID = 0 - - def shutdown(self): - try: - self.devicetree.teardownAll() - except Exception as e: - self.installer.log.error("Failure tearing down device tree: %s" % e) - - def reset(self): - """ Reset storage configuration to reflect actual system state. - - This should rescan from scratch but not clobber user-obtained - information like passphrases, &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 - - self.installer.window = self.installer.intf.waitWindow(_("Finding Devices"), - _("Finding storage devices...")) - self.devicetree = DeviceTree(self) - self.devicetree.populate() - self.fsset = FSSet(self.installer) - self.installer.window.pop() - - @property - def nextID(self): - id = self._nextID - self._nextID += 1 - return id - - @property - def devices(self): - """ A list of all the devices in the device tree. """ - devices = self.devicetree.devices.values() - devices.sort(key=lambda d: d.path) - 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 = [] - devices = self.devicetree.devices - for d in devices: - if isinstance(devices[d], DiskDevice) and devices[d].mediaPresent: - disks.append(devices[d]) - disks.sort(key=lambda d: d.name) - return disks - - @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 - - 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. - """ - checkSizes = [('/usr', 250), ('/tmp', 50), ('/var', 384), - ('/home', 100), ('/boot', 75)] - warnings = [] - errors = [] - - filesystems = self.fsset.mountpoints - root = self.fsset.rootDevice - swaps = self.fsset.swapDevices - #try: - # boot = self.anaconda.platform.bootDevice() - #except DeviceError: - # boot = None - boot = None - - if not root: - errors.append(_("You have not defined a root partition (/), " - "which is required for installation of %s " - "to continue.") % (PRODUCT_NAME,)) - - if root and root.size < 250: - warnings.append(_("Your root partition is less than 250 " - "megabytes which is usually too small to " - "install %s.") % (PRODUCT_NAME,)) - - if (root and root.size < self.installer.backend.getMinimumSizeMB("/")): - errors.append(_("Your / partition is less than %s " - "megabytes which is lower than recommended " - "for a normal %s install.") - %(self.installer.backend.getMinimumSizeMB("/"), - PRODUCT_NAME)) - - # livecds have to have the rootfs type match up - #if (root and - # self.installer.backend.rootFsType and - # root.format.type != self.installer.backend.rootFsType): - # errors.append(_("Your / partition does not match the " - # "the live image you are installing from. " - # "It must be formatted as %s.") - # % (self.anaconda.backend.rootFsType,)) - - for (mount, size) in checkSizes: - if mount in filesystems and filesystems[mount].size < size: - warnings.append(_("Your %s partition is less than %s " - "megabytes which is lower than recommended " - "for a normal %s install.") - %(mount, size, PRODUCT_NAME)) - - 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 not boot: - errors.append(_("You have not created a boot partition.")) - - if (boot and boot.type == "mdarray" and - boot.level != 1): - errors.append(_("Bootable partitions can only be on RAID1 " - "devices.")) - - # can't have bootable partition on LV - if boot and boot.type == "lvmlv": - errors.append(_("Bootable partitions cannot be on a " - "logical volume.")) - - # most arches can't have boot on RAID - if boot and boot.type == "mdarray" and not self.anaconda.platform.supportsMdRaidBoot: - errors.append(_("Bootable partitions cannot be on a RAID " - "device.")) - - # Lots of filesystems types don't support /boot. - if boot and not boot.format.bootable: - errors.append(_("Bootable partitions cannot be on an %s " - "filesystem.") % boot.format.name) - - # vfat /boot is insane. - if (boot and boot == root and boot.format.type == "vfat"): - errors.append(_("Bootable partitions cannot be on an %s " - "filesystem.") % boot.format.type) - - if (boot and filter(lambda d: d.type == "luks/dm-crypt", - self.deviceDeps(boot))): - errors.append(_("Bootable partitions cannot be on an " - "encrypted block device")) - - if not swaps: - warnings.append(_("You have not specified a swap partition. " - "Although not strictly required in all cases, " - "it will significantly improve performance for " - "most installations.")) - - return (errors, warnings) - - def checkNoDisks(self): - """Check that there are valid disk devices.""" - if not self.disks: - self.installer.intf.messageWindow(_("No Drives Found"), - _("An error has occurred - no valid devices were " - "found on which to create new file systems. " - "Please check your hardware for the cause " - "of this problem.")) - return True - return False - - def newPartition(self, *args, **kwargs): - """ Return a new PartitionDevice instance for configuring. """ - if kwargs.has_key("fmt_type"): - kwargs["format"] = getFormat(self.installer, kwargs.pop("fmt_type"), - mountpoint=kwargs.pop("mountpoint", None)) - - if kwargs.has_key("disks"): - parents = kwargs.pop("disks") - if isinstance(parents, Device): - kwargs["parents"] = [parents] - else: - kwargs["parents"] = parents - - if kwargs.has_key("name"): - name = kwargs.pop("name") - else: - name = "req%d" % self.nextID - - return PartitionDevice(self.installer, name, *args, **kwargs) - - def newVG(self, *args, **kwargs): - """ Return a new LVMVolumeGroupDevice instance. """ - pvs = kwargs.pop("pvs", []) - 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") - else: - # XXX name = self.createSuggestedVGName(self.anaconda.id.network) - name = self.createSuggestedVGName(None) - - if name in [d.name for d in self.devices]: - raise ValueError("name already in use") - - return LVMVolumeGroupDevice(self.installer, name, pvs, *args, **kwargs) - - def newLV(self, *args, **kwargs): - """ Return a new LVMLogicalVolumeDevice instance. """ - if kwargs.has_key("vg"): - vg = kwargs.pop("vg") - - mountpoint = kwargs.pop("mountpoint", None) - if kwargs.has_key("fmt_type"): - kwargs["format"] = getFormat(kwargs.pop("fmt_type"), - mountpoint=mountpoint) - - if kwargs.has_key("name"): - name = kwargs.pop("name") - else: - if kwargs.get("format") and kwargs["format"].type == "swap": - swap = True - else: - swap = False - name = self.createSuggestedLVName(vg, - swap=swap, - mountpoint=mountpoint) - - if name in [d.name for d in self.devices]: - raise ValueError("name already in use") - - return LVMLogicalVolumeDevice(self.installer, name, vg, *args, **kwargs) - - def createSuggestedVGName(self, network): - """ Return a reasonable, unused VG name. """ - # try to create a volume group name incorporating the hostname - #hn = network.hostname # XXX - hn = "ipfire.localdomain" - vgnames = [vg.name for vg in self.vgs] - if hn is not None and hn != '': - if hn == 'localhost' or hn == 'localhost.localdomain': - vgtemplate = "VolGroup" - elif hn.find('.') != -1: - hn = safeLvmName(hn) - vgtemplate = "vg_%s" % (hn.split('.')[0].lower(),) - else: - hn = safeLvmName(hn) - vgtemplate = "vg_%s" % (hn.lower(),) - else: - vgtemplate = "VolGroup" - - if vgtemplate not in vgnames and \ - vgtemplate not in lvm.lvm_vg_blacklist: - return vgtemplate - else: - i = 0 - while 1: - tmpname = "%s%02d" % (vgtemplate, i,) - if not tmpname in vgnames and \ - tmpname not in lvm.lvm_vg_blacklist: - break - - i += 1 - if i > 99: - tmpname = "" - - return tmpname - - def createSuggestedLVName(self, vg, swap=None, mountpoint=None): - """ Return a suitable, unused name for a new logical volume. """ - # FIXME: this is not at all guaranteed to work - if mountpoint: - # try to incorporate the mountpoint into the name - if mountpoint == '/': - lvtemplate = 'lv_root' - else: - tmp = safeLvmName(mountpoint) - lvtemplate = "lv_%s" % (tmp,) - else: - if swap: - if len([s for s in self.swaps if s in vg.lvs]): - idx = len([s for s in self.swaps if s in vg.lvs]) - while True: - lvtemplate = "lv_swap%02d" % idx - if lvtemplate in [lv.lvname for lv in vg.lvs]: - idx += 1 - else: - break - else: - lvtemplate = "lv_swap" - else: - idx = len(vg.lvs) - while True: - lvtemplate = "LogVol%02d" % idx - if lvtemplate in [l.lvname for l in vg.lvs]: - idx += 1 - else: - break - - return lvtemplate - - def deviceDeps(self, device): - return self.devicetree.getDependentDevices(device) - - 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(self.installer, device)) - if device.format.type: - self.devicetree.registerAction(ActionCreateFormat(self.installer, 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(self.installer, device)) - - action = ActionDestroyDevice(self.installer, 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 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 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 - -class FSSet(object): - """ A class to represent a set of filesystems. """ - def __init__(self, installer): - self.installer = installer - self.devicetree = installer.ds.storage.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 - - @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 devices(self): - devices = self.devicetree.devices.values() - devices.sort(key=lambda d: d.path) - return devices - - @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)): - # 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 - self.installer.log.warning("completely ignoring your loop mount") - elif ":" in devspec: - # NFS -- preserve but otherwise ignore - device = NFSDevice(devspec, - format=getFormat(fstype, - 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" - device = FileDevice(devspec, - parents=get_containing_device(devspec, self.devicetree), - exists=True) - device.format = getFormat("bind", - device=device.path, - exists=True) - elif mountpoint in ("/proc", "/sys", "/dev/shm", "/dev/pts"): - # 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) - else: - device = StorageDevice(devspec) - - if device is None: - self.installer.log.error("failed to resolve %s (%s) from fstab" % (devspec, - fstype)) - return None - - # make sure, if we're using a device from the tree, that - # the device's format we found matches what's in the fstab - fmt = getFormat(fstype, device=device.path) - if fmt.type != device.format.type: - self.installer.log.warning("scanned format (%s) differs from fstab " - "format (%s)" % (device.format.type, fstype)) - - 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=""): - """ 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 = "" - - path = "%s/etc/fstab" % chroot - if not os.access(path, os.R_OK): - # XXX should we raise an exception instead? - self.installer.log.info("cannot open %s for read" % path) - return - - blkidTab = BlkidTab(self.installer, chroot=chroot) - try: - blkidTab.parse() - self.installer.log.debug("blkid.tab devs: %s" % blkidTab.devices.keys()) - except Exception as e: - self.installer.log.info("error parsing blkid.tab: %s" % e) - blkidTab = None - - cryptTab = CryptTab(self.devicetree, blkidTab=blkidTab, chroot=chroot) - try: - cryptTab.parse(chroot=chroot) - self.installer.log.debug("crypttab maps: %s" % cryptTab.mappings.keys()) - except Exception as e: - self.installer.log.info("error parsing crypttab: %s" % e) - cryptTab = None - - self.blkidTab = blkidTab - self.cryptTab = cryptTab - - with open(path) as f: - self.installer.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 Exception as e: - raise Exception("fstab entry %s is malformed: %s" % (devspec, e)) - - if not device: - continue - - if device not in self.devicetree.devices.values(): - self.devicetree._addDevice(device) - - def fsFreeSpace(self, chroot='/'): - space = [] - for device in self.devices: - if not device.format.mountable or \ - not device.format.status: - continue - - path = "%s/%s" % (chroot, device.format.mountpoint) - try: - space.append((device.format.mountpoint, - isys.pathSpaceAvailable(path))) - except SystemError: - self.installer.log.error("failed to calculate free space for %s" % (device.format.mountpoint,)) - - space.sort(key=lambda s: s[1]) - return space - - def mtab(self): - format = "%s %s %s %s 0 0\n" - mtab = "" - devices = self.mountpoints.values() + self.swapDevices - devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) - devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) - for device in devices: - if not device.format.status: - continue - if not device.format.mountable: - continue - if device.format.mountpoint: - options = device.format.mountopts - if options: - options = options.replace("defaults,", "") - options = options.replace("defaults", "") - - if options: - options = "rw," + options - else: - options = "rw" - mtab = mtab + format % (device.path, - device.format.mountpoint, - device.format.type, - options) - return mtab - - def turnOnSwap(self): - intf = self.installer.intf - for device in self.swapDevices: - try: - device.setup() - device.format.setup() - except SuspendError: - if intf: - msg = _("The swap device:\n\n %s\n\n" - "in your /etc/fstab file is currently in " - "use as a software suspend device, " - "which means your system is hibernating. " - "If you are performing a new install, " - "make sure the installer is set " - "to format all swap devices.") \ - % device.path - intf.messageWindow(_("Error"), msg) - sys.exit(0) - except DeviceError as msg: - if intf: - err = _("Error enabling swap device %s: %s\n\n" - "This most likely means this swap " - "device has not been initialized.\n\n" - "Press OK to exit the installer.") % \ - (device.path, msg) - intf.messageWindow(_("Error"), err) - sys.exit(0) - - def mountFilesystems(self, installer, raiseErrors=None, readOnly=None, - skipRoot=False): - intf = installer.intf - devices = self.mountpoints.values() + self.swapDevices - devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, self.proc]) - 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 - - try: - device.setup() - except Exception as msg: - # FIXME: need an error popup - continue - - if readOnly: - options = "%s,%s" % (options, readOnly) - - try: - device.format.setup(options=options, - chroot=installer.rootPath) - except OSError as (num, msg): - if intf: - if num == errno.EEXIST: - intf.messageWindow(_("Invalid mount point"), - _("An error occurred when trying " - "to create %s. Some element of " - "this path is not a directory. " - "This is a fatal error and the " - "install cannot continue.\n\n" - "Press to exit the " - "installer.") - % (device.format.mountpoint,)) - else: - intf.messageWindow(_("Invalid mount point"), - _("An error occurred when trying " - "to create %s: %s. This is " - "a fatal error and the install " - "cannot continue.\n\n" - "Press to exit the " - "installer.") - % (device.format.mountpoint, msg)) - self.installer.log.error("OSError: (%d) %s" % (num, msg) ) - sys.exit(0) - except SystemError as (num, msg): - if raiseErrors: - raise - if intf and not device.format.linuxNative: - ret = intf.messageWindow(_("Unable to mount filesystem"), - _("An error occurred mounting " - "device %s as %s. You may " - "continue installation, but " - "there may be problems.") % - (device.path, - device.format.mountpoint), - type="custom", - custom_icon="warning", - custom_buttons=[_("_Exit installer"), - _("_Continue")]) - - if ret == 0: - sys.exit(0) - else: - continue - - self.installer.log.error("SystemError: (%d) %s" % (num, msg) ) - sys.exit(0) - except FSError as msg: - if intf: - intf.messageWindow(_("Unable to mount filesystem"), - _("An error occurred mounting " - "device %s as %s: %s. This is " - "a fatal error and the install " - "cannot continue.\n\n" - "Press to exit the " - "installer.") - % (device.path, - device.format.mountpoint, - msg)) - self.installer.log.error("FSError: %s" % msg) - sys.exit(0) - - self.active = True - - def umountFilesystems(self, instPath, ignoreErrors=True, swapoff=True): - devices = self.mountpoints.values() + self.swapDevices - devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, self.proc]) - devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) - devices.reverse() - for device in devices: - if not device.format.mountable and \ - (device.format.type != "swap" or swapoff): - continue - - device.format.teardown() - device.teardown() - - self.active = False - - def createSwapFile(self, rootPath, device, size): - """ Create and activate a swap file under rootPath. """ - filename = "/SWAP" - count = 0 - basedir = os.path.normpath("%s/%s" % (rootPath, - 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, instPath): - root = self.rootDevice - dev = "%s/%s" % (instPath, root.path) - if not os.path.exists("%s/dev/root" %(instPath,)) and os.path.exists(dev): - rdev = os.stat(dev).st_rdev - os.mknod("%s/dev/root" % (instPath,), 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 device in self.devices: - try: - mountpoint = device.format.mountpoint - except AttributeError: - mountpoint = None - - if mountpoint == "/": - return device - - @property - def migratableDevices(self): - """ List of devices whose filesystems can be migrated. """ - migratable = [] - for device in self.devices: - if device.format.migratable and device.format.exists: - migratable.append(device) - - return migratable - - def write(self, instPath): - """ write out all config files based on the set of filesystems """ - # /etc/fstab - fstab_path = os.path.normpath("%s/etc/fstab" % instPath) - fstab = self.fstab() - open(fstab_path, "w").write(fstab) - - # /etc/crypttab - crypttab_path = os.path.normpath("%s/etc/crypttab" % instPath) - crypttab = self.crypttab() - open(crypttab_path, "w").write(crypttab) - - # /etc/mdadm.conf - mdadm_path = os.path.normpath("%s/etc/mdadm.conf" % instPath) - mdadm_conf = self.mdadmConf() - open(mdadm_path, "w").write(mdadm_conf) - - 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") - conf = "" - devices = self.mountpoints.values() + self.swapDevices - for array in arrays: - writeConf = False - for device in devices: - if device == array or device.dependsOn(array): - writeConf = True - break - - if writeConf: - conf += array.mdadmConfEntry - - return conf - - def fstab (self): - format = "%-23s %-23s %-7s %-15s %d %d\n" - fstab = """ -# -# /etc/fstab -# Created by pomona on %s -# -# Accessible filesystems, by reference, are maintained under '/dev/disk' -# See man pages fstab(5), findfs(8), mount(8) and/or vol_id(8) for more info -# -""" % time.asctime() - - devices = self.mountpoints.values() + self.swapDevices - devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) - 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 - - fstype = device.format.type - if fstype == "swap": - mountpoint = "swap" - options = device.format.options - else: - mountpoint = device.format.mountpoint - options = device.format.mountopts - if not mountpoint: - self.installer.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 - 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) - return fstab - -class BlkidTab(object): - """ Dictionary-like interface to blkid.tab with device path keys """ - def __init__(self, installer, chroot=""): - self.installer = installer - self.chroot = chroot - self.devices = {} - - def parse(self): - path = "%s/etc/blkid/blkid.tab" % self.chroot - self.installer.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("\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.values(): - # 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) diff --git a/src/pomona/storage_old/deviceaction.py b/src/pomona/storage_old/deviceaction.py deleted file mode 100644 index 9e7478d41..000000000 --- a/src/pomona/storage_old/deviceaction.py +++ /dev/null @@ -1,352 +0,0 @@ -#!/usr/bin/python - -import copy - -from devices import StorageDevice, PartitionDevice -from formats import getFormat -from errors import * -from udev import * - -# 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_MIGRATE = 250 -ACTION_TYPE_CREATE = 100 - -action_strings = {ACTION_TYPE_NONE: "None", - ACTION_TYPE_DESTROY: "Destroy", - ACTION_TYPE_RESIZE: "Resize", - ACTION_TYPE_MIGRATE: "Migrate", - 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 - - def __init__(self, installer, device): - self.installer = installer - if not isinstance(device, StorageDevice): - raise ValueError("arg 1 must be a StorageDevice instance") - self.device = device - - - def execute(self, intf=None): - """ perform the action """ - pass - - def cancel(self): - """ cancel the action """ - pass - - def isDestroy(self): - return self.type == ACTION_TYPE_DESTROY - - def isCreate(self): - return self.type == ACTION_TYPE_CREATE - - def isMigrate(self): - return self.type == ACTION_TYPE_MIGRATE - - def isResize(self): - return self.type == ACTION_TYPE_RESIZE - - def isShrink(self): - return (self.type == ACTION_TYPE_RESIZE and self.dir == RESIZE_SHRINK) - - def isGrow(self): - return (self.type == ACTION_TYPE_RESIZE and self.dir == RESIZE_GROW) - - def isDevice(self): - return self.obj == ACTION_OBJECT_DEVICE - - def isFormat(self): - return self.obj == ACTION_OBJECT_FORMAT - - def __str__(self): - s = "%s %s" % (action_strings[self.type], object_strings[self.obj]) - if self.isResize(): - s += " (%s)" % resize_strings[self.dir] - if self.isFormat(): - if self.device.format: - fmt_type = self.device.format.type - else: - fmt_type = None - s += " %s on" % fmt_type - if self.isMigrate(): - pass - s += " %s (%s)" % (self.device.name, self.device.type) - return s - -class ActionCreateDevice(DeviceAction): - """ Action representing the creation of a new device. """ - type = ACTION_TYPE_CREATE - obj = ACTION_OBJECT_DEVICE - - def __init__(self, installer, device): - # FIXME: assert device.fs is None - DeviceAction.__init__(self, installer, device) - - def execute(self, intf=None): - self.device.create(intf=intf) - - -class ActionDestroyDevice(DeviceAction): - """ An action representing the deletion of an existing device. """ - type = ACTION_TYPE_DESTROY - obj = ACTION_OBJECT_DEVICE - - def __init__(self, installer, device): - # XXX should we insist that device.fs be None? - DeviceAction.__init__(self, installer, device) - if device.exists: - device.teardown() - - def execute(self, intf=None): - self.device.destroy() - - -class ActionResizeDevice(DeviceAction): - """ An action representing the resizing of an existing device. """ - type = ACTION_TYPE_RESIZE - obj = ACTION_OBJECT_DEVICE - - def __init__(self, installer, device, newsize): - if device.currentSize == newsize: - raise ValueError("new size same as old size") - - if not device.resizable: - raise ValueError("device is not resizable") - - DeviceAction.__init__(self, installer, device) - if newsize > device.currentSize: - self.dir = RESIZE_GROW - else: - self.dir = RESIZE_SHRINK - self.origsize = device.targetSize - self.device.targetSize = newsize - - def execute(self, intf=None): - self.device.resize(intf=intf) - - def cancel(self): - self.device.targetSize = self.origsize - - -class ActionCreateFormat(DeviceAction): - """ An action representing creation of a new filesystem. """ - type = ACTION_TYPE_CREATE - obj = ACTION_OBJECT_FORMAT - - def __init__(self, installer, device, format=None): - DeviceAction.__init__(self, installer, device) - if format: - self.origFormat = device.format - if self.device.format.exists: - self.device.format.teardown() - self.device.format = format - else: - self.origFormat = getFormat(self.installer, None) - - def execute(self, intf=None): - if isinstance(self.device, PartitionDevice): - if self.format.partedFlag is not None: - self.device.setFlag(self.format.partedFlag) - self.device.disk.commit() - - udev_settle() - self.device.setup() - self.device.format.create(intf=intf, - 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("/sys%s" % self.device.sysfsPath) - self.device.format.uuid = udev_device_get_uuid(info) - - def cancel(self): - self.device.format = self.origFormat - - @property - def format(self): - return self.device.format - - -class ActionDestroyFormat(DeviceAction): - """ An action representing the removal of an existing filesystem. - - XXX this seems unnecessary - """ - type = ACTION_TYPE_DESTROY - obj = ACTION_OBJECT_FORMAT - - def __init__(self, installer, device): - DeviceAction.__init__(self, installer, device) - # Save a deep copy of the device stack this format occupies. - # This is necessary since the stack of devices and formats - # required to get to this format may get yanked out from under - # us between now and execute. - self._device = copy.deepcopy(device) - self.origFormat = self._device.format - if device.format.exists: - device.format.teardown() - self.device.format = None - - def execute(self, intf=None): - """ wipe the filesystem signature from the device """ - if self.origFormat: - if isinstance(self.device, PartitionDevice) and \ - self.origFormat.partedFlag is not None: - # unset partition flags and commit - self.device.unsetFlag(self.origFormat.partedFlag) - self.device.disk.commit() - udev_settle() - - # set up our copy of the original device stack since the - # reference we got may have had any number of things changed - # since then (most notably, formats removed by this very - # class' constructor) - self._device.setup() - self.origFormat.destroy() - udev_settle() - self._device.teardown() - - def cancel(self): - self.device.format = self.origFormat - - @property - def format(self): - return self.origFormat - - -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, installer, device, newsize): - if device.targetSize == newsize: - raise ValueError("new size same as old size") - - DeviceAction.__init__(self, installer, device) - if newsize > 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, intf=None): - self.device.setup() - self.device.format.doResize(intf=intf) - - def cancel(self): - self.device.format.targetSize = self.origSize - -class ActionMigrateFormat(DeviceAction): - """ An action representing the migration of an existing filesystem. """ - type = ACTION_TYPE_MIGRATE - obj = ACTION_OBJECT_FORMAT - - def __init__(self, installer, device): - if not device.format.migratable or not device.format.exists: - raise ValueError("device format is not migratable") - - DeviceAction.__init__(self, installer, device) - self.device.format.migrate = True - - def execute(self, intf=None): - self.device.setup() - self.device.format.doMigrate(intf=intf) - - def cancel(self): - self.device.format.migrate = False diff --git a/src/pomona/storage_old/devicelibs/__init__.py b/src/pomona/storage_old/devicelibs/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/pomona/storage_old/devicelibs/lvm.py b/src/pomona/storage_old/devicelibs/lvm.py deleted file mode 100644 index 20fa02ac2..000000000 --- a/src/pomona/storage_old/devicelibs/lvm.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/python - -import os -import re - -import util - -MAX_LV_SLOTS = 256 - -def has_lvm(): - has_lvm = False - for path in os.environ["PATH"].split(":"): - if os.access("%s/lvm" % path, os.X_OK): - has_lvm = True - break - - if has_lvm: - has_lvm = False - for line in open("/proc/devices").readlines(): - if "device-mapper" in line.split(): - has_lvm = True - break - - return has_lvm - -# 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"] - # we don't need the accept for now. - # accepts = config_args_data["filterAccepts"] - # if len(accepts) > 0: - # for i in range(len(rejects)): - # filter_string = filter_string + ("\"a|%s|\", " % accpets[i]) - - if len(rejects) > 0: - for i in range(len(rejects)): - filter_string = filter_string + ("\"r|%s|\"," % rejects[i]) - - 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 - config_args_data["filterRejects"].append(regexp) - - # compoes config once more. - _composeConfig() - -def lvm_cc_resetFilter(): - global config_args_data - config_args_data["filterRejects"] = [] - config_args_data["filterAccepts"] = [] -# 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 util.getArch() in ("x86_64",): #64bit architectures - return (8*1024*1024*1024*1024) #Max is 8EiB (very large number..) - else: - return (16*1024*1024) #Max is 16TiB - -def safeLvmName(name): - tmp = name.strip() - tmp = tmp.replace("/", "_") - tmp = re.sub("[^0-9a-zA-Z._]", "", tmp) - tmp = tmp.lstrip("_") - - return tmp diff --git a/src/pomona/storage_old/devices.py b/src/pomona/storage_old/devices.py deleted file mode 100644 index ae9032d31..000000000 --- a/src/pomona/storage_old/devices.py +++ /dev/null @@ -1,1602 +0,0 @@ -#!/usr/bin/python - -import os -import math -import parted -import _ped - -from util import notify_kernel, numeric_type -from formats import get_device_format_class, getFormat -from devicelibs import lvm -from udev import * - -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:] - - return name - -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. - - """ - _type = "generic device" - _packages = [] - - def __init__(self, installer, name, parents=None, description=''): - """ Create a Device instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - parents -- a list of required Device instances - description -- a string describing the device - - """ - self.installer = installer - 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 - self.description = description - - 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 - shallow_copy_attrs = ('partedDisk', '_partedDevice', - '_partedPartition', '_origPartedDisk', - '_raidSet') - for (attr, value) in self.__dict__.items(): - if attr in shallow_copy_attrs: - setattr(new, attr, copy.copy(value)) - else: - setattr(new, attr, copy.deepcopy(value, memo)) - - return new - - def __str__(self): - s = ("%(type)s instance (%(id)s) --\n" - " description = %(descr)s name = %(name)s status = %(status)s" - " parents = %(parents)s\n" - " kids = %(kids)s\n" % - {"type": self.__class__.__name__, "id": "%#x" % id(self), - "name": self.name, "parents": self.parents, "kids": self.kids, - "descr": self.description, "status": self.status}) - return s - - def removeChild(self): - self.kids -= 1 - - def addChild(self): - self.kids += 1 - - def setup(self, intf=None): - """ 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, intf=None): - """ 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): - """ Run setup method of all parent devices. """ - for parent in self.parents: - parent.setup() - - def teardownParents(self, recursive=None): - """ Run teardown method of all parent devices. """ - for parent in self.parents: - parent.teardown(recursive=recursive) - - def createParents(self): - """ Run create method of all parent devices. """ - self.installer.log.info("NOTE: recursive device creation disabled") - for parent in self.parents: - if not parent.exists: - raise DeviceError("parent device does not exist") - #parent.create() - - 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 - - @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 mediaPresent(self): - return True - - -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 device" - _devDir = "/dev" - sysfsBlockDir = "class/block" - _resizable = False - - def __init__(self, installer, device, format=None, - size=None, major=None, minor=None, - sysfsPath='', parents=None, exists=None): - """ Create a StorageDevice instance. - - Arguments: - - device -- 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 - description -- a string describing the device - - """ - self.installer = installer - - # allow specification of individual parents - if isinstance(parents, Device): - parents = [parents] - - Device.__init__(self, installer, device, parents=parents) - - self.uuid = None - self._format = None - self._size = numeric_type(size) - self.major = numeric_type(major) - self.minor = numeric_type(minor) - self.sysfsPath = sysfsPath - self.exists = exists - - # this may be handy for disk, dmraid, mpath, mdraid - self.diskLabel = None - - self.format = format - self.fstabComment = "" - self._targetSize = self._size - - self._partedDevice = None - - @property - def partedDevice(self): - if self.exists and self.status and not self._partedDevice: - self.installer.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.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 __str__(self): - s = Device.__str__(self) - s += (" uuid = %(uuid)s format = %(format)r size = %(size)s\n" - " major = %(major)s minor = %(minor)r exists = %(exists)s\n" - " sysfs path = %(sysfs)s label = %(diskLabel)s\n" - " target size = %(targetSize)s path = %(path)s\n" - " format args = %(formatArgs)s" % - {"uuid": self.uuid, "format": self.format, "size": self.size, - "major": self.major, "minor": self.minor, "exists": self.exists, - "sysfs": self.sysfsPath, "diskLabel": self.diskLabel, - "targetSize": self.targetSize, "path": self.path, - "formatArgs": self.formatArgs}) - return s - - @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. """ - sysfsName = self.name.replace("/", "!") - path = os.path.join("/sys", self.sysfsBlockDir, sysfsName) - self.sysfsPath = os.path.realpath(path)[4:] - self.installer.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 - - def notifyKernel(self): - """ Send a 'change' uevent to the kernel for this device. """ - if not self.exists: - self.installer.log.debug("Not sending change uevent for non-existent device") - return - - if not self.status: - self.installer.log.debug("Not sending change uevent for inactive device") - return - - path = os.path.normpath("/sys/%s" % self.sysfsPath) - try: - notify_kernel(path, action="change") - except Exception, e: - self.installer.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, intf=None): - """ Resize the device. - - New size should already be set. - """ - raise NotImplementedError("resize method not defined for StorageDevice") - - def setup(self, intf=None): - """ Open, or set up, a device. """ - if not self.exists: - raise DeviceError("device has not been created") - - self.setupParents() - for parent in self.parents: - parent.format.setup() - - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - if not self.exists and not recursive: - raise DeviceError("device has not been created") - - if self.status and self.format.exists: - self.format.teardown() - udev_settle(timeout=10) - - if recursive: - self.teardownParents(recursive=recursive) - - 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._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.exists: - self.setup() - - 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, self.installer, device=self.path, exists=self.exists) - if self._format and self._format.status: - # FIXME: self.format.status doesn't mean much - raise DeviceError("cannot replace active format") - - self._format = format - - def _getFormat(self): - return self._format - - format = property(lambda d: d._getFormat(), - lambda d,f: d._setFormat(f), - doc="The device's formatting.") - - def create(self, intf=None): - """ Create the device. """ - if self.exists: - raise DeviceError("device has already been created") - - self.createParents() - self.setupParents() - self.exists = True - self.setup() - - def destroy(self): - """ Destroy the device. """ - if not self.exists: - raise DeviceError("device has not been created") - - if not self.isleaf: - raise DeviceError("Cannot destroy non-leaf device") - - self.exists = False - - @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") - -class NetworkStorageDevice(object): - """ Virtual base class for network backed storage devices """ - - def __init__(self, installer, host_address): - """ 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. - - Arguments: - - host_address -- host address of the backing server - """ - self.installer = installer - self.host_address = host_address - -class DiskDevice(StorageDevice): - """ A disk """ - _type = "disk" - - def __init__(self, installer, device, format=None, - size=None, major=None, minor=None, sysfsPath='', \ - parents=None, initcb=None, initlabel=None): - """ Create a DiskDevice instance. - - Arguments: - - device -- 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 - - initcb -- the call back to be used when initiating disk. - initlabel -- whether to start with a fresh disklabel - - - DiskDevices always exist. - """ - self.installer = installer - StorageDevice.__init__(self, self.installer, device, format=format, - size=size, major=major, minor=minor, exists=True, - sysfsPath=sysfsPath, parents=parents) - - self.partedDisk = None - - self.installer.log.debug("Looking up parted Device: %s" % self.path) - - if self.partedDevice: - self.installer.log.debug("Creating parted Disk: %s" % self.path) - if initlabel: - self.partedDisk = self.freshPartedDisk() - else: - try: - self.partedDisk = parted.Disk(device=self.partedDevice) - except _ped.DiskLabelException: - # if we have a cb function use it. else an error. - if initcb is not None and initcb(): - self.partedDisk = parted.freshDisk(device=self.partedDevice, \ - ty = platform.getPlatform(None).diskType) - else: - raise DeviceUserDeniedFormatError("User prefered to not format.") - - # We save the actual state of the disk here. Before the first - # modification (addPartition or removePartition) to the partition - # table we reset self.partedPartition to this state so we can - # perform the modifications one at a time. - if self.partedDisk: - self._origPartedDisk = self.partedDisk.duplicate() - else: - self._origPartedDisk = None - - def __str__(self): - s = StorageDevice.__str__(self) - s += (" removable = %(removable)s partedDevice = %(partedDevice)r\n" - " partedDisk = %(partedDisk)r" % - {"removable": self.removable, "partedDisk": self.partedDisk, - "partedDevice": self.partedDevice}) - return s - - def freshPartedDisk(self): - labelType = platform.getPlatform(None).diskType - return parted.freshDisk(device=self.partedDevice, ty=labelType) - - @property - def mediaPresent(self): - return self.partedDevice is not None - - @property - def model(self): - return getattr(self.partedDevice, "model", None) - - @property - def size(self): - """ The disk's size in MB """ - return super(DiskDevice, self).size - #size = property(StorageDevice._getSize) - - def resetPartedDisk(self): - """ Reset parted.Disk to reflect the actual layout of the disk. """ - self.partedDisk = self._origPartedDisk - - def removePartition(self, device): - if not self.mediaPresent: - raise DeviceError("cannot remove partition from disk %s which has no media" % self.name) - - partition = self.partedDisk.getPartitionByPath(device.path) - if partition: - self.partedDisk.removePartition(partition) - - def addPartition(self, device): - if not self.mediaPresent: - raise DeviceError("cannot add partition to disk %s which has no media" % self.name) - - for part in self.partedDisk.partitions: - self.installer.log.debug("Disk %s: partition %s has geom %s" % (self.name, - part.getDeviceNodeName(), - part.geometry)) - - geometry = device.partedPartition.geometry - constraint = parted.Constraint(exactGeom=geometry) - partition = parted.Partition(disk=self.partedDisk, - type=device.partedPartition.type, - geometry=geometry) - self.partedDisk.addPartition(partition, - constraint=constraint) - - def probe(self): - """ Probe for any missing information about this device. - - pyparted should be able to tell us anything we want to know. - size, disklabel type, maybe even partition layout - """ - if not self.diskLabel: - self.installer.log.debug("Setting %s diskLabel to %s" % (self.name, - self.partedDisk.type)) - self.diskLabel = self.partedDisk.type - - def commit(self, intf=None): - """ Commit changes to the device. """ - if not self.mediaPresent: - raise DeviceError("cannot commit to disk %s which has no media" % self.name) - - self.setupParents() - self.setup() - - # give committing 5 tries, failing that, raise an exception - attempt = 1 - maxTries = 5 - keepTrying = True - - while keepTrying and (attempt <= maxTries): - try: - self.partedDisk.commit() - keepTrying = False - except parted.DiskException as msg: - self.installer.log.warning(msg) - attempt += 1 - - if keepTrying: - raise DeviceError("cannot commit to disk %s after %d attempts" % (self.name, maxTries,)) - - # commit makes the kernel re-scan the partition table - udev_settle() - - def destroy(self): - """ Destroy the device. """ - if not self.mediaPresent: - raise DeviceError("cannot destroy disk %s which has no media" % self.name) - - self.partedDisk.deleteAllPartitions() - # this is perhaps a separate operation (wiping the disklabel) - self.partedDisk.clobber() - self.partedDisk.commit() - self.teardown() - - def setup(self, intf=None): - """ Open, or set up, a device. """ - if not os.path.exists(self.path): - raise DeviceError("device does not exist") - -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 - - def __init__(self, installer, name, format=None, - size=None, grow=False, maxsize=None, - major=None, minor=None, bootable=None, - sysfsPath='', parents=None, exists=None, - 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.installer = installer - 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, installer, 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 - - # FIXME: Validate size, but only if this is a new partition. - # For existing partitions we will get the size from - # parted. - - if self.exists: - self.installer.log.debug("Looking up parted Partition: %s" % self.path) - #self.partedPartition = parted.getPartitionByName(self.path) - self._partedPartition = self.disk.partedDisk.getPartitionByPath(self.path) - if not self._partedPartition: - raise DeviceError("cannot find parted partition instance") - - # collect information about the partition from parted - self.probe() - else: - # XXX It might be worthwhile to create a shit-simple - # PartitionRequest class and pass one to this constructor - # for new partitions. - self.req_name = name - self.req_partType = partType - self.req_primary = primary - self.req_max_size = 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 __str__(self): - s = StorageDevice.__str__(self) - s += (" grow = %(grow)s max size = %(maxsize)s bootable = %(bootable)s\n" - " part type = %(partType)s primary = %(primary)s\n" - " partedPartition = %(partedPart)r disk = %(disk)r" % - {"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}) - return s - - 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.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): - """ Device node representing this device. """ - if not self.parents: - # Bogus, but code in various places compares devices by path - # So we must return something unique - return self.name - - return "%s/%s" % (self.parents[0]._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 & parted.PARTITION_EXTENDED - - @property - def isLogical(self): - return self.partType & parted.PARTITION_LOGICAL - - @property - def isPrimary(self): - return self.partType == parted.PARTITION_NORMAL - - @property - def isProtected(self): - return self.partType & parted.PARTITION_PROTECTED - - def _getPartedPartition(self): - return self._partedPartition - - def _setPartedPartition(self, partition): - """ Set this PartitionDevice's parted Partition instance. """ - if partition is None: - path = None - elif isinstance(partition, parted.Partition): - path = partition.path - else: - raise ValueError("partition must be a parted.Partition instance") - - self.installer.log.debug("Device %s new partedPartition %s has path %s" % (self.name, partition, path)) - self._partedPartition = partition - self.updateName() - - partedPartition = property(lambda d: d._getPartedPartition(), - lambda d,p: d._setPartedPartition(p)) - - 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. """ - if not self.parents: - self.sysfsPath = '' - - elif self.parents[0]._devDir == "/dev/mapper": - 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:] - - 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: - return True - - return Device.dependsOn(self, dep) - - def _setFormat(self, format): - """ Set the Device's format. """ - StorageDevice._setFormat(self, format) - - def _setBootable(self, bootable): - """ Set the bootable flag for this partition. """ - if self.partedPartition: - 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._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): - if not self.partedPartition or not self.flagAvailable(flag): - return - - return self.partedPartition.getFlag(flag) - - def setFlag(self, flag): - if not self.partedPartition or not self.flagAvailable(flag): - return - - self.partedPartition.setFlag(flag) - - def unsetFlag(self, flag): - if not self.partedPartition or not self.flagAvailable(flag): - return - - self.partedPartition.unsetFlag(flag) - - def probe(self): - """ Probe for any missing information about this device. - - size, partition type, flags - """ - if not self.exists: - return - - # this is in MB - self._size = self.partedPartition.getSize() - self.targetSize = self._size - - self._partType = self.partedPartition.type - - self._bootable = self.getFlag(parted.PARTITION_BOOT) - - def create(self, intf=None): - """ Create the device. """ - if self.exists: - raise DeviceError("device already exists") - - self.createParents() - self.setupParents() - - self.disk.addPartition(self) - self.disk.commit() - - self.partedPartition = self.disk.partedDisk.getPartitionByPath(self.path) - - self.exists = True - self.setup() - - def _computeResize(self, partition): - # 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) - constraint = parted.Constraint(exactGeom=newGeometry) - - return (constraint, newGeometry) - - def resize(self, intf=None): - """ Resize the device. - - self.targetSize must be set to the new size. - """ - 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 - partition = self.disk.partedDisk.getPartitionByPath(self.path) - (constraint, geometry) = self._computeResize(partition) - - self.disk.partedDisk.setPartitionGeometry(partition=partition, - constraint=constraint, - start=geometry.start, - end=geometry.end) - - self.disk.commit() - self.notifyKernel() - - def destroy(self): - """ Destroy the device. """ - if not self.exists: - raise DeviceError("device has not been created") - - if not self.sysfsPath: - return - - if not self.isleaf: - raise DeviceError("Cannot destroy non-leaf device") - - self.setupParents() - self.disk.removePartition(self) - self.disk.commit() - - self.exists = False - - 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) - - """ - if not self.exists: - raise DeviceError("device does not exist") - - 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. - """ - 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: this is MB by default - maxPartSize = self.partedPartition.getMaxAvailableSize() - - if self.format.maxSize > maxPartSize: - return maxPartSize - else: - return self.format.maxSize - - -class OpticalDevice(StorageDevice): - """ An optical drive, eg: cdrom, dvd+r, &c. - - XXX Is this useful? - """ - _type = "cdrom" - - def __init__(self, installer, name, major=None, minor=None, exists=None, - format=None, parents=None, sysfsPath=''): - self.installer = installer - StorageDevice.__init__(self, installer, name, format=format, - major=major, minor=minor, exists=True, - parents=parents, sysfsPath=sysfsPath) - - @property - def mediaPresent(self): - """ Return a boolean indicating whether or not the device contains - media. - """ - if not self.exists: - raise DeviceError("device has not been created") - - 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. """ - #import _isys - - if not self.exists: - raise DeviceError("device has not been created") - - # XXX TODO - # Make a best effort attempt to do the eject. If it fails, it's not - # critical. - #fd = os.open(self.path, os.O_RDONLY | os.O_NONBLOCK) - - #try: - # _isys.ejectcdrom(fd) - #except SystemError as e: - # self.installer.log.warning("error ejecting cdrom %s: %s" % (self.name, e)) - - #os.close(fd) - -class DMDevice(StorageDevice): - """ A device-mapper device """ - _type = "dm" - _devDir = "/dev/mapper" - - def __init__(self, installer, name, format=None, size=None, dmUuid=None, - target=None, exists=None, 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, installer, name, format=format, size=size, - exists=exists, - parents=parents, sysfsPath=sysfsPath) - self.target = target - self.dmUuid = dmUuid - - def __str__(self): - s = StorageDevice.__str__(self) - s += (" target = %(target)s dmUuid = %(dmUuid)s" % - {"target": self.target, "dmUuid": self.dmUuid}) - return s - - @property - def fstabSpec(self): - """ Return the device specifier for use in /etc/fstab. """ - return self.path - - def updateSysfsPath(self): - """ Update this device's sysfs path. """ - if not self.exists: - raise DeviceError("device has not been created") - - 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. """ - if not self.exists: - raise DeviceError("device has not been created") - - return dm.dm_node_from_name(self.name) - - def _setName(self, name): - """ Set the device's map name. """ - if self.status: - raise DeviceError("device is active") - - 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)) - - -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" - - def __init__(self, installer, name, parents, size=None, free=None, - peSize=None, peCount=None, peFree=None, pvCount=None, - lvNames=[], uuid=None, exists=None, 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 - lvNames -- the names of this VG's LVs - uuid -- the VG's UUID - - """ - self.pvClass = get_device_format_class("lvmpv") - if not self.pvClass: - raise DeviceError("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, installer, name, parents=parents, - exists=exists, sysfsPath=sysfsPath) - - self.uuid = uuid - self.free = numeric_type(free) - self.peSize = numeric_type(peSize) - self.peCount = numeric_type(peCount) - self.peFree = numeric_type(peFree) - self.pvCount = numeric_type(pvCount) - self.lvNames = lvNames - - # circular references, here I come - self._lvs = [] - - # TODO: validate peSize if given - if not self.peSize: - self.peSize = 4.0 # MB - - #self.probe() - - def __str__(self): - s = DMDevice.__str__(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 = %(lvNames)s modified = %(modified)s\n" - " extents = %(extents)s free space = %(freeSpace)s\n" - " free extents = %(freeExtents)s\n" - " PVs = %(pvs)s\n" - " LVs = %(lvs)s" % - {"free": self.free, "peSize": self.peSize, "peCount": self.peCount, - "peFree": self.peFree, "pvCount": self.pvCount, - "lvNames": self.lvNames, "modified": self.isModified, - "extents": self.extents, "freeSpace": self.freeSpace, - "freeExtents": self.freeExtents, "pvs": self.pvs, "lvs": self.lvs}) - return s - - def probe(self): - """ Probe for any information about this device. """ - if not self.exists: - raise DeviceError("device has not been created") - - @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 len(self.pvs) != self.pvCount: - 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. - """ - if not self.exists: - raise DeviceError("device does not exist") - - if not isinstance(device.format, self.pvClass): - raise ValueError("addDevice requires a PV arg") - - if self.uuid and device.format.vgUuid != self.uuid: - raise ValueError("UUID mismatch") - - 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 len(self.parents) == self.pvCount: - self.setup() - - def _removeDevice(self, device): - """ Remove a physical volume from the volume group. - - This is for cases like clearing of preexisting partitions. - """ - try: - self.parents.remove(device) - except ValueError, e: - raise ValueError("cannot remove non-member PV device from VG") - - device.removeChild() - - def setup(self, intf=None): - """ Open, or set up, a device. - - XXX we don't do anything like "vgchange -ay" because we don't - want all of the LVs activated, just the VG itself. - """ - if not self.exists: - raise DeviceError("device has not been created") - - if self.status: - return - - if len(self.parents) < self.pvCount: - raise DeviceError("cannot activate VG with missing PV(s)") - - self.setupParents() - - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - if not self.exists and not recursive: - raise DeviceError("device has not been created") - - if self.status: - lvm.vgdeactivate(self.name) - - if recursive: - self.teardownParents(recursive=recursive) - - def create(self, intf=None): - """ Create the device. """ - if self.exists: - raise DeviceError("device already exists") - - pv_list = [] - #for pv in self.parents: - # This is a little bit different from other devices in that - # for VG we need the PVs to be formatted before we can create - # the VG. - # pv.create() - # pv.format.create() - # pv_list.append(pv.path) - pv_list = [pv.path for pv in self.parents] - self.createParents() - self.setupParents() - lvm.vgcreate(self.name, pv_list, self.peSize) - # FIXME set / update self.uuid here - self.exists = True - self.setup() - - def destroy(self): - """ Destroy the device. """ - if not self.exists: - raise DeviceError("device has not been created") - - # set up the pvs since lvm needs access to them to do the vgremove - self.setupParents() - - # this sometimes fails for some reason. - try: - lvm.vgreduce(self.name, [], rm=True) - lvm.vgremove(self.name) - except lvm.LVMError: - raise DeviceError("Could not completely remove VG %s" % self.name) - finally: - self.notifyKernel() - self.exists = False - - def reduce(self, pv_list): - """ Remove the listed PVs from the VG. """ - if not self.exists: - raise DeviceError("device has not been created") - - 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 [pv for pv in self.pvs if getattr(pv, "req_grow", None)] and \ - lv.size > self.freeSpace: - raise DeviceError("new lv is too large to fit in free space") - - 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.parents.append(pv) - pv.addChild() - - 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.parents.remove(pv) - pv.removeChild() - - # 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 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 - used = 0 - size = self.size - self.installer.log.debug("%s size is %dMB" % (self.name, size)) - for lv in self.lvs: - self.installer.log.debug("lv %s (%s) uses %dMB" % (lv.name, lv, lv.size)) - used += self.align(lv.size, roundup=True) - - free = self.size - used - self.installer.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 = 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. - """ - return len(self.pvs) == self.pvCount or not self.exists diff --git a/src/pomona/storage_old/devicetree.py b/src/pomona/storage_old/devicetree.py deleted file mode 100644 index 5e3858827..000000000 --- a/src/pomona/storage_old/devicetree.py +++ /dev/null @@ -1,444 +0,0 @@ - -import os - -import formats - -from devices import * -from errors import * -from udev import * - -class DeviceTree: - def __init__(self, storage): - self.installer = storage.installer - self.storage = storage - - # internal data members - self._devices = [] - self._actions = [] - - self._ignoredDisks = [] - for disk in self.storage.ignoredDisks: - self.addIgnoredDisk(disk) - - def addIgnoredDisk(self, disk): - self._ignoredDisks.append(disk) - lvm.lvm_cc_addFilterRejectRegexp(disk) - - def populate(self): - """ Locate all storage devices. """ - # each iteration scans any devices that have appeared since the - # previous iteration - old_devices = [] - ignored_devices = [] - while True: - devices = [] - new_devices = udev_get_block_devices() - - for new_device in new_devices: - found = False - for old_device in old_devices: - if old_device['name'] == new_device['name']: - found = True - break - - if not found: - devices.append(new_device) - - if len(devices) == 0: - # nothing is changing -- we are finished building devices - break - - old_devices = new_devices - self.installer.log.info("devices to scan: %s" % [d['name'] for d in devices]) - for dev in devices: - self.addUdevDevice(dev) - - # After having the complete tree we make sure that the system - # inconsistencies are ignored or resolved. - #for leaf in self.leaves: - # self._handleInconsistencies(leaf) - - #self.teardownAll() - try: - os.unlink("/etc/mdadm.conf") - except OSError: - self.installer.log.info("failed to unlink /etc/mdadm.conf") - - 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: - return True - - for ignored in self._ignoredDisks: - if ignored == os.path.basename(os.path.dirname(sysfs_path)): - # this is a partition on a disk in the ignore list - return True - - # Ignore partitions found on the raw disks which are part of a - # dmraidset - for set in self.getDevicesByType("dm-raid array"): - for disk in set.parents: - if disk.name == os.path.basename(os.path.dirname(sysfs_path)): - return True - - # 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("loop") or name.startswith("ram"): - return True - - # FIXME: check for virtual devices whose slaves are on the ignore list - - 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: - self.installer.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.partedDisk.getLogicalPartitions()) > 0: - raise ValueError("Cannot remove extended partition %s. " - "Logical partitions present." % dev.name) - - dev.disk.partedDisk.removePartition(dev.partedPartition) - - self._devices.remove(dev) - self.installer.log.debug("removed %s (%s) from device tree" % (dev.name, - dev.type)) - - 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. - """ - self.installer.log.debug("Trying to register action: %s" % action) - if (action.isDestroy() or action.isResize() or \ - (action.isCreate() and action.isFormat())) and \ - action.device not in self._devices: - raise DeviceTreeError("device is not in the tree") - elif (action.isCreate() and action.isDevice()): - # this allows multiple create actions w/o destroy in between; - # we will clean it up before processing actions - #raise DeviceTreeError("device is already in the tree") - if action.device in self._devices: - self._removeDevice(action.device) - for d in self._devices: - if d.path == action.device.path: - self._removeDevice(d) - - 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") - - self.installer.log.debug("registered action: %s" % action) - self._actions.append(action) - - def _addDevice(self, newdev): - """ Add a device to the tree. - - Raise ValueError if the device's identifier is already - in the list. - """ - if newdev.path in [d.path for d in self._devices]: - 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) - self.installer.log.debug("Added %s (%s) to device tree" % (newdev.name, newdev.type)) - - def addUdevPartitionDevice(self, info): - name = udev_device_get_name(info) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - device = None - - disk_name = os.path.basename(os.path.dirname(sysfs_path)) - disk = self.getDeviceByName(disk_name) - - if disk is None: - # create a device instance for the disk - path = os.path.dirname(os.path.realpath(sysfs_path)) - new_info = udev_get_block_device(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 - self.installer.log.error("Failure scanning device %s" % disk_name) - return - - try: - device = PartitionDevice(self.installer, name, sysfsPath=sysfs_path, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - exists=True, parents=[disk]) - except DeviceError: - # 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. - return - - self._addDevice(device) - return device - - def addUdevDiskDevice(self, info): - name = udev_device_get_name(info) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - device = None - - self.installer.log.debug("%s is a disk" % name) - - try: - device = DiskDevice(self.installer, name, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - sysfsPath=sysfs_path, - initlabel=False) - except DeviceUserDeniedFormatError: #drive not initialized? - self.addIgnoredDisk(name) - return - - self._addDevice(device) - return device - - def addUdevOpticalDevice(self, info): - # XXX should this be RemovableDevice instead? - # - # Looks like if it has ID_INSTANCE=0:1 we can ignore it. - device = OpticalDevice(self.installer, udev_device_get_name(info), - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - sysfsPath=udev_device_get_sysfs_path(info)) - self._addDevice(device) - return device - - def addUdevDevice(self, info): - # FIXME: this should be broken up into more discrete chunks - name = udev_device_get_name(info) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - - if self.isIgnored(info): - self.installer.log.debug("Ignoring %s (%s)" % (name, sysfs_path)) - return - - self.installer.log.debug("Scanning %s (%s)..." % (name, sysfs_path)) - device = self.getDeviceByName(name) - - # - # The first step is to either look up or create the device - # - if udev_device_is_dm(info): - self.installer.log.debug("%s is a device-mapper device" % name) - # try to look up the device - if device is None and uuid: - # try to find the device by uuid - device = self.getDeviceByUuid(uuid) - - if device is None: - device = self.addUdevDMDevice(info) - elif udev_device_is_md(info): - self.installer.log.debug("%s is an md device" % name) - if device is None and 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): - self.installer.log.debug("%s is a cdrom" % name) - if device is None: - device = self.addUdevOpticalDevice(info) - elif udev_device_is_dmraid(info): - # This is special handling to avoid the "unrecognized disklabel" - # code since dmraid member disks won't have a disklabel. We - # use a StorageDevice because DiskDevices need disklabels. - # Quite lame, but it doesn't matter much since we won't use - # the StorageDevice instances for anything. - self.installer.log.debug("%s is part of a dmraid" % name) - if device is None: - device = StorageDevice(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): - if device is None: - device = self.addUdevDiskDevice(info) - elif udev_device_is_partition(info): - self.installer.log.debug("%s is a partition" % name) - if device is None: - device = self.addUdevPartitionDevice(info) - - # now handle the device's formatting - self.handleUdevDeviceFormat(info, device) - - def handleUdevDeviceFormat(self, info, device): - #self.installer.log.debug("%s" % info) - 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) - - #self.installer.log.debug("Type is '%s'" % format_type) - - 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 - #self.installer.log.debug("bailing") - return - - # set up the common arguments for the format constructor - args = [format_type] - kwargs = {"uuid": uuid, - "label": label, - "device": device.path, - "exists": True} - - # set up type-specific arguments for the format constructor - if format_type == "crypto_LUKS": - # luks/dmcrypt - kwargs["name"] = "luks-%s" % uuid - elif format_type == "linux_raid_member": - # mdraid - try: - kwargs["mdUuid"] = udev_device_get_md_uuid(info) - except KeyError: - self.installer.log.debug("mdraid member %s has no md uuid" % name) - elif format_type == "LVM2_member": - # lvm - try: - kwargs["vgName"] = udev_device_get_vg_name(info) - except KeyError as e: - self.installer.log.debug("PV %s has no vg_name" % name) - try: - kwargs["vgUuid"] = udev_device_get_vg_uuid(info) - except KeyError: - self.installer.log.debug("PV %s has no vg_uuid" % name) - try: - kwargs["peStart"] = udev_device_get_pv_pe_start(info) - except KeyError: - self.installer.log.debug("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" - - device.format = formats.getFormat(self.installer, *args, **kwargs) - - # - # 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) - - def getDeviceByName(self, name): - #self.installer.log.debug("Looking for device '%s'..." % name) - found = None - for device in self._devices: - if device.name == name: - found = device - break - - #self.installer.log.debug("Found %s" % 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)] - - @property - def devices(self): - """ Dict with device path keys and Device values. """ - devices = {} - - for device in self._devices: - if device.path in devices: - raise DeviceTreeError("duplicate paths in device tree") - - devices[device.path] = device - - return devices - - 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) diff --git a/src/pomona/storage_old/devicetree.py_old b/src/pomona/storage_old/devicetree.py_old deleted file mode 100644 index dfa0d5ffa..000000000 --- a/src/pomona/storage_old/devicetree.py_old +++ /dev/null @@ -1,977 +0,0 @@ - -import os - -from udev import * - -class DeviceTree: - def __init__(self, storage): - self.installer = storage.installer - self.storage = storage - - # internal data members - self._devices = [] - self._actions = [] - - self._ignoredDisks = [] - for disk in self.storage.ignoredDisks: - self.addIgnoredDisk(disk) - - def addIgnoredDisk(self, disk): - self._ignoredDisks.append(disk) - lvm.lvm_cc_addFilterRejectRegexp(disk) - - def _handleInconsistencies(self, device): - def reinitializeVG(vg): - # First we remove VG data - try: - vg.destroy() - except DeviceError: - # the pvremoves will finish the job. - self.installer.log.debug("There was an error destroying the VG %s." % vg.name) - - # remove VG device from list. - self._removeDevice(vg) - - for parent in vg.parents: - parent.format.destroy() - - # Give the vg the a default format - kwargs = {"uuid": parent.uuid, - "label": parent.diskLabel, - "device": parent.path, - "exists": parent.exists} - parent.format = formats.getFormat(*[""], **kwargs) - - if device.type == "lvmvg": - paths = [] - for parent in device.parents: - paths.append(parent.path) - - # when zeroMbr is true he wont ask. - if not device.complete and (self.zeroMbr or \ - questionReinitILVM(intf=self.intf, \ - vg_name=device.name, pv_names=paths)): - reinitializeVG(device) - - elif not device.complete: - # The user chose not to reinitialize. - # hopefully this will ignore the vg components too. - self._removeDevice(device) - lvm.lvm_cc_addFilterRejectRegexp(device.name) - lvm.blacklistVG(device.name) - for parent in device.parents: - self._removeDevice(parent, moddisk=False) - lvm.lvm_cc_addFilterRejectRegexp(parent.name) - - return - - elif device.type == "lvmlv": - # we might have already fixed this. - if device not in self._devices or \ - device.name in self._ignoredDisks: - return - - paths = [] - for parent in device.vg.parents: - paths.append(parent.path) - - if not device.complete and (self.zeroMbr or \ - questionReinitILVM(intf=self.intf, \ - lv_name=device.name, pv_names=paths)): - - # destroy all lvs. - for lv in device.vg.lvs: - lv.destroy() - device.vg._removeLogVol(lv) - self._removeDevice(lv) - - reinitializeVG(device.vg) - - elif not device.complete: - # ignore all the lvs. - for lv in device.vg.lvs: - self._removeDevice(lv) - lvm.lvm_cc_addFilterRejectRegexp(lv.name) - # ignore the vg - self._removeDevice(device.vg) - lvm.lvm_cc_addFilterRejectRegexp(device.vg.name) - lvm.blacklistVG(device.vg.name) - # ignore all the pvs - for parent in device.vg.parents: - self._removeDevice(parent, moddisk=False) - lvm.lvm_cc_addFilterRejectRegexp(parent.name) - return - - def populate(self): - """ Locate all storage devices. """ - # each iteration scans any devices that have appeared since the - # previous iteration - old_devices = [] - ignored_devices = [] - while True: - devices = [] - new_devices = udev_get_block_devices() - - for new_device in new_devices: - found = False - for old_device in old_devices: - if old_device['name'] == new_device['name']: - found = True - break - - if not found: - devices.append(new_device) - - if len(devices) == 0: - # nothing is changing -- we are finished building devices - break - - old_devices = new_devices - self.installer.log.info("Devices to scan: %s" % [d['name'] for d in devices]) - for dev in devices: - self.addUdevDevice(dev) - - # After having the complete tree we make sure that the system - # inconsistencies are ignored or resolved. - for leaf in self.leaves: - self._handleInconsistencies(leaf) - - self.teardownAll() - try: - os.unlink("/etc/mdadm.conf") - except OSError: - self.installer.log.info("Failed to unlink /etc/mdadm.conf") - - def teardownAll(self): - """ Run teardown methods on all devices. """ - for device in self.leaves: - try: - device.teardown(recursive=True) - except (DeviceError, DeviceFormatError, LVMError) 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 e: - log.debug("setup of %s failed: %s" % (device.name, e)) - - def _addDevice(self, newdev): - """ Add a device to the tree. - - Raise ValueError if the device's identifier is already - in the list. - """ - if newdev.path in [d.path for d in self._devices]: - 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) - self.installer.log.debug("added %s (%s) to device tree" % (newdev.name, newdev.type)) - - 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: - return True - - for ignored in self._ignoredDisks: - if ignored == os.path.basename(os.path.dirname(sysfs_path)): - # this is a partition on a disk in the ignore list - return True - - # Ignore partitions found on the raw disks which are part of a - # dmraidset - for set in self.getDevicesByType("dm-raid array"): - for disk in set.parents: - if disk.name == os.path.basename(os.path.dirname(sysfs_path)): - return True - - # 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("loop") or name.startswith("ram"): - return True - - # FIXME: check for virtual devices whose slaves are on the ignore list - - def handleUdevLUKSFormat(self, info, device): - 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 passphrase: - device.format.passphrase = passphrase - else: - (passphrase, isglobal) = getLUKSPassphrase(self.intf, - device, - self.__passphrase) - if isglobal and device.format.status: - self.__passphrase = passphrase - - luks_device = LUKSDevice(device.format.mapName, - parents=[device], - exists=True) - try: - luks_device.setup() - except (LUKSError, CryptoError, DeviceError) as e: - sefl.installer.log.info("setup of %s failed: %s" % (device.format.mapName, e)) - device.removeChild() - else: - self._addDevice(luks_device) - else: - self.installer.log.warning("luks device %s already in the tree" - % device.format.mapName) - - def handleUdevLVMPVFormat(self, info, device): - # lookup/create the VG and LVs - try: - vg_name = udev_device_get_vg_name(info) - except KeyError: - # no vg name means no vg -- we're done with this pv - return - - vg_device = self.getDeviceByName(vg_name) - if vg_device: - vg_device._addDevice(device) - for lv in vg_device.lvs: - try: - lv.setup() - except DeviceError as e: - self.installer.log.info("setup of %s failed: %s" % (lv.name, e)) - else: - try: - vg_uuid = udev_device_get_vg_uuid(info) - 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: - self.installer.log.warning("invalid data for %s: %s" % (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) - - 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) - except KeyError as e: - self.installer.log.warning("invalid data for %s: %s" % (name, e)) - return - - if not lv_names: - self.installer.log.debug("no LVs listed for VG %s" % name) - return - - lvs = [] - for (index, lv_name) in enumerate(lv_names): - name = "%s-%s" % (vg_name, lv_name) - 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) - - try: - lv_device.setup() - except DeviceError as e: - self.installer.log.info("setup of %s failed: %s" - % (lv_device.name, e)) - - def handleUdevMDMemberFormat(self, info, device): - # 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 - # FIXME: why does this exact block appear twice? - 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: - self.installer.log.warning("invalid data for %s: %s" % (name, e)) - return - - # find the first unused minor - minor = 0 - while True: - if self.getDeviceByName("md%d" % minor): - minor += 1 - else: - break - - md_name = "md%d" % minor - md_array = MDRaidArrayDevice(md_name, - level=md_level, - minor=minor, - memberDevices=md_devices, - uuid=md_uuid, - sysfsPath=sysfs_path, - exists=True, - parents=[device]) - try: - md_array.setup() - except (DeviceError, MDRaidError) as e: - self.installer.log.info("setup of md array %s failed: %s" - % (md_array.name, e)) - self._addDevice(md_array) - - def handleUdevDMRaidMemberFormat(self, info, device): - 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. - # FIXME: Can we reformat a raid device? - 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) - - # Create the DMRaidArray - if self.zeroMbr: - cb = lambda: True - else: - cb = lambda: questionInitializeDisk(self.intf, - rs.name) - - # Create the DMRaidArray - if not self.clearPartDisks or \ - rs.name in self.clearPartDisks: - # if the disk contains protected partitions - # we will not wipe the disklabel even if - # clearpart --initlabel was specified - initlabel = self.reinitializeDisks - for protected in self.protectedPartitions: - disk_name = re.sub(r'p\d+$', '', protected) - if disk_name != protected and \ - disk_name == rs.name: - initlabel = False - break - - try: - dm_array = DMRaidArrayDevice(rs.name, - raidSet=rs, - parents=[device], - initcb=cb, - initlabel=initlabel) - - self._addDevice(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) - except DeviceUserDeniedFormatError: - # We should ignore the dmraid and its components - self.addIgnoredDisk(rs.name) - if _all_ignored(rss): - self.addIgnoredDisk(device.name) - rs.deactivate() - - def handleUdevDeviceFormat(self, info, device): - 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) - - self.installer.log.debug("Type is '%s'" % format_type) - - 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 - return - - # set up the common arguments for the format constructor - args = [format_type] - kwargs = {"uuid": uuid, - "label": label, - "device": device.path, - "exists": True} - - # set up type-specific arguments for the format constructor - if format_type == "crypto_LUKS": - # luks/dmcrypt - kwargs["name"] = "luks-%s" % uuid - elif format_type == "linux_raid_member": - # mdraid - try: - kwargs["mdUuid"] = udev_device_get_md_uuid(info) - except KeyError: - log.debug("mdraid member %s has no md uuid" % name) - elif format_type == "LVM2_member": - # lvm - try: - kwargs["vgName"] = udev_device_get_vg_name(info) - except KeyError as e: - log.debug("PV %s has no vg_name" % name) - try: - kwargs["vgUuid"] = udev_device_get_vg_uuid(info) - except KeyError: - log.debug("PV %s has no vg_uuid" % name) - try: - kwargs["peStart"] = udev_device_get_pv_pe_start(info) - except KeyError: - log.debug("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" - - device.format = formats.getFormat(*args, **kwargs) - - # - # 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) - - 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.values(): - 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. - self.installer.log.debug("Failed to find dm node for %s" % dmdev.name) - continue - - if device is None: - # we couldn't find it, so create it - # first, get a list of the slave devs and look them up - 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 first - 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)) - 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 - self.installer.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 and \ - udev_device_is_dmraid_partition(info, self): - diskname = udev_device_get_dmraid_partition_disk(info) - disk = self.getDeviceByName(diskname) - device = PartitionDevice(name, sysfsPath=sysfs_path, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - exists=True, parents=[disk]) - # DWL FIXME: call self.addUdevPartitionDevice here instead - 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: - self.installer.log.warning("Using generic DM device for %s" % name) - device = DMDevice(name, exists=True, parents=slaves) - self._addDevice(device) - - return device - - def addUdevMDDevice(self, info): - name = udev_device_get_name(info) - 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)) - 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 - self.installer.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 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: - self.installer.log.warning("Using MD RAID device for %s" % name) - 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, IndexError, ValueError) as e: - self.installer.log.warning("Invalid data for %s: %s" % (name, e)) - return - - device = MDRaidArrayDevice(name, - level=md_level, - memberDevices=md_devices, - uuid=md_uuid, - exists=True, - parents=slaves) - self._addDevice(device) - - return device - - def addUdevPartitionDevice(self, info): - name = udev_device_get_name(info) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - device = None - - disk_name = os.path.basename(os.path.dirname(sysfs_path)) - disk = self.getDeviceByName(disk_name) - - if disk is None: - # create a device instance for the disk - path = os.path.dirname(os.path.realpath(sysfs_path)) - new_info = udev_get_block_device(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 - self.installer.log.error("Failure scanning device %s" % disk_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: - # 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. - 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) - device = None - - kwargs = {} - diskType = DiskDevice - self.installer.log.debug("%s is a disk" % name) - cb = lambda: questionInitializeDisk(self.intf, name) - - # if the disk contains protected partitions we will - # not wipe the disklabel even if clearpart --initlabel - # was specified - - initlabel = self.reinitializeDisks - for protected in self.protectedPartitions: - _p = "/sys/%s/%s" % (sysfs_path, protected) - if os.path.exists(os.path.normpath(_p)): - initlabel = False - break - - try: - device = diskType(name, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - sysfsPath=sysfs_path, - initcb=cb, initlabel=initlabel, **kwargs) - except DeviceUserDeniedFormatError: #drive not initialized? - self.addIgnoredDisk(name) - return - - self._addDevice(device) - return device - - def addUdevDevice(self, info): - # FIXME: this should be broken up into more discrete chunks - name = udev_device_get_name(info) - uuid = udev_device_get_uuid(info) - sysfs_path = udev_device_get_sysfs_path(info) - - if self.isIgnored(info): - self.installer.log.debug("Ignoring %s (%s)" % (name, sysfs_path)) - return - - self.installer.log.debug("Scanning %s (%s)..." % (name, sysfs_path)) - device = self.getDeviceByName(name) - - # - # The first step is to either look up or create the device - # - if udev_device_is_dm(info): - self.installer.log.debug("%s is a device-mapper device" % name) - # try to look up the device - if device is None and uuid: - # try to find the device by uuid - device = self.getDeviceByUuid(uuid) - - if device is None: - device = self.addUdevDMDevice(info) - elif udev_device_is_md(info): - self.installer.log.debug("%s is an md device" % name) - if device is None and 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): - self.installer.log.debug("%s is a cdrom" % name) - if device is None: - device = self.addUdevOpticalDevice(info) - elif udev_device_is_dmraid(info): - # This is special handling to avoid the "unrecognized disklabel" - # code since dmraid member disks won't have a disklabel. We - # use a StorageDevice because DiskDevices need disklabels. - # Quite lame, but it doesn't matter much since we won't use - # the StorageDevice instances for anything. - self.installer.log.debug("%s is part of a dmraid" % name) - if device is None: - device = StorageDevice(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): - if device is None: - device = self.addUdevDiskDevice(info) - elif udev_device_is_partition(info): - self.installer.log.debug("%s is a partition" % name) - if device is None: - device = self.addUdevPartitionDevice(info) - - # now handle the device's formatting - self.handleUdevDeviceFormat(info, device) - - def getDeviceBySysfsPath(self, path): - found = None - for device in self._devices: - if device.sysfsPath == path: - found = device - break - - return found - - def getDeviceByUuid(self, uuid): - found = None - for device in self._devices: - if device.uuid == uuid: - found = device - break - elif device.format.uuid == uuid: - found = device - break - - return found - - def getDeviceByLabel(self, label): - found = None - for device in self._devices: - _label = getattr(device.format, "label", None) - if not _label: - continue - - if _label == label: - found = device - break - - return found - - def getDeviceByName(self, name): - self.installer.log.debug("Looking for device '%s'..." % name) - self.installer.log.debug("All devices: %s" % self._devices) - found = None - for device in self._devices: - if device.name == name: - found = device - break - - self.installer.log.debug("Found %s" % 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)] - - @property - def devices(self): - """ Dict with device path keys and Device values. """ - devices = {} - - for device in self._devices: - if device.path in devices: - raise DeviceTreeError("duplicate paths in device tree") - - devices[device.path] = 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: - if dev.format and getattr(dev.format, "label", None): - 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): - # find device in the tree - device = None - if devspec.startswith("UUID="): - # device-by-uuid - uuid = devspec.partition("=")[2] - device = self.uuids.get(uuid) - if device is None: - self.installer.log.error("Failed to resolve device %s" % devspec) - elif devspec.startswith("LABEL="): - # device-by-label - label = devspec.partition("=")[2] - device = self.labels.get(label) - if device is None: - self.installer.log.error("Failed to resolve device %s" % devspec) - elif devspec.startswith("/dev/"): - # device path - device = self.devices.get(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: - self.installer.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" - self.installer.log.debug("Found device '%s' in tree" % devstr) - if device and device.format and \ - device.format.type == "luks": - map_name = device.format.mapName - self.installer.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) - - if device: - self.installer.log.debug("Resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type)) - else: - self.installer.log.debug("Failed to resolve '%s'" % devspec) - return device diff --git a/src/pomona/storage_old/errors.py b/src/pomona/storage_old/errors.py deleted file mode 100644 index 7a02dc084..000000000 --- a/src/pomona/storage_old/errors.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/python - -class StorageError(Exception): - 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 FSError(DeviceFormatError): - pass - -class FSResizeError(FSError): - pass - -class FSMigrateError(FSError): - pass - -class LUKSError(DeviceFormatError): - pass - -class MDMemberError(DeviceFormatError): - pass - -class PhysicalVolumeError(DeviceFormatError): - pass - -class SwapSpaceError(DeviceFormatError): - pass - -# devicelibs -class SwapError(StorageError): - pass - -class SuspendError(SwapError): - pass - -class OldSwapError(SwapError): - pass - -class MDRaidError(StorageError): - pass - -class DMError(StorageError): - pass - -class LVMError(StorageError): - pass - -class CryptoError(StorageError): - pass - -# DeviceTree -class DeviceTreeError(StorageError): - pass - -# DeviceAction -class DeviceActionError(StorageError): - pass - -# partitioning -class PartitioningError(StorageError): - pass - -class PartitioningWarning(StorageError): - pass - -# udev -class UdevError(StorageError): - pass diff --git a/src/pomona/storage_old/formats/__init__.py b/src/pomona/storage_old/formats/__init__.py deleted file mode 100644 index bd5d41495..000000000 --- a/src/pomona/storage_old/formats/__init__.py +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/python - -import os - -from util import notify_kernel -from constants import * - -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") -default_boot_fstypes = ("ext3", "ext2") -def get_default_filesystem_type(boot=None): - if boot: - fstypes = default_boot_fstypes - else: - fstypes = default_fstypes - - for fstype in 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(fstypes)) - -def getFormat(installer, 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(installer, *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, e: - #log.debug("import of device format module '%s' failed" % mod_name) - pass - -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: - """ Generic device format. """ - _type = None - _name = "Unknown" - _udevTypes = [] - partedFlag = None - _formattable = False # can be formatted - _supported = False # is supported - _linuxNative = False # for clearpart - _resizable = False # can be resized - _bootable = False # can be used as boot - _migratable = False # can be migrated - _maxSize = 0 # maximum size in MB - _minSize = 0 # minimum size in MB - _dump = False - _check = False - - def __init__(self, installer, *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.installer = installer - - self.device = kwargs.get("device") - self.uuid = kwargs.get("uuid") - self.exists = kwargs.get("exists") - self.options = kwargs.get("options") - self._migrate = False - - # don't worry about existence if this is a DeviceFormat instance - #if self.__class__ is DeviceFormat: - # self.exists = True - - 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): - pass - - def notifyKernel(self): - if not self.device: - return - - if self.device.startswith("/dev/mapper/"): - try: - name = dm_node_from_name(os.path.basename(self.device)) - except Exception, e: - self.installer.log.warning("Failed to get dm node for %s" % self.device) - return - elif self.device: - name = os.path.basename(self.device) - - path = get_sysfs_path_by_name(name) - try: - notify_kernel(path, action="change") - except Exception, e: - self.installer.log.warning("Failed to notify kernel of change: %s" % e) - - def create(self, *args, **kwargs): - # 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") - - def destroy(self, *args, **kwargs): - # zero out the 1MB at the beginning and end of the device in the - # hope that it will wipe any metadata from filesystems that - # previously occupied this device - self.installer.log.debug("Zeroing out beginning and end of %s..." % self.device) - try: - fd = os.open(self.device, os.O_RDWR) - buf = '\0' * 1024 * 1024 - os.write(fd, buf) - os.lseek(fd, -1024 * 1024, 2) - os.write(fd, buf) - os.close(fd) - except OSError as e: - if getattr(e, "errno", None) == 28: # No space left in device - pass - else: - self.installer.log.error("Error zeroing out %s: %s" % (self.device, e)) - os.close(fd) - except Exception as e: - self.installer.log.error("error zeroing out %s: %s" % (self.device, e)) - os.close(fd) - - self.exists = False - - def setup(self, *args, **kwargs): - 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): - pass - - @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 resizable(self): - """ Can formats of this type be resized? """ - return self._resizable - - @property - def bootable(self): - """ Is this format type suitable for a boot partition? """ - return self._bootable - - @property - def migratable(self): - """ Can formats of this type be migrated? """ - return self._migratable - - @property - def migrate(self): - return self._migrate - - @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 - -collect_device_format_classes() diff --git a/src/pomona/storage_old/formats/fs.py b/src/pomona/storage_old/formats/fs.py deleted file mode 100644 index 2a18fe715..000000000 --- a/src/pomona/storage_old/formats/fs.py +++ /dev/null @@ -1,948 +0,0 @@ -# filesystems.py - -import os -import tempfile - -import util - -from ..errors import * -from . import DeviceFormat, register_device_format - -import gettext -_ = lambda x: gettext.ldgettext("pomona", x) - -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() - -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 - _migratefs = "" # fs migration utility - _defaultFormatOptions = [] # default options passed to mkfs - _defaultMountOptions = ["defaults"] # default options passed to mount - _defaultLabelOptions = [] - _defaultCheckOptions = [] - _defaultMigrateOptions = [] - _migrationTarget = None - lostAndFoundContext = None - - def __init__(self, installer, *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.") - - self.installer = installer - - DeviceFormat.__init__(self, self.installer, *args, **kwargs) - # TODO: fsprofiles and other ways to add format args - self.mountpoint = kwargs.get("mountpoint") - self.mountopts = kwargs.get("mountopts") - self.label = kwargs.get("label") - - # filesystem size does not necessarily equal device size - self._size = kwargs.get("size") - self._mountpoint = None # the current mountpoint when mounted - if self.exists: - self._size = self._getExistingSize() - - self._targetSize = self._size - - if self.supported: - self.loadModule() - - 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 = None - 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. - """ - size = 0 - - if self.mountable: - origMountPoint = self._mountpoint - - tmppath = tempfile.mkdtemp(prefix='getsize-', dir='/tmp') - self.mount(mountpoint=tmppath, options="ro") - buf = os.statvfs(tmppath) - self.unmount() - os.rmdir(tmppath) - - self._mountpoint = origMountPoint - - size = (buf.f_frsize * buf.f_blocks) / 1024.0 / 1024.0 - - return size - - @property - def currentSize(self): - """ The filesystem's current actual size. """ - size = 0 - if self.exists: - size = self._size - return float(size) - - def _getFormatOptions(self, options=None): - argv = [] - if options and isinstance(options, list): - argv.extend(options) - argv.extend(self.defaultFormatOptions) - argv.append(self.device) - return argv - - def doFormat(self, *args, **kwargs): - """ Create the filesystem. - - Arguments: - - None - - Keyword Arguments: - - intf -- InstallInterface instance - options -- list of options to pass to mkfs - - """ - intf = kwargs.get("intf") - 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) - - self.installer.window = None - self.installer.window = self.installer.intf.progressWindow(_("Formatting"), - _("Creating filesystem on %s...") % (self.device,), - 100, pulse = True) - - try: - rc = util.execWithPulseProgress(self.mkfsProg, - argv, - stdout="/dev/tty5", - stderr="/dev/tty5", - progress=w) - except Exception as e: - raise FormatCreateError(e, self.device) - finally: - if self.installer.window: - self.installer.window.pop() - - if rc: - raise FormatCreateError("format failed: %s" % rc, self.device) - - self.exists = True - self.notifyKernel() - - def doMigrate(self, intf=None): - if not self.exists: - raise FSError("filesystem has not been created") - - if not self.migratable or not self.migrate: - return - - if not os.path.exists(self.device): - raise FSError("device does not exist") - - # if journal already exists skip - if isys.ext2HasJournal(self.device): - self.installer.log.info("Skipping migration of %s, has a journal already." % self.device) - return - - argv = self._defaultMigrateOptions[:] - argv.append(self.device) - try: - rc = iutil.execWithRedirect(self.migratefsProg, - argv, - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - except Exception as e: - raise FSMigrateError("filesystem migration failed: %s" % e, self.device) - - if rc: - raise FSMigrateError("filesystem migration failed: %s" % rc, self.device) - - # the other option is to actually replace this instance with an - # instance of the new filesystem type. - self._type = self.migrationTarget - - @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 - - Keyword Arguments: - - intf -- InstallInterface instance - - """ - intf = kwargs.get("intf") - - 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(intf=intf) - - w = None - if intf: - w = intf.progressWindow(_("Resizing"), - _("Resizing filesystem on %s...") - % (self.device,), - 100, pulse = True) - - try: - rc = iutil.execWithPulseProgress(self.resizefsProg, - self.resizeArgs, - stdout="/dev/tty5", - stderr="/dev/tty5", - progress=w) - except Exception as e: - raise FSResizeError(e, self.device) - finally: - if w: - w.pop() - - if rc: - raise FSResizeError("resize failed: %s" % rc, self.device) - - # 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 doCheck(self, intf=None): - 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") - - w = None - if intf: - w = intf.progressWindow(_("Checking"), - _("Checking filesystem on %s...") - % (self.device), - 100, pulse = True) - - try: - rc = iutil.execWithPulseProgress(self.fsckProg, - self._getCheckArgs(), - stdout="/dev/tty5", - stderr="/dev/tty5", - progress = w) - except Exception as e: - raise FSError("filesystem check failed: %s" % e) - finally: - if w: - w.pop() - - if rc >= 4: - raise FSError("filesystem check failed: %s" % rc) - - 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 = iutil.execWithRedirect("modprobe", [module], - stdout="/dev/tty5", stderr="/dev/tty5", - searchPath=1) - except Exception as e: - self.installer.log.error("Could not load kernel module %s: %s" % (module, e)) - self._supported = False - return - - if rc: - self.installer.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 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) - mountpoint = os.path.normpath("%s/%s" % (chroot, mountpoint)) - util.mkdirChain(mountpoint) - - # passed in options override default options - if not options or not isinstance(options, str): - options = self.options - - try: - rc = isys.mount(self.device, mountpoint, - fstype=self.mountType, - options=options, - bindMount=isinstance(self, BindFS)) - except Exception as e: - raise FSError("mount failed: %s" % e) - - if rc: - raise FSError("mount failed: %s" % rc) - - self._mountpoint = mountpoint - - 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 = isys.umount(self._mountpoint, removeDir = False) - 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 = iutil.execWithRedirect(self.labelfsProg, - argv, - stderr="/dev/tty5", - searchPath=1) - if rc: - raise FSError("label failed") - - self.label = label - self.notifyKernel() - - @property - def isDirty(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 migratefsProg(self): - """ Program used to migrate filesystems of this type. """ - return self._migratefs - - @property - def migrationTarget(self): - return self._migrationTarget - - @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]: - if not prog: - continue - - if not filter(lambda d: os.access("%s/%s" % (d, prog), os.X_OK), - os.environ["PATH"].split(":")): - 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): - return (self.mountType in kernel_filesystems) or \ - (os.access("/sbin/mount.%s" % (self.mountType,), os.X_OK)) - - @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 migratable(self): - """ Can filesystems of this type be migrated? """ - return bool(self._migratable and self.migratefsProg and - filter(lambda d: os.access("%s/%s" - % (d, self.migratefsProg,), - os.X_OK), - os.environ["PATH"].split(":")) and - self.migrationTarget) - - def _setMigrate(self, migrate): - if not migrate: - self._migrate = migrate - return - - if self.migratable and self.exists: - self._migrate = migrate - else: - raise ValueError("cannot set migrate on non-migratable filesystem") - - migrate = property(lambda f: f._migrate, lambda f,m: f._setMigrate(m)) - - @property - def type(self): - _type = self._type - if self.migrate: - _type = self.migrationTarget - - return _type - - @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 - - -class Ext2FS(FS): - """ ext2 filesystem. """ - _type = "ext2" - _mkfs = "mke2fs" - _modules = ["ext2"] - _resizefs = "resize2fs" - _labelfs = "e2label" - _fsck = "e2fsck" - _formattable = True - _supported = True - _resizable = True - _bootable = True - _linuxNative = True - _maxSize = 8 * 1024 * 1024 - _minSize = 0 - _defaultFormatOptions = [] - _defaultMountOptions = ["defaults"] - _defaultCheckOptions = ["-f", "-p", "-C", "0"] - _dump = True - _check = True - _migratable = True - _migrationTarget = "ext3" - _migratefs = "tune2fs" - _defaultMigrateOptions = ["-j"] - - @property - def minSize(self): - """ Minimum size for this filesystem in MB. """ - size = self._minSize - if self.exists and os.path.exists(self.device): - buf = util.execWithCapture(self.resizefsProg, - ["-P", self.device], - stderr="/dev/tty5") - size = None - for line in buf.splitlines(): - if "minimum size of the filesystem:" not in line: - continue - - (text, sep, minSize) = line.partition(": ") - - size = int(minSize) / 1024.0 - - if size is None: - self.installer.log.warning("failed to get minimum size for %s filesystem " - "on %s" % (self.mountType, self.device)) - size = self._minSize - - return size - - @property - def isDirty(self): - return isys.ext2IsDirty(self.device) - - @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"] - _migrationTarget = "ext4" - _modules = ["ext3"] - _defaultMigrateOptions = ["-O", "extents"] - - @property - def migratable(self): - """ Can filesystems of this type be migrated? """ - return (flags.cmdline.has_key("ext4migrate") and - Ext2FS.migratable) - -register_device_format(Ext3FS) - - -class Ext4FS(Ext3FS): - """ ext4 filesystem. """ - _type = "ext4" - _bootable = False - _defaultFormatOptions = ["-t", "ext4"] - _migratable = False - _modules = ["ext4"] - -register_device_format(Ext4FS) - - -class FATFS(FS): - """ FAT filesystem. """ - _type = "vfat" - _mkfs = "mkdosfs" - _modules = ["vfat"] - _labelfs = "dosfslabel" - _fsck = "dosfsck" - _formattable = True - _maxSize = 1024 * 1024 - _defaultMountOptions = ["umask=0077", "shortname=winnt"] - -register_device_format(FATFS) - - -class EFIFS(FATFS): - _type = "efi" - _mountType = "vfat" - _modules = ["vfat"] - _name = "EFI System Partition" - _minSize = 50 - _maxSize = 256 - _bootable = True - - @property - def supported(self): - import platform - p = platform.getPlatform(None) - return (isinstance(p, platform.EFI) and - p.isEfi 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 - _bootable = False - _maxLabelChars = 256 - _supported = True - _dump = True - _check = True - _maxSize = 16 * 1024 * 1024 - - 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 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 - -register_device_format(XFS) - -class NTFS(FS): - """ ntfs filesystem. """ - _type = "ntfs" - _resizefs = "ntfsresize" - _fsck = "ntfsresize" - _resizable = True - _minSize = 1 - _maxSize = 16 * 1024 * 1024 - _defaultMountOptions = ["defaults"] - _defaultCheckOptions = ["-c"] - - @property - def minSize(self): - """ The minimum filesystem size in megabytes. """ - size = self._minSize - if self.exists and os.path.exists(self.device): - minSize = None - buf = iutil.execWithCapture(self.resizefsProg, - ["-m", self.device], - stderr = "/dev/tty5") - for l in buf.split("\n"): - if not l.startswith("Minsize"): - continue - try: - min = l.split(":")[1].strip() - minSize = int(min) + 250 - except Exception, e: - minSize = None - self.installer.log.warning("Unable to parse output for minimum size on %s: %s" %(self.device, e)) - - if minSize is None: - self.installer.log.warning("Unable to discover minimum size of filesystem " - "on %s" %(self.device,)) - else: - size = minSize - - return size - - @property - def resizeArgs(self): - # You must supply at least two '-f' options to ntfsresize or - # the proceed question will be presented to you. - argv = ["-ff", "-s", "%dM" % (self.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 :") - - @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 - _bootable = False - _linuxNative = False - _dump = False - _check = False - _migratable = 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 - -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 - -register_device_format(BindFS) diff --git a/src/pomona/storage_old/formats/lvmpv.py b/src/pomona/storage_old/formats/lvmpv.py deleted file mode 100644 index b1ef617d5..000000000 --- a/src/pomona/storage_old/formats/lvmpv.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/python - -from parted import PARTITION_LVM -from . import DeviceFormat, register_device_format -from ..errors import * -from ..devicelibs import lvm - -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 - - 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 - - """ - 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", 0.1875) # in MB - - def probe(self): - """ Probe for any missing information about this device. """ - 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. """ - 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) - self.exists = True - self.notifyKernel() - - def destroy(self, *args, **kwargs): - """ Destroy the format. """ - 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/src/pomona/storage_old/formats/swap.py b/src/pomona/storage_old/formats/swap.py deleted file mode 100644 index 289f98555..000000000 --- a/src/pomona/storage_old/formats/swap.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/python - -from parted import PARTITION_SWAP - -from . import DeviceFormat, register_device_format - -class SwapSpace(DeviceFormat): - """ Swap space """ - _type = "swap" - _name = None - _udevTypes = ["swap"] - partedFlag = PARTITION_SWAP - _formattable = True # can be formatted - _supported = True # is supported - _linuxNative = True # for clearpart - - def __init__(self, installer, *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 - - """ - self.installer = installer - DeviceFormat.__init__(self, self.installer, *args, **kwargs) - - self.priority = kwargs.get("priority") - self.label = kwargs.get("label") - - 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: - self.installer.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. """ - 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. """ - 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. """ - if self.exists: - raise SwapSpaceError("format already exists") - - if self.status: - raise SwapSpaceError("device exists and is active") - - DeviceFormat.create(self, *args, **kwargs) - swap.mkswap(self.device, label=self.label) - self.exists = True - - -register_device_format(SwapSpace) diff --git a/src/pomona/storage_old/partitioning.py b/src/pomona/storage_old/partitioning.py deleted file mode 100644 index 97fd49ddb..000000000 --- a/src/pomona/storage_old/partitioning.py +++ /dev/null @@ -1,1075 +0,0 @@ -#!/usr/bin/python - -import os -import parted - -from errors import * -from constants import * - -import gettext -_ = lambda x: gettext.ldgettext("pomona", x) - -def _createFreeSpacePartitions(installer): - # get a list of disks that have at least one free space region of at - # least 100MB - disks = [] - for disk in installer.ds.storage.disks: - if disk.name not in installer.ds.storage.clearDisks: - continue - - partedDisk = disk.partedDisk - part = disk.partedDisk.getFirstPartition() - while part: - if not part.type & parted.PARTITION_FREESPACE: - part = part.nextPartition() - continue - - if part.getSize(unit="MB") > 100: - disks.append(disk) - break - - part = part.nextPartition() - - # create a separate pv partition for each disk with free space - devs = [] - for disk in disks: - if installer.ds.storage.encryptedAutoPart: - fmt_type = "luks" - else: - fmt_type = "lvmpv" - part = installer.ds.storage.newPartition(fmt_type=fmt_type, - size=1, - grow=True, - disks=[disk]) - installer.ds.storage.createDevice(part) - devs.append(part) - - return (disks, devs) - -def _schedulePartitions(installer, disks): - # - # Convert storage.autoPartitionRequests into Device instances and - # schedule them for creation - # - # First pass is for partitions only. We'll do LVs later. - # - installer.log.debug("REQUESTS: %s" % installer.ds.storage.autoPartitionRequests) - for request in installer.ds.storage.autoPartitionRequests: - installer.log.debug("_schedulePartitions: Request: %s" % request) - if request.asVol: - continue - - if request.fstype is None: - request.fstype = installer.ds.storage.defaultFSType - - dev = installer.ds.storage.newPartition(fmt_type=request.fstype, - size=request.size, - grow=request.grow, - maxsize=request.maxSize, - mountpoint=request.mountpoint, - disks=disks, - weight=request.weight) - - # schedule the device for creation - installer.ds.storage.createDevice(dev) - - # make sure preexisting broken lvm/raid configs get out of the way - return - -def _scheduleLVs(installer, devs): - if installer.ds.storage.encryptedAutoPart: - pvs = [] - for dev in devs: - pv = LUKSDevice("luks-%s" % dev.name, - format=getFormat("lvmpv", device=dev.path), - size=dev.size, - parents=dev) - pvs.append(pv) - installer.ds.storage.createDevice(pv) - else: - pvs = devs - - # create a vg containing all of the autopart pvs - vg = installer.ds.storage.newVG(pvs=pvs) - installer.ds.storage.createDevice(vg) - - # - # Convert storage.autoPartitionRequests into Device instances and - # schedule them for creation. - # - # Second pass, for LVs only. - for request in installer.ds.storage.autoPartitionRequests: - if not request.asVol: - continue - - if request.fstype is None: - request.fstype = installer.ds.storage.defaultFSType - - # FIXME: move this to a function and handle exceptions - dev = installer.ds.storage.newLV(vg=vg, - fmt_type=request.fstype, - mountpoint=request.mountpoint, - grow=request.grow, - maxsize=request.maxSize, - size=request.size) - - # schedule the device for creation - installer.ds.storage.createDevice(dev) - -def clearPartitions(installer): - """ Clear partitions and dependent devices from disks. - - Arguments: - - storage -- a storage.Storage instance - - Keyword arguments: - - None - - NOTES: - - - Needs some error handling, especially for the parted bits. - - """ - storage = installer.ds.storage - - # we are only interested in partitions that physically exist - partitions = [p for p in storage.partitions if p.exists] - disks = [] # a list of disks from which we've removed partitions - clearparts = [] # list of partitions we'll remove - for part in partitions: - # if we got a list of disks to clear, make sure this one's on it - if storage.clearDisks and part.disk.name not in storage.clearDisks: - continue - - # don't clear partitions holding install media - #if part.name in storage.protectedPartitions: - # continue - - # we don't want to fool with extended partitions, freespace, &c - if part.partType not in (parted.PARTITION_NORMAL, - parted.PARTITION_LOGICAL): - continue - - installer.log.debug("clearPartitions: Clearing %s" % part.name) - - # XXX is there any argument for not removing incomplete devices? - # -- maybe some RAID devices - devices = storage.deviceDeps(part) - while devices: - installer.log.debug("clearPartitions: Devices to remove: %s" % ([d.name for d in devices],)) - leaves = [d for d in devices if d.isleaf] - installer.log.debug("clearPartitions: Leaves to remove: %s" % ([d.name for d in leaves],)) - for leaf in leaves: - storage.destroyDevice(leaf) - devices.remove(leaf) - - #installer.log.debug("clearPartitions: Partitions left: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions]) - disk_name = os.path.basename(part.partedPartition.disk.device.path) - if disk_name not in disks: - disks.append(disk_name) - - clearparts.append(part) - - for part in clearparts: - storage.destroyDevice(part) - - # now remove any empty extended partitions - removeEmptyExtendedPartitions(installer) - -def removeEmptyExtendedPartitions(installer): - storage = installer.ds.storage - for disk in storage.disks: - installer.log.debug("removeEmptyExtendedPartitions: Checking whether disk %s has an empty extended" % disk.name) - extended = disk.partedDisk.getExtendedPartition() - logical_parts = disk.partedDisk.getLogicalPartitions() - installer.log.debug("removeEmptyExtendedPartitions: Extended is %s ; logicals is %s" % (extended, [p.getDeviceNodeName() for p in logical_parts])) - if extended and not logical_parts: - installer.log.debug("removeEmptyExtendedPartitions: Removing empty extended partition from %s" % disk.name) - extended_name = devicePathToName(extended.getDeviceNodeName()) - extended = storage.devicetree.getDeviceByName(extended_name) - storage.destroyDevice(extended) - #disk.partedDisk.removePartition(extended.partedPartition) - -def doAutoPartition(installer): - if installer.dispatch.dir == DISPATCH_BACK: - installer.ds.storage.reset() - return - - installer.log.debug("All names: %s" % [d.name for d in installer.ds.storage.devicetree.devices.values()]) - - disks = [] - devs = [] - - if installer.ds.storage.doAutoPart: - clearPartitions(installer) - - if installer.ds.storage.doAutoPart: - (disks, devs) = _createFreeSpacePartitions(installer) - - if disks == []: - installer.intf.messageWindow(_("Error Partitioning"), - _("Could not find enough free space " - "for automatic partitioning, please " - "use another partitioning method.")) - return DISPATCH_BACK - - _schedulePartitions(installer, disks) - - # sanity check the individual devices - installer.log.warning("doAutoPartition: not sanity checking devices because I don't know how yet") - - # run the autopart function to allocate and grow partitions - try: - doPartitioning(installer) - - if installer.ds.storage.doAutoPart: - _scheduleLVs(installer, devs) - - # grow LVs - growLVM(installer) - except PartitioningWarning as msg: - installer.intf.messageWindow(_("Warnings During Automatic Partitioning"), - _("Following warnings occurred during automatic " - "partitioning:\n\n%s") % (msg,),) - log.warning(msg) - except PartitioningError as msg: - # restore drives to original state - installer.ds.storage.reset() - #installer.dispatch.skipStep("partition", skip = 0) - installer.intf.messageWindow(_("Error Partitioning"), - _("Could not allocate requested partitions: \n\n" - "%s.%s") % (msg, extra)) - return - - # sanity check the collection of devices - installer.log.warning("not sanity checking storage config because I don't know how yet") - # now do a full check of the requests - (errors, warnings) = installer.ds.storage.sanityCheck() - if warnings: - for warning in warnings: - installer.log.warning(warning) - if errors: - errortxt = "\n".join(errors) - installer.intf.messageWindow(_("Automatic Partitioning Errors"), - _("The following errors occurred with your " - "partitioning:\n\n%s\n\n" - "This can happen if there is not enough " - "space on your hard drive(s) for the " - "installation.\n\n" - "Press 'OK' to choose a different partitioning option.") - % (errortxt,),) - - installer.ds.storage.reset() - #return DISPATCH_BACK - return INSTALL_OK - -def doPartitioning(installer): - """ 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 arguments: - - exclusiveDisks -- list of names of disks to use - - """ - storage = installer.ds.storage - disks = storage.disks - - exclusiveDisks = storage.clearDisks - if exclusiveDisks: - disks = [d for d in disks if d.name in exclusiveDisks] - - for disk in disks: - disk.setup() - - partitions = storage.partitions - for part in partitions: - part.req_bootable = False - if not part.exists: - # start over with flexible-size requests - part.req_size = part.req_base_size - - # FIXME: isn't there a better place for this to happen? - #try: - # bootDev = anaconda.platform.bootDevice() - #except DeviceError: - # bootDev = None - - #if bootDev: - # bootDev.req_bootable = True - - # FIXME: make sure non-existent partitions have empty parents list - allocatePartitions(installer, disks, partitions) - growPartitions(installer, disks, partitions) - # 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 partitions: - part.updateName() - - # XXX hack -- if we created any extended partitions we need to add - # them to the tree now - for disk in disks: - extended = disk.partedDisk.getExtendedPartition() - if not extended: - continue - - extendedName = devicePathToName(extended.getDeviceNodeName()) - 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 - storage.createDevice(device) - -def allocatePartitions(installer, disks, partitions): - """ 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. - """ - installer.log.debug("allocatePartitions: disks=%s ; partitions=%s" % (disks, - partitions)) - new_partitions = [p for p in partitions if not p.exists] - new_partitions.sort(cmp=partitionCompare) - - # XXX is this needed anymore? - partedDisks = {} - for disk in disks: - if disk.path not in partedDisks.keys(): - partedDisks[disk.path] = disk.partedDisk #.duplicate() - - # remove all newly added partitions from the disk - installer.log.debug("removing all non-preexisting from disk(s)") - for _part in new_partitions: - if _part.partedPartition: - if _part.isExtended: - # these get removed last - continue - #_part.disk.partedDisk.removePartition(_part.partedPartition) - partedDisk = partedDisks[_part.disk.partedDisk.device.path] - installer.log.debug("removing part %s (%s) from disk %s (%s)" % - (_part.partedPartition.path, - [p.path for p in _part.partedPartition.disk.partitions], - partedDisk.device.path, - [p.path for p in partedDisk.partitions])) - - partedDisk.removePartition(_part.partedPartition) - _part.partedPartition = None - _part.disk = None - - # remove empty extended so it doesn't interfere - extended = partedDisk.getExtendedPartition() - if extended and not partedDisk.getLogicalPartitions(): - installer.log.debug("removing empty extended partition") - #partedDisk.minimizeExtendedPartition() - partedDisk.removePartition(extended) - - 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.disk: - # we have a already selected a disk for this request - req_disks = [_part.disk] - elif _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 - - installer.log.debug("allocating partition: %s ; disks: %s ; boot: %s ; " - "primary: %s ; size: %dMB ; grow: %s ; max_size: %s" % - (_part.name, req_disks, _part.req_bootable, _part.req_primary, - _part.req_size, _part.req_grow, _part.req_max_size)) - free = None - use_disk = None - part_type = None - # loop through disks - for _disk in req_disks: - disk = partedDisks[_disk.path] - #for p in disk.partitions: - # installer.log.debug("disk %s: part %s" % (disk.device.path, p.path)) - sectorSize = disk.device.physicalSectorSize - best = None - - installer.log.debug("checking freespace on %s" % _disk.name) - - new_part_type = getNextPartitionType(disk) - if new_part_type is None: - # can't allocate any more partitions on this disk - installer.log.debug("no free partition slots on %s" % _disk.name) - continue - - if _part.req_primary and new_part_type != parted.PARTITION_NORMAL: - # we need a primary slot and none are free on this disk - installer.log.debug("no primary slots available on %s" % _disk.name) - continue - - best = getBestFreeSpaceRegion(installer, disk, - new_part_type, - _part.req_size, - best_free=free, - boot=_part.req_bootable) - - 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 - installer.log.debug("not enough free space for primary -- trying logical") - new_part_type = getNextPartitionType(disk, no_primary=True) - if new_part_type: - best = getBestFreeSpaceRegion(disk, - new_part_type, - _part.req_size, - best_free=free, - boot=_part.req_bootable) - - if best and free != best: - # now we know we are choosing a new free space, - # so update the disk and part type - installer.log.debug("updating use_disk to %s (%s), type: %s" - % (_disk, _disk.name, new_part_type)) - part_type = new_part_type - use_disk = _disk - installer.log.debug("new free: %s (%d-%d / %dMB)" % (best, - best.start, - best.end, - best.getSize())) - free = best - - if free and _part.req_bootable: - # if this is a bootable partition we want to - # use the first freespace region large enough - # to satisfy the request - installer.log.debug("found free space for bootable request") - break - - if free is None: - raise PartitioningError("not enough free space on disks") - - _disk = use_disk - disk = _disk.partedDisk - - # create the extended partition if needed - # TODO: move to a function (disk, free) - if part_type == parted.PARTITION_EXTENDED: - installer.log.debug("creating extended partition") - geometry = parted.Geometry(device=disk.device, - start=free.start, - length=free.length, - end=free.end) - extended = parted.Partition(disk=disk, - type=parted.PARTITION_EXTENDED, - geometry=geometry) - constraint = parted.Constraint(device=disk.device) - # FIXME: we should add this to the tree as well - disk.addPartition(extended, constraint) - - # end proposed function - - # now the extended partition exists, so set type to logical - part_type = parted.PARTITION_LOGICAL - - # recalculate freespace - installer.log.debug("recalculating free space") - free = getBestFreeSpaceRegion(disk, - part_type, - _part.req_size, - boot=_part.req_bootable) - if not free: - raise PartitioningError("not enough free space after " - "creating extended partition") - - # create minimum geometry for this request - # req_size is in MB - sectors_per_track = disk.device.biosGeometry[2] - length = (_part.req_size * (1024 * 1024)) / sectorSize - new_geom = parted.Geometry(device=disk.device, - start=max(sectors_per_track, free.start), - length=length) - - # create maximum and minimum geometries for constraint - start = max(0 , free.start - 1) - max_geom = parted.Geometry(device=disk.device, - start=start, - length=min(length + 1, disk.device.length - start)) - min_geom = parted.Geometry(device=disk.device, - start=free.start + 1, - length=length-1) - - - # create the partition and add it to the disk - partition = parted.Partition(disk=disk, - type=part_type, - geometry=new_geom) - constraint = parted.Constraint(maxGeom=max_geom, minGeom=min_geom) - disk.addPartition(partition=partition, - constraint=constraint) - installer.log.debug("created partition %s of %dMB and added it to %s" % - (partition.getDeviceNodeName(), partition.getSize(), disk)) - - # 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 = disk.getPartitionByPath(_part.path) - -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 - - # bootable partitions to the front - ret -= cmp(part1.req_bootable, part2.req_bootable) * 1000 - - # more specific disk specs to the front of the list - ret += cmp(len(part1.parents), len(part2.parents)) * 500 - - # primary-only to the front of the list - ret -= cmp(part1.req_primary, part2.req_primary) * 200 - - # larger requests go to the front of the list - ret -= cmp(part1.size, part2.size) * 100 - - # fixed size requests to the front - ret += cmp(part1.req_grow, part2.req_grow) * 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 - - if ret > 0: - ret = 1 - elif ret < 0: - ret = -1 - - return ret - -def growPartitions(installer, disks, partitions): - """ Grow all growable partition requests. - - All requests should know what disk they will be on by the time - this function is called. This is reflected in the - PartitionDevice's disk attribute. Note that the req_disks - attribute remains unchanged. - - The total available free space is summed up for each disk and - partition requests are allocated a maximum percentage of the - available free space on their disk based on their own base size. - - Each attempted size means calling allocatePartitions again with - one request's size having changed. - - After taking into account several factors that may limit the - maximum size of a requested partition, we arrive at a firm - maximum number of sectors by which a request can potentially grow. - - An initial attempt is made to allocate the full maximum size. If - this fails, we begin a rough binary search with a maximum of three - iterations to settle on a new size. - - Arguments: - - disks -- a list of all usable disks (DiskDevice instances) - partitions -- a list of all partitions (PartitionDevice - instances) - """ - installer.log.debug("growPartitions: disks=%s, partitions=%s" % - ([d.name for d in disks], [p.name for p in partitions])) - all_growable = [p for p in partitions if p.req_grow] - if not all_growable: - return - - # sort requests by base size in decreasing order - all_growable.sort(key=lambda p: p.req_size, reverse=True) - - installer.log.debug("growable requests are %s" % [p.name for p in all_growable]) - - for disk in disks: - installer.log.debug("growing requests on %s" % disk.name) - for p in disk.partedDisk.partitions: - installer.log.debug(" %s: %s (%dMB)" % (disk.name, p.getDeviceNodeName(), - p.getSize())) - sectorSize = disk.partedDisk.device.physicalSectorSize - # get a list of free space regions on the disk - free = disk.partedDisk.getFreeSpaceRegions() - if not free: - installer.log.debug("no free space on %s" % disk.name) - continue - - # sort the free regions in decreasing order of size - free.sort(key=lambda r: r.length, reverse=True) - disk_free = reduce(lambda x,y: x + y, [f.length for f in free]) - installer.log.debug("total free: %d sectors ; largest: %d sectors (%dMB)" - % (disk_free, free[0].length, free[0].getSize())) - - # make a list of partitions currently allocated on this disk - # -- they're already sorted - growable = [] - disk_total = 0 - for part in all_growable: - #log.debug("checking if part %s (%s) is on this disk" % (part.name, - # part.disk.name)) - if part.disk == disk: - growable.append(part) - disk_total += part.partedPartition.geometry.length - installer.log.debug("add %s (%dMB/%d sectors) to growable total" - % (part.name, part.partedPartition.getSize(), - part.partedPartition.geometry.length)) - installer.log.debug("growable total is now %d sectors" % disk_total) - - # now we loop through the partitions... - # this first loop is to identify obvious chunks of free space that - # will be left over due to max size - leftover = 0 - limited = {} - unlimited_total = 0 - for part in growable: - # calculate max number of sectors this request can grow - req_sectors = part.partedPartition.geometry.length - share = float(req_sectors) / float(disk_total) - max_grow = (share * disk_free) - max_sectors = req_sectors + max_grow - limited[part.name] = False - - if part.req_max_size: - req_max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize - if req_max_sect < max_sectors: - mb = ((max_sectors - req_max_sect) * sectorSize) / (1024*1024) - - installer.log.debug("adding %dMB to leftovers from %s" - % (mb, part.name)) - leftover += (max_sectors - req_max_sect) - limited[part.name] = True - - if not limited[part.name]: - unlimited_total += req_sectors - - # now we loop through the partitions... - for part in growable: - # calculate max number of sectors this request can grow - req_sectors = part.partedPartition.geometry.length - share = float(req_sectors) / float(disk_total) - max_grow = (share * disk_free) - if not limited[part.name]: - leftover_share = float(req_sectors) / float(unlimited_total) - max_grow += leftover_share * leftover - max_sectors = req_sectors + max_grow - max_mb = (max_sectors * sectorSize) / (1024 * 1024) - - installer.log.debug("%s: base_size=%dMB, max_size=%sMB" % - (part.name, part.req_base_size, part.req_max_size)) - installer.log.debug("%s: current_size=%dMB (%d sectors)" % - (part.name, part.partedPartition.getSize(), - part.partedPartition.geometry.length)) - installer.log.debug("%s: %dMB (%d sectors, or %d%% of %d)" % - (part.name, max_mb, max_sectors, share * 100, disk_free)) - - installer.log.debug("checking constraints on max size...") - # don't grow beyond the request's maximum size - if part.req_max_size: - installer.log.debug("max_size: %dMB" % part.req_max_size) - # FIXME: round down to nearest cylinder boundary - req_max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize - if req_max_sect < max_sectors: - max_grow -= (max_sectors - req_max_sect) - max_sectors = req_sectors + max_grow - - # don't grow beyond the resident filesystem's max size - if part.format.maxSize > 0: - installer.log.debug("format maxsize: %dMB" % part.format.maxSize) - # FIXME: round down to nearest cylinder boundary - fs_max_sect = (part.format.maxSize * (1024 * 1024)) / sectorSize - if fs_max_sect < max_sectors: - max_grow -= (max_sectors - fs_max_sect) - max_sectors = req_sectors + max_grow - - # we can only grow as much as the largest free region on the disk - if free[0].length < max_grow: - installer.log.debug("largest free region: %d sectors (%dMB)" % - (free[0].length, free[0].getSize())) - # FIXME: round down to nearest cylinder boundary - max_grow = free[0].length - max_sectors = req_sectors + max_grow - - # Now, we try to grow this partition as close to max_grow - # sectors as we can. - # - # We could call allocatePartitions after modifying this - # request and saving the original value of part.req_size, - # or we could try to use disk.maximizePartition(). - max_size = (max_sectors * sectorSize) / (1024 * 1024) - orig_size = part.req_size - # try the max size to begin with - installer.log.debug("attempting to allocate maximum size: %dMB" % max_size) - part.req_size = max_size - try: - allocatePartitions(installer, disks, partitions) - except PartitioningError, e: - installer.log.debug("max size attempt failed: %s (%dMB)" % (part.name, - max_size)) - part.req_size = orig_size - else: - continue - - installer.log.debug("starting binary search: size=%d max_size=%d" % (part.req_size, max_size)) - count = 0 - op_func = add - increment = max_grow - last_good_size = part.req_size - last_outcome = None - while count < 3: - last_size = part.req_size - increment /= 2 - req_sectors = op_func(req_sectors, increment) - part.req_size = (req_sectors * sectorSize) / (1024 * 1024) - installer.log.debug("attempting size=%dMB" % part.req_size) - count += 1 - try: - allocatePartitions(disks, partitions) - except PartitioningError, e: - installer.log.debug("attempt at %dMB failed" % part.req_size) - op_func = sub - last_outcome = False - else: - op_func = add - last_good_size = part.req_size - last_outcome = True - - if not last_outcome: - part.req_size = last_good_size - installer.log.debug("backing up to size=%dMB" % part.req_size) - try: - allocatePartitions(disks, partitions) - except PartitioningError, e: - raise PartitioningError("failed to grow partitions") - - # reset all requests to their original requested size - for part in partitions: - if part.exists: - continue - part.req_size = part.req_base_size - -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(installer): - """ Grow LVs according to the sizes of the PVs. """ - storage = installer.ds.storage - for vg in storage.vgs: - total_free = vg.freeSpace - if not total_free: - installer.log.debug("vg %s has no free space" % vg.name) - continue - - installer.log.debug("vg %s: %dMB free ; lvs: %s" % (vg.name, vg.freeSpace, - [l.lvname for l in vg.lvs])) - - # figure out how much to grow each LV - grow_amounts = {} - lv_total = vg.size - total_free - installer.log.debug("used: %dMB ; vg.size: %dMB" % (lv_total, vg.size)) - - # This first loop is to calculate percentage-based growth - # amounts. These are based on total free space. - lvs = vg.lvs - lvs.sort(cmp=lvCompare) - for lv in lvs: - if not lv.req_grow or not lv.req_percent: - continue - - portion = (lv.req_percent * 0.01) - grow = portion * vg.vgFree - new_size = lv.req_size + grow - if lv.req_max_size and new_size > lv.req_max_size: - grow -= (new_size - lv.req_max_size) - - if lv.format.maxSize and lv.format.maxSize < new_size: - grow -= (new_size - lv.format.maxSize) - - # clamp growth amount to a multiple of vg extent size - grow_amounts[lv.name] = vg.align(grow) - total_free -= grow - lv_total += grow - - # This second loop is to calculate non-percentage-based growth - # amounts. These are based on free space remaining after - # calculating percentage-based growth amounts. - - # keep a tab on space not allocated due to format or requested - # maximums -- we'll dole it out to subsequent requests - leftover = 0 - for lv in lvs: - installer.log.debug("checking lv %s: req_grow: %s ; req_percent: %s" - % (lv.name, lv.req_grow, lv.req_percent)) - if not lv.req_grow or lv.req_percent: - continue - - portion = float(lv.req_size) / float(lv_total) - grow = portion * total_free - installer.log.debug("grow is %dMB" % grow) - - todo = lvs[lvs.index(lv):] - unallocated = reduce(lambda x,y: x+y, - [l.req_size for l in todo - if l.req_grow and not l.req_percent]) - extra_portion = float(lv.req_size) / float(unallocated) - extra = extra_portion * leftover - installer.log.debug("%s getting %dMB (%d%%) of %dMB leftover space" - % (lv.name, extra, extra_portion * 100, leftover)) - leftover -= extra - grow += extra - installer.log.debug("grow is now %dMB" % grow) - max_size = lv.req_size + grow - if lv.req_max_size and max_size > lv.req_max_size: - max_size = lv.req_max_size - - if lv.format.maxSize and max_size > lv.format.maxSize: - max_size = lv.format.maxSize - - installer.log.debug("max size is %dMB" % max_size) - max_size = max_size - leftover += (lv.req_size + grow) - max_size - grow = max_size - lv.req_size - installer.log.debug("lv %s gets %dMB" % (lv.name, vg.align(grow))) - grow_amounts[lv.name] = vg.align(grow) - - if not grow_amounts: - installer.log.debug("no growable lvs in vg %s" % vg.name) - continue - - # now grow the lvs by the amounts we've calculated above - for lv in lvs: - if lv.name not in grow_amounts.keys(): - continue - lv.size += grow_amounts[lv.name] - - # now there shouldn't be any free space left, but if there is we - # should allocate it to one of the LVs - vg_free = vg.freeSpace - installer.log.debug("vg %s has %dMB free" % (vg.name, vg_free)) - if vg_free: - for lv in lvs: - if not lv.req_grow: - continue - - if lv.req_max_size and lv.size == lv.req_max_size: - continue - - if lv.format.maxSize and lv.size == lv.format.maxSize: - continue - - # first come, first served - projected = lv.size + vg.freeSpace - if lv.req_max_size and projected > lv.req_max_size: - projected = lv.req_max_size - - if lv.format.maxSize and projected > lv.format.maxSize: - projected = lv.format.maxSize - - installer.log.debug("giving leftover %dMB to %s" % (projected - lv.size, - lv.name)) - lv.size = projected - -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 and \ - extended and logical_count < max_logicals: - part_type = parted.PARTITION_LOGICAL - elif primary_count == (disk.maxPrimaryPartitionCount - 1) and \ - not extended and supports_extended: - # last chance to create an extended partition - part_type = parted.PARTITION_EXTENDED - elif no_primary and extended and logical_count < max_logicals: - # create a logical even though we could presumably create a - # primary instead - part_type = parted.PARTITION_LOGICAL - elif not no_primary: - # XXX there is a possiblity that the only remaining free space on - # the disk lies within the extended partition, but we will - # try to create a primary first - part_type = parted.PARTITION_NORMAL - - return part_type - -def getBestFreeSpaceRegion(installer, disk, part_type, req_size, - boot=None, best_free=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 - - """ - installer.log.debug("getBestFreeSpaceRegion: disk=%s part_type=%d req_size=%dMB boot=%s best=%s" % - (disk.device.path, part_type, req_size, boot, best_free)) - extended = disk.getExtendedPartition() - for _range in disk.getFreeSpaceRegions(): - if extended: - # find out if there is any overlap between this region and the - # extended partition - installer.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, e: - # 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): - installer.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 - - installer.log.debug("current free range is %d-%d (%dMB)" % (free_geom.start, - free_geom.end, - free_geom.getSize())) - free_size = free_geom.getSize() - - if req_size <= free_size: - if not best_free or 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 diff --git a/src/pomona/storage_old/udev.py b/src/pomona/storage_old/udev.py deleted file mode 100644 index a9a73774e..000000000 --- a/src/pomona/storage_old/udev.py +++ /dev/null @@ -1,305 +0,0 @@ -#!/usr/bin/python - -import os -import sys - -import util - -def udev_settle(timeout=None): - argv = ["settle"] - if timeout: - argv.append("--timeout=%d" % int(timeout)) - - util.execWithRedirect("udevadm", argv, stderr="/dev/null", searchPath=1) - -def udev_trigger(subsystem=None): - argv = ["trigger"] - if subsystem: - argv.append("--subsystem-match=%s" % subsystem) - - util.execWithRedirect("udevadm", argv, stderr="/dev/null", searchPath=1) - -def udev_get_block_devices(): - #udev_settle(timeout=30) - entries = [] - for path in enumerate_block_devices(): - entry = udev_get_block_device(path) - if entry: - 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("loop") or 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: - return True - - return False - -def enumerate_block_devices(): - top_dir = "/sys/class/block" - devices = [] - for dev_name in os.listdir(top_dir): - if __is_blacklisted_blockdev(dev_name): - continue - full_path = os.path.join(top_dir, dev_name) - link_ref = os.readlink(full_path) - real_path = os.path.join(top_dir, link_ref) - sysfs_path = os.path.normpath(real_path) - devices.append(sysfs_path) - return devices - -def udev_get_block_device(sysfs_path): - if not os.path.exists(sysfs_path): - return None - - db_entry = sysfs_path[4:].replace("/", "\\x2f") - db_root = "/dev/.udev/db" - db_path = os.path.normpath("%s/%s" % (db_root, db_entry)) - if not os.access(db_path, os.R_OK): - return None - - entry = open(db_path).read() - dev = udev_parse_block_entry(entry) - if dev: - # XXX why do we do this? is /sys going to move during installation? - dev['sysfs_path'] = sysfs_path[4:] # strip off the leading '/sys' - dev = udev_parse_uevent_file(dev) - - # now add in the contents of the uevent file since they're handy - return dev - -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_parse_block_entry(buf): - dev = {'name': None, - 'symlinks': []} - - for line in buf.splitlines(): - line.strip() - (tag, sep, val) = line.partition(":") - if not sep: - continue - - if tag == "N": - dev['name'] = val - elif tag == "S": - dev['symlinks'].append(val) - elif tag == "E": - if val.count("=") > 1 and val.count(" ") > 0: - # eg: LVM2_LV_NAME when querying the VG for its LVs - vars = val.split() - vals = [] - var_name = None - for (index, subval) in enumerate(vars): - (var_name, sep, var_val) = subval.partition("=") - if sep: - vals.append(var_val) - - dev[var_name] = vals - else: - (var_name, sep, var_val) = val.partition("=") - if not sep: - continue - - if var_val.count(" "): - # eg: DEVLINKS - var_val = var_val.split() - - dev[var_name] = var_val - - if dev.get("name"): - 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. """ - return udev_info.get("DM_NAME", udev_info["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 uuid and not md_uuid == 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 is the device is an mdraid array device. """ - return info.has_key("MD_METADATA") - -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. """ - 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_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["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_vg_name(info): - return info['LVM2_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_is_dmraid(info): - # Note that this function does *not* identify raid sets. - # Tests to see if device is parto of a dmraid set. - # dmraid and mdriad 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 - #if info.has_key("ID_FS_TYPE") and \ - # info["ID_FS_TYPE"] in DMRaidMember._udevTypes: - # return True - # - return False - -def udev_device_get_dmraid_partition_disk(info): - try: - p_index = info["DM_NAME"].rindex("p") - except (KeyError, AttributeError, ValueError): - return None - - if not info["DM_NAME"][p_index+1:].isdigit(): - return None - - return info["DM_NAME"][:p_index] - -def udev_device_is_dmraid_partition(info, devicetree): - #diskname = udev_device_get_dmraid_partition_disk(info) - #dmraid_devices = devicetree.getDevicesByType("dm-raid array") - # - #for device in dmraid_devices: - # if diskname == device.name: - # return True - # - return False - -if __name__ == "__main__": - for device in udev_get_block_devices(): - print udev_device_get_name(device) - print " Label :", udev_device_get_label(device) - print " UUID :", udev_device_get_uuid(device) - print " Format :", udev_device_get_format(device) - print " Is disk :", udev_device_is_disk(device) - print " Is cdrom:", udev_device_is_cdrom(device) - print " Is part :", udev_device_is_partition(device) - print " Is dm :", udev_device_is_dm(device) - print " Is md :", udev_device_is_md(device) - #syspath = "/sys" + udev_device_get_sysfs_path(device) - #for (key, value) in udev_get_block_device(syspath).items(): - # print " (%s : %s)" % (key, value,) - print