diff options
| author | jwansek <eddie.atten.ea29@gmail.com> | 2025-05-18 22:41:07 +0100 | 
|---|---|---|
| committer | jwansek <eddie.atten.ea29@gmail.com> | 2025-05-18 22:41:07 +0100 | 
| commit | eaff8b05c20b19c59e32e2646ca17d7bc75250aa (patch) | |
| tree | 8ee8f65107674f641f6225d16a35ef4843d98ae0 /switch-snmp | |
| parent | 069e1484918fefdf409da09e88550251e919db4f (diff) | |
| download | power.eda.gay-eaff8b05c20b19c59e32e2646ca17d7bc75250aa.tar.gz power.eda.gay-eaff8b05c20b19c59e32e2646ca17d7bc75250aa.zip | |
Added fetching mikrotik POE usage to InfluxDB too
Diffstat (limited to 'switch-snmp')
| -rw-r--r-- | switch-snmp/Dockerfile | 4 | ||||
| -rw-r--r-- | switch-snmp/mikrotik-switches.conf | 9 | ||||
| -rw-r--r-- | switch-snmp/mikrotik.py | 131 | ||||
| -rw-r--r-- | switch-snmp/omada-switches.conf | 18 | ||||
| -rw-r--r-- | switch-snmp/port-names.conf | 10 | ||||
| -rw-r--r-- | switch-snmp/requirements.txt | 3 | ||||
| -rw-r--r-- | switch-snmp/snmpOmada.py (renamed from switch-snmp/snmp-omada.py) | 58 | ||||
| -rw-r--r-- | switch-snmp/switches.py | 7 | 
8 files changed, 196 insertions, 44 deletions
| diff --git a/switch-snmp/Dockerfile b/switch-snmp/Dockerfile index a056214..d09e341 100644 --- a/switch-snmp/Dockerfile +++ b/switch-snmp/Dockerfile @@ -11,7 +11,7 @@ RUN unzip -j "privateMibs(20220831).zip" -d  /usr/share/snmp/mibs  RUN pip3 install -r requirements.txt  RUN rm "privateMibs(20220831).zip" -RUN echo "*/1 * * * * root python3 /app/snmp-omada.py > /proc/1/fd/1 2>/proc/1/fd/2" > /etc/crontab -RUN echo "*/1 * * * * root sh -c 'sleep 30 && python3 /app/snmp-omada.py' > /proc/1/fd/1 2>/proc/1/fd/2" >> /etc/crontab +RUN echo "*/1 * * * * root python3 /app/switches.py > /proc/1/fd/1 2>/proc/1/fd/2" > /etc/crontab +RUN echo "*/1 * * * * root sh -c 'sleep 30 && python3 /app/switches.py' > /proc/1/fd/1 2>/proc/1/fd/2" >> /etc/crontab  ENTRYPOINT ["bash"]  CMD ["entrypoint.sh"] diff --git a/switch-snmp/mikrotik-switches.conf b/switch-snmp/mikrotik-switches.conf new file mode 100644 index 0000000..b777b53 --- /dev/null +++ b/switch-snmp/mikrotik-switches.conf @@ -0,0 +1,9 @@ +[192.168.69.22] +ether1 = Modem +ether2 = 2 +ether3 = EAP110 Wifi +ether4 = Amazon Firestick +ether5 = 5 +ether6 = 6 +ether7 = 7 +ether8 = 8
\ No newline at end of file diff --git a/switch-snmp/mikrotik.py b/switch-snmp/mikrotik.py new file mode 100644 index 0000000..afd3cfa --- /dev/null +++ b/switch-snmp/mikrotik.py @@ -0,0 +1,131 @@ +from dataclasses import dataclass +import configparser +import threading +import fabric +import time +import os +import re + +from influxdb_client import InfluxDBClient, Point, WritePrecision +from influxdb_client.client.write_api import SYNCHRONOUS + +INFLUXDB_MAPPINGS = { +    "poe-out-voltage": "tpPoeVoltage", +    "poe-out-current": "tpPoeCurrent", +    "poe-out-power": "tpPoePower", +} + +@dataclass +class MikroTikSSHDevice: + +    host: str +    ssh_key_path: str +    user: str = "admin" + +    def __post_init__(self): +        self.is_being_polled = threading.Event() + +    def _get_conn(self): +        return fabric.Connection( +            user = self.user, +            host = self.host, +            connect_kwargs = {"key_filename": self.ssh_key_path} +        ) +     +    def _poll_four_interfaces(self, four_interfaces): +        # only poll four interfaces at the same time since we can only get a certain amount of information through SSH at the same time +        self.is_being_polled.set() +        result = self._get_conn().run("/interface/ethernet/poe/monitor %s once" % ",".join(four_interfaces), hide = True) +        self.is_being_polled.clear() +        return self._parse_result(result) + +    def _parse_result(self, result): +        r = result.stdout +        # print(r) +        s = [re.split(r" +", row.rstrip())[1:] for row in r.split("\r\n")][:-2] +        out = {i: {} for i in s[0][1:]} +        off_interfaces = set() +        for row in s[1:]: +            column_decrimator = 0 +            output_name = row[0][:-1] +            # print(output_name) + +            for i, interface_name in enumerate(out.keys(), 0): +                # print("off_interfaces:", off_interfaces) +                # print(i, interface_name, row[1:][i]) +                if interface_name in off_interfaces: +                    # print("Skipping '%s' for %s..." % (output_name, interface_name)) +                    column_decrimator += 1 +                else: +                    out[interface_name][output_name] = row[1:][i - column_decrimator] + +                if output_name == "poe-out-status": +                    if row[1:][i] != "powered-on": +                        # print("Adding %s to off interfaces" % interface_name) +                        off_interfaces.add(interface_name) +        return out +     +    def get_poe_interfaces(self, interface_names): +        out = {} +        for four_interfaces in [interface_names[i:i + 4] for i in range(0, len(interface_names), 4)]: +            out |= self._poll_four_interfaces(four_interfaces) +        return out + +def remove_measurement_type(type_str): +    type_str = "".join([s for s in type_str if s.isdigit() or s == "."]) +    if "." in type_str: +        return float(type_str) +    else: +        return int(type_str) + +def fields_to_points(fields, switch_host, config): +    return [{ +        "measurement": "switch_status",  +        "tags": {"port": port, "port_name": config.get(switch_host, port), "switch_host": switch_host, "type": "MikroTik"},  +        "fields": {INFLUXDB_MAPPINGS[k]: remove_measurement_type(v) for k, v in values.items() if k in INFLUXDB_MAPPINGS} +    } for port, values in fields.items()] + +def get_points(): +    mikrotik_switches = configparser.ConfigParser() +    mikrotik_switches.read(os.path.join(os.path.dirname(__file__), "mikrotik-switches.conf")) +    points = [] +    for mikrotik_switch in mikrotik_switches.sections(): +        mikrotik_device = MikroTikSSHDevice(mikrotik_switch, os.path.join(os.path.dirname(__file__), "mikrotik.pem")) +        points += fields_to_points(mikrotik_device.get_poe_interfaces(list(mikrotik_switches[mikrotik_switch].keys())), mikrotik_switch, mikrotik_switches) +    return points + +def append(points): +    env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "config.env") +    if os.path.exists(env_path): +        import dotenv +        dotenv.load_dotenv(dotenv_path = env_path) +        INFLUXDB_HOST = "dns.athome" +    else: +        INFLUXDB_HOST = "influxdb" + +    influxc = InfluxDBClient( +        url = "http://%s:8086" % INFLUXDB_HOST, +        token = os.environ["DOCKER_INFLUXDB_INIT_ADMIN_TOKEN"], +        org = os.environ["DOCKER_INFLUXDB_INIT_ORG"]  +    ) +    influxc.ping() + +    write_api = influxc.write_api(write_options = SYNCHRONOUS) +    write_api.write( +        os.environ["DOCKER_INFLUXDB_INIT_BUCKET"], +        os.environ["DOCKER_INFLUXDB_INIT_ORG"], +        points, +        write_precision = WritePrecision.S +    ) + +if __name__ == "__main__": +    if not os.path.exists(os.path.join(os.path.dirname(__file__), "mikrotik-switches.conf")): +        raise FileNotFoundError("Couldn't find mikrotik config file") +    if not os.path.exists(os.path.join(os.path.dirname(__file__), "mikrotik.pem")): +        raise FileNotFoundError("Couldn't find mikrotik public key file") +     +    import json +    points = get_points() +    print(json.dumps(points, indent = 4)) +    append(points) +     diff --git a/switch-snmp/omada-switches.conf b/switch-snmp/omada-switches.conf new file mode 100644 index 0000000..3f3f3f0 --- /dev/null +++ b/switch-snmp/omada-switches.conf @@ -0,0 +1,18 @@ +[192.168.69.26] +1 = EAP225 Wifi +2 = Tasmota Zigbee +4 = Mikrotik CRS310-8G+2S+ +6 = Routerbox +16 = Intel Compute Stick +24 = Frigate Pi +23 = Modem & ES205G +8 = PiKVM +10 = TL-RP108GE & EAP110 +22 = Cluster Pi 9 +19 = Cluster Pi 5 +20 = Cluster Pi 7 +21 = Cluster Pi 8 +18 = Cluster Pi 6 +17 = Cluster Pi 4 +9 = Jetson Orin Nano + diff --git a/switch-snmp/port-names.conf b/switch-snmp/port-names.conf deleted file mode 100644 index 8f8d089..0000000 --- a/switch-snmp/port-names.conf +++ /dev/null @@ -1,10 +0,0 @@ -1 = EAP225 Wifi -2 = Tasmota Zigbee -4 = 2.5Gb Switch -6 = Routerbox -13 = Intel Compute Stick -24 = Frigate Pi -23 = Modem & ES205G -8 = PiKVM -10 = TL-RP108GE & EAP110 - diff --git a/switch-snmp/requirements.txt b/switch-snmp/requirements.txt index e7e63fd..aaf744d 100644 --- a/switch-snmp/requirements.txt +++ b/switch-snmp/requirements.txt @@ -1,3 +1,4 @@  python-dotenv  influxdb-client -pandas
\ No newline at end of file +pandas +fabric
\ No newline at end of file diff --git a/switch-snmp/snmp-omada.py b/switch-snmp/snmpOmada.py index aaa340e..f1546a7 100644 --- a/switch-snmp/snmp-omada.py +++ b/switch-snmp/snmpOmada.py @@ -8,15 +8,13 @@ from dataclasses import dataclass  import dotenv  import os  import pandas +import configparser  from influxdb_client import InfluxDBClient, Point, WritePrecision  from influxdb_client.client.write_api import SYNCHRONOUS  DIVIDE_BY_10_ENDPOINTS = ["tpPoePower", "tpPoeVoltage"] -PORT_NAMES = dotenv.dotenv_values(os.path.join(os.path.dirname(__file__), "port-names.conf")) -PORT_NAMES = {int(k): v for k, v in PORT_NAMES.items()} -  @dataclass  class SNMPReading:      endpoint: str @@ -38,9 +36,13 @@ class SNMPReading:          return cls(endpoint, int(port), reading) -def get_alternate_name(port): +def get_alternate_name(port, host): +    port_names = configparser.ConfigParser() +    port_names.read(os.path.join(os.path.dirname(__file__), "omada-switches.conf")) +    port_names = {int(k): v for k, v in port_names[host].items()} +      try: -        return PORT_NAMES[port] +        return port_names[port]      except KeyError:          return port @@ -64,37 +66,31 @@ def snmp_walk(host):  def readings_to_points(readings, switch_host):      points = []      df = pandas.DataFrame(readings) -    df["port_name"] = df["port"].apply(get_alternate_name) +    df["port_name"] = df["port"].apply(get_alternate_name, args = (switch_host, ))      for p, group_df in df.groupby(["port", "port_name"]):          port, port_name = p          fields = dict(zip(group_df['endpoint'], group_df['reading'])) -        points.append({"measurement": "switch_status", "tags": {"port": port, "port_name": port_name, "switch_host": switch_host}, "fields": fields}) +        points.append({ +            "measurement": "switch_status",  +            "tags": {"port": port, "port_name": port_name, "switch_host": switch_host, "type": "Omada"},  +            "fields": fields +        })      return points -if __name__ == "__main__": -    env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "config.env") -    if os.path.exists(env_path): -        import dotenv -        dotenv.load_dotenv(dotenv_path = env_path) -        INFLUXDB_HOST = "dns.athome" -    else: -        INFLUXDB_HOST = "influxdb" - -    influxc = InfluxDBClient( -        url = "http://%s:8086" % INFLUXDB_HOST, -        token = os.environ["DOCKER_INFLUXDB_INIT_ADMIN_TOKEN"], -        org = os.environ["DOCKER_INFLUXDB_INIT_ORG"]  -    ) -    influxc.ping() +def get_points(): +    if not os.path.exists(os.path.join(os.path.dirname(__file__), "omada-switches.conf")): +        raise FileNotFoundError("Couldn't find config file") +    switches = configparser.ConfigParser() +    switches.read(os.path.join(os.path.dirname(__file__), "omada-switches.conf")) +    points = [] +    for switch_host in switches.sections(): +        points += readings_to_points(snmp_walk(switch_host), switch_host) +    return points -    for switch_host in os.environ["OMADA_SWITCHES"].split(","): -        points = readings_to_points(snmp_walk(switch_host), switch_host) -        write_api = influxc.write_api(write_options = SYNCHRONOUS) -        write_api.write( -            os.environ["DOCKER_INFLUXDB_INIT_BUCKET"], -            os.environ["DOCKER_INFLUXDB_INIT_ORG"], -            points, -            write_precision = WritePrecision.S -        )
\ No newline at end of file +if __name__ == "__main__": +    import mikrotik +    points = get_points() +    print(points) +    mikrotik.append(points)
\ No newline at end of file diff --git a/switch-snmp/switches.py b/switch-snmp/switches.py new file mode 100644 index 0000000..52be19a --- /dev/null +++ b/switch-snmp/switches.py @@ -0,0 +1,7 @@ +import snmpOmada +import mikrotik + +if __name__ == "__main__": +    points = snmpOmada.get_points() + mikrotik.get_points() +    print(points) +    mikrotik.append(points)
\ No newline at end of file | 
