.. meta:: :description: Modern NetDevOps workflows center on a digital Source of Truth (SoT) such as **NetBox** or **Nautobot**. These platforms define the intended state of device... :keywords: Python, Snmp, Netbox, Nautobot, Netdevops, Ezsnmp, Inventory, Python, ezsnmp, Networking, DevOps :author: Carlos Santos (@carlkidcrypto) :property=og:title: Dynamic Inventory Sync: Populating NetBox & Nautobot via ezsnmp :property=og:description: Modern NetDevOps workflows center on a digital Source of Truth (SoT) such as **NetBox** or **Nautobot**. These platforms define the intended state of device... :property=og:type: article :property=og:url: https://carlkidcrypto.github.io/blog/ezsnmp-dynamic-inventory-sync-netbox.html :property=og:image: https://carlkidcrypto.github.io/_static/logo.png :name=twitter:card: summary :name=twitter:title: Dynamic Inventory Sync: Populating NetBox & Nautobot via ezsnmp :name=twitter:description: Modern NetDevOps workflows center on a digital Source of Truth (SoT) such as **NetBox** or **Nautobot**. These platforms define the intended state of device... :name=twitter:image: https://carlkidcrypto.github.io/_static/logo.png =============================================================== Dynamic Inventory Sync: Populating NetBox & Nautobot via ezsnmp =============================================================== .. note:: * **Published:** November 10, 2026 * **Authors:** Written by Gemini 3.8 Flash, Co-Authored & Reviewed by `Carlos Santos (@carlkidcrypto) `_ * **Estimated Reading Time:** 5 minutes * **Tags:** `Python` • `Snmp` • `Netbox` • `Nautobot` • `Netdevops` • `Ezsnmp` • `Inventory` The Challenge of Operational Drift in Source of Truth ===================================================== Modern NetDevOps workflows center on a digital Source of Truth (SoT) such as **NetBox** or **Nautobot**. These platforms define the intended state of device hardware, interface configurations, IP allocations, and serial numbers. However, operational reality frequently drifts from documented intent: * **Hardware RMAs**: Failed switches or linecards replaced during outages without updated serial numbers in the inventory database. * **Transceiver Moves**: SFP+ and QSFP modules swapped between ports, altering interface speeds and optical capabilities. * **Firmware Maintenance**: Operating systems patched during maintenance windows without synchronizing version records. Auditing this operational state via SSH screen-scraping (Netmiko/Scrapli) or heavy vendor REST APIs across thousands of switches requires privileged credentials, strains switch control-plane CPUs, and takes hours to complete. `ezsnmp `_ provides an ultra-fast, read-only discovery engine. By querying standard MIBs over SNMP, automation pipelines can extract serial numbers, interface MACs, and firmware versions across an entire data center in minutes, reconciling SoT drift with zero CLI overhead. The Hardware Discovery MIB Architecture ======================================= Two standard IETF MIBs provide complete hardware and operational inventory: 1. ENTITY-MIB (RFC 6933) ------------------------- The ``entPhysicalTable`` (OID ``.1.3.6.1.2.1.47.1.1.1``) catalogs physical components within a device chassis: * **``entPhysicalClass``**: Component category: ``chassis(3)``, ``module(9)``, ``port(10)``, ``powerSupply(6)``, or ``fan(7)``. * **``entPhysicalName``**: Component label (e.g. ``Chassis`` or ``Slot 1``). * **``entPhysicalModelName``**: Exact vendor hardware part number. * **``entPhysicalSerialNum``**: Factory hardware serial number. * **``entPhysicalSoftwareRev``**: Installed operating system or firmware. 2. IF-MIB (RFC 2863) -------------------- The ``ifXTable`` tracks physical interface parameters: * **``ifName``**: Canonical port name (e.g. ``HundredGigE1/0/1``). * **``ifPhysAddress``**: Hardware MAC address (binary octet string). * **``ifHighSpeed``**: Interface speed in Megabits per second (Mbps). * **``ifMtu``**: Configured Maximum Transmission Unit size. Practical Implementation: NetBox Reconciliation Script ====================================================== Below is a complete script demonstrating how to collect hardware identity via ezsnmp, query NetBox via ``pynetbox``, and detect configuration drift: .. code-block:: python import pynetbox from ezsnmp import Session from ezsnmp.exceptions import GenericError NETBOX_URL = "https://netbox.internal.net" NETBOX_TOKEN = "0123456789abcdef0123456789abcdef01234567" def parse_mac_address(raw_val: str | bytes) -> str: """Normalize raw octets or formatted hex strings to standard colon-separated MAC.""" if isinstance(raw_val, bytes): return ":".join(f"{b:02x}" for b in raw_val) cleaned = raw_val.replace(":", "").replace("-", "").replace(" ", "") try: raw_bytes = bytes.fromhex(cleaned) if len(raw_bytes) == 6: return ":".join(f"{b:02x}" for b in raw_bytes) except ValueError: pass if len(raw_val) == 6: return ":".join(f"{ord(c):02x}" for c in raw_val) return "" def discover_device_inventory( hostname: str, community: str = "public" ) -> dict: """Extract chassis serial and interfaces via ezsnmp.""" session = Session( hostname=hostname, community=community, version=2, timeout=2, retries=1, print_enums_numerically=True, set_max_repeaters_to_num=25 ) try: # 1. Query system metadata (unpack Result tuple from session.get) sys_name = session.get("sysName.0")[0].value sys_descr = session.get("sysDescr.0")[0].value # 2. Extract Chassis Serial from ENTITY-MIB class_vars = session.bulk_walk("entPhysicalClass") serial_vars = session.bulk_walk("entPhysicalSerialNum") name_vars = session.bulk_walk("entPhysicalName") serials = {v.index: v.value for v in serial_vars} names = {v.index: v.value for v in name_vars} chassis_serial = "UNKNOWN" for var in class_vars: # Class 3 = chassis (numeric or symbolic) if var.value in ("3", "chassis"): candidate = serials.get(var.index, "").strip() if candidate: chassis_serial = candidate break # 3. Collect interface MAC addresses from IF-MIB if_names = session.bulk_walk("ifName") if_macs = session.bulk_walk("ifPhysAddress") mac_map = { v.index: parse_mac_address(v.value) for v in if_macs if parse_mac_address(v.value) } interfaces = { v.value: mac_map.get(v.index, "") for v in if_names if v.index in mac_map } return { "hostname": sys_name, "serial": chassis_serial, "description": sys_descr, "interfaces": interfaces } except GenericError as exc: print(f"[{hostname}] Discovery failed: {exc}") return {} def reconcile_with_netbox(discovered: dict): """Compare operational reality against NetBox Source of Truth.""" nb = pynetbox.api(NETBOX_URL, token=NETBOX_TOKEN) device = nb.dcim.devices.get(name=discovered["hostname"]) if not device: print(f"Device {discovered['hostname']} not found in NetBox!") return print(f"\n--- Audit Report: {device.name} ---") # Serial number drift detection disc_serial = discovered.get("serial", "UNKNOWN") if disc_serial in ("", "UNKNOWN"): print( f" [INCONCLUSIVE] Chassis serial could not be discovered via SNMP;\n" f" preserving NetBox serial {device.serial} without overwriting." ) elif device.serial != disc_serial: print( f" [DRIFT DETECTED] Serial mismatch!\n" f" NetBox Intent : {device.serial}\n" f" Actual Switch : {disc_serial}" ) # Reconcile serial if authorized # device.serial = disc_serial # device.save() else: print(f" [OK] Serial number matches: {device.serial}") if __name__ == "__main__": inv = discover_device_inventory("edge-leaf01.internal") if inv: reconcile_with_netbox(inv) Production Best Practices ========================= * **Dry-Run Audit Before Writing**: Never blindly overwrite Source of Truth records during discovery. Always generate a structured diff and route changes through an approval workflow or review gate. * **Filter Chassis from Components**: Switches report dozens of physical serials (power supplies, fan trays, transceivers). Always check that ``entPhysicalClass == 3`` to identify the root chassis serial. * **Binary Safe MAC Parsing**: Physical interface MAC addresses in ``ifPhysAddress`` are returned as raw 6-byte octet strings. Encode with ``latin1`` and format with ``f"{b:02x}"`` to construct standardized colon- separated MAC strings. Migration Notes: Easy SNMP vs ezsnmp ==================================== * **Concurrency Architecture in ezsnmp**: While ``ezsnmp``'s C++17 session architecture ensures deterministic connection cleanup, Net-SNMP itself retains process-global C state protected by internal mutexes. For large multi-device crawls, isolate workers using ``concurrent.futures.ProcessPoolExecutor`` rather than large thread pools to prevent global lock contention. * **Binary Octet String Preservation**: Correctly handles raw binary strings in ``ifPhysAddress`` without truncation or encoding faults. Useful Links & Community ======================== * **Source Repository**: `carlkidcrypto/ezsnmp on GitHub `_ * **PyPI Distribution**: `ezsnmp Package on PyPI `_ * **Documentation**: `ezsnmp Online Documentation `_ * **Blog & Updates**: `Carlos Santos Blog Updates`_ .. _Carlos Santos Blog Updates: https://github.com/carlkidcrypto/carlkidcrypto.github.io/tree/feature/updates ---- .. warning:: AI-generated content may contain misinformation or mistakes. A human does review this, but we are only human.