From ca5fd2a1b64831dadba1b082b6b7f8c3fc03268c Mon Sep 17 00:00:00 2001 From: Alfredo Deza Date: Fri, 13 Jul 2018 08:55:28 -0400 Subject: [PATCH] ceph-volume api.lvm convert and use extents for sizing calculations Signed-off-by: Alfredo Deza (cherry picked from commit 99e034d7eed9508ee016431c040e3fddca9bca69) --- src/ceph-volume/ceph_volume/api/lvm.py | 69 ++++++++++++++++++++------ 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/src/ceph-volume/ceph_volume/api/lvm.py b/src/ceph-volume/ceph_volume/api/lvm.py index cc2c1132d0eb6..7525ba883d0d0 100644 --- a/src/ceph-volume/ceph_volume/api/lvm.py +++ b/src/ceph-volume/ceph_volume/api/lvm.py @@ -7,7 +7,7 @@ import logging import os import uuid from math import floor -from ceph_volume import process +from ceph_volume import process, util from ceph_volume.exceptions import ( MultipleLVsError, MultipleVGsError, MultiplePVsError, SizeAllocationError @@ -113,15 +113,15 @@ def sizing(device_size, parts=None, size=None): percentages = get_percentage(parts) if size: - parts = int(floor(device_size / size)) or 1 + parts = int(device_size / size) or 1 percentages = get_percentage(parts) - sizes = int(floor(device_size / parts)) if parts else int(floor(device_size)) + sizes = device_size / parts if parts else int(floor(device_size)) return { 'parts': parts, 'percentages': percentages, - 'sizes': sizes + 'sizes': int(sizes), } @@ -291,7 +291,7 @@ def get_api_vgs(): To normalize sizing, the units are forced in 'g' which is equivalent to gigabytes, which uses multiples of 1024 (as opposed to 1000) """ - fields = 'vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free' + fields = 'vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free,vg_free_count' stdout, stderr, returncode = process.call( ['vgs', '--noheadings', '--readonly', '--units=g', '--separator=";"', '-o', fields] ) @@ -482,7 +482,7 @@ def remove_lv(path): return True -def create_lv(name, group, size=None, tags=None): +def create_lv(name, group, extents=None, size=None, tags=None): """ Create a Logical Volume in a Volume Group. Command looks like:: @@ -494,6 +494,14 @@ def create_lv(name, group, size=None, tags=None): {"ceph.block_device": "/dev/ceph/osd-1"} """ + if tags is None: + tags = { + "ceph.osd_id": "null", + "ceph.type": "null", + "ceph.cluster_fsid": "null", + "ceph.osd_fsid": "null", + } + # XXX add CEPH_VOLUME_LVM_DEBUG to enable -vvvv on lv operations type_path_tag = { 'journal': 'ceph.journal_device', @@ -511,6 +519,14 @@ def create_lv(name, group, size=None, tags=None): '%s' % size, '-n', name, group ]) + elif extents: + process.run([ + 'lvcreate', + '--yes', + '-l', + '%s' % extents, + '-n', name, group + ]) # create the lv with all the space available, this is needed because the # system call is different for LVM else: @@ -555,6 +571,8 @@ def create_lvs(volume_group, parts=None, size=None, name_prefix='ceph-lv'): :type parts: int :param size: Size (in gigabytes) of LVs to create, e.g. "as many 10gb LVs as possible" :type size: int + :param extents: The number of LVM extents to use to create the LV. Useful if looking to have + accurate LV sizes (LVM rounds sizes otherwise) """ if parts is None and size is None: # fallback to just one part (using 100% of the vg) @@ -569,9 +587,10 @@ def create_lvs(volume_group, parts=None, size=None, name_prefix='ceph-lv'): sizing = volume_group.sizing(parts=parts, size=size) for part in range(0, sizing['parts']): size = sizing['sizes'] + extents = sizing['extents'] lv_name = '%s-%s' % (name_prefix, uuid.uuid4()) lvs.append( - create_lv(lv_name, volume_group.name, size="%sg" % size, tags=tags) + create_lv(lv_name, volume_group.name, extents=extents, tags=tags) ) return lvs @@ -916,14 +935,7 @@ class VolumeGroup(object): logger.exception(error_msg) raise RuntimeError(error_msg) - try: - integer = float(integer) - except (TypeError, ValueError): - logger.exception(error_msg) - raise RuntimeError(error_msg) - - # round down the float, convert to an integer - return int(floor(integer)) + return util.str_to_int(integer) @property def free(self): @@ -979,7 +991,32 @@ class VolumeGroup(object): :raises SizeAllocationError: When requested size cannot be allocated with :raises ValueError: If both ``parts`` and ``size`` are given """ - return sizing(self.free, parts=parts, size=size) + if parts is not None and size is not None: + raise ValueError( + "Cannot process sizing with both parts (%s) and size (%s)" % (parts, size) + ) + + # if size is given we need to map that to extents so that we avoid + # issues when trying to get this right with a size in gigabytes find + # the percentage first, cheating, because these values are thrown out + vg_free_count = util.str_to_int(self.vg_free_count) + + if size: + extents = int(size * vg_free_count / self.free) + disk_sizing = sizing(self.free, size=size, parts=parts) + else: + if parts is not None: + # Prevent parts being 0, falling back to 1 (100% usage) + parts = parts or 1 + size = int(self.free / parts) + extents = size * vg_free_count / self.free + disk_sizing = sizing(self.free, parts=parts) + + extent_sizing = sizing(vg_free_count, size=extents) + + disk_sizing['extents'] = int(extents) + disk_sizing['percentages'] = extent_sizing['percentages'] + return disk_sizing class Volume(object): -- 2.39.5