From 92d44f5c940c1f998e2e7a41fb2846b820dd2c31 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Sat, 23 Sep 2023 10:32:33 +0200 Subject: [PATCH] [WIP] overview in table form, test --- frappyman.py | 133 ++++++++++++++++++++++++++++++++++++----------- test_proposed.py | 24 +++++++++ 2 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 test_proposed.py diff --git a/frappyman.py b/frappyman.py index 8837205..3dc1840 100644 --- a/frappyman.py +++ b/frappyman.py @@ -32,6 +32,10 @@ from .base import ServiceManager, ServiceDown, UsageError from .seaman import SeaManager +MAIN = 1 +STICK = 2 + + class Namespace(dict): def __init__(self): self['Node'] = self.node @@ -58,6 +62,83 @@ class Namespace(dict): __builtins__ = builtins +def make_proposed(givencfgs, ourcfgs, seacfgs, sea_info, strict=False): + overview = {} + for service in 'main', 'stick': + given = givencfgs.get(service) + if given: + overview.setdefault(service, {})['given'] = given + frappy = ourcfgs.get(service) + if frappy: + overview.setdefault(service, {})['frappy'] = frappy + for addon in givencfgs.get('addons', '').split(','): + addon = addon.strip() + if addon: + overview.setdefault(addon, {})['given'] = addon + for addon in ourcfgs.get('addons', '').split(','): + addon = addon.strip() + if addon: + overview.setdefault(addon, {})['frappy'] = addon + + error = False + result = {} + addons = set() + for i, seacfg in enumerate(seacfgs): + if i == 0: + key, ext = 'main', '.config' + elif i == 1: + key, ext = 'stick', '.stick' + else: + key, ext = None, '.addon' + available = sea_info.get(seacfg + ext, ()) + if available: + proposed = list(available)[0] if len(available) == 1 else None + if not proposed: + for item in available: + if item == overview.get(key or item, {}).get('given'): + proposed = item + if item == overview.get(key or item, {}).get('frappy'): + frappy = item + if frappy and not proposed: + proposed = frappy + if proposed: + itemdict = overview.setdefault(key or proposed, {}) + given = itemdict.get('given') + itemdict.update(name=(key or 'addon'), sea=seacfg, + remark='ok' if given == proposed else f'change to {proposed}') + if key: + result[key] = given + else: + addons.add(given) + else: + for item in available: + overview.setdefault(item, {}).update(name=item, remark='ambiguous', sea=seacfg) + error = True + else: + overview.setdefault(key or seacfg, {}).update(name=key or '', sea=seacfg, remark='missing frappy config') + error = True + + if addons: + result['addons'] = ','.join(addons) + + if error and strict: + result = {} + else: + for service, cfg in ourcfgs.items(): + key = cfg if service is 'addons' else service + if cfg: + prop = result.get(service, '') + if prop == cfg: + result[service] = True + + columns = ['name', 'given', 'frappy', 'sea', 'remark'] + rows = [columns[:-1] + [' ']] + [[d.get(c) or '' for c in columns] for d in overview.values()] + wid = [max(len(v) for v in column) for column in zip(*rows)] + # insert title lines + rows.insert(1, ['-' * w for w in wid]) + return result, [' '.join(v.ljust(w) for w, v in zip(wid, row)) for row in rows] + + class FrappyManager(ServiceManager): group = 'frappy' services = ('main', 'stick', 'addons') @@ -290,40 +371,30 @@ class FrappyManager(ServiceManager): except Exception: return cfg + '?' - def guess_cfgs(self, ins, cfgs): + def propose_cfgs(self, ins, givencfgs, strict=False): + """get proposed configuration and an overview of the running servers + + :param ins: the instance to be checked for + :param givencfgs: a dict of cfg given by the ECS + :param strict: in case of errors (ambiguities or missing frappy config files) + proposed will be return as None + + :return: a tuple (, ) + where is information from running servers: + a dict of dict with optional items 'given', 'frappy', 'sea', 'proposed', 'error' + + given: the values for the cfgs argument + frappy: the cfg from running frappy servers + sea: the sea config file running + proposed: proposed configuration for the ECS + error: information about missing or ambiguous cfg files + """ + ourcfgs = self.get_cfg(ins, None) sea = SeaManager() seacfgs = sea.get_cfg(ins, 'sea').split('/') - sea_info = {} if len(seacfgs) < 2: seacfgs.append('') + sea_info = {} self.all_cfg(ins, None, sea_info=sea_info) + return self.make_proposed(givencfgs, ourcfgs, seacfgs, sea_info, strict) - guess = defaultdict(lambda: defaultdict(list)) - - def check_cfg(service, ext, frappyset, seacfgs): - for seacfg in seacfgs: - available = sea_info.get(seacfg + ext, ()) - if available: - for a in available: - if a in frappyset: - guess[service]['ok'].append(a) - break - else: - if len(available) == 1: - available = next(iter(available)) - # available is either a string or a set of strings - guess[service]['proposed'].append(available) - else: - guess[service]['missing'].append(seacfg) - - main = cfgs.get('main') - check_cfg('main', '.config', set() if main is None else {main}, {seacfgs[0]}) - - if len(seacfgs) > 1: - stick = cfgs.get('stick') - check_cfg('stick', '.stick', set() if stick is None else {stick}, {seacfgs[1]}) - - if len(seacfgs) > 2: - addons = set(cfgs.get('addons').split(',')) - check_cfg('addons', '.addons', set(addons), set(seacfgs[2:])) - return guess diff --git a/test_proposed.py b/test_proposed.py new file mode 100644 index 0000000..2cf3c29 --- /dev/null +++ b/test_proposed.py @@ -0,0 +1,24 @@ +import pytest +from servicemanager.frappyman import make_proposed + +sea_info = { + 'ma10.config': {'ma10'}, + 'ma10.stick': {'ma10stick', 'ma10'}, + 'rt.addon': {'rt', 'roomt'}, + 'befilter.addon': {'befilter'}, +} + +given = dict(main='ma10', stick='ma10stick') +cfgs = dict(given) +seacfg = ['ma10', 'ma10'] + +@pytest.mark.parametrize('given, current, seacfg, output, proposed, proposedstrict', [ + + ]) +def test_proposed(given, current, seacfg, output, proposed, proposedstrict): + prop, overview = make_proposed(given, current, seacfg, sea_info) + assert overview == output + assert prop == proposed + props, overview = make_proposed(given, current, seacfg, sea_info, True) + assert overview == output + assert props == proposedstrict