initial version
This commit is contained in:
parent
c5f9c12e98
commit
61ba9336b8
@ -1,3 +1,5 @@
|
|||||||
# servman
|
# servman
|
||||||
|
|
||||||
A manager for starting, stopping and listing services/servers like frappy, nicos and sea.
|
A manager for starting, stopping and listing services/servers like frappy, nicos and sea.
|
||||||
|
|
||||||
|
Several instances of nicos, frappy and sea might run on the same machine.
|
||||||
|
394
__init__.py
Normal file
394
__init__.py
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
# -*- 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>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
"""start/stop/list of services
|
||||||
|
|
||||||
|
this code is currently used:
|
||||||
|
|
||||||
|
- from NICOS to start and stop frappy servers
|
||||||
|
- from a script allowing to start/stop/list (and more) multiple frappy and nicos servers
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from os.path import expanduser
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import psutil
|
||||||
|
from collections import OrderedDict, defaultdict
|
||||||
|
from configparser import ConfigParser
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceDown(Exception):
|
||||||
|
"""the service is not running"""
|
||||||
|
|
||||||
|
|
||||||
|
class UsageError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def printTable(headers, items, printfunc, minlen=0, rjust=False):
|
||||||
|
"""Print tabular information nicely formatted.
|
||||||
|
|
||||||
|
accept less columns for some rows. The last item of such a row
|
||||||
|
may extend over columns. stolen from nicos.utils and modifed.
|
||||||
|
"""
|
||||||
|
if not headers and not items:
|
||||||
|
return
|
||||||
|
ncolumns = len(headers or items[0])
|
||||||
|
rowlens = [minlen] * ncolumns
|
||||||
|
for row in [headers or []] + items:
|
||||||
|
# do not consider length of last column when row has less columns
|
||||||
|
cntrow = row if len(row) == ncolumns else row[:-1]
|
||||||
|
for i, item in enumerate(cntrow):
|
||||||
|
rowlens[i] = max(rowlens[i], len(item))
|
||||||
|
rfmtstr = ('%%%ds ' * ncolumns) % tuple(rowlens)
|
||||||
|
lfmtstr = ('%%-%ds ' * ncolumns) % tuple(rowlens)
|
||||||
|
if headers:
|
||||||
|
printfunc(lfmtstr % tuple(headers))
|
||||||
|
printfunc(lfmtstr % tuple('=' * n for n in rowlens))
|
||||||
|
for row in items:
|
||||||
|
printfunc((rfmtstr if rjust else lfmtstr) % (tuple(row) + ('',) * (ncolumns - len(row))))
|
||||||
|
|
||||||
|
|
||||||
|
def run(serv, arglist):
|
||||||
|
arglist = arglist + [''] # add dummy argument
|
||||||
|
action = arglist.pop(0) if hasattr(serv, 'do_' + arglist[0]) else 'gui'
|
||||||
|
instance = arglist.pop(0) if arglist[0] and arglist[0] not in serv.services else None
|
||||||
|
if instance is None and len(serv.info) == 1:
|
||||||
|
instance = list(serv.info)[0]
|
||||||
|
if instance is not None:
|
||||||
|
arglist.insert(0, instance)
|
||||||
|
arglist.pop() # remove dummy argument
|
||||||
|
try:
|
||||||
|
serv.action(action, *arglist)
|
||||||
|
except AttributeError as e:
|
||||||
|
raise
|
||||||
|
print(repr(e))
|
||||||
|
raise ValueError("do not know '%s'" % ' '.join([serv.group, action] + arglist))
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceManager:
|
||||||
|
services = None
|
||||||
|
need_cfg = False
|
||||||
|
start_dir = None
|
||||||
|
group = None
|
||||||
|
all = {} # for the list command, we want to register all service managers
|
||||||
|
virtualenv = None
|
||||||
|
pkg = ''
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.env = {}
|
||||||
|
self.commands = {}
|
||||||
|
self.revcmd = {}
|
||||||
|
self.info = {}
|
||||||
|
self.all[self.group] = self
|
||||||
|
#prog, args = self.command.split(None, 1)
|
||||||
|
#self.cmdpat = re.compile('.* ' + # do not match prog, as it might be modified by the os
|
||||||
|
# (args % dict(ins=r'(?P<ins>\S*)', serv=r'(?P<serv>\S*)',
|
||||||
|
# cfg=r'(?P<cfg>\S*)', port=r'\S*', pkg=r'\S*')))
|
||||||
|
self.get_info()
|
||||||
|
self.stopped = defaultdict(dict)
|
||||||
|
|
||||||
|
def get_services(self, section):
|
||||||
|
ports = {}
|
||||||
|
nr = '%02d' % int(section[self.group])
|
||||||
|
gr = self.group.upper()
|
||||||
|
for service in self.services:
|
||||||
|
sv = '%s_%s' % (gr, service.upper())
|
||||||
|
port = section.get('%s_PORT' % sv)
|
||||||
|
if port or json.loads(section.get(sv, '0').lower()): # e.g. NICOS_POLLER = True leads to port = 0
|
||||||
|
ports[service] = int((port or '0').replace('nr', nr))
|
||||||
|
return ports
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
"""returns port numbers,commands and environment variables
|
||||||
|
|
||||||
|
the result is a dict[<service>] of dict(ins=.., port= ..., cmd= ...)
|
||||||
|
if ins is omitted, return a list of above for all ins
|
||||||
|
"""
|
||||||
|
result = OrderedDict()
|
||||||
|
parser = ConfigParser(interpolation=None)
|
||||||
|
parser.optionxform = str
|
||||||
|
parser.read(expanduser('servman.cfg'))
|
||||||
|
defaults = parser['DEFAULT']
|
||||||
|
self.commands = {}
|
||||||
|
self.revcmd = {}
|
||||||
|
for ins in parser.sections():
|
||||||
|
section = dict(parser[ins])
|
||||||
|
command = section.get('%s_command' % self.group)
|
||||||
|
self.revcmd[command] = self.group
|
||||||
|
if self.group in section:
|
||||||
|
self.commands[ins] = command
|
||||||
|
services = self.get_services(parser[ins])
|
||||||
|
env = {k: expanduser(section.get(k)) for k in defaults if k.isupper()}
|
||||||
|
result[ins] = services
|
||||||
|
self.env[ins] = env
|
||||||
|
self.info = result
|
||||||
|
|
||||||
|
def get_cmdpats(self, groups):
|
||||||
|
return self.cmdpats
|
||||||
|
|
||||||
|
def get_ins_info(self, ins):
|
||||||
|
self.get_info()
|
||||||
|
return self.info[ins]
|
||||||
|
|
||||||
|
def get_cfg(self, cmd):
|
||||||
|
"""return info about running program, if relevant
|
||||||
|
|
||||||
|
example for frappy: return cfg
|
||||||
|
"""
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def get_procs(self, groups=None):
|
||||||
|
"""return processes
|
||||||
|
|
||||||
|
result is a dict[ins] of dict[service] of list of tuples (process, cfg)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
cmdpatterns = []
|
||||||
|
if groups is None:
|
||||||
|
groups = [self.group]
|
||||||
|
for cmd, group in self.revcmd.items():
|
||||||
|
if group not in groups:
|
||||||
|
continue
|
||||||
|
args = cmd.split(None, 1)[1]
|
||||||
|
cmdpatterns.append(
|
||||||
|
re.compile('.* ' + # do not match prog, as it might be modified by the os
|
||||||
|
(args % dict(ins=r'(?P<ins>\S*)',
|
||||||
|
serv=r'(?P<serv>\S*)',
|
||||||
|
cfg=r'(?P<cfg>\S*)',
|
||||||
|
port=r'\S*',
|
||||||
|
pkg=r'\S*'))))
|
||||||
|
for p in psutil.process_iter(attrs=['pid', 'cmdline']):
|
||||||
|
cmdline = p.info['cmdline']
|
||||||
|
if cmdline:
|
||||||
|
cmd = ' '.join(cmdline)
|
||||||
|
for cmdpat in cmdpatterns:
|
||||||
|
match = cmdpat.match(cmd)
|
||||||
|
if match:
|
||||||
|
gdict = match.groupdict()
|
||||||
|
ins = gdict['ins']
|
||||||
|
serv = gdict['serv']
|
||||||
|
result.setdefault(ins, {}).setdefault(serv, []).append(p)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def check_running(self, ins, service):
|
||||||
|
self.get_info()
|
||||||
|
if ins not in self.info:
|
||||||
|
raise KeyError("don't know %r" % ins)
|
||||||
|
if not self.get_procs().get(ins, {}).get(service):
|
||||||
|
raise ServiceDown('%s %s is not running' % (service, ins))
|
||||||
|
|
||||||
|
def stop(self, ins, service=None):
|
||||||
|
"""stop service (or all services) of instance <ins>
|
||||||
|
|
||||||
|
return a dict[<ins>][<service>] of <cfg> for all stopped processes
|
||||||
|
this information may be used for restarts
|
||||||
|
"""
|
||||||
|
procs = self.get_procs()
|
||||||
|
done = False
|
||||||
|
services = self.services if service is None else [service]
|
||||||
|
for service in reversed(services):
|
||||||
|
for p in procs.get(ins, {}).get(service, []):
|
||||||
|
print_wait = True
|
||||||
|
for action in ('terminate', 'kill'):
|
||||||
|
getattr(p, action)() # p.terminate or p.kill
|
||||||
|
for i in range(10): # total 0.1 * 10 * 9 / 2 = 4.5 sec
|
||||||
|
try:
|
||||||
|
p.wait(0.1 * i)
|
||||||
|
except psutil.TimeoutExpired:
|
||||||
|
if p.status() == psutil.STATUS_ZOMBIE:
|
||||||
|
break
|
||||||
|
if print_wait and i > 4:
|
||||||
|
print('wait for %s %s' % (ins, service))
|
||||||
|
print_wait = False
|
||||||
|
continue
|
||||||
|
self.stopped[ins][service] = ' '.join(p.info['cmdline'])
|
||||||
|
done = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if action == 'kill':
|
||||||
|
action = 'kill fail'
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
print('%s %s %s' % (ins, service, (action + 'ed').replace('eed', 'ed')))
|
||||||
|
return done
|
||||||
|
|
||||||
|
def do_stop(self, ins, service=None, *args):
|
||||||
|
self.get_info()
|
||||||
|
if not self.stop(ins, service):
|
||||||
|
print('nothing to stop')
|
||||||
|
|
||||||
|
def prepare_start(self, ins):
|
||||||
|
if ins not in self.env:
|
||||||
|
self.get_info()
|
||||||
|
gr = self.group.upper()
|
||||||
|
env = self.env[ins]
|
||||||
|
return env.get('%s_ROOT' % gr, ''), env
|
||||||
|
|
||||||
|
def do_start(self, ins, service=None, cfg='', restart=False, wait=False):
|
||||||
|
if ins is None:
|
||||||
|
print('nothing to start')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
service_ports = self.get_ins_info(ins)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError('do not know %r' % ins)
|
||||||
|
services = list(service_ports) if service is None else [service]
|
||||||
|
if restart:
|
||||||
|
self.stop(ins, service)
|
||||||
|
else:
|
||||||
|
procs = self.get_procs()
|
||||||
|
to_start = []
|
||||||
|
for service in services:
|
||||||
|
n = len(procs.get(ins, {}).get(service, []))
|
||||||
|
if n == 0:
|
||||||
|
to_start.append(service)
|
||||||
|
else:
|
||||||
|
count = '' if n == 1 else ' %sx' % n
|
||||||
|
print('%s %s is already running%s' % (ins, service, count))
|
||||||
|
services = to_start
|
||||||
|
for service in services:
|
||||||
|
port = service_ports[service]
|
||||||
|
cmd = self.commands[ins] % dict(ins=ins, serv=service, port=port, cfg=cfg, pkg=self.pkg)
|
||||||
|
if '%(cfg)s' in self.commands[ins] and not cfg:
|
||||||
|
cmd = self.stopped[ins].get(service)
|
||||||
|
if not cmd:
|
||||||
|
raise ValueError('missing cfg for %s %s' % (ins, service))
|
||||||
|
wd = os.getcwd()
|
||||||
|
try:
|
||||||
|
start_dir, env = self.prepare_start(ins)
|
||||||
|
env = dict(os.environ, **env)
|
||||||
|
os.chdir(start_dir)
|
||||||
|
if wait:
|
||||||
|
proc = subprocess.Popen(cmd.split(), env=env)
|
||||||
|
proc.wait()
|
||||||
|
return
|
||||||
|
process = subprocess.Popen(cmd.split(), env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
if not port:
|
||||||
|
print('%s %s started' % (ins, service))
|
||||||
|
continue
|
||||||
|
|
||||||
|
print_wait = True
|
||||||
|
for i in range(10): # total 10 * 9 / 2 = 4.5 sec
|
||||||
|
returnvalue = process.poll()
|
||||||
|
if returnvalue is not None:
|
||||||
|
print('started process finished with %r' % returnvalue)
|
||||||
|
process = subprocess.Popen(cmd.split(), env=env, stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
try:
|
||||||
|
_, erroutput = process.communicate(timeout=10)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
process.kill()
|
||||||
|
_, erroutput = process.communicate()
|
||||||
|
print(erroutput.decode())
|
||||||
|
print('%s %s died' % (ins, service))
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
if print_wait and i > 4:
|
||||||
|
print('wait for port %s' % port)
|
||||||
|
print_wait = False
|
||||||
|
s = socket.create_connection(('localhost', port), timeout=5)
|
||||||
|
s.close()
|
||||||
|
except socket.error:
|
||||||
|
time.sleep(0.1 * i)
|
||||||
|
continue
|
||||||
|
if restart:
|
||||||
|
print('%s %s restarted' % (ins, service))
|
||||||
|
else:
|
||||||
|
print('%s %s started' % (ins, service))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(cmd)
|
||||||
|
print('starting %s %s failed' % (ins, service))
|
||||||
|
finally:
|
||||||
|
os.chdir(wd)
|
||||||
|
|
||||||
|
def do_restart(self, ins, service=None, cfg=None):
|
||||||
|
self.do_start(ins, service, cfg, True)
|
||||||
|
|
||||||
|
def do_run(self, ins, service, cfg=None):
|
||||||
|
"""for tests: run and wait"""
|
||||||
|
self.do_start(ins, service, cfg, wait=True)
|
||||||
|
|
||||||
|
def do_list(self, ins=None, *args):
|
||||||
|
"""info about running services"""
|
||||||
|
procs = self.get_procs(self.all)
|
||||||
|
rows = []
|
||||||
|
merged = OrderedDict()
|
||||||
|
show_unused = ins == 'all'
|
||||||
|
if show_unused:
|
||||||
|
ins = None
|
||||||
|
for group, sm in self.all.items():
|
||||||
|
sm.get_info()
|
||||||
|
for ins_i, info_dict in sm.info.items():
|
||||||
|
if ins is not None and ins != ins_i:
|
||||||
|
continue
|
||||||
|
for serv, port in info_dict.items():
|
||||||
|
if ins_i not in merged:
|
||||||
|
merged[ins_i] = {g: {} for g in self.all}
|
||||||
|
merged[ins_i][group][serv] = port
|
||||||
|
for ins_i, info_dict in merged.items():
|
||||||
|
show_ins = show_unused
|
||||||
|
run_info = [[''], [ins_i]]
|
||||||
|
procs_dict = procs.get(ins_i, {})
|
||||||
|
for group, sm in self.all.items():
|
||||||
|
info_grp = info_dict.get(group, {})
|
||||||
|
for serv, port in info_grp.items():
|
||||||
|
plist = procs_dict.get(serv)
|
||||||
|
if plist:
|
||||||
|
if sm == self:
|
||||||
|
show_ins = True
|
||||||
|
cfg = sm.get_cfg(' '.join(plist[0].info['cmdline']))
|
||||||
|
gs = '%s %s' % (group, serv)
|
||||||
|
port = str(port or '')
|
||||||
|
run_info.append(('', gs, port, cfg))
|
||||||
|
if len(plist) > 1:
|
||||||
|
rows.append(['', ' WARNING: multiple processes %s'
|
||||||
|
% ', '.join(str(p.pid) for p, _ in plist)])
|
||||||
|
extra = sm.extra_info(ins_i)
|
||||||
|
if extra and run_info:
|
||||||
|
run_info.append(['', extra])
|
||||||
|
if show_ins:
|
||||||
|
rows.extend(run_info)
|
||||||
|
print('')
|
||||||
|
printTable(('inst', 'service', 'port', 'cfg'), rows, print)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extra_info(ins):
|
||||||
|
"""provide extra info or None"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def action(self, action, *args):
|
||||||
|
method = getattr(self, 'do_' + action, None)
|
||||||
|
if not callable(method):
|
||||||
|
raise UsageError('%s is no valid action' % action)
|
||||||
|
try:
|
||||||
|
method(*args)
|
||||||
|
except TypeError as e:
|
||||||
|
errtxt = str(e)
|
||||||
|
if ' do_%s(' % action in errtxt and 'argument' in errtxt:
|
||||||
|
raise UsageError(errtxt)
|
||||||
|
raise
|
55
bin/frappy
Executable file
55
bin/frappy
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- 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 join, abspath
|
||||||
|
|
||||||
|
sys.path.insert(1, abspath(join(__file__, '../../..')))
|
||||||
|
|
||||||
|
from servman.frappy import FrappyManager
|
||||||
|
from servman.nicos import NicosManager
|
||||||
|
from servman.sea import SeaManager
|
||||||
|
from servman import run
|
||||||
|
|
||||||
|
NicosManager()
|
||||||
|
serv = FrappyManager()
|
||||||
|
SeaManager()
|
||||||
|
|
||||||
|
USAGE = """
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
frappy list [<instance>]
|
||||||
|
frappy start <instance> <service> <cfgfiles>
|
||||||
|
frappy restart <instance> [<service>] [<cfgfiles>]
|
||||||
|
frappy stop <instance> [<service>]
|
||||||
|
|
||||||
|
<service> is one of main, stick, addons
|
||||||
|
<instance> is one of %s
|
||||||
|
""" % ', '.join(serv.info)
|
||||||
|
|
||||||
|
try:
|
||||||
|
run(serv, sys.argv[1:])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
||||||
|
print(''.join(USAGE))
|
71
bin/nicos
Executable file
71
bin/nicos
Executable file
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env 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 os
|
||||||
|
from os.path import join, abspath, expanduser
|
||||||
|
import sys
|
||||||
|
|
||||||
|
#nicos_root = os.environ.get('NICOS_ROOT', expanduser('~/nicos'))
|
||||||
|
#pkg_dir = abspath(join(nicos_root, 'nicos_linse'))
|
||||||
|
#sys.path.insert(1, join(pkg_dir, 'common'))
|
||||||
|
|
||||||
|
sys.path.insert(1, abspath(join(__file__, '../../..')))
|
||||||
|
|
||||||
|
from servman.frappy import FrappyManager
|
||||||
|
from servman.nicos import NicosManager
|
||||||
|
from servman.sea import SeaManager
|
||||||
|
from servman import run, UsageError
|
||||||
|
|
||||||
|
serv = NicosManager()
|
||||||
|
FrappyManager()
|
||||||
|
SeaManager()
|
||||||
|
|
||||||
|
|
||||||
|
USAGE = """
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
nicos gui <instance>
|
||||||
|
nicos <instance> (the same as above)
|
||||||
|
nicos list [<instance>]
|
||||||
|
nicos start <instance> [<service>]
|
||||||
|
nicos restart <instance> [<service>]
|
||||||
|
nicos stop <instance> [<service>]
|
||||||
|
nicos create <instance> <nr>
|
||||||
|
nicos create all
|
||||||
|
nicos link <instance> (create links to nicos data and scripts)
|
||||||
|
|
||||||
|
<service> is one of main, stick, addons
|
||||||
|
<instance> is one of %s
|
||||||
|
|
||||||
|
to be done after the experiment:
|
||||||
|
nicos copy (copy data and scripts from link)
|
||||||
|
nicos copy [<instance> [<year>/<proposal>]] (copy specific data)
|
||||||
|
|
||||||
|
""" % ', '.join(serv.info)
|
||||||
|
|
||||||
|
try:
|
||||||
|
run(serv, sys.argv[1:])
|
||||||
|
|
||||||
|
except UsageError as e:
|
||||||
|
print(repr(e))
|
||||||
|
print(''.join(USAGE))
|
62
bin/nicos-cache
Executable file
62
bin/nicos-cache
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
# NICOS, the Networked Instrument Control System of the MLZ
|
||||||
|
# Copyright (c) 2009-2019 by the NICOS contributors (see AUTHORS)
|
||||||
|
#
|
||||||
|
# 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:
|
||||||
|
# Georg Brandl <georg.brandl@fz-juelich.de>
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from os import path, environ
|
||||||
|
|
||||||
|
sys.path.insert(0, path.dirname(path.dirname(path.dirname(path.realpath(__file__)))))
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('-d', '--daemon', dest='daemon', action='store_true',
|
||||||
|
help='daemonize the cache process')
|
||||||
|
parser.add_argument('-D', '--systemd', dest='daemon', action='store_const',
|
||||||
|
const='systemd', help='run in systemd service mode')
|
||||||
|
parser.add_argument('-S', '--setup', action='store', dest='setupname',
|
||||||
|
default='cache',
|
||||||
|
help="name of the setup, default is 'cache'")
|
||||||
|
parser.add_argument('--clear', dest='clear', action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='clear the whole cache')
|
||||||
|
parser.add_argument('-I', '--instrument', action='store',
|
||||||
|
type=str, default='',
|
||||||
|
help='instrument as <package>.<instrument>\n', )
|
||||||
|
parser.add_argument('args', nargs=argparse.REMAINDER, help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
opts = parser.parse_args()
|
||||||
|
|
||||||
|
if opts.clear:
|
||||||
|
opts.args.append('clear')
|
||||||
|
|
||||||
|
if opts.instrument:
|
||||||
|
environ['INSTRUMENT'] = opts.instrument
|
||||||
|
|
||||||
|
from nicos.core.sessions.simple import NoninteractiveSession
|
||||||
|
|
||||||
|
NoninteractiveSession.run(opts.setupname, 'Server', setupname=opts.setupname,
|
||||||
|
daemon=opts.daemon, start_args=opts.args)
|
54
bin/nicos-daemon
Executable file
54
bin/nicos-daemon
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
# NICOS, the Networked Instrument Control System of the MLZ
|
||||||
|
# Copyright (c) 2009-2019 by the NICOS contributors (see AUTHORS)
|
||||||
|
#
|
||||||
|
# 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:
|
||||||
|
# Georg Brandl <georg.brandl@fz-juelich.de>
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from os import path, environ
|
||||||
|
|
||||||
|
sys.path.insert(0, path.dirname(path.dirname(path.dirname(path.realpath(__file__)))))
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('-d', '--daemon', dest='daemon', action='store_true',
|
||||||
|
help='daemonize the daemon process')
|
||||||
|
parser.add_argument('-D', '--systemd', dest='daemon', action='store_const',
|
||||||
|
const='systemd', help='run in systemd service mode')
|
||||||
|
parser.add_argument('-S', '--setup', action='store', dest='setupname',
|
||||||
|
default='daemon',
|
||||||
|
help="name of the setup, default is 'daemon'")
|
||||||
|
parser.add_argument('-I', '--instrument', action='store',
|
||||||
|
type=str, default='',
|
||||||
|
help='instrument as <package>.<instrument>\n', )
|
||||||
|
opts = parser.parse_args()
|
||||||
|
|
||||||
|
if opts.instrument:
|
||||||
|
environ['INSTRUMENT'] = opts.instrument
|
||||||
|
|
||||||
|
from nicos.services.daemon.session import DaemonSession
|
||||||
|
|
||||||
|
DaemonSession.run(opts.setupname, 'Daemon', daemon=opts.daemon)
|
63
bin/nicos-poller
Executable file
63
bin/nicos-poller
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
# NICOS, the Networked Instrument Control System of the MLZ
|
||||||
|
# Copyright (c) 2009-2019 by the NICOS contributors (see AUTHORS)
|
||||||
|
#
|
||||||
|
# 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:
|
||||||
|
# Georg Brandl <georg.brandl@fz-juelich.de>
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from os import path, environ
|
||||||
|
|
||||||
|
sys.path.insert(0, path.dirname(path.dirname(path.dirname(path.realpath(__file__)))))
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('-d', '--daemon', dest='daemon', action='store_true',
|
||||||
|
help='daemonize the poller processes')
|
||||||
|
parser.add_argument('-D', '--systemd', dest='daemon', action='store_const',
|
||||||
|
const='systemd', help='run in systemd service mode')
|
||||||
|
parser.add_argument('-S', '--setup', action='store', dest='setupname',
|
||||||
|
default='poller',
|
||||||
|
help="name of the setup, default is 'poller'")
|
||||||
|
parser.add_argument('-I', '--instrument', action='store',
|
||||||
|
type=str, default='',
|
||||||
|
help='instrument as <package>.<instrument>\n', )
|
||||||
|
parser.add_argument('setup', nargs=argparse.OPTIONAL, help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
opts = parser.parse_args()
|
||||||
|
|
||||||
|
if opts.setup:
|
||||||
|
appname = 'poller-' + opts.setup
|
||||||
|
args = [opts.setup]
|
||||||
|
else:
|
||||||
|
appname = 'poller'
|
||||||
|
args = []
|
||||||
|
|
||||||
|
if opts.instrument:
|
||||||
|
environ['INSTRUMENT'] = opts.instrument
|
||||||
|
|
||||||
|
from nicos.services.poller.psession import PollerSession
|
||||||
|
|
||||||
|
PollerSession.run(appname, setupname=opts.setupname, maindevname='Poller',
|
||||||
|
start_args=args, daemon=opts.daemon)
|
58
bin/sea
Executable file
58
bin/sea
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- 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 os
|
||||||
|
from os.path import join, abspath, split, dirname, expanduser
|
||||||
|
import sys
|
||||||
|
|
||||||
|
nicos_root = os.environ.get('NICOS_ROOT', expanduser('~/nicos'))
|
||||||
|
pkg_dir = abspath(join(nicos_root, 'nicos_linse'))
|
||||||
|
sys.path.insert(1, join(pkg_dir, 'common'))
|
||||||
|
|
||||||
|
from clitools import FrappyManager, NicosManager, SeaManager, run
|
||||||
|
|
||||||
|
NicosManager(pkg_dir)
|
||||||
|
FrappyManager(pkg_dir)
|
||||||
|
serv = SeaManager()
|
||||||
|
|
||||||
|
USAGE = """
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
sea gui <instance>
|
||||||
|
sea <instance> # the same as sea gui <instance>
|
||||||
|
sea cli <instance> (the same as old seacmd)
|
||||||
|
sea start <instance> <service>
|
||||||
|
sea restart <instance> [<service>]
|
||||||
|
sea stop <instance> [<service>]
|
||||||
|
sea list [<instance>]
|
||||||
|
|
||||||
|
<service> is one of main, stick, addons
|
||||||
|
<instance> is one of %s
|
||||||
|
""" % ', '.join(serv.info)
|
||||||
|
|
||||||
|
try:
|
||||||
|
run(serv, sys.argv[1:])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
||||||
|
print(''.join(USAGE))
|
35
frappy.py
Normal file
35
frappy.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# -*- 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>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from servman import ServiceManager
|
||||||
|
|
||||||
|
|
||||||
|
class FrappyManager(ServiceManager):
|
||||||
|
group = 'frappy'
|
||||||
|
services = ('main', 'stick', 'addons')
|
||||||
|
|
||||||
|
def get_cfg(self, cmd):
|
||||||
|
if cmd:
|
||||||
|
match = self.cmdpat.match(cmd)
|
||||||
|
if match:
|
||||||
|
return match.groupdict().get('cfg')
|
||||||
|
return ''
|
255
nicos.py
Normal file
255
nicos.py
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
# -*- 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 os
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
from glob import glob
|
||||||
|
from os.path import join, abspath, dirname, expanduser, exists, islink
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from servman import ServiceManager
|
||||||
|
|
||||||
|
|
||||||
|
ENV_KEYS = {
|
||||||
|
'NICOS_CACHE_PORT',
|
||||||
|
'NICOS_DAEMON_PORT',
|
||||||
|
'FRAPPY_MAIN_PORT',
|
||||||
|
'FRAPPY_STICK_PORT',
|
||||||
|
'FRAPPY_ADDONS_PORT',
|
||||||
|
'SEA_PORT',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def copy_all(srcdir, dstdir):
|
||||||
|
"""copy all files from srcdir to dstdir"""
|
||||||
|
files = glob(join(srcdir, '*'))
|
||||||
|
for src in files:
|
||||||
|
shutil.copy2(src, dstdir)
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
class NicosManager(ServiceManager):
|
||||||
|
group = 'nicos'
|
||||||
|
services = ('cache', 'daemon', 'poller')
|
||||||
|
|
||||||
|
def do_create(self, ins, nr=None, *args):
|
||||||
|
"""TODO: redo"""
|
||||||
|
self.get_info()
|
||||||
|
if ins == 'all' or ins == 'check':
|
||||||
|
inslist = list(self.info)
|
||||||
|
else:
|
||||||
|
inslist = [ins]
|
||||||
|
for ins_i in inslist:
|
||||||
|
env = self.env[ins_i]
|
||||||
|
base = join(env['NICOS_ROOT'], env['NICOS_PACKAGE'], ins)
|
||||||
|
nicos_conf = join(base, 'nicos.conf')
|
||||||
|
content = {
|
||||||
|
'nicos': {
|
||||||
|
'setup_subdirs': '%s, common, frappy' % ins,
|
||||||
|
'logging_path': '%s/%s' % (env['NICOS_LOG'], ins),
|
||||||
|
'pid_path': '%s/%s' % (env['NICOS_LOG'], ins),
|
||||||
|
},
|
||||||
|
'environment': {
|
||||||
|
key: env[key] for key in env if key in ENV_KEYS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
cp = ConfigParser()
|
||||||
|
cp.optionsxform = str
|
||||||
|
cp.read(nicos_conf)
|
||||||
|
if set(cp.sections) != set(content):
|
||||||
|
oldcontent = None
|
||||||
|
else:
|
||||||
|
oldcontent = {key: cp[key] for key in content}
|
||||||
|
except FileNotFoundError:
|
||||||
|
oldcontent = None
|
||||||
|
if content != oldcontent:
|
||||||
|
cp = ConfigParser()
|
||||||
|
cp.optionsxform = str
|
||||||
|
for key, sdict in content.items():
|
||||||
|
cp[key] = sdict
|
||||||
|
cp.write(nicos_conf + 'x')
|
||||||
|
|
||||||
|
# pdir = self.start_dir
|
||||||
|
# if ins is None or ins == 'common':
|
||||||
|
# raise ValueError("nothing to do")
|
||||||
|
# insdict = {info['cache']['port'] % 100: ins_i for ins_i, info in self.info.items()}
|
||||||
|
# nrdict = {v: k for k, v in insdict.items()}
|
||||||
|
# if nr is not None:
|
||||||
|
# nr = int(nr)
|
||||||
|
# if ins == 'all':
|
||||||
|
# if nr is not None:
|
||||||
|
# raise ValueError("'nicos create all' has no <nr> argument")
|
||||||
|
# action = 'update'
|
||||||
|
# else:
|
||||||
|
# if nr is None:
|
||||||
|
# nr = nrdict.get(ins)
|
||||||
|
# if nr is None:
|
||||||
|
# raise ValueError('%s not known, <nr> has to specified' % ins)
|
||||||
|
# if insdict.get(nr, ins) != ins:
|
||||||
|
# raise ValueError('%d conflicts with %s' % (nr, insdict[nr]))
|
||||||
|
# action = 'create' if ins in insdict.values() else 'update'
|
||||||
|
# insdict = {nr: ins}
|
||||||
|
# for nr, ins in insdict.items():
|
||||||
|
# print('%s %3d %s %s' % (action, nr, ins, pdir))
|
||||||
|
# os.makedirs(join(pdir, ins), exist_ok=True)
|
||||||
|
# with open(join(pdir, ins, 'nicos.conf'), 'w') as f:
|
||||||
|
# f.write(NICOS_CONF % dict(insnr='%02d' % nr, ins=ins, sea=8630 + nr,
|
||||||
|
# logroot=os.environ.get('NICOS_LOG')))
|
||||||
|
# shutil.copyfile(join(pdir, 'common', 'guiconfig.py'), join(pdir, ins, 'guiconfig.py'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extra_info(ins):
|
||||||
|
try:
|
||||||
|
datadir = join(os.environ.get('NICOS_DATA', '.'), ins)
|
||||||
|
datadir = join(datadir, os.readlink(join(datadir, 'current')))
|
||||||
|
except FileNotFoundError:
|
||||||
|
return None
|
||||||
|
return 'nicos data: %s' % datadir
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def linked_dir(current_wd):
|
||||||
|
link = join(current_wd, 'data')
|
||||||
|
if islink(link):
|
||||||
|
return join(link, os.readlink(link))
|
||||||
|
if exists(link):
|
||||||
|
raise ValueError('%s is not a symlink' % link)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def handle_linked(target, wd=None):
|
||||||
|
if not target:
|
||||||
|
print('no links from %s' % os.getcwd())
|
||||||
|
return
|
||||||
|
targetdir = abspath(join(target, '..'))
|
||||||
|
analist_file = join(targetdir, 'data_links.txt')
|
||||||
|
linked_from = []
|
||||||
|
if exists(analist_file):
|
||||||
|
with open(analist_file) as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if line and line != wd:
|
||||||
|
if islink(join(line, 'data')):
|
||||||
|
linked_from.append(line)
|
||||||
|
if wd:
|
||||||
|
linked_from.append(wd)
|
||||||
|
linked_from = '\n'.join(linked_from)
|
||||||
|
if wd:
|
||||||
|
with open(analist_file, 'w') as f:
|
||||||
|
f.write('%s\n' % linked_from)
|
||||||
|
print('links to %s from:\n%s' % (target, linked_from))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_symlink(ins):
|
||||||
|
data = join(os.environ['NICOS_DATA'], ins)
|
||||||
|
target = join(data, os.readlink(join(data, 'current')), 'data')
|
||||||
|
link = join(os.getcwd(), 'data')
|
||||||
|
if islink(link):
|
||||||
|
os.remove(link)
|
||||||
|
elif exists(link):
|
||||||
|
raise ValueError('%s is not a symlink' % link)
|
||||||
|
os.symlink(target, link, True)
|
||||||
|
NicosManager.handle_linked(target, os.getcwd())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def copy_linked(datadir=None):
|
||||||
|
src = NicosManager.linked_dir(os.getcwd())
|
||||||
|
if src:
|
||||||
|
if not datadir:
|
||||||
|
if src.rsplit('/', 1)[-1] != 'data':
|
||||||
|
raise ValueError('%s is already a copy' % src)
|
||||||
|
datadir = dirname(src)
|
||||||
|
else:
|
||||||
|
if not datadir:
|
||||||
|
raise ValueError('missing data dir')
|
||||||
|
src = join(datadir, 'data')
|
||||||
|
dst = join(os.getcwd(), '%s_%s_data' % tuple(datadir.rsplit('/', 2)[-2:]))
|
||||||
|
os.makedirs(dst, exist_ok=True)
|
||||||
|
n = len(copy_all(src, dst))
|
||||||
|
print('copy %d files to %s' % (n, dst))
|
||||||
|
|
||||||
|
# copy scripts
|
||||||
|
src = join(datadir, 'scripts')
|
||||||
|
dst = join(dst, 'scripts')
|
||||||
|
os.makedirs(dst, exist_ok=True)
|
||||||
|
n = len(copy_all(src, dst))
|
||||||
|
print('copy %d files to %s' % (n, dst))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def do_link(ins, *args):
|
||||||
|
"""make/show symlink to nicos data in current wd"""
|
||||||
|
if ins is None:
|
||||||
|
NicosManager.handle_linked(NicosManager.linked_dir(os.getcwd()))
|
||||||
|
else:
|
||||||
|
NicosManager.make_symlink(ins)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def do_copy(ins, year_prop, *args):
|
||||||
|
"""copy nicos data to current wd"""
|
||||||
|
if ins is None:
|
||||||
|
NicosManager.copy_linked()
|
||||||
|
else:
|
||||||
|
data = join(os.environ['NICOS_DATA'], ins)
|
||||||
|
if year_prop:
|
||||||
|
year, sep, proposal = year_prop.rpartition('/')
|
||||||
|
if year:
|
||||||
|
src = join(data, year, proposal)
|
||||||
|
else:
|
||||||
|
# get proposal from most recent year
|
||||||
|
src = next(reversed(sorted(glob(join(data, '*', proposal)))))
|
||||||
|
else:
|
||||||
|
src = join(data, os.readlink(join(data, 'current')))
|
||||||
|
NicosManager.copy_linked(src)
|
||||||
|
|
||||||
|
def prepare_start(self, ins):
|
||||||
|
start_dir, env = super().prepare_start(ins)
|
||||||
|
instr = '%s.%s' % (env['NICOS_PACKAGE'], ins)
|
||||||
|
env['INSTRUMENT'] = instr
|
||||||
|
start_dir = env.get('NICOS_START', start_dir)
|
||||||
|
return start_dir, env
|
||||||
|
|
||||||
|
def prepare_client(self, ins):
|
||||||
|
self.check_running(ins, 'daemon')
|
||||||
|
env = self.prepare_start(ins)[1]
|
||||||
|
os.environ.update(env)
|
||||||
|
os.chdir(join(os.environ['NICOS_ROOT'], env['NICOS_PACKAGE']))
|
||||||
|
|
||||||
|
def run_client(self, ins, main, app, **kwargs):
|
||||||
|
serverhost = os.environ.get('NICOS_SERVER_HOST', 'localhost')
|
||||||
|
sys.argv[:] = [app, 'guest:guest@%s:%d' % (serverhost, self.info[ins]['daemon'])]
|
||||||
|
sys.exit(main(sys.argv, **kwargs))
|
||||||
|
|
||||||
|
def do_cli(self, ins):
|
||||||
|
self.prepare_client(ins)
|
||||||
|
from nicos.clients.cli import main
|
||||||
|
os.environ['NICOS_HISTORY_FILE'] = expanduser('~/.nicoshistory_%s' % ins)
|
||||||
|
self.run_client(ins, main, 'nicos-client',
|
||||||
|
userpath=expanduser('~/.config/nicos_client_%s' % ins))
|
||||||
|
|
||||||
|
def do_gui(self, ins):
|
||||||
|
self.prepare_client(ins)
|
||||||
|
from nicos.clients.gui.main import main
|
||||||
|
print('starting nicos gui %s' % ins, expanduser('~/.config/nicos_%s' % ins))
|
||||||
|
self.run_client(ins, main, 'nicos-gui', instance=ins)
|
||||||
|
|
||||||
|
|
62
sea.py
Normal file
62
sea.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# -*- 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
|
||||||
|
import time
|
||||||
|
import termios
|
||||||
|
import subprocess
|
||||||
|
from servman import ServiceManager, ServiceDown
|
||||||
|
|
||||||
|
|
||||||
|
def run_command(cmd, wait=False):
|
||||||
|
if wait:
|
||||||
|
old = termios.tcgetattr(sys.stdin)
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(cmd.split())
|
||||||
|
proc.wait()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
proc.terminate()
|
||||||
|
finally:
|
||||||
|
# in case cmd changed tty attributes
|
||||||
|
termios.tcsetattr(sys.stdin, termios.TCSAFLUSH, old)
|
||||||
|
print('')
|
||||||
|
else:
|
||||||
|
subprocess.Popen(cmd.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
|
||||||
|
class SeaManager(ServiceManager):
|
||||||
|
group = 'sea'
|
||||||
|
services = ('sea', 'graph')
|
||||||
|
|
||||||
|
def do_cli(self, ins):
|
||||||
|
self.check_running(ins, 'sea')
|
||||||
|
run_command('six -sea %s' % ins, wait=True)
|
||||||
|
|
||||||
|
def do_gui(self, ins):
|
||||||
|
try:
|
||||||
|
self.check_running(ins, 'sea')
|
||||||
|
except ServiceDown as e:
|
||||||
|
print('%s, try to start...' % e)
|
||||||
|
self.do_start(ins)
|
||||||
|
time.sleep(1) # make sure caller did read the message
|
||||||
|
run_command('SeaClient %s' % ins)
|
||||||
|
print('starting sea gui %s' % ins)
|
Loading…
x
Reference in New Issue
Block a user