improve frappy server management
- do connect in background when the frappy server is not running on startup
This commit is contained in:
222
devices.py
222
devices.py
@ -86,32 +86,31 @@ def cleanup_defunct():
|
||||
|
||||
|
||||
def all_info(all_cfg, prefix='currently configured: '):
|
||||
info = []
|
||||
addkwd = False
|
||||
info = []
|
||||
for srv in SERVICES:
|
||||
cfginfo = all_cfg.get(srv)
|
||||
if cfginfo is None:
|
||||
cfglist = all_cfg.get(srv)
|
||||
if cfglist is None:
|
||||
addkwd = True
|
||||
elif addkwd:
|
||||
info.append('%s=%r' % (srv, cfginfo))
|
||||
else:
|
||||
info.append(repr(cfginfo))
|
||||
if isinstance(cfglist, str):
|
||||
cfglist = [cfglist]
|
||||
cfginfo = ','.join(c if isinstance(c, str)
|
||||
else f"<one of {', '.join(repr(v) for v in c)}>"
|
||||
for c in cfglist)
|
||||
if addkwd:
|
||||
info.append('%s=%r' % (srv, cfginfo))
|
||||
else:
|
||||
info.append(repr(cfginfo))
|
||||
return f"{prefix}frappy({', '.join(info)})"
|
||||
|
||||
|
||||
def get_guess(allcfg):
|
||||
guess = {}
|
||||
seacfg = FrappyManager().cfg_from_sea(config.instrument)
|
||||
guess.pop('confirmed', '')
|
||||
for s in ('main', 'stick', 'addons'):
|
||||
info = allcfg.get(s, '')
|
||||
prev = info.split(' ', 1)[0]
|
||||
if prev != info:
|
||||
guess[s] = prev
|
||||
fromsea = seacfg.get(s)
|
||||
if fromsea and fromsea != prev:
|
||||
guess[s] = fromsea
|
||||
return guess
|
||||
def get_frappy_config():
|
||||
try:
|
||||
return session.devices['frappy_config']
|
||||
except KeyError:
|
||||
session.log.error("'frappy_config' is not available - 'frappy' setup is not loaded")
|
||||
return None
|
||||
|
||||
|
||||
class FrappyConfig(Device):
|
||||
@ -141,10 +140,16 @@ class FrappyConfig(Device):
|
||||
meanings.remove('nodes')
|
||||
_trigger_change = None
|
||||
_previous_shown = None
|
||||
_initial_config = None
|
||||
_servers_loaded = False
|
||||
|
||||
def doInit(self, mode):
|
||||
if mode != SIMULATION and session.sessiontype != POLLER:
|
||||
self._trigger_change = threading.Event()
|
||||
for name in self.nodes:
|
||||
secnode = session.devices.get(name)
|
||||
if secnode:
|
||||
secnode.uri = ''
|
||||
createThread('frappy change notification', self.handle_notifications)
|
||||
|
||||
def handle_notifications(self):
|
||||
@ -156,17 +161,42 @@ class FrappyConfig(Device):
|
||||
if self._trigger_change.is_set():
|
||||
continue
|
||||
try:
|
||||
current = self.check_or_start()
|
||||
if current != self._previous_shown:
|
||||
cfgs = self.check_services()
|
||||
guess_info = self.to_consider(cfgs)
|
||||
if (cfgs, guess_info) != self._previous_shown and (guess_info or not self._servers_loaded):
|
||||
cmd = 'frappy_changed()'
|
||||
controller.new_request(ScriptRequest(cmd, None, User('guest', USER)))
|
||||
except RequestError as e:
|
||||
session.log.error(f'can not queue request {e!r}')
|
||||
|
||||
def check_or_start(self, main=None, stick=None, addons=None):
|
||||
def to_consider(self, cfgs):
|
||||
"""return info from sea and frappy servers
|
||||
|
||||
for a potential "please consider calling frappy(...)" message
|
||||
"""
|
||||
guess_info = {}
|
||||
for service, guess in FrappyManager().guess_cfgs(config.instrument, cfgs).items():
|
||||
proposed = guess.get('proposed')
|
||||
if proposed:
|
||||
guess_info[service] = proposed
|
||||
else:
|
||||
missing = guess.get('missing')
|
||||
if missing:
|
||||
guess_info[service] = [m + '?' for m in missing]
|
||||
return guess_info
|
||||
|
||||
def check_services(self):
|
||||
cfgs = {}
|
||||
for secnodename in self.nodes:
|
||||
secnode = session.devices.get(secnodename)
|
||||
if secnode:
|
||||
cfgs[secnode.service] = secnode.get_info()
|
||||
return cfgs
|
||||
|
||||
def start_services(self, main=None, stick=None, addons=None):
|
||||
"""start/stop frappy servers
|
||||
|
||||
for example: check_or_start(main='xy', stick='')
|
||||
for example: start_services(main='xy', stick='')
|
||||
- restart main server with cfg='xy'
|
||||
- stop stick server
|
||||
- do not touch addons server
|
||||
@ -231,26 +261,78 @@ class FrappyConfig(Device):
|
||||
self.set_envlist()
|
||||
for secnode in remove_cfg:
|
||||
secnode.disable()
|
||||
return all_cfg, get_guess(all_cfg)
|
||||
self._cache.put(self, 'config', all_cfg)
|
||||
return all_cfg
|
||||
|
||||
def show_config(self, allcfg, guess):
|
||||
def show_config(self, allcfg):
|
||||
guess_info = self.to_consider(allcfg)
|
||||
# remove 'frappy_changed()' commands in script queue
|
||||
controller = session.daemon_device._controller
|
||||
controller.block_requests(r['reqid'] for r in controller.get_queue() if r['script'] == 'frappy_changed()')
|
||||
self._previous_shown = allcfg, guess
|
||||
self._previous_shown = allcfg, guess_info
|
||||
session.log.info(all_info(allcfg))
|
||||
if guess:
|
||||
info = all_info(guess, '')
|
||||
|
||||
if guess_info:
|
||||
info = all_info(guess_info, '')
|
||||
session.log.warning('please consider to call:')
|
||||
session.log.info(info)
|
||||
if '?' in info:
|
||||
session.log.warning("but create cfg files first for items marked with '?'")
|
||||
|
||||
def initial_restart_cfg(self, service):
|
||||
"""get cfg for (re)start of the service
|
||||
|
||||
returns:
|
||||
cfg, when the server has to (re)started with a new cfg
|
||||
True, when the server is running and does not need a restart
|
||||
None, when the server is not running, but does not need a restart
|
||||
"""
|
||||
if self._servers_loaded:
|
||||
return None
|
||||
if self._initial_config is None:
|
||||
success = True
|
||||
fm = FrappyManager()
|
||||
running = fm.get_cfg(config.instrument, None)
|
||||
cache = self._getCache()
|
||||
cfgs = {}
|
||||
for serv, secnode in zip(fm.services, self.nodes):
|
||||
cfg = running.get(serv)
|
||||
if not cfg and cache:
|
||||
cfg = cache.get(secnode, 'value')
|
||||
if cfg:
|
||||
cfgs[serv] = cfg
|
||||
running_main = running.get('main')
|
||||
if running_main and running_main != cfgs.get('main'):
|
||||
# new main device: clear old stick
|
||||
running_stick = running.get('stick')
|
||||
if running_stick:
|
||||
cfgs['stick'] = running_stick
|
||||
else:
|
||||
cfgs.pop('stick', None)
|
||||
for serv, guess in fm.guess_cfgs(config.instrument, cfgs).items():
|
||||
ok = guess.get('ok', [])
|
||||
for proposed in guess.get('proposed', []):
|
||||
if isinstance(proposed, str): # start only when ambiguous
|
||||
ok.append(proposed)
|
||||
else:
|
||||
success = False
|
||||
if ok:
|
||||
cfgs[serv] = ','.join(ok)
|
||||
if not success:
|
||||
cfgs = {}
|
||||
result = {}
|
||||
for serv, cfg in cfgs.items():
|
||||
runcfg = running.get(serv)
|
||||
if runcfg != cfg:
|
||||
result[serv] = cfg
|
||||
elif runcfg:
|
||||
result[serv] = True # restart not needed as cfg has not changed
|
||||
self._initial_config = result
|
||||
return self._initial_config.get(service)
|
||||
|
||||
def changed(self):
|
||||
current = self.check_or_start()
|
||||
#if current == self._previous_shown:
|
||||
# return
|
||||
self.show_config(*current)
|
||||
self._servers_loaded = True
|
||||
self.show_config(self.check_services())
|
||||
|
||||
def remove_aliases(self):
|
||||
for meaning in self.meanings:
|
||||
@ -279,7 +361,7 @@ class FrappyConfig(Device):
|
||||
return result
|
||||
|
||||
def set_envlist(self):
|
||||
"""create aliases for SECoP devices
|
||||
"""create aliases and envlist for SECoP devices
|
||||
|
||||
depending on their meaning
|
||||
"""
|
||||
@ -355,7 +437,7 @@ class FrappyConfig(Device):
|
||||
newenv[devname] = aliasname
|
||||
break
|
||||
else:
|
||||
to_remove.union(aliasnames)
|
||||
to_remove.update(aliasnames)
|
||||
|
||||
for aliasname in previous_aliases:
|
||||
session.destroyDevice(aliasname)
|
||||
@ -397,17 +479,30 @@ class FrappyNode(SecNodeDevice, Moveable):
|
||||
def doStart(self, value):
|
||||
if value == 'None':
|
||||
value = None
|
||||
self.restart(value, True) # frappy server will be restarted even when unchanged
|
||||
self.restart(value)
|
||||
|
||||
def doInit(self, mode):
|
||||
if mode == SIMULATION or session.sessiontype == POLLER:
|
||||
super().doInit(mode)
|
||||
else:
|
||||
fc = session.devices.get('frappy_config')
|
||||
if fc:
|
||||
cfg = fc.initial_restart_cfg(self.service)
|
||||
if isinstance(cfg, str): # may also be None or True
|
||||
self.restart(cfg)
|
||||
if cfg is None: # None means: server is not running, and does not need to be restarted
|
||||
# connect in background, as the server might be started later
|
||||
createThread('connect', self._connect)
|
||||
# TODO: check if it is not better to add a try_period argument to SecNode._connect()
|
||||
return
|
||||
try:
|
||||
self._connect()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def doStop(self):
|
||||
"""never busy"""
|
||||
|
||||
def doInit(self, mode):
|
||||
if mode != SIMULATION and session.sessiontype != POLLER:
|
||||
self._lastcfg = self.doRead()
|
||||
self.restart(self._lastcfg, False) # do not restart when not changed
|
||||
super().doInit(mode)
|
||||
|
||||
def doRead(self, maxage=0):
|
||||
return self._cfgvalue or ''
|
||||
|
||||
@ -415,7 +510,9 @@ class FrappyNode(SecNodeDevice, Moveable):
|
||||
cfg = self.read()
|
||||
super().createDevices()
|
||||
if cfg != self._lastcfg:
|
||||
session.devices.get('frappy_config')._trigger_change.set()
|
||||
fc = get_frappy_config()
|
||||
if fc:
|
||||
fc._trigger_change.set()
|
||||
if self.param_category:
|
||||
for devname, (_, devcfg) in self.setup_info.items():
|
||||
params_cfg = devcfg['params_cfg']
|
||||
@ -437,10 +534,9 @@ class FrappyNode(SecNodeDevice, Moveable):
|
||||
cfg = self.read()
|
||||
if self._lastcfg != cfg:
|
||||
self._lastcfg = cfg
|
||||
try:
|
||||
session.devices.get('frappy_config')._trigger_change.set()
|
||||
except AttributeError: # frappy_config does not exist
|
||||
pass
|
||||
fc = get_frappy_config()
|
||||
if fc:
|
||||
fc._trigger_change.set()
|
||||
|
||||
def disable(self):
|
||||
seaconn = session.devices.get('sea_%s' % self.service)
|
||||
@ -453,13 +549,10 @@ class FrappyNode(SecNodeDevice, Moveable):
|
||||
code, text = status.DISABLED, 'disabled'
|
||||
SecNodeDevice._set_status(self, code, text)
|
||||
|
||||
def restart(self, cfg=None, forced=True):
|
||||
def restart(self, cfg=None):
|
||||
"""restart frappy server
|
||||
|
||||
:param cfg: config for frappy server, if not given, restart with the same config
|
||||
:param forced: True: restart anyway, force using cfg
|
||||
False: try to get cfg (1) from sea, (2) from running frappy server, (3) from given cfg
|
||||
when cfg has not changed, do not restart
|
||||
"""
|
||||
if cfg is None:
|
||||
cfg = self._cfgvalue
|
||||
@ -467,25 +560,8 @@ class FrappyNode(SecNodeDevice, Moveable):
|
||||
fm = FrappyManager()
|
||||
info = fm.get_ins_info(ins)
|
||||
running_cfg = fm.get_cfg(ins, self.service) or ''
|
||||
if not forced or cfg is None:
|
||||
sea_cfg = fm.cfg_from_sea(ins).get(self.service, '')
|
||||
if '?' in sea_cfg:
|
||||
if sea_cfg == '?':
|
||||
self.log.warning('undefined sea device')
|
||||
else:
|
||||
self.log.warning(f"missing frappy cfg file for {sea_cfg.replace('?', '')}")
|
||||
cfg = '' # stop server
|
||||
elif sea_cfg:
|
||||
cfg = sea_cfg
|
||||
elif running_cfg:
|
||||
self._cfgvalue = running_cfg
|
||||
return
|
||||
if cfg == running_cfg:
|
||||
self._cfgvalue = running_cfg
|
||||
return
|
||||
if cfg is None:
|
||||
if forced:
|
||||
self.log.error('can not restart - previous cfg unknown')
|
||||
self.log.error('can not restart - previous cfg unknown')
|
||||
return
|
||||
if cfg != running_cfg:
|
||||
self.disable()
|
||||
@ -494,15 +570,15 @@ class FrappyNode(SecNodeDevice, Moveable):
|
||||
fm.do_stop(ins, self.service)
|
||||
is_cfg = cfg and ':' not in cfg
|
||||
if is_cfg:
|
||||
available_cfg = FrappyManager().all_cfg(config.instrument, self.service)
|
||||
failed = False
|
||||
available_cfg = None
|
||||
for cfgitem in cfg.split(','):
|
||||
if cfgitem not in available_cfg:
|
||||
failed = True
|
||||
if not fm.is_cfg(config.instrument, self.service, cfgitem):
|
||||
if available_cfg is None:
|
||||
available_cfg = fm.all_cfg(config.instrument, self.service)
|
||||
suggestions = suggest(cfgitem, available_cfg)
|
||||
if suggestions:
|
||||
session.log.error('%s unknown, did you mean: %s' % (cfgitem, ', '.join(suggestions)))
|
||||
if failed:
|
||||
if available_cfg is not None:
|
||||
raise ValueError('use "frappy_list()" to get a list of valid frappy configurations')
|
||||
uri = 'localhost:%d' % info[self.service]
|
||||
else:
|
||||
|
Reference in New Issue
Block a user