Files
linsetools/frappy.py
2026-03-24 08:56:56 +01:00

134 lines
4.7 KiB
Python

import re
from pathlib import Path
from .base import MarcheControl, Logger, ArgError
WRAPPER_CFG = """interface = '{port}'
include({cfg!r})
overrideNode(interface=interface)
"""
WRAPPER_PAT = re.compile(r"interface\s=\s*'(\d*)'\s*\n")
class FrappyMarche(MarcheControl):
services = 'main', 'stick', 'addons'
def __init__(self, instance, host='localhost', port=None, user=None):
super().__init__(host, port, user, instance)
superconfig = self.config.sections['superfrappy']
self.instance = instance
self.wrapperdir = superconfig.pop('wrapperdir')
self.cfgdirs = superconfig.pop('cfgdirs')
frappy_ports = self.ins_config.get('frappy_ports')
ports = frappy_ports.split('-')
self.frappy_ports = list(range(int(ports[0]), int(ports[-1])))
self.frappy_servers = [f'{host}:{p}' for p in self.frappy_ports]
if not Path(self.wrapperdir).is_dir():
raise ValueError(f'{self.wrapperdir} does not exist')
self.get_cfg_info() # do we need to update this from time to time?
self.argmap.update((k, 'service') for k in self.services)
def get_service(self, instance):
return f'frappy.{instance}' if self.instance == 'this' else f'frappy.{self.instrument}-{instance}'
def wrapper_file(self, cfg):
return Path(self.wrapperdir) / f'{cfg}_cfg.py'
def cfg_file(self, cfgdirs, service, cfg):
if '/' in cfg:
return Path(cfg)
cfgpy = f'{cfg}_cfg.py'
tries = []
services = [service] if service else list(self.services)
services.append('')
for servicedir in services:
for cfgdir in cfgdirs.split(':'):
cfgfile = Path(cfgdir) / servicedir / cfgpy
tries.append(cfgfile)
if cfgfile.is_file():
return servicedir, cfgfile
else:
raise FileNotFoundError(f'can not find {cfgpy} in {tries}')
def get_std_port(self, service):
if service == 'main':
return self.frappy_ports[0]
if service == 'stick':
return self.frappy_ports[1]
return self.frappy_ports[2:]
def get_local_ports(self):
self.get_cfg_info()
run_state = self.status('frappy')
result = {}
for host_port, cfg in self.cfg_info.items():
host, port = host_port.split(':')
if host == 'localhost':
busy, on, _ = run_state.get(cfg, (0,0,0))
if on or busy:
result.setdefault(port, []).append((on, busy, cfg))
return {sorted(v)[-1][-1]: k for k, v in result.items()}
def get_port(self, service):
if service not in {'main', 'stick', 'addons', 'addon'}:
raise ArgError('illegal service argument')
ports = self.get_std_port(service)
if isinstance(ports, int):
return ports
self.get_cfg_info()
used_ports = self.get_local_ports()
for port in ports:
if port not in used_ports:
return port
raise ValueError('too many frappy servers')
def add_frappy_service(self, service, cfg, port, log=None):
if log is None:
log = Logger('info')
log.info('add %r port=%r', cfg, port)
service, cfgfile = self.cfg_file(self.cfgdirs, service, cfg)
if not port:
if not service:
raise ArgError('service is not given and can not be determined from cfg file location')
port = self.get_port(service)
wrapper_content = WRAPPER_CFG.format(cfg=str(cfgfile), port=port)
cfgname = cfgfile.stem.removesuffix('_cfg')
self.wrapper_file(cfgname).write_text(wrapper_content)
self.get_cfg_info()
log.info('wrapper %r %r', self.wrapper_file(cfgname), wrapper_content)
self.reload()
log.info('registered %r', cfgname)
def get_cfg_info(self):
"""get info from wrapper dir"""
result = {}
for cfgfile in Path(self.wrapperdir).glob('*_cfg.py'):
cfg = cfgfile.stem[:-4]
match = WRAPPER_PAT.match(cfgfile.read_text())
if match:
result[f'localhost:{match.group(1)}'] = cfg
self.cfg_info = result
def delete_frappy_service(self, cfg):
try:
self.wrapper_file(cfg).unlink()
self.reload()
except FileNotFoundError:
pass
def _run(self, action, service=None, other=None):
cfg= other
if action == 'start':
self.add_frappy_service(service, cfg, None)
self.start(cfg)
elif action == 'restart':
self.restart(cfg)
elif action == 'stop':
self.stop(cfg)
else:
raise ArgError(f'unknown action {action!r}')