From 72a8918ee05c7b839ff0b8e738bf1e90187a06e2 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 17 Dec 2018 14:47:06 -0600 Subject: [PATCH] mgr/balancer: document and specify options Signed-off-by: Sage Weil --- src/pybind/mgr/balancer/module.py | 134 +++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 31 deletions(-) diff --git a/src/pybind/mgr/balancer/module.py b/src/pybind/mgr/balancer/module.py index 711922ddb23..c6c5e80b473 100644 --- a/src/pybind/mgr/balancer/module.py +++ b/src/pybind/mgr/balancer/module.py @@ -14,10 +14,6 @@ from mgr_module import MgrModule, CommandResult from threading import Event from mgr_module import CRUSHMap -# available modes: 'none', 'crush', 'crush-compat', 'upmap', 'osd_weight' -default_mode = 'none' -default_sleep_interval = 60 # seconds - TIME_FORMAT = '%Y-%m-%d_%H:%M:%S' class MappingState: @@ -202,17 +198,94 @@ class Eval: class Module(MgrModule): MODULE_OPTIONS = [ - {'name': 'active'}, - {'name': 'begin_time'}, - {'name': 'crush_compat_max_iterations'}, - {'name': 'crush_compat_metrics'}, - {'name': 'crush_compat_step'}, - {'name': 'end_time'}, - {'name': 'min_score'}, - {'name': 'mode'}, - {'name': 'sleep_interval'}, - {'name': 'upmap_max_iterations'}, - {'name': 'upmap_max_deviation'}, + { + 'name': 'active', + 'type': 'bool', + 'default': False, + 'desc': 'automatically balance PGs across cluster', + 'runtime': True, + }, + { + 'name': 'begin_time', + 'type': 'str', + 'default': '0000', + 'desc': 'beginning time of day to automatically balance', + 'long_desc': 'This is a time of day in the format HHMM.', + 'runtime': True, + }, + { + 'name': 'end_time', + 'type': 'str', + 'default': '2400', + 'desc': 'ending time of day to automatically balance', + 'long_desc': 'This is a time of day in the format HHMM.', + 'runtime': True, + }, + { + 'name': 'crush_compat_max_iterations', + 'type': 'uint', + 'default': 25, + 'min': '1', + 'max': '250', + 'desc': 'maximum number of iterations to attempt optimization', + 'runtime': True, + }, + { + 'name': 'crush_compat_metrics', + 'type': 'str', + 'default': 'pgs,objects,bytes', + 'desc': 'metrics with which to calculate OSD utilization', + 'long_desc': 'Value is a list of one or more of "pgs", "objects", or "bytes", and indicates which metrics to use to balance utilization.', + 'runtime': True, + }, + { + 'name': 'crush_compat_step', + 'type': 'float', + 'default': .5, + 'min': '.001', + 'max': '.999', + 'desc': 'aggressiveness of optimization', + 'long_desc': '.99 is very aggressive, .01 is less aggressive', + 'runtime': True, + }, + { + 'name': 'min_score', + 'type': 'float', + 'default': 0, + 'desc': 'minimum score, below which no optimization is attempted', + 'runtime': True, + }, + { + 'name': 'mode', + 'desc': 'Balancer mode', + 'default': 'none', + 'enum_allowed': ['none', 'crush-compat', 'upmap'], + 'runtime': True, + }, + { + 'name': 'sleep_interval', + 'type': 'secs', + 'default': 60, + 'desc': 'how frequently to wake up and attempt optimization', + 'runtime': True, + }, + { + 'name': 'upmap_max_iterations', + 'type': 'uint', + 'default': 10, + 'desc': 'maximum upmap optimization iterations', + 'runtime': True, + }, + { + 'name': 'upmap_max_deviation', + 'type': 'float', + 'default': .01, + 'min': 0, + 'max': 1, + 'desc': 'deviation below which no optimization is attempted', + 'long_desc': 'If the ratio between the fullest and least-full OSD is below this value then we stop trying to optimize placement.', + 'runtime': True, + }, ] COMMANDS = [ @@ -297,7 +370,7 @@ class Module(MgrModule): s = { 'plans': list(self.plans.keys()), 'active': self.active, - 'mode': self.get_module_option('mode', default_mode), + 'mode': self.get_module_option('mode'), } return (0, json.dumps(s, indent=4), '') elif command['prefix'] == 'balancer mode': @@ -305,13 +378,13 @@ class Module(MgrModule): return (0, '', '') elif command['prefix'] == 'balancer on': if not self.active: - self.set_module_option('active', '1') + self.set_module_option('active', 'true') self.active = True self.event.set() return (0, '', '') elif command['prefix'] == 'balancer off': if self.active: - self.set_module_option('active', '') + self.set_module_option('active', 'false') self.active = False self.event.set() return (0, '', '') @@ -399,15 +472,14 @@ class Module(MgrModule): def serve(self): self.log.info('Starting') while self.run: - self.active = self.get_module_option('active', '') is not '' - begin_time = self.get_module_option('begin_time') or '0000' - end_time = self.get_module_option('end_time') or '2400' + self.active = self.get_module_option('active') + begin_time = self.get_module_option('begin_time') + end_time = self.get_module_option('end_time') timeofday = time.strftime('%H%M', time.localtime()) self.log.debug('Waking up [%s, scheduled for %s-%s, now %s]', "active" if self.active else "inactive", begin_time, end_time, timeofday) - sleep_interval = float(self.get_module_option('sleep_interval', - default_sleep_interval)) + sleep_interval = self.get_module_option('sleep_interval') if self.active and self.time_in_interval(timeofday, begin_time, end_time): self.log.debug('Running') name = 'auto_%s' % time.strftime(TIME_FORMAT, time.gmtime()) @@ -629,7 +701,7 @@ class Module(MgrModule): self.log.debug('score_by_root %s' % pe.score_by_root) # get the list of score metrics, comma separated - metrics = self.get_module_option('crush_compat_metrics', 'pgs,objects,bytes').split(',') + metrics = self.get_module_option('crush_compat_metrics').split(',') # total score is just average of normalized stddevs pe.score = 0.0 @@ -646,7 +718,7 @@ class Module(MgrModule): def optimize(self, plan): self.log.info('Optimize plan %s' % plan.name) - plan.mode = self.get_module_option('mode', default_mode) + plan.mode = self.get_module_option('mode') max_misplaced = float(self.get_ceph_option('target_max_misplaced_ratio')) self.log.info('Mode %s, max misplaced %f' % (plan.mode, max_misplaced)) @@ -692,8 +764,8 @@ class Module(MgrModule): def do_upmap(self, plan): self.log.info('do_upmap') - max_iterations = int(self.get_module_option('upmap_max_iterations', 10)) - max_deviation = float(self.get_module_option('upmap_max_deviation', .01)) + max_iterations = self.get_module_option('upmap_max_iterations') + max_deviation = self.get_module_option('upmap_max_deviation') ms = plan.initial if len(plan.pools): @@ -725,10 +797,10 @@ class Module(MgrModule): def do_crush_compat(self, plan): self.log.info('do_crush_compat') - max_iterations = int(self.get_module_option('crush_compat_max_iterations', 25)) + max_iterations = self.get_module_option('crush_compat_max_iterations') if max_iterations < 1: return -errno.EINVAL, '"crush_compat_max_iterations" must be >= 1' - step = float(self.get_module_option('crush_compat_step', .5)) + step = self.get_module_option('crush_compat_step') if step <= 0 or step >= 1.0: return -errno.EINVAL, '"crush_compat_step" must be in (0, 1)' max_misplaced = float(self.get_ceph_option('target_max_misplaced_ratio')) @@ -738,7 +810,7 @@ class Module(MgrModule): osdmap = ms.osdmap crush = osdmap.get_crush() pe = self.calc_eval(ms, plan.pools) - min_score_to_optimize = float(self.get_module_option('min_score', 0)) + min_score_to_optimize = self.get_module_option('min_score') if pe.score <= min_score_to_optimize: if pe.score == 0: detail = 'Distribution is already perfect' @@ -781,7 +853,7 @@ class Module(MgrModule): return -errno.EOPNOTSUPP, detail # rebalance by pgs, objects, or bytes - metrics = self.get_module_option('crush_compat_metrics', 'pgs,objects,bytes').split(',') + metrics = self.get_module_option('crush_compat_metrics').split(',') key = metrics[0] # balancing using the first score metric if key not in ['pgs', 'bytes', 'objects']: self.log.warn("Invalid crush_compat balancing key %s. Using 'pgs'." % key) -- 2.39.5