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
|
||||
|
||||
example for sea: return samenv name
|
||||
if service is None return a dict <service> of <cfg>
|
||||
"""
|
||||
cfginfo = {}
|
||||
self.get_procs(self.group, cfginfo)
|
||||
result = cfginfo.get((ins, service))
|
||||
if result:
|
||||
return result
|
||||
return ''
|
||||
if service is None:
|
||||
return {s: cfginfo.get((ins, s)) or '' for s in self.services}
|
||||
return cfginfo.get((ins, service)) or ''
|
||||
|
||||
def get_procs(self, groups=None, cfginfo=None):
|
||||
"""return processes
|
||||
|
211
frappyman.py
211
frappyman.py
@ -22,23 +22,41 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import builtins
|
||||
from glob import glob
|
||||
from collections import defaultdict
|
||||
from os.path import join, isdir, basename, expanduser, exists
|
||||
from configparser import ConfigParser
|
||||
from .base import ServiceManager, ServiceDown, UsageError
|
||||
from .seaman import SeaManager
|
||||
|
||||
|
||||
def dummy(*args, **kwds):
|
||||
pass
|
||||
class Namespace(dict):
|
||||
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:
|
||||
description = ''
|
||||
|
||||
def __call__(self, equipment_id, description, *args, **kwds):
|
||||
def node(self, equipment_id, description, *args, **kwds):
|
||||
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):
|
||||
group = 'frappy'
|
||||
@ -156,70 +174,99 @@ class FrappyManager(ServiceManager):
|
||||
init(*nodes)
|
||||
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
|
||||
|
||||
:param ins: instance
|
||||
:param service: service nor None for all services
|
||||
:param details:
|
||||
True: return a dict of <cfgdir> of <cfg> of <description>
|
||||
False: return a set of <cfg>
|
||||
:param list_info: None or a dict to collect info in the form
|
||||
dict <cfgdir> of <cfg> of <description>
|
||||
:param sea_info: None or a dict <sea config> of set() to be populated
|
||||
:return: set of available config
|
||||
"""
|
||||
result = {}
|
||||
all_cfg = set()
|
||||
if not ins:
|
||||
return {}
|
||||
namespace = {k: dummy for k in ('Mod', 'Param', 'Command', 'Group')}
|
||||
namespace['Node'] = node = Node()
|
||||
namespace = Namespace()
|
||||
details = sea_info is not None or list_info is not None
|
||||
for service in [service] if service else self.services:
|
||||
for cfgdir in self.config_dirs(ins, service):
|
||||
result.setdefault(cfgdir, {})
|
||||
cfgs = result[cfgdir]
|
||||
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 = ''
|
||||
for cfgfile in glob(join(cfgdir, '*_cfg.py')):
|
||||
cfg = basename(cfgfile)[:-7]
|
||||
if details:
|
||||
try:
|
||||
if cfgfile.endswith('.py'):
|
||||
node.description = ''
|
||||
try:
|
||||
with open(cfgfile, encoding='utf-8') as f:
|
||||
exec(f.read(), namespace)
|
||||
except Exception as e:
|
||||
node.description = repr(e)
|
||||
desc = node.description
|
||||
else:
|
||||
parser = ConfigParser()
|
||||
parser.read(cfgfile)
|
||||
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
|
||||
desc, sea_cfg = self.get_cfg_details(namespace, cfgfile)
|
||||
except Exception as e:
|
||||
sea_cfg = None
|
||||
desc = repr(e)
|
||||
if cfg not in all_cfg:
|
||||
if sea_cfg and sea_info is not None:
|
||||
sea_info.setdefault(sea_cfg, set()).add(cfg)
|
||||
all_cfg.add(cfg)
|
||||
if list_info is not None:
|
||||
list_info.setdefault(cfgdir, {})[cfg] = desc.split('\n', 1)[0]
|
||||
return all_cfg
|
||||
|
||||
def do_listcfg(self, ins='', service='', prt=print):
|
||||
if not ins:
|
||||
raise UsageError('missing instance')
|
||||
list_info = {}
|
||||
sea_info = {}
|
||||
if service:
|
||||
all_cfg = self.all_cfg(ins, service, True)
|
||||
self.all_cfg(ins, service, list_info, sea_info)
|
||||
else:
|
||||
all_cfg = {}
|
||||
for service in self.services:
|
||||
all_cfg.update(self.all_cfg(ins, service, True))
|
||||
for cfgdir, cfgs in all_cfg.items():
|
||||
self.all_cfg(ins, service, list_info, sea_info)
|
||||
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:
|
||||
prt('')
|
||||
prt('--- %s:' % cfgdir)
|
||||
keylen = max(len(k) for k in cfgs)
|
||||
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(' ')
|
||||
gap = ' ' * keylen
|
||||
prt(f'{gap} * need sea')
|
||||
if ambiguous:
|
||||
print(f'{gap} ! {ambiguous} ambiguous mappings sea -> frappy')
|
||||
|
||||
def treat_args(self, argdict, unknown=(), extra=()):
|
||||
if len(unknown) == 1:
|
||||
@ -228,35 +275,55 @@ class FrappyManager(ServiceManager):
|
||||
argdict['service'] = cfg
|
||||
return super().treat_args(argdict, (), ())
|
||||
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, 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.get_info()
|
||||
cfgs, confirmed = sea.get_cfg(ins, 'sea', True)
|
||||
cfgs = cfgs.split('/')
|
||||
result = {}
|
||||
seacfgs = sea.get_cfg(ins, 'sea').split('/')
|
||||
sea_info = {}
|
||||
if len(seacfgs) < 2:
|
||||
seacfgs.append('')
|
||||
self.all_cfg(ins, None, sea_info=sea_info)
|
||||
|
||||
def check_cfg_file(cfg, allcfg):
|
||||
if cfg is 'none':
|
||||
return ''
|
||||
return cfg if cfg in allcfg else cfg + '?'
|
||||
guess = defaultdict(lambda: defaultdict(list))
|
||||
|
||||
allmain = self.all_cfg(ins, 'main')
|
||||
if cfgs[0]:
|
||||
result['main'] = check_cfg_file(cfgs[0], allmain)
|
||||
if len(cfgs) > 1:
|
||||
stick = cfgs[1]
|
||||
allsticks = self.all_cfg(ins, 'stick')
|
||||
if stick:
|
||||
if stick not in allsticks and stick in allmain:
|
||||
stick += 'stick'
|
||||
result['stick'] = check_cfg_file(stick, allsticks)
|
||||
alladdons = self.all_cfg(ins, 'addons')
|
||||
addons = [check_cfg_file(a, alladdons) for a in cfgs[2:]]
|
||||
if addons:
|
||||
result['addons'] = ','.join(addons)
|
||||
result['confirmed'] = confirmed
|
||||
return result
|
||||
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
|
||||
|
@ -118,7 +118,7 @@ class SeaManager(ServiceManager):
|
||||
"""
|
||||
if service != 'sea': # ignore when service == 'graph'
|
||||
return ''
|
||||
if 'sea' not in self.get_procs().get(ins):
|
||||
if 'sea' not in self.get_procs().get(ins, ()):
|
||||
return ''
|
||||
try:
|
||||
searoot = self.env[ins].get('SEA_ROOT', '')
|
||||
@ -135,7 +135,7 @@ class SeaManager(ServiceManager):
|
||||
if match:
|
||||
key, dev, addon = match.groups()
|
||||
if addon:
|
||||
if addon != result[1]:
|
||||
if addon != result[1]: # skip stick appearing also in addon_list
|
||||
result.append(addon)
|
||||
elif key == 'name':
|
||||
result[0] = dev
|
||||
@ -145,8 +145,9 @@ class SeaManager(ServiceManager):
|
||||
confirmed = dev
|
||||
if not result[-1]:
|
||||
result.pop()
|
||||
result = '/'.join(result)
|
||||
return (result, confirmed) if addconfirmed else result
|
||||
if addconfirmed:
|
||||
result.insert(0, confirmed)
|
||||
return '/'.join(result)
|
||||
except Exception as e:
|
||||
return repr(e)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user