update feature #1

This commit is contained in:
AleaJactaEst 2018-10-09 12:21:50 +02:00
parent 41b23d2e66
commit ef3cc5347d
4 changed files with 357 additions and 276 deletions

View file

@ -17,4 +17,4 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
__version__ = '1.0.0' __version__ = '1.1.0'

View file

@ -70,12 +70,12 @@ This script need configuration file (see below for model)::
bufsize = 100 bufsize = 100
# It's possible to collect some message on output (example player conected) with regex command # It's possible to collect some message on output (example player conected) with regex command
# keep some data on array/dict state # keep some data on array/dict state
keep_state = yes activate_filter = yes
# size array/dict state # size array/dict state
size_max_state = 1000 size_max_filter = 1000
# search regex to add state (python regex) # search regex to add state (python regex)
add_state = "^((.*)(setActiveCharForPlayer).*(: set active char )[\d]+( for )(?P<ActivePlayer>.*)|(.*)(disconnectPlayer)(.+:.+<.+>){0,1}[\s]+(?P<InactivePlayer>.*)[\s]+(is disconnected))" add_filter = "^((.*)(setActiveCharForPlayer).*(: set active char )[\d]+( for )(?P<ActivePlayer>.*)|(.*)(disconnectPlayer)(.+:.+<.+>){0,1}[\s]+(?P<InactivePlayer>.*)[\s]+(is disconnected))"
del_state = "^((.*)(setActiveCharForPlayer).*(: set active char )[\d]+( for )(?P<InactivePlayer>.*)|(.*)(disconnectPlayer)(.+:.+<.+>){0,1}[\s]+(?P<ActivePlayer>.*)[\s]+(is disconnected))" del_filter = "^((.*)(setActiveCharForPlayer).*(: set active char )[\d]+( for )(?P<InactivePlayer>.*)|(.*)(disconnectPlayer)(.+:.+<.+>){0,1}[\s]+(?P<ActivePlayer>.*)[\s]+(is disconnected))"
# autostart (when start OpenNelManager, launch this program) # autostart (when start OpenNelManager, launch this program)
autostart = no autostart = no
# restart after crash # restart after crash
@ -111,37 +111,37 @@ Design
http(s) command : http(s) command :
----------------- -----------------
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **Html command** | **Path** | **Argument** {json format} | **Comment** | | **Html command** | **Path** | **Argument** {json format} | **Comment** | **Return*** |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **POST** | /SHUTDOWN | | Stop all process and stop pymanager | | **POST** | /SHUTDOWN | | Stop all process and stop pymanager | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **POST** | /STARTALL | | Start all processes | | **POST** | /STARTALL | | Start all processes | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **GET** | /STATUSALL | | Get status all processes | | **GET** | /STATUSALL | | Get status all processes | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **POST** | /STOPALL | | Stop all processes | | **POST** | /STOPALL | | Stop all processes | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **POST** | /START | {'name': program} | Start for one program | | **POST** | /START | {'name': program} | Start for one program | {'state': '<STATE>'} |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **POST** | /STDIN | {'name': program, 'action': action} | Send action for one program (send to input) | | **POST** | /STDIN | {'name': program, 'action': action} | Send action for one program (send to input) | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **GET** | /STATUS | {'name': program} | Get status for one program | | **GET** | /STATUS | {'name': program} | Get status for one program | {'state': '<STATE>'} |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **POST** | /STOP | {'name': program} | Stop for one program | | **POST** | /STOP | {'name': program} | Stop for one program | {'state': '<STATE>'} |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **GET** | /STDOUT | {'name': program, 'first-line': firstline } | Get log for one program | | **GET** | /STDOUT | {'name': program, 'first-line': firstline } | Get log for one program | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **GET** | /GETSTATE | {'name': program } | Get all state (key find in stdout add/remove) | | **GET** | /FILTER | {'name': program } | Get all state (key find in stdout add/remove) | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **GET** | /CONFIG | {'name': program } | Get configuration | | **GET** | /CONFIG | {'name': program } | Get configuration | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **GET** | /INFO | {'name': program } | Get Information (number player, ...) | | **GET** | /INFO | {'name': program } | Get Information (number player, ...) | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **GET** | /PLAYER | {'name': program } | Get active player | | **GET** | /PLAYER | {'name': program } | Get active player | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
| **GET** | /ADMINCOMMAND | {'name': program } | Get admin commmand | | **GET** | /ADMINCOMMAND | {'name': program } | Get admin commmand | |
+------------------+------------------+---------------------------------------------+-----------------------------------------------+ +------------------+------------------+---------------------------------------------+-----------------------------------------------+------------------------------+
Example :: Example ::
@ -275,7 +275,7 @@ class ManageHttpRequest(http.server.SimpleHTTPRequestHandler):
return return
self.server.listSemaphore[name].release() self.server.listSemaphore[name].release()
self._set_headers() self._set_headers()
self.wfile.write(bytes(item, "utf-8")) self.wfile.write(bytes(json.dumps(item), "utf-8"))
logging.debug("item : %s" % item) logging.debug("item : %s" % item)
def _send_list(self): def _send_list(self):
@ -343,15 +343,21 @@ class ManageHttpRequest(http.server.SimpleHTTPRequestHandler):
logging.debug("%s:%s" % (name, action)) logging.debug("%s:%s" % (name, action))
self.server.listSemaphore[name].acquire() self.server.listSemaphore[name].acquire()
self.server.listQueueIn[name].put("STDIN %s" % action) self.server.listQueueIn[name].put("STDIN %s" % action)
logging.debug("message envoye: %s" % (name)) logging.debug("message sent: %s" % (name))
try: try:
result = self.server.listQueueOut[name].get(timeout=4) outjson = self.server.listQueueOut[name].get(timeout=4)
except queue.Empty: except queue.Empty:
logging.debug("pas de message recu pour %s" % name) logging.debug("no return %s" % name)
self.server.listSemaphore[name].release()
return
except Exception as e:
# Trap all error (release semaphore)
self.send_error(500, 'Internal Error')
logging.error("unknown error %s (%s)" % (name, e))
self.server.listSemaphore[name].release()
return return
self.server.listSemaphore[name].release() self.server.listSemaphore[name].release()
outjson = {'state': result}
self._set_headers() self._set_headers()
self.wfile.write(bytes(json.dumps(outjson), "utf-8")) self.wfile.write(bytes(json.dumps(outjson), "utf-8"))
@ -377,15 +383,20 @@ class ManageHttpRequest(http.server.SimpleHTTPRequestHandler):
self.server.listSemaphore[name].acquire() self.server.listSemaphore[name].acquire()
self.server.listQueueIn[name].put(command) self.server.listQueueIn[name].put(command)
try: try:
result = self.server.listQueueOut[name].get(timeout=4) outjson = self.server.listQueueOut[name].get(timeout=4)
except queue.Empty: except queue.Empty:
self.send_error(500, 'Missing return') self.send_error(500, 'Missing return')
logging.debug("[%s %s] Missing return" % (command, name)) logging.debug("[%s %s] Missing return" % (command, name))
self.server.listSemaphore[name].release()
return
except Exception as e:
# Trap all error (release semaphore)
self.send_error(500, 'Internal Error')
logging.error("unknown error %s (%s)" % (name, e))
self.server.listSemaphore[name].release()
return return
self.server.listSemaphore[name].release() self.server.listSemaphore[name].release()
logging.debug("[%s %s] => %s" % (command, name, result)) logging.debug("[%s %s] => %s" % (command, name, outjson))
outjson = {'state': result}
self._set_headers() self._set_headers()
self.wfile.write(bytes(json.dumps(outjson), "utf-8")) self.wfile.write(bytes(json.dumps(outjson), "utf-8"))
@ -426,8 +437,8 @@ class ManageHttpRequest(http.server.SimpleHTTPRequestHandler):
logging.error("Wrong authentication") logging.error("Wrong authentication")
elif self.path == '/STDOUT': elif self.path == '/STDOUT':
self._command_log() self._command_log()
elif self.path == "/STATE": elif self.path == "/FILTER":
self._send_command("STATE") self._send_command("FILTER")
elif self.path == "/INFO": elif self.path == "/INFO":
self._send_command("INFO") self._send_command("INFO")
elif self.path == "/PLAYER": elif self.path == "/PLAYER":
@ -571,8 +582,9 @@ class ServerHttp(multiprocessing.Process):
logging.error("Bad value 'method' (%s)" % str(self.method)) logging.error("Bad value 'method' (%s)" % str(self.method))
raise ValueError raise ValueError
httpd.serve_forever() httpd.serve_forever()
logging.info("End")
def append(self, name, queueIn, queueOut, semaphore): def appendchild(self, name, queueIn, queueOut, semaphore):
self.listQueueIn.setdefault(name, queueIn) self.listQueueIn.setdefault(name, queueIn)
self.listQueueOut.setdefault(name, queueOut) self.listQueueOut.setdefault(name, queueOut)
self.listSemaphore.setdefault(name, semaphore) self.listSemaphore.setdefault(name, semaphore)
@ -588,7 +600,7 @@ class ManageCommand():
def __init__(self, name, def __init__(self, name,
command, path, command, path,
logsize, bufsize, queueIn, queueOut, semaphore, logsize, bufsize, queueIn, queueOut, semaphore,
keep_state, size_max_state, add_state, del_state, activate_filter, size_max_filter, add_filter, del_filter,
autostart, restart_after_crash, restart_delay, autostart, restart_after_crash, restart_delay,
egs_filter, egs_filter,
maxWaitEnd=10, waitDelay=1): maxWaitEnd=10, waitDelay=1):
@ -605,36 +617,38 @@ class ManageCommand():
self.bufsize = bufsize self.bufsize = bufsize
self.threadRead = None self.threadRead = None
self.running = False self.running = False
self.state = multiprocessing.Queue() # self.state = multiprocessing.Queue()
self.pipeIn, self.pipeOut = multiprocessing.Pipe() self.pipeIn, self.pipeOut = multiprocessing.Pipe()
self.eventRunningReader = threading.Event() self.eventRunningReader = threading.Event()
self.eventRunningRestart = threading.Event() self.eventRunningRestart = threading.Event()
self.maxWaitEnd = maxWaitEnd self.maxWaitEnd = maxWaitEnd
self.waitDelay = waitDelay self.waitDelay = waitDelay
self.keep_state = keep_state self.activate_filter = activate_filter
self.size_max_state = size_max_state self.size_max_filter = size_max_filter
self.add_state_cmd = add_state[1:-1] self.add_filter_cmd = add_filter[1:-1]
self.del_state_cmd = del_state[1:-1] self.del_filter_cmd = del_filter[1:-1]
self.filter_add_state = re.compile(self.add_state_cmd) self.filter_add_filter = re.compile(self.add_filter_cmd)
self.filter_del_state = re.compile(self.del_state_cmd) self.filter_del_filter = re.compile(self.del_filter_cmd)
self.state = {} self.filter = {}
self.autostart = autostart self.autostart = autostart
self.restart_after_crash = restart_after_crash self.restart_after_crash = restart_after_crash
self.restart_delay = restart_delay self.restart_delay = restart_delay
self.threadRestart = None self.threadRestart = None
self.egs_filter = egs_filter self.egs_filter = egs_filter
self.egs_filter_load_character = re.compile(".*(LOADED User )'(?P<UID>[\d]+)' Character '(?P<NameDomain>[^']+)' from BS stream file 'characters/([\d]+)/account_(?P<UIDBIS>[\d]+)_(?P<IDCHAR>[\d]+)_pdr.bin") self.egs_filter_load_character = re.compile(".*(egs_plinfo).*(: LOADED User )'(?P<UID>[\d]+)' Character '(?P<NameDomain>[^']+)' from BS stream file 'characters/([\d]+)/account_(?P<UIDBIS>[\d]+)_(?P<IDCHAR>[\d]+)_pdr.bin")
self.egs_filter_active_character = re.compile(".*(setActiveCharForPlayer).*(: set active char )(?P<IDCHAR>[\d]+)( for player )(?P<UID>[\d]+)") self.egs_filter_active_character = re.compile(".*(setActiveCharForPlayer).*(: set active char )(?P<IDCHAR>[\d]+)( for player )(?P<UID>[\d]+)")
self.egs_filter_sid = re.compile(".*(Mapping UID )(?P<UID>[\d]+)( => Sid )\((?P<SID>.*)\)") self.egs_filter_sid = re.compile(".*(Mapping UID )(?P<UID>[\d]+)( => Sid )\((?P<SID>.*)\)")
self.egs_filter_client_ready = re.compile(".*(Updating IS_NEWBIE flag for character: )\((?P<ID>.*)\)") self.egs_filter_client_ready = re.compile(".*(Updating IS_NEWBIE flag for character: )\((?P<ID>.*)\)")
self.egs_filter_disconnected = re.compile(".*(disconnectPlayer).+[\s]+(player )(?P<UID>[\d]+)[\s]+(is disconnected)") self.egs_filter_disconnected = re.compile(".*(disconnectPlayer).+[\s]+(player )(?P<UID>[\d]+)[\s]+(is disconnected)")
self.egs_filter_admin = re.compile("(.*)(cbClientAdmin EGS-136 : )(ADMIN)(: Player )\((?P<SID>.*)\)(?P<ACTION>.+)") self.egs_filter_admin = re.compile("(.*)(cbClientAdmin).*(: ADMIN)(: Player )\((?P<SID>.*)\)(?P<ACTION>.+)")
self.state_load_character = {} # cbClientAdmin EGS-133 : ADMIN: Player (0x0000000021:00:00:86) tried to execute a no valid client admin command 'info'
self.state_active_character = {} self.filter_load_character = {}
self.state_admin = [] self.filter_active_character = {}
self.filter_admin = {}
self.number_start = 0 self.number_start = 0
self.first_line = 0 self.first_line = 0
self.last_line = 0 self.last_line = 0
self.pos_admin = 0
def _analyze_line(self, msg): def _analyze_line(self, msg):
now = time.strftime('%Y/%m/%d %H:%M:%S %Z') now = time.strftime('%Y/%m/%d %H:%M:%S %Z')
@ -646,40 +660,40 @@ class ManageCommand():
self.last_line = self.last_line + 1 self.last_line = self.last_line + 1
# If option sate is defined, analyze message and keep state (example , all player connected) # If option sate is defined, analyze message and keep state (example , all player connected)
logging.debug("recu: '%s'" % (msg)) logging.debug("recu: '%s'" % (msg))
if self.keep_state: if self.activate_filter:
res = self.filter_add_state.match(msg) res = self.filter_add_filter.match(msg)
if res: if res:
logging.debug("add_state found") logging.debug("add_filter found")
if len(self.state) < self.size_max_state: if len(self.filter) < self.size_max_filter:
logging.debug("include add_state found") logging.debug("include add_filter found")
dico = res.groupdict() dico = res.groupdict()
for key in dico: for key in dico:
logging.debug("set add_state found [%s]" % (str(key))) logging.debug("set add_filter found [%s]" % (str(key)))
if dico[key]: if dico[key]:
logging.debug("set1 add_state found [%s][%s]" % (str(key), str(dico[key]))) logging.debug("set1 add_filter found [%s][%s]" % (str(key), str(dico[key])))
self.state.setdefault(key, {}) self.filter.setdefault(key, {})
self.state[key].setdefault(dico[key], now) self.filter[key].setdefault(dico[key], now)
res = self.filter_del_state.match(msg) res = self.filter_del_filter.match(msg)
if res: if res:
logging.debug("del_state found") logging.debug("del_filter found")
dico = res.groupdict() dico = res.groupdict()
for key in dico: for key in dico:
logging.debug("prepare del_state found %s" % str(key)) logging.debug("prepare del_filter found %s" % str(key))
if dico[key]: if dico[key]:
self.state.setdefault(key, {}) self.filter.setdefault(key, {})
if dico[key] in self.state[key]: if dico[key] in self.filter[key]:
logging.debug("del1 del_state found [%s][%s][%s]" % (str(key), str(dico[key]), str(self.state[key]))) logging.debug("del1 del_filter found [%s][%s][%s]" % (str(key), str(dico[key]), str(self.filter[key])))
del self.state[key][dico[key]] del self.filter[key][dico[key]]
if self.egs_filter: if self.egs_filter:
res = self.egs_filter_load_character.match(msg) res = self.egs_filter_load_character.match(msg)
if res: if res:
logging.debug("egs_filter_load_character found") logging.debug("egs_filter_load_character found")
if len(self.state_load_character) < self.size_max_state: if len(self.filter_load_character) < self.size_max_filter:
logging.debug("include add_state found") logging.debug("include add_filter found")
dico = res.groupdict() dico = res.groupdict()
try: try:
self.state_load_character.setdefault(dico['UID'], {}) self.filter_load_character.setdefault(dico['UID'], {})
self.state_load_character[dico['UID']].setdefault(dico['IDCHAR'], {'NameDomain': dico['NameDomain'], 'UIDBIS': dico['UIDBIS'], 'when': now}) self.filter_load_character[dico['UID']].setdefault(dico['IDCHAR'], {'NameDomain': dico['NameDomain'], 'UID': dico['UIDBIS'], 'when': now})
except KeyError as e: except KeyError as e:
logging.error('Missing key when read "load_character" (%s)' % e) logging.error('Missing key when read "load_character" (%s)' % e)
else: else:
@ -688,12 +702,12 @@ class ManageCommand():
res = self.egs_filter_active_character.match(msg) res = self.egs_filter_active_character.match(msg)
if res: if res:
logging.debug("egs_filter_active_character found") logging.debug("egs_filter_active_character found")
if len(self.state_active_character) < self.size_max_state: if len(self.filter_active_character) < self.size_max_filter:
dico = res.groupdict() dico = res.groupdict()
try: try:
self.state_active_character.setdefault(dico['UID'], {}) self.filter_active_character.setdefault(dico['UID'], {})
self.state_active_character[dico['UID']] = self.state_load_character[dico['UID']][dico['IDCHAR']] self.filter_active_character[dico['UID']] = self.filter_load_character[dico['UID']][dico['IDCHAR']]
del self.state_load_character[dico['UID']] del self.filter_load_character[dico['UID']]
except KeyError as e: except KeyError as e:
logging.error('Missing key when read "active_character" (%s)' % e) logging.error('Missing key when read "active_character" (%s)' % e)
else: else:
@ -704,8 +718,8 @@ class ManageCommand():
logging.debug("egs_filter_sid found") logging.debug("egs_filter_sid found")
dico = res.groupdict() dico = res.groupdict()
try: try:
if dico['UID'] in self.state_active_character: if dico['UID'] in self.filter_active_character:
self.state_active_character[dico['UID']].setdefault("SID", dico['SID']) self.filter_active_character[dico['UID']].setdefault("SID", dico['SID'])
else: else:
logging.error('Impossible to add SID on player %s (Player not found)' % dico['UID']) logging.error('Impossible to add SID on player %s (Player not found)' % dico['UID'])
except KeyError as e: except KeyError as e:
@ -716,28 +730,30 @@ class ManageCommand():
logging.debug("egs_filter_sid found") logging.debug("egs_filter_sid found")
dico = res.groupdict() dico = res.groupdict()
try: try:
if dico['UID'] in self.state_active_character: if dico['UID'] in self.filter_active_character:
del self.state_active_character[dico['UID']] del self.filter_active_character[dico['UID']]
else: else:
logging.error('Impossible to remove player %s (Player not found)' % dico['UID']) logging.error('Impossible to remove player %s (Player not found)' % dico['UID'])
except KeyError as e: except KeyError as e:
logging.error('Missing key when remove player (%s)' % e) logging.error('Missing key when remove player (%s)' % e)
return return
res =self.egs_filter_admin.match(msg) res = self.egs_filter_admin.match(msg)
if res: if res:
logging.debug("egs_filter_admin found") logging.debug("egs_filter_admin found")
while len(self.state_admin) >= self.maxlog: while len(self.filter_admin) >= self.maxlog:
self.state_admin.pop(0) print(self.pos_admin, self.pos_admin - self.maxlog )
del self.filter_admin[self.pos_admin - self.maxlog]
try: try:
dico = res.groupdict()
username = '' username = ''
try: try:
for key in self.state_active_character: for key in self.filter_active_character:
if self.state_active_character[key]['SID'] == dico['SID']: if self.filter_active_character[key]['SID'] == dico['SID']:
username = self.state_active_character[key]['NameDomain'] username = self.filter_active_character[key]['NameDomain']
break break
except KeyError: except KeyError:
pass pass
self.state_admin.append( {'pos': self.pos_admin, 'when': now, 'SID': dico['SID'], 'ACTION': dico['ACTION'], 'USER': username}) self.filter_admin.setdefault( self.pos_admin, {'when': now, 'SID': dico['SID'], 'ACTION': dico['ACTION'], 'USER': username})
except KeyError as e: except KeyError as e:
logging.error('Missing key when admin player (%s)' % e) logging.error('Missing key when admin player (%s)' % e)
self.pos_admin = self.pos_admin + 1 self.pos_admin = self.pos_admin + 1
@ -789,9 +805,9 @@ class ManageCommand():
if wait_semaphore == True: if wait_semaphore == True:
self.queueIn.put("STOPPED") self.queueIn.put("STOPPED")
self.semaphore.release() self.semaphore.release()
if self.keep_state: if self.activate_filter:
self.state_load_character = {} self.filter_load_character = {}
self.state_active_character = {} self.filter_active_character = {}
logging.debug("End reader: '%s'" % self.name) logging.debug("End reader: '%s'" % self.name)
def restart(self): def restart(self):
@ -812,14 +828,11 @@ class ManageCommand():
self.semaphore.release() self.semaphore.release()
logging.debug('Prepare restart service %s (step 3)' % (self.name)) logging.debug('Prepare restart service %s (step 3)' % (self.name))
def handler(self, signum, frame): def receive_signal(self, signum, frame):
""" Managed signal (not used) """ """ Managed signal (not used) """
if self.process: logging.info("Received signal %s (%d)" % (self.name, signum))
# logging.debug("Send signal %d to '%s'" %(signum, self.name)) self.queueIn.put("SHUTDOWN")
self.process.send_signal(signum) self.queueIn.put("SHUTDOWN")
else:
logging.error("Impossible to send signal %d to '%s'" % (signum, self.name))
raise IOError("signal received")
def start(self): def start(self):
""" Start program """ """ Start program """
@ -956,40 +969,40 @@ class ManageCommand():
pos += 1 pos += 1
outjson.setdefault('first-line', firstlinefound) outjson.setdefault('first-line', firstlinefound)
outjson.setdefault('last-line', pos - 1) outjson.setdefault('last-line', pos - 1)
return json.dumps(outjson) return outjson
def getstate(self): def getfilter(self):
""" Get state """ """ Get filter """
return json.dumps(self.state) return self.filter
def getconfig(self): def getconfig(self):
outjson = { 'keep_state': str(self.keep_state), outjson = { 'activate_filter': str(self.activate_filter),
'bufsize': str(self.bufsize), 'bufsize': str(self.bufsize),
'size_max_state': str(self.size_max_state), 'size_max_filter': str(self.size_max_filter),
'path': str(self.path), 'path': str(self.path),
'add_state': str(self.add_state_cmd), 'add_filter': str(self.add_filter_cmd),
'del_state': str(self.del_state_cmd), 'del_filter': str(self.del_filter_cmd),
'command': str(self.command), 'command': str(self.command),
'maxWaitEnd': str(self.maxWaitEnd), 'maxWaitEnd': str(self.maxWaitEnd),
'waitDelay': str(self.waitDelay), 'waitDelay': str(self.waitDelay),
'maxlog': str(self.maxlog), 'maxlog': str(self.maxlog),
'state': str(self.keep_state), 'filter': str(self.activate_filter),
'egs': str(self.egs_filter) } 'egs': str(self.egs_filter) }
return json.dumps(outjson) return outjson
def getinfo(self): def getinfo(self):
outjson = { 'number_launch': str(self.number_start), outjson = { 'number_launch': str(self.number_start),
'first_line': str(self.first_line), 'first_line': str(self.first_line),
'last_line': str(self.last_line), 'last_line': str(self.last_line),
'number_state': len(self.state), 'number_filter': len(self.filter),
'player_connected': len(self.state_active_character) } 'player_connected': len(self.filter_active_character) }
return json.dumps(outjson) return outjson
def getplayer(self): def getplayer(self):
return json.dumps(self.state_active_character) return self.filter_active_character
def getadmincommand(self): def getadmincommand(self):
return json.dumps(self.state_admin) return self.filter_admin
def action(self, action): def action(self, action):
""" Send action to program (send input to stdin) """ """ Send action to program (send input to stdin) """
@ -1005,6 +1018,8 @@ class ManageCommand():
def run(self): def run(self):
""" loop, run child (wait command) """ """ loop, run child (wait command) """
signal.signal(signal.SIGABRT, self.receive_signal)
signal.signal(signal.SIGTERM, self.receive_signal)
statuscmd = {0:'started', 1:'stopped', 2:'crashed'} statuscmd = {0:'started', 1:'stopped', 2:'crashed'}
loop = True loop = True
if self.autostart: if self.autostart:
@ -1022,18 +1037,22 @@ class ManageCommand():
elif command == "START": elif command == "START":
#if savedstate != 0: #if savedstate != 0:
savedstate = self.start() savedstate = self.start()
self.queueOut.put(statuscmd[savedstate]) self.queueOut.put({'state': statuscmd[savedstate]})
elif command == "STATUS": elif command == "STATUS":
currentstate = self.status() currentstate = self.status()
if currentstate != 1 or savedstate != 2: if currentstate != 1 or savedstate != 2:
savedstate = currentstate savedstate = currentstate
self.queueOut.put(statuscmd[savedstate]) self.queueOut.put({'state': statuscmd[savedstate],
'last_line': str(self.last_line),
'number_launch': str(self.number_start),
'filter': str(self.activate_filter),
'egs': str(self.egs_filter)})
elif command == "STOP": elif command == "STOP":
savedstate = self.stop() savedstate = self.stop()
self.queueOut.put(statuscmd[savedstate]) self.queueOut.put({'state': statuscmd[savedstate]})
elif command == "STDIN": elif command == "STDIN":
data = msg.split(maxsplit=1)[1] data = msg.split(maxsplit=1)[1]
self.queueOut.put(self.action(data)) self.queueOut.put({'state': self.action(data)})
elif command == "STDOUT": elif command == "STDOUT":
try: try:
firstline = int(msg.split(maxsplit=1)[1]) firstline = int(msg.split(maxsplit=1)[1])
@ -1043,8 +1062,8 @@ class ManageCommand():
except IndexError: except IndexError:
firstline = 0 firstline = 0
self.queueOut.put(self.getlog(firstline)) self.queueOut.put(self.getlog(firstline))
elif command == "STATE": elif command == "FILTER":
self.queueOut.put(self.getstate()) self.queueOut.put(self.getfilter())
elif command == "CONFIG": elif command == "CONFIG":
self.queueOut.put(self.getconfig()) self.queueOut.put(self.getconfig())
elif command == "INFO": elif command == "INFO":
@ -1072,7 +1091,7 @@ class ManageCommand():
self.threadRestart.start() self.threadRestart.start()
else: else:
logging.warning("Bad command (%s)" % command) logging.warning("Bad command (%s)" % command)
self.queueOut.put("error : command unknown") self.queueOut.put( {"error" : "command unknown"} )
logging.debug('Stop %s' % self.name) logging.debug('Stop %s' % self.name)
self.stop() self.stop()
logging.debug('prepare end') logging.debug('prepare end')
@ -1211,42 +1230,42 @@ class Manager():
raise ValueError raise ValueError
else: else:
bufsize = 100 bufsize = 100
if 'keep_state' in config[name]: if 'activate_filter' in config[name]:
try: try:
tmp = config[name]['keep_state'] tmp = config[name]['activate_filter']
if tmp.upper().strip() == 'YES': if tmp.upper().strip() == 'YES':
keep_state = True activate_filter = True
else: else:
keep_state = False activate_filter = False
except (TypeError, KeyError, ValueError): except (TypeError, KeyError, ValueError):
logging.error("Impossible to read param keep_state (command:%s)", name) logging.error("Impossible to read param activate_filter (command:%s)", name)
raise ValueError raise ValueError
else: else:
keep_state = False activate_filter = False
if 'size_max_state' in config[name]: if 'size_max_filter' in config[name]:
try: try:
size_max_state = int(config[name]['size_max_state']) size_max_filter = int(config[name]['size_max_filter'])
except (TypeError, KeyError, ValueError): except (TypeError, KeyError, ValueError):
logging.error("Impossible to read param size_max_state (command:%s)", name) logging.error("Impossible to read param size_max_filter (command:%s)", name)
raise ValueError raise ValueError
else: else:
size_max_state = 100 size_max_filter = 100
if 'add_state' in config[name]: if 'add_filter' in config[name]:
try: try:
add_state = config[name]['add_state'] add_filter = config[name]['add_filter']
except (TypeError, KeyError, ValueError): except (TypeError, KeyError, ValueError):
logging.error("Impossible to read param add_state (command:%s)", name) logging.error("Impossible to read param add_filter (command:%s)", name)
raise ValueError raise ValueError
else: else:
add_state = '' add_filter = ''
if 'del_state' in config[name]: if 'del_filter' in config[name]:
try: try:
del_state = config[name]['del_state'] del_filter = config[name]['del_filter']
except (TypeError, KeyError, ValueError): except (TypeError, KeyError, ValueError):
logging.error("Impossible to read param del_state (command:%s)", name) logging.error("Impossible to read param del_filter (command:%s)", name)
raise ValueError raise ValueError
else: else:
del_state = '' del_filter = ''
if 'autostart' in config[name]: if 'autostart' in config[name]:
try: try:
tmp = config[name]['autostart'] tmp = config[name]['autostart']
@ -1295,10 +1314,10 @@ class Manager():
'path': path, 'path': path,
'logsize': logsize, 'logsize': logsize,
'bufsize': bufsize, 'bufsize': bufsize,
'keep_state': keep_state, 'activate_filter': activate_filter,
'size_max_state': size_max_state, 'size_max_filter': size_max_filter,
'add_state': add_state, 'add_filter': add_filter,
'del_state': del_state, 'del_filter': del_filter,
'autostart': autostart, 'autostart': autostart,
'restart_after_crash': restart_after_crash, 'restart_after_crash': restart_after_crash,
'restart_delay': restart_delay, 'restart_delay': restart_delay,
@ -1308,6 +1327,7 @@ class Manager():
""" """
Initialize object serverHttp Initialize object serverHttp
""" """
logging.debug("Initialize server http(s)")
self.serverHttp = ServerHttp(self.keyfile, self.serverHttp = ServerHttp(self.keyfile,
self.certfile, self.certfile,
self.ca_cert, self.ca_cert,
@ -1318,7 +1338,7 @@ class Manager():
users=self.users) users=self.users)
def runCommand(self, name, command, path, logsize, bufsize, queueIn, queueOut, semaphore, def runCommand(self, name, command, path, logsize, bufsize, queueIn, queueOut, semaphore,
keep_state, size_max_state, add_state, del_state, activate_filter, size_max_filter, add_filter, del_filter,
autostart, restart_after_crash, restart_delay, egs_filter): autostart, restart_after_crash, restart_delay, egs_filter):
""" """
Thread to manage khaganat program Thread to manage khaganat program
@ -1332,10 +1352,10 @@ class Manager():
queueIn=queueIn, queueIn=queueIn,
queueOut=queueOut, queueOut=queueOut,
semaphore=semaphore, semaphore=semaphore,
keep_state=keep_state, activate_filter=activate_filter,
size_max_state=size_max_state, size_max_filter=size_max_filter,
add_state=add_state, add_filter=add_filter,
del_state=del_state, del_filter=del_filter,
autostart=autostart, autostart=autostart,
restart_after_crash=restart_after_crash, restart_after_crash=restart_after_crash,
restart_delay=restart_delay, restart_delay=restart_delay,
@ -1355,7 +1375,7 @@ class Manager():
queueOut = multiprocessing.Queue() queueOut = multiprocessing.Queue()
# semaphore = multiprocessing.Semaphore() # semaphore = multiprocessing.Semaphore()
semaphore = multiprocessing.BoundedSemaphore() semaphore = multiprocessing.BoundedSemaphore()
self.serverHttp.append(name, queueIn, queueOut, semaphore) self.serverHttp.appendchild(name, queueIn, queueOut, semaphore)
if self.launch_program: if self.launch_program:
autostart = True autostart = True
else: else:
@ -1369,10 +1389,10 @@ class Manager():
queueIn, queueIn,
queueOut, queueOut,
semaphore, semaphore,
self.param[name]['keep_state'], self.param[name]['activate_filter'],
self.param[name]['size_max_state'], self.param[name]['size_max_filter'],
self.param[name]['add_state'], self.param[name]['add_filter'],
self.param[name]['del_state'], self.param[name]['del_filter'],
autostart, autostart,
self.param[name]['restart_after_crash'], self.param[name]['restart_after_crash'],
self.param[name]['restart_delay'], self.param[name]['restart_delay'],
@ -1387,10 +1407,10 @@ class Manager():
'path': self.param[name]['path'], 'path': self.param[name]['path'],
'logsize': self.param[name]['logsize'], 'logsize': self.param[name]['logsize'],
'bufsize': self.param[name]['bufsize'], 'bufsize': self.param[name]['bufsize'],
'keep_state': self.param[name]['keep_state'], 'activate_filter': self.param[name]['activate_filter'],
'size_max_state': self.param[name]['size_max_state'], 'size_max_filter': self.param[name]['size_max_filter'],
'add_state': self.param[name]['add_state'], 'add_filter': self.param[name]['add_filter'],
'del_state': self.param[name]['del_state'], 'del_filter': self.param[name]['del_filter'],
'autostart': autostart, 'autostart': autostart,
'restart_after_crash': self.param[name]['restart_after_crash'], 'restart_after_crash': self.param[name]['restart_after_crash'],
'restart_delay': self.param[name]['restart_delay'], 'restart_delay': self.param[name]['restart_delay'],
@ -1398,10 +1418,19 @@ class Manager():
def receive_signal(self, signum, frame): def receive_signal(self, signum, frame):
""" Managed signal """ """ Managed signal """
logging.info("Received signal (%d)" % (signum))
for child in self.threadCommand: for child in self.threadCommand:
child.terminate() logging.info("send signal to child %s" % (child.name))
try:
child.terminate()
child.join()
except AttributeError:
logging.info("child not started")
pass
if self.serverHttp: if self.serverHttp:
logging.info("send signal to server http")
self.serverHttp.terminate() self.serverHttp.terminate()
logging.info("Finalize signal (%d)" % (signum))
def wait_children_commands(self): def wait_children_commands(self):
for child in self.threadCommand: for child in self.threadCommand:
@ -1413,6 +1442,8 @@ class Manager():
def run(self): def run(self):
""" launch all """ """ launch all """
signal.signal(signal.SIGABRT, self.receive_signal)
signal.signal(signal.SIGTERM, self.receive_signal)
self.launch_command() self.launch_command()
self.launch_server_http() self.launch_server_http()
logging.info('started') logging.info('started')
@ -1478,6 +1509,7 @@ def main(args=sys.argv[1:]):
logLevel=param.log, logLevel=param.log,
launch_program=param.launch_program, launch_program=param.launch_program,
show_log_console=param.show_log_console) show_log_console=param.show_log_console)
logging.debug("End")
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -73,6 +73,12 @@ class SimulateProgram():
if pos > n: if pos > n:
self.print_output("alpha finalizeClientReady EGS-132 : Updating IS_NEWBIE flag for character: (0x0000000021:00:00:81)") self.print_output("alpha finalizeClientReady EGS-132 : Updating IS_NEWBIE flag for character: (0x0000000021:00:00:81)")
n = n + 1 n = n + 1
if pos > n:
self.print_output("cbClientAdmin EGS-136 : ADMIN: Player (0x0000000021:00:00:81) doesn't have privilege to execute the client admin command 'infos'")
n = n + 1
if pos > n:
self.print_output("cbClientAdmin EGS-136 : ADMIN: Player (0x0000000021:00:00:81) tried to execute a no valid client admin command 'INFO'")
n = n + 1
if pos > n: if pos > n:
self.print_output("alpha 1383 disconnectPlayer EGS-132 : (EGS) player 2 (Row 90501) removed") self.print_output("alpha 1383 disconnectPlayer EGS-132 : (EGS) player 2 (Row 90501) removed")
n = n + 1 n = n + 1

View file

@ -28,7 +28,6 @@ import queue
import signal import signal
import http.server import http.server
import logging import logging
import json
from unittest.mock import patch from unittest.mock import patch
from unittest.mock import MagicMock from unittest.mock import MagicMock
import traceback import traceback
@ -47,6 +46,9 @@ except ImportError:
# sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# import pymanager.certificate as cert # import pymanager.certificate as cert
class TimeoutError(Exception):
pass
def handlerCrash(signum, frame): def handlerCrash(signum, frame):
print("handlerCrash - TimeOut !") print("handlerCrash - TimeOut !")
@ -60,7 +62,7 @@ def handlerRaise(signum, frame):
print("handlerRaise - TimeOut !") print("handlerRaise - TimeOut !")
for line in traceback.format_stack(): for line in traceback.format_stack():
print(line.strip()) print(line.strip())
raise "Timeout" raise TimeoutError
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -103,10 +105,10 @@ class TestManager(unittest.TestCase):
config.set('command:test', 'command', '/bin/sleep 10') config.set('command:test', 'command', '/bin/sleep 10')
config.set('command:test', 'logsize', '10') config.set('command:test', 'logsize', '10')
config.set('command:test', 'bufsize', '10') config.set('command:test', 'bufsize', '10')
config.set('command:test', 'keep_state', 'yes') config.set('command:test', 'keep_filter', 'yes')
config.set('command:test', 'size_max_state', '1000') config.set('command:test', 'size_max_filter', '1000')
config.set('command:test', 'add_state', '"^(.*)(setActiveCharForPlayer).*(: set active char )[\d]+( for )(?P<ActivePlayer>.*)"') config.set('command:test', 'add_filter', '"^(.*)(setActiveCharForPlayer).*(: set active char )[\d]+( for )(?P<ActivePlayer>.*)"')
config.set('command:test', 'del_state', '"^(.*)(disconnectPlayer).+[\s]+(?P<ActivePlayer>.*)[\s]+(is disconnected)"') config.set('command:test', 'del_filter', '"^(.*)(disconnectPlayer).+[\s]+(?P<ActivePlayer>.*)[\s]+(is disconnected)"')
config.add_section('config:user') config.add_section('config:user')
config.set('config:user', 'usename', 'filter_all, filter_admin') config.set('config:user', 'usename', 'filter_all, filter_admin')
try: try:
@ -267,37 +269,42 @@ class TestManager(unittest.TestCase):
True) True)
manage._analyze_line("message 1") manage._analyze_line("message 1")
manage._analyze_line("alpha egs_plinfo EGS-132 : LOADED User '2' Character 'Kezxaa(Lirria)' from BS stream file 'characters/002/account_2_0_pdr.bin'") manage._analyze_line("alpha egs_plinfo EGS-132 : LOADED User '2' Character 'Kezxaa(Lirria)' from BS stream file 'characters/002/account_2_0_pdr.bin'")
if '2' not in manage.state_load_character: if '2' not in manage.filter_load_character:
self.assertTrue(False, "LOADED - Missing player 2") self.assertTrue(False, "LOADED - Missing player 2")
if '0' not in manage.state_load_character['2']: if '0' not in manage.filter_load_character['2']:
self.assertTrue(False, "LOADED - Missing charactere 0 for player 2") self.assertTrue(False, "LOADED - Missing charactere 0 for player 2")
manage._analyze_line("alpha egs_plinfo EGS-132 : LOADED User '2' Character 'Puskle(Lirria)' from BS stream file 'characters/002/account_2_1_pdr.bin'") manage._analyze_line("alpha egs_plinfo EGS-132 : LOADED User '2' Character 'Puskle(Lirria)' from BS stream file 'characters/002/account_2_1_pdr.bin'")
if '1' not in manage.state_load_character['2']: if '1' not in manage.filter_load_character['2']:
self.assertTrue(False, "LOADED - Missing charactere 1 for player 2") self.assertTrue(False, "LOADED - Missing charactere 1 for player 2")
manage._analyze_line("alpha egs_plinfo EGS-132 : LOADED User '3' Character 'Puskle(Lirria)' from BS stream file 'characters/003/account_3_4_pdr.bin'") manage._analyze_line("alpha egs_plinfo EGS-132 : LOADED User '3' Character 'Puskle(Lirria)' from BS stream file 'characters/003/account_3_4_pdr.bin'")
if '3' not in manage.state_load_character: if '3' not in manage.filter_load_character:
self.assertTrue(False, "LOADED - Missing player 2") self.assertTrue(False, "LOADED - Missing player 2")
manage._analyze_line("alpha egs_ecinfo EGS-132 : setActiveCharForPlayer EGS-132 : set active char 1 for player 2") manage._analyze_line("alpha egs_ecinfo EGS-132 : setActiveCharForPlayer EGS-132 : set active char 1 for player 2")
if '2' not in manage.state_active_character: if '2' not in manage.filter_active_character:
self.assertTrue(False, "setActiveCharForPlayer - Missing player 2") self.assertTrue(False, "setActiveCharForPlayer - Missing player 2")
if 'NameDomain' not in manage.state_active_character['2']: if 'NameDomain' not in manage.filter_active_character['2']:
self.assertTrue(False, "setActiveCharForPlayer - Missing info player 2") self.assertTrue(False, "setActiveCharForPlayer - Missing info player 2")
manage._analyze_line("alpha egs_ecinfo EGS-132 : Mapping UID 2 => Sid (0x0000000021:00:00:81)") manage._analyze_line("alpha egs_ecinfo EGS-132 : Mapping UID 2 => Sid (0x0000000021:00:00:83)")
if 'SID' not in manage.state_active_character['2']: if 'SID' not in manage.filter_active_character['2']:
self.assertTrue(False, "setActiveCharForPlayer - Missing SID player 2") self.assertTrue(False, "setActiveCharForPlayer - Missing SID player 2")
manage._analyze_line("alpha egs_ecinfo EGS-132 : Client ready (entity (0x0000000021:00:00:81) (Row 90501) added to mirror)") manage._analyze_line("alpha egs_ecinfo EGS-132 : Client ready (entity (0x0000000021:00:00:83) (Row 90501) added to mirror)")
manage._analyze_line("alpha finalizeClientReady EGS-132 : Updating IS_NEWBIE flag for character: (0x0000000021:00:00:81)") manage._analyze_line("alpha finalizeClientReady EGS-132 : Updating IS_NEWBIE flag for character: (0x0000000021:00:00:83)")
manage._analyze_line("alpha 1383 disconnectPlayer EGS-132 : (EGS) player 2 (Row 90501) removed") manage._analyze_line("alpha 1383 disconnectPlayer EGS-132 : (EGS) player 2 (Row 90501) removed")
manage._analyze_line("alpha egs_plinfo EGS-132 : Player with userId = 2 removed") manage._analyze_line("alpha egs_plinfo EGS-132 : Player with userId = 2 removed")
manage._analyze_line("cbClientAdmin EGS-133 : ADMIN: Player (0x0000000021:00:00:83) doesn't have privilege to execute the client admin command 'infos'")
manage._analyze_line("cbClientAdmin EGS-133 : ADMIN: Player (0x0000000021:00:00:83) tried to execute a no valid client admin command 'INFO'")
for i in range(0, 2000):
manage._analyze_line("cbClientAdmin EGS-133 : ADMIN: Player (0x0000000021:00:00:83) tried to execute a no valid client admin command 'INFO' %d" % i)
self.assertTrue(len(manage.filter_admin), 1000)
manage._analyze_line("alpha disconnectPlayer EGS-132 : player 2 is disconnected") manage._analyze_line("alpha disconnectPlayer EGS-132 : player 2 is disconnected")
if '2' in manage.state_active_character: if '2' in manage.filter_active_character:
self.assertTrue(False, "disconnectPlayer - player 2 always live") self.assertTrue(False, "disconnectPlayer - player 2 always live")
def test_manager_command_player(self): def test_manager_command_player(self):
signal.signal(signal.SIGALRM, handlerRaise) signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(30) signal.alarm(30)
class MockServerHttp: class MockServerHttp:
def append(self, name, queueIn, queueOut, semaphore): def appendchild(self, name, queueIn, queueOut, semaphore):
pass pass
def terminate(self): def terminate(self):
pass pass
@ -317,6 +324,7 @@ class TestManager(unittest.TestCase):
manage._load_config(config) manage._load_config(config)
manage.launch_command() manage.launch_command()
signal.alarm(10)
key = list(manage.info.keys())[0] key = list(manage.info.keys())[0]
queueIn = manage.info[key]['queueIn'] queueIn = manage.info[key]['queueIn']
queueOut = manage.info[key]['queueOut'] queueOut = manage.info[key]['queueOut']
@ -325,29 +333,26 @@ class TestManager(unittest.TestCase):
signal.alarm(30) signal.alarm(30)
item = "started" item = "started"
while item == "started": while item == "started":
print("sleep") logging.debug("sleep")
time.sleep(1) time.sleep(1)
print("semaphore") logging.debug("semaphore")
semaphore.acquire() semaphore.acquire()
print("status") logging.debug("status")
queueIn.put("STATUS") queueIn.put("STATUS")
print("queue") logging.debug("queue")
item = queueOut.get(timeout=4) item = queueOut.get()
print(item)
semaphore.release() semaphore.release()
print("Lecture STDOUT") logging.debug("Lecture STDOUT")
print("sleep")
time.sleep(1) time.sleep(1)
signal.alarm(30) signal.alarm(30)
print("semaphore") logging.debug("semaphore")
semaphore.acquire() semaphore.acquire()
print("stdout") logging.debug("stdout")
queueIn.put("STDOUT") queueIn.put("STDOUT")
print("Attend le retour STDOUT") logging.debug("Attend le retour STDOUT")
item = queueOut.get() item = queueOut.get()
semaphore.release() semaphore.release()
print("Resultat STDOUT") logging.debug("Resultat STDOUT")
print(item)
time.sleep(1) time.sleep(1)
signal.alarm(10) signal.alarm(10)
semaphore.acquire() semaphore.acquire()
@ -362,17 +367,26 @@ class TestManager(unittest.TestCase):
signal.alarm(0) signal.alarm(0)
self.assertTrue(True) self.assertTrue(True)
signal.alarm(0) signal.alarm(0)
except: except TimeoutError:
print("Prepare Crash - TimeOut !") print("Prepare Crash - TimeOut !")
#self.fail('Error initialize object ManageCommand') #self.fail('Error initialize object ManageCommand')
manage.receive_signal(15, 1) manage.receive_signal(15, 1)
manage.wait_children_commands() manage.wait_children_commands()
print("Force Crash - TimeOut !") print("Force Crash - TimeOut !")
os._exit(2) os._exit(2)
except Exception as e:
print("Prepare Crash - %s" % e)
#self.fail('Error initialize object ManageCommand')
manage.receive_signal(15, 1)
manage.wait_children_commands()
handlerCrash(15, 1)
print("Force Crash - TimeOut !")
os._exit(2)
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(0) signal.alarm(0)
signal.signal(signal.SIGALRM, handlerCrash)
def test_execute_manager_command(self): def test_execute_manager_command(self):
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(10) signal.alarm(10)
try: try:
logsize = 10 logsize = 10
@ -406,10 +420,10 @@ class TestManager(unittest.TestCase):
while loop > 0: while loop > 0:
time.sleep(1) time.sleep(1)
out = manageCommand.getlog(0) out = manageCommand.getlog(0)
if foundEnd.match(out): if foundEnd.match(str(out)):
break break
loop -= 1 loop -= 1
if not foundEnd.match(out): if not foundEnd.match(str(out)):
manageCommand.stop() manageCommand.stop()
self.assertTrue(False, 'Missing message in log') self.assertTrue(False, 'Missing message in log')
manageCommand.list_thread() manageCommand.list_thread()
@ -424,10 +438,10 @@ class TestManager(unittest.TestCase):
while loop > 0: while loop > 0:
time.sleep(1) time.sleep(1)
out = manageCommand.getlog(0) out = manageCommand.getlog(0)
if foundY.match(out): if foundY.match(str(out)):
break break
loop -= 1 loop -= 1
if not foundY.match(out): if not foundY.match(str(out)):
manageCommand.stop() manageCommand.stop()
self.assertTrue(False, 'Missing message in log') self.assertTrue(False, 'Missing message in log')
@ -439,9 +453,11 @@ class TestManager(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
except: except:
self.fail('Error initialize object ManageCommand') self.fail('Error initialize object ManageCommand')
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(0) signal.alarm(0)
def test_execute_crash_manager_command(self): def test_execute_crash_manager_command(self):
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(10) signal.alarm(10)
try: try:
logsize = 10 logsize = 10
@ -480,10 +496,12 @@ class TestManager(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
except: except:
self.fail('Error initialize object ManageCommand') self.fail('Error initialize object ManageCommand')
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(0) signal.alarm(0)
def test_execute_not_kill_manager_command(self): def test_execute_not_kill_manager_command(self):
signal.alarm(30) signal.alarm(30)
signal.signal(signal.SIGALRM, handlerCrash)
try: try:
logsize = 10 logsize = 10
bufsize = 10 bufsize = 10
@ -519,9 +537,8 @@ class TestManager(unittest.TestCase):
self.assertEqual(res, 0) self.assertEqual(res, 0)
res = manageCommand.getlog(0) res = manageCommand.getlog(0)
try: try:
resjson = json.loads(res)
if 'last-line' in res: if 'last-line' in res:
if resjson['last-line'] == 5: if res['last-line'] == 5:
wait = False wait = False
except: except:
pass pass
@ -532,6 +549,7 @@ class TestManager(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
except Exception as e: except Exception as e:
self.fail('Error when run test (%s)' % str(e)) self.fail('Error when run test (%s)' % str(e))
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(0) signal.alarm(0)
def test_execute_command_crashed(self): def test_execute_command_crashed(self):
@ -658,10 +676,10 @@ class TestManager(unittest.TestCase):
queueIn=queueIn, queueIn=queueIn,
queueOut=queueOut, queueOut=queueOut,
semaphore=semaphore, semaphore=semaphore,
keep_state=False, keep_filter=False,
size_max_state=1, size_max_filter=1,
add_state="", add_filter="",
del_state="", del_filter="",
autostart=False, autostart=False,
restart_after_crash=False, restart_after_crash=False,
restart_delay=1, restart_delay=1,
@ -691,11 +709,15 @@ class TestManager(unittest.TestCase):
signal.alarm(0) signal.alarm(0)
def test_main(self): def test_main(self):
signal.signal(signal.SIGALRM, handlerCrash)
signal.alarm(10) signal.alarm(10)
config = tempfile.NamedTemporaryFile(suffix="password.cfg", mode='w+t') config = tempfile.NamedTemporaryFile(suffix="password.cfg", mode='w+t')
config.write('[config:server]\nauthentification=no\n') config.write('[config:server]\nauthentification=no\n')
config.flush() config.flush()
logging.debug("load")
Manager.main(['--conf=' + config.name]) Manager.main(['--conf=' + config.name])
logging.debug("end")
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(0) signal.alarm(0)
def test_run_manager_command(self): def test_run_manager_command(self):
@ -703,7 +725,7 @@ class TestManager(unittest.TestCase):
signal.signal(signal.SIGALRM, handlerCrash) signal.signal(signal.SIGALRM, handlerCrash)
signal.alarm(10) signal.alarm(10)
class MockServerHttp: class MockServerHttp:
def append(self, name, queueIn, queueOut, semaphore): def appendchild(self, name, queueIn, queueOut, semaphore):
pass pass
def terminate(self): def terminate(self):
pass pass
@ -730,9 +752,9 @@ class TestManager(unittest.TestCase):
queueIn.put("START") queueIn.put("START")
# Enable timeout # Enable timeout
signal.alarm(10) signal.alarm(10)
item = queueOut.get(timeout=4) item = queueOut.get()
semaphore.release() semaphore.release()
self.assertEqual(item, "started", 'Error impossible to start program') self.assertEqual(item['state'], 'started', 'Error impossible to start program')
signal.alarm(0) signal.alarm(0)
time.sleep(1) time.sleep(1)
logging.debug("[2] --------- send STATUS") logging.debug("[2] --------- send STATUS")
@ -741,7 +763,7 @@ class TestManager(unittest.TestCase):
queueIn.put("STATUS") queueIn.put("STATUS")
item = queueOut.get() item = queueOut.get()
semaphore.release() semaphore.release()
self.assertEqual(item, "started", 'Error impossible to read status') self.assertEqual(item['state'], 'started', 'Error impossible to read status')
signal.alarm(0) signal.alarm(0)
time.sleep(1) time.sleep(1)
logging.debug("[3] --------- send STDIN arg") logging.debug("[3] --------- send STDIN arg")
@ -750,7 +772,7 @@ class TestManager(unittest.TestCase):
queueIn.put("STDIN arg") queueIn.put("STDIN arg")
item = queueOut.get() item = queueOut.get()
semaphore.release() semaphore.release()
self.assertEqual(item, "ok", 'Error when send STDIN') self.assertEqual(item['state'], 'ok', 'Error when send STDIN')
signal.alarm(0) signal.alarm(0)
time.sleep(1) time.sleep(1)
logging.debug("[4] --------- send STDOUT 4") logging.debug("[4] --------- send STDOUT 4")
@ -765,14 +787,12 @@ class TestManager(unittest.TestCase):
logging.debug("[4.4] --------- send STDOUT 4") logging.debug("[4.4] --------- send STDOUT 4")
signal.alarm(0) signal.alarm(0)
logging.debug("[4.5] --------- send STDOUT 4") logging.debug("[4.5] --------- send STDOUT 4")
self.assertRegex(item, self.assertEqual(item['first-line'], 4, 'Error when read STDOUT (Missing first-line)')
'^[{](.*)("first-line": 4)(.*)[}]$', self.assertEqual(item['last-line'], 6, 'Error when read STDOUT (Missing first-line)')
'Error when read STDOUT (Missing first-line)') # self.assertRegex(item, '^[{](.*)("first-line": 4)(.*)[}]$', 'Error when read STDOUT (Missing first-line)')
self.assertRegex(item, # self.assertRegex(item, '^[{](.*)("last-line": 6)(.*)[}]$', 'Error when read STDOUT (Missing last-line)')
'^[{](.*)("last-line": 6)(.*)[}]$', self.assertRegex(str(item),
'Error when read STDOUT (Missing last-line)') '^(.*)(6 arg)(.*)$',
self.assertRegex(item,
'^[{](.*)(6 arg")(.*)[}]$',
'Error when read STDOUT (bad record)') 'Error when read STDOUT (bad record)')
time.sleep(1) time.sleep(1)
logging.debug("[5] --------- send BADCOMMAND") logging.debug("[5] --------- send BADCOMMAND")
@ -781,7 +801,7 @@ class TestManager(unittest.TestCase):
queueIn.put("BADCOMMAND") queueIn.put("BADCOMMAND")
item = queueOut.get() item = queueOut.get()
semaphore.release() semaphore.release()
self.assertEqual(item, "error : command unknown", 'Error impossible to read status') self.assertEqual(item['error'], 'command unknown', 'Error impossible to read status')
signal.alarm(0) signal.alarm(0)
time.sleep(1) time.sleep(1)
logging.debug("[6] --------- send STOP") logging.debug("[6] --------- send STOP")
@ -790,7 +810,7 @@ class TestManager(unittest.TestCase):
queueIn.put("STOP") queueIn.put("STOP")
item = queueOut.get() item = queueOut.get()
semaphore.release() semaphore.release()
self.assertEqual(item, "stopped", 'Error impossible to read status') self.assertEqual(item["state"], "stopped", 'Error impossible to read status')
signal.alarm(0) signal.alarm(0)
time.sleep(1) time.sleep(1)
logging.debug("[7] --------- send SHUTDOWN") logging.debug("[7] --------- send SHUTDOWN")
@ -816,13 +836,13 @@ class TestManager(unittest.TestCase):
print("test_run_manager_command - Force Crash - TimeOut !") print("test_run_manager_command - Force Crash - TimeOut !")
os._exit(2) os._exit(2)
signal.alarm(0) signal.alarm(0)
signal.signal(signal.SIGALRM, handlerCrash) signal.signal(signal.SIGALRM, handlerRaise)
def test_run_manager_command_autostart(self): def test_run_manager_command_autostart(self):
# Enable timeout # Enable timeout
signal.alarm(10) signal.alarm(10)
class MockServerHttp: class MockServerHttp:
def append(self, name, queueIn, queueOut, semaphore): def appendchild(self, name, queueIn, queueOut, semaphore):
pass pass
def terminate(self): def terminate(self):
pass pass
@ -848,7 +868,7 @@ class TestManager(unittest.TestCase):
queueIn.put("STATUS") queueIn.put("STATUS")
item = queueOut.get(timeout=4) item = queueOut.get(timeout=4)
semaphore.release() semaphore.release()
self.assertEqual(item, "started", 'Error impossible to read status') self.assertEqual(item['state'], "started", 'Error impossible to read status')
time.sleep(1) time.sleep(1)
signal.alarm(10) signal.alarm(10)
semaphore.acquire() semaphore.acquire()
@ -866,9 +886,10 @@ class TestManager(unittest.TestCase):
def test_run_manager_command_autostart_option(self): def test_run_manager_command_autostart_option(self):
# Enable timeout # Enable timeout
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(10) signal.alarm(10)
class MockServerHttp: class MockServerHttp:
def append(self, name, queueIn, queueOut, semaphore): def appendchild(self, name, queueIn, queueOut, semaphore):
pass pass
def terminate(self): def terminate(self):
pass pass
@ -881,44 +902,61 @@ class TestManager(unittest.TestCase):
config.set('command:test', 'autostart', 'yes') config.set('command:test', 'autostart', 'yes')
manage = Manager.Manager(False) manage = Manager.Manager(False)
manage.serverHttp = MockServerHttp() try:
manage._load_config(config) manage.serverHttp = MockServerHttp()
manage.launch_command() manage._load_config(config)
time.sleep(5) manage.launch_command()
time.sleep(5)
key = list(manage.info.keys())[0] key = list(manage.info.keys())[0]
queueIn = manage.info[key]['queueIn'] queueIn = manage.info[key]['queueIn']
queueOut = manage.info[key]['queueOut'] queueOut = manage.info[key]['queueOut']
semaphore = manage.info[key]['semaphore'] semaphore = manage.info[key]['semaphore']
signal.alarm(10) signal.alarm(10)
semaphore.acquire() semaphore.acquire()
queueIn.put("STATUS") queueIn.put("STATUS")
item = queueOut.get(timeout=4)
semaphore.release()
self.assertEqual(item, "started", 'program not started')
time.sleep(1)
signal.alarm(10)
semaphore.acquire()
queueIn.put("SHUTDOWN")
with self.assertRaises(queue.Empty):
item = queueOut.get(timeout=4) item = queueOut.get(timeout=4)
semaphore.release() semaphore.release()
#threadCommand.join() self.assertEqual(item["state"], "started", 'program not started')
manage.receive_signal(15, 1) time.sleep(1)
manage.wait_children_commands() signal.alarm(10)
#Disable timeout semaphore.acquire()
signal.alarm(0) queueIn.put("SHUTDOWN")
with self.assertRaises(queue.Empty):
item = queueOut.get(timeout=4)
semaphore.release()
#threadCommand.join()
manage.receive_signal(15, 1)
manage.wait_children_commands()
#Disable timeout
signal.alarm(0)
except TimeoutError:
print("test_run_manager_command_restart_after_crash - Prepare Crash - TimeOut !")
#self.fail('Error initialize object ManageCommand')
manage.receive_signal(15, 1)
manage.wait_children_commands()
print("test_run_manager_command_restart_after_crash - Force Crash - TimeOut !")
os._exit(2)
except Exception as e:
print("test_run_manager_command_restart_after_crash - Prepare Crash - %s" % e)
#self.fail('Error initialize object ManageCommand')
manage.receive_signal(15, 1)
manage.wait_children_commands()
handlerCrash(15, 1)
print("test_run_manager_command_restart_after_crash - Force Crash - TimeOut !")
os._exit(2)
self.assertTrue(True) self.assertTrue(True)
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(0) signal.alarm(0)
def test_run_manager_command_restart_after_crash(self): def test_run_manager_command_restart_after_crash(self):
# Enable timeout # Enable timeout
signal.signal(signal.SIGALRM, handlerCrash) signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(10) signal.alarm(10)
logging.debug("--------- test_run_manager_command_restart_after_crash => Start") logging.debug("--------- test_run_manager_command_restart_after_crash => Start")
class MockServerHttp: class MockServerHttp:
def append(self, name, queueIn, queueOut, semaphore): def appendchild(self, name, queueIn, queueOut, semaphore):
pass pass
def terminate(self): def terminate(self):
pass pass
@ -946,22 +984,21 @@ class TestManager(unittest.TestCase):
logging.debug("--------- wait not started") logging.debug("--------- wait not started")
signal.alarm(20) signal.alarm(20)
item = "started" item = {"state": "started"}
while item == "started": while item['state'] == "started":
time.sleep(1) time.sleep(1)
semaphore.acquire() semaphore.acquire()
queueIn.put("STATUS") queueIn.put("STATUS")
try: try:
item = queueOut.get(timeout=4) item = queueOut.get()
except queue.Empty: except queue.Empty:
pass pass
semaphore.release() semaphore.release()
self.assertEqual(item, "crashed", 'program not started') self.assertEqual(item['state'], "crashed", 'program not started')
signal.alarm(20) signal.alarm(20)
logging.debug("--------- wait not crashed") logging.debug("--------- wait not crashed")
# item = "crashed" while item['state'] == "crashed":
while item == "crashed":
time.sleep(1) time.sleep(1)
semaphore.acquire() semaphore.acquire()
queueIn.put("STATUS") queueIn.put("STATUS")
@ -972,12 +1009,11 @@ class TestManager(unittest.TestCase):
semaphore.release() semaphore.release()
signal.alarm(20) signal.alarm(20)
self.assertEqual(item, "started", 'program not started') self.assertEqual(item['state'], "started", 'program not started')
logging.debug("--------- wait not started") logging.debug("--------- wait not started")
signal.alarm(20) signal.alarm(20)
item = "started" while item['state'] == "started":
while item == "started":
time.sleep(1) time.sleep(1)
semaphore.acquire() semaphore.acquire()
queueIn.put("STATUS") queueIn.put("STATUS")
@ -986,20 +1022,17 @@ class TestManager(unittest.TestCase):
except queue.Empty: except queue.Empty:
pass pass
semaphore.release() semaphore.release()
self.assertEqual(item, "crashed", 'program not started') self.assertEqual(item['state'], "crashed", 'program not started')
signal.alarm(20)
self.assertEqual(item, "crashed", 'program not started')
signal.alarm(20) signal.alarm(20)
logging.debug("--------- wait not crashed") logging.debug("--------- wait not crashed")
# item = "crashed" # item = "crashed"
while item == "crashed": while item['state'] == "crashed":
time.sleep(1) time.sleep(1)
semaphore.acquire() semaphore.acquire()
queueIn.put("STATUS") queueIn.put("STATUS")
try: try:
item = queueOut.get(timeout=4) item = queueOut.get()
except queue.Empty: except queue.Empty:
pass pass
semaphore.release() semaphore.release()
@ -1016,14 +1049,23 @@ class TestManager(unittest.TestCase):
manage.wait_children_commands() manage.wait_children_commands()
#Disable timeout #Disable timeout
signal.alarm(0) signal.alarm(0)
except: except TimeoutError:
print("Prepare Crash - TimeOut !") print("test_run_manager_command_restart_after_crash - Prepare Crash - TimeOut !")
#self.fail('Error initialize object ManageCommand') #self.fail('Error initialize object ManageCommand')
manage.receive_signal(15, 1) manage.receive_signal(15, 1)
manage.wait_children_commands() manage.wait_children_commands()
print("Force Crash - TimeOut !") print("test_run_manager_command_restart_after_crash - Force Crash - TimeOut !")
os._exit(2)
except Exception as e:
print("test_run_manager_command_restart_after_crash - Prepare Crash - %s" % e)
#self.fail('Error initialize object ManageCommand')
manage.receive_signal(15, 1)
manage.wait_children_commands()
handlerCrash(15, 1)
print("test_run_manager_command_restart_after_crash - Force Crash - TimeOut !")
os._exit(2) os._exit(2)
self.assertTrue(True) self.assertTrue(True)
signal.signal(signal.SIGALRM, handlerRaise)
signal.alarm(0) signal.alarm(0)
class MockStreamRequestHandler(): class MockStreamRequestHandler():
@ -1222,7 +1264,8 @@ class TestManager(unittest.TestCase):
manage.rfile.define_return( '{"name": "test", "first-line" : "1"}' ) manage.rfile.define_return( '{"name": "test", "first-line" : "1"}' )
manage.headers = {'content-length' : '1000', 'content-type' : 'application/json'} manage.headers = {'content-length' : '1000', 'content-type' : 'application/json'}
manage._command_log() manage._command_log()
self.assertEqual(b'empty',manage.wfile.message) print(manage.wfile.message)
self.assertEqual(b'"empty"',manage.wfile.message)
signal.alarm(0) signal.alarm(0)
@patch.object(http.server.SimpleHTTPRequestHandler, '__init__') @patch.object(http.server.SimpleHTTPRequestHandler, '__init__')
@ -1417,7 +1460,7 @@ class TestManager(unittest.TestCase):
manage.server.listSemaphore = {'test': multiprocessing.Semaphore() } manage.server.listSemaphore = {'test': multiprocessing.Semaphore() }
manage.server.listQueueIn = {'test': multiprocessing.Queue() } manage.server.listQueueIn = {'test': multiprocessing.Queue() }
manage.server.listQueueOut = {'test': multiprocessing.Queue()} manage.server.listQueueOut = {'test': multiprocessing.Queue()}
manage.server.listQueueOut['test'].put("empty") manage.server.listQueueOut['test'].put({"state": "empty"})
manage.rfile.define_return( '{"name": "test", "action": "example"}' ) manage.rfile.define_return( '{"name": "test", "action": "example"}' )
manage.headers = {'content-length' : '1000', 'content-type' : 'application/json'} manage.headers = {'content-length' : '1000', 'content-type' : 'application/json'}
manage._send_action() manage._send_action()
@ -1494,7 +1537,7 @@ class TestManager(unittest.TestCase):
manage.server.listQueueIn = {'test': multiprocessing.Queue() } manage.server.listQueueIn = {'test': multiprocessing.Queue() }
manage.server.listQueueOut = {'test': multiprocessing.Queue()} manage.server.listQueueOut = {'test': multiprocessing.Queue()}
manage.headers = {'content-length' : '1000', 'content-type' : 'application/json'} manage.headers = {'content-length' : '1000', 'content-type' : 'application/json'}
manage.server.listQueueOut['test'].put("empty") manage.server.listQueueOut['test'].put({"state": "empty"})
manage.rfile.define_return( '{"name": "test"}' ) manage.rfile.define_return( '{"name": "test"}' )
manage._send_command("STATUS") manage._send_command("STATUS")
self.assertEqual(b'{"state": "empty"}',manage.wfile.message) self.assertEqual(b'{"state": "empty"}',manage.wfile.message)