diff --git a/.gitignore b/.gitignore index c38fa4e..33d33d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea *.iml +config/influx.json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..015465d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3 + +ADD keentic_influxdb_exporter.py /home +ADD requirements.txt /home +ADD value_normalizer.py /home +ADD influxdb_writter.py /home +ADD config/metrics.json /home/config/metrics.json + +RUN pip install -r /home/requirements.txt +CMD [ "python", "-u", "/home/keentic_influxdb_exporter.py" ] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..088ba74 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +``` + _ __ _ _ _____ _ _ _ + | |/ / | | (_) / ____| | | | | | + | ' / ___ ___ _ __ ___| |_ _ ___ | | ___ | | | ___ ___| |_ ___ _ __ + | < / _ \/ _ \ '_ \ / _ \ __| |/ __| | | / _ \| | |/ _ \/ __| __/ _ \| '__| + | . \ __/ __/ | | | __/ |_| | (__ | |___| (_) | | | __/ (__| || (_) | | + |_|\_\___|\___|_| |_|\___|\__|_|\___| \_____\___/|_|_|\___|\___|\__\___/|_| + +``` + +# Build from sources + +`docker build -t keenetic-grafana-monitoring .` + +# Preparation + +* Create InfluxDB configuration file `influx.json` + +```json +{ + "influxdb": { + "host": "", + "port": 80, + "username": "admin", + "password": "", + "db": "keenetic" + } +} +``` + +* Expose Keenetic API on your router + +For doing this add port forwarding (Network rules -> Forwarding): +``` +Input: Other destination +IP address: Your network ip (like 192.168.1.0) +Subnet mask: 255.255.255.0 +Output: This Keenetic +Open the port: 79 +Destination port: 79 +``` + +# Run with docker-compose.yml + +``` + +``` + diff --git a/config/influx.json.sample b/config/influx.json.sample new file mode 100644 index 0000000..2f1ae58 --- /dev/null +++ b/config/influx.json.sample @@ -0,0 +1,9 @@ +{ + "influxdb": { + "host": "", + "port": 80, + "username": "admin", + "password": "", + "db": "keenetic" + } +} \ No newline at end of file diff --git a/config.json b/config/metrics.json similarity index 86% rename from config.json rename to config/metrics.json index fdbcc19..84c003e 100644 --- a/config.json +++ b/config/metrics.json @@ -1,13 +1,6 @@ { "endpoint" : "http://192.168.1.1:79/rci", "interval_sec" : 30, - "influxdb" : { - "host" : "", - "port" : 80, - "username" : "", - "password" : "", - "db" : "" - }, "metrics" : [ { "command": "processes", @@ -51,13 +44,13 @@ "type": "$.type", "description": "$.description", "interface-name": "$.interface-name", - "state": "$.state", "address": "$.address" }, "values" : { "mtu": "$.mtu", "uptime": "$.uptime", - "tx-queue": "$.tx-queue" + "tx-queue": "$.tx-queue", + "state": "$.state" } }, { @@ -106,12 +99,12 @@ "root" : "$.*.partition.[*]", "tags" : { "label" : "$.label", - "uuid" : "$.uuid", - "state" : "$.state" + "uuid" : "$.uuid" }, "values" : { "total": "$.total", - "free": "$.free" + "free": "$.free", + "state" : "$.state" } }, { @@ -127,6 +120,20 @@ "rxspeed": "$.rxspeed", "txspeed": "$.txspeed" } + }, + { + "command": "interface stat", + "root" : "$", + "param" : { + "name" : "PPTP0" + }, + "tags" : {}, + "values" : { + "rxbytes": "$.rxbytes", + "txbytes": "$.txbytes", + "rxspeed": "$.rxspeed", + "txspeed": "$.txspeed" + } } ] } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..48e0e49 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +--- +version: '3.7' +services: + keenetic-grafana-monitoring: + build: . + container_name: keenetic-grafana-monitoring + environment: + - TZ=Europe/Kiev + volumes: + - ./config/influx.json:/home/config/influx.json + restart: always \ No newline at end of file diff --git a/influxdb_writter.py b/influxdb_writter.py index 3587de9..509ea1e 100644 --- a/influxdb_writter.py +++ b/influxdb_writter.py @@ -12,6 +12,7 @@ class InfuxWritter(object): def init_database(self): print("Connecting to InfluxDB: " + self._configuration['host']) db_name = self._configuration['db'] + # self._client.drop_database(db_name) if db_name not in self._client.get_list_database(): print("Creating InfluxDB database: " + db_name) diff --git a/keentic_influxdb_exporter.py b/keentic_influxdb_exporter.py index a6a382b..f156975 100644 --- a/keentic_influxdb_exporter.py +++ b/keentic_influxdb_exporter.py @@ -1,10 +1,10 @@ import json +import os import time import urllib import requests - -from jsonpath_ng.ext import parse +from jsonpath_rw import parse from influxdb_writter import InfuxWritter from value_normalizer import normalize_value @@ -90,13 +90,15 @@ if __name__ == '__main__': print( " _ __ _ _ _____ _ _ _ \n | |/ / | | (_) / ____| | | | | | \n | ' / ___ ___ _ __ ___| |_ _ ___ | | ___ | | | ___ ___| |_ ___ _ __ \n | < / _ \/ _ \ '_ \ / _ \ __| |/ __| | | / _ \| | |/ _ \/ __| __/ _ \| '__|\n | . \ __/ __/ | | | __/ |_| | (__ | |___| (_) | | | __/ (__| || (_) | | \n |_|\_\___|\___|_| |_|\___|\__|_|\___| \_____\___/|_|_|\___|\___|\__\___/|_| \n \n ") - configuration = json.load(open("config.json", "r")) - endpoint = configuration['endpoint'] - metrics = configuration['metrics'] + metrics_configuration = json.load(open(os.path.dirname(os.path.realpath(__file__)) + "/config/metrics.json", "r")) + influx_configuration = json.load(open(os.path.dirname(os.path.realpath(__file__)) + "/config/influx.json", "r")) + + endpoint = metrics_configuration['endpoint'] + metrics = metrics_configuration['metrics'] collectors = [] - infuxdb_writter = InfuxWritter(configuration) + infuxdb_writter = InfuxWritter(influx_configuration) print("Connecting to router: " + endpoint) @@ -104,8 +106,8 @@ if __name__ == '__main__': print("Configuring metric: " + metric_configuration['command']) collectors.append(KeeneticCollector(infuxdb_writter, endpoint, metric_configuration)) - print("Configuration done. Start collecting with interval: " + str(configuration['interval_sec']) + " sec") + print("Configuration done. Start collecting with interval: " + str(metrics_configuration['interval_sec']) + " sec") while True: for collector in collectors: collector.collect() - time.sleep(configuration['interval_sec']) + time.sleep(metrics_configuration['interval_sec']) diff --git a/requirements.txt b/requirements.txt index 826acfa..ec722d5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,2 @@ -certifi==2020.6.20 -chardet==3.0.4 -decorator==4.4.2 -idna==2.10 influxdb==5.3.0 -jsonpath==0.82 -jsonpath-ng==1.5.1 -msgpack==0.6.1 -parse==1.16.0 -ply==3.11 -prometheus-client==0.8.0 -python-dateutil==2.8.1 -pytz==2020.1 -requests==2.24.0 -six==1.15.0 -urllib3==1.25.10 +jsonpath-rw==1.4.0 diff --git a/value_normalizer.py b/value_normalizer.py index 5aadeb1..dee3e21 100644 --- a/value_normalizer.py +++ b/value_normalizer.py @@ -6,11 +6,24 @@ def isfloat(value: str): return (re.match(r'^-?\d+(?:\.\d+)?$', value) is not No def isinteger(value: str): return (re.match('^\d+$', value) is not None) def isvalidmetric(value) : return isinstance(value, int) or isinstance(value, float) or isinstance(value, bool) +type_mapping = { + "yes" : 1, + "no" : 0, + "up" : 1, + "down" : 0, + True: 1, + False: 0, + "MOUNTED" : 1, + "UNMOUNTED" : 0 +} + def normalize_value(value): if value is None: return None + value = type_mapping.get(value) if type_mapping.get(value) is not None else value + if isstring(value): value = parse_string(value) @@ -22,11 +35,14 @@ def normalize_value(value): def parse_string(value): + value = remove_data_unit(value) + if isinteger(value): value = int(value) elif isfloat(value): value = float(value) + return value