249 lines
8.7 KiB
Python
249 lines
8.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
# *****************************************************************************
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation; either version 2 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
# Module authors:
|
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
|
#
|
|
# *****************************************************************************
|
|
|
|
import sys
|
|
from os.path import expanduser, basename, join
|
|
from glob import glob
|
|
from configparser import ConfigParser
|
|
|
|
from nicos import session, config
|
|
from nicos.core import status
|
|
from nicos.utils import printTable
|
|
from nicos.commands import helparglist, usercommand
|
|
from nicos.commands.basic import AddSetup, CreateAllDevices, CreateDevice
|
|
from nicos.devices.secop.devices import get_attaching_devices
|
|
from nicos_sinq.frappy_sinq.devices import applyAliasConfig
|
|
|
|
home = expanduser('~')
|
|
if home not in sys.path:
|
|
# the first Frappy installations have /home/nicos in the PYTHONPATH in nicos.conf
|
|
# for newer Frappy installations this should be home (= /home/<instrument>)
|
|
# the following line fixes this in case nicos.conf is not yet updated
|
|
sys.path.append(home)
|
|
from nicos_sinq.frappy_sinq.devices import FrappyNode
|
|
from servicemanager import FrappyManager
|
|
|
|
|
|
def cleanup_defunct():
|
|
for devname, setupname in list(session.dynamic_devices.items()):
|
|
dev = session.devices.get(devname)
|
|
if dev and dev._defunct:
|
|
devnames = [d.name for d, _ in get_attaching_devices(dev)]
|
|
if devnames:
|
|
session.log.warning('can not remove device %r due to dependencies on %s'
|
|
% (devname, ', '.join(devnames)))
|
|
else:
|
|
session.destroyDevice(devname)
|
|
session.dynamic_devices.pop(devname, None)
|
|
|
|
|
|
SERVICES = FrappyManager.services
|
|
|
|
|
|
def all_info(all_cfg):
|
|
info = []
|
|
addkwd = False
|
|
for srv in SERVICES:
|
|
cfginfo = all_cfg.get(srv)
|
|
if cfginfo is None:
|
|
addkwd = True
|
|
elif addkwd:
|
|
info.append('%s=%r' % (srv, cfginfo))
|
|
else:
|
|
info.append(repr(cfginfo))
|
|
return 'currently configured: frappy(%s)' % ', '.join(info)
|
|
|
|
|
|
def frappy_start(**services):
|
|
"""start/stop frappy servers
|
|
|
|
for example: frappy_start(main='xy', stick='')
|
|
- restart main server with cfg='xy'
|
|
- stop stick server
|
|
- do not touch addons server
|
|
|
|
in addition, if a newly given cfg is already used on a running server,
|
|
this cfg is removed from the server (remark: cfg might be a comma separated list)
|
|
"""
|
|
frappy_config = session.devices.get('frappy_config')
|
|
for service in SERVICES:
|
|
if services.get(service) == '':
|
|
seaconn = session.devices.get('seaconn')
|
|
if seaconn and seaconn._attached_secnode:
|
|
seaconn.communicate('frappy_remove %s' % service)
|
|
used_cfg = {}
|
|
all_cfg = {}
|
|
new_cfg = []
|
|
remove_cfg = []
|
|
for service in SERVICES:
|
|
secnode = session.devices.get('se_' + service)
|
|
cfginfo = services.get(service)
|
|
if cfginfo is None:
|
|
if not secnode:
|
|
continue
|
|
cfginfo = secnode() or ''
|
|
secnode.restart(secnode.read(), False) # restart when needed only
|
|
else:
|
|
if cfginfo:
|
|
new_cfg.append((service, secnode, cfginfo))
|
|
else:
|
|
remove_cfg.append(secnode)
|
|
if secnode:
|
|
secnode('')
|
|
if secnode:
|
|
all_cfg[service] = secnode.get_info()
|
|
|
|
# check cfg is not used twice
|
|
for cfg in cfginfo.split(','):
|
|
cfg = cfg.strip()
|
|
if cfg:
|
|
prev = used_cfg.get(cfg)
|
|
if prev:
|
|
raise ValueError('%r can not be used in both %s and %s' % (cfg, prev, service))
|
|
used_cfg[cfg] = service
|
|
|
|
if new_cfg:
|
|
for service, secnode, cfginfo in new_cfg:
|
|
nodename = 'se_' + service
|
|
if not secnode:
|
|
AddSetup('frappy_' + service)
|
|
secnode = session.devices[nodename]
|
|
secnode(cfginfo)
|
|
all_cfg[service] = secnode.get_info()
|
|
CreateDevice(nodename)
|
|
cleanup_defunct()
|
|
CreateAllDevices()
|
|
if frappy_config:
|
|
frappy_config.set_envlist()
|
|
else:
|
|
applyAliasConfig()
|
|
for secnode in remove_cfg:
|
|
secnode.disable()
|
|
return all_cfg
|
|
|
|
|
|
@usercommand
|
|
def set_se_list():
|
|
frappy_config = session.devices['frappy_config']
|
|
frappy_config.set_envlist()
|
|
|
|
|
|
@usercommand
|
|
@helparglist('main [, stick [, addons]]')
|
|
def frappy(*args, main=None, stick=None, addons=None):
|
|
"""(re)start frappy server(s) with given configs and load setup if needed
|
|
|
|
- without argument: list running frappy servers, restart failed frappy servers
|
|
- frappy('<cfg>'): if available, the standard stick is added too
|
|
- frappy(''): the stick is removed too
|
|
- addons are not changed when not given
|
|
- frappy(main='<cfg>') # main cfg is changed, but stick is kept
|
|
"""
|
|
if args:
|
|
if main is not None:
|
|
raise TypeError('got multiple values for main')
|
|
main = args[0]
|
|
if len(args) == 1: # special case: main given as single argument
|
|
if stick is None: # auto stick
|
|
if main == '':
|
|
stick = '' # remove stick with main
|
|
else:
|
|
stickcfg = main + 'stick'
|
|
if stickcfg in FrappyNode.available_cfg('stick'):
|
|
# if a default stick is available, start this also
|
|
stick = stickcfg
|
|
else:
|
|
if stick is not None:
|
|
raise TypeError('got multiple values for stick')
|
|
stick, *alist = args[1:]
|
|
if alist:
|
|
if addons is not None:
|
|
raise TypeError('got multiple values for addons')
|
|
addons = ','.join(alist)
|
|
all_cfg = frappy_start(main=main, stick=stick, addons=addons)
|
|
session.log.info(all_info(all_cfg))
|
|
|
|
|
|
@usercommand
|
|
@helparglist('cfg')
|
|
def frappy_main(cfg=None):
|
|
"""(re)start frappy_main server with given cfg and load setup if needed
|
|
|
|
- without argument: list running frappy servers
|
|
- cfg = "": stop frappy_main server
|
|
"""
|
|
session.log.info(all_info(frappy_start(main=cfg)))
|
|
|
|
|
|
@usercommand
|
|
@helparglist('cfg')
|
|
def frappy_stick(cfg=None):
|
|
"""(re)start frappy_stick server with given cfg and load setup if needed
|
|
|
|
- without argument: list running frappy servers
|
|
- cfg = "": stop frappy_stick server
|
|
"""
|
|
session.log.info(all_info(frappy_start(stick=cfg)))
|
|
|
|
|
|
@usercommand
|
|
@helparglist('cfg,...')
|
|
def frappy_addons(cfg=None):
|
|
"""(re)start frappy_addons server with given cfg and load setup if needed
|
|
|
|
- without argument: list running frappy servers
|
|
- cfg = "": stop frappy_addons server
|
|
"""
|
|
session.log.info(all_info(frappy_start(addons=cfg)))
|
|
|
|
|
|
@usercommand
|
|
@helparglist('')
|
|
def frappy_list(service=None):
|
|
"""list available configuration files"""
|
|
table = []
|
|
bases = list(dict.fromkeys(expanduser(p) for p in FrappyNode.config_dirs(config.instrument, service or 'main')))
|
|
if service is None:
|
|
session.log.info('Available configuration files')
|
|
session.log.info(' ')
|
|
session.log.info('Hint: if no config file can be found which matches your needs exactly')
|
|
session.log.info('make a copy of an existing one, and change the description accordingly')
|
|
session.log.info(' ')
|
|
session.log.info('Usage (default argument "main"):')
|
|
session.log.info(' ')
|
|
printTable(['command'], [['frappy_list(%r)' % s] for s in SERVICES], session.log.info)
|
|
session.log.info(' ')
|
|
for cfgdir in bases:
|
|
table.append(['---\n', cfgdir])
|
|
for cfgfile in glob(join(cfgdir, '*.cfg')):
|
|
parser = ConfigParser()
|
|
parser.read(cfgfile)
|
|
desc = ''
|
|
for s in parser.sections():
|
|
if s == 'NODE' or s.startswith('node '):
|
|
desc = parser[s].get('description', '').split('\n')[0]
|
|
break
|
|
table.append([basename(cfgfile)[:-4], desc])
|
|
printTable(['cfg file', 'description'], table, session.log.info)
|
|
|
|
|