def worker_vacuum(commands, command, config): start_time = time.time() * 1000 set_logger_name("vacuum_worker") logger = get_logger(config) logger.info("Starting with pid=%s" % (os.getpid())) logger.debug("commandid=%s" % (command.commandid, )) try: command.state = COMMAND_START command.time = time.time() commands.update(command) parameters = pickle.loads(base64.b64decode(command.parameters)) logger.debug("table=%s, mode=%s, database=%s" % ( parameters['table'], parameters['mode'], parameters['database'], )) conn = connector(host=config.postgresql['host'], port=config.postgresql['port'], user=config.postgresql['user'], password=config.postgresql['password'], database=parameters['database']) conn.connect() if parameters['mode'] == 'standard': query = "VACUUM %s" % (parameters['table'], ) else: query = "VACUUM %s %s" % ( parameters['mode'], parameters['table'], ) conn.execute(query) conn.close() except (error, SharedItem_not_found, Exception) as e: command.state = COMMAND_ERROR command.result = str(e) command.time = time.time() logger.traceback(get_tb()) logger.error(str(e)) try: commands.update(command) conn.close() except Exception as e: pass logger.info("Failed.") return try: command.state = COMMAND_DONE command.time = time.time() commands.update(command) except Exception as e: logger.traceback(get_tb()) logger.error(str(e)) logger.info("Done.") logger.debug(" in %s s." % (str( (time.time() * 1000 - start_time) / 1000), ))
def api_vacuum(http_context, queue_in = None, config = None, sessions = None, commands = None): set_logger_name("administration") worker = b'vacuum' # Get a new logger. logger = get_logger(config) try: check_sessionid(http_context['headers'], sessions) post = http_context['post'] # Check POST parameters. validate_parameters(post, [ ('database', T_OBJECTNAME, False), ('table', T_OBJECTNAME, False), ('mode', T_VACUUMMODE, False) ]) # Serialize parameters. parameters = base64.b64encode( pickle.dumps({ 'database': post['database'], 'table': post['table'], 'mode': post['mode'] })).decode('utf-8') except (Exception, HTTPError) as e: logger.traceback(get_tb()) logger.error(str(e)) if isinstance(e, HTTPError): raise e else: raise HTTPError(500, "Internal error.") # Check command uniqueness. try: commands.check_uniqueness(worker, parameters) except SharedItem_exists as e: logger.traceback(get_tb()) logger.error(str(e)) raise HTTPError(402, "Vaccum '%s' already running on table '%s'." % (post['mode'], post['table'])) cid = hash_id(worker + b'-' + parameters.encode('utf-8')) command = Command( cid.encode('utf-8'), time.time(), 0, worker, parameters, 0, u'') try: commands.add(command) # Put the Command in the command queue queue_in.put(command) return {"cid": cid} except SharedItem_no_free_slot_left as e: logger.traceback(get_tb()) logger.error(str(e)) raise HTTPError(500, "Internal error.")
def supervision_sender_worker(commands, command, config): signal.signal(signal.SIGTERM, supervision_worker_sigterm_handler) start_time = time.time() * 1000 set_logger_name("supervision_sender_worker") logger = get_logger(config) # TODO: logging methods in supervision plugin must be aligned. logging.root = logger logger.debug("Starting with pid=%s" % (os.getpid())) logger.debug("commandid=%s" % (command.commandid)) command.state = COMMAND_START command.time = time.time() command.pid = os.getpid() commands.update(command) c = 0 while True: # Let's do it smoothly.. time.sleep(0.5) q = Queue('%s/metrics.q' % (config.temboard['home']), max_size=1024 * 1024 * 10, overflow_mode='slide') msg = q.shift(delete=False) if msg is None: break try: send_output(config.plugins['supervision']['ssl_ca_cert_file'], config.plugins['supervision']['collector_url'], config.temboard['key'], msg.content) except urllib2.HTTPError as e: logger.traceback(get_tb()) logger.error(str(e)) # On an error 409 (DB Integrity) we need to remove the message. if int(e.code) != 409: logger.debug("Duration: %s." % (str(time.time() * 1000 - start_time))) logger.debug("Failed.") sys.exit(1) except Exception as e: logger.traceback(get_tb()) logger.error(str(e)) logger.debug("Duration: %s." % (str(time.time() * 1000 - start_time))) logger.debug("Failed.") sys.exit(1) _ = q.shift(delete=True, check_msg=msg) if c > 60: break c += 1 logger.debug("Duration: %s." % (str(time.time() * 1000 - start_time))) logger.debug("Done.")
def exec_scheduler(queue_in, config, commands, logger): """ In charge of running the scheduling function of each plugin. """ global PLUGINS_LAST_SCHEDULE for plugin_name in config.plugins: first_run = False if not (plugin_name in sys.modules): # The plugin seems not to be loaded. continue if not ('scheduler_interval' in config.plugins[plugin_name]): # If scheduler_interval is not set, we don't want to run it. continue if not (plugin_name in PLUGINS_LAST_SCHEDULE): # Check if this is the first shoot. PLUGINS_LAST_SCHEDULE[plugin_name] = time.time() first_run = True if not first_run and (time.time() - PLUGINS_LAST_SCHEDULE[plugin_name]) < \ config.plugins[plugin_name]['scheduler_interval']: continue try: logger.debug("Running %s.scheduler()" % (plugin_name)) # Call plugin's scheduler() function. getattr(sys.modules[plugin_name], 'scheduler')(queue_in, config, commands) PLUGINS_LAST_SCHEDULE[plugin_name] = time.time() logger.debug("Done.") except AttributeError as e: # scheduler() function does not exist. logger.debug("Function does not exist.") pass except Exception as e: logger.traceback(get_tb()) logger.error(str(e)) logger.debug("Failed.")
def api_function_wrapper(config, http_context, sessions, module, function_name): """ API function wrapper in charge of: - instanciate a new logger; - check the user session id; - call a function named 'function_name' from 'module_name' module and return its result; """ logger = get_logger(config) logger.debug("Calling %s.%s()." % (module.__name__, function_name,)) logger.debug(http_context) try: username = check_sessionid(http_context['headers'], sessions) http_context['username'] = username dm = getattr(module, function_name)(config, http_context) logger.debug("Done.") return dm except (Exception, HTTPError) as e: logger.traceback(get_tb()) logger.error(str(e)) logger.debug("Failed.") if isinstance(e, HTTPError): raise e else: raise HTTPError(500, "Internal error.")
def get_command(http_context, queue_in=None, config=None, sessions=None, commands=None): headers = http_context['headers'] set_logger_name("api") logger = get_logger(config) logger.info("Get command status.") try: check_sessionid(headers, sessions) except HTTPError as e: logger.traceback(get_tb()) logger.error(e.message) logger.info("Invalid session.") raise e cid = http_context['urlvars'][0] try: command = commands.get_by_commandid(cid.encode('utf-8')) c_time = command.time c_state = command.state c_result = command.result if c_state == COMMAND_DONE or c_state == COMMAND_ERROR: commands.delete(cid.encode('utf-8')) logger.info("Done.") return { 'cid': cid, 'time': c_time, 'state': c_state, 'result': c_result } except SharedItem_not_found as e: logger.traceback(get_tb()) logger.error(e.message) logger.info("Failed.") raise HTTPError(401, "Invalid command.")
def httpd_run(commands, queue_in, config, sessions): """ Serve HTTP for ever and reload configuration from the conf file on SIGHUP signal catch. """ server_address = (config.temboard['address'], config.temboard['port']) handler_class = handleRequestsUsing(commands, queue_in, config, sessions) httpd = ThreadedHTTPServer(server_address, handler_class) httpd.socket = ssl.wrap_socket(httpd.socket, keyfile=config.temboard['ssl_key_file'], certfile=config.temboard['ssl_cert_file'], server_side=True) # We need a timeout here because the code after httpd.handle_request() call # is written to handle configuration re-loading and needs to be ran periodicaly. httpd.timeout = 1 set_logger_name("httpd") logger = get_logger(config) while True: httpd.handle_request() if reload_true(): # SIGHUP caught # Try to load configuration from the configuration file. try: logger.info("SIGHUP signal caught, trying to reload " "configuration.") new_config = Configuration(config.configfile) # Prevent any change on plugins list.. new_config.temboard['plugins'] = config.temboard['plugins'] new_config.plugins = load_plugins_configurations(new_config) # New RequestHandler using the new configuration. httpd.RequestHandlerClass = handleRequestsUsing( commands, queue_in, new_config, sessions) del logger # ... and re-create a new one with the new # configuration. set_logger_name("httpd") logger = get_logger(new_config) logger.info("Done.") except (ConfigurationError, ImportError) as e: logger.traceback(get_tb()) logger.error(str(e)) logger.info("Keeping previous configuration.") # Reset the global var indicating a SIGHUP signal. set_global_reload(False)
def api_function_wrapper_pg(config, http_context, sessions, module, function_name): """ API function wrapper in charge of: - instanciate a new logger; - check the user session id; - start a new PostgreSQL connexion; - call a function named 'function_name' from 'module_name' module and return its result; - close the PG connexion. """ logger = get_logger(config) logger.debug("Calling %s.%s()." % (module.__name__, function_name,)) logger.debug(http_context) try: username = check_sessionid(http_context['headers'], sessions) http_context['username'] = username conn = connector( host = config.postgresql['host'], port = config.postgresql['port'], user = config.postgresql['user'], password = config.postgresql['password'], database = config.postgresql['dbname'] ) conn.connect() dm = getattr(module, function_name)(conn, config, http_context) conn.close() logger.debug("Done.") return dm except (error, Exception, HTTPError) as e: logger.traceback(get_tb()) logger.error(str(e)) logger.debug("Failed.") try: conn.close() except Exception: pass if isinstance(e, HTTPError): raise e else: raise HTTPError(500, "Internal error.")
def dashboard_collector_worker(commands, command, config): try: signal.signal(signal.SIGTERM, dashboard_worker_sigterm_handler) start_time = time.time() * 1000 set_logger_name("dashboard_collector") logger = get_logger(config) logger.debug("Starting with pid=%s" % (getpid())) logger.debug("commandid=%s" % (command.commandid)) command.state = COMMAND_START command.time = time.time() command.pid = getpid() commands.update(command) conn = connector(host=config.postgresql['host'], port=config.postgresql['port'], user=config.postgresql['user'], password=config.postgresql['password'], database=config.postgresql['dbname']) conn.connect() db_metrics = metrics.get_metrics(conn, config) # We don't want to store notifications in the history. db_metrics.pop('notifications', None) conn.close() q = Queue('%s/dashboard.q' % (config.temboard['home']), max_length=(config.plugins['dashboard']['history_length'] + 1), overflow_mode='slide') q.push(Message(content=json.dumps(db_metrics))) logger.debug("Duration: %s." % (str(time.time() * 1000 - start_time))) logger.debug("Done.") except (error, Exception) as e: logger.traceback(get_tb()) logger.error(str(e)) logger.debug("Failed.") try: conn.close() except Exception: pass sys.exit(1)
def load_plugins_configurations(config): """ Intend to load plugins and run their configuration() function. Plugins are defined as a module located in plugins/ directory. The list of plugins to load is set into temboard section of the configuration file: [temboard] plugins = [ "plugin1", "plugin2" ] """ # Get this module's path. path = os.path.dirname(__file__) ret = dict() # Get the logger. logger = get_logger(config) # PostgreSQL version pg_version = 0 while pg_version == 0: try: conn = connector( host = config.postgresql['host'], port = config.postgresql['port'], user = config.postgresql['user'], password = config.postgresql['password'], database = config.postgresql['dbname'] ) """ Trying to get PostgreSQL version number. """ conn.connect() pg_version = conn.get_pg_version() conn.close() except Exception as e: logger.traceback(get_tb()) logger.error(str(e)) logger.error("Not able to get PostgreSQL version number.") try: conn.close() except Exception: pass # If we reach this point, PostgreSQL is not available, so we # wait 5 seconds and try again if pg_version == 0: time.sleep(5) # Loop through each plugin listed in the configuration file. for plugin_name in config.temboard['plugins']: logger.info("Loading plugin '%s'." % (plugin_name,)) try: # Loading compat.py file fp_s, pathname_s, description_s = imp.find_module('compat', [path + '/plugins/'+plugin_name]) module_compat = imp.load_module('compat', fp_s, pathname_s, description_s) # Check modules's PG_MIN_VERSION try: if (module_compat.PG_MIN_VERSION > pg_version): # Version not supported logger.error("PostgreSQL version (%s) is not supported (min:%s)." % (pg_version, module_compat.PG_MIN_VERSION)) logger.info("Failed.") continue except ValueError as e: # PG_MIN_VERSION not set pass except Exception as e: if fp_s: fp_s.close() logger.info("Not able to load the compatibility file: compat.py.") logger.info("Done.") try: # Locate and load the module with imp. fp, pathname, description = imp.find_module(plugin_name, [path + '/plugins']) module = imp.load_module(plugin_name, fp, pathname, description) # Try to run module's configuration() function. logger.info("Loading plugin '%s' configuration." % (plugin_name)) plugin_configuration = getattr(module, 'configuration')(config) ret.update({module.__name__: plugin_configuration}) logger.info("Done.") except AttributeError as e: logger.info("No configuration.") except Exception as e: if fp: fp.close() logger.traceback(get_tb()) logger.error(str(e)) logger.info("Failed.") return ret
def post_pg_control(http_context, queue_in = None, config = None, sessions = None, commands = None): # NOTE: in this case we don't want to use api functions wrapper, it leads # to "Broken pipe" error with debian init.d on start/restart. This is # probably due to getattr() call. set_logger_name("administration") # Get a new logger. logger = get_logger(config) post = http_context['post'] try: check_sessionid(http_context['headers'], sessions) # Check POST parameters. validate_parameters(post, [ ('action', T_CONTROL, False) ]) session = sessions.get_by_sessionid(http_context['headers']['X-Session'].encode('utf-8')) except (Exception, HTTPError) as e: logger.traceback(get_bt()) logger.error(str(e)) logger.debug(http_context) if isinstance(e, HTTPError): raise e else: raise HTTPError(500, "Internal error.") try: NotificationMgmt.push(config, Notification( username = session.username, message = "PostgreSQL %s" % (post['action']))) except (NotificationError, Exception) as e: logger.traceback(get_tb()) logger.error(str(e)) try: logger.info("PostgreSQL '%s' requested." % (post['action'])) cmd_args = oneline_cmd_to_array(config.plugins['administration']['pg_ctl'] % (post['action'])) (rcode, stdout, stderr) = exec_script(cmd_args) if rcode != 0: raise Exception(str(stderr)) # Let's check if postgresql is up & running on 'start' or 'restart' action. if post['action'] in ['start', 'restart']: conn = connector( host = config.postgresql['host'], port = config.postgresql['port'], user = config.postgresql['user'], password = config.postgresql['password'], database = config.postgresql['dbname'] ) # When a start/restart operation is requested, after the startup/pg_ctl # script is executed we check that postgres is up & running: while the # PG connection is not working, during 10 seconds (max) we'll check # (connect/SELECT 1/disconnect) the connection, every 0.5 second. retry = True t_start = time.time() while retry: try: conn.connect() conn.execute('SELECT 1') conn.close() logger.info("Done.") return {'action': post['action'], 'state': 'ok'} except error as e: if (time.time() - t_start) > 10: try: conn.close() except error as e: pass except Exception: pass logger.info("Failed.") return {'action': post['action'], 'state': 'ko'} time.sleep(0.5) elif post['action'] == 'stop': conn = connector( host = config.postgresql['host'], port = config.postgresql['port'], user = config.postgresql['user'], password = config.postgresql['password'], database = config.postgresql['dbname'] ) # Check the PG conn is not working anymore. try: retry = True t_start = time.time() while retry: conn.connect() conn.execute('SELECT 1') conn.close() time.sleep(0.5) if (time.time() - t_start) > 10: retry = False logger.info("Failed.") return {'action': post['action'], 'state': 'ko'} except error as e: logger.info("Done.") return {'action': post['action'], 'state': 'ok'} logger.info("Done.") return {'action': post['action'], 'state': 'ok'} except (Exception, error, HTTPError) as e: logger.traceback(get_tb()) logger.error(str(e)) logger.info("Failed") if isinstance(e, HTTPError): raise e else: raise HTTPError(500, "Internal error.")
def Scheduler(commands, queue_in, config, sessions): """ Asynchronous command scheduler in charge of: - fetching new async command from the command queue. - if any new command, starting a new worker process. - doing maintenance tasks like sessions and commands clean-up. - executing function named scheduler() from each loaded plugins. """ set_logger_name("scheduler") logger = get_logger(config) # Add a signal handler on SIGTERM and SIGHUP signals. signal.signal(signal.SIGTERM, scheduler_sigterm_handler) signal.signal(signal.SIGHUP, scheduler_sighup_handler) logger.debug("Starting with pid=%s", (getpid())) workers = [] while True: if reload_true(): # SIGHUP signal caught. try: logger.info("SIGHUP signal caught, trying to reload" " configuration.") new_config = Configuration(config.configfile) # Prevent any change on plugins list.. new_config.temboard['plugins'] = config.temboard['plugins'] new_config.plugins = load_plugins_configurations(new_config) # Logger re-creation. del logger set_logger_name("scheduler") logger = get_logger(new_config) config = new_config logger.info("New configuration loaded.") except (ConfigurationError, ImportError) as e: logger.traceback(get_tb()) logger.error(str(e)) logger.info("Some error occured, keeping old configuration.") set_global_reload(False) try: # Fetch one new input from the command queue. # There is a 0.5s second timeout and the call isn't blocking. command = queue_in.get(True, 0.5) except Exception: # No new command. pass else: # Start the worker process. newworker = Process(target=Worker, args=(commands, command, config)) workers.append(newworker) newworker.start() # Check that worker processes are still alive. for worker in workers: if not worker.is_alive(): worker.join() workers.remove(worker) # Let store workers in a global var. set_global_workers(workers) # Execute plugins scheduler() function. exec_scheduler(queue_in, config, commands, logger) # Purge expired sessions if any. sessions.purge_expired(3600, logger, config) # Remove old unchecked commands. commands.purge_expired(60, logger)
def login(http_context, queue_in=None, config=None, sessions=None, commands=None): """ @api {get} /login User login @apiVersion 0.0.1 @apiName UserLogin @apiGroup User @apiParam {String} username Username. @apiParam {String} password Password. @apiSuccess {String} sessions Session ID. @apiExample {curl} Example usage: curl -k -X POST -H "Content-Type: application/json" -d '{"username": "******", "password": "******"}' \ https://localhost:2345/login @apiSuccessExample Success-Reponse: HTTP/1.0 200 OK Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:19:48 GMT Content-type: application/json {"session": "fa452548403ac53f2158a65f5eb6db9723d2b07238dd83f5b6d9ca52ce817b63"} @apiError (500 error) error Internal error. @apiError (404 error) error Invalid username or password. @apiError (406 error) error Username or password malformed or missing. @apiErrorExample 404 error example HTTP/1.0 404 Not Found Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:20:33 GMT Content-type: application/json {"error": "Invalid username/password."} @apiErrorExample 406 error example HTTP/1.0 406 Not Acceptable Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:21:01 GMT Content-type: application/json {"error": "Parameter 'password' is malformed."} """ post = http_context['post'] set_logger_name("api") logger = get_logger(config) # Add an unconditional sleeping time to reduce brute-force risks time.sleep(1) logger.info("Authenticating user: %s" % (post['username'])) try: validate_parameters(post, [('username', T_USERNAME, False), ('password', T_PASSWORD, False)]) auth_user(config.temboard['users'], post['username'], post['password']) except HTTPError as e: logger.traceback(get_tb()) logger.error(e.message) logger.info("Authentication failed.") raise e try: session = sessions.get_by_username(post['username']) if not session: sessionid = gen_sessionid(post['username']) session = Session(sessionid.encode('utf-8'), time.time(), post['username'].encode('utf-8')) sessions.add(session) else: sessionid = session.sessionid session.time = time.time() sessions.update(session) try: NotificationMgmt.push( config, Notification(username=post['username'], message="Login")) except NotificationError as e: logger.traceback(get_tb()) logger.error(e.message) except (SharedItem_exists, SharedItem_no_free_slot_left) as e: logger.traceback(get_tb()) logger.error(e.message) raise HTTPError(500, "Internal error.") return {'session': sessionid}
def notifications(http_context, queue_in=None, config=None, sessions=None, commands=None): """ @api {get} /notifications Get all notifications. @apiVersion 0.0.1 @apiName Notifications @apiGroup User @apiHeader {String} X-Session Session ID. @apiSuccess {Object[]} notifications List of notifications. @apiSuccess {String} notifications.date Notification datetime. @apiSuccess {String} notifications.username Username. @apiSuccess {String} notifications.message Message. @apiExample {curl} Example usage: curl -k -H "X-Session: fa452548403ac53f2158a65f5eb6db9723d2b07238dd83f5b6d9ca52ce817b63" https://localhost:2345/notifications @apiSuccessExample Success-Reponse: HTTP/1.0 200 OK Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:33:19 GMT Content-type: application/json [ {"date": "2016-04-11T16:12:38", "username": "******", "message": "Login"}, {"date": "2016-04-11T16:02:03", "username": "******", "message": "Login"}, {"date": "2016-04-11T15:51:15", "username": "******", "message": "HBA file version '2016-04-11T15:32:53' removed."}, {"date": "2016-04-11T15:51:10", "username": "******", "message": "HBA file version '2016-04-11T15:47:26' removed."}, {"date": "2016-04-11T15:51:04", "username": "******", "message": "HBA file version '2016-04-11T15:48:50' removed."}, {"date": "2016-04-11T15:50:57", "username": "******", "message": "PostgreSQL reload"}, {"date": "2016-04-11T15:50:57", "username": "******", "message": "HBA file updated"}, {"date": "2016-04-11T15:48:50", "username": "******", "message": "PostgreSQL reload"} ] @apiError (500 error) error Internal error. @apiError (401 error) error Invalid session ID. @apiError (406 error) error Session ID malformed. @apiErrorExample 401 error example HTTP/1.0 401 Unauthorized Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:36:33 GMT Content-type: application/json {"error": "Invalid session."} @apiErrorExample 406 error example HTTP/1.0 406 Not Acceptable Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:37:23 GMT Content-type: application/json {"error": "Parameter 'X-Session' is malformed."} """ headers = http_context['headers'] set_logger_name("api") logger = get_logger(config) logger.info("Get notifications.") try: username = check_sessionid(headers, sessions) except HTTPError as e: logger.traceback(get_tb()) logger.error(e.message) logger.info("Invalid session.") raise e try: notifications = NotificationMgmt.get_last_n(config, -1) logger.info("Done.") return list(notifications) except (NotificationError, Exception) as e: logger.traceback(get_tb()) logger.error(e.message) logger.info("Failed.") raise HTTPError(500, "Internal error.")
def instance_info(conninfo, hostname): """Gather PostgreSQL instance information.""" instance_info = { 'hostname': hostname, 'instance': conninfo['instance'], 'local_name': conninfo.get('local_name', conninfo['instance']), 'available': True, 'host': conninfo['host'], 'port': conninfo['port'], 'user': conninfo['user'], 'database': conninfo['database'], 'password': conninfo['password'] } # Try the connection conn = connector(conninfo['host'], conninfo['port'], conninfo['user'], conninfo['password'], conninfo['database']) try: conn.connect() # Get PostgreSQL informations using PgInfo pginfo = PgInfo(conn) pgv = pginfo.version() # Gather the info while where are connected instance_info['version_num'] = pgv['num'] instance_info['version'] = pgv['server'] instance_info['data_directory'] = pginfo.setting('data_directory') # hot standby is available from 9.0 instance_info['standby'] = pginfo.is_in_recovery() # Grab the list of tablespaces instance_info['tablespaces'] = pginfo.tablespaces( instance_info['data_directory']) # When the user has not given a dbnames list or '*' in the # configuration file, we must get the list of databases. Since # we have a working connection, let's do it now. dbs = pginfo.databases() instance_info['dbnames'] = [] for db in conninfo['dbnames']: if db == '*': instance_info['dbnames'] = dbs.values() break if db in dbs.keys(): instance_info['dbnames'].append(dbs[db]) conn.close() # Now that we have the data_directory, find the owner try: statinfo = os.stat(instance_info['data_directory']) instance_info['sysuser'] = pwd.getpwuid(statinfo.st_uid).pw_name except OSError as e: logging.warning("Unable to get the owner of PGDATA: %s", str(e)) instance_info['sysuser'] = None except error as e: logging.error(get_tb()) logging.error(str(e)) logging.warning("Unable to gather information for cluster \"%s\"", conninfo['instance']) instance_info['available'] = False return instance_info
def profile(http_context, queue_in=None, config=None, sessions=None, commands=None): """ @api {get} /profile Get current user name. @apiVersion 0.0.1 @apiName Profile @apiGroup User @apiHeader {String} X-Session Session ID. @apiSuccess {String} username Username. @apiExample {curl} Example usage: curl -k -H "X-Session: fa452548403ac53f2158a65f5eb6db9723d2b07238dd83f5b6d9ca52ce817b63" https://localhost:2345/profile @apiSuccessExample Success-Reponse: HTTP/1.0 200 OK Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:33:19 GMT Content-type: application/json { "username": "******" } @apiError (500 error) error Internal error. @apiError (401 error) error Invalid session ID. @apiError (406 error) error Session ID malformed. @apiErrorExample 401 error example HTTP/1.0 401 Unauthorized Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:36:33 GMT Content-type: application/json {"error": "Invalid session."} @apiErrorExample 406 error example HTTP/1.0 406 Not Acceptable Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:37:23 GMT Content-type: application/json {"error": "Parameter 'X-Session' is malformed."} """ headers = http_context['headers'] set_logger_name("api") logger = get_logger(config) logger.info("Get user profile.") try: check_sessionid(headers, sessions) except HTTPError as e: logger.traceback(get_tb()) logger.error(e.message) logger.info("Invalid session.") raise e try: session = sessions.get_by_sessionid( headers['X-Session'].encode('utf-8')) logger.info("Done.") return {'username': session.username} except SharedItem_not_found as e: logger.traceback(get_tb()) logger.error(e.message) logger.info("Failed.") raise HTTPError(401, "Invalid session.")
def get_discover(http_contexte, queue_in=None, config=None, sessions=None, commands=None): """ @api {get} /discover Get global informations about the env. @apiVersion 0.0.1 @apiName Discover @apiGroup User @apiSuccess {String} hostname Hostname. @apiSuccess {String} pg_data PostgreSQL data directory. @apiSuccess {Number} pg_port PostgreSQL listen port. @apiSuccess {String} pg_version PostgreSQL version. @apiSuccess {String[]} plugins List or available plugins. @apiSuccess {Number} memory_size Memory size (bytes). @apiSuccess {Number} cpu Number of CPU. @apiExample {curl} Example usage: curl -k https://localhost:2345/discover @apiSuccessExample Success-Reponse: HTTP/1.0 200 OK Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:33:19 GMT Content-type: application/json { "hostname": "neptune", "pg_data": "/var/lib/postgresql/9.4/main", "pg_port": 5432, "plugins": ["supervision", "dashboard", "settings", "administration", "activity"], "memory_size": 8241508352, "pg_version": "PostgreSQL 9.4.5 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2, 64-bit", "cpu": 4 } @apiError (500 error) error Internal error. """ set_logger_name("api") logger = get_logger(config) conn = connector(host=config.postgresql['host'], port=config.postgresql['port'], user=config.postgresql['user'], password=config.postgresql['password'], database=config.postgresql['dbname']) logger.info('Starting discovery.') try: conn.connect() sysinfo = SysInfo() pginfo = PgInfo(conn) ret = { 'hostname': sysinfo.hostname(config.temboard['hostname']), 'cpu': sysinfo.n_cpu(), 'memory_size': sysinfo.memory_size(), 'pg_port': pginfo.setting('port'), 'pg_version': pginfo.version()['full'], 'pg_data': pginfo.setting('data_directory'), 'plugins': [plugin_name for plugin_name in config.temboard['plugins']] } conn.close() logger.info('Discovery done.') return ret except (error, Exception, HTTPError) as e: logger.traceback(get_tb()) logger.error(str(e)) logger.info('Discovery failed.') try: conn.close() except Exception: pass if isinstance(e, HTTPError): raise e else: raise HTTPError(500, "Internal error.")
def logout(http_context, queue_in=None, config=None, sessions=None, commands=None): """ @api {get} /logout User logout @apiVersion 0.0.1 @apiName UserLogout @apiGroup User @apiHeader {String} X-Session Session ID. @apiSuccess {Bool} logout True if logout succeeds. @apiExample {curl} Example usage: curl -k -H "X-Session: fa452548403ac53f2158a65f5eb6db9723d2b07238dd83f5b6d9ca52ce817b63" https://localhost:2345/logout @apiSuccessExample Success-Reponse: HTTP/1.0 200 OK Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:33:19 GMT Content-type: application/json {"logout": true} @apiError (500 error) error Internal error. @apiError (401 error) error Invalid session ID. @apiError (406 error) error Session ID malformed. @apiErrorExample 401 error example HTTP/1.0 401 Unauthorized Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:36:33 GMT Content-type: application/json {"error": "Invalid session."} @apiErrorExample 406 error example HTTP/1.0 406 Not Acceptable Server: temboard-agent/0.0.1 Python/2.7.8 Date: Wed, 22 Apr 2015 12:37:23 GMT Content-type: application/json {"error": "Parameter 'X-Session' is malformed."} """ headers = http_context['headers'] set_logger_name("api") logger = get_logger(config) logger.info("Removing session: %s" % (headers['X-Session'])) try: username = check_sessionid(headers, sessions) except HTTPError as e: logger.traceback(get_tb()) logger.error(e.message) logger.info("Invalid session.") raise e try: NotificationMgmt.push( config, Notification(username=username, message="Logout")) except NotificationError as e: logger.traceback(get_tb()) logger.error(e.message) try: sessions.delete(headers['X-Session'].encode('utf-8')) except (SharedItem_exists, SharedItem_no_free_slot_left) as e: logger.traceback(get_tb()) logger.error(e.message) raise HTTPError(500, "Internal error.") return {'logout': True}
def supervision_collector_worker(commands, command, config): """ Run probes and push collected metrics in a queue. """ signal.signal(signal.SIGTERM, supervision_worker_sigterm_handler) start_time = time.time() * 1000 set_logger_name("supervision_collector_worker") logger = get_logger(config) # TODO: logging methods in supervision plugin must be aligned. logging.root = logger logger.debug("Starting with pid=%s" % (os.getpid())) logger.debug("commandid=%s" % (command.commandid)) command.state = COMMAND_START command.time = time.time() try: command.pid = os.getpid() commands.update(command) system_info = host_info(config.temboard['hostname']) except (ValueError, Exception) as e: logger.traceback(get_tb()) logger.error(str(e)) logger.debug("Failed.") sys.exit(1) # Load the probes to run try: probes = load_probes(config.plugins['supervision'], config.temboard['home']) config.plugins['supervision']['conninfo'] = [{ 'host': config.postgresql['host'], 'port': config.postgresql['port'], 'user': config.postgresql['user'], 'database': config.postgresql['dbname'], 'password': config.postgresql['password'], 'dbnames': config.plugins['supervision']['dbnames'], 'instance': config.postgresql['instance'] }] # Validate connection information from the config, and ensure # the instance is available instances = [] for conninfo in config.plugins['supervision']['conninfo']: logging.debug("Validate connection information on instance \"%s\"", conninfo['instance']) instances.append(instance_info(conninfo, system_info['hostname'])) # Gather the data from probes data = run_probes(probes, instances) # Prepare and send output output = { 'datetime': now(), 'hostinfo': system_info, 'instances': remove_passwords(instances), 'data': data, 'version': __VERSION__ } logger.debug("Collected data: %s" % (output)) q = Queue('%s/metrics.q' % (config.temboard['home']), max_size=1024 * 1024 * 10, overflow_mode='slide') q.push(Message(content=json.dumps(output))) except Exception as e: logger.traceback(get_tb()) logger.error(str(e)) logger.debug("Failed.") sys.exit(1) logger.debug("Duration: %s." % (str(time.time() * 1000 - start_time))) logger.debug("Done.")