← Engineering articles

Troubleshooting guide and hypothetical lab · Python network monitoring

Python SNMP Timeout Handling: Separate Missing Metrics and Collector Errors

Build a bounded Python Net-SNMP wrapper that keeps valid zero, missing OIDs, no response and collector errors separate, with reproducible offline fixtures.

Conceptual SNMP collector with distinct valid metric, missing object and timeout result cards

A timeout should not become a zero counter. A missing OID should not mark every metric on the host as unavailable. Keep the original failure boundary in the poll result so the dashboard can distinguish a missing value from missing communication and a broken collector.

Scope and evidence: Python 3.10+ on Linux with Net-SNMP snmpget installed. This example wraps one numeric SNMPv3 authPriv metric per process; it does not depend on a changing Python SNMP SDK. Agent 192.0.2.50, user labPoller and the counter instance are documentation examples. Authentication/privacy settings and secrets already live in the collector account’s protected Net-SNMP configuration. No live agent was polled for this article; fixtures below are synthetic.

Define Python SNMP Results for Values, Missing OIDs and Timeouts

The wrapper returns value, missing_object, missing_instance, no_response, collector_deadline, collector_error or unexpected_value. The value is None for every failure and remains a real integer for a valid zero. Preserve agent and OID on each result. A timeout is an observation from the poller, not proof that the device is down; credentials, ACLs or a path can suppress responses.

Python collector invokes bounded Net-SNMP GET and preserves separate numeric value, missing OID, no-response and collector-failure records without substituting zero
Proposed collector contract. Synthetic fixtures verify classification; no real SNMP response is claimed. Scroll horizontally on small screens to read all labels. Open full-size diagram.

The Net-SNMP command reference defines the retry/time limit and v3 options. The output tutorial describes -Oqv. This example pins English diagnostic matching through the C locale and accepts only a single unsigned decimal Counter64 value within its 64-bit range; it intentionally rejects strings, enums and timeticks formats. An API exposing protocol exceptions directly is preferable when broader type support is needed.

Python Example: Collect One SNMP Counter with Bounded Timeouts

Save this Python code as snmp_metric.py. Defining the functions performs no polling. Calling poll_counter invokes a real subprocess and network request, so restrict calls to an approved inventory. Arguments use an array with shell=False; validate inventory and OIDs before a scheduler supplies them. A protected profile is still a secret-bearing file and needs restricted permissions.

import os
import re
import subprocess

def classify(returncode, stdout, stderr):
    out = stdout.strip()
    text = (stdout + "\n" + stderr).lower()
    if "no such object" in text:
        return {"status": "missing_object", "value": None}
    if "no such instance" in text:
        return {"status": "missing_instance", "value": None}
    if returncode and "timeout: no response from" in text:
        return {"status": "no_response", "value": None}
    if returncode:
        return {"status": "collector_error", "value": None}
    if not re.fullmatch(r"[0-9]+", out):
        return {"status": "unexpected_value", "value": None}
    value = int(out)
    if value > 2**64 - 1:
        return {"status": "unexpected_value", "value": None}
    return {"status": "value", "value": value}

def poll_counter(agent, oid, runner=subprocess.run):
    if not re.fullmatch(r"\.?[0-9]+(?:\.[0-9]+)+", oid):
        raise ValueError("Use a numeric OID from approved inventory")
    if agent.startswith("-") or not agent:
        raise ValueError("Invalid agent")
    command = ["snmpget", "-v3", "-l", "authPriv", "-u", "labPoller",
               "-t", "2", "-r", "0", "-Oqv", agent, oid]
    identity = {"agent": agent, "oid": oid}
    try:
        reply = runner(command, capture_output=True, text=True,
                       timeout=5, check=False, shell=False,
                       env={**os.environ, "LC_ALL": "C"})
    except subprocess.TimeoutExpired:
        return {**identity, "status": "collector_deadline", "value": None}
    except OSError:
        return {**identity, "status": "collector_error", "value": None}
    return {**identity, **classify(reply.returncode, reply.stdout, reply.stderr)}

-r 0 prevents automatic retries; -t 2 is the request timeout, while Python’s five-second limit bounds a stuck process separately. The outer limit is not a precise global scheduler deadline—process creation and OS scheduling have their own costs. A local deadline cannot establish whether a packet reached the agent. Python documents subprocess timeout and return-code behavior.

The function avoids returning raw stderr because it may reveal operational details. In production, retain sanitized diagnostic codes and bounded local evidence under the collector’s security policy. Do not convert an unrecognized authentication/security failure into no_response unless that is what the client actually reported.

Test SNMP Result Classification with Offline Fixtures

Append these synthetic Python fixtures to the same file and run python snmp_metric.py. The injected runner returns a constructed process result; it never executes snmpget:

if __name__ == "__main__":
    fixtures = [
        (0, "0\n", "", "value"),
        (0, "123456\n", "", "value"),
        (0, "No Such Object available on this agent at this OID", "", "missing_object"),
        (0, "No Such Instance currently exists at this OID", "", "missing_instance"),
        (1, "", "Timeout: No Response from 192.0.2.50", "no_response"),
        (1, "", "Authentication failure", "collector_error"),
        (0, "unexpected text", "", "unexpected_value"),
    ]
    for rc, out, err, expected in fixtures:
        def fake_runner(command, **kwargs):
            return subprocess.CompletedProcess(command, rc, out, err)
        result = poll_counter("192.0.2.50", ".1.3.6.1.2.1.31.1.1.1.6.7", fake_runner)
        assert result["status"] == expected
        if out == "0\n":
            assert result["value"] == 0
    print("7 offline fixtures passed")

Expected output is exactly 7 offline fixtures passed. This verifies the documented classifier for those strings, not compatibility with every Net-SNMP build. The example recognizes only the pinned client’s English forms. Unknown responses remain explicit errors until reviewed; never add a blanket exception handler that returns zero.

Decision flow separates completed collection, protocol missing-object response and unsigned counter parsing before storing the typed result
Retain per-OID outcomes; one response does not determine the status of every metric on a host. Scroll horizontally on small screens to read all labels. Open full-size diagram.

Integrate Per-OID SNMP Outcomes into Monitoring and Rollback

Store poll time, elapsed duration, identity and status alongside the value. A host summary may be partial when one object is readable and another is missing. Avoid a label such as host-down unless independent evidence and policy justify it. On a no-response status, follow the SNMP timeout guide; for an exception value, follow the OID/view guide.

Collector rollout change: save the current parser, dashboard mappings and alert rules. Pilot this outcome contract on one approved metric, with concurrency/rate limits and a fixed Net-SNMP version. Keep SNMP credentials out of command arguments and general logs. Add separate collector-health and missing-data handling instead of suppressing all errors. Counter values still need continuity-aware rate conversion; the utilization guide covers that stage.

Rollback restores the saved parser/template and starts a fresh metric baseline. Do not splice a zero-filled legacy interval into a new valid rate calculation. Retain fixture coverage and raw-to-sanitized classification evidence so a future client upgrade can be reviewed before deployment. No SNMP agent configuration change is required by this collector example.

Discuss a related technical problem

Need help tracing an infrastructure failure?

Share sanitized evidence, the platform version and the intended behavior. Imran Sarwar can assess a scoped infrastructure investigation.

Discuss your project Review Imran Sarwar’s experience

Remote English-language inquiries: USA, UK, Canada, Europe and beyond. UAE-based (UTC+4); working overlap is agreed per project.