diff options
| author | jwansek <eddie.atten.ea29@gmail.com> | 2023-12-24 14:46:04 +0000 | 
|---|---|---|
| committer | jwansek <eddie.atten.ea29@gmail.com> | 2023-12-24 14:46:04 +0000 | 
| commit | a1e03826c0a904b93d60bd8d44f14cc6ba087314 (patch) | |
| tree | 4a01fb95a428b0d88cb79a4c5ad82b1334b3f714 /switch-snmp | |
| parent | f9da9ab448f59580180a46d2a9efc099ff09beb3 (diff) | |
| download | power.eda.gay-a1e03826c0a904b93d60bd8d44f14cc6ba087314.tar.gz power.eda.gay-a1e03826c0a904b93d60bd8d44f14cc6ba087314.zip | |
Added SNMP client for monitoring omada switches
Diffstat (limited to 'switch-snmp')
| -rw-r--r-- | switch-snmp/.dockerignore | 1 | ||||
| -rw-r--r-- | switch-snmp/Dockerfile | 16 | ||||
| -rw-r--r-- | switch-snmp/entrypoint.sh | 4 | ||||
| -rw-r--r-- | switch-snmp/port-names.conf | 4 | ||||
| -rw-r--r-- | switch-snmp/requirements.txt | 3 | ||||
| -rw-r--r-- | switch-snmp/snmp-omada.py | 100 | 
6 files changed, 128 insertions, 0 deletions
| diff --git a/switch-snmp/.dockerignore b/switch-snmp/.dockerignore new file mode 100644 index 0000000..ea6cd72 --- /dev/null +++ b/switch-snmp/.dockerignore @@ -0,0 +1 @@ +port-names.conf
\ No newline at end of file diff --git a/switch-snmp/Dockerfile b/switch-snmp/Dockerfile new file mode 100644 index 0000000..66857fa --- /dev/null +++ b/switch-snmp/Dockerfile @@ -0,0 +1,16 @@ +FROM ubuntu:20.04 +ENV TZ=Europe/London +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN apt-get update -y +RUN apt-get install -y python3-pip iputils-ping cron wget snmp snmp-mibs-downloader unzip +COPY . /app +WORKDIR /app +RUN wget "https://static.tp-link.com/upload/software/2022/202209/20220915/privateMibs(20220831).zip" +RUN mkdir -p /usr/share/snmp/mibs +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 +ENTRYPOINT ["bash"] +CMD ["entrypoint.sh"]
\ No newline at end of file diff --git a/switch-snmp/entrypoint.sh b/switch-snmp/entrypoint.sh new file mode 100644 index 0000000..610bf84 --- /dev/null +++ b/switch-snmp/entrypoint.sh @@ -0,0 +1,4 @@ +# https://stackoverflow.com/questions/27771781/how-can-i-access-docker-set-environment-variables-from-a-cron-job/35088810#35088810 +printenv | grep -v "no_proxy" >> /etc/environment + +cron -f
\ No newline at end of file diff --git a/switch-snmp/port-names.conf b/switch-snmp/port-names.conf new file mode 100644 index 0000000..e3f61da --- /dev/null +++ b/switch-snmp/port-names.conf @@ -0,0 +1,4 @@ +1 = Routerbox +5 = PiKVM +6 = EAP225 WiFi +8 = Git Raspberry Pi
\ No newline at end of file diff --git a/switch-snmp/requirements.txt b/switch-snmp/requirements.txt new file mode 100644 index 0000000..e7e63fd --- /dev/null +++ b/switch-snmp/requirements.txt @@ -0,0 +1,3 @@ +python-dotenv +influxdb-client +pandas
\ No newline at end of file diff --git a/switch-snmp/snmp-omada.py b/switch-snmp/snmp-omada.py new file mode 100644 index 0000000..aaa340e --- /dev/null +++ b/switch-snmp/snmp-omada.py @@ -0,0 +1,100 @@ +# wget https://static.tp-link.com/upload/software/2022/202209/20220915/privateMibs(20220831).zip +# cp -v *.mib /home/eden/.snmp/mibs +# sudo apt install snmp +# sudo apt-get install snmp-mibs-downloader + +import subprocess +from dataclasses import dataclass +import dotenv +import os +import pandas + +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 +    port: int +    reading: float + +    @classmethod +    def from_string(cls, str_): +        s = str_.split() +        if len(s) != 4: +            raise Exception("Couldn't parse") +        endpoint_and_port, _, type_, reading = s +        endpoint, port = endpoint_and_port.split(".") + +        if reading.isdigit(): +            reading = int(reading) +        if endpoint in DIVIDE_BY_10_ENDPOINTS: +            reading = reading / 10 + +        return cls(endpoint, int(port), reading) + +def get_alternate_name(port): +    try: +        return PORT_NAMES[port] +    except KeyError: +        return port + +def snmp_walk(host): +    proc = subprocess.Popen( +        ["snmpwalk", "-Os", "-c", "tplink", "-v", "2c", "-m", "TPLINK-POWER-OVER-ETHERNET-MIB", host, "tplinkPowerOverEthernetMIB"], +        stdout = subprocess.PIPE +    ) +    out = [] +    while True: +        line = proc.stdout.readline() +        if not line: +            break +        try: +            out.append(SNMPReading.from_string(line.rstrip().decode())) +        except Exception: +            pass + +    return out + +def readings_to_points(readings, switch_host): +    points = [] +    df = pandas.DataFrame(readings) +    df["port_name"] = df["port"].apply(get_alternate_name) +    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}) +     +    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() + +    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 | 
