include Makefile.inc
-SUBDIRS = po
+SUBDIRS = isys po
all: subdirs
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,),
]
--- /dev/null
+###############################################################################
+# #
+# 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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+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
--- /dev/null
+
+
+#include <Python.h>
+
+#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;
+}
--- /dev/null
+#ifndef H_ISYS
+#define H_ISYS
+
+
+#endif
--- /dev/null
+#!/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
--- /dev/null
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#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;
+}
--- /dev/null
+
+#ifndef H_MOUNT
+#define H_MOUNT
+
+#define MOUNT_ERR_ERRNO 1
+#define MOUNT_ERR_OTHER 2
+
+#include <sys/mount.h> /* for umount() */
+
+int doPwMount(char *dev, char *where, char *fs, char *options, char **err);
+int mkdirChain(char * origChain);
+
+#endif
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,
--- /dev/null
+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
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):
import os
import tempfile
+import isys
+
import util
from ..errors import *
+++ /dev/null
-#/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 <Enter> 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 <Enter> 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 <Enter> 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("<device "):
- continue
-
- line = line[len("<device "):-len("</device>\n")]
- (data, sep, device) = line.partition(">")
- if not device:
- continue
-
- self.devices[device] = {}
- for pair in data.split():
- try:
- (key, value) = pair.split("=")
- except ValueError:
- continue
-
- self.devices[device][key] = value[1:-1] # strip off quotes
-
- def __getitem__(self, key):
- return self.devices[key]
-
- def get(self, key, default=None):
- return self.devices.get(key, default)
-
-
-class CryptTab(object):
- """ Dictionary-like interface to crypttab entries with map name keys """
- def __init__(self, devicetree, blkidTab=None, chroot=""):
- self.devicetree = devicetree
- self.blkidTab = blkidTab
- self.chroot = chroot
- self.mappings = {}
-
- def parse(self, chroot=""):
- """ Parse /etc/crypttab from an existing installation. """
- if not chroot or not os.path.isdir(chroot):
- chroot = ""
-
- path = "%s/etc/crypttab" % chroot
- log.debug("parsing %s" % path)
- with open(path) as f:
- if not self.blkidTab:
- try:
- self.blkidTab = BlkidTab(chroot=chroot)
- self.blkidTab.parse()
- except Exception:
- self.blkidTab = None
-
- for line in f.readlines():
- (line, pound, comment) = line.partition("#")
- fields = line.split()
- if not 2 <= len(fields) <= 4:
- continue
- elif len(fields) == 2:
- fields.extend(['none', ''])
- elif len(fields) == 3:
- fields.append('')
-
- (name, devspec, keyfile, options) = fields
-
- # resolve devspec to a device in the tree
- device = self.devicetree.resolveDevice(devspec,
- blkidTab=self.blkidTab)
- if device:
- self.mappings[name] = {"device": device,
- "keyfile": keyfile,
- "options": options}
-
- def populate(self):
- """ Populate the instance based on the device tree's contents. """
- for device in self.devicetree.devices.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)
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-
-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)
+++ /dev/null
-
-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
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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()
+++ /dev/null
-# 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 <host>:<path>")
-
- @property
- def mountable(self):
- return False
-
- def _setDevice(self, devspec):
- self._deviceCheck(devspec)
- self._device = devspec
-
- def _getDevice(self):
- return self._device
-
- device = property(lambda f: f._getDevice(),
- lambda f,d: f._setDevice(d),
- doc="Full path the device this format occupies")
-
-register_device_format(NFS)
-
-
-class NFSv4(NFS):
- """ NFSv4 filesystem. """
- _type = "nfs4"
- _modules = ["nfs4"]
-
-register_device_format(NFSv4)
-
-
-class Iso9660FS(FS):
- """ ISO9660 filesystem. """
- _type = "iso9660"
- _formattable = False
- _supported = True
- _resizable = False
- _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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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