improve detect_changes
check also if envlist and aliases have to be rebuilt
This commit is contained in:
143
devices.py
143
devices.py
@ -144,6 +144,9 @@ class FrappyConfig(Device):
|
|||||||
_shutdown_event = None
|
_shutdown_event = None
|
||||||
_restarting = False
|
_restarting = False
|
||||||
_within_update_setup = False
|
_within_update_setup = False
|
||||||
|
_current_cfgs = None
|
||||||
|
_target_cfgs = None
|
||||||
|
_rebuild_env = None
|
||||||
|
|
||||||
def doInit(self, mode):
|
def doInit(self, mode):
|
||||||
if mode != SIMULATION and session.sessiontype != POLLER:
|
if mode != SIMULATION and session.sessiontype != POLLER:
|
||||||
@ -160,15 +163,23 @@ class FrappyConfig(Device):
|
|||||||
def detect_changes(self):
|
def detect_changes(self):
|
||||||
before_check = before_change = prev_shown = None
|
before_check = before_change = prev_shown = None
|
||||||
cnt = 0
|
cnt = 0
|
||||||
|
normal_state = None
|
||||||
while not self._shutdown_event.wait(1):
|
while not self._shutdown_event.wait(1):
|
||||||
busy = bool(session.experiment.scripts or session.daemon_device._controller.queue.scripts)
|
busy = bool(session.experiment.scripts or session.daemon_device._controller.queue.scripts)
|
||||||
if busy and cnt < 10:
|
if busy and cnt < 10:
|
||||||
# check only every 10 sec when busy
|
# check only every 10 sec when busy
|
||||||
cnt += 1
|
cnt += 1
|
||||||
continue
|
continue
|
||||||
|
if not busy and normal_state == 'back_to_normal':
|
||||||
|
session.log.warning(all_info(self._current_cfgs, 'servers match configuration: '))
|
||||||
|
normal_state = None
|
||||||
cnt = 0
|
cnt = 0
|
||||||
need_change, changes, fm = self.to_consider()
|
need_change, changes, fm = self.to_consider()
|
||||||
if fm.state == before_change or not need_change:
|
if fm.state == before_change:
|
||||||
|
continue
|
||||||
|
if not need_change:
|
||||||
|
if normal_state == 'changed':
|
||||||
|
normal_state = 'back_to_normal'
|
||||||
continue
|
continue
|
||||||
if fm.state != before_check:
|
if fm.state != before_check:
|
||||||
# must be twice the same
|
# must be twice the same
|
||||||
@ -176,27 +187,36 @@ class FrappyConfig(Device):
|
|||||||
continue
|
continue
|
||||||
if busy or fm.state == prev_shown:
|
if busy or fm.state == prev_shown:
|
||||||
continue
|
continue
|
||||||
|
normal_state = 'changed'
|
||||||
self.show_changes(changes)
|
self.show_changes(changes)
|
||||||
prev_shown = fm.state
|
prev_shown = fm.state
|
||||||
|
|
||||||
def show_changes(self, changes):
|
def show_changes(self, changes):
|
||||||
if changes is None:
|
if changes is None:
|
||||||
need_change, changes, fm = self.to_consider()
|
need_change, changes, fm = self.to_consider()
|
||||||
session.log.warning('sample environment has changed:')
|
session.log.info(' ')
|
||||||
args = []
|
session.log.warning('sample environment configuration should be changed:')
|
||||||
|
session.log.info(' ')
|
||||||
|
kwargs = {}
|
||||||
for service, cfg in changes.items():
|
for service, cfg in changes.items():
|
||||||
arg = str(cfg)
|
kwargs[service] = str(cfg)
|
||||||
|
prev = self._target_cfgs.get(service)
|
||||||
if cfg:
|
if cfg:
|
||||||
if isinstance(cfg, Keep):
|
if isinstance(cfg, Keep):
|
||||||
arg = f'={cfg}'
|
kwargs.pop(service, None)
|
||||||
session.log.info('keep %s: %s', service, cfg)
|
session.log.info('%s: keep %s', service, cfg)
|
||||||
else:
|
else:
|
||||||
session.log.warning('change %s to %s', service, cfg)
|
if prev:
|
||||||
|
session.log.info('%s: change from %s to %s', service, prev, cfg)
|
||||||
|
else:
|
||||||
|
session.log.info('%s: start as %s', service, cfg)
|
||||||
elif cfg == '':
|
elif cfg == '':
|
||||||
session.log.warning('remove %s', service)
|
session.log.info('%s: remove %s', service, prev)
|
||||||
args.append(arg)
|
if self._rebuild_env:
|
||||||
args = tuple(args)
|
session.log.info('%s', self._rebuild_env)
|
||||||
session.log.warning('use frappy%r or frappy.update() to activate', args)
|
alternative = f" or {all_info(kwargs, '')}" if kwargs else ''
|
||||||
|
session.log.info(' ')
|
||||||
|
session.log.info('use frappy.update()%s to activate', alternative)
|
||||||
|
|
||||||
def to_consider(self, cfgs=None):
|
def to_consider(self, cfgs=None):
|
||||||
"""return info about a proposed changes
|
"""return info about a proposed changes
|
||||||
@ -206,37 +226,48 @@ class FrappyConfig(Device):
|
|||||||
|
|
||||||
the information takes into account running frappy and sea servers
|
the information takes into account running frappy and sea servers
|
||||||
and their configuration
|
and their configuration
|
||||||
|
|
||||||
|
side effect: try to reconnect a frappy server which should run with
|
||||||
|
the target cfg
|
||||||
"""
|
"""
|
||||||
current_cfgs, target_cfgs = self.check_services()
|
current_cfgs, target_cfgs = self.check_services()
|
||||||
if cfgs is None:
|
if cfgs is None:
|
||||||
cfgs = current_cfgs
|
cfgs = current_cfgs
|
||||||
self._current_cfgs = cfgs
|
|
||||||
fm = FrappyManager()
|
fm = FrappyManager()
|
||||||
proposed = fm.get_server_state(config.instrument, cfgs)
|
proposed = fm.get_server_state(config.instrument, cfgs)
|
||||||
changes = dict(proposed)
|
changes = dict(proposed)
|
||||||
for service, cfg in proposed.items():
|
|
||||||
if cfg == 'reconnect': # means: server is running, no restart needed
|
|
||||||
if service in current_cfgs:
|
|
||||||
changes.pop(service)
|
|
||||||
need_change = False
|
need_change = False
|
||||||
for service in SERVICES:
|
for service in SERVICES:
|
||||||
cfg = changes.get(service) # proposed cfg
|
cfg = changes.get(service) # proposed cfg
|
||||||
info = cfgs.get(service) # running cfg
|
running = fm.frappy_cfgs.get(service) # running cfg
|
||||||
if not info:
|
if running:
|
||||||
|
prop = changes.get(service)
|
||||||
|
if prop:
|
||||||
|
if prop == target_cfgs.get(service) == running:
|
||||||
|
secnode = session.devices.get('se_' + service)
|
||||||
|
if secnode:
|
||||||
|
if secnode._secnode:
|
||||||
|
secnode._secnode.connect()
|
||||||
|
else:
|
||||||
|
secnode._connect()
|
||||||
|
else:
|
||||||
|
changes[service] = ''
|
||||||
|
need_change = True
|
||||||
|
cfg = ''
|
||||||
|
else:
|
||||||
if cfg == '':
|
if cfg == '':
|
||||||
changes.pop(service)
|
changes.pop(service)
|
||||||
cfg = None
|
cfg = None
|
||||||
if target_cfgs.get(service):
|
if target_cfgs.get(service):
|
||||||
need_change = True
|
need_change = 1
|
||||||
elif info == '<disconnected>':
|
|
||||||
if not changes.get(service):
|
|
||||||
changes[service] = ''
|
|
||||||
need_change = True
|
|
||||||
cfg = ''
|
|
||||||
if cfg and not isinstance(cfg, Keep):
|
if cfg and not isinstance(cfg, Keep):
|
||||||
|
need_change = cfg
|
||||||
|
if not need_change and all(isinstance(v, Keep) for v in changes.values()):
|
||||||
|
self.set_envlist(checkonly=True)
|
||||||
|
if self._rebuild_env:
|
||||||
need_change = True
|
need_change = True
|
||||||
# elif target_cfgs.get(service) != cfgs.get(service):
|
else:
|
||||||
# need_change = True
|
self._rebuild_env = None
|
||||||
return need_change, changes, fm
|
return need_change, changes, fm
|
||||||
|
|
||||||
def check_services(self):
|
def check_services(self):
|
||||||
@ -247,6 +278,8 @@ class FrappyConfig(Device):
|
|||||||
if secnode:
|
if secnode:
|
||||||
cfgs[secnode.service] = secnode.get_info()
|
cfgs[secnode.service] = secnode.get_info()
|
||||||
targets[secnode.service] = secnode.target
|
targets[secnode.service] = secnode.target
|
||||||
|
self._current_cfgs = cfgs
|
||||||
|
self._target_cfgs = targets
|
||||||
return cfgs, targets
|
return cfgs, targets
|
||||||
|
|
||||||
def start_services(self, main=None, stick=None, addons=None,):
|
def start_services(self, main=None, stick=None, addons=None,):
|
||||||
@ -265,7 +298,6 @@ class FrappyConfig(Device):
|
|||||||
self._restarting = True
|
self._restarting = True
|
||||||
try:
|
try:
|
||||||
services = {'main': main, 'stick': stick, 'addons': addons}
|
services = {'main': main, 'stick': stick, 'addons': addons}
|
||||||
to_reconnect = {}
|
|
||||||
for service, cfg in services.items():
|
for service, cfg in services.items():
|
||||||
if cfg == '':
|
if cfg == '':
|
||||||
seaconn = session.devices.get(f'se_sea_{service}')
|
seaconn = session.devices.get(f'se_sea_{service}')
|
||||||
@ -286,8 +318,6 @@ class FrappyConfig(Device):
|
|||||||
all_cfg[service] = chkinfo = secnode.get_info()
|
all_cfg[service] = chkinfo = secnode.get_info()
|
||||||
if cfginfo is not None and (cfginfo != chkinfo or not isinstance(cfginfo, Reconnect)):
|
if cfginfo is not None and (cfginfo != chkinfo or not isinstance(cfginfo, Reconnect)):
|
||||||
new_cfg[service] = chkinfo = cfginfo
|
new_cfg[service] = chkinfo = cfginfo
|
||||||
if secnode:
|
|
||||||
to_reconnect[service] = secnode
|
|
||||||
|
|
||||||
# check cfg is not used twice
|
# check cfg is not used twice
|
||||||
for cfg in chkinfo.split(','):
|
for cfg in chkinfo.split(','):
|
||||||
@ -315,13 +345,13 @@ class FrappyConfig(Device):
|
|||||||
AddSetup('frappy_' + service)
|
AddSetup('frappy_' + service)
|
||||||
secnode = session.devices[nodename]
|
secnode = session.devices[nodename]
|
||||||
secnode(cfginfo)
|
secnode(cfginfo)
|
||||||
to_reconnect.pop(service, None)
|
|
||||||
all_cfg[service] = secnode.get_info()
|
all_cfg[service] = secnode.get_info()
|
||||||
CreateDevice(nodename)
|
CreateDevice(nodename)
|
||||||
cleanup_defunct()
|
cleanup_defunct()
|
||||||
CreateAllDevices()
|
CreateAllDevices()
|
||||||
for secnode in to_reconnect.values():
|
for service, secnode in secnodes.items():
|
||||||
secnode._secnode.connect()
|
if services.get(service):
|
||||||
|
secnode._secnode.connect()
|
||||||
self.set_envlist()
|
self.set_envlist()
|
||||||
for secnode in remove_cfg:
|
for secnode in remove_cfg:
|
||||||
secnode.disable()
|
secnode.disable()
|
||||||
@ -404,11 +434,12 @@ class FrappyConfig(Device):
|
|||||||
if need_change:
|
if need_change:
|
||||||
self.show_changes(changes)
|
self.show_changes(changes)
|
||||||
|
|
||||||
def update(self, *args):
|
def update(self, main=None, stick=None, addons=None):
|
||||||
if args:
|
if main is None and stick is None and addons is None:
|
||||||
changes = {k: (Keep(v[1:]) if v.startswith('=') else v) for k, v in zip(SERVICES, args) if v is not None}
|
|
||||||
else:
|
|
||||||
changes = self.to_consider()[1]
|
changes = self.to_consider()[1]
|
||||||
|
else:
|
||||||
|
changes = {k: Keep(v) for k, v in zip(SERVICES, (main, stick, addons))
|
||||||
|
if v is not None}
|
||||||
self.show_config(self.start_services(**changes))
|
self.show_config(self.start_services(**changes))
|
||||||
|
|
||||||
def get_init_info(self, service):
|
def get_init_info(self, service):
|
||||||
@ -457,7 +488,7 @@ class FrappyConfig(Device):
|
|||||||
result[aliasname] = aliasdev
|
result[aliasname] = aliasdev
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def set_envlist(self):
|
def set_envlist(self, checkonly=False):
|
||||||
"""create aliases and envlist for SECoP devices
|
"""create aliases and envlist for SECoP devices
|
||||||
|
|
||||||
depending on their meaning
|
depending on their meaning
|
||||||
@ -511,6 +542,18 @@ class FrappyConfig(Device):
|
|||||||
dev = session.devices.get(devname)
|
dev = session.devices.get(devname)
|
||||||
if dev is None or info.get('drivable_only', False) and not isinstance(dev, Moveable):
|
if dev is None or info.get('drivable_only', False) and not isinstance(dev, Moveable):
|
||||||
continue
|
continue
|
||||||
|
if checkonly:
|
||||||
|
if aliasnames and devname not in newenv:
|
||||||
|
newenv[devname] = aliasnames[0]
|
||||||
|
for aliasname in aliasnames:
|
||||||
|
adev = previous_aliases.pop(aliasname, None)
|
||||||
|
if not adev:
|
||||||
|
self._rebuild_env = f'need alias {aliasname} for {devname}'
|
||||||
|
return
|
||||||
|
if adev.alias != devname:
|
||||||
|
self._rebuild_env = f'alias {aliasname} must point to {devname}'
|
||||||
|
return
|
||||||
|
continue
|
||||||
for aliasname in aliasnames:
|
for aliasname in aliasnames:
|
||||||
devcfg = ('nicos.core.DeviceAlias', {})
|
devcfg = ('nicos.core.DeviceAlias', {})
|
||||||
session.configured_devices[aliasname] = devcfg
|
session.configured_devices[aliasname] = devcfg
|
||||||
@ -526,6 +569,10 @@ class FrappyConfig(Device):
|
|||||||
session.cache.put(aliasname, 'description', dev.description)
|
session.cache.put(aliasname, 'description', dev.description)
|
||||||
aliasdev = session.createDevice(aliasname, recreate=True, explicit=True)
|
aliasdev = session.createDevice(aliasname, recreate=True, explicit=True)
|
||||||
aliasdev.alias = devname
|
aliasdev.alias = devname
|
||||||
|
# make sure device panel is updated
|
||||||
|
dev = aliasdev._obj
|
||||||
|
dev._cache.put(dev, 'status', dev.status())
|
||||||
|
dev._cache.put(dev, 'value', dev.read())
|
||||||
if aliasnames:
|
if aliasnames:
|
||||||
# only the first item of aliasnames is added to the envlist
|
# only the first item of aliasnames is added to the envlist
|
||||||
aliasname = aliasnames[0]
|
aliasname = aliasnames[0]
|
||||||
@ -539,14 +586,25 @@ class FrappyConfig(Device):
|
|||||||
else:
|
else:
|
||||||
to_remove.update(aliasnames)
|
to_remove.update(aliasnames)
|
||||||
|
|
||||||
for aliasname in previous_aliases:
|
if checkonly:
|
||||||
session.destroyDevice(aliasname)
|
if previous_aliases:
|
||||||
session.configured_devices.pop(aliasname, None)
|
self._rebuild_env = f'need to remove aliases {", ".join(previous_aliases)}'
|
||||||
session.dynamic_devices.pop(aliasname, None)
|
return
|
||||||
|
else:
|
||||||
|
for aliasname in previous_aliases:
|
||||||
|
session.destroyDevice(aliasname)
|
||||||
|
session.configured_devices.pop(aliasname, None)
|
||||||
|
session.dynamic_devices.pop(aliasname, None)
|
||||||
|
|
||||||
applyAliasConfig() # for other aliases
|
applyAliasConfig() # for other aliases
|
||||||
|
|
||||||
envlist = [k for k in session.experiment.envlist if k not in to_remove] + list(newenv.values())
|
envlist = [k for k in session.experiment.envlist if k not in to_remove] + list(newenv.values())
|
||||||
|
if checkonly:
|
||||||
|
if set(envlist) != set(session.experiment.envlist):
|
||||||
|
self._rebuild_env = f'need to update envlist to {envlist}'
|
||||||
|
else:
|
||||||
|
self._rebuild_env = None
|
||||||
|
return
|
||||||
if envlist != session.experiment.envlist:
|
if envlist != session.experiment.envlist:
|
||||||
removed = set(session.experiment.envlist).difference(envlist)
|
removed = set(session.experiment.envlist).difference(envlist)
|
||||||
session.experiment.setEnvironment(envlist)
|
session.experiment.setEnvironment(envlist)
|
||||||
@ -645,7 +703,6 @@ class FrappyNode(SecNodeDevice, Moveable):
|
|||||||
self.__log_recording = ()
|
self.__log_recording = ()
|
||||||
|
|
||||||
def nodeStateChange(self, online, state):
|
def nodeStateChange(self, online, state):
|
||||||
print(f'NODE {self.service} {online} {state}')
|
|
||||||
# self.log.info('NODE %r %r', online, state)
|
# self.log.info('NODE %r %r', online, state)
|
||||||
if online:
|
if online:
|
||||||
super().nodeStateChange(online, state)
|
super().nodeStateChange(online, state)
|
||||||
|
Reference in New Issue
Block a user