This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository mum. See http://git.chorem.org/mum.git commit 04ff1e845d96aac9db23a953e7aeee3d48b2bbe2 Author: Alexis Guilbaud <guilbaud@codelutin.com> Date: Wed Mar 11 13:02:43 2015 +0100 hostpage et serveur : OK pour l'activation et la désactivation de modules --- app/app.py | 12 ++-- app/module_loader.py | 53 +++++++++++++-- app/modules/storage_modules/shelve_db.py | 107 +++++++++++++++++++++++++++---- app/process_monitoring.py | 10 ++- static/js/controllers/headCtrl.js | 2 + static/js/controllers/hostPageCtrl.js | 101 +++++++++++++++++++++++++++++ views/hostpage.html | 31 ++++++++- 7 files changed, 290 insertions(+), 26 deletions(-) diff --git a/app/app.py b/app/app.py index 1d793e1..42536fc 100755 --- a/app/app.py +++ b/app/app.py @@ -28,7 +28,7 @@ class ThreadDetect(threading.Thread): db = self.ml.get_db() scanned_ip = self.ml.run_nmap_detection(self.param, self.opt, db, self.ws, self.ml.get_conection_modules_list(), - self.ml.get_info_mod_monitoring()) + self.ml.get_monitoring_modules_list()) if scanned_ip is not None: self.ws.send(json.dumps({"SUCCESS_MODULE": scanned_ip})) for ip in json.loads(scanned_ip): @@ -141,7 +141,11 @@ def receive(ws): else: print 'res is None' elif code == "GET_LOADED_CONN_MOD": - ws.send(json_dumps({"RES_GET_LOADED_CONN_MOD": ml.get_conection_modules_list()})) + ws.send(json_dumps({"RES_GET_LOADED_CONN_MOD": ml.get_info_mod_conn()})) + elif code == "GET_LOADED_MONI_MOD": + ws.send(json_dumps({"RES_GET_LOADED_MONI_MOD": ml.get_info_mod_monitoring()})) + elif code == "SET_MOD_ACTIVATION": + ml.update_activated_modules(msg["SET_MOD_ACTIVATION"]) else: break except WebSocketError: # Should be WebSocketError when closing the connection @@ -153,10 +157,10 @@ def receive(ws): if __name__ == '__main__': global ml global wsc - ml = ModuleLoader() + ml = ModuleLoader(process_monitoring.add_to_waiting_list, process_monitoring.remove_to_waiting_list) ml.load_all_monitoring_modules() ml.load_all_connection_modules() wsc = WebSocketContainer(ml.get_db()) - #process_monitoring.init(ml, wsc) + process_monitoring.init(ml, wsc) port = int(os.environ.get('PORT', 1337)) run(host='0.0.0.0', port=port, debug=True, server=GeventWebSocketServer) \ No newline at end of file diff --git a/app/module_loader.py b/app/module_loader.py index 6b12871..4a274e3 100644 --- a/app/module_loader.py +++ b/app/module_loader.py @@ -20,7 +20,7 @@ class ModuleLoader: Loads dynamically modules from packages connection_modules, detection_modules, monitoring_modules, storage_modules and contains several methods in order to call the methods once these modules loaded. """ - def __init__(self): + def __init__(self, add_func, rem_func): dict_conf = {} # See conf.txt fconf = open('conf.txt', 'r') for line in fconf.read().splitlines(): @@ -29,18 +29,18 @@ class ModuleLoader: fconf.close() self.db_loc = dict_conf['db_location'] self.external_mod_loc = dict_conf['external_modules_location'] - self.db = self.load_db() + self.db = self.load_db(add_func, rem_func) self.loaded_mod_moni = {} # See load_all_monitoring_modules self.loaded_mod_conn = {} # See load_all_connection_modules - def load_db(self): + def load_db(self, add_func, rem_func): """ Creates an instance of the class shelve_db from storage_modules. :return: an instance of the shelve_db class """ db_name = "shelve_db" db = __import__("modules.storage_modules." + db_name, fromlist=modules.storage_modules) - db_instance = getattr(db, db_name)(self.db_loc) + db_instance = getattr(db, db_name)(self.db_loc, add_func, rem_func) return db_instance def get_db(self): @@ -160,7 +160,7 @@ class ModuleLoader: except modules.CommandNotFoundException.CommandNotFoundException as cnfe: print cnfe.__str__() - def get_info_mod_monitoring(self): + def get_monitoring_modules_list(self): """ Get information about the output, block, compatible os and class name of the monitoring modules. These informations must be specified on each modules, and must be loaded at the launch of the application. @@ -179,6 +179,27 @@ class ModuleLoader: """ return self.loaded_mod_moni + def get_info_mod_monitoring(self): + """ + Get the necessary informations about the monitoring modules for the web application side. + :return: a JSON string containing the monitoring informations: + { + mod_name: + { + 'compatible_os': [val1, val2, ...], => a list containing the compatibles os + 'unit': val, => the unit type of return ('%', 'bool' or other) + 'block': val, => the monitoring block of the module + } + } + """ + res = {} + for mod in self.loaded_mod_moni: + res[mod] = {} + res[mod]['compatible_os'] = self.loaded_mod_moni[mod]['compatible_os'] + res[mod]['unit'] = self.loaded_mod_moni[mod]['unit'] + res[mod]['block'] = self.loaded_mod_moni[mod]['block'] + return json.dumps(res) + def load_all_connection_modules(self): """ Instanciates and stores the informations about each connection modules avaliable @@ -207,6 +228,23 @@ class ModuleLoader: { mod_name: { + 'class_name': val, => the name of the class to instanciate + 'imported': module, => the module imported + 'params': {param1: type1, param2: type2, ...} => the parameters necessary to create the connection + } + } + """ + return self.loaded_mod_conn + + def get_info_mod_conn(self): + """ + Get informations about of the connection modules loaded and their parameters necessary to instanciate the + connection. The type of the parameters is necessary for the web application in order to show a corresponding + form (can be 'string' for a textfield, 'int' for a number field, 'file' for a file location). + :return: a JSON string containing informations about the connection modules: + { + mod_name: + { 'params': {param1: type1, param2: type2, ...} => the parameters necessary to create the connection } } @@ -245,4 +283,7 @@ class ModuleLoader: } :return: the result of the called function. Or None if the function does not returns a value. """ - return getattr(self.db, dict_instr['func'])(dict_instr['args']) \ No newline at end of file + return getattr(self.db, dict_instr['func'])(dict_instr['args']) + + def update_activated_modules(self, args): + self.db.config_mod_activation(args, self.get_monitoring_modules_list()) \ No newline at end of file diff --git a/app/modules/storage_modules/shelve_db.py b/app/modules/storage_modules/shelve_db.py index cb1f914..052e712 100644 --- a/app/modules/storage_modules/shelve_db.py +++ b/app/modules/storage_modules/shelve_db.py @@ -13,9 +13,11 @@ class shelve_db: Storage module for the persistant objects in Python : Shelve. Every function in need to access the database have to be moved in this class. """ - def __init__(self, db_loc): + def __init__(self, db_loc, add_func, rem_func): self.db = None self.db_loc = db_loc + self.add_to_monitoring_list = add_func + self.rem_to_monitoring_list = rem_func def open_db(self): """ @@ -124,12 +126,13 @@ class shelve_db: :return a list containing the default parameters for each monitoring module (at least ping) """ res = {} + # creating the configuration for all modules compatibles if not self.db["hosts"][addr_host]["conf"]["connections"] == {} and not os_host == 'unknown': for mod in dict_mod_info: if os_host in dict_mod_info[mod]['compatible_os']: mod_conf = {} mod_conf['block'] = dict_mod_info[mod]['block'] - mod_conf['activated'] = True + mod_conf['activated'] = False mod_conf['check_frequency'] = 60 mod_conf['nb_minute'] = 30 mod_conf['nb_hour'] = 12 @@ -166,6 +169,48 @@ class shelve_db: res['ping'] = ping_conf return res + def generate_unique_conf(self, dict_mod_info, addr_host, mod_name, activated): + """ + Configures automatically the monitoring for a host for each of the monitoring modules avaliable, in + function of the unit of the result of the monitoring module. + By default, only the ping monitoring is activated, while the connection is not configured and tje os not + detected + :param dict_mod_info: dictionary containing informations about all notification modules, in the form: + { + mod_name: + { + 'class_name': val, => the name of the class to instanciate + 'compatible_os': [val1, val2, ...], => a list containing the compatibles os + 'unit': val, => the unit type of return ('%', 'bool' or other) + 'block': val, => the monitoring block of the module + 'external': bool => indicates if this modules comes from external directory + } + } + :return a list containing the default parameters for each monitoring module (at least ping) + """ + mod_conf = {} + mod_conf['block'] = dict_mod_info[mod_name]['block'] + mod_conf['activated'] = activated + mod_conf['check_frequency'] = 60 + mod_conf['nb_minute'] = 30 + mod_conf['nb_hour'] = 12 + mod_conf['nb_day'] = 15 + mod_conf['nb_week'] = 2 + mod_conf['nb_month'] = 6 + mod_conf['nb_year'] = None + unit = dict_mod_info[mod_name]['unit'] + mod_conf['unit'] = unit + if unit == '%': + mod_conf['minor_limit'] = 95 + mod_conf['major_limit'] = 100 + elif unit == 'bool': + mod_conf['minor_limit'] = True + mod_conf['major_limit'] = False + else: + mod_conf['minor_limit'] = 8 + mod_conf['major_limit'] = 10 + self.db["hosts"][addr_host]["conf"]["monitoring"][mod_name] = mod_conf + def get_conn_param(self, args): """ Returns the connection parameters of an host. @@ -310,6 +355,9 @@ class shelve_db: 'last_check':date } } + 'activated_monitoring':{ + mod_name: bool + } 'detection':{ mod_name:res } @@ -331,6 +379,9 @@ class shelve_db: res['detected'] = {} for mod in self.db['hosts'][addr_host]['detected']: res['detected'][mod] = json.loads(self.db['hosts'][addr_host]['detected'][mod]) + res['activated_monitoring'] = {} + for mod in self.db['hosts'][addr_host]['conf']['monitoring']: + res['activated_monitoring'][mod] = self.db['hosts'][addr_host]['conf']['monitoring'][mod]['activated'] res['custom_infos'] = self.db['hosts'][addr_host]['conf']['custom_info'] res['interventions'] = self.db['hosts'][addr_host]['conf']['interventions'] except Exception as e: @@ -381,7 +432,6 @@ class shelve_db: 'txt': val } """ - print args addr_host = args['addr_host'] txt = args['txt'] self.open_db() @@ -412,20 +462,53 @@ class shelve_db: finally: self.close_db() - def config_mod_activation(self, addr_host, data_list): + def config_mod_activation(self, args, dict_mod_info): """ Activates or desactivates monitoring modules for a given host - :param addr_host: the IP adress of the host - :param data_list: the activation data on the form : - [ - {mod_name : bool}, - ... - ] + :param args: a dictionary containing : + { + 'addr_host': val, + 'activated':{ + mod_name: bool + } + } + :param dict_mod_info: A dictionary containing informations about all notification modules, in the form: + { + mod_name: + { + 'class_name': val, => the name of the class to instanciate + 'compatible_os': [val1, val2, ...], => a list containing the compatibles os + 'unit': val, => the unit type of return ('%', 'bool' or other) + 'block': val, => the monitoring block of the module + 'external': bool => indicates if this modules comes from external directory + } + } """ + addr_host = args['addr_host'] self.open_db() try: - for mod_name in data_list: - self.db["hosts"][addr_host]["conf"]["monitoring"][mod_name]["activated"] = data_list[mod_name] + for mod_name in args['activated']: + # first case : the monitoring module have never been activated for this host + if not mod_name in self.db["hosts"][addr_host]["conf"]["monitoring"]: + self.db["hosts"][addr_host]["conf"]["monitoring"][mod_name] = {} + self.generate_unique_conf(dict_mod_info, addr_host, mod_name, args['activated'][mod_name]) + elif not self.db["hosts"][addr_host]["conf"]["monitoring"][mod_name]["activated"] == \ + args['activated'][mod_name]: + # second case, the configuration module have changed + if args['activated'][mod_name]: + # if it is now activated, we have to add it to the monitoring list + self.db["hosts"][addr_host]["conf"]["monitoring"][mod_name]["activated"] = True + dict_instr = {} + dict_instr['addr'] = addr_host + dict_instr['os'] = json.loads(self.db['hosts'][addr_host]['detected']['nmap'])['os'] + dict_instr['mod_name'] = mod_name + dict_instr['time'] = datetime.now() + dict_instr['freq'] = self.db['hosts'][addr_host]['conf']['monitoring'][mod_name]['check_frequency'] + self.add_to_monitoring_list(dict_instr) + else: + # if not, we have to remove it from the monitoring list + self.db["hosts"][addr_host]["conf"]["monitoring"][mod_name]["activated"] = False + self.rem_to_monitoring_list(args['addr_host'], mod_name) finally: self.close_db() diff --git a/app/process_monitoring.py b/app/process_monitoring.py index a3aaee9..a4e852d 100644 --- a/app/process_monitoring.py +++ b/app/process_monitoring.py @@ -10,7 +10,7 @@ waiting_list = [] def init(ml,wsc): - for instr in ml.get_all_monitoring_instructions(ml.get_db()): + for instr in ml.get_all_monitoring_instructions(): #print "adding : " + str(instr) sys.stdout.flush() add_to_waiting_list(instr) @@ -21,7 +21,7 @@ def init(ml,wsc): class ProcessMonitoring(threading.Thread): """ Runs the monitoring modules at the specified time. - Attributes : - waiting_queue : a list containing structured data concerning the monitring modules to launch + Attributes : - waiting_list : a list containing structured data concerning the monitring modules to launch in the form { 'addr' : val, => the IP address of the host @@ -77,6 +77,12 @@ def add_to_waiting_list(dict_mod): waiting_list.insert(pos_queue, dict_mod) +def remove_to_waiting_list(addr_host, modname): + global waiting_list + for i in range(len(waiting_list)): + if waiting_list[i]['addr'] == addr_host and waiting_list[i]['mod_name'] == modname: + waiting_list.pop(i) + class RunMonitoring(threading.Thread): def __init__(self, list_dict_mod, ml, wsc): threading.Thread.__init__(self) diff --git a/static/js/controllers/headCtrl.js b/static/js/controllers/headCtrl.js index fa1bd32..10bbc86 100644 --- a/static/js/controllers/headCtrl.js +++ b/static/js/controllers/headCtrl.js @@ -42,6 +42,8 @@ mumApp.controller('headCtrl', function($scope, $rootScope, toastr, $interval, $r case "RES_GET_LOADED_CONN_MOD": $rootScope.$broadcast("resGetLoadedConnMod", JSON.parse(value)); break; + case "RES_GET_LOADED_MONI_MOD": + $rootScope.$broadcast("resGetLoadedMoniMod", JSON.parse(value)); case "RES_CALL_FUNC_DB": // Get a result after calling a funcion on the db $rootScope.$broadcast("resCall", JSON.parse(value)); break; diff --git a/static/js/controllers/hostPageCtrl.js b/static/js/controllers/hostPageCtrl.js index 669c62c..19effef 100644 --- a/static/js/controllers/hostPageCtrl.js +++ b/static/js/controllers/hostPageCtrl.js @@ -26,6 +26,9 @@ mumApp.controller('hostPageCtrl', function($scope, $rootScope, $routeParams, $mo "value":val } }, + 'activated_monitoring':{ + mod_name: bool + }, "custom_infos":val } */ @@ -91,6 +94,20 @@ mumApp.controller('hostPageCtrl', function($scope, $rootScope, $routeParams, $mo } }); }; + + $scope.open_modal_block = function () { + var modalInstance = $modal.open({ + templateUrl: 'modal_block_label.html', + controller: 'ModalBlockInstanceCtrl', + resolve: { + block_args: function(){ + return {'addr_host' : $scope.addr_host, + 'activated_mod': $scope.items['activated_monitoring'], + 'os_host': $scope.items.detected.nmap.os}; + } + } + }); + }; }); // modals controllers @@ -295,6 +312,90 @@ mumApp.controller('ModalConfConnInstanceCtrl', function ($scope, $rootScope, $mo $modalInstance.close(); }; }); + +mumApp.controller('ModalBlockInstanceCtrl', function ($scope, $rootScope, $modalInstance, block_args) { + $scope.block_args = block_args; /* {'addr_host': val, + 'activated_mod':{ + mod_name: bool + }, + 'os_host': val */ + + $scope.loaded_moni_mods = {}; /* { + mod_name: + { + 'compatible_os': [val1, val2, ...], => a list containing the compatibles os + 'unit': val, => the unit type of return ('%', 'bool' or other) + 'block': val, => the monitoring block of the module + } + } */ + + $scope.blocks = {}; /* + block_name: + { + mod_name: + { + 'modname':val, + 'activated': bool + } + } + */ + + $rootScope.$broadcast("sendViaWs", JSON.stringify({"GET_LOADED_MONI_MOD": ""})); + + $scope.$on("resGetLoadedMoniMod", function (event, args) { + $scope.$apply(function(){ + $scope.loaded_moni_mods = args; + for(mod in args){ + var activated = false; + var block = args[mod]['block']; + if($scope.block_args['activated_mod'].hasOwnProperty(mod)) { // if the module is known by the host + if($scope.block_args['activated_mod'][mod]){ // if the module is activated + activated = true; + } + } + if(!$scope.blocks.hasOwnProperty(block)){ + $scope.blocks[block] = []; + } + $scope.blocks[block][$scope.blocks[block].length] = {modname: mod, activated: activated}; + } + }); + }); + + $scope.select_all = function(block){ + for(mod in block){ + mod.activated = true; + } + }; + + $scope.select_none = function(block){ + for(mod in block){ + mod.activated = false; + } + }; + + $scope.is_not_compatible = function(modname){ + return $scope.loaded_moni_mods[modname]['compatible_os'].lastIndexOf($scope.block_args.os_host) < 0; + }; + + $scope.ok = function () { + var args = {}; + args['addr_host'] = $scope.block_args['addr_host']; + args['activated'] = {}; + for(block in $scope.blocks){ + for(mod in $scope.blocks[block]){ + args['activated'][$scope.blocks[block][mod]['modname']] = $scope.blocks[block][mod]['activated']; + } + } + + $rootScope.$broadcast("sendViaWs", JSON.stringify({"SET_MOD_ACTIVATION": args})); + + $modalInstance.close(); + }; + + $scope.cancel = function () { + $modalInstance.close(); + }; +}); /* mumApp.directive("formLoader", ['$compile', '$templateCache', function ($compile, $templateCache) { diff --git a/views/hostpage.html b/views/hostpage.html index b7df634..729266a 100644 --- a/views/hostpage.html +++ b/views/hostpage.html @@ -1,7 +1,7 @@ <div class="col-md-offset-2 main"> <h1 class="page-header">Current state of {{addr_host}} <small>{{items.hostname}}</small></h1> - <button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#modal_block">Activate/Deactivate</button> + <button type="button" class="btn btn-primary btn-xs" ng-click="open_modal_block()">Activate/Deactivate</button> <button type="button" class="btn btn-primary btn-xs" ng-click="open_modal_conn()">Connection settings</button> <button type="button" class="btn btn-info btn-xs">Launch a new detection</button> <button type="button" class="btn btn-danger btn-xs" ng-click="call_func('remove_host', [addr_host])">Remove this host</button> @@ -187,7 +187,34 @@ <h3 class="modal-title">Advanced settings for {{conf_conn_args['modname']}}</h3>{{loaded_conn_mods}} </div> {{form}} - <div ng-include=" 'templateForm.html' "></div> + <div class="modal-body"> + <div ng-include=" 'templateForm.html' "></div> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal" ng-click="cancel()">Close</button> + <button type="button" class="btn btn-primary" ng-click="ok()">Save changes</button> + </div> + </script> + + <script type="text/ng-template" id="modal_block_label.html"> + <div class="modal-header"> + <h3 class="modal-title">Activate/Deactivate monitoring blocks</h3> + </div> + <div class="modal-body"> + <table class="table table-bordered table-hover"> + <tr ng-repeat-start="(blockname, block) in blocks"> + <th>{{blockname}}</th> + <th><button type="button" class="btn" ng-click="select_all(block)">Select all</button> + <button type="button" class="btn" ng-click="select_none(block)">Select none</button> + </th> + </tr> + <tr ng-repeat-end + ng-repeat="mod in block"> + <td>{{mod.modname}}</td> + <td><input type="checkbox" ng-model="mod.activated" ng-disabled="is_not_compatible(mod.modname)"></td> + </tr> + </table> + </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal" ng-click="cancel()">Close</button> <button type="button" class="btn btn-primary" ng-click="ok()">Save changes</button> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.