improve frappy manager
- add functionality to guess combination of configurations from the running sea and frappy servers - instead of the name only, the references to the config in the SeaClient modules are used - make this information also visibible in frappy list
This commit is contained in:
parent
957a5f0b2c
commit
fc364f0fc6
8
base.py
8
base.py
@ -164,13 +164,13 @@ class ServiceManager:
|
|||||||
"""return cfg info about running programs, if relevant
|
"""return cfg info about running programs, if relevant
|
||||||
|
|
||||||
example for sea: return samenv name
|
example for sea: return samenv name
|
||||||
|
if service is None return a dict <service> of <cfg>
|
||||||
"""
|
"""
|
||||||
cfginfo = {}
|
cfginfo = {}
|
||||||
self.get_procs(self.group, cfginfo)
|
self.get_procs(self.group, cfginfo)
|
||||||
result = cfginfo.get((ins, service))
|
if service is None:
|
||||||
if result:
|
return {s: cfginfo.get((ins, s)) or '' for s in self.services}
|
||||||
return result
|
return cfginfo.get((ins, service)) or ''
|
||||||
return ''
|
|
||||||
|
|
||||||
def get_procs(self, groups=None, cfginfo=None):
|
def get_procs(self, groups=None, cfginfo=None):
|
||||||
"""return processes
|
"""return processes
|
||||||
|
211
frappyman.py
211
frappyman.py
@ -22,23 +22,41 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
import builtins
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
from collections import defaultdict
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
def dummy(*args, **kwds):
|
class Namespace(dict):
|
||||||
pass
|
def __init__(self):
|
||||||
|
self['Node'] = self.node
|
||||||
|
self['Mod'] = self.mod
|
||||||
|
for fun in 'Param', 'Command', 'Group':
|
||||||
|
self[fun] = self.dummy
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.description = ''
|
||||||
|
self.sea_cfg = None
|
||||||
|
|
||||||
class Node:
|
def node(self, equipment_id, description, *args, **kwds):
|
||||||
description = ''
|
|
||||||
|
|
||||||
def __call__(self, equipment_id, description, *args, **kwds):
|
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
|
def mod(self, name, cls, description, config=None, **kwds):
|
||||||
|
cls = getattr(cls, '__name__', cls)
|
||||||
|
if cls.endswith('SeaClient'):
|
||||||
|
self.sea_cfg = config
|
||||||
|
|
||||||
|
def dummy(self, *args, **kwds):
|
||||||
|
return None
|
||||||
|
|
||||||
|
__builtins__ = builtins
|
||||||
|
|
||||||
|
|
||||||
class FrappyManager(ServiceManager):
|
class FrappyManager(ServiceManager):
|
||||||
group = 'frappy'
|
group = 'frappy'
|
||||||
@ -156,70 +174,99 @@ class FrappyManager(ServiceManager):
|
|||||||
init(*nodes)
|
init(*nodes)
|
||||||
interact()
|
interact()
|
||||||
|
|
||||||
def all_cfg(self, ins, service, details=False):
|
def get_cfg_details(self, namespace, cfgfile):
|
||||||
|
namespace.init()
|
||||||
|
local = {}
|
||||||
|
with open(cfgfile, encoding='utf-8') as f:
|
||||||
|
exec(f.read(), namespace, local)
|
||||||
|
return namespace.description, local.get('sea_cfg', namespace.sea_cfg)
|
||||||
|
|
||||||
|
def cfg_details(self, ins, service, cfg):
|
||||||
|
namespace = Namespace()
|
||||||
|
for cfgdir in self.config_dirs(ins, service):
|
||||||
|
cfgfile = join(cfgdir, f'{cfg}_cfg.py')
|
||||||
|
if exists(cfgfile):
|
||||||
|
return self.get_cfg_details(namespace, cfgfile)
|
||||||
|
raise FileNotFoundError(f'{cfg} not found')
|
||||||
|
|
||||||
|
def is_cfg(self, ins, service, cfg):
|
||||||
|
try:
|
||||||
|
self.cfg_details(ins, service, cfg)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def all_cfg(self, ins, service, list_info=None, sea_info=None):
|
||||||
"""get available cfg files
|
"""get available cfg files
|
||||||
|
|
||||||
:param ins: instance
|
:param ins: instance
|
||||||
:param service: service nor None for all services
|
:param service: service nor None for all services
|
||||||
:param details:
|
:param list_info: None or a dict to collect info in the form
|
||||||
True: return a dict of <cfgdir> of <cfg> of <description>
|
dict <cfgdir> of <cfg> of <description>
|
||||||
False: return a set of <cfg>
|
:param sea_info: None or a dict <sea config> of set() to be populated
|
||||||
|
:return: set of available config
|
||||||
"""
|
"""
|
||||||
result = {}
|
|
||||||
all_cfg = set()
|
all_cfg = set()
|
||||||
if not ins:
|
if not ins:
|
||||||
return {}
|
return {}
|
||||||
namespace = {k: dummy for k in ('Mod', 'Param', 'Command', 'Group')}
|
namespace = Namespace()
|
||||||
namespace['Node'] = node = Node()
|
details = sea_info is not None or list_info is not None
|
||||||
for service in [service] if service else self.services:
|
for service in [service] if service else self.services:
|
||||||
for cfgdir in self.config_dirs(ins, service):
|
for cfgdir in self.config_dirs(ins, service):
|
||||||
result.setdefault(cfgdir, {})
|
for cfgfile in glob(join(cfgdir, '*_cfg.py')):
|
||||||
cfgs = result[cfgdir]
|
cfg = basename(cfgfile)[:-7]
|
||||||
root = self.env[ins].get('FRAPPY_ROOT')
|
|
||||||
cfg_pattern ='*_cfg.py' if exists(join(root, 'frappy')) else '*.cfg'
|
|
||||||
for cfgfile in glob(join(cfgdir, cfg_pattern)):
|
|
||||||
desc = ''
|
|
||||||
if details:
|
if details:
|
||||||
try:
|
try:
|
||||||
if cfgfile.endswith('.py'):
|
desc, sea_cfg = self.get_cfg_details(namespace, cfgfile)
|
||||||
node.description = ''
|
except Exception as e:
|
||||||
try:
|
sea_cfg = None
|
||||||
with open(cfgfile, encoding='utf-8') as f:
|
desc = repr(e)
|
||||||
exec(f.read(), namespace)
|
if cfg not in all_cfg:
|
||||||
except Exception as e:
|
if sea_cfg and sea_info is not None:
|
||||||
node.description = repr(e)
|
sea_info.setdefault(sea_cfg, set()).add(cfg)
|
||||||
desc = node.description
|
all_cfg.add(cfg)
|
||||||
else:
|
if list_info is not None:
|
||||||
parser = ConfigParser()
|
list_info.setdefault(cfgdir, {})[cfg] = desc.split('\n', 1)[0]
|
||||||
parser.read(cfgfile)
|
return all_cfg
|
||||||
for s in parser.sections():
|
|
||||||
if s == 'NODE' or s.startswith('node '):
|
|
||||||
desc = parser[s].get('description', '').split('\n')[0]
|
|
||||||
break
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
cfg = basename(cfgfile)[:1-len(cfg_pattern)]
|
|
||||||
if cfg not in all_cfg:
|
|
||||||
all_cfg.add(cfg)
|
|
||||||
cfgs[cfg] = desc
|
|
||||||
return result if details else all_cfg
|
|
||||||
|
|
||||||
def do_listcfg(self, ins='', service='', prt=print):
|
def do_listcfg(self, ins='', service='', prt=print):
|
||||||
if not ins:
|
if not ins:
|
||||||
raise UsageError('missing instance')
|
raise UsageError('missing instance')
|
||||||
|
list_info = {}
|
||||||
|
sea_info = {}
|
||||||
if service:
|
if service:
|
||||||
all_cfg = self.all_cfg(ins, service, True)
|
self.all_cfg(ins, service, list_info, sea_info)
|
||||||
else:
|
else:
|
||||||
all_cfg = {}
|
|
||||||
for service in self.services:
|
for service in self.services:
|
||||||
all_cfg.update(self.all_cfg(ins, service, True))
|
self.all_cfg(ins, service, list_info, sea_info)
|
||||||
for cfgdir, cfgs in all_cfg.items():
|
sea_ambig = {} # collect info about ambiguous sea info
|
||||||
|
seacfgpat = re.compile(r'(.*)(\.config|\.stick|\.addon)')
|
||||||
|
for seacfg, cfgset in sea_info.items():
|
||||||
|
name, ext = seacfgpat.match(seacfg).groups()
|
||||||
|
sea_ambig.update({k: (name, ext, len(cfgset)) for k in cfgset})
|
||||||
|
ambiguous = 0
|
||||||
|
keylen = max(max(len(k) for k in cfgs) for cfgs in list_info.values())
|
||||||
|
for cfgdir, cfgs in list_info.items():
|
||||||
if cfgs:
|
if cfgs:
|
||||||
prt('')
|
prt('')
|
||||||
prt('--- %s:' % cfgdir)
|
prt('--- %s:' % cfgdir)
|
||||||
keylen = max(len(k) for k in cfgs)
|
|
||||||
for cfg, desc in cfgs.items():
|
for cfg, desc in cfgs.items():
|
||||||
|
if cfg in sea_ambig:
|
||||||
|
name, ext, n = sea_ambig[cfg]
|
||||||
|
if name == cfg or name + 'stick' == cfg:
|
||||||
|
prefix = '* '
|
||||||
|
else:
|
||||||
|
prefix = f'* ({name}{ext}) '
|
||||||
|
if n > 1:
|
||||||
|
prefix = '!' + prefix[1:]
|
||||||
|
ambiguous += 1
|
||||||
|
desc = prefix + desc
|
||||||
prt('%s %s' % (cfg.ljust(keylen), desc))
|
prt('%s %s' % (cfg.ljust(keylen), desc))
|
||||||
|
prt(' ')
|
||||||
|
gap = ' ' * keylen
|
||||||
|
prt(f'{gap} * need sea')
|
||||||
|
if ambiguous:
|
||||||
|
print(f'{gap} ! {ambiguous} ambiguous mappings sea -> frappy')
|
||||||
|
|
||||||
def treat_args(self, argdict, unknown=(), extra=()):
|
def treat_args(self, argdict, unknown=(), extra=()):
|
||||||
if len(unknown) == 1:
|
if len(unknown) == 1:
|
||||||
@ -228,35 +275,55 @@ class FrappyManager(ServiceManager):
|
|||||||
argdict['service'] = cfg
|
argdict['service'] = cfg
|
||||||
return super().treat_args(argdict, (), ())
|
return super().treat_args(argdict, (), ())
|
||||||
if (',' in cfg or cfg.endswith('.cfg') or
|
if (',' in cfg or cfg.endswith('.cfg') or
|
||||||
cfg in self.all_cfg(argdict.get('ins'), argdict.get('service'))):
|
self.is_cfg(argdict.get('ins'), argdict.get('service'), cfg)):
|
||||||
return super().treat_args(argdict, (), unknown)
|
return super().treat_args(argdict, (), unknown)
|
||||||
return super().treat_args(argdict, unknown, extra)
|
return super().treat_args(argdict, unknown, extra)
|
||||||
|
|
||||||
def cfg_from_sea(self, ins):
|
def check_cfg_file(self, ins, service, cfg, needsea=False):
|
||||||
|
if cfg is 'none':
|
||||||
|
return ''
|
||||||
|
try:
|
||||||
|
desc, sea_cfg = self.cfg_details(self, ins, service, cfg)
|
||||||
|
if needsea and not sea_cfg:
|
||||||
|
return None
|
||||||
|
return cfg
|
||||||
|
except Exception:
|
||||||
|
return cfg + '?'
|
||||||
|
|
||||||
|
def guess_cfgs(self, ins, cfgs):
|
||||||
sea = SeaManager()
|
sea = SeaManager()
|
||||||
sea.get_info()
|
seacfgs = sea.get_cfg(ins, 'sea').split('/')
|
||||||
cfgs, confirmed = sea.get_cfg(ins, 'sea', True)
|
sea_info = {}
|
||||||
cfgs = cfgs.split('/')
|
if len(seacfgs) < 2:
|
||||||
result = {}
|
seacfgs.append('')
|
||||||
|
self.all_cfg(ins, None, sea_info=sea_info)
|
||||||
|
|
||||||
def check_cfg_file(cfg, allcfg):
|
guess = defaultdict(lambda: defaultdict(list))
|
||||||
if cfg is 'none':
|
|
||||||
return ''
|
|
||||||
return cfg if cfg in allcfg else cfg + '?'
|
|
||||||
|
|
||||||
allmain = self.all_cfg(ins, 'main')
|
def check_cfg(service, ext, frappyset, seacfgs):
|
||||||
if cfgs[0]:
|
for seacfg in seacfgs:
|
||||||
result['main'] = check_cfg_file(cfgs[0], allmain)
|
available = sea_info.get(seacfg + ext, ())
|
||||||
if len(cfgs) > 1:
|
if available:
|
||||||
stick = cfgs[1]
|
for a in available:
|
||||||
allsticks = self.all_cfg(ins, 'stick')
|
if a in frappyset:
|
||||||
if stick:
|
guess[service]['ok'].append(a)
|
||||||
if stick not in allsticks and stick in allmain:
|
break
|
||||||
stick += 'stick'
|
else:
|
||||||
result['stick'] = check_cfg_file(stick, allsticks)
|
if len(available) == 1:
|
||||||
alladdons = self.all_cfg(ins, 'addons')
|
available = next(iter(available))
|
||||||
addons = [check_cfg_file(a, alladdons) for a in cfgs[2:]]
|
# available is either a string or a set of strings
|
||||||
if addons:
|
guess[service]['proposed'].append(available)
|
||||||
result['addons'] = ','.join(addons)
|
else:
|
||||||
result['confirmed'] = confirmed
|
guess[service]['missing'].append(seacfg)
|
||||||
return result
|
|
||||||
|
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
|
||||||
|
@ -118,7 +118,7 @@ class SeaManager(ServiceManager):
|
|||||||
"""
|
"""
|
||||||
if service != 'sea': # ignore when service == 'graph'
|
if service != 'sea': # ignore when service == 'graph'
|
||||||
return ''
|
return ''
|
||||||
if 'sea' not in self.get_procs().get(ins):
|
if 'sea' not in self.get_procs().get(ins, ()):
|
||||||
return ''
|
return ''
|
||||||
try:
|
try:
|
||||||
searoot = self.env[ins].get('SEA_ROOT', '')
|
searoot = self.env[ins].get('SEA_ROOT', '')
|
||||||
@ -135,7 +135,7 @@ class SeaManager(ServiceManager):
|
|||||||
if match:
|
if match:
|
||||||
key, dev, addon = match.groups()
|
key, dev, addon = match.groups()
|
||||||
if addon:
|
if addon:
|
||||||
if addon != result[1]:
|
if addon != result[1]: # skip stick appearing also in addon_list
|
||||||
result.append(addon)
|
result.append(addon)
|
||||||
elif key == 'name':
|
elif key == 'name':
|
||||||
result[0] = dev
|
result[0] = dev
|
||||||
@ -145,8 +145,9 @@ class SeaManager(ServiceManager):
|
|||||||
confirmed = dev
|
confirmed = dev
|
||||||
if not result[-1]:
|
if not result[-1]:
|
||||||
result.pop()
|
result.pop()
|
||||||
result = '/'.join(result)
|
if addconfirmed:
|
||||||
return (result, confirmed) if addconfirmed else result
|
result.insert(0, confirmed)
|
||||||
|
return '/'.join(result)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return repr(e)
|
return repr(e)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user