From bef18d2b52cca642d53061cee364f1b8b0a4f6f5 Mon Sep 17 00:00:00 2001 From: Jerome Sagnole Date: Thu, 2 Nov 2017 21:55:29 +0100 Subject: [PATCH] adding manager (manage khaganat process) --- code/khaganat/tools/client.py | 179 ++++ code/khaganat/tools/manage.py | 824 ++++++++++++++++++ dist/docker/server/debian/common/khaganat.cfg | 246 ++++++ .../common/servercontainer_configure_auto.sh | 1 + .../servercontainer_configure_launcher.sh | 88 ++ .../common/servercontainer_configure_link.sh | 5 + .../common/servercontainer_launch_auto.sh | 18 +- .../debian/jessie/x86_64/server-container.sh | 8 + 8 files changed, 1362 insertions(+), 7 deletions(-) create mode 100755 code/khaganat/tools/client.py create mode 100755 code/khaganat/tools/manage.py create mode 100644 dist/docker/server/debian/common/khaganat.cfg create mode 100755 dist/docker/server/debian/common/servercontainer_configure_launcher.sh diff --git a/code/khaganat/tools/client.py b/code/khaganat/tools/client.py new file mode 100755 index 000000000..477b5b7e8 --- /dev/null +++ b/code/khaganat/tools/client.py @@ -0,0 +1,179 @@ +#!/usr/bin/python3 +# +# script to send command to manager khaganat process +# +# Copyright (C) 2017 AleaJactaEst +# +# 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 3 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, see . + +# ./client.py --server='172.17.0.2' + +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --program="aes" --command="START" +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --program="aes" --command="STATUS" +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --program="aes" --command="ACTION" --action="coucou" +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --program="aes" --command="LOG" --firstline=0 +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --program="aes" --command="STOP" +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --command="LIST" +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --command="SHUTDOWN" +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --command="STARTALL" +# ./client.py --log="debug" --show-log-console --server='172.17.0.2' --command="STATUSALL" +# ./client.py --key="/home/gameserver/khanat/key.pem" --cert="/home/gameserver/khanat/cert.pem" --log="debug" --show-log-console --command="STATUSALL" + + +import argparse +import logging +import logging.config +import http.client +import json + +#ip='localhost' +def send_command(command='GET', path='/', host='localhost', port=8000): + conn = http.client.HTTPSConnection(host=host, port=port, key_file='crt/key.pem', cert_file='crt/cert.pem' ) + conn.putrequest(command, path) + conn.endheaders() + response = conn.getresponse() + print(response.read()) + +def cmp_to_key(): + 'Convert a cmp= function into a key= function' + class K(object): + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + try: + return int(self.obj) < int(other.obj) + except: + return self.obj < other.obj + def __gt__(self, other): + try: + return int(self.obj) > int(other.obj) + except: + return self.obj > other.obj + def __eq__(self, other): + try: + return int(self.obj) == int(other.obj) + except: + return self.obj == other.obj + def __le__(self, other): + try: + return int(self.obj) <= int(other.obj) + except: + return self.obj <= other.obj + def __ge__(self, other): + try: + return int(self.obj) >= int(other.obj) + except: + return self.obj >= other.obj + def __ne__(self, other): + try: + return int(self.obj) != int(other.obj) + except: + return self.obj != other.obj + return K + +def send_json(jsonin={}, command='GET', path='/', host='localhost', port=8000, raw_data=False, remove_color=False, + key_file=None, cert_file=None): + conn = http.client.HTTPSConnection(host=host, port=port, key_file=key_file, cert_file=cert_file ) + conn.putrequest(command, path) + out=json.dumps(jsonin) + conn.putheader('Content-type', 'application/json') + #length = int(self.headers.getheader('content-length')) + conn.putheader('Content-length', len(out)) + conn.endheaders() + conn.send(bytes(out, "utf-8")) + response = conn.getresponse() + if raw_data: + print(response.read()) + else: + if remove_color: + endText = '\x1b[0m' + else: + endText = '' + if response.status != 200: + logging.error("Error detected (html code:%d)" % response.status) + print(response.read()) + return + ret = response.read().decode() + try: + msgjson = json.loads(ret) + except: + logging.error("Impossible to decode Json output") + print(ret) + return + for key in sorted(msgjson, key=cmp_to_key()): + print("%s: %s %s" % (key, msgjson[key], endText)) + + +def main(server, command, program, action, firstline, fileLog, logLevel, show_log_console, port=8000, + raw_data=False, remove_color=False, key_file=None, cert_file=None): + # Manage log + logging.getLogger('logging') + numeric_level = getattr(logging, logLevel.upper(), None) + if not isinstance(numeric_level, int): + raise ValueError('Invalid log level: %s' % logLevel) + handlers=[] + if show_log_console: + handlers.append(logging.StreamHandler()) + if fileLog: + handlers.append(logging.FileHandler(fileLog.name)) + logging.basicConfig(handlers=handlers, level=numeric_level, + format='%(asctime)s %(levelname)s [pid:%(process)d] [%(funcName)s:%(lineno)d] %(message)s') + #client(server, command, data) + #send_json({'name': 'aes', 'first-line': 0}, 'GET', '/LOG', server, port) + if command == 'START' or command == 'STOP': + send_json({'name': program}, 'POST', "/" + command, server, port) + elif command == 'STATUS': + send_json({'name': program}, 'GET', "/" + command, server, port) + elif command == 'ACTION': + send_json({'name': program, 'action' : action}, 'POST', "/" + command, server, port) + elif command == 'LOG': + send_json({'name': program, 'first-line' : firstline }, 'GET', "/" + command, server, port, raw_data, remove_color) + elif command == 'LIST': + send_json({}, 'GET', "/" + command, server, port) + elif command == 'SHUTDOWN' or command == 'STARTALL' or command == 'STOPALL': + send_json({}, 'POST', "/" + command, server, port) + elif command == 'STATUSALL': + send_json({}, 'GET', "/" + command, server, port) + else: + logging.error("command unknown (%s)" % command) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Manipulate khaganat process') + parser.add_argument('--version', action='version', version='%(prog)s 1.0') + parser.add_argument( '--show-log-console', action='store_true', + help='show message in console', default=False) + parser.add_argument('--filelog', type=argparse.FileType('wt'), + default=None, help='log file') + parser.add_argument('--log', + default='INFO', help='log level [DEBUG, INFO, WARNING, ERROR') + parser.add_argument('--key', help='key file', default=None) + parser.add_argument('--cert', help='cert file', default=None) + parser.add_argument('--server', help='server khganat', default='127.0.0.1') + parser.add_argument('--command', help='command send to khganat', default='/STATUS') + parser.add_argument('--program', help='program khaganat id ', default='aes') + parser.add_argument('--action', help='action ', default='') + parser.add_argument('--firstline', type=int, + help='define fistline read for log command', default=0) + parser.add_argument( '--raw-data', action='store_true', + help='show raw message', default=False) + parser.add_argument( '--keep-color', action='store_true', + help='some message have color define, by default we reset the color (this option keep current color state)', default=False) + args = parser.parse_args() + main(server = args.server, action = args.action, firstline = args.firstline, + command = args.command, program = args.program, fileLog = args.filelog, + logLevel=args.log, show_log_console=args.show_log_console, + raw_data = args.raw_data, + key_file=args.key, cert_file=args.cert, + remove_color=not args.keep_color) diff --git a/code/khaganat/tools/manage.py b/code/khaganat/tools/manage.py new file mode 100755 index 000000000..93fe852aa --- /dev/null +++ b/code/khaganat/tools/manage.py @@ -0,0 +1,824 @@ +#!/usr/bin/python3 +# +# script to start/stop/status/send command/read log for khaganat process +# +# Copyright (C) 2017 AleaJactaEst +# +# 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 3 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, see . + +""" + Manage all process khaganat + Launch this prorgam in background and use clientManager to manipulate process + + you can launch command : + [POST] SHUTDOWN : Stop all process and stop manager + [POST] STARTALL : Start all process + [GET] STATUSALL : Get status all process + [POST] STOPALL : Stop all process + [POST] START {'name': program} : Start one program + [POST] ACTION {'name': program, 'action' : action} : Send action one program (send to input program) + [GET] STATUS {'name': program} : Get status one program + [POST] STOP {'name': program} : Stop one program + [GET] LOG {'name': program, 'first-line': firstline } : Get log for one program + +Configuration File : This script need configuration file (see below for model) +------------------------------------------------------------------------------ +[config] +# Define port listen (default 8000) +port = 8000 + +# Generate key +# openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -subj "/C=FR/ST=France/L=Paris/O=khaganat/CN=khaganat.org" + +# key +keyfile = crt/key.pem + +# certificate +certfile = crt/cert.pem + +# address listen (default all port) +address = + +# Admin Executor Service +[aes] +# command to launch the program +command = ryzom_admin_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/log/khanat --nobreak --fulladminname=admin_executor_service --shortadminname=AES +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 +# buffer size (define value bufsize on subprocess.Popen, this buffer is use before read by manager) +bufsize = 100 + +# bms_master : backup_service +[bms_master] +# command to launch the program +command = ryzom_backup_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid -P49990 +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# we keep [logsize] last number line stdout +logsize = 1000 +# buffer size (define value bufsize on subprocess.Popen) +bufsize = 100 +------------------------------------------------------------------------------ +Example : + nohup ./manage.py --log info --filelog /home/gameserver/log/manager.log -c khaganat.cfg 2>/dev/null 1>/dev/null 0 %s" % (command, name, result)) + + outjson={'state': result} + self._set_headers() + self.wfile.write(bytes(json.dumps(outjson), "utf-8")) + + def do_GET(self): # READ + """ Manage request READ + currently, we execute LOG, STATUS & LIST + """ + logging.debug('get recieved : %s' % self.path) + if self.path == '/LOG': + self.command_log() + elif self.path == '/STATUS': + self.send_command("STATUS") + elif self.path == '/LIST': + self.send_list() + elif self.path == '/STATUSALL': + self.send_command_all("STATUS") + else: + self.send_error(400,'Path unknown') + logging.error("Path unknwon '%s'" % self.path) + return + + def do_POST(self): # CREATE + """ Manage request POST + currently, we execute START, STOP, ACTION & SHUTDOWN + """ + logging.debug('post recieved : %s' % self.path) + if self.path == '/START': + self.send_command("START") + elif self.path == '/STOP': + self.send_command("STOP") + elif self.path == '/ACTION': + self.send_action() + elif self.path == '/SHUTDOWN': + self.send_shutdown() + elif self.path == '/STARTALL': + self.send_command_all("START") + elif self.path == '/STOPALL': + self.send_command_all("STOP") + else: + self.send_error(400,'Path unknown') + logging.error("Path unknwon '%s'" % self.path) + return + + def do_HEAD(self): + """ request HEAD received """ + logging.debug('head recieved : %s' % self.path) + self.send_error(404,'File Not Found: %s' % self.path) + + def do_PUT(self): # UPDATE/REPLACE + """ request PUT received """ + logging.debug('put recieved!') + self.send_error(404,'File Not Found: %s' % self.path) + def do_PATCH(self): # UPDATE/MODIFY + """ request PATCH received """ + logging.debug('patch recieved!') + self.send_error(404,'File Not Found: %s' % self.path) + def do_DELETE(self): # DELETE + """ request DELETE received """ + logging.debug('delete recieved!') + self.send_error(404,'File Not Found: %s' % self.path) + + +class khaganatHTTPServer(http.server.HTTPServer): + """ + Class khaganatHTTPServer + """ + def __init__(self, + listQueueIn, + listQueueOut, + listEvent, + server_address, + RequestHandlerClass, + bind_and_activate=True): + http.server.HTTPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate) + self.listQueueIn = listQueueIn + self.listQueueOut = listQueueOut + self.listEvent = listEvent + +class ServerHttp(multiprocessing.Process): + """ Initialize server HTTPS """ + def __init__(self, keyfile, certfile, address = '', port=8000): + multiprocessing.Process.__init__(self) + self.listQueueIn = {} + self.listQueueOut = {} + self.listEvent = {} + self.port = port + self.keyfile = keyfile + self.certfile = certfile + self.address = address + + def run(self): + server_address = (self.address, self.port) + httpd = khaganatHTTPServer(self.listQueueIn, + self.listQueueOut, + self.listEvent, + server_address, + ManageHttpRequest) + httpd.socket = ssl.wrap_socket (httpd.socket, + keyfile = self.keyfile, + certfile = self.certfile, + ca_certs=None, + server_side = True) + httpd.serve_forever() + + def append(self, name, queueIn, queueOut, event): + self.listQueueIn.setdefault(name, queueIn) + self.listQueueOut.setdefault(name, queueOut) + self.listEvent.setdefault(name, event) + + +class ManageCommand(): + """ + Thread manage all program + """ + def __init__(self, name, command, path, logsize, bufsize, queueIn, queueOut, event): + self.process = None + self.queueIn = queueIn + self.queueOut = queueOut + self.name = name + self.command = command + self.path = path + self.log = [] + self.poslastlog = 0 + self.maxlog = logsize + self.event = event + self.bufsize = bufsize + self.threadRead = None + self.running = False + self.state = multiprocessing.Queue() + self.pipeIn, self.pipeOut = multiprocessing.Pipe() + self.eventRunning = threading.Event() + + def read_output(self): + + fl = fcntl.fcntl(self.process.stdout, fcntl.F_GETFL) + fcntl.fcntl(self.process.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK) + logging.debug("Start reader %s " % self.name) + while self.eventRunning.is_set(): + #logging.debug("Start reader %s " % self.name) + try: + line = self.process.stdout.readline() + if not line: + time.sleep(1) + continue + now = time.strftime('%Y/%m/%d %H:%M:%S %Z') + logging.debug("line %s " % line) + self.poslastlog += 1 + while len(self.log) >= self.maxlog: + self.log.pop(0) + msg = line.decode().strip() + self.log.append(now + ' ' + msg) + logging.debug("recu: '%s'" %(msg)) + except: + continue + logging.debug("End reader: '%s'" % self.name) + + def handler(self, signum, frame): + if self.process: + #logging.debug("Send signal %d to '%s'" %(signum, self.name)) + self.process.send_signal(signum) + else: + logging.error("Impossible to send signal %d to '%s'" %(signum, self.name)) + raise IOError("signal received") + + def start(self): + logging.debug("start %s" % (self.name)) + if self.process: + logging.debug("%s already exist" % self.name) + code = self.process.poll() + if code is None: + logging.debug("%s already exist" % self.name) + return "already-started" + else: + logging.debug("%s crashed" % self.name) + code = self.process.wait() + logging.error("%s crashed (return code:%d) - restart program" % (self.name, code)) + try: + self.process = subprocess.Popen(self.command.split(), + cwd = self.path, + shell=False, + bufsize=self.bufsize, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + close_fds=True) + except FileNotFoundError as e: + logging.error("Impossible to start %s (%s)" % (self.name, e)) + return "crashed" + self.eventRunning.set() + if self.threadRead: + self.eventRunning.clear() + self.threadRead.join() + self.threadRead = None + self.running = True + self.threadRead = threading.Thread(target=self.read_output) + self.threadRead.start() + return "started" + + def status(self): + logging.debug("status %s" % (self.name)) + if self.process: + logging.debug("status %s - check" % (self.name)) + code = self.process.poll() + if code is None: + logging.debug("%s status" % (self.name)) + return "started" + else: + logging.error("%s crashed (return code:%d)" % (self.name, code)) + self.process = None + return "stopped" + else: + logging.debug("%s status" % (self.name)) + return "stopped" + + def list_thread(self): + logging.debug('list thread') + #main_thread = threading.currentThread() + for t in threading.enumerate(): + logging.debug('thread %s', t.getName()) + logging.debug("id %d" % t.ident) + + + def stop(self): + logging.debug("stop %s" % (self.name)) + if not self.process: + return "stopped" + else: + code = self.process.poll() + loop = 10 + while (code is None) and (loop > 0): + logging.debug("stop process %s" , self.name) + self.process.send_signal(15) + time.sleep(1) + code = self.process.poll() + loop -= 1 + + loop = 10 + while (code is None) and (loop > 0): + logging.debug("terminate process %s" , self.name) + self.process.terminate() + time.sleep(1) + code = self.process.poll() + loop -= 1 + + loop = 10 + while (code is None) and (loop > 0): + logging.debug("kill process %s" , self.name) + self.process.send_signal(9) + time.sleep(1) + code = self.process.poll() + loop -= 1 + + code = self.process.wait() + self.process = None + if self.threadRead: + self.eventRunning.clear() + self.threadRead.join() + self.threadRead = None + logging.info("%s stopped (return code:%d)" % (self.name, code)) + return "stopped" + + def getlog(self, firstline): + logging.debug("read log %d " % firstline) + outjson = {} + pos = self.poslastlog - len(self.log) + 1 + firstlinefound = None + for line in self.log: + if pos >= firstline: + outjson.setdefault(pos, line) + if not firstlinefound: + firstlinefound = pos + pos += 1 + outjson.setdefault('first-line', firstlinefound) + outjson.setdefault('last-line', pos - 1) + return json.dumps(outjson) + + def action(self, action): + logging.debug("ACTION '%s'" % action) + if self.process: + code = self.process.poll() + if code is None: + if action: + self.process.stdin.write(bytes(action +'\n', 'UTF-8')) + self.process.stdin.flush() + return "ok" + return "ko" + + def run(self): + loop = True + while loop: + logging.debug('wait %s' % self.name) + self.event.wait() + logging.debug('received event %s' % self.name) + try: + msg = self.queueIn.get(timeout = 4) + except queue.Empty: + self.event.clear() + logging.debug("pas de message recu pour %s" % self.name) + return + logging.debug("command : '%s'" % msg) + command = msg.split()[0] + if command == "SHUTDOWN": + loop = False + continue + elif command == "START": + self.queueOut.put(self.start()) + elif command == "STATUS": + self.queueOut.put(self.status()) + elif command == "STOP": + self.queueOut.put(self.stop()) + elif command == "ACTION": + data = msg.split(maxsplit=1)[1] + self.queueOut.put(self.action(data)) + elif command == "LOG": + try: + firstline = int(msg.split(maxsplit=1)[1]) + except: + firstline = 0 + self.queueOut.put(self.getlog(firstline)) + else: + self.queueOut.put("error : command unknown") + self.event.clear() + self.stop() + self.event.clear() + logging.debug('end') + + +def runCommand(name, command, path, logsize, bufsize, queueIn, queueOut, event): + """ + Launch Manager + (thread to manage khaganat program) + """ + logging.debug("Initialize '%s'" % name) + manageCommand = ManageCommand(name=name, + command=command, + path=path, + logsize=logsize, + bufsize=bufsize, + queueIn=queueIn, + queueOut=queueOut, + event=event) + manageCommand.run() + + +class Manager(): + def __init__(self, filecfg, launch_program): + self.threadCommand = None + self.command = [] + self.launch_program = launch_program + self.param = {} + + config = configparser.ConfigParser() + config.read_file(filecfg) + logging.debug("Sections :%s" % config.sections()) + for name in config.sections(): + if name == 'config': + logging.debug("read config '%s'" % name) + try: + port = int(config[name]['port']) + except: + port = 8000 + try: + address = config[name]['address'] + except: + address = '' + try: + keyfile = config[name]['keyfile'] + except: + keyfile = 'crt/key.pem' + try: + certfile = config[name]['certfile'] + except: + certfile = 'crt/cert.pem' + elif 'command' in config[name]: + logging.debug("read command '%s'" % name) + if 'path' in config[name]: + path = config[name]['path'] + else: + path = None + if 'logsize' in config[name]: + try: + logsize = int(config[name]['logsize']) + except: + logsize = 100 + logging.warning("Impossible to read param logsize (command:%s)", name) + else: + logsize = 100 + if 'bufsize' in config[name]: + try: + bufsize = int(config[name]['bufsize']) + except: + bufsize = 100 + logging.warning("Impossible to read param bufsize (command:%s)", name) + else: + bufsize = 100 + self.param.setdefault(name, {'command': config[name]['command'], 'path': path, 'logsize': logsize, 'bufsize': bufsize}) + + self.serverHttp = ServerHttp(keyfile, certfile, address, port) + if filecfg is None: + raise ValueError + + def launch_server_http(self): + self.serverHttp.daemon = True + self.serverHttp .start() + + def launch_command(self): + for name in self.param: + logging.debug("Initialize '%s'" % name) + queueIn = multiprocessing.Queue() + queueOut = multiprocessing.Queue() + event = multiprocessing.Event() + self.serverHttp.append(name, queueIn, queueOut, event) + self.threadCommand = multiprocessing.Process(target=runCommand, + args=(name, + self.param[name]['command'], + self.param[name]['path'], + self.param[name]['logsize'], + self.param[name]['bufsize'], + queueIn, + queueOut, + event)) + self.threadCommand.start() + if self.launch_program: + event.set() + queueIn.put("START") + try: + item = queueOut.get(timeout = 4) + except queue.Empty: + item = "" + logging.debug("pas de message recu pour %s" % name) + return + logging.info("%s => %s" % (name, item)) + + + def receive_signal(self, signum, frame): + if self.threadCommand: + print(dir(self.threadCommand)) + self.threadCommand.terminate() + if self.serverHttp: + self.serverHttp.terminate() + + def run(self): + self.launch_command() + self.launch_server_http() + logging.info('started') + self.threadCommand.join() + logging.info('end') + signal.alarm(0) + logging.info('wait thread http') + time.sleep(1) + self.serverHttp.terminate() + self.serverHttp.join() + logging.info('end') + + +def main(filecfg, fileLog, logLevel, launch_program, show_log_console): + """ Main function """ + # Manage log + logging.getLogger('logging') + numeric_level = getattr(logging, logLevel.upper(), None) + if not isinstance(numeric_level, int): + raise ValueError('Invalid log level: %s' % logLevel) + handlers=[] + if show_log_console: + handlers.append(logging.StreamHandler()) + if fileLog: + handlers.append(logging.FileHandler(fileLog.name)) + logging.basicConfig(handlers=handlers, level=numeric_level, + format='%(asctime)s %(levelname)s [pid:%(process)d] [%(funcName)s:%(lineno)d] %(message)s') + if filecfg is None: + logging.error("Missing configuration file") + raise ValueError + manager = Manager(filecfg, launch_program) + manager.run() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Manage khaganat process') + parser.add_argument('--version', action='version', version='%(prog)s 1.0') + parser.add_argument('-c', '--conf', type=argparse.FileType('r'), + default='khaganat.cfg', help='configuration file') + parser.add_argument( '--show-log-console', action='store_true', + help='show message in console', default=False) + parser.add_argument('--filelog', type=argparse.FileType('wt'), + default=None, help='log file') + parser.add_argument('--log', + default='INFO', help='log level [DEBUG, INFO, WARNING, ERROR') + parser.add_argument( '--launch-program', action='store_true', + help='launch program when start manager', default=False) + args = parser.parse_args() + main(filecfg=args.conf, + fileLog=args.filelog, + logLevel=args.log, + launch_program=args.launch_program, + show_log_console=args.show_log_console) diff --git a/dist/docker/server/debian/common/khaganat.cfg b/dist/docker/server/debian/common/khaganat.cfg new file mode 100644 index 000000000..3e8a0aedf --- /dev/null +++ b/dist/docker/server/debian/common/khaganat.cfg @@ -0,0 +1,246 @@ +# +# Configuration process khaganat +# +# Copyright (C) 2017 AleaJactaEst +# +# 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 3 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, see . + +############################## +############################## +# Global parameter +############################## +############################## +[config] +# Define port listen (default 8000) +port = 8000 + +# Generate key +# openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -subj "/C=FR/ST=France/L=Paris/O=khaganat/CN=khaganat.org" + +# key +keyfile = /home/gameserver/khanat/key.pem + +# certificate +certfile = /home/gameserver/khanat/cert.pem + +# address listen (default all port) +address = + +############################## +############################## +# List all program we manage # +############################## +############################## + +############################## +# Admin Executor Service +############################## +[aes] +# command to launch the program +command = ryzom_admin_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/log/khanat --nobreak --fulladminname=admin_executor_service --shortadminname=AES +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 +# buffer size (define value bufsize on subprocess.Popen, this buffer is use before read by manager) +bufsize = 100 + +############################## +# bms_master : backup_service +############################## +[bms_master] +# command to launch the program +command = ryzom_backup_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid -P49990 +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +#[bms_pd_master] +# # command to launch the program +# command = ryzom_backup_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid -P49992 +# # Path : where this program is launched +# path = /home/gameserver/khanat/server/ +# # size buffer log for each program launched (number line stdout) +# logsize = 1000 + +############################## +# egs : entities_game_service +############################## +[egs] +# command to launch the program +command = ryzom_entities_game_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# gpms : gpm_service +############################## +[gpms] +# command to launch the program +command = ryzom_gpm_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/gpms +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# ios : input_output_service +############################## +[ios] +# command to launch the program +command = ryzom_ios_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# rns : naming_service +############################## +[rns] +# command to launch the program +command = ryzom_naming_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# rws : welcome_service +############################## +[rws] +# command to launch the program +command = ryzom_welcome_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# ts : tick_service +############################## +[ts] +# command to launch the program +command = ryzom_tick_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# ms : mirror_service +############################## +[ms] +# command to launch the program +command = ryzom_mirror_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# ais_newbyland : ai_service +############################## +[ais_newbyland] +# command to launch the program +command = ryzom_ai_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid -mCommon:Newbieland:Post +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# mfs : mail_forum_service +############################## +[mfs] +# command to launch the program +command = ryzom_mail_forum_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# su : shard_unifier_service +############################## +[su] +# command to launch the program +command = ryzom_shard_unifier_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# fes : frontend_service +############################## +[fes] +# command to launch the program +command = ryzom_frontend_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# sbs : session_browser_server +############################## +[sbs] +# command to launch the program +command = ryzom_session_browser_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +############################## +# lgs : logger_service +############################## +[lgs] +# command to launch the program +command = ryzom_logger_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 + +# [mos] +# # command to launch the program +# command = ryzom_monitor_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# # Path : where this program is launched +# path = /home/gameserver/khanat/server/ +# # size buffer log for each program launched (number line stdout) +# logsize = 1000 + +# [pdss] +# # command to launch the program +# command = ryzom_pd_support_service -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# # Path : where this program is launched +# path = /home/gameserver/khanat/server/ +# # size buffer log for each program launched (number line stdout) +# logsize = 1000 + +############################## +# ras : admin_service +############################## +[ras] +# command to launch the program +command = ryzom_admin_service --fulladminname=admin_service --shortadminname=AS -A/home/gameserver/khanat/server -C/home/gameserver/khanat/server -L/home/gameserver/khanat/server/log --nobreak --writepid +# Path : where this program is launched +path = /home/gameserver/khanat/server/ +# size buffer log for each program launched (number line stdout) +logsize = 1000 diff --git a/dist/docker/server/debian/common/servercontainer_configure_auto.sh b/dist/docker/server/debian/common/servercontainer_configure_auto.sh index d3ff91f73..67b8e3d91 100755 --- a/dist/docker/server/debian/common/servercontainer_configure_auto.sh +++ b/dist/docker/server/debian/common/servercontainer_configure_auto.sh @@ -24,6 +24,7 @@ sync /opt/ext/servercontainer_configure_world.sh || exit 2 su -c '/opt/ext/servercontainer_configure_khanat.sh' gameserver || exit 2 su -c '/opt/ext/servercontainer_configure_patch.sh' gameserver || exit 2 +su -c '/opt/ext/servercontainer_configure_launcher.sh' gameserver || exit 2 su -c 'touch /home/gameserver/khanat/step_configure.ok' gameserver || exit 2 sync exit 0 diff --git a/dist/docker/server/debian/common/servercontainer_configure_launcher.sh b/dist/docker/server/debian/common/servercontainer_configure_launcher.sh new file mode 100755 index 000000000..290c10ef6 --- /dev/null +++ b/dist/docker/server/debian/common/servercontainer_configure_launcher.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# +# Configure Launcher +# +# Copyright (C) 2017 AleaJactaEst +# +# 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 3 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, see . + +usage() +{ +cat << EOF +usage:$0 [options] + Configure Launcher (certificate) + +options: + -h, --help : Show this help + -d, --debug : Show debug message +EOF +} + +##################### +# MAIN +##################### +source /opt/ext/servercontainer_function.sh +msg_info "$(basename $0) => START" + +while test $# -gt 0 +do + case "$1" in + -h|--help) + usage + exit 1 + ;; + -d|--debug) + set_debug 1 + shift + ;; + *) + msg_error "options '$1' not recoginze" + usage + exit 1 + ;; + esac +done + +#################################### +# Load Environment +#################################### +msg_debug "Load environment" +if [[ ! -f /opt/khanat_config.sh ]] +then + echo "ERROR - missing /opt/khanat_config.sh" + exit 2 +fi +source /opt/khanat_config.sh + +if [[ ! -f /home/gameserver/.bashrc ]] +then + echo "ERROR - missing /home/gameserver/.bashrc" + exit 2 +fi +source /home/gameserver/.bashrc + +#################################### +# Create new certificat +#################################### +openssl req -nodes -x509 -newkey rsa:2048 \ + -keyout "$KHANAT_PATH/key.pem" \ + -out "$KHANAT_PATH/cert.pem" \ + -days 365 \ + -subj "/C=FR/ST=France/L=Paris/O=khaganat/CN=khaganat.org" || exit 2 + +#################################### +# End +#################################### +msg_info "$(basename $0) => END" + diff --git a/dist/docker/server/debian/common/servercontainer_configure_link.sh b/dist/docker/server/debian/common/servercontainer_configure_link.sh index 516307db2..17b0370a5 100755 --- a/dist/docker/server/debian/common/servercontainer_configure_link.sh +++ b/dist/docker/server/debian/common/servercontainer_configure_link.sh @@ -178,6 +178,11 @@ create_dir_gameserver "$KHANAT_PATH/tools/scripts/linux" || exit 2 create_recursive_link '/home/gameserver/ext/ryzom-ressources/tools/scripts/linux' "$KHANAT_PATH/tools/scripts/linux" || exit 2 +#################################### +# Link tools khaganat +#################################### +create_link '/opt/ext/khaganat.cfg' "$KHANAT_PATH" + #################################### # End #################################### diff --git a/dist/docker/server/debian/common/servercontainer_launch_auto.sh b/dist/docker/server/debian/common/servercontainer_launch_auto.sh index 87ffa5f67..57743cc9f 100755 --- a/dist/docker/server/debian/common/servercontainer_launch_auto.sh +++ b/dist/docker/server/debian/common/servercontainer_launch_auto.sh @@ -32,6 +32,7 @@ options: --show-status-nagios : show status (ater start) --show-status : show status (ater start) --bash-after-start : command bash after start + --start-with-manager : start khanat with manager EOF } @@ -191,6 +192,10 @@ do METHOD_START=5 shift ;; + --start-with-manager) + METHOD_START=6 + shift + ;; *) msg_error "options '$1' not recoginze" usage @@ -273,39 +278,38 @@ WHERE domain_id = 12;" || exit 2 msg_debug "Start khanat" if [[ $METHOD_START -eq 0 ]] then - #create_default_file_for_screen source /home/gameserver/.bashrc; export RYZOM_PATH=$KHANAT_PATH; cd "$RYZOM_PATH"; $KHANAT_HOME/khanat/tools/scripts/linux/shard start - #sudo -u gameserver 'source /home/gameserver/.bashrc; export RYZOM_PATH=$KHANAT_PATH; echo ".$RYZOM_PATH."; $KHANAT_HOME/khanat/tools/scripts/linux/shard start' elif [[ $METHOD_START -eq 1 ]] then - #su -c /opt/ext/servercontainer_launch_service.sh gameserver /opt/ext/servercontainer_launch_service.sh sleep 10 tail -n+0 -f /home/gameserver/log/khanat/log.log elif [[ $METHOD_START -eq 2 ]] then - #su -c /opt/ext/servercontainer_launch_service.sh gameserver /opt/ext/servercontainer_launch_service.sh sleep 10 watch cat /home/gameserver/khanat/server/aes_nagios_report.txt elif [[ $METHOD_START -eq 3 ]] then - #su -c /opt/ext/servercontainer_launch_service.sh gameserver /opt/ext/servercontainer_launch_service.sh sleep 10 tail -n+0 -f /home/gameserver/log/apache2/* /home/gameserver/log/mysql/* /home/gameserver/log/khanat/* elif [[ $METHOD_START -eq 4 ]] then - #su -c /opt/ext/servercontainer_launch_service.sh gameserver /opt/ext/servercontainer_launch_service.sh sleep 10 watch /opt/ext/servercontainer_launch_status.sh --no-color elif [[ $METHOD_START -eq 5 ]] then - #su -c /opt/ext/servercontainer_launch_service.sh gameserver /opt/ext/servercontainer_launch_service.sh #sleep 10 bash +elif [[ $METHOD_START -eq 6 ]] +then + mkdir -p /home/gameserver/log/khanat + mkdir -p /home/gameserver/khanat/server/gpms + /home/gameserver/ext/khaganat/tools/manage.py --log info --show-log-console --filelog /home/gameserver/log/khanat/manager.log -c /home/gameserver/khanat/khaganat.cfg --launch-program + bash else msg_error 'Bad option' exit 2 diff --git a/dist/docker/server/debian/jessie/x86_64/server-container.sh b/dist/docker/server/debian/jessie/x86_64/server-container.sh index b0946268c..d45d1975f 100755 --- a/dist/docker/server/debian/jessie/x86_64/server-container.sh +++ b/dist/docker/server/debian/jessie/x86_64/server-container.sh @@ -85,6 +85,7 @@ options: --start-khanat-with-watch-state : start server khanat and show state (loop) --start-khanat-with-watch-state-nagios : start server khanat and show state [nagios format] (loop) --start-khanat-with-bash-after : start server khanat and launch bash + --start-with-manager : start khanat with manager --ssh : connect on khanat server (with ssh) [Exclusive action, can't execute other action] --client-version=[INTEGER] : version client khanat (we need to communicate with our server) @@ -198,6 +199,10 @@ do METHODSTARTSERVER="--bash-after-start" shift ;; + --start-with-manager) + METHODSTARTSERVER="--start-with-manager" + shift + ;; --client-version*) KHANAT_CLIENT_VERSION="${1#*=}" shift @@ -438,6 +443,7 @@ then -v ${rootdir}/${LOCALBUILDDIR}/bin:/usr/local/bin:ro \ -v ${rootdir}/code/web/:/home/gameserver/ext/khanatweb:ro \ -v ${rootdir}/code/ryzom:/home/gameserver/ext/ryzom-ressources:ro \ + -v ${rootdir}/code/khaganat:/home/gameserver/ext/khaganat:ro \ -v ${KHANAT_RESSOURCES_DIR}:/home/gameserver/ext/khanat-ressources:ro \ -v ${KHANAT_DATA_CLIENT_DIR}:/home/gameserver/ext/khanat-data-client:ro \ -v ${rootdir}/$DIRLOG:/home/gameserver/log:rw \ @@ -455,6 +461,7 @@ then -v ${rootdir}/${LOCALBUILDDIR}/bin:/usr/local/bin:ro \ -v ${rootdir}/code/web/:/home/gameserver/ext/khanatweb:ro \ -v ${rootdir}/code/ryzom:/home/gameserver/ext/ryzom-ressources:ro \ + -v ${rootdir}/code/khaganat:/home/gameserver/ext/khaganat:ro \ -v ${KHANAT_RESSOURCES_DIR}:/home/gameserver/ext/khanat-ressources:ro \ -v ${KHANAT_DATA_CLIENT_DIR}:/home/gameserver/ext/khanat-data-client:ro \ -v ${rootdir}/$DIRLOG:/home/gameserver/log:rw \ @@ -473,6 +480,7 @@ then -v ${rootdir}/${LOCALBUILDDIR}/bin:/usr/local/bin:ro \ -v ${rootdir}/code/web/:/home/gameserver/ext/khanatweb:ro \ -v ${rootdir}/code/ryzom:/home/gameserver/ext/ryzom-ressources:ro \ + -v ${rootdir}/code/khaganat:/home/gameserver/ext/khaganat:ro \ -v ${KHANAT_RESSOURCES_DIR}:/home/gameserver/ext/khanat-ressources:ro \ -v ${KHANAT_DATA_CLIENT_DIR}:/home/gameserver/ext/khanat-data-client:ro \ -v ${rootdir}/$DIRLOG:/home/gameserver/log:rw \