# ***************************************************************************** # 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 # ***************************************************************************** import sys import time import socket from pathlib import Path from configparser import ConfigParser DEFAULT_CFG = """[this] instrument={instrument} frappy_ports = 15000-15009 """ MARCHESRC = ['/home/software/marche'] CFGDIRS = ['/home/linse/config', '/home/l_samenv/linse_config'] def read_config(filename): parser = ConfigParser() parser.optxform = str parser.read([str(filename)]) return dict(parser) def write_config(filename, newvalues): parser = ConfigParser() parser.optxform = str parser.read([str(filename)]) parser.read_dict(newvalues) with open(filename, 'w') as f: parser.write(f) class ArgError(ValueError): pass class Config: def __init__(self): configfile = Path('~/.config/linsetools.cfg').expanduser() self.sections = read_config(configfile) self.instruments = {} for key, section in self.sections.items(): if key == 'this': host = socket.gethostname().split('.')[0] section = {'instrument': host, 'frappy_ports': '15000-15009'} mandatory = set(section) this = self.sections.get('this', {}) section.update(this) self.sections['this'] = section if set(this) & mandatory != mandatory: write_config(configfile, self.sections) self.this = section['instrument'] self.instruments[self.this] = section else: head, _, tail = key.partition('.') if tail and head == 'instrument': self.instruments[tail] = section class Logger: loggers = {} levels = ['error', 'warning', 'info', 'debug'] def __new__(cls, level): log = cls.loggers.get(level) if log: print('old') return log log = object.__new__(cls) cls.loggers[level] = log method = log.show for lev in log.levels: setattr(log, lev, method) print(lev, level) if lev == level: method = log.hide return log def show(self, fmt, *args): print(fmt % args) def hide(self, fmt, *args): pass for marchedir in MARCHESRC: if Path(marchedir).is_dir(): sys.path.append(marchedir) import marche.jobs as mj from marche.client import Client break STATUS_MAP = { # values are (, name) mj.DEAD: (False, False, 'DEAD'), mj.NOT_RUNNING: (False, False, 'NOT RUNNING'), mj.STARTING: (True, True, 'STARTING'), mj.INITIALIZING: (True, True, 'INITIALIZING'), mj.RUNNING: (False, True, 'RUNNING'), mj.WARNING: (False, True, 'WARNING'), mj.STOPPING: (True, False, 'STOPPING'), mj.NOT_AVAILABLE: (False, False, 'NOT AVAILABLE'), } def wait_status(cl, service): delay = 0.2 while True: sts = cl.getServiceStatus(service) if STATUS_MAP[sts][0]: # busy if delay > 1.5: # this happens after about 5 sec return False time.sleep(delay) delay *= 1.225 continue return True class MarcheControl: port = 8124 def __init__(self, host, port=None, user=None, instrument=None): self.config = Config() self.host = host self.instance = instrument or 'this' self.instrument = self.config.this if self.instance == 'this' else self.instance self.ins_config = self.config.instruments[self.insttrument] self.user = user or self.instrument # SINQ instruments if port is not None: self.port = port self._client = None self.argmap = {k: 'action' for k in ('start', 'restart', 'stop', 'gui', 'cli', 'list')} self.argmap.update((k, 'instrument') for k in self.config.instruments) def connect(self): if self._client is None: # TODO: may need more generic solution for last arg print(self.host, self.port, self.user) self._client = Client(self.host, self.port, self.user, self.user.upper() + 'LNS') # TODO; do we need disconnect? def get_service(self, instance): return instance def start(self, instance): self.connect() self._client.startService(self.get_service(instance)) def restart(self, instance): self.connect() self._client.restartService(self.get_service(instance)) def stop(self, instance): self.connect() self._client.stopService(self.get_service(instance)) def status(self, service): """returns a dict of (, , )""" self.connect() servdict = self._client.getAllServiceInfo().get(service) if not servdict: return {} statedict = servdict['instances'] return {k: STATUS_MAP[v['state']] for k, v in statedict.items()} def reload(self): self.connect() self._client.reloadJobs() def _run(self, action, other): if action == 'start': self.start(other) elif action == 'restart': self.restart(other) elif action == 'stop': self.stop(other) else: raise ArgError(f'unknown action {action!r}') def run(self, *args): self._run(self.parse_args(*args)) def parse_args(self, *args): result = {} for arg in args: if arg in self.argmap: kind = self.argmap.get(arg, 'other') if kind in result: raise ArgError(f'duplicate {kind}') result[kind] = arg return result