diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py index 394aa4051..7e5b62cf3 100644 --- a/blivet/devices/lvm.py +++ b/blivet/devices/lvm.py @@ -48,6 +48,8 @@ from .dm import DMDevice from .md import MDRaidArrayDevice +INTERNAL_LV_CLASSES = [] + class LVMVolumeGroupDevice(ContainerDevice): """ An LVM Volume Group """ _type = "lvmvg" @@ -495,9 +497,6 @@ def __init__(self, name, parents=None, size=None, uuid=None, exists=exists) self.uuid = uuid - self.copies = copies - self.logSize = logSize or Size(0) - self.metaDataSize = Size(0) self.segType = segType or "linear" self.snapshots = [] @@ -517,6 +516,8 @@ def __init__(self, name, parents=None, size=None, uuid=None, self._check_parents() self._add_to_parents() + self._internal_lvs = [] + def _check_parents(self): """Check that this device has parents as expected""" @@ -808,6 +809,229 @@ def isNameValid(cls, name): return True + def addInternalLV(self, int_lv): + if int_lv not in self._internal_lvs: + self._internal_lvs.append(int_lv) + + def removeInternalLV(self, int_lv): + if int_lv in self._internal_lvs: + self._internal_lvs.remove(int_lv) + else: + msg = "the specified internal LV '%s' doesn't belong to this LV ('%s')" % (int_lv.lv_name, + self.name) + raise ValueError(msg) + +@add_metaclass(abc.ABCMeta) +class LVMInternalLogicalVolumeDevice(LVMLogicalVolumeDevice): + """Abstract base class for internal LVs + + A common class for all classes representing internal Logical Volumes like + data and metadata parts of pools, RAID images, etc. + + Internal LVs are only referenced by their "parent" LVs (normal LVs they + back) as all queries and manipulations with them should be done via their + parent LVs. + + """ + + _type = "lvminternallv" + + # generally changes should be done on the parent LV (exceptions should + # override these) + _resizable = False + _readonly = True + + attr_letters = abc.abstractproperty(doc="letters representing the type of the internal LV in the attrs") + name_suffix = abc.abstractproperty(doc="pattern matching typical/default suffices for internal LVs of this type") + takes_extra_space = abc.abstractproperty(doc="whether LVs of this type take space in a VG or are part of their parent LVs") + + @classmethod + def isNameValid(cls, name): + # override checks for normal LVs, internal LVs typically have names that + # are forbidden for normal LVs + return True + + def __init__(self, name, vg, parent_lv=None, size=None, uuid=None, + exists=False, segType=None, sysfsPath=''): + """ + :param vg: the VG this internal LV belongs to + :type vg: :class:`LVMVolumeGroupDevice` + :param parent_lv: the parent LV of this internal LV + :type parent_lv: :class:`LVMLogicalVolumeDevice` + + See :method:`LVMLogicalVolumeDevice.__init__` for details about the + rest of the parameters. + """ + + # VG name has to be set for parent class' constructors + self._vg = vg + + # construct the internal LV just like a normal one just with no parents + # and some parameters set to values reflecting the fact that this is an + # internal LV + super(LVMInternalLogicalVolumeDevice, self).__init__(name, parents=None, + size=size, uuid=uuid, segType=segType, fmt=None, exists=exists, + sysfsPath=sysfsPath, grow=None, maxsize=None, percent=None) + + self._parent_lv = parent_lv + if parent_lv: + self._parent_lv.addInternalLV(self) + + def _check_parents(self): + # an internal LV should have no parents + if self._parents: + raise ValueError("an internal LV should have no parents") + + def _add_to_parents(self): + # nothing to do here, an internal LV has no parents (in the DeviceTree's + # meaning of 'parents') + pass + + @property + def vg(self): + return self._vg + + @vg.setter + def vg(self, vg): + self._vgpp = vg + + @property + def parent_lv(self): + return self._parent_lv + + @parent_lv.setter + def parent_lv(self, parent_lv): + if self._parent_lv: + self._parent_lv.removeInternalLV(self) + self._parent_lv = parent_lv + if self._parent_lv: + self._parent_lv.addInternalLV(self) + + # internal LVs have no format + def _setFormat(self, f): + return + + def _getFormat(self): + return None + + def _setSize(self, size): + if not isinstance(size, Size): + raise ValueError("new size must of type Size") + + if not self.takes_extra_space: + if size <= self.parent_lv.size: + self._size = size + else: + raise ValueError("Internal LV cannot be bigger than its parent LV") + else: + # same rules apply as for any other LV + super(LVMInternalLogicalVolumeDevice, self)._setSize(size) + + # ancestor classes' __repr__ expect originalFormat to be != None and some + # other things + def __repr__(self): + s = "%s:\n" % self.__class__.__name__ + s += (" name = %s, status = %s exists = %s\n" % (self.lvname, self.status, self.exists)) + s += (" uuid = %s, size = %s\n" % (self.uuid, self.size)) + s += (" parent LV = %r\n" % self.parent_lv) + s += (" VG device = %(vgdev)r\n" + " segment type = %(type)s percent = %(percent)s\n" + " mirror copies = %(copies)d" + " VG space used = %(vgspace)s" % + {"vgdev": self.vg, "percent": self.req_percent, + "copies": self.copies, "type": self.segType, + "vgspace": self.vgSpaceUsed }) + return s + + # generally changes should be done on the parent LV (exceptions should + # override these) + def setup(self, orig=False): + raise errors.DeviceError("An internal LV cannot be set up separately") + + def teardown(self, recursive=None): + raise errors.DeviceError("An internal LV cannot be torn down separately") + + def destroy(self): + raise errors.DeviceError("An internal LV cannot be destroyed separately") + + def resize(self): + raise errors.DeviceError("An internal LV cannot be resized") + + @property + def growable(self): + return False + + @property + def display_lvname(self): + """Name of the internal LV as displayed by the lvm utilities""" + return "[%s]" % self.lvname + + # these two methods are not needed right now, because they are only called + # when devices are added/removed to/from the DeviceTree, but they may come + # handy in the future + def addHook(self, new=True): + # skip LVMLogicalVolumeDevice in the class hierarchy -- we don't want to + # add an internal LV to the VG (it's only referenced by the parent LV) + super(LVMLogicalVolumeDevice, self).addHook(new=new) + self._parent_lv.addInternalLV(self) + + def removeHook(self, modparent=True): + if modparent: + self._parent_lv.removeInternalLV(self) + + # skip LVMLogicalVolumeDevice in the class hierarchy -- we cannot remove + # an internal LV from the VG (it's only referenced by the parent LV) + super(LVMLogicalVolumeDevice, self).removeHook(modparent=modparent) + + @property + def direct(self): + # internal LVs are not directly accessible + return False + +class LVMDataLogicalVolumeDevice(LVMInternalLogicalVolumeDevice): + """Internal data LV (used by thin/cache pools)""" + + attr_letters = ["T", "C"] + name_suffix = r"_[tc]data" + takes_extra_space = False +_INTERNAL_LV_CLASSES.append(LVMDataLogicalVolumeDevice) + +class LVMMetadataLogicalVolumeDevice(LVMInternalLogicalVolumeDevice): + """Internal metadata LV (used by thin/cache pools, RAIDs, etc.)""" + + attr_letters = ["e"] + # RAIDs can have multiple (numbered) metadata LVs + name_suffix = r"_[trc]meta(_[0-9]+)?" + takes_extra_space = True + + # TODO: override and allow resize() +_INTERNAL_LV_CLASSES.append(LVMMetadataLogicalVolumeDevice) + +class LVMLogLogicalVolumeDevice(LVMInternalLogicalVolumeDevice): + """Internal log LV (used by mirrored LVs)""" + + attr_letters = ["l", "L"] + name_suffix = "_mlog" + takes_extra_space = True +_INTERNAL_LV_CLASSES.append(LVMLogLogicalVolumeDevice) + +class LVMImageLogicalVolumeDevice(LVMInternalLogicalVolumeDevice): + """Internal image LV (used by mirror/RAID LVs)""" + + attr_letters = ["i"] + # RAIDs have multiple (numbered) image LVs + name_suffix = r"_[rm]image(_[0-9]+)?" + takes_extra_space = False +_INTERNAL_LV_CLASSES.append(LVMImageLogicalVolumeDevice) + +class LVMOriginLogicalVolumeDevice(LVMInternalLogicalVolumeDevice): + """Internal origin LV (e.g. the raw/uncached part of a cached LV)""" + + attr_letters = ["o"] + name_suffix = r"_c?orig" + takes_extra_space = False +_INTERNAL_LV_CLASSES.append(LVMOriginLogicalVolumeDevice) + @add_metaclass(abc.ABCMeta) class LVMSnapShotBase(object): """ Abstract base class for lvm snapshots