From b01d70aa3cfe55fc1ac6fad9f95b8737bb0b55ee Mon Sep 17 00:00:00 2001 From: Christian Manivong Date: Fri, 16 Oct 2026 12:02:32 +0200 Subject: [PATCH] feat: declare the SNMP health OIDs instead of collecting them (0.2.0) The driver no longer collects over SNMP. SNMP_HEALTH_PROFILE states where the switch publishes uptime (TimeTicks), CPU and memory (tables with one row per stack unit, first row as before); the application polls and computes (napalm-device-types docs/STRUCTURE.md). Interface counters now come from the 64-bit ifXTable where the switch has it, as the application reads them for every device. --- napalm_tplink_jetstream/tplink_jetstream.py | 64 ++++----------------- pyproject.toml | 2 +- tests/unit/test_snmp_profile.py | 25 ++++++++ 3 files changed, 36 insertions(+), 55 deletions(-) create mode 100644 tests/unit/test_snmp_profile.py diff --git a/napalm_tplink_jetstream/tplink_jetstream.py b/napalm_tplink_jetstream/tplink_jetstream.py index f5cb12b..8825dd8 100644 --- a/napalm_tplink_jetstream/tplink_jetstream.py +++ b/napalm_tplink_jetstream/tplink_jetstream.py @@ -21,7 +21,7 @@ from __future__ import annotations import re import socket -from typing import Any +from typing import Any, ClassVar import netaddr from netmiko import ConnectHandler @@ -64,6 +64,15 @@ class TPLinkJetstreamDriver(ConfigLifecycleMixin, SwitchDriver): # Netmiko device type for TP-Link Jetstream NETMIKO_DEVICE_TYPE = "tplink_jetstream" + #: Where the switch publishes its health values over SNMP + #: (napalm-device-types ``SnmpHealthProfile``); the application polls. + #: CPU and memory are tables with one row per stack unit. + SNMP_HEALTH_PROFILE: ClassVar[dict[str, Any]] = { + "uptime": {"oids": ["1.3.6.1.4.1.11863.6.1.1.9.0"]}, + "cpu": {"table": "1.3.6.1.4.1.11863.6.4.1.1.1.1.3", "fold": "first"}, + "memory": {"percent": {"table": "1.3.6.1.4.1.11863.6.4.1.2.1.1.2", "fold": "first"}}, + } + # PoE priority mapping between the CLI's "low/high/critical" and the # generic priority strings used across drivers. _POE_PRIORITY_MAP = {"low": "PPP_LOW", "high": "PPP_HIGH", "critical": "PPP_CRITICAL"} @@ -2004,56 +2013,3 @@ class TPLinkJetstreamDriver(ConfigLifecycleMixin, SwitchDriver): lines.append(f"[warn] Verification failed: {out[:100]}") return {"success": success, "output": "\n".join(lines)} - - # ------------------------------------------------------------------ - # Health metrics (SNMP) - # ------------------------------------------------------------------ - - @classmethod - async def get_health_metrics(cls, snmp_get, snmp_walk) -> dict: - import asyncio - from napalm_device_types._ucd_metrics import build_if_metrics, ticks_to_seconds - - _TPLINK_SYS_UPTIME = "1.3.6.1.4.1.11863.6.1.1.9.0" - _TPLINK_CPU_1MIN = "1.3.6.1.4.1.11863.6.4.1.1.1.1.3" - _TPLINK_MEM_UTIL = "1.3.6.1.4.1.11863.6.4.1.2.1.1.2" - _OID_IF_DESCR = "1.3.6.1.2.1.2.2.1.2" - _OID_IF_SPEED = "1.3.6.1.2.1.2.2.1.5" - _OID_IF_IN_OCT = "1.3.6.1.2.1.2.2.1.10" - _OID_IF_OUT_OCT = "1.3.6.1.2.1.2.2.1.16" - _OID_IF_IN_ERR = "1.3.6.1.2.1.2.2.1.14" - _OID_IF_OUT_ERR = "1.3.6.1.2.1.2.2.1.20" - - (tp_uptime, cpu_rows, mem_rows, - descr, speed, in_oct, out_oct, in_err, out_err) = await asyncio.gather( - snmp_get(_TPLINK_SYS_UPTIME), - snmp_walk(_TPLINK_CPU_1MIN), - snmp_walk(_TPLINK_MEM_UTIL), - snmp_walk(_OID_IF_DESCR), - snmp_walk(_OID_IF_SPEED), - snmp_walk(_OID_IF_IN_OCT), - snmp_walk(_OID_IF_OUT_OCT), - snmp_walk(_OID_IF_IN_ERR), - snmp_walk(_OID_IF_OUT_ERR), - ) - - metrics: dict = {} - - secs = ticks_to_seconds(tp_uptime) - if secs is not None: - metrics["uptime_seconds"] = secs - - if cpu_rows: - try: - metrics["cpu_percent"] = float(next(iter(cpu_rows.values()))) - except (ValueError, StopIteration): - pass - - if mem_rows: - try: - metrics["memory_percent"] = float(next(iter(mem_rows.values()))) - except (ValueError, StopIteration): - pass - - build_if_metrics(metrics, descr, speed, in_oct, out_oct, in_err, out_err) - return metrics diff --git a/pyproject.toml b/pyproject.toml index 4307dac..fe9f88b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "napalm-tplink-jetstream" -version = "0.1.2" +version = "0.2.0" description = "NAPALM driver for TP-Link Jetstream managed switches" readme = "README.md" license = { text = "Apache-2.0" } diff --git a/tests/unit/test_snmp_profile.py b/tests/unit/test_snmp_profile.py new file mode 100644 index 0000000..0a16cbe --- /dev/null +++ b/tests/unit/test_snmp_profile.py @@ -0,0 +1,25 @@ +"""The switch's SNMP health OIDs, declared as data (napalm-device-types docs/STRUCTURE.md). + +The driver does not collect over SNMP; the application polls with this profile. +""" + +from napalm_tplink_jetstream.tplink_jetstream import TPLinkJetstreamDriver + +PROFILE = TPLinkJetstreamDriver.SNMP_HEALTH_PROFILE + + +def test_the_driver_does_not_collect_itself(): + assert "get_health_metrics" not in vars(TPLinkJetstreamDriver) + + +def test_uptime_in_timeticks_from_the_tp_link_mib(): + # swt-l2-3-38 answered 129434122 (2026-10-16): TimeTicks, about 15 days. + assert PROFILE["uptime"] == {"oids": ["1.3.6.1.4.1.11863.6.1.1.9.0"]} + + +def test_cpu_and_memory_per_unit_take_the_first_row(): + # One row per stack unit; a single switch answers row 1 only. + assert PROFILE["cpu"] == {"table": "1.3.6.1.4.1.11863.6.4.1.1.1.1.3", "fold": "first"} + assert PROFILE["memory"] == { + "percent": {"table": "1.3.6.1.4.1.11863.6.4.1.2.1.1.2", "fold": "first"} + }