diff --git a/README.md b/README.md index 1fca767..980336c 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,15 @@ password = LongAccountPassword [mqtt] broker = 127.0.0.1 port = 1883 +topic_prefix = aaisp username = aaisp-service password = AnotherLongPassword -topic_prefix = aaisp +``` + +Install the dependencies: + +``` +$ pip install -r requirements.txt ``` Run the service: @@ -56,49 +62,89 @@ $ aaisp-to-mqtt.py /etc/aaisp-mqtt.conf Single account: ``` -aaisp/$accounts gb12@a -aaisp/$lines 32891 -aaisp/$version 0.1 -aaisp/account/gb12@a/quota/remaining 3333889 -aaisp/account/gb12@a/quota/monthly 10000000 -aaisp/account/gb12@a/syncrate/down 7800000 -aaisp/account/gb12@a/syncrate/up 1900000 -aaisp/line/32891/quota/remaining 3333889 -aaisp/line/32891/quota/monthly 10000000 -aaisp/line/32891/syncrate/down 7800000 -aaisp/line/32891/syncrate/up 1900000 +aaisp/$lines 32891 +aaisp/$logins gb12@a.1 +aaisp/$version 0.1 +aaisp/line/32891/postcode SA65 9RR +aaisp/line/32891/quota/monthly 100000000000 +aaisp/line/32891/quota/monthly/human 100 GB +aaisp/line/32891/quota/remaining 84667320096 +aaisp/line/32891/quota/remaining/human 84.67 GB +aaisp/line/32891/syncrate/down 5181000 +aaisp/line/32891/syncrate/down/human 5.18 MB +aaisp/line/32891/syncrate/up 1205000 +aaisp/line/32891/syncrate/up/human 1.21 MB +aaisp/login/gb12@a.1/postcode SA65 9RR +aaisp/login/gb12@a.1/quota/monthly 100000000000 +aaisp/login/gb12@a.1/quota/monthly/human 100 GB +aaisp/login/gb12@a.1/quota/remaining 84667320096 +aaisp/login/gb12@a.1/quota/remaining/human 84.67 GB +aaisp/login/gb12@a.1/syncrate/down 5181000 +aaisp/login/gb12@a.1/syncrate/down/human 5.18 MB +aaisp/login/gb12@a.1/syncrate/up 1205000 +aaisp/login/gb12@a.1/syncrate/up/human 1.21 MB ``` For multiple accounts: ``` -aaisp/$accounts el6@a.1,el6@a.2,gb12@a -aaisp/$lines 37835,37964,32891 -aaisp/$version 0.1 -aaisp/account/gb12@a/quota/remaining 3333889 -aaisp/account/gb12@a/quota/monthly 10000000 -aaisp/account/gb12@a/syncrate/down 7800000 -aaisp/account/gb12@a/syncrate/up 1900000 -aaisp/account/el6@a.1/quota/remaining 3333889 -aaisp/account/el6@a.1/quota/monthly 10000000 -aaisp/account/el6@a.1/syncrate/down 7400000 -aaisp/account/el6@a.1/syncrate/up 1700000 -aaisp/account/el6@a.2/quota/remaining 3333889 -aaisp/account/el6@a.2/quota/monthly 10000000 -aaisp/account/el6@a.2/syncrate/down 7300000 -aaisp/account/el6@a.2/syncrate/up 1600000 -aaisp/line/32891/quota/remaining 3333889 -aaisp/line/32891/quota/monthly 10000000 -aaisp/line/32891/syncrate/down 7800000 -aaisp/line/32891/syncrate/up 1900000 -aaisp/line/37835/quota/remaining 3333889 -aaisp/line/37835/quota/monthly 10000000 -aaisp/line/37835/syncrate/down 7300000 -aaisp/line/37835/syncrate/up 1600000 -aaisp/line/37964/quota/remaining 3333889 -aaisp/line/37964/quota/monthly 10000000 -aaisp/line/37964/syncrate/down 7400000 -aaisp/line/37964/syncrate/up 1700000 +aaisp/$lines 32891,37835,37964 +aaisp/$logins gb12@a.1,el6@a.1,el6@a.2 +aaisp/$version 0.1 +aaisp/line/32891/postcode SA65 9RR +aaisp/line/32891/quota/monthly 100000000000 +aaisp/line/32891/quota/monthly/human 100 GB +aaisp/line/32891/quota/remaining 84667320096 +aaisp/line/32891/quota/remaining/human 84.67 GB +aaisp/line/32891/syncrate/down 5181000 +aaisp/line/32891/syncrate/down/human 5.18 MB +aaisp/line/32891/syncrate/up 1205000 +aaisp/line/32891/syncrate/up/human 1.21 MB +aaisp/line/37835/postcode SA62 5EY +aaisp/line/37835/quota/monthly 1000000000000 +aaisp/line/37835/quota/monthly/human 1 TB +aaisp/line/37835/quota/remaining 752408843915 +aaisp/line/37835/quota/remaining/human 752.41 GB +aaisp/line/37835/syncrate/down 68083000 +aaisp/line/37835/syncrate/down/human 68.08 MB +aaisp/line/37835/syncrate/up 19999000 +aaisp/line/37835/syncrate/up/human 20 MB +aaisp/line/37964/postcode SA62 5EY +aaisp/line/37964/quota/monthly 1000000000000 +aaisp/line/37964/quota/monthly/human 1 TB +aaisp/line/37964/quota/remaining 819343151266 +aaisp/line/37964/quota/remaining/human 819.34 GB +aaisp/line/37964/syncrate/down 74425000 +aaisp/line/37964/syncrate/down/human 74.42 MB +aaisp/line/37964/syncrate/up 19978000 +aaisp/line/37964/syncrate/up/human 19.98 MB +aaisp/login/el6@a.1/postcode SA62 5EY +aaisp/login/el6@a.1/quota/monthly 1000000000000 +aaisp/login/el6@a.1/quota/monthly/human 1 TB +aaisp/login/el6@a.1/quota/remaining 752408843915 +aaisp/login/el6@a.1/quota/remaining/human 752.41 GB +aaisp/login/el6@a.1/syncrate/down 68083000 +aaisp/login/el6@a.1/syncrate/down/human 68.08 MB +aaisp/login/el6@a.1/syncrate/up 19999000 +aaisp/login/el6@a.1/syncrate/up/human 20 MB +aaisp/login/el6@a.2/postcode SA62 5EY +aaisp/login/el6@a.2/quota/monthly 1000000000000 +aaisp/login/el6@a.2/quota/monthly/human 1 TB +aaisp/login/el6@a.2/quota/remaining 819343151266 +aaisp/login/el6@a.2/quota/remaining/human 819.34 GB +aaisp/login/el6@a.2/syncrate/down 74425000 +aaisp/login/el6@a.2/syncrate/down/human 74.42 MB +aaisp/login/el6@a.2/syncrate/up 19978000 +aaisp/login/el6@a.2/syncrate/up/human 19.98 MB +aaisp/login/gb12@a.1/postcode SA65 9RR +aaisp/login/gb12@a.1/quota/monthly 100000000000 +aaisp/login/gb12@a.1/quota/monthly/human 100 GB +aaisp/login/gb12@a.1/quota/remaining 84667320096 +aaisp/login/gb12@a.1/quota/remaining/human 84.67 GB +aaisp/login/gb12@a.1/syncrate/down 5181000 +aaisp/login/gb12@a.1/syncrate/down/human 5.18 MB +aaisp/login/gb12@a.1/syncrate/up 1205000 +aaisp/login/gb12@a.1/syncrate/up/human 1.21 MB ``` ## Setup ## diff --git a/aaisp-to-mqtt.py b/aaisp-to-mqtt.py index 4c6fb84..1bb1ce0 100755 --- a/aaisp-to-mqtt.py +++ b/aaisp-to-mqtt.py @@ -1,33 +1,113 @@ #!/usr/bin/env python -import os import sys import logging import json import urllib import configparser +import paho.mqtt.client as mqtt +import humanfriendly LOG = logging.getLogger(__name__) VERSION = 0.1 + def main(): logging.basicConfig(level=logging.INFO, format='%(levelname)8s [%(asctime)s] %(message)s') if len(sys.argv) != 2: LOG.fatal("Config file not supplied") - + sys.exit(1) cfgfile = sys.argv[1] - + # load the config config = configparser.ConfigParser() config.read(cfgfile) + # check it has the correct sections for section in ["aaisp", "mqtt"]: if section not in config.sections(): LOG.fatal("%s section not found in config file %s", section, cfgfile) + aaisp_username = config.get("aaisp", "username") + aaisp_password = config.get("aaisp", "password") + + # attempt to get details from aaisp + LOG.info("Connecting to AAISP CHAOSv2 endpoint") + post_params = "control_login=%s&control_password=%s" % (aaisp_username, aaisp_password) + url = "https://chaos2.aa.net.uk/broadband/info" + response = urllib.urlopen(url, data=post_params) + data = json.loads(response.read()) + if "info" not in data: + LOG.fatal("info section not found in AAISP CHAOSv2 response") + sys.exit(1) + circuits = data["info"] + LOG.info("Got %s circuits", len(circuits)) + if len(circuits) == 0: + LOG.fatal("No circuits returned from AAISP CHAOSv2") + + # work out unique line IDs and logins + logins = [] + lines = [] + for circuit in circuits: + if circuit["login"] not in logins: + logins.append(circuit["login"]) + if circuit["ID"] not in lines: + lines.append(circuit["ID"]) + LOG.info("* Lines: %s", ', '.join(lines)) + LOG.info("* Logins: %s", ', '.join(logins)) + + + # get MQTT config + mqtt_broker = config.get("mqtt", "broker") + mqtt_port = int(config.get("mqtt", "port")) + mqtt_username = config.get("mqtt", "username") + mqtt_password = config.get("mqtt", "password") + mqtt_topic_prefix = config.get("mqtt", "topic_prefix") + # connect to the broker + LOG.info("Connecting to MQTT broker %s:%s", mqtt_broker, mqtt_port) + client = mqtt.Client() + # do auth? + if mqtt_username is not None and mqtt_password is not None: + client.username_pw_set(mqtt_username, mqtt_password) + client.connect(mqtt_broker, mqtt_port, 60) + LOG.info("Connected OK to MQTT") + + # version and indexes + publish(client=client, topic="%s/$version" % (mqtt_topic_prefix), payload=VERSION) + publish(client=client, topic="%s/$lines" % (mqtt_topic_prefix), payload=','.join(lines)) + publish(client=client, topic="%s/$logins" % (mqtt_topic_prefix), payload=','.join(logins)) + LOG.info("Published version and index messages") + + # publish per circuit + for circuit in circuits: + publish_per_circuit(client=client, circuit=circuit, mqtt_topic_prefix=mqtt_topic_prefix) + LOG.info("Published details for %s circuits", len(circuits)) + # disconnect + LOG.info("Disconnecting from MQTT") + client.disconnect() sys.exit(0) +def publish_per_circuit(client, circuit, mqtt_topic_prefix): + line_prefix = "%s/line/%s" % (mqtt_topic_prefix, circuit["ID"]) + login_prefix = "%s/login/%s" % (mqtt_topic_prefix, circuit["login"]) + for prefix in [line_prefix, login_prefix]: + for metric in [ + ("quota/remaining", int(circuit["quota_remaining"])), + ("quota/monthly", int(circuit["quota_monthly"])), + ("syncrate/up", int(circuit["rx_rate"])), + ("syncrate/down", int(circuit["tx_rate"])), + ("postcode", str(circuit["postcode"].strip())) + ]: + topic = "%s/%s" % (prefix, metric[0]) + publish(client=client, topic=topic, payload=metric[1]) + if type(metric[1]) == int: + publish(client=client, topic="%s/human" % (topic), payload=humanfriendly.format_size(metric[1])) + return + +def publish(client, topic, payload): + client.publish(topic=topic, payload=payload) + if __name__ == "__main__": main() diff --git a/requirements.txt b/requirements.txt index 46c81b6..e16a4d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -paho-mqtt -configparser +paho-mqtt>=1.2 +configparser>=3.5.0 +humanfriendly>=2.1