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
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),
}
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]
)
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::
{"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',
'%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:
: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)
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
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):
: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):