Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ceph-volume lvm.strategies add bluestore with validators
Signed-off-by: Alfredo Deza <adeza@redhat.com> (cherry picked from commit 9f13951)
- Loading branch information
1 parent
414836b
commit f96cb9c
Showing
2 changed files
with
278 additions
and
0 deletions.
There are no files selected for viewing
265 changes: 265 additions & 0 deletions
265
src/ceph-volume/ceph_volume/devices/lvm/strategies/bluestore.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
from __future__ import print_function | ||
import json | ||
from ceph_volume.util import disk | ||
from ceph_volume.api import lvm | ||
from . import validators | ||
from ceph_volume.devices.lvm.create import Create | ||
|
||
# TODO: get these templates out so filestore can re-use them | ||
|
||
osd_header_template = """ | ||
{:-^80}""".format('') | ||
|
||
|
||
osd_component_titles = """ | ||
Type Path LV Size % of device""" | ||
|
||
|
||
osd_component_template = """ | ||
{_type: <15} {path: <25} {size: <15} {percent}%""" | ||
|
||
|
||
header_template = """ | ||
Total OSDs: {total_osds} | ||
""" | ||
|
||
vg_template = """ | ||
Solid State VG: | ||
Targets: {target: <25} Total size: {total_lv_size: <25} | ||
Total LVs: {total_lvs: <25} Size per LV: {lv_size: <25} | ||
Devices: {block_db_devices} | ||
""" | ||
|
||
|
||
class SingleType(object): | ||
""" | ||
Support for all SSDs, or all HDDS | ||
""" | ||
|
||
def __init__(self, devices, args): | ||
self.args = args | ||
self.devices = devices | ||
self.hdds = [device for device in devices if device['rotational'] == '1'] | ||
self.ssds = [device for device in devices if device['rotational'] == '0'] | ||
self.computed = {'osds': [], 'vgs': []} | ||
self.validate() | ||
self.compute() | ||
|
||
def report_json(self): | ||
print(json.dumps(self.computed, indent=4, sort_keys=True)) | ||
|
||
def report_pretty(self): | ||
string = "" | ||
string += header_template.format( | ||
total_osds=len(self.hdds) or len(self.ssds) * 2 | ||
) | ||
string += osd_component_titles | ||
|
||
for osd in self.computed['osds']: | ||
string += osd_header_template | ||
string += osd_component_template.format( | ||
_type='[data]', | ||
path=osd['data']['path'], | ||
size=osd['data']['human_readable_size'], | ||
percent=osd['data']['percentage'], | ||
) | ||
|
||
print(string) | ||
|
||
def validate(self): | ||
""" | ||
Ensure that the minimum requirements for this type of scenario is | ||
met, raise an error if the provided devices would not work | ||
""" | ||
# validate minimum size for all devices | ||
validators.minimum_device_size(self.devices) | ||
|
||
def compute(self): | ||
""" | ||
Go through the rules needed to properly size the lvs, return | ||
a dictionary with the result | ||
""" | ||
osds = self.computed['osds'] | ||
vgs = self.computed['vgs'] | ||
for device in self.hdds: | ||
vgs.append({'devices': [device['path']], 'parts': 1}) | ||
osd = {'data': {}, 'block.db': {}} | ||
osd['data']['path'] = device['path'] | ||
osd['data']['size'] = device['size'] | ||
osd['data']['parts'] = 1 | ||
osd['data']['percentage'] = 100 | ||
osd['data']['human_readable_size'] = str(disk.Size(b=device['size'])) | ||
osds.append(osd) | ||
|
||
for device in self.ssds: | ||
# TODO: creates 2 OSDs per device, make this configurable (env var?) | ||
extents = lvm.sizing(device['size'], parts=2) | ||
vgs.append({'devices': [device['path']], 'parts': 2}) | ||
for ssd in range(2): | ||
osd = {'data': {}, 'block.db': {}} | ||
osd['data']['path'] = device['path'] | ||
osd['data']['size'] = extents['sizes'] | ||
osd['data']['parts'] = extents['parts'] | ||
osd['data']['percentage'] = 50 | ||
osd['data']['human_readable_size'] = str(disk.Size(b=extents['sizes'])) | ||
osds.append(osd) | ||
|
||
def execute(self): | ||
""" | ||
Create vgs/lvs from the incoming set of devices, assign their roles | ||
(block, block.db, block.wal, etc..) and offload the OSD creation to | ||
``lvm create`` | ||
""" | ||
osd_vgs = dict([(osd['data']['path'], None) for osd in self.computed['osds']]) | ||
|
||
# create the vgs first, mapping them to the device path | ||
for osd in self.computed['osds']: | ||
vg = osd_vgs.get(osd['data']['path']) | ||
if not vg: | ||
vg = lvm.create_vg(osd['data']['path']) | ||
osd_vgs[osd['data']['path']] = {'vg': vg, 'parts': osd['data']['parts']} | ||
|
||
# create the lvs from the vgs captured in the beginning | ||
for create in osd_vgs.values(): | ||
lvs = lvm.create_lvs(create['vg'], parts=create['parts'], name_prefix='osd-data') | ||
vg_name = create['vg'].name | ||
for lv in lvs: | ||
# FIXME: no support for dmcrypt, crush class, etc... | ||
Create([ | ||
'--bluestore', | ||
'--data', "%s/%s" % (vg_name, lv.name), | ||
]).main() | ||
|
||
|
||
class MixedType(object): | ||
|
||
def __init__(self, devices, args): | ||
self.args = args | ||
self.devices = devices | ||
self.hdds = [device for device in devices if device['rotational'] == '1'] | ||
self.ssds = [device for device in devices if device['rotational'] == '0'] | ||
self.computed = {'osds': [], 'vgs': []} | ||
self.block_db_size = None | ||
# For every HDD we get 1 block.db | ||
self.db_lvs = len(self.hdds) | ||
self.validate() | ||
self.compute() | ||
|
||
def report_json(self): | ||
print(json.dumps(self.computed, indent=4, sort_keys=True)) | ||
|
||
def report_pretty(self): | ||
vg_extents = lvm.sizing(self.total_ssd_size.b, parts=self.db_lvs) | ||
db_size = str(disk.Size(b=(vg_extents['sizes']))) | ||
|
||
string = "" | ||
string += header_template.format( | ||
targets='block.db', | ||
total_lv_size=str(self.total_ssd_size), | ||
total_lvs=vg_extents['parts'], | ||
block_lv_size=db_size, | ||
block_db_devices=', '.join([ssd['path'] for ssd in self.ssds]), | ||
lv_size=str(disk.Size(b=(vg_extents['sizes']))), | ||
total_osds=len(self.hdds) | ||
) | ||
string += vg_template.format( | ||
target='block.db', | ||
total_lv_size=str(self.total_ssd_size), | ||
total_lvs=vg_extents['parts'], | ||
block_lv_size=db_size, | ||
block_db_devices=', '.join([ssd['path'] for ssd in self.ssds]), | ||
lv_size=str(disk.Size(b=(vg_extents['sizes']))), | ||
total_osds=len(self.hdds) | ||
) | ||
|
||
string += osd_component_titles | ||
for osd in self.computed['osds']: | ||
string += osd_header_template | ||
string += osd_component_template.format( | ||
_type='[data]', | ||
path=osd['data']['path'], | ||
size=osd['data']['human_readable_size'], | ||
percent=osd['data']['percentage']) | ||
|
||
string += osd_component_template.format( | ||
_type='[block.db]', | ||
path='(volume-group/lv)', | ||
size=osd['block.db']['human_readable_size'], | ||
percent=osd['block.db']['percentage']) | ||
|
||
print(string) | ||
|
||
def compute(self): | ||
osds = self.computed['osds'] | ||
for device in self.hdds: | ||
osd = {'data': {}, 'block.db': {}} | ||
osd['data']['path'] = device['path'] | ||
osd['data']['size'] = device['size'] | ||
osd['data']['percentage'] = 100 | ||
osd['data']['human_readable_size'] = str(disk.Size(b=(device['size']))) | ||
osd['block.db']['path'] = None | ||
osd['block.db']['size'] = int(self.block_db_size.b) | ||
osd['block.db']['human_readable_size'] = str(self.block_db_size) | ||
osd['block.db']['percentage'] = self.vg_extents['percentages'] | ||
osds.append(osd) | ||
|
||
self.computed['vgs'] = [{ | ||
'devices': [d['path'] for d in self.ssds], | ||
'parts': self.db_lvs, | ||
'percentages': self.vg_extents['percentages'], | ||
'sizes': self.vg_extents['sizes'], | ||
'size': int(self.total_ssd_size.b), | ||
'human_readable_sizes': str(disk.Size(b=self.vg_extents['sizes'])), | ||
'human_readable_size': str(self.total_ssd_size), | ||
}] | ||
|
||
def execute(self): | ||
""" | ||
Create vgs/lvs from the incoming set of devices, assign their roles | ||
(block, block.db, block.wal, etc..) and offload the OSD creation to | ||
``lvm create`` | ||
""" | ||
# create the single vg for all block.db lv's first | ||
vg_info = self.computed['vgs'][0] | ||
vg = lvm.create_vg(vg_info['devices']) | ||
|
||
# now produce all the block.db lvs needed from that single vg | ||
db_lvs = lvm.create_lvs(vg, parts=vg_info['parts'], name_prefix='osd-block-db') | ||
|
||
# create the data lvs, and create the OSD with the matching block.db lvs from before | ||
for osd in self.computed['osds']: | ||
vg = lvm.create_vg(osd['data']['path']) | ||
from uuid import uuid4 | ||
data_lv = lvm.create_lv('osd-data-%s' % str(uuid4()), vg.name) | ||
db_lv = db_lvs.pop() | ||
# FIXME: no support for dmcrypt, crush class, etc... | ||
Create([ | ||
'--bluestore', | ||
'--data', "%s/%s" % (data_lv.vg_name, data_lv.name), | ||
'--block.db', '%s/%s' % (db_lv.vg_name, db_lv.name) | ||
]).main() | ||
|
||
def validate(self): | ||
""" | ||
HDDs represent data devices, and solid state devices are for block.db, | ||
make sure that the number of data devices would have enough LVs and | ||
those LVs would be large enough to accommodate a block.db | ||
""" | ||
# validate minimum size for all devices | ||
validators.minimum_device_size(self.devices) | ||
|
||
# add all the size available in solid drives and divide it by the | ||
# expected number of osds, the expected output should be larger than | ||
# the minimum alllowed for block.db | ||
self.total_ssd_size = disk.Size(b=0) | ||
for ssd in self.ssds: | ||
self.total_ssd_size + disk.Size(b=ssd['size']) | ||
|
||
self.block_db_size = self.total_ssd_size / self.db_lvs | ||
self.vg_extents = lvm.sizing(self.total_ssd_size.b, parts=self.db_lvs) | ||
|
||
# min 2GB of block.db is allowed | ||
msg = 'Total solid size (%s) is not enough for block.db LVs larger than 2 GB' | ||
if self.block_db_size < disk.Size(gb=2): | ||
# use ad-hoc exception here | ||
raise RuntimeError(msg % self.total_ssd_size) |
13 changes: 13 additions & 0 deletions
13
src/ceph-volume/ceph_volume/devices/lvm/strategies/validators.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from ceph_volume.util import disk | ||
|
||
|
||
def minimum_device_size(devices): | ||
""" | ||
Ensure that the minimum requirements for this type of scenario is | ||
met, raise an error if the provided devices would not work | ||
""" | ||
msg = 'Unable to use device smaller than 5GB: %s (%s)' | ||
for device in devices: | ||
device_size = disk.Size(b=device['size']) | ||
if device_size < disk.Size(gb=5): | ||
raise RuntimeError(msg % (device, device_size)) |