#!/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 . """ Manipulate manager khaganat We can end some command to manager. Global: SHUTDOWN : Stop manager & Stop all programs STARTALL : Start all programs STATUSALL : Get status of all programs STOPALL : Stop all programs LIST : List all programs available For one program : START : Start program STOP : Stop program STATUS : Get status LOG : Get log firstline : option to define first line we need send ACTION : Send action (command) in stdin action : option to define which action you need send to stdin Example : ./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 import socket import ssl def cmp_to_key(): 'compare key (check if int or other)' 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 class HTTPSConnectionCertificate(http.client.HTTPConnection): """ Class HTTP connection with check certificate (if certicate is defined) """ def __init__(self, key_file, cert_file, ca_cert, host='localhost', port=8000, timeout=10): """ Constructor """ logging.debug("constructor") http.client.HTTPConnection.__init__(self, host, port, timeout) self.key_file = key_file self.cert_file = cert_file self.ca_cert = ca_cert self.host = host self.port = port def connect(self): """ connect in https (and check certificate if defined) """ logging.debug("connect launched") sock = socket.create_connection((self.host, self.port), self.timeout) # If there's no CA File, don't force Server Certificate Check if self.ca_cert: logging.debug("key_file: " + self.key_file) logging.debug("cert_file: " + self.cert_file) logging.debug("ca_cert: " + self.ca_cert) self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ca_certs=self.ca_cert, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1_2 #PROTOCOL_SSLv23 #, ciphers="ADH-AES256-SHA" ) else: self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, cert_reqs=ssl.CERT_NONE) def send_json(jsonin={}, command='GET', path='/', host='localhost', port=8000, raw_data=False, remove_color=False, key_file=None, cert_file=None, ca_cert=None, timeout=10): """ send command with https & json format """ conn = HTTPSConnectionCertificate(host=host, port=port, key_file=key_file, cert_file=cert_file, ca_cert=ca_cert, timeout=timeout) conn.putrequest(command, path) out=json.dumps(jsonin) conn.putheader('Content-type', 'application/json') 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(host, command, program, action, firstline, fileLog, logLevel, show_log_console, port=8000, raw_data=False, remove_color=False, key_file=None, cert_file=None, ca_cert=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') # Send command if command == 'START' or command == 'STOP': send_json({'name': program}, 'POST', "/" + command, host, port, key_file=key_file, cert_file=cert_file, ca_cert=ca_cert) elif command == 'STATUS': send_json({'name': program}, 'GET', "/" + command, host, port, key_file=key_file, cert_file=cert_file, ca_cert=ca_cert) elif command == 'ACTION': send_json({'name': program, 'action' : action}, 'POST', "/" + command, host, port, key_file=key_file, cert_file=cert_file, ca_cert=ca_cert) elif command == 'LOG': send_json({'name': program, 'first-line' : firstline }, 'GET', "/" + command, host, port, raw_data, remove_color, key_file=key_file, cert_file=cert_file, ca_cert=ca_cert) elif command == 'LIST': send_json({}, 'GET', "/" + command, host, port, key_file=key_file, cert_file=cert_file, ca_cert=ca_cert) elif command == 'SHUTDOWN' or command == 'STARTALL' or command == 'STOPALL': send_json({}, 'POST', "/" + command, host, port, key_file=key_file, cert_file=cert_file, ca_cert=ca_cert) elif command == 'STATUSALL': send_json({}, 'GET', "/" + command, host, port, key_file=key_file, cert_file=cert_file, ca_cert=ca_cert) 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('--ca_cert', help='ca_cert file', default=None) parser.add_argument('--host', 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(host = args.host, 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, ca_cert=args.ca_cert, remove_color=not args.keep_color)