aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'storage/formats/__init__.py')
-rw-r--r--storage/formats/__init__.py403
1 files changed, 403 insertions, 0 deletions
diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py
new file mode 100644
index 0000000..8a8abd3
--- /dev/null
+++ b/storage/formats/__init__.py
@@ -0,0 +1,403 @@
+# __init__.py
+# Entry point for anaconda storage formats subpackage.
+#
+# Copyright (C) 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+import os
+
+from iutil import notify_kernel, get_sysfs_path_by_name
+from ..storage_log import log_method_call
+from ..errors import *
+from ..devicelibs.dm import dm_node_from_name
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+import logging
+log = logging.getLogger("storage")
+
+
+device_formats = {}
+def register_device_format(fmt_class):
+ if not issubclass(fmt_class, DeviceFormat):
+ raise ValueError("arg1 must be a subclass of DeviceFormat")
+
+ device_formats[fmt_class._type] = fmt_class
+ log.debug("registered device format class %s as %s" % (fmt_class.__name__,
+ fmt_class._type))
+
+default_fstypes = ("ext4", "ext3", "ext2")
+def get_default_filesystem_type(boot=None):
+ import pyanaconda.platform as platform
+
+ if boot:
+ fstypes = [platform.getPlatform(None).defaultBootFSType]
+ 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(fmt_type, *args, **kwargs):
+ """ Return a DeviceFormat instance based on fmt_type and args.
+
+ Given a device format type and a set of constructor arguments,
+ return a DeviceFormat instance.
+
+ Return None if no suitable format class is found.
+
+ Arguments:
+
+ fmt_type -- the name of the format type (eg: 'ext3', 'swap')
+
+ Keyword Arguments:
+
+ The keyword arguments may vary according to the format type,
+ but here is the common set:
+
+ device -- path to the device on which the format resides
+ uuid -- the UUID of the (preexisting) formatted device
+ exists -- whether or not the format exists on the device
+
+ """
+ fmt_class = get_device_format_class(fmt_type)
+ fmt = None
+ if fmt_class:
+ fmt = fmt_class(*args, **kwargs)
+ try:
+ className = fmt.__class__.__name__
+ except AttributeError:
+ className = None
+ log.debug("getFormat('%s') returning %s instance" % (fmt_type, className))
+ return fmt
+
+def collect_device_format_classes():
+ """ Pick up all device format classes from this directory.
+
+ Note: Modules must call register_device_format(FormatClass) in
+ order for the format class to be picked up.
+ """
+ dir = os.path.dirname(__file__)
+ for module_file in os.listdir(dir):
+ # make sure we're not importing this module
+ if module_file.endswith(".py") and module_file != __file__:
+ mod_name = module_file[:-3]
+ # imputil is deprecated in python 2.6
+ try:
+ globals()[mod_name] = __import__(mod_name, globals(), locals(), [], -1)
+ except ImportError, e:
+ log.debug("import of device format module '%s' failed" % mod_name)
+
+def get_device_format_class(fmt_type):
+ """ Return an appropriate format class based on fmt_type. """
+ if not device_formats:
+ collect_device_format_classes()
+
+ fmt = device_formats.get(fmt_type)
+ if not fmt:
+ for fmt_class in device_formats.values():
+ if fmt_type and fmt_type == fmt_class._name:
+ fmt = fmt_class
+ break
+ elif fmt_type in fmt_class._udevTypes:
+ fmt = fmt_class
+ break
+
+ # default to no formatting, AKA "Unknown"
+ if not fmt:
+ fmt = DeviceFormat
+
+ return fmt
+
+class DeviceFormat(object):
+ """ Generic device format. """
+ _type = None
+ _name = "Unknown"
+ _udevTypes = []
+ partedFlag = None
+ partedSystem = None
+ _formattable = False # can be formatted
+ _supported = False # is supported
+ _linuxNative = False # for clearpart
+ _packages = [] # required packages
+ _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
+ _hidden = False # hide devices with this formatting?
+
+ def __init__(self, *args, **kwargs):
+ """ Create a DeviceFormat instance.
+
+ Keyword Arguments:
+
+ device -- path to the underlying device
+ uuid -- this format's UUID
+ exists -- indicates whether this is an existing format
+
+ """
+ self.device = kwargs.get("device")
+ self.uuid = kwargs.get("uuid")
+ self.exists = kwargs.get("exists")
+ self.options = kwargs.get("options")
+ self._migrate = False
+
+ # don't worry about existence if this is a DeviceFormat instance
+ #if self.__class__ is DeviceFormat:
+ # self.exists = True
+
+ def __str__(self):
+ s = ("%(classname)s instance (%(id)s) --\n"
+ " type = %(type)s name = %(name)s status = %(status)s\n"
+ " device = %(device)s uuid = %(uuid)s exists = %(exists)s\n"
+ " options = %(options)s supported = %(supported)s"
+ " formattable = %(format)s resizable = %(resize)s\n" %
+ {"classname": self.__class__.__name__, "id": "%#x" % id(self),
+ "type": self.type, "name": self.name, "status": self.status,
+ "device": self.device, "uuid": self.uuid, "exists": self.exists,
+ "options": self.options, "supported": self.supported,
+ "format": self.formattable, "resize": self.resizable})
+ return s
+
+ @property
+ def dict(self):
+ d = {"type": self.type, "name": self.name, "device": self.device,
+ "uuid": self.uuid, "exists": self.exists,
+ "options": self.options, "supported": self.supported,
+ "resizable": self.resizable}
+ return d
+
+ def _setOptions(self, options):
+ self._options = options
+
+ def _getOptions(self):
+ return self._options
+
+ options = property(_getOptions, _setOptions)
+
+ def _setDevice(self, devspec):
+ if devspec and not devspec.startswith("/"):
+ raise ValueError("device must be a fully qualified path")
+ self._device = devspec
+
+ def _getDevice(self):
+ return self._device
+
+ device = property(lambda f: f._getDevice(),
+ lambda f,d: f._setDevice(d),
+ doc="Full path the device this format occupies")
+
+ @property
+ def name(self):
+ if self._name:
+ name = self._name
+ else:
+ name = self.type
+ return name
+
+ @property
+ def type(self):
+ return self._type
+
+ def probe(self):
+ log_method_call(self, device=self.device,
+ type=self.type, status=self.status)
+
+ def notifyKernel(self):
+ log_method_call(self, device=self.device,
+ type=self.type)
+ if not self.device:
+ return
+
+ if self.device.startswith("/dev/mapper/"):
+ try:
+ name = dm_node_from_name(os.path.basename(self.device))
+ except Exception, e:
+ 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:
+ log.warning("failed to notify kernel of change: %s" % e)
+
+
+ def create(self, *args, **kwargs):
+ log_method_call(self, device=self.device,
+ type=self.type, status=self.status)
+ # allow late specification of device path
+ device = kwargs.get("device")
+ if device:
+ self.device = device
+
+ if not os.path.exists(self.device):
+ raise FormatCreateError("invalid device specification", self.device)
+
+ def destroy(self, *args, **kwargs):
+ log_method_call(self, device=self.device,
+ type=self.type, status=self.status)
+ # 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
+ log.debug("zeroing out beginning and end of %s..." % self.device)
+ fd = None
+
+ 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:
+ log.error("error zeroing out %s: %s" % (self.device, e))
+
+ if fd:
+ os.close(fd)
+ except Exception as e:
+ log.error("error zeroing out %s: %s" % (self.device, e))
+ if fd:
+ os.close(fd)
+
+ self.exists = False
+
+ def setup(self, *args, **kwargs):
+ log_method_call(self, device=self.device,
+ type=self.type, status=self.status)
+
+ if not self.exists:
+ raise FormatSetupError("format has not been created")
+
+ if self.status:
+ return
+
+ # allow late specification of device path
+ device = kwargs.get("device")
+ if device:
+ self.device = device
+
+ if not self.device or not os.path.exists(self.device):
+ raise FormatSetupError("invalid device specification")
+
+ def teardown(self, *args, **kwargs):
+ log_method_call(self, device=self.device,
+ type=self.type, status=self.status)
+
+ @property
+ def status(self):
+ return (self.exists and
+ self.__class__ is not DeviceFormat and
+ isinstance(self.device, str) and
+ self.device and
+ os.path.exists(self.device))
+
+ @property
+ def formattable(self):
+ """ Can we create formats of this type? """
+ return self._formattable
+
+ @property
+ def supported(self):
+ """ Is this format a supported type? """
+ return self._supported
+
+ @property
+ def packages(self):
+ """ Packages required to manage formats of this type. """
+ return self._packages
+
+ @property
+ def resizable(self):
+ """ Can formats of this type be resized? """
+ return self._resizable and self.exists
+
+ @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
+
+ @property
+ def hidden(self):
+ """ Whether devices with this formatting should be hidden in UIs. """
+ return self._hidden
+
+ def writeKS(self, f):
+ return
+
+
+collect_device_format_classes()
+
+