import python-blivet-3.6.0-8.el8

c8-beta imports/c8-beta/python-blivet-3.6.0-8.el8
MSVSphere Packaging Team 9 months ago
commit 1d11a1125d

2
.gitignore vendored

@ -0,0 +1,2 @@
SOURCES/blivet-3.6.0-tests.tar.gz
SOURCES/blivet-3.6.0.tar.gz

@ -0,0 +1,2 @@
8393baa22cb433d1012e3923ad0bc232401116c6 SOURCES/blivet-3.6.0-tests.tar.gz
e9d95c1165703fed3da1f35a9199197bfff68f98 SOURCES/blivet-3.6.0.tar.gz

@ -0,0 +1,35 @@
From 83ccc9f9f14845fcce7a5ba5fa21fbb97b1dbbb7 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 11 Jul 2018 15:36:24 +0200
Subject: [PATCH] Force command line based libblockdev LVM plugin
---
blivet/__init__.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/blivet/__init__.py b/blivet/__init__.py
index dd8d0f54..62cc539a 100644
--- a/blivet/__init__.py
+++ b/blivet/__init__.py
@@ -63,11 +63,16 @@ gi.require_version("BlockDev", "2.0")
from gi.repository import GLib
from gi.repository import BlockDev as blockdev
if arch.is_s390():
- _REQUESTED_PLUGIN_NAMES = set(("lvm", "btrfs", "swap", "crypto", "loop", "mdraid", "mpath", "dm", "s390", "nvdimm"))
+ _REQUESTED_PLUGIN_NAMES = set(("btrfs", "swap", "crypto", "loop", "mdraid", "mpath", "dm", "s390", "nvdimm"))
else:
- _REQUESTED_PLUGIN_NAMES = set(("lvm", "btrfs", "swap", "crypto", "loop", "mdraid", "mpath", "dm", "nvdimm"))
+ _REQUESTED_PLUGIN_NAMES = set(("btrfs", "swap", "crypto", "loop", "mdraid", "mpath", "dm", "nvdimm"))
_requested_plugins = blockdev.plugin_specs_from_names(_REQUESTED_PLUGIN_NAMES)
+# XXX force non-dbus LVM plugin
+lvm_plugin = blockdev.PluginSpec()
+lvm_plugin.name = blockdev.Plugin.LVM
+lvm_plugin.so_name = "libbd_lvm.so.2"
+_requested_plugins.append(lvm_plugin)
try:
# do not check for dependencies during libblockdev initializtion, do runtime
# checks instead
--
2.38.1

@ -0,0 +1,28 @@
From c098d4112635b3ea55d5bd7e1817edbd519735fc Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Mon, 16 Jul 2018 14:26:11 +0200
Subject: [PATCH] Remove btrfs from requested libblockdev plugins
---
blivet/__init__.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/blivet/__init__.py b/blivet/__init__.py
index 62cc539a..bbc7ea3a 100644
--- a/blivet/__init__.py
+++ b/blivet/__init__.py
@@ -63,9 +63,9 @@ gi.require_version("BlockDev", "2.0")
from gi.repository import GLib
from gi.repository import BlockDev as blockdev
if arch.is_s390():
- _REQUESTED_PLUGIN_NAMES = set(("btrfs", "swap", "crypto", "loop", "mdraid", "mpath", "dm", "s390", "nvdimm"))
+ _REQUESTED_PLUGIN_NAMES = set(("swap", "crypto", "loop", "mdraid", "mpath", "dm", "s390", "nvdimm"))
else:
- _REQUESTED_PLUGIN_NAMES = set(("btrfs", "swap", "crypto", "loop", "mdraid", "mpath", "dm", "nvdimm"))
+ _REQUESTED_PLUGIN_NAMES = set(("swap", "crypto", "loop", "mdraid", "mpath", "dm", "nvdimm"))
_requested_plugins = blockdev.plugin_specs_from_names(_REQUESTED_PLUGIN_NAMES)
# XXX force non-dbus LVM plugin
--
2.38.1

@ -0,0 +1,330 @@
From f6f90805020d7c6ac46f17a13a00f319fc4351f6 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 26 May 2021 12:15:54 +0200
Subject: [PATCH] Revert "More consistent lvm errors (API break)"
This reverts commit 49ec071c6d0673224a0774d613904387c52c7381.
---
blivet/devices/lvm.py | 72 +++++++++++------------
tests/unit_tests/devices_test/lvm_test.py | 14 ++---
2 files changed, 43 insertions(+), 43 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index 38e49e18..b8595d63 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -304,7 +304,7 @@ class LVMVolumeGroupDevice(ContainerDevice):
def _add_log_vol(self, lv):
""" Add an LV to this VG. """
if lv in self._lvs:
- raise errors.DeviceError("lv is already part of this vg")
+ 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)
@@ -337,7 +337,7 @@ class LVMVolumeGroupDevice(ContainerDevice):
def _remove_log_vol(self, lv):
""" Remove an LV from this VG. """
if lv not in self.lvs:
- raise errors.DeviceError("specified lv is not part of this vg")
+ raise ValueError("specified lv is not part of this vg")
self._lvs.remove(lv)
@@ -430,7 +430,7 @@ class LVMVolumeGroupDevice(ContainerDevice):
@thpool_reserve.setter
def thpool_reserve(self, value):
if value is not None and not isinstance(value, ThPoolReserveSpec):
- raise AttributeError("Invalid thpool_reserve given, must be of type ThPoolReserveSpec")
+ raise ValueError("Invalid thpool_reserve given, must be of type ThPoolReserveSpec")
self._thpool_reserve = value
@property
@@ -665,14 +665,14 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
if seg_type not in [None, "linear", "thin", "thin-pool", "cache", "vdo-pool", "vdo", "cache-pool"] + lvm.raid_seg_types:
raise ValueError("Invalid or unsupported segment type: %s" % seg_type)
if seg_type and seg_type in lvm.raid_seg_types and not pvs:
- raise errors.DeviceError("List of PVs has to be given for every non-linear LV")
+ raise ValueError("List of PVs has to be given for every non-linear LV")
elif (not seg_type or seg_type == "linear") and pvs:
if not all(isinstance(pv, LVPVSpec) for pv in pvs):
- raise errors.DeviceError("Invalid specification of PVs for a linear LV: either no or complete "
- "specification (with all space split into PVs has to be given")
+ raise ValueError("Invalid specification of PVs for a linear LV: either no or complete "
+ "specification (with all space split into PVs has to be given")
elif sum(spec.size for spec in pvs) != size:
- raise errors.DeviceError("Invalid specification of PVs for a linear LV: the sum of space "
- "assigned to PVs is not equal to the size of the LV")
+ raise ValueError("Invalid specification of PVs for a linear LV: the sum of space "
+ "assigned to PVs is not equal to the size of the LV")
# When this device's format is set in the superclass constructor it will
# try to access self.snapshots.
@@ -721,13 +721,13 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
self._from_lvs = from_lvs
if self._from_lvs:
if exists:
- raise errors.DeviceError("Only new LVs can be created from other LVs")
+ raise ValueError("Only new LVs can be created from other LVs")
if size or maxsize or percent:
- raise errors.DeviceError("Cannot specify size for a converted LV")
+ raise ValueError("Cannot specify size for a converted LV")
if fmt:
- raise errors.DeviceError("Cannot specify format for a converted LV")
+ raise ValueError("Cannot specify format for a converted LV")
if any(lv.vg != self.vg for lv in self._from_lvs):
- raise errors.DeviceError("Conversion of LVs only possible inside a VG")
+ raise ValueError("Conversion of LVs only possible inside a VG")
self._cache = None
if cache_request and not self.exists:
@@ -746,13 +746,13 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
elif isinstance(pv_spec, StorageDevice):
self._pv_specs.append(LVPVSpec(pv_spec, Size(0)))
else:
- raise AttributeError("Invalid PV spec '%s' for the '%s' LV" % (pv_spec, self.name))
+ raise ValueError("Invalid PV spec '%s' for the '%s' LV" % (pv_spec, self.name))
# Make sure any destination PVs are actually PVs in this VG
if not set(spec.pv for spec in self._pv_specs).issubset(set(self.vg.parents)):
missing = [r.name for r in
set(spec.pv for spec in self._pv_specs).difference(set(self.vg.parents))]
msg = "invalid destination PV(s) %s for LV %s" % (missing, self.name)
- raise errors.DeviceError(msg)
+ raise ValueError(msg)
if self._pv_specs:
self._assign_pv_space()
@@ -1130,7 +1130,7 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
else:
msg = "the specified internal LV '%s' doesn't belong to this LV ('%s')" % (int_lv.lv_name,
self.name)
- raise errors.DeviceError(msg)
+ raise ValueError(msg)
def populate_ksdata(self, data):
super(LVMLogicalVolumeBase, self).populate_ksdata(data)
@@ -1229,7 +1229,7 @@ class LVMInternalLogicalVolumeMixin(object):
def _init_check(self):
# an internal LV should have no parents
if self._parent_lv and self._parents:
- raise errors.DeviceError("an internal LV should have no parents")
+ raise ValueError("an internal LV should have no parents")
@property
def is_internal_lv(self):
@@ -1289,7 +1289,7 @@ class LVMInternalLogicalVolumeMixin(object):
@readonly.setter
def readonly(self, value): # pylint: disable=unused-argument
- raise errors.DeviceError("Cannot make an internal LV read-write")
+ raise ValueError("Cannot make an internal LV read-write")
@property
def type(self):
@@ -1325,7 +1325,7 @@ class LVMInternalLogicalVolumeMixin(object):
def _check_parents(self):
# an internal LV should have no parents
if self._parents:
- raise errors.DeviceError("an internal LV should have no 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
@@ -1335,13 +1335,13 @@ class LVMInternalLogicalVolumeMixin(object):
# internal LVs follow different rules limitting size
def _set_size(self, newsize):
if not isinstance(newsize, Size):
- raise AttributeError("new size must of type Size")
+ raise ValueError("new size must of type Size")
if not self.takes_extra_space:
if newsize <= self.parent_lv.size: # pylint: disable=no-member
self._size = newsize # pylint: disable=attribute-defined-outside-init
else:
- raise errors.DeviceError("Internal LV cannot be bigger than its parent LV")
+ raise ValueError("Internal LV cannot be bigger than its parent LV")
else:
# same rules apply as for any other LV
raise NotTypeSpecific()
@@ -1419,18 +1419,18 @@ class LVMSnapshotMixin(object):
return
if self.origin and not isinstance(self.origin, LVMLogicalVolumeDevice):
- raise errors.DeviceError("lvm snapshot origin must be a logical volume")
+ raise ValueError("lvm snapshot origin must be a logical volume")
if self.vorigin and not self.exists:
- raise errors.DeviceError("only existing vorigin snapshots are supported")
+ raise ValueError("only existing vorigin snapshots are supported")
if isinstance(self.origin, LVMLogicalVolumeDevice) and \
isinstance(self.parents[0], LVMVolumeGroupDevice) and \
self.origin.vg != self.parents[0]:
- raise errors.DeviceError("lvm snapshot and origin must be in the same vg")
+ raise ValueError("lvm snapshot and origin must be in the same vg")
if self.is_thin_lv:
if self.origin and self.size and not self.exists:
- raise errors.DeviceError("thin snapshot size is determined automatically")
+ raise ValueError("thin snapshot size is determined automatically")
@property
def is_snapshot_lv(self):
@@ -1606,7 +1606,7 @@ class LVMThinPoolMixin(object):
def _check_from_lvs(self):
if self._from_lvs:
if len(self._from_lvs) != 2:
- raise errors.DeviceError("two LVs required to create a thin pool")
+ raise ValueError("two LVs required to create a thin pool")
def _convert_from_lvs(self):
data_lv, metadata_lv = self._from_lvs
@@ -1652,7 +1652,7 @@ class LVMThinPoolMixin(object):
def _add_log_vol(self, lv):
""" Add an LV to this pool. """
if lv in self._lvs:
- raise errors.DeviceError("lv is already part of this vg")
+ raise ValueError("lv is already part of this vg")
# TODO: add some checking to prevent overcommit for preexisting
self.vg._add_log_vol(lv)
@@ -1663,7 +1663,7 @@ class LVMThinPoolMixin(object):
def _remove_log_vol(self, lv):
""" Remove an LV from this pool. """
if lv not in self._lvs:
- raise errors.DeviceError("specified lv is not part of this vg")
+ raise ValueError("specified lv is not part of this vg")
self._lvs.remove(lv)
self.vg._remove_log_vol(lv)
@@ -1772,14 +1772,14 @@ class LVMThinLogicalVolumeMixin(object):
"""Check that this device has parents as expected"""
if isinstance(self.parents, (list, ParentList)):
if len(self.parents) != 1:
- raise errors.DeviceError("constructor requires a single thin-pool LV")
+ raise ValueError("constructor requires a single thin-pool LV")
container = self.parents[0]
else:
container = self.parents
if not container or not isinstance(container, LVMLogicalVolumeDevice) or not container.is_thin_pool:
- raise errors.DeviceError("constructor requires a thin-pool LV")
+ raise ValueError("constructor requires a thin-pool LV")
@property
def is_thin_lv(self):
@@ -1816,7 +1816,7 @@ class LVMThinLogicalVolumeMixin(object):
def _set_size(self, newsize):
if not isinstance(newsize, Size):
- raise AttributeError("new size must of type Size")
+ raise ValueError("new size must of type Size")
newsize = self.vg.align(newsize)
newsize = self.vg.align(util.numeric_type(newsize))
@@ -2499,7 +2499,7 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
container = self.parents
if not isinstance(container, LVMVolumeGroupDevice):
- raise AttributeError("constructor requires a LVMVolumeGroupDevice")
+ raise ValueError("constructor requires a LVMVolumeGroupDevice")
@type_specific
def _add_to_parents(self):
@@ -2510,12 +2510,12 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
@type_specific
def _check_from_lvs(self):
"""Check the LVs to create this LV from"""
- raise errors.DeviceError("Cannot create a new LV of type '%s' from other LVs" % self.seg_type)
+ raise ValueError("Cannot create a new LV of type '%s' from other LVs" % self.seg_type)
@type_specific
def _convert_from_lvs(self):
"""Convert the LVs to create this LV from into its internal LVs"""
- raise errors.DeviceError("Cannot create a new LV of type '%s' from other LVs" % self.seg_type)
+ raise ValueError("Cannot create a new LV of type '%s' from other LVs" % self.seg_type)
@property
def external_dependencies(self):
@@ -2535,7 +2535,7 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
@type_specific
def _set_size(self, newsize):
if not isinstance(newsize, Size):
- raise AttributeError("new size must be of type Size")
+ raise ValueError("new size must be of type Size")
newsize = self.vg.align(newsize)
log.debug("trying to set lv %s size to %s", self.name, newsize)
@@ -2544,7 +2544,7 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
# space for it. A similar reasoning applies to shrinking the LV.
if not self.exists and newsize > self.size and newsize > self.vg.free_space + self.vg_space_used:
log.error("failed to set size: %s short", newsize - (self.vg.free_space + self.vg_space_used))
- raise errors.DeviceError("not enough free space in volume group")
+ raise ValueError("not enough free space in volume group")
LVMLogicalVolumeBase._set_size(self, newsize)
@@ -2910,7 +2910,7 @@ class LVMCache(Cache):
spec.size = spec.pv.format.free
space_to_assign -= spec.pv.format.free
if space_to_assign > 0:
- raise errors.DeviceError("Not enough free space in the PVs for this cache: %s short" % space_to_assign)
+ raise ValueError("Not enough free space in the PVs for this cache: %s short" % space_to_assign)
@property
def size(self):
diff --git a/tests/unit_tests/devices_test/lvm_test.py b/tests/unit_tests/devices_test/lvm_test.py
index 47613fdc..995c2da4 100644
--- a/tests/unit_tests/devices_test/lvm_test.py
+++ b/tests/unit_tests/devices_test/lvm_test.py
@@ -32,10 +32,10 @@ class LVMDeviceTest(unittest.TestCase):
lv = LVMLogicalVolumeDevice("testlv", parents=[vg],
fmt=blivet.formats.get_format("xfs"))
- with six.assertRaisesRegex(self, errors.DeviceError, "lvm snapshot origin must be a logical volume"):
+ with six.assertRaisesRegex(self, ValueError, "lvm snapshot origin must be a logical volume"):
LVMLogicalVolumeDevice("snap1", parents=[vg], origin=pv)
- with six.assertRaisesRegex(self, errors.DeviceError, "only existing vorigin snapshots are supported"):
+ with six.assertRaisesRegex(self, ValueError, "only existing vorigin snapshots are supported"):
LVMLogicalVolumeDevice("snap1", parents=[vg], vorigin=True)
lv.exists = True
@@ -60,7 +60,7 @@ class LVMDeviceTest(unittest.TestCase):
pool = LVMLogicalVolumeDevice("pool1", parents=[vg], size=Size("500 MiB"), seg_type="thin-pool")
thinlv = LVMLogicalVolumeDevice("thinlv", parents=[pool], size=Size("200 MiB"), seg_type="thin")
- with six.assertRaisesRegex(self, errors.DeviceError, "lvm snapshot origin must be a logical volume"):
+ with six.assertRaisesRegex(self, ValueError, "lvm snapshot origin must be a logical volume"):
LVMLogicalVolumeDevice("snap1", parents=[pool], origin=pv, seg_type="thin")
# now make the constructor succeed so we can test some properties
@@ -310,21 +310,21 @@ class LVMDeviceTest(unittest.TestCase):
vg = LVMVolumeGroupDevice("testvg", parents=[pv, pv2])
# pvs have to be specified for non-linear LVs
- with self.assertRaises(errors.DeviceError):
+ with self.assertRaises(ValueError):
lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("512 MiB"),
fmt=blivet.formats.get_format("xfs"),
exists=False, seg_type="raid1")
- with self.assertRaises(errors.DeviceError):
+ with self.assertRaises(ValueError):
lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("512 MiB"),
fmt=blivet.formats.get_format("xfs"),
exists=False, seg_type="striped")
# no or complete specification has to be given for linear LVs
- with self.assertRaises(errors.DeviceError):
+ with self.assertRaises(ValueError):
lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("512 MiB"),
fmt=blivet.formats.get_format("xfs"),
exists=False, pvs=[pv])
- with self.assertRaises(errors.DeviceError):
+ with self.assertRaises(ValueError):
pv_spec = LVPVSpec(pv, Size("256 MiB"))
pv_spec2 = LVPVSpec(pv2, Size("250 MiB"))
lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("512 MiB"),
--
2.38.1

@ -0,0 +1,899 @@
From d8a8d96450bf0d3458671b9b7d23d972aa540396 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 26 May 2021 12:27:34 +0200
Subject: [PATCH] Revert "Terminology cleanups"
This reverts following commits:
- 3d46339fe9cf12e9082fcbe4dc5acc9f92617e8d
- 63c9c7165e5cdfa4a47dcf0ed9d717b71e7921f2
- 8956b9af8a785ae25e0e7153d2ef0702ce2f567c
---
blivet/devicefactory.py | 24 +++----
blivet/devices/dm.py | 9 ++-
blivet/devices/loop.py | 20 +++---
blivet/devices/luks.py | 26 ++++---
blivet/errors.py | 2 +-
blivet/partitioning.py | 22 +++++-
blivet/populator/helpers/dm.py | 4 +-
blivet/populator/helpers/luks.py | 4 +-
blivet/populator/helpers/lvm.py | 2 +-
blivet/populator/helpers/mdraid.py | 14 ++--
blivet/populator/helpers/multipath.py | 8 +--
blivet/populator/populator.py | 67 ++++++++++---------
blivet/threads.py | 3 +-
blivet/udev.py | 34 +++++-----
tests/unit_tests/devicefactory_test.py | 10 +--
.../devices_test/device_size_test.py | 6 +-
tests/unit_tests/populator_test.py | 34 +++++-----
tests/unit_tests/udev_test.py | 12 ++--
tests/vmtests/vmbackedtestcase.py | 2 +-
19 files changed, 167 insertions(+), 136 deletions(-)
diff --git a/blivet/devicefactory.py b/blivet/devicefactory.py
index 6f460f6d..90082c28 100644
--- a/blivet/devicefactory.py
+++ b/blivet/devicefactory.py
@@ -859,12 +859,12 @@ class DeviceFactory(object):
parent_container.parents.remove(orig_device)
if self.encrypted and isinstance(self.device, LUKSDevice) and \
- self.raw_device.format.luks_version != self.luks_version:
- self.raw_device.format.luks_version = self.luks_version
+ self.device.slave.format.luks_version != self.luks_version:
+ self.device.slave.format.luks_version = self.luks_version
if self.encrypted and isinstance(self.device, LUKSDevice) and \
- self.raw_device.format.luks_sector_size != self.luks_sector_size:
- self.raw_device.format.luks_sector_size = self.luks_sector_size
+ self.device.slave.format.luks_sector_size != self.luks_sector_size:
+ self.device.slave.format.luks_sector_size = self.luks_sector_size
def _set_name(self):
if not self.device_name:
@@ -1201,11 +1201,11 @@ class PartitionSetFactory(PartitionFactory):
container.parents.remove(member)
self.storage.destroy_device(member)
members.remove(member)
- self.storage.format_device(member.raw_device,
+ self.storage.format_device(member.slave,
get_format(self.fstype))
- members.append(member.raw_device)
+ members.append(member.slave)
if container:
- container.parents.append(member.raw_device)
+ container.parents.append(member.slave)
continue
@@ -1227,10 +1227,10 @@ class PartitionSetFactory(PartitionFactory):
continue
- if member_encrypted and self.encrypted and self.luks_version != member.raw_device.format.luks_version:
- member.raw_device.format.luks_version = self.luks_version
- if member_encrypted and self.encrypted and self.luks_sector_size != member.raw_device.format.luks_sector_size:
- member.raw_device.format.luks_sector_size = self.luks_sector_size
+ if member_encrypted and self.encrypted and self.luks_version != member.slave.format.luks_version:
+ member.slave.format.luks_version = self.luks_version
+ if member_encrypted and self.encrypted and self.luks_sector_size != member.slave.format.luks_sector_size:
+ member.slave.format.luks_sector_size = self.luks_sector_size
##
# Prepare previously allocated member partitions for reallocation.
@@ -1290,7 +1290,7 @@ class PartitionSetFactory(PartitionFactory):
if isinstance(member, LUKSDevice):
self.storage.destroy_device(member)
- member = member.raw_device
+ member = member.slave
self.storage.destroy_device(member)
diff --git a/blivet/devices/dm.py b/blivet/devices/dm.py
index 2f936170..ae25e8e6 100644
--- a/blivet/devices/dm.py
+++ b/blivet/devices/dm.py
@@ -154,6 +154,11 @@ class DMDevice(StorageDevice):
log_method_call(self, self.name, status=self.status)
super(DMDevice, self)._set_name(value)
+ @property
+ def slave(self):
+ """ This device's backing device. """
+ return self.parents[0]
+
class DMLinearDevice(DMDevice):
_type = "dm-linear"
@@ -189,8 +194,8 @@ class DMLinearDevice(DMDevice):
""" Open, or set up, a device. """
log_method_call(self, self.name, orig=orig, status=self.status,
controllable=self.controllable)
- parent_length = self.parents[0].current_size / LINUX_SECTOR_SIZE
- blockdev.dm.create_linear(self.name, self.parents[0].path, parent_length,
+ slave_length = self.slave.current_size / LINUX_SECTOR_SIZE
+ blockdev.dm.create_linear(self.name, self.slave.path, slave_length,
self.dm_uuid)
def _post_setup(self):
diff --git a/blivet/devices/loop.py b/blivet/devices/loop.py
index 0f4d7775..78f88d7d 100644
--- a/blivet/devices/loop.py
+++ b/blivet/devices/loop.py
@@ -73,7 +73,7 @@ class LoopDevice(StorageDevice):
def update_name(self):
""" Update this device's name. """
- if not self.parents[0].status:
+ if not self.slave.status:
# if the backing device is inactive, so are we
return self.name
@@ -81,7 +81,7 @@ class LoopDevice(StorageDevice):
# if our name is loopN we must already be active
return self.name
- name = blockdev.loop.get_loop_name(self.parents[0].path)
+ name = blockdev.loop.get_loop_name(self.slave.path)
if name.startswith("loop"):
self.name = name
@@ -89,24 +89,24 @@ class LoopDevice(StorageDevice):
@property
def status(self):
- return (self.parents[0].status and
+ return (self.slave.status and
self.name.startswith("loop") and
- blockdev.loop.get_loop_name(self.parents[0].path) == self.name)
+ blockdev.loop.get_loop_name(self.slave.path) == self.name)
@property
def size(self):
- return self.parents[0].size
+ return self.slave.size
def _pre_setup(self, orig=False):
- if not os.path.exists(self.parents[0].path):
- raise errors.DeviceError("specified file (%s) does not exist" % self.parents[0].path)
+ if not os.path.exists(self.slave.path):
+ raise errors.DeviceError("specified file (%s) does not exist" % self.slave.path)
return StorageDevice._pre_setup(self, orig=orig)
def _setup(self, orig=False):
""" Open, or set up, a device. """
log_method_call(self, self.name, orig=orig, status=self.status,
controllable=self.controllable)
- blockdev.loop.setup(self.parents[0].path)
+ blockdev.loop.setup(self.slave.path)
def _post_setup(self):
StorageDevice._post_setup(self)
@@ -123,3 +123,7 @@ class LoopDevice(StorageDevice):
StorageDevice._post_teardown(self, recursive=recursive)
self.name = "tmploop%d" % self.id
self.sysfs_path = ''
+
+ @property
+ def slave(self):
+ return self.parents[0]
diff --git a/blivet/devices/luks.py b/blivet/devices/luks.py
index 2eb1f130..5ab840ea 100644
--- a/blivet/devices/luks.py
+++ b/blivet/devices/luks.py
@@ -66,13 +66,17 @@ class LUKSDevice(DMCryptDevice):
@property
def raw_device(self):
+ return self.slave
+
+ @property
+ def slave(self):
if self._has_integrity:
return self.parents[0].parents[0]
return self.parents[0]
def _get_size(self):
if not self.exists:
- size = self.raw_device.size - crypto.LUKS_METADATA_SIZE
+ size = self.slave.size - crypto.LUKS_METADATA_SIZE
elif self.resizable and self.target_size != Size(0):
size = self.target_size
else:
@@ -80,8 +84,8 @@ class LUKSDevice(DMCryptDevice):
return size
def _set_size(self, newsize):
- if not self.exists and not self.raw_device.exists:
- self.raw_device.size = newsize + crypto.LUKS_METADATA_SIZE
+ if not self.exists and not self.slave.exists:
+ self.slave.size = newsize + crypto.LUKS_METADATA_SIZE
# just run the StorageDevice._set_size to make sure we are in the format limits
super(LUKSDevice, self)._set_size(newsize - crypto.LUKS_METADATA_SIZE)
@@ -108,22 +112,22 @@ class LUKSDevice(DMCryptDevice):
raise ValueError("size is smaller than the minimum for this device")
# don't allow larger luks than size (or target size) of backing device
- if newsize > (self.raw_device.size - crypto.LUKS_METADATA_SIZE):
+ if newsize > (self.slave.size - crypto.LUKS_METADATA_SIZE):
log.error("requested size %s is larger than size of the backing device %s",
- newsize, self.raw_device.size)
+ newsize, self.slave.size)
raise ValueError("size is larger than the size of the backing device")
if self.align_target_size(newsize) != newsize:
raise ValueError("new size would violate alignment requirements")
def _get_target_size(self):
- return self.raw_device.format.target_size
+ return self.slave.format.target_size
@property
def max_size(self):
""" The maximum size this luks device can be. Maximum is based on the
maximum size of the backing device. """
- max_luks = self.raw_device.max_size - crypto.LUKS_METADATA_SIZE
+ max_luks = self.slave.max_size - crypto.LUKS_METADATA_SIZE
max_format = self.format.max_size
return min(max_luks, max_format) if max_format else max_luks
@@ -131,7 +135,7 @@ class LUKSDevice(DMCryptDevice):
def resizable(self):
""" Can this device be resized? """
return (self._resizable and self.exists and self.format.resizable and
- self.raw_device.resizable and not self._has_integrity)
+ self.slave.resizable and not self._has_integrity)
def resize(self):
# size of LUKSDevice depends on size of the LUKS format on backing
@@ -139,7 +143,7 @@ class LUKSDevice(DMCryptDevice):
log_method_call(self, self.name, status=self.status)
def _post_create(self):
- self.name = self.raw_device.format.map_name
+ self.name = self.slave.format.map_name
StorageDevice._post_create(self)
def _post_teardown(self, recursive=False):
@@ -162,10 +166,10 @@ class LUKSDevice(DMCryptDevice):
self.name = new_name
def dracut_setup_args(self):
- return set(["rd.luks.uuid=luks-%s" % self.raw_device.format.uuid])
+ return set(["rd.luks.uuid=luks-%s" % self.slave.format.uuid])
def populate_ksdata(self, data):
- self.raw_device.populate_ksdata(data)
+ self.slave.populate_ksdata(data)
data.encrypted = True
super(LUKSDevice, self).populate_ksdata(data)
diff --git a/blivet/errors.py b/blivet/errors.py
index b886ffec..30c9921a 100644
--- a/blivet/errors.py
+++ b/blivet/errors.py
@@ -201,7 +201,7 @@ class DeviceTreeError(StorageError):
pass
-class NoParentsError(DeviceTreeError):
+class NoSlavesError(DeviceTreeError):
pass
diff --git a/blivet/partitioning.py b/blivet/partitioning.py
index ce77e4eb..2cd6554c 100644
--- a/blivet/partitioning.py
+++ b/blivet/partitioning.py
@@ -32,7 +32,7 @@ import _ped
from .errors import DeviceError, PartitioningError, AlignmentError
from .flags import flags
-from .devices import Device, PartitionDevice, device_path_to_name
+from .devices import Device, PartitionDevice, LUKSDevice, device_path_to_name
from .size import Size
from .i18n import _
from .util import stringize, unicodeize, compare
@@ -1635,7 +1635,15 @@ class TotalSizeSet(object):
:param size: the target combined size
:type size: :class:`~.size.Size`
"""
- self.devices = [d.raw_device for d in devices]
+ self.devices = []
+ for device in devices:
+ if isinstance(device, LUKSDevice):
+ partition = device.slave
+ else:
+ partition = device
+
+ self.devices.append(partition)
+
self.size = size
self.requests = []
@@ -1673,7 +1681,15 @@ class SameSizeSet(object):
:keyword max_size: the maximum size for growable devices
:type max_size: :class:`~.size.Size`
"""
- self.devices = [d.raw_device for d in devices]
+ self.devices = []
+ for device in devices:
+ if isinstance(device, LUKSDevice):
+ partition = device.slave
+ else:
+ partition = device
+
+ self.devices.append(partition)
+
self.size = size / len(devices)
self.grow = grow
self.max_size = max_size
diff --git a/blivet/populator/helpers/dm.py b/blivet/populator/helpers/dm.py
index 4721390e..0ad065e2 100644
--- a/blivet/populator/helpers/dm.py
+++ b/blivet/populator/helpers/dm.py
@@ -47,13 +47,13 @@ class DMDevicePopulator(DevicePopulator):
name = udev.device_get_name(self.data)
log_method_call(self, name=name)
sysfs_path = udev.device_get_sysfs_path(self.data)
- parent_devices = self._devicetree._add_parent_devices(self.data)
+ slave_devices = self._devicetree._add_slave_devices(self.data)
device = self._devicetree.get_device_by_name(name)
if device is None:
device = DMDevice(name, dm_uuid=self.data.get('DM_UUID'),
sysfs_path=sysfs_path, exists=True,
- parents=[parent_devices[0]])
+ parents=[slave_devices[0]])
device.protected = True
device.controllable = False
self._devicetree._add_device(device)
diff --git a/blivet/populator/helpers/luks.py b/blivet/populator/helpers/luks.py
index 3221122a..9b5023f8 100644
--- a/blivet/populator/helpers/luks.py
+++ b/blivet/populator/helpers/luks.py
@@ -43,7 +43,7 @@ class LUKSDevicePopulator(DevicePopulator):
return udev.device_is_dm_luks(data)
def run(self):
- parents = self._devicetree._add_parent_devices(self.data)
+ parents = self._devicetree._add_slave_devices(self.data)
device = LUKSDevice(udev.device_get_name(self.data),
sysfs_path=udev.device_get_sysfs_path(self.data),
parents=parents,
@@ -58,7 +58,7 @@ class IntegrityDevicePopulator(DevicePopulator):
return udev.device_is_dm_integrity(data)
def run(self):
- parents = self._devicetree._add_parent_devices(self.data)
+ parents = self._devicetree._add_slave_devices(self.data)
name = udev.device_get_name(self.data)
try:
diff --git a/blivet/populator/helpers/lvm.py b/blivet/populator/helpers/lvm.py
index 6ef2f417..b549e8d3 100644
--- a/blivet/populator/helpers/lvm.py
+++ b/blivet/populator/helpers/lvm.py
@@ -58,7 +58,7 @@ class LVMDevicePopulator(DevicePopulator):
log.warning("found non-vg device with name %s", vg_name)
device = None
- self._devicetree._add_parent_devices(self.data)
+ self._devicetree._add_slave_devices(self.data)
# LVM provides no means to resolve conflicts caused by duplicated VG
# names, so we're just being optimistic here. Woo!
diff --git a/blivet/populator/helpers/mdraid.py b/blivet/populator/helpers/mdraid.py
index a7602d20..9bec11ef 100644
--- a/blivet/populator/helpers/mdraid.py
+++ b/blivet/populator/helpers/mdraid.py
@@ -31,7 +31,7 @@ from ... import udev
from ...devicelibs import raid
from ...devices import MDRaidArrayDevice, MDContainerDevice
from ...devices import device_path_to_name
-from ...errors import DeviceError, NoParentsError
+from ...errors import DeviceError, NoSlavesError
from ...flags import flags
from ...storage_log import log_method_call
from .devicepopulator import DevicePopulator
@@ -52,12 +52,12 @@ class MDDevicePopulator(DevicePopulator):
log_method_call(self, name=name)
try:
- self._devicetree._add_parent_devices(self.data)
- except NoParentsError:
- log.error("no parents found for mdarray %s, skipping", name)
+ self._devicetree._add_slave_devices(self.data)
+ except NoSlavesError:
+ log.error("no slaves found for mdarray %s, skipping", name)
return None
- # try to get the device again now that we've got all the parents
+ # try to get the device again now that we've got all the slaves
device = self._devicetree.get_device_by_name(name, incomplete=flags.allow_imperfect_devices)
if device is None:
@@ -74,8 +74,8 @@ class MDDevicePopulator(DevicePopulator):
device.name = name
if device is None:
- # if we get here, we found all of the parent devices and
- # something must be wrong -- if all of the parents are in
+ # 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 name is None:
name = udev.device_get_name(self.data)
diff --git a/blivet/populator/helpers/multipath.py b/blivet/populator/helpers/multipath.py
index 96c0a9ad..10c745bf 100644
--- a/blivet/populator/helpers/multipath.py
+++ b/blivet/populator/helpers/multipath.py
@@ -40,13 +40,13 @@ class MultipathDevicePopulator(DevicePopulator):
name = udev.device_get_name(self.data)
log_method_call(self, name=name)
- parent_devices = self._devicetree._add_parent_devices(self.data)
+ slave_devices = self._devicetree._add_slave_devices(self.data)
device = None
- if parent_devices:
- device = MultipathDevice(name, parents=parent_devices,
+ if slave_devices:
+ device = MultipathDevice(name, parents=slave_devices,
sysfs_path=udev.device_get_sysfs_path(self.data),
- wwn=parent_devices[0].wwn)
+ wwn=slave_devices[0].wwn)
self._devicetree._add_device(device)
return device
diff --git a/blivet/populator/populator.py b/blivet/populator/populator.py
index 3a419418..068270b2 100644
--- a/blivet/populator/populator.py
+++ b/blivet/populator/populator.py
@@ -31,7 +31,7 @@ gi.require_version("BlockDev", "2.0")
from gi.repository import BlockDev as blockdev
-from ..errors import DeviceError, DeviceTreeError, NoParentsError
+from ..errors import DeviceError, DeviceTreeError, NoSlavesError
from ..devices import DMLinearDevice, DMRaidArrayDevice
from ..devices import FileDevice, LoopDevice
from ..devices import MDRaidArrayDevice
@@ -92,55 +92,56 @@ class PopulatorMixin(object):
self._cleanup = False
- def _add_parent_devices(self, info):
- """ Add all parents of a device, raising DeviceTreeError on failure.
+ def _add_slave_devices(self, info):
+ """ Add all slaves of a device, raising DeviceTreeError on failure.
:param :class:`pyudev.Device` info: the device's udev info
- :raises: :class:`~.errors.DeviceTreeError if no parents are found or
- if we fail to add any parent
- :returns: a list of parent devices
+ :raises: :class:`~.errors.DeviceTreeError if no slaves are found or
+ if we fail to add any slave
+ :returns: a list of slave devices
:rtype: list of :class:`~.StorageDevice`
"""
name = udev.device_get_name(info)
sysfs_path = udev.device_get_sysfs_path(info)
- parent_dir = os.path.normpath("%s/slaves" % sysfs_path)
- parent_names = os.listdir(parent_dir)
- parent_devices = []
- if not parent_names:
- log.error("no parents found for %s", name)
- raise NoParentsError("no parents found for device %s" % name)
-
- for parent_name in parent_names:
- path = os.path.normpath("%s/%s" % (parent_dir, parent_name))
- parent_info = udev.get_device(os.path.realpath(path))
-
- if not parent_info:
- msg = "unable to get udev info for %s" % parent_name
+ slave_dir = os.path.normpath("%s/slaves" % sysfs_path)
+ slave_names = os.listdir(slave_dir)
+ slave_devices = []
+ if not slave_names:
+ log.error("no slaves found for %s", name)
+ raise NoSlavesError("no slaves found for device %s" % name)
+
+ for slave_name in slave_names:
+ path = os.path.normpath("%s/%s" % (slave_dir, slave_name))
+ slave_info = udev.get_device(os.path.realpath(path))
+
+ if not slave_info:
+ msg = "unable to get udev info for %s" % slave_name
raise DeviceTreeError(msg)
# cciss in sysfs is "cciss!cXdYpZ" but we need "cciss/cXdYpZ"
- parent_name = udev.device_get_name(parent_info).replace("!", "/")
-
- parent_dev = self.get_device_by_name(parent_name)
- if not parent_dev and parent_info:
- # we haven't scanned the parent yet, so do it now
- self.handle_device(parent_info)
- parent_dev = self.get_device_by_name(parent_name)
- if parent_dev is None:
+ slave_name = udev.device_get_name(slave_info).replace("!", "/")
+
+ slave_dev = self.get_device_by_name(slave_name)
+ if not slave_dev and slave_info:
+ # we haven't scanned the slave yet, so do it now
+ self.handle_device(slave_info)
+ slave_dev = self.get_device_by_name(slave_name)
+ if slave_dev is None:
if udev.device_is_dm_lvm(info):
- if parent_name not in lvs_info.cache:
+ if slave_name not in lvs_info.cache:
# we do not expect hidden lvs to be in the tree
continue
- # if the current parent is still not in
+ # if the current slave is still not in
# the tree, something has gone wrong
- log.error("failure scanning device %s: could not add parent %s", name, parent_name)
- msg = "failed to add parent %s of device %s" % (parent_name, name)
+ log.error("failure scanning device %s: could not add slave %s", name, slave_name)
+ msg = "failed to add slave %s of device %s" % (slave_name,
+ name)
raise DeviceTreeError(msg)
- parent_devices.append(parent_dev)
+ slave_devices.append(slave_dev)
- return parent_devices
+ return slave_devices
def _add_name(self, name):
if name not in self.names:
diff --git a/blivet/threads.py b/blivet/threads.py
index 5e2dff3f..1a5cc6db 100644
--- a/blivet/threads.py
+++ b/blivet/threads.py
@@ -63,11 +63,12 @@ class SynchronizedMeta(type):
"""
def __new__(cls, name, bases, dct):
new_dct = {}
+ blacklist = dct.get('_unsynchronized_methods', [])
for n in dct:
obj = dct[n]
# Do not decorate class or static methods.
- if n in dct.get('_unsynchronized_methods', []):
+ if n in blacklist:
pass
elif isinstance(obj, FunctionType):
obj = exclusive(obj)
diff --git a/blivet/udev.py b/blivet/udev.py
index efbc53d6..ddc49a37 100644
--- a/blivet/udev.py
+++ b/blivet/udev.py
@@ -39,7 +39,7 @@ from gi.repository import BlockDev as blockdev
global_udev = pyudev.Context()
log = logging.getLogger("blivet")
-ignored_device_names = []
+device_name_blacklist = []
""" device name regexes to ignore; this should be empty by default """
@@ -77,7 +77,7 @@ def get_devices(subsystem="block"):
result = []
for device in global_udev.list_devices(subsystem=subsystem):
- if not __is_ignored_blockdev(device.sys_name):
+ if not __is_blacklisted_blockdev(device.sys_name):
dev = device_to_dict(device)
result.append(dev)
@@ -176,13 +176,13 @@ def resolve_glob(glob):
return ret
-def __is_ignored_blockdev(dev_name):
+def __is_blacklisted_blockdev(dev_name):
"""Is this a blockdev we never want for an install?"""
if dev_name.startswith("ram") or dev_name.startswith("fd"):
return True
- if ignored_device_names:
- if any(re.search(expr, dev_name) for expr in ignored_device_names):
+ if device_name_blacklist:
+ if any(re.search(expr, dev_name) for expr in device_name_blacklist):
return True
dev_path = "/sys/class/block/%s" % dev_name
@@ -375,7 +375,7 @@ def device_is_disk(info):
device_is_dm_crypt(info) or
device_is_dm_stratis(info) or
(device_is_md(info) and
- (not device_get_md_container(info) and not all(device_is_disk(d) for d in device_get_parents(info))))))
+ (not device_get_md_container(info) and not all(device_is_disk(d) for d in device_get_slaves(info))))))
def device_is_partition(info):
@@ -454,18 +454,18 @@ def device_get_devname(info):
return info.get('DEVNAME')
-def device_get_parents(info):
- """ Return a list of udev device objects representing this device's parents. """
- parents_dir = device_get_sysfs_path(info) + "/slaves/"
+def device_get_slaves(info):
+ """ Return a list of udev device objects representing this device's slaves. """
+ slaves_dir = device_get_sysfs_path(info) + "/slaves/"
names = list()
- if os.path.isdir(parents_dir):
- names = os.listdir(parents_dir)
+ if os.path.isdir(slaves_dir):
+ names = os.listdir(slaves_dir)
- parents = list()
+ slaves = list()
for name in names:
- parents.append(get_device(device_node="/dev/" + name))
+ slaves.append(get_device(device_node="/dev/" + name))
- return parents
+ return slaves
def device_get_holders(info):
@@ -742,7 +742,7 @@ def device_get_partition_disk(info):
disk = None
majorminor = info.get("ID_PART_ENTRY_DISK")
sysfs_path = device_get_sysfs_path(info)
- parents_dir = "%s/slaves" % sysfs_path
+ slaves_dir = "%s/slaves" % sysfs_path
if majorminor:
major, minor = majorminor.split(":")
for device in get_devices():
@@ -750,8 +750,8 @@ def device_get_partition_disk(info):
disk = device_get_name(device)
break
elif device_is_dm_partition(info):
- if os.path.isdir(parents_dir):
- parents = os.listdir(parents_dir)
+ if os.path.isdir(slaves_dir):
+ parents = os.listdir(slaves_dir)
if len(parents) == 1:
disk = resolve_devspec(parents[0].replace('!', '/'))
else:
diff --git a/tests/unit_tests/devicefactory_test.py b/tests/unit_tests/devicefactory_test.py
index ff6bcb9e..552aadc1 100644
--- a/tests/unit_tests/devicefactory_test.py
+++ b/tests/unit_tests/devicefactory_test.py
@@ -115,9 +115,9 @@ class DeviceFactoryTestCase(unittest.TestCase):
kwargs.get("encrypted", False) or
kwargs.get("container_encrypted", False))
if kwargs.get("encrypted", False):
- self.assertEqual(device.parents[0].format.luks_version,
+ self.assertEqual(device.slave.format.luks_version,
kwargs.get("luks_version", crypto.DEFAULT_LUKS_VERSION))
- self.assertEqual(device.raw_device.format.luks_sector_size,
+ self.assertEqual(device.slave.format.luks_sector_size,
kwargs.get("luks_sector_size", 0))
self.assertTrue(set(device.disks).issubset(kwargs["disks"]))
@@ -357,7 +357,7 @@ class LVMFactoryTestCase(DeviceFactoryTestCase):
device = args[0]
if kwargs.get("encrypted"):
- container = device.parents[0].container
+ container = device.slave.container
else:
container = device.container
@@ -376,7 +376,7 @@ class LVMFactoryTestCase(DeviceFactoryTestCase):
self.assertIsInstance(pv, member_class)
if pv.encrypted:
- self.assertEqual(pv.parents[0].format.luks_version,
+ self.assertEqual(pv.slave.format.luks_version,
kwargs.get("luks_version", crypto.DEFAULT_LUKS_VERSION))
@patch("blivet.formats.lvmpv.LVMPhysicalVolume.formattable", return_value=True)
@@ -592,7 +592,7 @@ class LVMThinPFactoryTestCase(LVMFactoryTestCase):
device = args[0]
if kwargs.get("encrypted", False):
- thinlv = device.parents[0]
+ thinlv = device.slave
else:
thinlv = device
diff --git a/tests/unit_tests/devices_test/device_size_test.py b/tests/unit_tests/devices_test/device_size_test.py
index d0c0a3f4..a1efa86d 100644
--- a/tests/unit_tests/devices_test/device_size_test.py
+++ b/tests/unit_tests/devices_test/device_size_test.py
@@ -107,8 +107,8 @@ class LUKSDeviceSizeTest(StorageDeviceSizeTest):
def _get_device(self, *args, **kwargs):
exists = kwargs.get("exists", False)
- parent = StorageDevice(*args, size=kwargs["size"] + crypto.LUKS_METADATA_SIZE, exists=exists)
- return LUKSDevice(*args, **kwargs, parents=[parent])
+ slave = StorageDevice(*args, size=kwargs["size"] + crypto.LUKS_METADATA_SIZE, exists=exists)
+ return LUKSDevice(*args, **kwargs, parents=[slave])
def test_size_getter(self):
initial_size = Size("10 GiB")
@@ -116,4 +116,4 @@ class LUKSDeviceSizeTest(StorageDeviceSizeTest):
# for LUKS size depends on the backing device size
self.assertEqual(dev.size, initial_size)
- self.assertEqual(dev.raw_device.size, initial_size + crypto.LUKS_METADATA_SIZE)
+ self.assertEqual(dev.slave.size, initial_size + crypto.LUKS_METADATA_SIZE)
diff --git a/tests/unit_tests/populator_test.py b/tests/unit_tests/populator_test.py
index 369fe878..7ba04bac 100644
--- a/tests/unit_tests/populator_test.py
+++ b/tests/unit_tests/populator_test.py
@@ -86,7 +86,7 @@ class DMDevicePopulatorTestCase(PopulatorHelperTestCase):
@patch.object(DeviceTree, "get_device_by_name")
@patch.object(DMDevice, "status", return_value=True)
@patch.object(DMDevice, "update_sysfs_path")
- @patch.object(DeviceTree, "_add_parent_devices")
+ @patch.object(DeviceTree, "_add_slave_devices")
@patch("blivet.udev.device_get_name")
@patch("blivet.udev.device_get_sysfs_path", return_value=sentinel.sysfs_path)
def test_run(self, *args):
@@ -95,7 +95,7 @@ class DMDevicePopulatorTestCase(PopulatorHelperTestCase):
devicetree = DeviceTree()
- # The general case for dm devices is that adding the parent devices
+ # The general case for dm devices is that adding the slave/parent devices
# will result in the dm device itself being in the tree.
device = Mock()
device.id = 0
@@ -106,7 +106,7 @@ class DMDevicePopulatorTestCase(PopulatorHelperTestCase):
parent = Mock()
parent.id = 0
parent.parents = []
- devicetree._add_parent_devices.return_value = [parent]
+ devicetree._add_slave_devices.return_value = [parent]
devicetree._add_device(parent)
devicetree.get_device_by_name.return_value = None
device_name = "dmdevice"
@@ -235,7 +235,7 @@ class LVMDevicePopulatorTestCase(PopulatorHelperTestCase):
# could be the first helper class checked.
@patch.object(DeviceTree, "get_device_by_name")
- @patch.object(DeviceTree, "_add_parent_devices")
+ @patch.object(DeviceTree, "_add_slave_devices")
@patch("blivet.udev.device_get_name")
@patch("blivet.udev.device_get_lv_vg_name")
def test_run(self, *args):
@@ -247,7 +247,7 @@ class LVMDevicePopulatorTestCase(PopulatorHelperTestCase):
devicetree = DeviceTree()
data = Mock()
- # Add parent devices and then look up the device.
+ # Add slave/parent devices and then look up the device.
device_get_name.return_value = sentinel.lv_name
devicetree.get_device_by_name.return_value = None
@@ -267,7 +267,7 @@ class LVMDevicePopulatorTestCase(PopulatorHelperTestCase):
call(sentinel.vg_name),
call(sentinel.lv_name)])
- # Add parent devices, but the device is still not in the tree
+ # Add slave/parent devices, but the device is still not in the tree
get_device_by_name.side_effect = None
get_device_by_name.return_value = None
self.assertEqual(helper.run(), None)
@@ -639,7 +639,7 @@ class MDDevicePopulatorTestCase(PopulatorHelperTestCase):
# could be the first helper class checked.
@patch.object(DeviceTree, "get_device_by_name")
- @patch.object(DeviceTree, "_add_parent_devices")
+ @patch.object(DeviceTree, "_add_slave_devices")
@patch("blivet.udev.device_get_name")
@patch("blivet.udev.device_get_md_uuid")
@patch("blivet.udev.device_get_md_name")
@@ -650,7 +650,7 @@ class MDDevicePopulatorTestCase(PopulatorHelperTestCase):
devicetree = DeviceTree()
- # base case: _add_parent_devices gets the array into the tree
+ # base case: _add_slave_devices gets the array into the tree
data = Mock()
device = Mock()
device.parents = []
@@ -713,12 +713,12 @@ class MultipathDevicePopulatorTestCase(PopulatorHelperTestCase):
# could be the first helper class checked.
@patch("blivet.udev.device_get_sysfs_path")
- @patch.object(DeviceTree, "_add_parent_devices")
+ @patch.object(DeviceTree, "_add_slave_devices")
@patch("blivet.udev.device_get_name")
def test_run(self, *args):
"""Test multipath device populator."""
device_get_name = args[0]
- add_parent_devices = args[1]
+ add_slave_devices = args[1]
devicetree = DeviceTree()
# set up some fake udev data to verify handling of specific entries
@@ -733,13 +733,13 @@ class MultipathDevicePopulatorTestCase(PopulatorHelperTestCase):
device_name = "mpathtest"
device_get_name.return_value = device_name
- parent_1 = Mock(tags=set(), wwn=wwn[2:], id=0)
- parent_1.parents = []
- parent_2 = Mock(tags=set(), wwn=wwn[2:], id=0)
- parent_2.parents = []
- devicetree._add_device(parent_1)
- devicetree._add_device(parent_2)
- add_parent_devices.return_value = [parent_1, parent_2]
+ slave_1 = Mock(tags=set(), wwn=wwn[2:], id=0)
+ slave_1.parents = []
+ slave_2 = Mock(tags=set(), wwn=wwn[2:], id=0)
+ slave_2.parents = []
+ devicetree._add_device(slave_1)
+ devicetree._add_device(slave_2)
+ add_slave_devices.return_value = [slave_1, slave_2]
helper = self.helper_class(devicetree, data)
diff --git a/tests/unit_tests/udev_test.py b/tests/unit_tests/udev_test.py
index b208efa8..ebcd59e2 100644
--- a/tests/unit_tests/udev_test.py
+++ b/tests/unit_tests/udev_test.py
@@ -49,11 +49,11 @@ class UdevTest(unittest.TestCase):
@mock.patch('blivet.udev.device_is_dm_crypt', return_value=False)
@mock.patch('blivet.udev.device_is_md')
@mock.patch('blivet.udev.device_get_md_container')
- @mock.patch('blivet.udev.device_get_parents')
+ @mock.patch('blivet.udev.device_get_slaves')
def test_udev_device_is_disk_md(self, *args):
import blivet.udev
info = dict(DEVTYPE='disk', SYS_PATH=mock.sentinel.md_path)
- (device_get_parents, device_get_md_container, device_is_md) = args[:3] # pylint: disable=unbalanced-tuple-unpacking
+ (device_get_slaves, device_get_md_container, device_is_md) = args[:3] # pylint: disable=unbalanced-tuple-unpacking
disk_parents = [dict(DEVTYPE="disk", SYS_PATH='/fake/path/2'),
dict(DEVTYPE="disk", SYS_PATH='/fake/path/3')]
@@ -68,20 +68,20 @@ class UdevTest(unittest.TestCase):
# Intel FW RAID (MD RAID w/ container layer)
# device_get_container will return some mock value which will evaluate to True
device_get_md_container.return_value = mock.sentinel.md_container
- device_get_parents.side_effect = lambda info: list()
+ device_get_slaves.side_effect = lambda info: list()
self.assertTrue(blivet.udev.device_is_disk(info))
# Normal MD RAID
- device_get_parents.side_effect = lambda info: partition_parents if info['SYS_PATH'] == mock.sentinel.md_path else list()
+ device_get_slaves.side_effect = lambda info: partition_parents if info['SYS_PATH'] == mock.sentinel.md_path else list()
device_get_md_container.return_value = None
self.assertFalse(blivet.udev.device_is_disk(info))
# Dell FW RAID (MD RAID whose members are all whole disks)
- device_get_parents.side_effect = lambda info: disk_parents if info['SYS_PATH'] == mock.sentinel.md_path else list()
+ device_get_slaves.side_effect = lambda info: disk_parents if info['SYS_PATH'] == mock.sentinel.md_path else list()
self.assertTrue(blivet.udev.device_is_disk(info))
# Normal MD RAID (w/ at least one non-disk member)
- device_get_parents.side_effect = lambda info: mixed_parents if info['SYS_PATH'] == mock.sentinel.md_path else list()
+ device_get_slaves.side_effect = lambda info: mixed_parents if info['SYS_PATH'] == mock.sentinel.md_path else list()
self.assertFalse(blivet.udev.device_is_disk(info))
diff --git a/tests/vmtests/vmbackedtestcase.py b/tests/vmtests/vmbackedtestcase.py
index 797bac85..6255104f 100644
--- a/tests/vmtests/vmbackedtestcase.py
+++ b/tests/vmtests/vmbackedtestcase.py
@@ -50,7 +50,7 @@ class VMBackedTestCase(unittest.TestCase):
defined in set_up_disks.
"""
- udev.ignored_device_names = [r'^zram']
+ udev.device_name_blacklist = [r'^zram']
#
# create disk images
--
2.38.1

@ -0,0 +1,86 @@
From f6490c469904f4808c63a170210e53acc908b018 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 17 Aug 2022 14:24:21 +0200
Subject: [PATCH 1/2] Use MD populator instead of DM to handle DDF RAID format
---
blivet/formats/dmraid.py | 2 +-
blivet/formats/mdraid.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/blivet/formats/dmraid.py b/blivet/formats/dmraid.py
index 2ba9dcfe..ce15905d 100644
--- a/blivet/formats/dmraid.py
+++ b/blivet/formats/dmraid.py
@@ -43,7 +43,7 @@ class DMRaidMember(DeviceFormat):
#
# One problem that presents is the possibility of someone passing
# a dmraid member to the MDRaidArrayDevice constructor.
- _udev_types = ["adaptec_raid_member", "ddf_raid_member",
+ _udev_types = ["adaptec_raid_member",
"hpt37x_raid_member", "hpt45x_raid_member",
"isw_raid_member",
"jmicron_raid_member", "lsi_mega_raid_member",
diff --git a/blivet/formats/mdraid.py b/blivet/formats/mdraid.py
index 41ddef81..4aa3f3b0 100644
--- a/blivet/formats/mdraid.py
+++ b/blivet/formats/mdraid.py
@@ -41,7 +41,7 @@ class MDRaidMember(DeviceFormat):
""" An mdraid member disk. """
_type = "mdmember"
_name = N_("software RAID")
- _udev_types = ["linux_raid_member"]
+ _udev_types = ["linux_raid_member", "ddf_raid_member"]
parted_flag = PARTITION_RAID
_formattable = True # can be formatted
_supported = True # is supported
--
2.38.1
From 5fadd850aae217d7692a6c8a50b2dcd5e61a63cd Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 17 Aug 2022 14:24:58 +0200
Subject: [PATCH 2/2] Do not read DDF RAID UUID from udev
The UUID we get from udev isn't the array UUID, we need to get
that using libblockdev.
---
blivet/populator/helpers/mdraid.py | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/blivet/populator/helpers/mdraid.py b/blivet/populator/helpers/mdraid.py
index 3479e3f7..a7602d20 100644
--- a/blivet/populator/helpers/mdraid.py
+++ b/blivet/populator/helpers/mdraid.py
@@ -98,17 +98,21 @@ class MDFormatPopulator(FormatPopulator):
def _get_kwargs(self):
kwargs = super(MDFormatPopulator, self)._get_kwargs()
- try:
- # ID_FS_UUID contains the array UUID
- kwargs["md_uuid"] = udev.device_get_uuid(self.data)
- except KeyError:
- log.warning("mdraid member %s has no md uuid", udev.device_get_name(self.data))
+ kwargs["biosraid"] = udev.device_is_biosraid_member(self.data)
+ if not kwargs["biosraid"]:
+ try:
+ # ID_FS_UUID contains the array UUID
+ kwargs["md_uuid"] = udev.device_get_uuid(self.data)
+ except KeyError:
+ log.warning("mdraid member %s has no md uuid", udev.device_get_name(self.data))
+ else:
+ # for BIOS RAIDs we can't get the UUID from udev, we'll get it from mdadm in `run` below
+ kwargs["md_uuid"] = None
# reset the uuid to the member-specific value
# this will be None for members of v0 metadata arrays
kwargs["uuid"] = udev.device_get_md_device_uuid(self.data)
- kwargs["biosraid"] = udev.device_is_biosraid_member(self.data)
return kwargs
def run(self):
--
2.38.1

@ -0,0 +1,77 @@
From 62af1d7f96b8ed8eb8f2732787576161ae5da79f Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 13 Oct 2022 10:47:52 +0200
Subject: [PATCH] Revert "Remove the Blivet.roots attribute"
This reverts commit 19a826073345ca6b57a8f9a95ec855892320300e.
---
blivet/blivet.py | 21 +++++++++++++++++++++
blivet/devicefactory.py | 3 +++
2 files changed, 24 insertions(+)
diff --git a/blivet/blivet.py b/blivet/blivet.py
index bf72ee9c..dc066b03 100644
--- a/blivet/blivet.py
+++ b/blivet/blivet.py
@@ -88,6 +88,7 @@ class Blivet(object):
self.devicetree = DeviceTree(ignored_disks=self.ignored_disks,
exclusive_disks=self.exclusive_disks,
disk_images=self.disk_images)
+ self.roots = []
@property
def short_product_name(self):
@@ -1314,5 +1315,25 @@ class Blivet(object):
p = partition.disk.format.parted_disk.getPartitionByPath(partition.path)
partition.parted_partition = p
+ for root in new.roots:
+ root.swaps = [new.devicetree.get_device_by_id(d.id, hidden=True) for d in root.swaps]
+ root.swaps = [s for s in root.swaps if s]
+
+ removed = set()
+ for (mountpoint, old_dev) in root.mounts.items():
+ if old_dev is None:
+ continue
+
+ new_dev = new.devicetree.get_device_by_id(old_dev.id, hidden=True)
+ if new_dev is None:
+ # if the device has been removed don't include this
+ # mountpoint at all
+ removed.add(mountpoint)
+ else:
+ root.mounts[mountpoint] = new_dev
+
+ for mnt in removed:
+ del root.mounts[mnt]
+
log.debug("finished Blivet copy")
return new
diff --git a/blivet/devicefactory.py b/blivet/devicefactory.py
index 8105bfc7..6f460f6d 100644
--- a/blivet/devicefactory.py
+++ b/blivet/devicefactory.py
@@ -383,6 +383,7 @@ class DeviceFactory(object):
# used for error recovery
self.__devices = []
self.__actions = []
+ self.__roots = []
def _is_container_encrypted(self):
return all(isinstance(p, LUKSDevice) for p in self.device.container.parents)
@@ -994,10 +995,12 @@ class DeviceFactory(object):
_blivet_copy = self.storage.copy()
self.__devices = _blivet_copy.devicetree._devices
self.__actions = _blivet_copy.devicetree._actions
+ self.__roots = _blivet_copy.roots
def _revert_devicetree(self):
self.storage.devicetree._devices = self.__devices
self.storage.devicetree._actions = self.__actions
+ self.storage.roots = self.__roots
class PartitionFactory(DeviceFactory):
--
2.38.1

@ -0,0 +1,45 @@
From 1561bfe8820118178bbb07021adc1cacd875c4c7 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Tue, 18 Oct 2022 12:28:37 +0200
Subject: [PATCH] Fix potential AttributeError when getting stratis blockdev
info
---
blivet/static_data/stratis_info.py | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/blivet/static_data/stratis_info.py b/blivet/static_data/stratis_info.py
index bd1c5a18..42f230ee 100644
--- a/blivet/static_data/stratis_info.py
+++ b/blivet/static_data/stratis_info.py
@@ -124,20 +124,22 @@ class StratisInfo(object):
log.error("Failed to get DBus properties of '%s'", blockdev_path)
return None
+ blockdev_uuid = str(uuid.UUID(properties["Uuid"]))
+
pool_path = properties["Pool"]
if pool_path == "/":
pool_name = ""
+ return StratisBlockdevInfo(path=properties["Devnode"], uuid=blockdev_uuid,
+ pool_name="", pool_uuid="", object_path=blockdev_path)
else:
pool_info = self._get_pool_info(properties["Pool"])
if not pool_info:
return None
pool_name = pool_info.name
- blockdev_uuid = str(uuid.UUID(properties["Uuid"]))
-
- return StratisBlockdevInfo(path=properties["Devnode"], uuid=blockdev_uuid,
- pool_name=pool_name, pool_uuid=pool_info.uuid,
- object_path=blockdev_path)
+ return StratisBlockdevInfo(path=properties["Devnode"], uuid=blockdev_uuid,
+ pool_name=pool_name, pool_uuid=pool_info.uuid,
+ object_path=blockdev_path)
def _get_locked_pools_info(self):
locked_pools = []
--
2.38.1

@ -0,0 +1,27 @@
From b747c4ed07937f54a546ffb2f2c8c95e0797dd6c Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 20 Oct 2022 15:19:29 +0200
Subject: [PATCH] tests: Skip XFS resize test on CentOS/RHEL 8
Partitions on loop devices are broken on CentOS/RHEL 8.
---
tests/skip.yml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/tests/skip.yml b/tests/skip.yml
index 568c3fff..66b34493 100644
--- a/tests/skip.yml
+++ b/tests/skip.yml
@@ -29,3 +29,9 @@
- distro: "centos"
version: "9"
reason: "Creating RAID 1 LV on CentOS/RHEL 9 causes a system deadlock"
+
+- test: storage_tests.formats_test.fs_test.XFSTestCase.test_resize
+ skip_on:
+ - distro: ["centos", "enterprise_linux"]
+ version: "8"
+ reason: "Creating partitions on loop devices is broken on CentOS/RHEL 8 latest kernel"
--
2.37.3

@ -0,0 +1,160 @@
From 9618b84f94187efddc7316c2546bed923a91ecf9 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 3 Nov 2022 08:36:27 +0100
Subject: [PATCH 1/2] Revert "Set XFS minimal size to 300 MiB"
This reverts commit 307d49833771d161314bae50c68e70dc35c3bb36.
---
blivet/formats/fs.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blivet/formats/fs.py b/blivet/formats/fs.py
index 8c346aa5..33922f3a 100644
--- a/blivet/formats/fs.py
+++ b/blivet/formats/fs.py
@@ -1091,7 +1091,7 @@ class XFS(FS):
_modules = ["xfs"]
_labelfs = fslabeling.XFSLabeling()
_uuidfs = fsuuid.XFSUUID()
- _min_size = Size("300 MiB")
+ _min_size = Size("16 MiB")
_max_size = Size("16 EiB")
_formattable = True
_linux_native = True
--
2.38.1
From 24d94922d6879baa85aaa101f6b21efa568a9cbc Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 3 Nov 2022 08:36:39 +0100
Subject: [PATCH 2/2] Revert "tests: Create bigger devices for XFS testing"
This reverts commit 467cb8024010b2cabb1e92d9e64f6d3cbe949ad9.
---
tests/storage_tests/formats_test/fs_test.py | 7 +++----
tests/storage_tests/formats_test/fslabeling.py | 4 +---
tests/storage_tests/formats_test/fsuuid.py | 4 +---
tests/storage_tests/formats_test/labeling_test.py | 2 --
tests/storage_tests/formats_test/uuid_test.py | 3 ---
5 files changed, 5 insertions(+), 15 deletions(-)
diff --git a/tests/storage_tests/formats_test/fs_test.py b/tests/storage_tests/formats_test/fs_test.py
index cf8fb441..97f4cbbe 100644
--- a/tests/storage_tests/formats_test/fs_test.py
+++ b/tests/storage_tests/formats_test/fs_test.py
@@ -54,7 +54,6 @@ class ReiserFSTestCase(fstesting.FSAsRoot):
class XFSTestCase(fstesting.FSAsRoot):
_fs_class = fs.XFS
- _DEVICE_SIZE = Size("500 MiB")
def can_resize(self, an_fs):
resize_tasks = (an_fs._resize, an_fs._size_info)
@@ -96,12 +95,12 @@ class XFSTestCase(fstesting.FSAsRoot):
self.assertFalse(an_fs.resizable)
# Not resizable, so can not do resizing actions.
with self.assertRaises(DeviceFormatError):
- an_fs.target_size = Size("300 MiB")
+ an_fs.target_size = Size("64 MiB")
with self.assertRaises(DeviceFormatError):
an_fs.do_resize()
else:
disk = DiskDevice(os.path.basename(self.loop_devices[0]))
- part = self._create_partition(disk, Size("300 MiB"))
+ part = self._create_partition(disk, Size("50 MiB"))
an_fs = self._fs_class()
an_fs.device = part.path
self.assertIsNone(an_fs.create())
@@ -114,7 +113,7 @@ class XFSTestCase(fstesting.FSAsRoot):
part = self._create_partition(disk, size=part.size + Size("40 MiB"))
# Try a reasonable target size
- TARGET_SIZE = Size("325 MiB")
+ TARGET_SIZE = Size("64 MiB")
an_fs.target_size = TARGET_SIZE
self.assertEqual(an_fs.target_size, TARGET_SIZE)
self.assertNotEqual(an_fs._size, TARGET_SIZE)
diff --git a/tests/storage_tests/formats_test/fslabeling.py b/tests/storage_tests/formats_test/fslabeling.py
index ebe0b70a..0e0dc261 100644
--- a/tests/storage_tests/formats_test/fslabeling.py
+++ b/tests/storage_tests/formats_test/fslabeling.py
@@ -21,10 +21,8 @@ class LabelingAsRoot(loopbackedtestcase.LoopBackedTestCase):
_invalid_label = abc.abstractproperty(
doc="A label which is invalid for this filesystem.")
- _DEVICE_SIZE = Size("100 MiB")
-
def __init__(self, methodName='run_test'):
- super(LabelingAsRoot, self).__init__(methodName=methodName, device_spec=[self._DEVICE_SIZE])
+ super(LabelingAsRoot, self).__init__(methodName=methodName, device_spec=[Size("100 MiB")])
def setUp(self):
an_fs = self._fs_class()
diff --git a/tests/storage_tests/formats_test/fsuuid.py b/tests/storage_tests/formats_test/fsuuid.py
index 0b9762fd..16aa19a6 100644
--- a/tests/storage_tests/formats_test/fsuuid.py
+++ b/tests/storage_tests/formats_test/fsuuid.py
@@ -23,11 +23,9 @@ class SetUUID(loopbackedtestcase.LoopBackedTestCase):
_invalid_uuid = abc.abstractproperty(
doc="An invalid UUID for this filesystem.")
- _DEVICE_SIZE = Size("100 MiB")
-
def __init__(self, methodName='run_test'):
super(SetUUID, self).__init__(methodName=methodName,
- device_spec=[self._DEVICE_SIZE])
+ device_spec=[Size("100 MiB")])
def setUp(self):
an_fs = self._fs_class()
diff --git a/tests/storage_tests/formats_test/labeling_test.py b/tests/storage_tests/formats_test/labeling_test.py
index 0702260a..d24e6619 100644
--- a/tests/storage_tests/formats_test/labeling_test.py
+++ b/tests/storage_tests/formats_test/labeling_test.py
@@ -1,7 +1,6 @@
import unittest
from blivet.formats import device_formats
-from blivet.size import Size
import blivet.formats.fs as fs
import blivet.formats.swap as swap
@@ -62,7 +61,6 @@ class InitializationTestCase(unittest.TestCase):
class XFSTestCase(fslabeling.CompleteLabelingAsRoot):
_fs_class = fs.XFS
_invalid_label = "root filesystem"
- _DEVICE_SIZE = Size("500 MiB")
class FATFSTestCase(fslabeling.CompleteLabelingAsRoot):
diff --git a/tests/storage_tests/formats_test/uuid_test.py b/tests/storage_tests/formats_test/uuid_test.py
index af35c0ee..ee8d452e 100644
--- a/tests/storage_tests/formats_test/uuid_test.py
+++ b/tests/storage_tests/formats_test/uuid_test.py
@@ -2,7 +2,6 @@ import unittest
import blivet.formats.fs as fs
import blivet.formats.swap as swap
-from blivet.size import Size
from . import fsuuid
@@ -53,14 +52,12 @@ class XFSTestCase(fsuuid.SetUUIDWithMkFs):
_fs_class = fs.XFS
_invalid_uuid = "abcdefgh-ijkl-mnop-qrst-uvwxyz123456"
_valid_uuid = "97e3d40f-dca8-497d-8b86-92f257402465"
- _DEVICE_SIZE = Size("500 MiB")
class XFSAfterTestCase(fsuuid.SetUUIDAfterMkFs):
_fs_class = fs.XFS
_invalid_uuid = "abcdefgh-ijkl-mnop-qrst-uvwxyz123456"
_valid_uuid = "97e3d40f-dca8-497d-8b86-92f257402465"
- _DEVICE_SIZE = Size("500 MiB")
class FATFSTestCase(fsuuid.SetUUIDWithMkFs):
--
2.38.1

@ -0,0 +1,55 @@
From fed62af06eb1584adbacd821dfe79c2df52c6aa4 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 2 Nov 2022 12:14:28 +0100
Subject: [PATCH] Catch BlockDevNotImplementedError for btrfs plugin calls
This is a workaround for RHEL where the btrfs plugin is not
available and where we might still try to call some libblockdev
functions to gather information about preexisting btrfs devices.
---
blivet/devices/btrfs.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/blivet/devices/btrfs.py b/blivet/devices/btrfs.py
index 0e029715..1ae6a04d 100644
--- a/blivet/devices/btrfs.py
+++ b/blivet/devices/btrfs.py
@@ -362,7 +362,7 @@ class BTRFSVolumeDevice(BTRFSDevice, ContainerDevice, RaidDevice):
try:
subvols = blockdev.btrfs.list_subvolumes(mountpoint,
snapshots_only=snapshots_only)
- except blockdev.BtrfsError as e:
+ except (blockdev.BtrfsError, blockdev.BlockDevNotImplementedError) as e:
log.debug("failed to list subvolumes: %s", e)
else:
self._get_default_subvolume_id()
@@ -400,7 +400,7 @@ class BTRFSVolumeDevice(BTRFSDevice, ContainerDevice, RaidDevice):
with self._do_temp_mount() as mountpoint:
try:
subvolid = blockdev.btrfs.get_default_subvolume_id(mountpoint)
- except blockdev.BtrfsError as e:
+ except (blockdev.BtrfsError, blockdev.BlockDevNotImplementedError) as e:
log.debug("failed to get default subvolume id: %s", e)
self._default_subvolume_id = subvolid
@@ -413,7 +413,7 @@ class BTRFSVolumeDevice(BTRFSDevice, ContainerDevice, RaidDevice):
with self._do_temp_mount() as mountpoint:
try:
blockdev.btrfs.set_default_subvolume(mountpoint, vol_id)
- except blockdev.BtrfsError as e:
+ except (blockdev.BtrfsError, blockdev.BlockDevNotImplementedError) as e:
log.error("failed to set new default subvolume id (%s): %s",
vol_id, e)
# The only time we set a new default subvolume is so we can remove
@@ -471,7 +471,7 @@ class BTRFSVolumeDevice(BTRFSDevice, ContainerDevice, RaidDevice):
if not self.format.vol_uuid:
try:
bd_info = blockdev.btrfs.filesystem_info(self.parents[0].path)
- except blockdev.BtrfsError as e:
+ except (blockdev.BtrfsError, blockdev.BlockDevNotImplementedError) as e:
log.error("failed to get filesystem info for new btrfs volume %s", e)
else:
self.format.vol_uuid = bd_info.uuid
--
2.38.1

@ -0,0 +1,57 @@
From 2aba050e74dc5df483da022dcf436b101c7a4301 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 11 Jan 2023 14:59:24 +0100
Subject: [PATCH] Default to encryption sector size 512 for LUKS devices
We are currently letting cryptsetup decide the optimal encryption
sector size for LUKS. The problem is that for disks with physical
sector size 4096 cryptsetup will default to 4096 encryption sector
size even if the drive logical sector size is 512 which means
these disks cannot be combined with other 512 logical sector size
disks in LVM. This requires a more sophisticated solution in the
future, but for now just default to 512 if not specified by the
user otherwise.
Resolves: rhbz#2103800
---
blivet/formats/luks.py | 10 +++++++---
tests/unit_tests/formats_tests/luks_test.py | 2 +-
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/blivet/formats/luks.py b/blivet/formats/luks.py
index 8de4911f..2637e0c5 100644
--- a/blivet/formats/luks.py
+++ b/blivet/formats/luks.py
@@ -166,9 +166,13 @@ class LUKS(DeviceFormat):
if self.pbkdf_args.type == "pbkdf2" and self.pbkdf_args.max_memory_kb:
log.warning("Memory limit is not used for pbkdf2 and it will be ignored.")
- self.luks_sector_size = kwargs.get("luks_sector_size") or 0
- if self.luks_sector_size and self.luks_version != "luks2":
- raise ValueError("Sector size argument is valid only for LUKS version 2.")
+ self.luks_sector_size = kwargs.get("luks_sector_size")
+ if self.luks_version == "luks2":
+ if self.luks_sector_size is None:
+ self.luks_sector_size = 512 # XXX we don't want cryptsetup choose automatically here so fallback to 512
+ else:
+ if self.luks_sector_size:
+ raise ValueError("Sector size argument is valid only for LUKS version 2.")
def __repr__(self):
s = DeviceFormat.__repr__(self)
diff --git a/tests/unit_tests/formats_tests/luks_test.py b/tests/unit_tests/formats_tests/luks_test.py
index 5ae6acfe..ec7b7592 100644
--- a/tests/unit_tests/formats_tests/luks_test.py
+++ b/tests/unit_tests/formats_tests/luks_test.py
@@ -53,7 +53,7 @@ class LUKSNodevTestCase(unittest.TestCase):
def test_sector_size(self):
fmt = LUKS()
- self.assertEqual(fmt.luks_sector_size, 0)
+ self.assertEqual(fmt.luks_sector_size, 512)
with self.assertRaises(ValueError):
fmt = LUKS(luks_version="luks1", luks_sector_size=4096)
--
2.39.0

@ -0,0 +1,172 @@
From 11c3e695d9a2130f325bb5459a9881ff70338f71 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 9 Mar 2023 13:18:42 +0100
Subject: [PATCH] Add support for specifying stripe size for RAID LVs
---
blivet/devices/lvm.py | 28 +++++++++++++++++---
tests/storage_tests/devices_test/lvm_test.py | 12 +++++++--
tests/unit_tests/devices_test/lvm_test.py | 27 +++++++++++++++++++
3 files changed, 61 insertions(+), 6 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index b8595d63..41358e9b 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -659,7 +659,8 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
fmt=None, exists=False, sysfs_path='', grow=None, maxsize=None,
- percent=None, cache_request=None, pvs=None, from_lvs=None):
+ percent=None, cache_request=None, pvs=None, from_lvs=None,
+ stripe_size=0):
if not exists:
if seg_type not in [None, "linear", "thin", "thin-pool", "cache", "vdo-pool", "vdo", "cache-pool"] + lvm.raid_seg_types:
@@ -756,6 +757,15 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
if self._pv_specs:
self._assign_pv_space()
+ self._stripe_size = stripe_size
+ if not self.exists and self._stripe_size:
+ if self.seg_type not in lvm.raid_seg_types:
+ raise errors.DeviceError("Stripe size can be specified only for RAID volumes")
+ if self.seg_type in ("raid1", "RAID1", "1", 1, "mirror"):
+ raise errors.DeviceError("Specifying stripe size is not allowed for RAID1 or mirror")
+ if self.cache:
+ raise errors.DeviceError("Creating cached LVs with custom stripe size is not supported")
+
def _assign_pv_space(self):
if not self.is_raid_lv:
# nothing to do for non-RAID (and thus non-striped) LVs here
@@ -2295,7 +2305,7 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
parent_lv=None, int_type=None, origin=None, vorigin=False,
metadata_size=None, chunk_size=None, profile=None, from_lvs=None,
compression=False, deduplication=False, index_memory=0,
- write_policy=None, cache_mode=None, attach_to=None):
+ write_policy=None, cache_mode=None, attach_to=None, stripe_size=0):
"""
:param name: the device name (generally a device node's basename)
:type name: str
@@ -2375,6 +2385,11 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
be attached to when created
:type attach_to: :class:`LVMLogicalVolumeDevice`
+ For RAID LVs only:
+
+ :keyword stripe_size: size of the RAID stripe
+ :type stripe_size: :class:`~.size.Size`
+
"""
if isinstance(parents, (list, ParentList)):
@@ -2395,7 +2410,8 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
LVMCachePoolMixin.__init__(self, metadata_size, cache_mode, attach_to)
LVMLogicalVolumeBase.__init__(self, name, parents, size, uuid, seg_type,
fmt, exists, sysfs_path, grow, maxsize,
- percent, cache_request, pvs, from_lvs)
+ percent, cache_request, pvs, from_lvs,
+ stripe_size)
LVMVDOPoolMixin.__init__(self, compression, deduplication, index_memory,
write_policy)
LVMVDOLogicalVolumeMixin.__init__(self)
@@ -2651,8 +2667,12 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
pvs = [spec.pv.path for spec in self._pv_specs]
pvs = pvs or None
+ extra = dict()
+ if self._stripe_size:
+ extra["stripesize"] = str(int(self._stripe_size.convert_to("KiB")))
+
blockdev.lvm.lvcreate(self.vg.name, self._name, self.size,
- type=self.seg_type, pv_list=pvs)
+ type=self.seg_type, pv_list=pvs, **extra)
else:
fast_pvs = [pv.path for pv in self.cache.fast_pvs]
diff --git a/tests/storage_tests/devices_test/lvm_test.py b/tests/storage_tests/devices_test/lvm_test.py
index a055fc27..97ef1c4b 100644
--- a/tests/storage_tests/devices_test/lvm_test.py
+++ b/tests/storage_tests/devices_test/lvm_test.py
@@ -1,4 +1,5 @@
import os
+import subprocess
from ..storagetestcase import StorageTestCase
@@ -127,7 +128,7 @@ class LVMTestCase(StorageTestCase):
self.assertTrue(snap.is_snapshot_lv)
self.assertEqual(snap.origin, thinlv)
- def _test_lvm_raid(self, seg_type, raid_level):
+ def _test_lvm_raid(self, seg_type, raid_level, stripe_size=0):
disk1 = self.storage.devicetree.get_device_by_path(self.vdevs[0])
self.assertIsNotNone(disk1)
self.storage.initialize_disk(disk1)
@@ -151,7 +152,7 @@ class LVMTestCase(StorageTestCase):
raidlv = self.storage.new_lv(fmt_type="ext4", size=blivet.size.Size("50 MiB"),
parents=[vg], name="blivetTestRAIDLV",
- seg_type=seg_type, pvs=[pv1, pv2])
+ seg_type=seg_type, pvs=[pv1, pv2], stripe_size=stripe_size)
self.storage.create_device(raidlv)
self.storage.do_it()
@@ -163,9 +164,16 @@ class LVMTestCase(StorageTestCase):
self.assertEqual(raidlv.raid_level, raid_level)
self.assertEqual(raidlv.seg_type, seg_type)
+ if stripe_size:
+ out = subprocess.check_output(["lvs", "-o", "stripe_size", "--noheadings", "--nosuffix", "--units=b", raidlv.vg.name + "/" + raidlv.lvname])
+ self.assertEqual(out.decode().strip(), str(int(stripe_size.convert_to())))
+
def test_lvm_raid_raid0(self):
self._test_lvm_raid("raid0", blivet.devicelibs.raid.RAID0)
+ def test_lvm_raid_raid0_stripe_size(self):
+ self._test_lvm_raid("raid0", blivet.devicelibs.raid.RAID0, stripe_size=blivet.size.Size("1 MiB"))
+
def test_lvm_raid_striped(self):
self._test_lvm_raid("striped", blivet.devicelibs.raid.Striped)
diff --git a/tests/unit_tests/devices_test/lvm_test.py b/tests/unit_tests/devices_test/lvm_test.py
index 995c2da4..d7b55224 100644
--- a/tests/unit_tests/devices_test/lvm_test.py
+++ b/tests/unit_tests/devices_test/lvm_test.py
@@ -363,6 +363,33 @@ class LVMDeviceTest(unittest.TestCase):
self.assertEqual(pv.format.free, Size("264 MiB"))
self.assertEqual(pv2.format.free, Size("256 MiB"))
+ def test_lvm_logical_volume_raid_stripe_size(self):
+ pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"),
+ size=Size("1025 MiB"))
+ pv2 = StorageDevice("pv2", fmt=blivet.formats.get_format("lvmpv"),
+ size=Size("513 MiB"))
+ vg = LVMVolumeGroupDevice("testvg", parents=[pv, pv2])
+
+ with self.assertRaises(blivet.errors.DeviceError):
+ # non-raid LV
+ lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("1 GiB"),
+ fmt=blivet.formats.get_format("xfs"),
+ exists=False, stripe_size=Size("1 MiB"))
+
+ with self.assertRaises(blivet.errors.DeviceError):
+ # raid1 LV
+ lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("1 GiB"),
+ fmt=blivet.formats.get_format("xfs"),
+ exists=False, seg_type="raid1", pvs=[pv, pv2],
+ stripe_size=Size("1 MiB"))
+
+ lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("1 GiB"),
+ fmt=blivet.formats.get_format("xfs"),
+ exists=False, seg_type="raid0", pvs=[pv, pv2],
+ stripe_size=Size("1 MiB"))
+
+ self.assertEqual(lv._stripe_size, Size("1 MiB"))
+
def test_target_size(self):
pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"),
size=Size("1 GiB"))
--
2.40.1

@ -0,0 +1,68 @@
From 1af0d3c37a93e431790e641a329a7f34dabf291a Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 2 Mar 2023 12:34:42 +0100
Subject: [PATCH] Fix setting kickstart data
When changing our code to PEP8 compliant we also changed some
pykickstart properties like onPart by accident. This PR fixes this.
Resolves: rhbz#2175166
---
blivet/devices/btrfs.py | 4 ++--
blivet/devices/lvm.py | 2 +-
blivet/devices/partition.py | 6 +++---
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/blivet/devices/btrfs.py b/blivet/devices/btrfs.py
index 1ae6a04d..3f56624e 100644
--- a/blivet/devices/btrfs.py
+++ b/blivet/devices/btrfs.py
@@ -498,8 +498,8 @@ class BTRFSVolumeDevice(BTRFSDevice, ContainerDevice, RaidDevice):
def populate_ksdata(self, data):
super(BTRFSVolumeDevice, self).populate_ksdata(data)
- data.data_level = self.data_level.name if self.data_level else None
- data.metadata_level = self.metadata_level.name if self.metadata_level else None
+ data.dataLevel = self.data_level.name if self.data_level else None
+ data.metaDataLevel = self.metadata_level.name if self.metadata_level else None
data.devices = ["btrfs.%d" % p.id for p in self.parents]
data.preexist = self.exists
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index 41358e9b..c3132457 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -1161,7 +1161,7 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
if self.req_grow:
# base size could be literal or percentage
- data.max_size_mb = self.req_max_size.convert_to(MiB)
+ data.maxSizeMB = self.req_max_size.convert_to(MiB)
elif data.resize:
data.size = self.target_size.convert_to(MiB)
diff --git a/blivet/devices/partition.py b/blivet/devices/partition.py
index 89d907c2..0e9250ce 100644
--- a/blivet/devices/partition.py
+++ b/blivet/devices/partition.py
@@ -982,14 +982,14 @@ class PartitionDevice(StorageDevice):
data.size = self.req_base_size.round_to_nearest(MiB, rounding=ROUND_DOWN).convert_to(spec=MiB)
data.grow = self.req_grow
if self.req_grow:
- data.max_size_mb = self.req_max_size.convert_to(MiB)
+ data.maxSizeMB = self.req_max_size.convert_to(MiB)
# data.disk = self.disk.name # by-id
if self.req_disks and len(self.req_disks) == 1:
data.disk = self.disk.name
- data.prim_only = self.req_primary
+ data.primOnly = self.req_primary
else:
- data.on_part = self.name # by-id
+ data.onPart = self.name # by-id
if data.resize:
# on s390x in particular, fractional sizes are reported, which
--
2.40.1

@ -0,0 +1,133 @@
From c2b06150df0b876c7d442097b6c9ca90c9ca2ecc Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 4 May 2023 11:35:44 +0200
Subject: [PATCH] Do not set memory limit for LUKS2 when running in FIPS mode
With FIPS enabled LUKS uses pbkdf and not argon so the memory
limit is not a valid parameter.
Resolves: rhbz#2183437
---
blivet/devicelibs/crypto.py | 11 +++++++
blivet/formats/luks.py | 12 ++++----
tests/unit_tests/formats_tests/luks_test.py | 30 +++++++++++++++++++
.../unit_tests/formats_tests/methods_test.py | 3 +-
4 files changed, 50 insertions(+), 6 deletions(-)
diff --git a/blivet/devicelibs/crypto.py b/blivet/devicelibs/crypto.py
index f0caf0f7..68e68db1 100644
--- a/blivet/devicelibs/crypto.py
+++ b/blivet/devicelibs/crypto.py
@@ -21,6 +21,7 @@
#
import hashlib
+import os
import gi
gi.require_version("BlockDev", "2.0")
@@ -100,3 +101,13 @@ def calculate_integrity_metadata_size(device_size, algorithm=DEFAULT_INTEGRITY_A
jsize = (jsize / SECTOR_SIZE + 1) * SECTOR_SIZE # round up to sector
return msize + jsize
+
+
+def is_fips_enabled():
+ if not os.path.exists("/proc/sys/crypto/fips_enabled"):
+ # if the file doesn't exist, we are definitely not in FIPS mode
+ return False
+
+ with open("/proc/sys/crypto/fips_enabled", "r") as f:
+ enabled = f.read()
+ return enabled.strip() == "1"
diff --git a/blivet/formats/luks.py b/blivet/formats/luks.py
index 2637e0c5..adf3c711 100644
--- a/blivet/formats/luks.py
+++ b/blivet/formats/luks.py
@@ -303,11 +303,13 @@ class LUKS(DeviceFormat):
if luks_data.pbkdf_args:
self.pbkdf_args = luks_data.pbkdf_args
else:
- mem_limit = crypto.calculate_luks2_max_memory()
- if mem_limit:
- self.pbkdf_args = LUKS2PBKDFArgs(max_memory_kb=int(mem_limit.convert_to(KiB)))
- luks_data.pbkdf_args = self.pbkdf_args
- log.info("PBKDF arguments for LUKS2 not specified, using defaults with memory limit %s", mem_limit)
+ # argon is not used with FIPS so we don't need to adjust the memory when in FIPS mode
+ if not crypto.is_fips_enabled():
+ mem_limit = crypto.calculate_luks2_max_memory()
+ if mem_limit:
+ self.pbkdf_args = LUKS2PBKDFArgs(max_memory_kb=int(mem_limit.convert_to(KiB)))
+ luks_data.pbkdf_args = self.pbkdf_args
+ log.info("PBKDF arguments for LUKS2 not specified, using defaults with memory limit %s", mem_limit)
if self.pbkdf_args:
pbkdf = blockdev.CryptoLUKSPBKDF(type=self.pbkdf_args.type,
diff --git a/tests/unit_tests/formats_tests/luks_test.py b/tests/unit_tests/formats_tests/luks_test.py
index ec7b7592..1127e968 100644
--- a/tests/unit_tests/formats_tests/luks_test.py
+++ b/tests/unit_tests/formats_tests/luks_test.py
@@ -6,9 +6,14 @@ except ImportError:
import unittest
from blivet.formats.luks import LUKS
+from blivet.size import Size
+from blivet.static_data import luks_data
class LUKSNodevTestCase(unittest.TestCase):
+ def setUp(self):
+ luks_data.pbkdf_args = None
+
def test_create_discard_option(self):
# flags.discard_new=False --> no discard
fmt = LUKS(exists=False)
@@ -51,6 +56,31 @@ class LUKSNodevTestCase(unittest.TestCase):
fmt = LUKS(cipher="aes-cbc-plain64")
self.assertEqual(fmt.key_size, 0)
+ def test_luks2_pbkdf_memory_fips(self):
+ fmt = LUKS()
+ with patch("blivet.formats.luks.blockdev.crypto") as bd:
+ # fips enabled, pbkdf memory should not be set
+ with patch("blivet.formats.luks.crypto") as crypto:
+ attrs = {"is_fips_enabled.return_value": True,
+ "get_optimal_luks_sector_size.return_value": 0,
+ "calculate_luks2_max_memory.return_value": Size("256 MiB")}
+ crypto.configure_mock(**attrs)
+
+ fmt._create()
+ crypto.calculate_luks2_max_memory.assert_not_called()
+ self.assertEqual(bd.luks_format.call_args[1]["extra"].pbkdf.max_memory_kb, 0)
+
+ # fips disabled, pbkdf memory should be set
+ with patch("blivet.formats.luks.crypto") as crypto:
+ attrs = {"is_fips_enabled.return_value": False,
+ "get_optimal_luks_sector_size.return_value": 0,
+ "calculate_luks2_max_memory.return_value": Size("256 MiB")}
+ crypto.configure_mock(**attrs)
+
+ fmt._create()
+ crypto.calculate_luks2_max_memory.assert_called()
+ self.assertEqual(bd.luks_format.call_args[1]["extra"].pbkdf.max_memory_kb, 256 * 1024)
+
def test_sector_size(self):
fmt = LUKS()
self.assertEqual(fmt.luks_sector_size, 512)
diff --git a/tests/unit_tests/formats_tests/methods_test.py b/tests/unit_tests/formats_tests/methods_test.py
index 2743b7db..5d30c260 100644
--- a/tests/unit_tests/formats_tests/methods_test.py
+++ b/tests/unit_tests/formats_tests/methods_test.py
@@ -366,7 +366,8 @@ class LUKSMethodsTestCase(FormatMethodsTestCase):
def _test_create_backend(self):
self.format.exists = False
- self.format.create()
+ with patch("blivet.devicelibs.crypto.is_fips_enabled", return_value=False):
+ self.format.create()
self.assertTrue(self.patches["blockdev"].crypto.luks_format.called) # pylint: disable=no-member
def _test_setup_backend(self):
--
2.40.1

@ -0,0 +1,265 @@
From eb16230427fc1081f8515e6ad69ccf99ca521e5d Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Tue, 4 Apr 2023 13:31:40 +0200
Subject: [PATCH 1/2] Add support for filesystem online resize
Resolves: rhbz#2168680
---
blivet/devices/lvm.py | 13 ++++++++-----
blivet/devices/partition.py | 11 ++++++-----
blivet/flags.py | 3 +++
blivet/formats/fs.py | 32 ++++++++++++++++++++++++++++----
blivet/formats/fslib.py | 7 +++++++
5 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index c3132457..ca45c4b5 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -42,6 +42,7 @@ from .. import errors
from .. import util
from ..storage_log import log_method_call
from .. import udev
+from ..flags import flags
from ..size import Size, KiB, MiB, ROUND_UP, ROUND_DOWN
from ..static_data.lvm_info import lvs_info
from ..tasks import availability
@@ -2729,12 +2730,14 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
# Setup VG parents (in case they are dmraid partitions for example)
self.vg.setup_parents(orig=True)
- if self.original_format.exists:
- self.original_format.teardown()
- if self.format.exists:
- self.format.teardown()
+ if not flags.allow_online_fs_resize:
+ if self.original_format.exists:
+ self.original_format.teardown()
+ if self.format.exists:
+ self.format.teardown()
+
+ udev.settle()
- udev.settle()
blockdev.lvm.lvresize(self.vg.name, self._name, self.size)
@type_specific
diff --git a/blivet/devices/partition.py b/blivet/devices/partition.py
index 0e9250ce..6ae4b8d3 100644
--- a/blivet/devices/partition.py
+++ b/blivet/devices/partition.py
@@ -745,11 +745,12 @@ class PartitionDevice(StorageDevice):
if not self.exists:
raise errors.DeviceError("device has not been created")
- # don't teardown when resizing luks
- if self.format.type == "luks" and self.children:
- self.children[0].format.teardown()
- else:
- self.teardown()
+ if not flags.allow_online_fs_resize:
+ # don't teardown when resizing luks
+ if self.format.type == "luks" and self.children:
+ self.children[0].format.teardown()
+ else:
+ self.teardown()
if not self.sysfs_path:
return
diff --git a/blivet/flags.py b/blivet/flags.py
index 6364164d..ecfa7ad7 100644
--- a/blivet/flags.py
+++ b/blivet/flags.py
@@ -91,6 +91,9 @@ class Flags(object):
self.debug_threads = False
+ # Allow online filesystem resizes
+ self.allow_online_fs_resize = False
+
def get_boot_cmdline(self):
with open("/proc/cmdline") as f:
buf = f.read().strip()
diff --git a/blivet/formats/fs.py b/blivet/formats/fs.py
index 33922f3a..3f553eb0 100644
--- a/blivet/formats/fs.py
+++ b/blivet/formats/fs.py
@@ -56,7 +56,7 @@ from ..i18n import N_
from .. import udev
from ..mounts import mounts_cache
-from .fslib import kernel_filesystems
+from .fslib import kernel_filesystems, FSResize
import logging
log = logging.getLogger("blivet")
@@ -88,6 +88,9 @@ class FS(DeviceFormat):
# value is already unpredictable and can change in the future...
_metadata_size_factor = 1.0
+ # support for resize: grow/shrink, online/offline
+ _resize_support = 0
+
config_actions_map = {"label": "write_label"}
def __init__(self, **kwargs):
@@ -436,12 +439,27 @@ class FS(DeviceFormat):
self.write_uuid()
def _pre_resize(self):
- # file systems need a check before being resized
- self.do_check()
+ if self.status:
+ if flags.allow_online_fs_resize:
+ if self.target_size > self.size and not self._resize_support & FSResize.ONLINE_GROW:
+ raise FSError("This filesystem doesn't support online growing")
+ if self.target_size < self.size and not self._resize_support & FSResize.ONLINE_SHRINK:
+ raise FSError("This filesystem doesn't support online shrinking")
+ else:
+ raise FSError("Resizing of mounted filesystems is disabled")
+
+ if self.status:
+ # fsck tools in general don't allow checks on mounted filesystems
+ log.debug("Filesystem on %s is mounted, not checking", self.device)
+ else:
+ # file systems need a check before being resized
+ self.do_check()
+
super(FS, self)._pre_resize()
def _post_resize(self):
- self.do_check()
+ if not self.status:
+ self.do_check()
super(FS, self)._post_resize()
def do_check(self):
@@ -838,6 +856,7 @@ class Ext2FS(FS):
_formattable = True
_supported = True
_resizable = True
+ _resize_support = FSResize.ONLINE_GROW | FSResize.OFFLINE_GROW | FSResize.OFFLINE_SHRINK
_linux_native = True
_max_size = Size("8 TiB")
_dump = True
@@ -1097,6 +1116,7 @@ class XFS(FS):
_linux_native = True
_supported = True
_resizable = True
+ _resize_support = FSResize.ONLINE_GROW | FSResize.OFFLINE_GROW
_packages = ["xfsprogs"]
_fsck_class = fsck.XFSCK
_info_class = fsinfo.XFSInfo
@@ -1247,6 +1267,7 @@ class NTFS(FS):
_labelfs = fslabeling.NTFSLabeling()
_uuidfs = fsuuid.NTFSUUID()
_resizable = True
+ _resize_support = FSResize.OFFLINE_GROW | FSResize.OFFLINE_SHRINK
_formattable = True
_supported = True
_min_size = Size("1 MiB")
@@ -1490,6 +1511,9 @@ class TmpFS(NoDevFS):
# same, nothing actually needs to be set
pass
+ def _pre_resize(self):
+ self.do_check()
+
def do_resize(self):
# Override superclass method to record whether mount options
# should include an explicit size specification.
diff --git a/blivet/formats/fslib.py b/blivet/formats/fslib.py
index ea93b1fd..8722e942 100644
--- a/blivet/formats/fslib.py
+++ b/blivet/formats/fslib.py
@@ -36,3 +36,10 @@ def update_kernel_filesystems():
update_kernel_filesystems()
+
+
+class FSResize():
+ OFFLINE_SHRINK = 1 << 1
+ OFFLINE_GROW = 1 << 2
+ ONLINE_SHRINK = 1 << 3
+ ONLINE_GROW = 1 << 4
--
2.40.1
From 3fce5d0bfd7b09a976ff49feed15077477c6a425 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 6 Apr 2023 14:02:11 +0200
Subject: [PATCH 2/2] Add a test case for filesystem online resize
Related: rhbz#2168680
---
tests/storage_tests/formats_test/fs_test.py | 43 ++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/tests/storage_tests/formats_test/fs_test.py b/tests/storage_tests/formats_test/fs_test.py
index 97f4cbbe..1d42dc21 100644
--- a/tests/storage_tests/formats_test/fs_test.py
+++ b/tests/storage_tests/formats_test/fs_test.py
@@ -6,9 +6,10 @@ import parted
import blivet.formats.fs as fs
from blivet.size import Size, ROUND_DOWN
-from blivet.errors import DeviceFormatError
+from blivet.errors import DeviceFormatError, FSError
from blivet.formats import get_format
from blivet.devices import PartitionDevice, DiskDevice
+from blivet.flags import flags
from .loopbackedtestcase import LoopBackedTestCase
@@ -26,6 +27,46 @@ class Ext3FSTestCase(Ext2FSTestCase):
class Ext4FSTestCase(Ext3FSTestCase):
_fs_class = fs.Ext4FS
+ def test_online_resize(self):
+ an_fs = self._fs_class()
+ if not an_fs.formattable:
+ self.skipTest("can not create filesystem %s" % an_fs.name)
+ an_fs.device = self.loop_devices[0]
+ self.assertIsNone(an_fs.create())
+ an_fs.update_size_info()
+
+ if not self.can_resize(an_fs):
+ self.skipTest("filesystem is not resizable")
+
+ # shrink offline first (ext doesn't support online shrinking)
+ TARGET_SIZE = Size("64 MiB")
+ an_fs.target_size = TARGET_SIZE
+ self.assertEqual(an_fs.target_size, TARGET_SIZE)
+ self.assertNotEqual(an_fs._size, TARGET_SIZE)
+ self.assertIsNone(an_fs.do_resize())
+
+ with tempfile.TemporaryDirectory() as mountpoint:
+ an_fs.mount(mountpoint=mountpoint)
+
+ # grow back when mounted
+ TARGET_SIZE = Size("100 MiB")
+ an_fs.target_size = TARGET_SIZE
+ self.assertEqual(an_fs.target_size, TARGET_SIZE)
+ self.assertNotEqual(an_fs._size, TARGET_SIZE)
+
+ # should fail, online resize disabled by default
+ with self.assertRaisesRegex(FSError, "Resizing of mounted filesystems is disabled"):
+ an_fs.do_resize()
+
+ # enable online resize
+ flags.allow_online_fs_resize = True
+ an_fs.do_resize()
+ flags.allow_online_fs_resize = False
+ self._test_sizes(an_fs)
+ self.assertEqual(an_fs.system_mountpoint, mountpoint)
+
+ an_fs.unmount()
+
class FATFSTestCase(fstesting.FSAsRoot):
_fs_class = fs.FATFS
--
2.40.1

@ -0,0 +1,382 @@
From d06c45db59d0e917dbab4c283f2f04c8f9206a6e Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Mon, 6 Mar 2023 10:51:42 +0100
Subject: [PATCH 1/5] Allow changing iSCSI initiator name after setting it
Resolves: rhbz#2083139
---
blivet/iscsi.py | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/blivet/iscsi.py b/blivet/iscsi.py
index 86451db3..0d063f2a 100644
--- a/blivet/iscsi.py
+++ b/blivet/iscsi.py
@@ -212,14 +212,23 @@ class iSCSI(object):
@initiator.setter
@storaged_iscsi_required(critical=True, eval_mode=util.EvalMode.onetime)
def initiator(self, val):
- if self.initiator_set and val != self._initiator:
- raise ValueError(_("Unable to change iSCSI initiator name once set"))
if len(val) == 0:
raise ValueError(_("Must provide an iSCSI initiator name"))
+ active = self._get_active_sessions()
+ if active:
+ raise errors.ISCSIError(_("Cannot change initiator name with an active session"))
+
log.info("Setting up iSCSI initiator name %s", self.initiator)
args = GLib.Variant("(sa{sv})", (val, None))
self._call_initiator_method("SetInitiatorName", args)
+
+ if self.initiator_set and val != self._initiator:
+ log.info("Restarting iscsid after initiator name change")
+ rc = util.run_program(["systemctl", "restart", "iscsid"])
+ if rc != 0:
+ raise errors.ISCSIError(_("Failed to restart iscsid after initiator name change"))
+
self._initiator = val
def active_nodes(self, target=None):
--
2.40.1
From b71991d65c270c023364b03c499b4bf3e245fbd0 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Mon, 6 Mar 2023 15:10:28 +0100
Subject: [PATCH 2/5] Add a basic test case for the iscsi module
Related: rhbz#2083139
---
tests/storage_tests/__init__.py | 2 +
tests/storage_tests/iscsi_test.py | 157 +++++++++++++++++++++++++++++
3 files changed, 162 insertions(+)
create mode 100644 tests/storage_tests/iscsi_test.py
diff --git a/tests/storage_tests/__init__.py b/tests/storage_tests/__init__.py
index 3b2a6cc4..e69fcc34 100644
--- a/tests/storage_tests/__init__.py
+++ b/tests/storage_tests/__init__.py
@@ -3,3 +3,5 @@ from .formats_test import *
from .partitioning_test import *
from .unsupported_disklabel_test import *
+
+from .iscsi_test import *
diff --git a/tests/storage_tests/iscsi_test.py b/tests/storage_tests/iscsi_test.py
new file mode 100644
index 00000000..00cc7c36
--- /dev/null
+++ b/tests/storage_tests/iscsi_test.py
@@ -0,0 +1,157 @@
+import glob
+import os
+import re
+import shutil
+import subprocess
+import unittest
+
+from contextlib import contextmanager
+
+from .storagetestcase import create_sparse_tempfile
+
+
+def read_file(filename, mode="r"):
+ with open(filename, mode) as f:
+ content = f.read()
+ return content
+
+
+@contextmanager
+def udev_settle():
+ try:
+ yield
+ finally:
+ os.system("udevadm settle")
+
+
+def _delete_backstore(name):
+ status = subprocess.call(["targetcli", "/backstores/fileio/ delete %s" % name],
+ stdout=subprocess.DEVNULL)
+ if status != 0:
+ raise RuntimeError("Failed to delete the '%s' fileio backstore" % name)
+
+
+def delete_iscsi_target(iqn, backstore=None):
+ status = subprocess.call(["targetcli", "/iscsi delete %s" % iqn],
+ stdout=subprocess.DEVNULL)
+ if status != 0:
+ raise RuntimeError("Failed to delete the '%s' iscsi device" % iqn)
+
+ if backstore is not None:
+ _delete_backstore(backstore)
+
+
+def create_iscsi_target(fpath, initiator_name=None):
+ """
+ Creates a new iSCSI target (using targetcli) on top of the
+ :param:`fpath` backing file.
+
+ :param str fpath: path of the backing file
+ :returns: iSCSI IQN, backstore name
+ :rtype: tuple of str
+
+ """
+
+ # "register" the backing file as a fileio backstore
+ store_name = os.path.basename(fpath)
+ status = subprocess.call(["targetcli", "/backstores/fileio/ create %s %s" % (store_name, fpath)], stdout=subprocess.DEVNULL)
+ if status != 0:
+ raise RuntimeError("Failed to register '%s' as a fileio backstore" % fpath)
+
+ out = subprocess.check_output(["targetcli", "/backstores/fileio/%s info" % store_name])
+ out = out.decode("utf-8")
+ store_wwn = None
+ for line in out.splitlines():
+ if line.startswith("wwn: "):
+ store_wwn = line[5:]
+ if store_wwn is None:
+ raise RuntimeError("Failed to determine '%s' backstore's wwn" % store_name)
+
+ # create a new iscsi device
+ out = subprocess.check_output(["targetcli", "/iscsi create"])
+ out = out.decode("utf-8")
+ match = re.match(r'Created target (.*).', out)
+ if match:
+ iqn = match.groups()[0]
+ else:
+ _delete_backstore(store_name)
+ raise RuntimeError("Failed to create a new iscsi target")
+
+ if initiator_name:
+ status = subprocess.call(["targetcli", "/iscsi/%s/tpg1/acls create %s" % (iqn, initiator_name)], stdout=subprocess.DEVNULL)
+ if status != 0:
+ delete_iscsi_target(iqn, store_name)
+ raise RuntimeError("Failed to set ACLs for '%s'" % iqn)
+
+ with udev_settle():
+ status = subprocess.call(["targetcli", "/iscsi/%s/tpg1/luns create /backstores/fileio/%s" % (iqn, store_name)], stdout=subprocess.DEVNULL)
+ if status != 0:
+ delete_iscsi_target(iqn, store_name)
+ raise RuntimeError("Failed to create a new LUN for '%s' using '%s'" % (iqn, store_name))
+
+ status = subprocess.call(["targetcli", "/iscsi/%s/tpg1 set attribute generate_node_acls=1" % iqn], stdout=subprocess.DEVNULL)
+ if status != 0:
+ raise RuntimeError("Failed to set ACLs for '%s'" % iqn)
+
+ return iqn, store_name
+
+
+@unittest.skipUnless(os.geteuid() == 0, "requires root privileges")
+@unittest.skipUnless(os.environ.get("JENKINS_HOME"), "jenkins only test")
+@unittest.skipUnless(shutil.which("iscsiadm"), "iscsiadm not available")
+class ISCSITestCase(unittest.TestCase):
+
+ _disk_size = 512 * 1024**2
+ initiator = 'iqn.1994-05.com.redhat:iscsi-test'
+
+ def setUp(self):
+ self.addCleanup(self._clean_up)
+
+ self._dev_file = None
+ self.dev = None
+
+ self._dev_file = create_sparse_tempfile("blivet_test", self._disk_size)
+ try:
+ self.dev, self.backstore = create_iscsi_target(self._dev_file, self.initiator)
+ except RuntimeError as e:
+ raise RuntimeError("Failed to setup targetcli device for testing: %s" % e)
+
+ def _force_logout(self):
+ subprocess.call(["iscsiadm", "--mode", "node", "--logout", "--name", self.dev], stdout=subprocess.DEVNULL)
+
+ def _clean_up(self):
+ self._force_logout()
+ delete_iscsi_target(self.dev, self.backstore)
+ os.unlink(self._dev_file)
+
+ def test_discover_login(self):
+ from blivet.iscsi import iscsi, has_iscsi
+
+ if not has_iscsi():
+ self.skipTest("iSCSI not available, skipping")
+
+ iscsi.initiator = self.initiator
+ nodes = iscsi.discover("127.0.0.1")
+ self.assertTrue(nodes)
+
+ if len(nodes) > 1:
+ self.skipTest("Discovered more than one iSCSI target on localhost, skipping")
+
+ self.assertEqual(nodes[0].address, "127.0.0.1")
+ self.assertEqual(nodes[0].port, 3260)
+ self.assertEqual(nodes[0].name, self.dev)
+
+ # change the initiator name
+ iscsi.initiator = self.initiator + "_1"
+ self.assertEqual(iscsi.initiator, self.initiator + "_1")
+
+ # try to login
+ ret, err = iscsi.log_into_node(nodes[0])
+ self.assertTrue(ret, "Login failed: %s" % err)
+
+ # check the session for initiator name
+ sessions = glob.glob("/sys/class/iscsi_session/*/")
+ self.assertTrue(sessions)
+ self.assertEqual(len(sessions), 1)
+ initiator = read_file(sessions[0] + "initiatorname").strip()
+ self.assertEqual(initiator, iscsi.initiator)
--
2.40.1
From 65e8150a7404e37dd2740841a88e7f2565836406 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Mon, 6 Mar 2023 15:14:40 +0100
Subject: [PATCH 3/5] tests: Use blivet-specific prefix for targetcli backing
files
The code is originally from libblockdev hence the "bd" prefix, we
should use a different prefix for blivet to be able to identify
which test suite failed to clean the files.
Related: rhbz#2083139
---
tests/storage_tests/storagetestcase.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/storage_tests/storagetestcase.py b/tests/storage_tests/storagetestcase.py
index 35d57ce9..9f859977 100644
--- a/tests/storage_tests/storagetestcase.py
+++ b/tests/storage_tests/storagetestcase.py
@@ -39,7 +39,7 @@ def create_sparse_tempfile(name, size):
:param size: the file size (in bytes)
:returns: the path to the newly created file
"""
- (fd, path) = tempfile.mkstemp(prefix="bd.", suffix="-%s" % name)
+ (fd, path) = tempfile.mkstemp(prefix="blivet.", suffix="-%s" % name)
os.close(fd)
create_sparse_file(path, size)
return path
--
2.40.1
From 41278ef1b3f949303fd30fff2ccdde75f713c9f8 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 19 Jul 2023 13:57:39 +0200
Subject: [PATCH 4/5] iscsi: Save firmware initiator name to
/etc/iscsi/initiatorname.iscsi
Resolves: rhbz#2084043
---
blivet/iscsi.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/blivet/iscsi.py b/blivet/iscsi.py
index 0d063f2a..8080a671 100644
--- a/blivet/iscsi.py
+++ b/blivet/iscsi.py
@@ -160,6 +160,11 @@ class iSCSI(object):
self._initiator = initiatorname
except Exception as e: # pylint: disable=broad-except
log.info("failed to get initiator name from iscsi firmware: %s", str(e))
+ else:
+ # write the firmware initiator to /etc/iscsi/initiatorname.iscsi
+ log.info("Setting up firmware iSCSI initiator name %s", self.initiator)
+ args = GLib.Variant("(sa{sv})", (initiatorname, None))
+ self._call_initiator_method("SetInitiatorName", args)
# So that users can write iscsi() to get the singleton instance
def __call__(self):
--
2.40.1
From fce8b73965d968aab546bc7e0ecb65d1995da46f Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 19 Jul 2023 10:38:45 +0200
Subject: [PATCH 5/5] tests: Improve iscsi_test.ISCSITestCase
Changed how we create the initiator name ACLs based on RTT test
case for rhbz#2084043 and also improved the test case itself.
Related: rhbz#2083139
---
tests/storage_tests/iscsi_test.py | 36 +++++++++++++++++++++----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/tests/storage_tests/iscsi_test.py b/tests/storage_tests/iscsi_test.py
index 00cc7c36..6cc83a59 100644
--- a/tests/storage_tests/iscsi_test.py
+++ b/tests/storage_tests/iscsi_test.py
@@ -77,21 +77,17 @@ def create_iscsi_target(fpath, initiator_name=None):
_delete_backstore(store_name)
raise RuntimeError("Failed to create a new iscsi target")
- if initiator_name:
- status = subprocess.call(["targetcli", "/iscsi/%s/tpg1/acls create %s" % (iqn, initiator_name)], stdout=subprocess.DEVNULL)
- if status != 0:
- delete_iscsi_target(iqn, store_name)
- raise RuntimeError("Failed to set ACLs for '%s'" % iqn)
-
with udev_settle():
status = subprocess.call(["targetcli", "/iscsi/%s/tpg1/luns create /backstores/fileio/%s" % (iqn, store_name)], stdout=subprocess.DEVNULL)
if status != 0:
delete_iscsi_target(iqn, store_name)
raise RuntimeError("Failed to create a new LUN for '%s' using '%s'" % (iqn, store_name))
- status = subprocess.call(["targetcli", "/iscsi/%s/tpg1 set attribute generate_node_acls=1" % iqn], stdout=subprocess.DEVNULL)
- if status != 0:
- raise RuntimeError("Failed to set ACLs for '%s'" % iqn)
+ if initiator_name:
+ status = subprocess.call(["targetcli", "/iscsi/%s/tpg1/acls create %s" % (iqn, initiator_name)], stdout=subprocess.DEVNULL)
+ if status != 0:
+ delete_iscsi_target(iqn, store_name)
+ raise RuntimeError("Failed to set ACLs for '%s'" % iqn)
return iqn, store_name
@@ -130,6 +126,7 @@ class ISCSITestCase(unittest.TestCase):
if not has_iscsi():
self.skipTest("iSCSI not available, skipping")
+ # initially set the initiator to the correct/allowed one
iscsi.initiator = self.initiator
nodes = iscsi.discover("127.0.0.1")
self.assertTrue(nodes)
@@ -141,11 +138,28 @@ class ISCSITestCase(unittest.TestCase):
self.assertEqual(nodes[0].port, 3260)
self.assertEqual(nodes[0].name, self.dev)
- # change the initiator name
+ # change the initiator name to a wrong one
iscsi.initiator = self.initiator + "_1"
self.assertEqual(iscsi.initiator, self.initiator + "_1")
- # try to login
+ # check the change made it to /etc/iscsi/initiatorname.iscsi
+ initiator_file = read_file("/etc/iscsi/initiatorname.iscsi").strip()
+ self.assertEqual(initiator_file, "InitiatorName=%s" % self.initiator + "_1")
+
+ # try to login (should fail)
+ ret, err = iscsi.log_into_node(nodes[0])
+ self.assertFalse(ret)
+ self.assertIn("authorization failure", err)
+
+ # change the initiator name back to the correct one
+ iscsi.initiator = self.initiator
+ self.assertEqual(iscsi.initiator, self.initiator)
+
+ # check the change made it to /etc/iscsi/initiatorname.iscsi
+ initiator_file = read_file("/etc/iscsi/initiatorname.iscsi").strip()
+ self.assertEqual(initiator_file, "InitiatorName=%s" % self.initiator)
+
+ # try to login (should work now)
ret, err = iscsi.log_into_node(nodes[0])
self.assertTrue(ret, "Login failed: %s" % err)
--
2.40.1

@ -0,0 +1,206 @@
From faef0408d2f7c61aade6d187389c61e64f9f373b Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 20 Apr 2023 12:35:30 +0200
Subject: [PATCH] Add support for creating shared LVM setups
This feature is requested by GFS2 for the storage role. This adds
support for creating shared VGs and activating LVs in shared mode.
Resolves: RHEL-14021
---
blivet/devices/lvm.py | 44 +++++++++++++++++++----
blivet/tasks/availability.py | 9 +++++
tests/unit_tests/devices_test/lvm_test.py | 25 +++++++++++++
3 files changed, 72 insertions(+), 6 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index ca45c4b5..068c5368 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -97,7 +97,8 @@ class LVMVolumeGroupDevice(ContainerDevice):
def __init__(self, name, parents=None, size=None, free=None,
pe_size=None, pe_count=None, pe_free=None, pv_count=None,
- uuid=None, exists=False, sysfs_path='', exported=False):
+ uuid=None, exists=False, sysfs_path='', exported=False,
+ shared=False):
"""
:param name: the device name (generally a device node's basename)
:type name: str
@@ -124,6 +125,11 @@ class LVMVolumeGroupDevice(ContainerDevice):
:type pv_count: int
:keyword uuid: the VG UUID
:type uuid: str
+
+ For non-existing VGs only:
+
+ :keyword shared: whether to create this VG as shared
+ :type shared: bool
"""
# These attributes are used by _add_parent, so they must be initialized
# prior to instantiating the superclass.
@@ -137,6 +143,7 @@ class LVMVolumeGroupDevice(ContainerDevice):
self.pe_count = util.numeric_type(pe_count)
self.pe_free = util.numeric_type(pe_free)
self.exported = exported
+ self._shared = shared
# TODO: validate pe_size if given
if not self.pe_size:
@@ -254,7 +261,19 @@ class LVMVolumeGroupDevice(ContainerDevice):
""" Create the device. """
log_method_call(self, self.name, status=self.status)
pv_list = [pv.path for pv in self.parents]
- blockdev.lvm.vgcreate(self.name, pv_list, self.pe_size)
+ extra = dict()
+ if self._shared:
+ extra["shared"] = ""
+ blockdev.lvm.vgcreate(self.name, pv_list, self.pe_size, **extra)
+
+ if self._shared:
+ if availability.BLOCKDEV_LVM_PLUGIN_SHARED.available:
+ try:
+ blockdev.lvm.vglock_start(self.name)
+ except blockdev.LVMError as err:
+ raise errors.LVMError(err)
+ else:
+ raise errors.LVMError("Shared LVM is not fully supported: %s" % ",".join(availability.BLOCKDEV_LVM_PLUGIN_SHARED.availability_errors))
def _post_create(self):
self._complete = True
@@ -661,7 +680,7 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
fmt=None, exists=False, sysfs_path='', grow=None, maxsize=None,
percent=None, cache_request=None, pvs=None, from_lvs=None,
- stripe_size=0):
+ stripe_size=0, shared=False):
if not exists:
if seg_type not in [None, "linear", "thin", "thin-pool", "cache", "vdo-pool", "vdo", "cache-pool"] + lvm.raid_seg_types:
@@ -690,6 +709,7 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
self.seg_type = seg_type or "linear"
self._raid_level = None
self.ignore_skip_activation = 0
+ self._shared = shared
self.req_grow = None
self.req_max_size = Size(0)
@@ -2306,7 +2326,8 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
parent_lv=None, int_type=None, origin=None, vorigin=False,
metadata_size=None, chunk_size=None, profile=None, from_lvs=None,
compression=False, deduplication=False, index_memory=0,
- write_policy=None, cache_mode=None, attach_to=None, stripe_size=0):
+ write_policy=None, cache_mode=None, attach_to=None, stripe_size=0,
+ shared=False):
"""
:param name: the device name (generally a device node's basename)
:type name: str
@@ -2337,6 +2358,8 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
:type cache_request: :class:`~.devices.lvm.LVMCacheRequest`
:keyword pvs: list of PVs to allocate extents from (size could be specified for each PV)
:type pvs: list of :class:`~.devices.StorageDevice` or :class:`LVPVSpec` objects (tuples)
+ :keyword shared: whether to activate the newly create LV in shared mode
+ :type shared: bool
For internal LVs only:
@@ -2412,7 +2435,7 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
LVMLogicalVolumeBase.__init__(self, name, parents, size, uuid, seg_type,
fmt, exists, sysfs_path, grow, maxsize,
percent, cache_request, pvs, from_lvs,
- stripe_size)
+ stripe_size, shared)
LVMVDOPoolMixin.__init__(self, compression, deduplication, index_memory,
write_policy)
LVMVDOLogicalVolumeMixin.__init__(self)
@@ -2634,7 +2657,13 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
log_method_call(self, self.name, orig=orig, status=self.status,
controllable=self.controllable)
ignore_skip_activation = self.is_snapshot_lv or self.ignore_skip_activation > 0
- blockdev.lvm.lvactivate(self.vg.name, self._name, ignore_skip=ignore_skip_activation)
+ if self._shared:
+ if availability.BLOCKDEV_LVM_PLUGIN_SHARED.available:
+ blockdev.lvm.lvactivate(self.vg.name, self._name, ignore_skip=ignore_skip_activation, shared=True)
+ else:
+ raise errors.LVMError("Shared LVM is not fully supported: %s" % ",".join(availability.BLOCKDEV_LVM_PLUGIN_SHARED.availability_errors))
+ else:
+ blockdev.lvm.lvactivate(self.vg.name, self._name, ignore_skip=ignore_skip_activation)
@type_specific
def _pre_create(self):
@@ -2672,6 +2701,9 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
if self._stripe_size:
extra["stripesize"] = str(int(self._stripe_size.convert_to("KiB")))
+ if self._shared:
+ extra["activate"] = "sy"
+
blockdev.lvm.lvcreate(self.vg.name, self._name, self.size,
type=self.seg_type, pv_list=pvs, **extra)
else:
diff --git a/blivet/tasks/availability.py b/blivet/tasks/availability.py
index bba1ba84..85945c77 100644
--- a/blivet/tasks/availability.py
+++ b/blivet/tasks/availability.py
@@ -435,6 +435,14 @@ if hasattr(blockdev.LVMTech, "VDO"):
else:
BLOCKDEV_LVM_TECH_VDO = _UnavailableMethod(error_msg="Installed version of libblockdev doesn't support LVM VDO technology")
+if hasattr(blockdev.LVMTech, "SHARED"):
+ BLOCKDEV_LVM_SHARED = BlockDevTechInfo(plugin_name="lvm",
+ check_fn=blockdev.lvm_is_tech_avail,
+ technologies={blockdev.LVMTech.SHARED: blockdev.LVMTechMode.MODIFY}) # pylint: disable=no-member
+ BLOCKDEV_LVM_TECH_SHARED = BlockDevMethod(BLOCKDEV_LVM_SHARED)
+else:
+ BLOCKDEV_LVM_TECH_SHARED = _UnavailableMethod(error_msg="Installed version of libblockdev doesn't support shared LVM technology")
+
# libblockdev mdraid plugin required technologies and modes
BLOCKDEV_MD_ALL_MODES = (blockdev.MDTechMode.CREATE |
blockdev.MDTechMode.DELETE |
@@ -476,6 +484,7 @@ BLOCKDEV_DM_PLUGIN_RAID = blockdev_plugin("libblockdev dm plugin (raid technolog
BLOCKDEV_LOOP_PLUGIN = blockdev_plugin("libblockdev loop plugin", BLOCKDEV_LOOP_TECH)
BLOCKDEV_LVM_PLUGIN = blockdev_plugin("libblockdev lvm plugin", BLOCKDEV_LVM_TECH)
BLOCKDEV_LVM_PLUGIN_VDO = blockdev_plugin("libblockdev lvm plugin (vdo technology)", BLOCKDEV_LVM_TECH_VDO)
+BLOCKDEV_LVM_PLUGIN_SHARED = blockdev_plugin("libblockdev lvm plugin (shared LVM technology)", BLOCKDEV_LVM_TECH_SHARED)
BLOCKDEV_MDRAID_PLUGIN = blockdev_plugin("libblockdev mdraid plugin", BLOCKDEV_MD_TECH)
BLOCKDEV_MPATH_PLUGIN = blockdev_plugin("libblockdev mpath plugin", BLOCKDEV_MPATH_TECH)
BLOCKDEV_SWAP_PLUGIN = blockdev_plugin("libblockdev swap plugin", BLOCKDEV_SWAP_TECH)
diff --git a/tests/unit_tests/devices_test/lvm_test.py b/tests/unit_tests/devices_test/lvm_test.py
index d7b55224..e645309f 100644
--- a/tests/unit_tests/devices_test/lvm_test.py
+++ b/tests/unit_tests/devices_test/lvm_test.py
@@ -476,6 +476,31 @@ class LVMDeviceTest(unittest.TestCase):
lv.setup()
lvm.lvactivate.assert_called_with(vg.name, lv.lvname, ignore_skip=False)
+ @patch("blivet.tasks.availability.BLOCKDEV_LVM_PLUGIN_SHARED",
+ new=blivet.tasks.availability.ExternalResource(blivet.tasks.availability.AvailableMethod, ""))
+ def test_lv_activate_shared(self):
+ pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"),
+ size=Size("1 GiB"), exists=True)
+ vg = LVMVolumeGroupDevice("testvg", parents=[pv], exists=True)
+ lv = LVMLogicalVolumeDevice("data_lv", parents=[vg], size=Size("500 MiB"), exists=True, shared=True)
+
+ with patch("blivet.devices.lvm.blockdev.lvm") as lvm:
+ with patch.object(lv, "_pre_setup"):
+ lv.setup()
+ lvm.lvactivate.assert_called_with(vg.name, lv.lvname, ignore_skip=False, shared=True)
+
+ @patch("blivet.tasks.availability.BLOCKDEV_LVM_PLUGIN_SHARED",
+ new=blivet.tasks.availability.ExternalResource(blivet.tasks.availability.AvailableMethod, ""))
+ def test_vg_create_shared(self):
+ pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"),
+ size=Size("1 GiB"), exists=True)
+ vg = LVMVolumeGroupDevice("testvg", parents=[pv], shared=True)
+
+ with patch("blivet.devices.lvm.blockdev.lvm") as lvm:
+ vg._create()
+ lvm.vgcreate.assert_called_with(vg.name, [pv.path], Size("4 MiB"), shared="")
+ lvm.vglock_start.assert_called_with(vg.name)
+
def test_vg_is_empty(self):
pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"),
size=Size("1024 MiB"))
--
2.41.0

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save