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 \