From 9a426d0666d35a8b47d00752c6930b675f8bafda Mon Sep 17 00:00:00 2001 From: Alfredo Deza Date: Fri, 29 Jun 2018 11:53:27 -0400 Subject: [PATCH] ceph-volume lvm.auto help menu and dynamic strategy loading Signed-off-by: Alfredo Deza --- .../ceph_volume/devices/lvm/auto.py | 158 ++++++++++++++++-- 1 file changed, 147 insertions(+), 11 deletions(-) diff --git a/src/ceph-volume/ceph_volume/devices/lvm/auto.py b/src/ceph-volume/ceph_volume/devices/lvm/auto.py index d9b50195f74dc..58ebf83aaca23 100644 --- a/src/ceph-volume/ceph_volume/devices/lvm/auto.py +++ b/src/ceph-volume/ceph_volume/devices/lvm/auto.py @@ -2,6 +2,7 @@ import argparse from textwrap import dedent from ceph_volume import terminal, decorators from ceph_volume.util import disk +from . import strategies device_list_template = """ @@ -19,24 +20,93 @@ def device_formatter(devices): return ''.join(lines) +# Scenario filtering/detection +def bluestore_single_type(device_facts): + """ + Detect devices that are just HDDs or solid state so that a 1:1 + device-to-osd provisioning can be done + """ + types = [device['rotational'] for device in device_facts] + if len(set(types)) == 1: + return strategies.bluestore.SingleType + + +def bluestore_mixed_type(device_facts): + """ + Detect if devices are HDDs as well as solid state so that block.db can be + placed in solid devices while data is kept in the spinning drives. + """ + types = [device['rotational'] for device in device_facts] + if len(set(types)) > 1: + return strategies.bluestore.MixedType + + +def filestore_single_type(device_facts): + """ + Detect devices that are just HDDs or solid state so that a 1:1 + device-to-osd provisioning can be done, keeping the journal on the OSD + """ + types = [device['rotational'] for device in device_facts] + if len(set(types)) == 1: + return strategies.filestore.SingleType + + +def filestore_mixed_type(device_facts): + """ + Detect if devices are HDDs as well as solid state so that the journal can be + placed in solid devices while data is kept in the spinning drives. + """ + types = [device['rotational'] for device in device_facts] + if len(set(types)) > 1: + return strategies.filestore.MixedType + + +def get_strategy(devices, args): + """ + Given a set of devices as input, go through the different detection + mechanisms to narrow down on a strategy to use. The strategies are 4 in + total: + + * Single device type on Bluestore + * Mixed device types on Bluestore + * Single device type on Filestore + * Mixed device types on Filestore + + When the function matches to a scenario it returns the strategy class. This + allows for dynamic loading of the conditions needed for each scenario, with + normalized classes + """ + bluestore_strategies = [bluestore_mixed_type, bluestore_single_type] + filestore_strategies = [filestore_mixed_type, filestore_single_type] + if args.bluestore: + strategies = bluestore_strategies + else: + strategies = filestore_strategies + + for strategy in strategies: + backend = strategy(devices) + if backend: + return backend(devices) + + class Auto(object): - help = 'Auto-detect devices for multi-OSD provisioning with minimal interaction' + help = 'Automatically size devices for multi-OSD provisioning with minimal interaction' _help = dedent(""" - Automatically detect devices ready for OSD provisioning based on configurable strategies. + Automatically size devices ready for OSD provisioning based on default strategies. Detected devices: {detected_devices} - Current strategy: {strategy_name} - Path: {strategy_path} + Usage: - """) + ceph-volume lvm auto [{device}..] - # TODO: add the reporting sub-command here (list?) - mapper = { - } + Optional reporting on possible outcomes is enabled with --report + + ceph-volume lvm auto --report [{device}..] + """) def __init__(self, argv): self.argv = argv @@ -54,8 +124,33 @@ class Auto(object): def print_help(self, sub_help): return self._help.format( detected_devices=self.get_devices(), - strategy_name='default', - strategy_path='/etc/ceph/osd/strategies/default') + ) + + def get_filtered_devices(self, devices): + """ + Parse all devices in the current system and keep only the ones that are + being explicity passed in + """ + system_devices = disk.get_devices() + if not devices: + return system_devices + parsed_devices = [] + for device in devices: + try: + parsed_devices.append(system_devices[device]) + except KeyError: + continue + + return parsed_devices + + def report(self, args): + strategy = get_strategy(self.get_filtered_devices(args.devices), args) + if args.format == 'pretty': + strategy.report_pretty() + elif args.format == 'json': + strategy.report_json() + else: + raise RuntimeError('report format must be "pretty" or "json"') @decorators.needs_root def main(self): @@ -65,6 +160,47 @@ class Auto(object): formatter_class=argparse.RawDescriptionHelpFormatter, description=self.print_help(terminal.subhelp(self.mapper)), ) - parser.parse_args(self.argv) + parser.add_argument( + 'devices', + metavar='DEVICES', + nargs='*', + default=[], + help='Devices to provision OSDs', + ) + parser.add_argument( + '--bluestore', + action='store_true', + help='bluestore objectstore (default)', + ) + parser.add_argument( + '--filestore', + action='store_true', + help='filestore objectstore', + ) + parser.add_argument( + '--report', + action='store_true', + help='Autodetect the objectstore by inspecting the OSD', + ) + parser.add_argument( + '--format', + help='output format, defaults to "pretty"', + default='pretty', + choices=['json', 'pretty'], + ) + parser.add_argument( + '--no-systemd', + dest='no_systemd', + action='store_true', + help='Skip creating and enabling systemd units and starting OSD services', + ) + args = parser.parse_args(self.argv) if len(self.argv) <= 1: return parser.print_help() + # Default to bluestore here since defaulting it in add_argument may + # cause both to be True + if not args.bluestore and not args.filestore: + args.bluestore = True + + if args.report: + self.report(args) -- 2.39.5