renamed to 'servicemanager'

the PYTHONPATH should be set to the directory above servicemanager
This commit is contained in:
zolliker 2021-02-26 14:48:15 +01:00
parent 632beda430
commit 51abcab182
9 changed files with 158 additions and 134 deletions

View File

@ -1,5 +1,5 @@
# servman # servicemanager
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. Several instances of nicos, frappy, sea and seweb might run on the same machine.

View File

@ -27,7 +27,65 @@ this code is currently used:
- from a script allowing to start/stop/list (and more) multiple frappy and nicos servers - from a script allowing to start/stop/list (and more) multiple frappy and nicos servers
""" """
from servman.base import ServiceManager, ServiceDown, run, UsageError from servicemanager.base import ServiceManager, ServiceDown, UsageError, get_config
from servman.frappyman import FrappyManager from servicemanager.nicosman import NicosManager
from servman.nicosman import NicosManager from servicemanager.seaman import SeaManager
from servman.seaman import SeaManager
class FrappyManager(ServiceManager):
group = 'frappy'
services = ('main', 'stick', 'addons')
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
"""
class SewebManager(ServiceManager):
group = 'seweb'
services = ('',)
USAGE = """
Usage:
seaweb list [<instance>]
seaweb start <instance>
seaweb restart <instance>
seaweb stop <instance>
<instance> is one of %s
"""
all = NicosManager, FrappyManager, SeaManager, SewebManager
def run(group, arglist):
try:
parser = get_config()
defaults = parser['DEFAULT']
managers = {cls.group: cls() for cls in all if cls.group + '_command' in defaults}
serv = managers[group]
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:
raise UsageError("do not know '%s'" % ' '.join([serv.group, action] + arglist))
except UsageError as e:
print(repr(e))
print(serv.usage())

59
base.py
View File

@ -21,6 +21,7 @@
# ***************************************************************************** # *****************************************************************************
import sys
import json import json
import os import os
from os.path import expanduser from os.path import expanduser
@ -65,25 +66,15 @@ def printTable(headers, items, printfunc, minlen=0, rjust=False):
printfunc((rfmtstr if rjust else lfmtstr) % (tuple(row) + ('',) * (ncolumns - len(row)))) printfunc((rfmtstr if rjust else lfmtstr) % (tuple(row) + ('',) * (ncolumns - len(row))))
def run(serv, arglist): def get_config():
arglist = arglist + [''] # add dummy argument parser = ConfigParser(interpolation=None)
action = arglist.pop(0) if hasattr(serv, 'do_' + arglist[0]) else 'gui' parser.optionxform = str
instance = arglist.pop(0) if arglist[0] and arglist[0] not in serv.services else None parser.read(expanduser('~/servicemanager.cfg'))
if instance is None and len(serv.info) == 1: return parser
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: class ServiceManager:
services = None services = ['SINGLE']
need_cfg = False need_cfg = False
start_dir = None start_dir = None
group = None group = None
@ -91,6 +82,7 @@ class ServiceManager:
virtualenv = None virtualenv = None
pkg = '' pkg = ''
revcmd = {} revcmd = {}
USAGE = None
def __init__(self): def __init__(self):
self.env = {} self.env = {}
@ -109,10 +101,11 @@ class ServiceManager:
ports = {} ports = {}
nr = '%02d' % int(section[self.group]) nr = '%02d' % int(section[self.group])
gr = self.group.upper() gr = self.group.upper()
singlekey = gr + '_PORT'
for service in self.services: for service in self.services:
sv = '%s_%s' % (gr, service.upper()) sv = gr + '_' + service.upper()
key = '%s_PORT' % sv key = '%s_PORT' % sv
port = section.get(key) port = section.get(key) or section.get(singlekey)
if port or json.loads(section.get(sv, '0').lower()): if port or json.loads(section.get(sv, '0').lower()):
# e.g. NICOS_POLLER = True leads to port = 0 # e.g. NICOS_POLLER = True leads to port = 0
port = (port or '0').replace('nr', nr) port = (port or '0').replace('nr', nr)
@ -128,24 +121,28 @@ class ServiceManager:
if ins is omitted, return a list of above for all ins if ins is omitted, return a list of above for all ins
""" """
result = OrderedDict() result = OrderedDict()
parser = ConfigParser(interpolation=None) parser = get_config()
parser.optionxform = str
parser.read(expanduser('~/servman.cfg'))
defaults = parser['DEFAULT'] defaults = parser['DEFAULT']
self.commands = {} self.commands = {}
# self.revcmd = {} # self.revcmd = {}
def expand_path(pathlist, ins): def get_subs(section, key, ins, nr):
return ':'.join(expanduser(p % dict(ins=ins)) for p in pathlist.split(':')) """get item <key> from section and substitute nr or expand filename"""
value = section.get(key)
if key.endswith('_PORT'):
return value.replace('nr', nr)
return ':'.join(expanduser(p % dict(ins=ins)) for p in value.split(':'))
for ins in parser.sections(): for ins in parser.sections():
section = dict(parser[ins]) section = dict(parser[ins])
command = section.get('%s_command' % self.group) command = section.get('%s_command' % self.group)
self.revcmd[command] = self.group self.revcmd[command] = self.group
if self.group in section: nr = section.get(self.group)
if nr is not None:
nr = '%02d' % int(nr)
self.commands[ins] = command self.commands[ins] = command
services = self.get_services(section) services = self.get_services(section)
env = {k: expand_path(section.get(k), ins) for k in defaults if k.isupper()} env = {k: get_subs(section, k, ins, nr) for k in defaults if k.isupper()}
result[ins] = services result[ins] = services
self.env[ins] = env self.env[ins] = env
self.info = result self.info = result
@ -194,7 +191,7 @@ class ServiceManager:
if match: if match:
gdict = match.groupdict() gdict = match.groupdict()
ins = gdict['ins'] ins = gdict['ins']
serv = gdict['serv'] serv = gdict.get('serv', '')
if cfginfo is not None and 'cfg' in gdict: if cfginfo is not None and 'cfg' in gdict:
cfginfo[ins, serv] = gdict['cfg'] cfginfo[ins, serv] = gdict['cfg']
result.setdefault(ins, {}).setdefault(serv, []).append(p) result.setdefault(ins, {}).setdefault(serv, []).append(p)
@ -264,6 +261,7 @@ class ServiceManager:
except ValueError: except ValueError:
raise ValueError('do not know %r' % ins) raise ValueError('do not know %r' % ins)
services = list(service_ports) if service is None else [service] services = list(service_ports) if service is None else [service]
print('start', services, service_ports)
if restart: if restart:
self.stop(ins, service) self.stop(ins, service)
else: else:
@ -280,6 +278,7 @@ class ServiceManager:
for service_i in services: for service_i in services:
port = service_ports[service_i] port = service_ports[service_i]
cmd = self.commands[ins] % dict(ins=ins, serv=service_i, port=port, cfg=cfg, pkg=self.pkg) cmd = self.commands[ins] % dict(ins=ins, serv=service_i, port=port, cfg=cfg, pkg=self.pkg)
print('COMMAND', cmd)
if '%(cfg)s' in self.commands[ins] and not cfg: if '%(cfg)s' in self.commands[ins] and not cfg:
cmd = self.stopped[ins].get(service_i) cmd = self.stopped[ins].get(service_i)
if not cmd: if not cmd:
@ -338,8 +337,10 @@ class ServiceManager:
def do_restart(self, ins, service=None, cfg=None): def do_restart(self, ins, service=None, cfg=None):
self.do_start(ins, service, cfg, True) self.do_start(ins, service, cfg, True)
def do_run(self, ins, service, cfg=None): def do_run(self, ins, service=None, cfg=None):
"""for tests: run and wait""" """for tests: run and wait"""
if not service:
service, = self.services
self.do_start(ins, service, cfg, wait=True) self.do_start(ins, service, cfg, wait=True)
def do_list(self, ins=None, *args): def do_list(self, ins=None, *args):
@ -403,3 +404,7 @@ class ServiceManager:
if ' do_%s(' % action in errtxt and 'argument' in errtxt: if ' do_%s(' % action in errtxt and 'argument' in errtxt:
raise UsageError(errtxt) raise UsageError(errtxt)
raise raise
def usage(self):
print(self.USAGE % ', '.join(self.info))

View File

@ -24,31 +24,11 @@
import sys import sys
from os.path import join, abspath, dirname from os.path import join, abspath, dirname
# above packages: servman, nicos, frappy, history # for packages: servicemanager, frappyhistory
sys.path.insert(0, abspath(join(dirname(__file__), '../..'))) sys.path.insert(0, abspath(join(dirname(__file__), '../..')))
# for frappy:
sys.path.insert(0, abspath(join(dirname(__file__), '../../frappy')))
from servman import run, FrappyManager, NicosManager, SeaManager, UsageError from servicemanager import run
run('frappy', sys.argv[1:])
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: # TODO: change to UsageError
print(repr(e))
print(''.join(USAGE))

View File

@ -24,42 +24,11 @@
import sys import sys
from os.path import join, abspath, dirname from os.path import join, abspath, dirname
# above packages: servman, nicos, frappy, history # for packages: servicemanager, frappyhistory
sys.path.insert(0, abspath(join(dirname(__file__), '../..'))) sys.path.insert(0, abspath(join(dirname(__file__), '../..')))
# for nicos:
sys.path.insert(0, abspath(join(dirname(__file__), '../../nicos')))
from servman import run, FrappyManager, NicosManager, SeaManager, UsageError from servicemanager import run
run('nicos', sys.argv[1:])
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))

32
bin/sea
View File

@ -24,35 +24,9 @@
import sys import sys
from os.path import join, abspath, dirname from os.path import join, abspath, dirname
# above packages: servman, nicos, frappy, history # for packages: servicemanager, frappyhistory
sys.path.insert(0, abspath(join(dirname(__file__), '../..'))) sys.path.insert(0, abspath(join(dirname(__file__), '../..')))
from servman import run, FrappyManager, NicosManager, SeaManager, UsageError from servicemanager import run
run('sea', sys.argv[1:])
NicosManager()
FrappyManager()
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: # TODO: change to UsageError
raise
print(repr(e))
print(''.join(USAGE))

12
frappyman.py → bin/seweb Normal file → Executable file
View File

@ -1,3 +1,4 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# ***************************************************************************** # *****************************************************************************
# #
@ -20,9 +21,12 @@
# #
# ***************************************************************************** # *****************************************************************************
from servman.base import ServiceManager import sys
from os.path import join, abspath, dirname
# for packages: servicemanager, frappyhistory
sys.path.insert(0, abspath(join(dirname(__file__), '../..')))
class FrappyManager(ServiceManager): from servicemanager import run
group = 'frappy'
services = ('main', 'stick', 'addons') run('seweb', sys.argv[1:])

View File

@ -26,7 +26,7 @@ import shutil
from glob import glob from glob import glob
from os.path import join, abspath, dirname, expanduser, exists, islink from os.path import join, abspath, dirname, expanduser, exists, islink
from configparser import ConfigParser from configparser import ConfigParser
from servman.base import ServiceManager from servicemanager.base import ServiceManager
ENV_KEYS = { ENV_KEYS = {
@ -50,6 +50,27 @@ def copy_all(srcdir, dstdir):
class NicosManager(ServiceManager): class NicosManager(ServiceManager):
group = 'nicos' group = 'nicos'
services = ('cache', 'daemon', 'poller') services = ('cache', 'daemon', 'poller')
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)
"""
def do_create(self, ins, *args): def do_create(self, ins, *args):
self.get_info() self.get_info()

View File

@ -24,15 +24,15 @@ import sys
import time import time
import termios import termios
import subprocess import subprocess
from servman.base import ServiceManager from servicemanager.base import ServiceManager, ServiceDown
from servman.sicsclient import sics_client from servicemanager.sicsclient import sics_client
def run_command(cmd, wait=False): def run_command(cmd, wait=False):
if wait: if wait:
old = termios.tcgetattr(sys.stdin) old = termios.tcgetattr(sys.stdin)
proc = subprocess.Popen(cmd.split())
try: try:
proc = subprocess.Popen(cmd.split())
proc.wait() proc.wait()
except KeyboardInterrupt: except KeyboardInterrupt:
proc.terminate() proc.terminate()
@ -47,6 +47,20 @@ def run_command(cmd, wait=False):
class SeaManager(ServiceManager): class SeaManager(ServiceManager):
group = 'sea' group = 'sea'
services = ('sea', 'graph') services = ('sea', 'graph')
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
"""
def do_cli(self, ins): def do_cli(self, ins):
try: try:
@ -55,7 +69,7 @@ class SeaManager(ServiceManager):
print('%s, try to start...' % e) print('%s, try to start...' % e)
self.do_start(ins) self.do_start(ins)
time.sleep(1) # make sure caller did read the message time.sleep(1) # make sure caller did read the message
except KeyError as e: # running on an other machine? except KeyError: # running on an other machine?
pass pass
run_command('six -sea %s' % ins, wait=True) run_command('six -sea %s' % ins, wait=True)
@ -66,13 +80,12 @@ class SeaManager(ServiceManager):
print('%s, try to start...' % e) print('%s, try to start...' % e)
self.do_start(ins) self.do_start(ins)
time.sleep(1) # make sure caller did read the message time.sleep(1) # make sure caller did read the message
except KeyError as e: # running on an other machine? except KeyError: # running on an other machine?
pass pass
run_command('SeaClient %s' % ins) run_command('SeaClient %s' % ins)
print('starting sea gui %s' % ins) print('starting sea gui %s' % ins)
time.sleep(5) time.sleep(5)
def get_cfg(self, ins, service): def get_cfg(self, ins, service):
"""return cfg info about running programs, if relevant """return cfg info about running programs, if relevant