diff --git a/icinga2_exporter/main.py b/icinga2_exporter/main.py index 4b5526d..a87acf4 100644 --- a/icinga2_exporter/main.py +++ b/icinga2_exporter/main.py @@ -24,7 +24,7 @@ import icinga2_exporter.fileconfiguration as config import icinga2_exporter.proxy as proxy -from flask import Flask +from quart import Quart import icinga2_exporter.monitorconnection as monitorconnection @@ -62,9 +62,9 @@ def start(): monitorconnection.MonitorConfig(configuration) log.info('Starting web app on port: ' + str(port)) - app = Flask(__name__) + app = proxy.app #Quart(__name__) - app.register_blueprint(proxy.app, url_prefix='/') + #app.register_blueprint(proxy.app, url_prefix='/') app.run(host='0.0.0.0', port=port) @@ -86,8 +86,9 @@ def create_app(config_path=None): monitorconnection.MonitorConfig(configuration) log.info('Starting web app') - app = Flask(__name__) + app = proxy.app # Quart(__name__) - app.register_blueprint(proxy.app, url_prefix='/') + #app.register_blueprint(proxy.app, url_prefix='/') + #app.register_blueprint(proxy.app) return app diff --git a/icinga2_exporter/monitorconnection.py b/icinga2_exporter/monitorconnection.py index fe5ddb1..2b6ff9b 100644 --- a/icinga2_exporter/monitorconnection.py +++ b/icinga2_exporter/monitorconnection.py @@ -21,6 +21,7 @@ import requests import json +import aiohttp from requests.auth import HTTPBasicAuth import icinga2_exporter.log as log @@ -142,3 +143,34 @@ def post(self, url, body): log.error("{}".format(str(err))) return data_json + + + async def async_get_perfdata(self, hostname): + # Get performance data from Monitor and return in json format + body = {"joins": ["host.vars"], + "attrs": ["__name", "display_name", "check_command", "last_check_result", "vars", "host_name"], + "filter": 'service.host_name==\"{}\"'.format(hostname)} + + data_json = await self.async_post(self.url_query_service_perfdata, body) + + if not data_json: + log.warn('Received no perfdata from Icinga2') + + return data_json + + + async def async_post(self, url, body): + data_json = {} + try: + async with aiohttp.ClientSession() as session: + async with session.post(url, auth=aiohttp.BasicAuth(self.user, self.passwd), + verify_ssl=False, + headers={'Content-Type': 'application/json', + 'X-HTTP-Method-Override': 'GET'}, + data=json.dumps(body)) as response: + re = await response.text() + print(re) + print(response.status) + return json.loads(re) + finally: + pass diff --git a/icinga2_exporter/perfdata.py b/icinga2_exporter/perfdata.py index 38c01de..7a606ad 100644 --- a/icinga2_exporter/perfdata.py +++ b/icinga2_exporter/perfdata.py @@ -47,6 +47,7 @@ def get_perfdata(self) -> dict: Collect icinga2 data and parse it into prometheus metrics :return: """ + data_json = self.monitor.get_perfdata(self.query_hostname) if 'results' in data_json: for serivce_attrs in data_json['results']: @@ -60,6 +61,50 @@ def get_perfdata(self) -> dict: # For all host custom vars add as label labels.update(Perfdata.get_host_custom_vars(serivce_attrs)) + for perf_string in serivce_attrs['attrs']['last_check_result']['performance_data']: + perf = Perfdata.parse_perfdata(perf_string) + + # For each perfdata metrics + for perf_data_key, perf_data_value in perf.items(): + + if 'value' in perf_data_value: + prometheus_key = self.format_promethues_metrics_name(check_command, perf_data_key, + perf_data_value) + + # Add more labels based on perfname + if check_command in self.perfname_to_label: + labels.update( + Perfdata.add_labels_by_items( + self.perfname_to_label[check_command]['label_name'], + perf_data_key)) + + prometheus_key_with_labels = Perfdata.concat_metrics_name_and_labels(labels, + prometheus_key) + + self.perfdatadict.update({prometheus_key_with_labels: str(perf_data_value['value'])}) + + return self.perfdatadict + + async def async_get_perfdata(self) -> dict: + """ + Collect icinga2 data and parse it into prometheus metrics + :return: + """ + + data_json = await self.monitor.async_get_perfdata(self.query_hostname) + if 'results' in data_json: + for serivce_attrs in data_json['results']: + if 'attrs' in serivce_attrs and 'last_check_result' in serivce_attrs['attrs'] and 'performance_data' in \ + serivce_attrs['attrs']['last_check_result']: + check_command = serivce_attrs['attrs']['check_command'] + # Get default labels + labels = {'hostname': serivce_attrs['attrs']['host_name'], + 'service': serivce_attrs['attrs']['display_name']} + + # For all host custom vars add as label + labels.update(Perfdata.get_host_custom_vars(serivce_attrs)) + + for perf_string in serivce_attrs['attrs']['last_check_result']['performance_data']: perf = Perfdata.parse_perfdata(perf_string) diff --git a/icinga2_exporter/proxy.py b/icinga2_exporter/proxy.py index b1c8a46..e68fd8b 100644 --- a/icinga2_exporter/proxy.py +++ b/icinga2_exporter/proxy.py @@ -18,17 +18,19 @@ along with icinga2-exporter-exporter. If not, see . """ +import asyncio +from quart import request, Response, jsonify, Quart -from flask import request, Response, jsonify, Blueprint from prometheus_client import (CONTENT_TYPE_LATEST, Counter) from icinga2_exporter.perfdata import Perfdata import icinga2_exporter.monitorconnection as monitorconnection import icinga2_exporter.log as log -app = Blueprint("prom", __name__) +app = Quart( __name__) total_requests = Counter('requests', 'Total requests to monitor-exporter endpoint') +loop = asyncio.new_event_loop() @app.route('/', methods=['GET']) def hello_world(): @@ -54,13 +56,31 @@ def get_metrics(): return resp +@app.route("/ametrics", methods=['GET']) +async def get_ametrics(): + log.info(request.url) + target = request.args.get('target') + + log.info('Collect metrics', {'target': target}) + + monitor_data = Perfdata(monitorconnection.MonitorConfig(), target) + + # Fetch performance data from Monitor + await asyncio.get_event_loop().create_task(monitor_data.async_get_perfdata()) + + target_metrics = monitor_data.prometheus_format() + + resp = Response(target_metrics) + resp.headers['Content-Type'] = CONTENT_TYPE_LATEST + #after_request_func(resp) + return resp @app.route("/health", methods=['GET']) def get_health(): return chech_healthy() -@app.after_request +#@app.after_request def after_request_func(response): total_requests.inc() @@ -75,72 +95,3 @@ def chech_healthy() -> Response: resp = jsonify({'status': 'ok'}) resp.status_code = 200 return resp - -# def read_config(config_file: str) -> dict: -# """ -# Read configuration file -# :param config_file: -# :return: -# """ -# config = {} -# try: -# ymlfile = open(config_file, 'r') -# config = yaml.load(ymlfile, Loader=yaml.SafeLoader) -# except (FileNotFoundError, IOError): -# print("Config file {} not found".format(config_file)) -# exit(1) -# except (yaml.YAMLError, yaml.MarkedYAMLError) as err: -# print("Error will reading config file - {}".format(err)) -# exit(1) -# -# return config - - -# def start(): -# parser = argparse.ArgumentParser(description='monitor_exporter') -# -# parser.add_argument('-f', '--configfile', -# dest="configfile", help="configuration file") -# -# parser.add_argument('-p', '--port', -# dest="port", help="Server port") -# -# args = parser.parse_args() -# -# port = 5000 -# if args.port: -# port = args.port -# -# config_file = 'config.yml' -# if args.configfile: -# config_file = args.configfile -# -# configuration = config.read_config(config_file) -# -# formatter = log.configure_logger(configuration) -# ## -# -# monitorconnection.MonitorConfig(configuration) -# log.info('Starting web app on port: ' + str(port)) -# -# -# app.run(host='0.0.0.0', port=port) -# app.logger.handlers[0].setFormatter(formatter) - - -# def create_app(config_path = None): -# -# config_file = 'config.yml' -# if config_path: -# config_file = config_path -# -# config = read_config(config_file) -# -# formatter = log.configure_logger(config) -# -# monitorconnection.MonitorConfig(config) -# log.info('Starting web app') -# -# app.logger.handlers[0].setFormatter(formatter) -# -# return app