rework guess_cfg to get_server_state

This commit is contained in:
zolliker 2023-10-17 15:14:34 +02:00
parent fc364f0fc6
commit 2d3394e205

View File

@ -25,13 +25,17 @@ import os
import re import re
import builtins import builtins
from glob import glob from glob import glob
from collections import defaultdict from itertools import zip_longest
from os.path import join, isdir, basename, expanduser, exists from os.path import join, isdir, basename, expanduser, exists
from configparser import ConfigParser from configparser import ConfigParser
from .base import ServiceManager, ServiceDown, UsageError from .base import ServiceManager, ServiceDown, UsageError
from .seaman import SeaManager from .seaman import SeaManager
MAIN = 1
STICK = 2
class Namespace(dict): class Namespace(dict):
def __init__(self): def __init__(self):
self['Node'] = self.node self['Node'] = self.node
@ -58,6 +62,117 @@ class Namespace(dict):
__builtins__ = builtins __builtins__ = builtins
SEAEXT = {'main': '.config', 'stick': '.stick'}
def summarize_server_state(givencfgs, ourcfgs, sealist, sea_info, strict=False):
"""get a guess for the configuration from information about running services
:param givencfgs: dict <service> of given configuration (from nicos cache)
:param outcfgs: dict <service> of running configuration (frappy)
:param sealist: list of running sea configuration
:param sea_info: dict <frappycfg> of <seacfg> with info about sea configs
:param strict: when True return empty cfg result on error
:return: tuple (<error>, <proposed>, (<frappyitems>, <seaitems>), <remarks> where:
<error>: proposed config not sure
<proposed>: dict <service> of proposed cfg
<frappyitems>: dict of items runnning in frappy servers (addons are separated)
<seaitems>: dict of items running on the sea server
<remarks>: dict of actions / remarks
"""
givencfgs = dict(givencfgs)
addons = givencfgs.pop('addons', '')
for addon in addons.split(','):
addon = addon.strip()
if addon:
givencfgs[addon] = addon
frappycfgs = dict(ourcfgs)
addons = frappycfgs.pop('addons', '')
for addon in addons.split(','):
addon = addon.strip()
if addon:
frappycfgs[addon] = addon
seacfgfiles = [(c or '') + SEAEXT.get(s, '.addon')
for c, s in zip_longest(sealist, FrappyManager.services)]
seacfgset = set(seacfgfiles)
inverted_sea_info = {k: [] for k in seacfgfiles}
for cfg, seacfg in sea_info.items():
if seacfg in seacfgset:
inverted_sea_info[seacfg].append(cfg)
error = False
result = {}
addons = set()
seacfgs = {}
remarks = {}
for seacfg, seacfgfile, key in zip_longest(sealist, seacfgfiles, ('main', 'stick')):
if not seacfg:
continue
available = inverted_sea_info[seacfgfile]
if available:
proposed = available[0] if len(available) == 1 else None
if not proposed:
running = None
for item in available:
if item == givencfgs.get(key or item):
proposed = item
if item == frappycfgs.get(key or item):
running = item
if running and not proposed:
proposed = running
if proposed:
pkey = key or proposed
given = givencfgs.get(pkey)
running = frappycfgs.get(pkey)
if running == proposed:
remarks[pkey] = '' if given == running else 'reconnect'
elif running:
remarks[pkey] = 'frappy restart needed'
else:
remarks[pkey] = 'frappy start needed'
seacfgs[pkey] = seacfg
if key:
result[key] = proposed
else:
addons.add(proposed)
else:
for item in available:
remarks[item] = 'ambiguous'
seacfgs[item] = seacfg
error = True
else:
pkey = key or seacfg
seacfgs[pkey] = seacfg
remarks[pkey] = 'missing frappy config'
error = True
restart = set()
for key, running in frappycfgs.items():
if running:
if key in ('main', 'stick'):
service = key
else:
service = 'addons'
addons.add(running)
if not seacfgs.get(key):
if sea_info.get(running):
restart.add(service)
remarks[key] = 'restart to start sea'
elif givencfgs.get(key) != running:
remarks[key] = 'reconnect'
elif remarks.get(key) is None:
remarks[key] = ''
if addons:
result['addons'] = ','.join(addons)
for service, cfg in ourcfgs.items():
if cfg:
prop = result.get(service, '')
if prop == cfg and service not in restart:
result[service] = True
return error, result, (frappycfgs, seacfgs), remarks
class FrappyManager(ServiceManager): class FrappyManager(ServiceManager):
group = 'frappy' group = 'frappy'
services = ('main', 'stick', 'addons') services = ('main', 'stick', 'addons')
@ -203,7 +318,8 @@ class FrappyManager(ServiceManager):
:param service: service nor None for all services :param service: service nor None for all services
:param list_info: None or a dict to collect info in the form :param list_info: None or a dict to collect info in the form
dict <cfgdir> of <cfg> of <description> dict <cfgdir> of <cfg> of <description>
:param sea_info: None or a dict <sea config> of set() to be populated :param sea_info: None or a dict <frappycfg> of <seacfg> with info about sea configs
in frappy cfgs to be populated
:return: set of available config :return: set of available config
""" """
all_cfg = set() all_cfg = set()
@ -224,6 +340,7 @@ class FrappyManager(ServiceManager):
if cfg not in all_cfg: if cfg not in all_cfg:
if sea_cfg and sea_info is not None: if sea_cfg and sea_info is not None:
sea_info.setdefault(sea_cfg, set()).add(cfg) sea_info.setdefault(sea_cfg, set()).add(cfg)
sea_info[cfg] = sea_cfg
all_cfg.add(cfg) all_cfg.add(cfg)
if list_info is not None: if list_info is not None:
list_info.setdefault(cfgdir, {})[cfg] = desc.split('\n', 1)[0] list_info.setdefault(cfgdir, {})[cfg] = desc.split('\n', 1)[0]
@ -239,11 +356,10 @@ class FrappyManager(ServiceManager):
else: else:
for service in self.services: for service in self.services:
self.all_cfg(ins, service, list_info, sea_info) self.all_cfg(ins, service, list_info, sea_info)
sea_ambig = {} # collect info about ambiguous sea info
seacfgpat = re.compile(r'(.*)(\.config|\.stick|\.addon)') seacfgpat = re.compile(r'(.*)(\.config|\.stick|\.addon)')
for seacfg, cfgset in sea_info.items(): sea_ambig = {} # collect info about ambiguous sea info
name, ext = seacfgpat.match(seacfg).groups() for cfg, seacfg in sea_info.items():
sea_ambig.update({k: (name, ext, len(cfgset)) for k in cfgset}) sea_ambig.setdefault(seacfg, set()).add(cfg)
ambiguous = 0 ambiguous = 0
keylen = max(max(len(k) for k in cfgs) for cfgs in list_info.values()) keylen = max(max(len(k) for k in cfgs) for cfgs in list_info.values())
for cfgdir, cfgs in list_info.items(): for cfgdir, cfgs in list_info.items():
@ -251,12 +367,14 @@ class FrappyManager(ServiceManager):
prt('') prt('')
prt('--- %s:' % cfgdir) prt('--- %s:' % cfgdir)
for cfg, desc in cfgs.items(): for cfg, desc in cfgs.items():
if cfg in sea_ambig: seacfg = sea_info.get(cfg)
name, ext, n = sea_ambig[cfg] if seacfg:
name, ext = seacfgpat.match(seacfg).groups()
if name == cfg or name + 'stick' == cfg: if name == cfg or name + 'stick' == cfg:
prefix = '* ' prefix = '* '
else: else:
prefix = f'* ({name}{ext}) ' prefix = f'* ({name}{ext}) '
n = len(sea_ambig.get(seacfg))
if n > 1: if n > 1:
prefix = '!' + prefix[1:] prefix = '!' + prefix[1:]
ambiguous += 1 ambiguous += 1
@ -290,40 +408,25 @@ class FrappyManager(ServiceManager):
except Exception: except Exception:
return cfg + '?' return cfg + '?'
def guess_cfgs(self, ins, cfgs): def get_server_state(self, ins, givencfgs):
"""get proposed configuration and an overview of the running servers
:param ins: the instance to be checked for
:param givencfgs: a dict <service> of cfg given by the ECS
:return: tuple (<error>, <proposed>, (<frappyitems>, <seaitems>), <remarks> where:
<error>: proposed config not sure
<proposed>: dict <service> of proposed cfg
<frappyitems>: dict of items runnning in frappy servers (addons are separated)
<seaitems>: dict of items running on the sea server
<remarks>: dict of actions to do / remarks
"""
ourcfgs = self.get_cfg(ins, None)
sea = SeaManager() sea = SeaManager()
seacfgs = sea.get_cfg(ins, 'sea').split('/') seacfgs = sea.get_cfg(ins, 'sea').split('/')
sea_info = {}
if len(seacfgs) < 2: if len(seacfgs) < 2:
seacfgs.append('') seacfgs.append('')
sea_info = {}
self.all_cfg(ins, None, sea_info=sea_info) self.all_cfg(ins, None, sea_info=sea_info)
return summarize_server_state(givencfgs, ourcfgs, seacfgs, sea_info)
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