From: Vratislav Podzimek <vpodzime(a)redhat.com>
We need that information in order to do checks when adding more LVs and users
need this information to decide about where to place their LVs.
Let's not bother with existing LVs allocations for now. We can just ignore those
and only care about newly added (non-existing) LVs which we need to place
somewhere.
---
blivet/devices/lvm.py | 7 ++++---
blivet/populator.py | 2 ++
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index 4cc39e2..9a75635 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -312,7 +312,7 @@ def _add_log_vol(self, lv):
# PV space accounting
pv_sizes = lv.pv_space_used
- if pv_sizes:
+ if not lv.exists and pv_sizes:
for size_spec in pv_sizes:
# check that we have enough space in the PVs for the LV and
# account for it
@@ -335,7 +335,7 @@ def _remove_log_vol(self, lv):
# PV space accounting
pv_sizes = lv.pv_space_used
- if pv_sizes:
+ if not lv.exists and pv_sizes:
for size_spec in pv_sizes:
size_spec.pv.format.free += size_spec.size
@@ -348,7 +348,8 @@ def _add_parent(self, member):
# this PV object is just being added so it has all its space available
# (adding LVs will eat that space later)
- member.format.free = self._get_pv_usable_space(member)
+ if not member.format.exists:
+ member.format.free = self._get_pv_usable_space(member)
def _remove_parent(self, member):
# XXX It would be nice to raise an exception if removing this member
diff --git a/blivet/populator.py b/blivet/populator.py
index 7d28a10..0adc0e6 100644
--- a/blivet/populator.py
+++ b/blivet/populator.py
@@ -1427,6 +1427,8 @@ def handle_udev_device_format(self, info, device):
kwargs["pe_start"] = Size(pv_info.pe_start)
else:
log.warning("PV %s has no pe_start", name)
+ if pv_info.pv_free:
+ kwargs["free"] = Size(pv_info.pv_free)
elif format_type == "vfat":
# efi magic
if isinstance(device, PartitionDevice) and device.bootable:
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/509becb32353322d9a20748459a99a…
From: Vratislav Podzimek <vpodzime(a)redhat.com>
Right now, we only support creation of non-linear LVs under some conditions that
allow us avoid trying to do too crazy things. Let's make sure these conditions
are met when a new LVMLogicalVolumeDevice object is being created.
---
blivet/devices/lvm.py | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index a836ed4..4cc39e2 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -595,6 +595,17 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
"""
+ if not exists:
+ if seg_type and seg_type != "linear" and not pvs:
+ 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 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 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.
self.snapshots = []
@@ -1612,7 +1623,7 @@ def __init__(self, name, parents=None, size=None, uuid=None,
:type sysfs_path: str
:keyword uuid: the device UUID
:type uuid: str
- :keyword seg_type: segment type
+ :keyword seg_type: segment type (only "linear" supported for non-existing ThinPool LVs)
:type seg_type: str
For non-existent pools only:
@@ -1639,6 +1650,9 @@ def __init__(self, name, parents=None, size=None, uuid=None,
not blockdev.lvm.is_valid_thpool_chunk_size(chunksize):
raise ValueError("invalid chunksize value")
+ if not exists and seg_type and seg_type != "linear":
+ raise ValueError("creation of non-linear thin pool LVs is not supported (yet)")
+
super(LVMThinPoolDevice, self).__init__(name, parents=parents,
size=size, uuid=uuid,
fmt=fmt, exists=exists,
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/335d6e68517de5fbbbe0084c112b0a…
From: Vratislav Podzimek <vpodzime(a)redhat.com>
A useful simplification of what we have to check in a few places.
---
blivet/devices/lvm.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index 31fdf31..a836ed4 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -656,7 +656,7 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
self._add_to_parents()
def _assign_pv_space(self):
- if self.seg_type == "linear" or not self._raid_level:
+ if not self.is_raid_lv:
# nothing to do for non-RAID (and thus non-striped) LVs here
return
for spec in self._pv_specs:
@@ -683,6 +683,10 @@ def _add_to_parents(self):
self._parents[0]._add_log_vol(self)
@property
+ def is_raid_lv(self):
+ return self.seg_type != "linear" and self._raid_level
+
+ @property
def copies(self):
image_lvs = [int_lv for int_lv in self._internal_lvs if isinstance(int_lv, LVMImageLogicalVolumeDevice)]
return len(image_lvs) or 1
@@ -695,7 +699,7 @@ def log_size(self):
@property
def metadata_size(self):
if self._metadata_size:
- if self.seg_type != "linear" and self._raid_level:
+ if self.is_raid_lv:
zero_superblock = lambda x: Size(0)
return self._raid_level.get_space(self._metadata_size, len(self._pv_specs),
superblock_size_func=zero_superblock)
@@ -763,7 +767,7 @@ def max_size(self):
def data_vg_space_used(self):
""" Space occupied by the data part of this LV, not including snapshots """
rounded_size = self.vg.align(self.size, roundup=True)
- if self.seg_type != "linear" and self._raid_level:
+ if self.is_raid_lv:
zero_superblock = lambda x: Size(0)
return self._raid_level.get_space(rounded_size, len(self._pv_specs),
superblock_size_func=zero_superblock)
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/85b0548c0e6ac4b7c3c9f06fbab7c0…
From: Vratislav Podzimek <vpodzime(a)redhat.com>
Useful for some manual testing as well as for people wondering how to do
something like that.
---
examples/lvm_non_linear.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
create mode 100644 examples/lvm_non_linear.py
diff --git a/examples/lvm_non_linear.py b/examples/lvm_non_linear.py
new file mode 100644
index 0000000..c75b10e
--- /dev/null
+++ b/examples/lvm_non_linear.py
@@ -0,0 +1,64 @@
+import os
+
+from examples.common import print_devices
+
+import blivet
+from blivet.size import Size
+from blivet.util import set_up_logging, create_sparse_tempfile
+
+set_up_logging()
+b = blivet.Blivet() # create an instance of Blivet (don't add system devices)
+
+# create a disk image file on which to create new devices
+disk1_file = create_sparse_tempfile("disk1", Size("100GiB"))
+b.config.disk_images["disk1"] = disk1_file
+disk2_file = create_sparse_tempfile("disk2", Size("100GiB"))
+b.config.disk_images["disk2"] = disk2_file
+
+b.reset()
+
+try:
+ disk1 = b.devicetree.get_device_by_name("disk1")
+ disk2 = b.devicetree.get_device_by_name("disk2")
+
+ b.initialize_disk(disk1)
+ b.initialize_disk(disk2)
+
+ pv = b.new_partition(size=Size("50GiB"), fmt_type="lvmpv", parents=[disk1])
+ b.create_device(pv)
+ pv2 = b.new_partition(size=Size("50GiB"), fmt_type="lvmpv", parents=[disk2])
+ b.create_device(pv2)
+
+ # allocate the partitions (decide where and on which disks they'll reside)
+ blivet.partitioning.do_partitioning(b)
+
+ vg = b.new_vg(parents=[pv, pv2])
+ b.create_device(vg)
+
+ # new lv with base size 5GiB and unbounded growth and an ext4 filesystem
+ dev = b.new_lv(fmt_type="ext4", size=Size("5GiB"), grow=True,
+ parents=[vg], name="unbounded")
+ b.create_device(dev)
+
+ # new lv with base size 5GiB and growth up to 15GiB and an ext4 filesystem
+ dev = b.new_lv(fmt_type="ext4", size=Size("5GiB"), grow=True,
+ maxsize=Size("15GiB"), parents=[vg], name="bounded")
+ b.create_device(dev)
+
+ # new lv with a fixed size of 2GiB formatted as swap space
+ dev = b.new_lv(fmt_type="swap", size=Size("2GiB"), parents=[vg], seg_type="raid1", pvs=[pv, pv2])
+ b.create_device(dev)
+
+ # allocate the growable lvs
+ blivet.partitioning.grow_lvm(b)
+ print_devices(b)
+
+ # write the new partitions to disk and format them as specified
+ b.do_it()
+ print_devices(b)
+ input("Check the state and hit ENTER to trigger cleanup")
+finally:
+ b.devicetree.teardown_disk_images()
+ os.unlink(disk1_file)
+ os.unlink(disk2_file)
+
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/bfc4a88011e6715520516226febda1…
From: Vratislav Podzimek <vpodzime(a)redhat.com>
non-linear LVs require space on specific PVs and thus have stronger restrictions
than linear LVs which can be allocated from anywhere.
---
blivet/deviceaction.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py
index 6c42fe9..879de12 100644
--- a/blivet/deviceaction.py
+++ b/blivet/deviceaction.py
@@ -348,6 +348,10 @@ def requires(self, action):
# is not taken by non-cached LVs
if not self.device.cached and action.device.cached:
rc = True
+ # create non-linear LVs before linear LVs because the latter ones
+ # can be allocated anywhere
+ elif self.device.seg_type == "linear" and action.device.seg_type != "linear":
+ rc = True
elif (action.is_add and action.container == self.container):
rc = True
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/fc2feb452f65760a6b1e1a5cfed0e3…
From: Vratislav Podzimek <vpodzime(a)redhat.com>
LVM creates small internal LVs for RAID LVs that hold the necessary metadata. We
need to account for that in order to be able to do calculations of PV/VG free
space etc. This requires us to do some of the calculations in a more granular
manner -- separating data and metadata parts.
---
blivet/devices/lvm.py | 37 +++++++++++++++++++++++++++++++------
blivet/partitioning.py | 11 ++++++-----
2 files changed, 37 insertions(+), 11 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index 2e3b8d8..31fdf31 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -620,7 +620,11 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
self.req_size = self._size
self.req_percent = util.numeric_type(percent)
- self._metadata_size = Size(0)
+ if not self.exists and self.seg_type.startswith("raid"):
+ # RAID LVs create one extent big internal metadata LVs
+ self._metadata_size = self.vg.pe_size
+ else:
+ self._metadata_size = Size(0)
self._internal_lvs = []
self._cache = None
@@ -653,10 +657,10 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
def _assign_pv_space(self):
if self.seg_type == "linear" or not self._raid_level:
- # nothing to do for non-RAID (including striped) LVs here
+ # nothing to do for non-RAID (and thus non-striped) LVs here
return
for spec in self._pv_specs:
- spec.size = self._raid_level.get_base_member_size(self.size, len(self._pv_specs))
+ spec.size = self._raid_level.get_base_member_size(self.size + self._metadata_size, len(self._pv_specs))
def _check_parents(self):
"""Check that this device has parents as expected"""
@@ -691,7 +695,12 @@ def log_size(self):
@property
def metadata_size(self):
if self._metadata_size:
- return self._metadata_size
+ if self.seg_type != "linear" and self._raid_level:
+ zero_superblock = lambda x: Size(0)
+ return self._raid_level.get_space(self._metadata_size, len(self._pv_specs),
+ superblock_size_func=zero_superblock)
+ else:
+ return self._metadata_size
elif self.cached:
return self.cache.md_size
@@ -751,14 +760,30 @@ def max_size(self):
return min(max_lv, max_format) if max_format else max_lv
@property
+ def data_vg_space_used(self):
+ """ Space occupied by the data part of this LV, not including snapshots """
+ rounded_size = self.vg.align(self.size, roundup=True)
+ if self.seg_type != "linear" and self._raid_level:
+ zero_superblock = lambda x: Size(0)
+ return self._raid_level.get_space(rounded_size, len(self._pv_specs),
+ superblock_size_func=zero_superblock)
+ else:
+ return rounded_size
+
+ @property
+ def metadata_vg_space_used(self):
+ """ Space occupied by the metadata part of this LV, not including snapshots """
+ return self.log_size + self.metadata_size
+
+ @property
def vg_space_used(self):
""" Space occupied by this LV, not including snapshots. """
if self.cached:
cache_size = self.cache.size
else:
cache_size = Size(0)
- return (self.vg.align(self.size, roundup=True) * self.copies
- + self.log_size + self.metadata_size + cache_size)
+
+ return self.data_vg_space_used + self.metadata_vg_space_used + cache_size
@property
def pv_space_used(self):
diff --git a/blivet/partitioning.py b/blivet/partitioning.py
index c5c1a20..dfbd218 100644
--- a/blivet/partitioning.py
+++ b/blivet/partitioning.py
@@ -1039,7 +1039,7 @@ def __init__(self, lv):
# Round up to nearest pe. For growable requests this will mean that
# first growth is to fill the remainder of any unused extent.
- self.base = int(lv.vg.align(lv.req_size, roundup=True) // lv.vg.pe_size)
+ self.base = int(lv.data_vg_space_used // lv.vg.pe_size)
if lv.req_grow:
limits = [int(l // lv.vg.pe_size) for l in
@@ -1055,11 +1055,12 @@ def __init__(self, lv):
@property
def reserve_request(self):
+ lv = self.device
reserve = super(LVRequest, self).reserve_request
- if self.device.cached:
- total_cache_size = self.device.cache.size + self.device.cache.md_size
- reserve += int(self.device.vg.align(total_cache_size, roundup=True) / self.device.vg.pe_size)
-
+ if lv.cached:
+ reserve += int(lv.vg.align(lv.cache.size, roundup=True) / lv.vg.pe_size)
+ if lv.metadata_size:
+ reserve += int(lv.vg.align(lv.metadata_vg_space_used, roundup=True) / lv.vg.pe_size)
return reserve
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/62f4762b072b5116b894c799dd82a1…
From: Vratislav Podzimek <vpodzime(a)redhat.com>
We already support various segment types for LVs to some extent. However, we
need to get a better picture of how much space on such LVs' PVs is required. We
already have a code for that, so let's just use it.
This unfortunately requires the LVPVSpec to have read-write attributes/fields
and thus it cannot be a namedtuple. We should probably come up with some
"read-write namedtuple" thing for cases like this in the future.
---
blivet/devices/lvm.py | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index 2242214..2e3b8d8 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -73,9 +73,11 @@ def get_internal_lv_class(lv_attr):
return None
-LVPVSpec = namedtuple("LVPVSpec", ["pv", "size"])
-""" A namedtuple class for specifying how much space on a PV should be allocated for some LV """
-
+class LVPVSpec(object):
+ """ Class for specifying how much space on a PV should be allocated for some LV """
+ def __init__(self, pv, size):
+ self.pv = pv
+ self.size = size
PVFreeInfo = namedtuple("PVFreeInfo", ["pv", "size", "free"])
""" A namedtuple class holding the information about PV's (usable) size and free space """
@@ -602,6 +604,9 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
self.uuid = uuid
self.seg_type = seg_type or "linear"
+ self._raid_level = None
+ if self.seg_type in (level.name for level in lvm.raid_levels):
+ self._raid_level = lvm.raid_levels.raid_level(self.seg_type)
self.req_grow = None
self.req_max_size = Size(0)
@@ -638,12 +643,21 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
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 ValueError(msg)
+ if self._pv_specs:
+ self._assign_pv_space()
# check that we got parents as expected and add this device to them now
# that it is fully-initialized
self._check_parents()
self._add_to_parents()
+ def _assign_pv_space(self):
+ if self.seg_type == "linear" or not self._raid_level:
+ # nothing to do for non-RAID (including striped) LVs here
+ return
+ for spec in self._pv_specs:
+ spec.size = self._raid_level.get_base_member_size(self.size, len(self._pv_specs))
+
def _check_parents(self):
"""Check that this device has parents as expected"""
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/9f224e6fd9e60b50db30afb57db306…
From: Vratislav Podzimek <vpodzime(a)redhat.com>
If we want to support other types of LV than linear we need to know how much
space we have in each PV. For example a 1GiB RAID1 LV requires not only 2GiB
total space in the VG with 2 PVs, but at the same time it requires 1GiB space on
each of the PVs.
---
blivet/devices/lvm.py | 80 +++++++++++++++++++++++++++++++++++++++----------
blivet/formats/lvmpv.py | 22 ++++++++++++++
2 files changed, 87 insertions(+), 15 deletions(-)
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
index de40fd6..2242214 100644
--- a/blivet/devices/lvm.py
+++ b/blivet/devices/lvm.py
@@ -77,6 +77,10 @@ def get_internal_lv_class(lv_attr):
""" A namedtuple class for specifying how much space on a PV should be allocated for some LV """
+PVFreeInfo = namedtuple("PVFreeInfo", ["pv", "size", "free"])
+""" A namedtuple class holding the information about PV's (usable) size and free space """
+
+
class LVMVolumeGroupDevice(ContainerDevice):
""" An LVM Volume Group """
@@ -128,22 +132,22 @@ def __init__(self, name, parents=None, size=None, free=None,
self.pv_count = util.numeric_type(pv_count)
if exists and not pv_count:
self._complete = True
+ self.pe_size = util.numeric_type(pe_size)
+ self.pe_count = util.numeric_type(pe_count)
+ self.pe_free = util.numeric_type(pe_free)
+
+ # TODO: validate pe_size if given
+ if not self.pe_size:
+ self.pe_size = lvm.LVM_PE_SIZE
super(LVMVolumeGroupDevice, self).__init__(name, parents=parents,
uuid=uuid, size=size,
exists=exists, sysfs_path=sysfs_path)
self.free = util.numeric_type(free)
- self.pe_size = util.numeric_type(pe_size)
- self.pe_count = util.numeric_type(pe_count)
- self.pe_free = util.numeric_type(pe_free)
self._reserved_percent = 0
self._reserved_space = Size(0)
- # TODO: validate pe_size if given
- if not self.pe_size:
- self.pe_size = lvm.LVM_PE_SIZE
-
if not self.exists:
self.pv_count = len(self.parents)
@@ -304,6 +308,17 @@ def _add_log_vol(self, lv):
if origin:
origin.snapshots.append(lv)
+ # PV space accounting
+ pv_sizes = lv.pv_space_used
+ if pv_sizes:
+ for size_spec in pv_sizes:
+ # check that we have enough space in the PVs for the LV and
+ # account for it
+ if size_spec.pv.format.free < size_spec.size:
+ msg = "not enough space in the '%s' PV for the '%s' LV's extents" % (size_spec.pv.name, lv.name)
+ raise errors.DeviceError(msg)
+ size_spec.pv.format.free -= size_spec.size
+
def _remove_log_vol(self, lv):
""" Remove an LV from this VG. """
if lv not in self.lvs:
@@ -316,6 +331,12 @@ def _remove_log_vol(self, lv):
if origin:
origin.snapshots.remove(lv)
+ # PV space accounting
+ pv_sizes = lv.pv_space_used
+ if pv_sizes:
+ for size_spec in pv_sizes:
+ size_spec.pv.format.free += size_spec.size
+
def _add_parent(self, member):
super(LVMVolumeGroupDevice, self)._add_parent(member)
@@ -323,6 +344,10 @@ def _add_parent(self, member):
len(self.parents) + 1 == self.pv_count):
self._complete = True
+ # this PV object is just being added so it has all its space available
+ # (adding LVs will eat that space later)
+ member.format.free = self._get_pv_usable_space(member)
+
def _remove_parent(self, member):
# XXX It would be nice to raise an exception if removing this member
# would not leave enough space, but the devicefactory relies on it
@@ -331,6 +356,7 @@ def _remove_parent(self, member):
# Maybe remove_member could be a wrapper with the checks and the
# devicefactory could call the _ versions to bypass the checks.
super(LVMVolumeGroupDevice, self)._remove_parent(member)
+ member.format.free = None
# We can't rely on lvm to tell us about our size, free space, &c
# since we could have modifications queued, unless the VG and all of
@@ -358,6 +384,12 @@ def reserved_space(self):
return self.align(reserved, roundup=True)
+ def _get_pv_usable_space(self, pv):
+ if isinstance(pv, MDRaidArrayDevice):
+ return self.align(pv.size - 2 * pv.format.pe_start)
+ else:
+ return self.align(pv.size - pv.format.pe_start)
+
@property
def lvm_metadata_space(self):
""" The amount of the space LVM metadata cost us in this VG's PVs """
@@ -372,10 +404,7 @@ def lvm_metadata_space(self):
# class once it exists
diff = Size(0)
for pv in self.pvs:
- if isinstance(pv, MDRaidArrayDevice):
- diff += pv.size - self.align(pv.size - 2 * pv.format.pe_start)
- else:
- diff += pv.size - self.align(pv.size - pv.format.pe_start)
+ diff += pv.size - self._get_pv_usable_space(pv)
return diff
@@ -422,6 +451,16 @@ def free_extents(self):
# TODO: just ask lvm if is_modified returns False
return int(self.free_space / self.pe_size)
+ @property
+ def pv_free_info(self):
+ """
+ :returns: information about sizes and free space in this VG's PVs
+ :rtype: list of PVFreeInfo
+
+ """
+ return [PVFreeInfo(pv, self._get_pv_usable_space(pv.size), pv.format.free)
+ for pv in self.pvs]
+
def align(self, size, roundup=False):
""" Align a size to a multiple of physical extent size. """
size = util.numeric_type(size)
@@ -576,10 +615,6 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
self.req_size = self._size
self.req_percent = util.numeric_type(percent)
- # check that we got parents as expected and add this device to them
- self._check_parents()
- self._add_to_parents()
-
self._metadata_size = Size(0)
self._internal_lvs = []
self._cache = None
@@ -589,6 +624,7 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
fast_pvs=cache_request.fast_devs, mode=cache_request.mode)
self._pv_specs = []
+ pvs = pvs or []
for pv_spec in pvs:
if isinstance(pv_spec, LVPVSpec):
self._pv_specs.append(pv_spec)
@@ -603,6 +639,11 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
msg = "invalid destination PV(s) %s for LV %s" % (missing, self.name)
raise ValueError(msg)
+ # check that we got parents as expected and add this device to them now
+ # that it is fully-initialized
+ self._check_parents()
+ self._add_to_parents()
+
def _check_parents(self):
"""Check that this device has parents as expected"""
@@ -705,6 +746,15 @@ def vg_space_used(self):
return (self.vg.align(self.size, roundup=True) * self.copies
+ self.log_size + self.metadata_size + cache_size)
+ @property
+ def pv_space_used(self):
+ """
+ :returns: space occupied by this LV on its VG's PVs (if we have and idea)
+ :rtype: list of LVPVSpec
+
+ """
+ return self._pv_specs
+
def _set_format(self, fmt):
super(LVMLogicalVolumeDevice, self)._set_format(fmt)
for snapshot in (s for s in self.snapshots if not s.exists):
diff --git a/blivet/formats/lvmpv.py b/blivet/formats/lvmpv.py
index 65bab91..f0031ef 100644
--- a/blivet/formats/lvmpv.py
+++ b/blivet/formats/lvmpv.py
@@ -33,6 +33,7 @@
from ..tasks import availability
from ..i18n import N_
from ..size import Size
+from ..errors import PhysicalVolumeError
from . import DeviceFormat, register_device_format
import logging
@@ -66,6 +67,8 @@ def __init__(self, **kwargs):
:type pe_start: :class:`~.size.Size`
:keyword data_alignment: data alignment (for non-existent PVs)
:type data_alignment: :class:`~.size.Size`
+ :keyword free: free space in the PV
+ :type free: :class:`~.size.Size`
.. note::
@@ -81,6 +84,7 @@ def __init__(self, **kwargs):
self.vg_uuid = kwargs.get("vg_uuid")
self.pe_start = kwargs.get("pe_start", lvm.LVM_PE_START)
self.data_alignment = kwargs.get("data_alignment", Size(0))
+ self._free = kwargs.get("free") # None means unknown
self.inconsistent_vg = False
@@ -143,4 +147,22 @@ def status(self):
return (self.exists and self.vg_name and
os.path.isdir("/dev/%s" % self.vg_name))
+ @property
+ def free(self):
+ """ Information about the free space in this PV """
+ if self._free is None:
+ if self.exists:
+ # we don't have any actual value, but the PV exists and is
+ # active, we should try to determine it
+ pv_info = blockdev.lvm.pvinfo(self.device)
+ self._free = Size(pv_info.pv_free)
+ else:
+ raise PhysicalVolumeError("Unknown free space information for the PV '%s'" % self.device)
+
+ return self._free
+
+ @free.setter
+ def free(self, value):
+ self._free = value
+
register_device_format(LVMPhysicalVolume)
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/e668c30c4e534c6d927fafeafac21d…