This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
inventory_monitor_scripts is a Python package that collects network device inventory data via SNMP and uploads it to NetBox using the inventory-monitor-plugin. It supports multiple vendor platforms (Cisco, Juniper, Nokia) and SNMP versions (v2c, v3).
# Install in development mode with dependencies
pip install -e .
# Install system dependencies (required for easysnmp)
# macOS
brew install net-snmp
# Ubuntu/Debian
sudo apt-get install libsnmp-dev
# RHEL/CentOS
sudo yum install net-snmp-devel# Process all devices matching NetBox filters
python -m inventory_monitor.main
# Process a specific device
python -m inventory_monitor.main --device devicename
# Use a custom environment file (e.g., production, staging, dev)
python -m inventory_monitor.main --env-file .env.production
python -m inventory_monitor.main --env-file .env.staging --device test-router
# Override SNMP credentials via CLI
python -m inventory_monitor.main --snmp-version 2 --snmp-community my_secret
python -m inventory_monitor.main --snmp-user admin --snmp-auth Pass --snmp-auth-method SHA
# Adjust SNMP connection tuning
python -m inventory_monitor.main --snmp-timeout 30 --snmp-retries 3
# Override environment variables at runtime
SNMP_VERSION='2' NETBOX_TAGS='production' python -m inventory_monitor.main
# Set log level
LOG_LEVEL=DEBUG python -m inventory_monitor.main
# Dump raw SNMP data as JSON for debugging
python -m inventory_monitor.main --device devicename --debug-dump debug_output.json
# Override NetBox settings via CLI
python -m inventory_monitor.main --netbox-url https://netbox.example.com/ --netbox-api-token mytoken
# Filter by tags via CLI
python -m inventory_monitor.main --netbox-tags production,core --netbox-tags-exclude decommissioned
# Adjust parallel jobs
python -m inventory_monitor.main --parallel-jobs 10- Logs are stored in the
logs/directory logs/all.log- All messages at configured level (rotates at 10 MB)logs/errors.log- Only error/critical messages with detailed traces (rotates at 10 MB)- Retention policy: 1 week
- Log level configured via
LOG_LEVELenvironment variable (default: INFO)
# Run all tests
pytest tests/ -v
# Install test dependencies
pip install -e ".[test]"tests/conftest.py— Mocks theeasysnmpC-extension so tests run withoutnet-snmpsystem librariestests/test_snmp.py— SNMP handler tests (serial validation, handler factory, human-readable conversion)tests/test_models.py— Config dataclass and env-loading teststests/test_netbox.py— Probe diff, timestamps, and param preparation teststests/test_invmon.py— Inventory priority merging and device processing teststests/test_helper.py—stripper()utility tests
The conftest.py mock only activates when easysnmp is not importable (i.e., net-snmp is not installed). When the real library is present, tests use it directly.
Configuration uses frozen dataclasses defined in inventory_monitor/models.py:
AppConfig
├── snmp: SnmpConfig
│ ├── versions: List[int] # e.g. [3, 2]
│ ├── v2: SnmpV2Config
│ │ └── community: str
│ ├── v3: SnmpV3Config
│ │ ├── user, auth_password, auth_protocol
│ │ ├── privacy_password, privacy_protocol
│ │ └── security_level
│ ├── timeout: int
│ └── retries: int
├── netbox: NetBoxConfig
│ ├── api_token, instance_url
│ ├── tags, tags_exclude
│ └── verify_ssl: bool
├── parallel_jobs: int
├── log_level: str
└── model_name_excludes: List[str]
- Frozen dataclasses are immutable and picklable (safe for joblib multiprocessing)
load_config_from_env()buildsAppConfigfrom.env->os.environ- CLI overrides are merged via
dataclasses.replace()in_build_config()inmain.py
All core classes accept their config through constructors:
InventoryMonitor(config: AppConfig)— SNMP + model excludesNetbox(config: NetBoxConfig)— API token, URL, SSLISnmp(snmp_config: SnmpConfig)— credentials, timeout, retries
All constructors default to None, falling back to load_config_from_env() if not provided. This allows gradual migration and standalone usage.
The application follows a three-stage pipeline defined in inventory_monitor/main.py:
-
Device Discovery & Collection (
collect_device_inventory()):- Queries NetBox API with filters (active status, primary IP, optional tags)
- Filters built from
config.netbox.tagsandconfig.netbox.tags_exclude - Processes devices in parallel using
joblib(configurable viaconfig.parallel_jobs, default: 50) - Single devices are processed sequentially (no parallelization)
-
Inventory Preparation (
InventoryMonitor.prepare_device_sn_inventory()):- Merges inventory items by serial number
- Prioritizes items based on device class (e.g., chassis > port > module > sensor)
- Filters out excluded models (from
config.model_name_excludes)
-
NetBox Upload (
process_device_inventory()):- Creates or updates Probes in NetBox via the inventory-monitor-plugin
- Only processes devices with successful SNMP inventory retrieval
models.py: Configuration dataclasses and environment loading
- Frozen dataclasses:
SnmpV2Config,SnmpV3Config,SnmpConfig,NetBoxConfig,AppConfig load_config_from_env(env=None, env_file=".env"): BuildsAppConfigfrom dotenv files and os.environenv_fileparameter allows loading from custom files (e.g.,.env.production)- Default is
.envfor backward compatibility
- Accepts both legacy
entPhysicalModelName_exludesand correctedentPhysicalModelName_excludesspelling
snmp/: SNMP protocol handlers (subpackage)
snmp/constants.py: OID dicts and named field mappings (CISCO_ENTITY_FIELD,SROS_CHASSIS_FIELD,JUNOS_COMPONENT_FIELD, etc.)snmp/errors.py: Credential sanitization for SNMP error messagessnmp/base.py:ISnmpabstract base class- Accepts
SnmpConfigin constructor, defines interface with three methods:get_snmp_connection(): Establish connectionget_snmp_inventory(): Retrieve raw SNMP dataget_human_readable_inventory(): Parse into standardized format
snmp_session()is an instance method readingself.snmp_configfor credentials, timeout, retries- Retry loop: retries up to
snmp_config.retriestimes per SNMP version before moving to next - Serial number validation: must be string, >=4 chars, not "BUILTIN"
- Accepts
snmp/cisco.py:SnmpCisco(Entity MIB),SnmpCiscoLegacy(older OID structure)snmp/junos.py:SnmpJunos— Juniper JunOS devicessnmp/sros.py:SnmpSros— Nokia SROS devicessnmp/__init__.py: Re-exports all public names,PLATFORM_HANDLERSregistry,get_snmp_handler()factory
netbox.py: NetBox API integration
Netbox(config: NetBoxConfig): Accepts config for token, URL, SSL verifycreate_or_update_probe(): Creates or updates probe recordsget_latest_probe_by_serial(): Retrieves existing probes for comparison_check_probe_item_diff(): Detects if probe data has changed
invmon.py: Business logic orchestration
InventoryMonitor(config: AppConfig): Stores config for SNMP and model excludesprocess_device(): Instance method — main device processing- Validates device has primary IPv4 address
- Gets appropriate SNMP handler via
get_snmp_handler(), passingself.config.snmp - Tries standard SNMP first, falls back to legacy for Cisco devices
- Returns tuple of (nb_device, result_dict)
prepare_device_sn_inventory(): Instance method — serial-number based merging with class prioritization, readsself.config.model_name_excludes
logger.py: Logging configuration
- Uses loguru for structured logging
- Console output with color codes and function/line info
- File rotation at 10 MB with 1-week retention
- Separate error log for diagnostics
helper.py: Utility functions
stripper(): Safely strips whitespace from string values
main.py: CLI entry point and orchestration
- Click CLI with
--env-file,--device, SNMP override, NetBox override, and--parallel-jobsoptions CliOverridesdataclass: Holds all CLI option values includingenv_file_build_config(overrides): LoadsAppConfigfrom env file (default.envor custom via--env-file), merges non-None CLI args viadataclasses.replace()(SNMP, NetBox, and top-level overrides)--debug-dumpflag: optionally dumps raw SNMP results as JSON- NetBox API errors (
RequestError) are caught gracefully with logging (handles 500, invalid tags 400, etc.) - NetBox filter logic built inline from
config.netbox.tags/config.netbox.tags_exclude
Required environment variables (from .env file):
NETBOX_API_TOKEN: NetBox API authenticationNETBOX_INSTANCE_URL: NetBox instance URL (e.g., https://netbox.example.com/)SNMP_VERSION: Comma-separated versions to try (e.g., "3,2")SNMP_COMMUNITY: SNMPv2 community stringSNMP_USER,SNMP_AUTH,SNMP_AUTH_METHOD,SNMP_PRIVACY,SNMP_PRIVACY_METHOD,SNMP_SECURITY: SNMPv3 credentials
Optional environment variables:
NETBOX_TAGS: Comma-separated tags; only process devices with these tagsNETBOX_TAGS__N: Comma-separated tags; exclude devices with these tagsentPhysicalModelName_excludes: Comma-separated model names to exclude (legacy spellingentPhysicalModelName_exludesalso accepted)SNMP_TIMEOUT: SNMP timeout in seconds (default: 1)SNMP_RETRIES: Number of retries per SNMP version before moving to next (default: 1)NETBOX_VERIFY_SSL: Set to "false" to skip SSL certificate verification (default: true)LOG_LEVEL: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL; default: INFO)PARALLEL_JOBS: Number of parallel jobs for device processing (default: 50)
See .env.sample for complete example.
All SNMP parameters can be overridden from the command line (defaults to None, meaning use env value).
SECURITY NOTE: Command-line arguments for sensitive credentials (--snmp-community, --snmp-auth, --snmp-privacy, --netbox-api-token) are visible in process listings (ps aux) and shell history. Use --env-file with separate environment files for production deployments.
| Option | Description |
|---|---|
--env-file |
Path to environment file (default: .env) |
-d, --device |
Process a specific device by name |
--snmp-version |
Comma-separated SNMP versions (e.g. "3,2") |
--snmp-community |
SNMPv2 community string SNMP_COMMUNITY env var instead |
--snmp-user |
SNMPv3 username |
--snmp-auth |
SNMPv3 auth password SNMP_AUTH env var instead |
--snmp-auth-method |
SNMPv3 auth protocol (MD5/SHA) |
--snmp-privacy |
SNMPv3 privacy password SNMP_PRIVACY env var instead |
--snmp-privacy-method |
SNMPv3 privacy protocol (DES/AES) |
--snmp-security |
SNMPv3 security level |
--snmp-timeout |
SNMP timeout in seconds (default: 1) |
--snmp-retries |
Number of retries per SNMP version (default: 1) |
--netbox-api-token |
NetBox API token NETBOX_API_TOKEN env var instead |
--netbox-url |
NetBox instance URL |
--netbox-tags |
Comma-separated tags to filter devices |
--netbox-tags-exclude |
Comma-separated tags to exclude devices |
--parallel-jobs |
Number of parallel processing jobs (int) |
--log-level |
Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
--model-excludes |
Comma-separated model names to exclude from inventory |
--debug-dump |
Dump raw SNMP data to JSON file |
Inventory items with same serial number are merged with priority (low to high):
- backplane, container, fan(s), other, powerSupply(-ies), sensor(s), "-" (unrecognized)
- module, port, chassis (highest priority - these override duplicates)
- Attempts each version listed in
snmp_config.versionsin order (default:[3, 2]) - For each version, retries up to
snmp_config.retriestimes before moving to next - SNMPv3 tests connectivity with a
get(SYS_DESCR)before attempting bulkwalk - Falls back to SNMPv2 with community string if v3 fails
- For Cisco devices, if standard Entity MIB returns empty, tries legacy OID structure
- Connection failures are logged with specific error reasons (auth failure, timeout, unknown user)
- Uses joblib's
Parallel()with multiprocessing backend - Single device processing: sequential (no parallelization)
- Multiple devices: parallel with configurable job count (
config.parallel_jobs) - Each process handles one device's complete SNMP walk and parsing
- Frozen dataclasses are picklable, so
AppConfigsafely crosses process boundaries
- User runs:
python -m inventory_monitor.main --env-file .env.staging --device "router-1" --snmp-version 2 _build_config()loads config from.env.staging, overlays--snmp-version 2viadataclasses.replace()Netbox(config=config.netbox)andInventoryMonitor(config=config)are constructedcollect_device_inventory()fetches device from NetBox by namemonitor.process_device()is called:- Extracts IP from device.primary_ip4
get_snmp_handler(platform, config.snmp)returnsSnmpCisco(snmp_config=...)snmp_session()readsself.snmp_configfor credentials/timeout/retries- Establishes SNMP connection (only v2 since versions=[2])
- Walks Entity MIB OIDs, collects raw items
- Converts to human-readable format using named field constants
monitor.prepare_device_sn_inventory()groups by serial number, applies priority, filtersconfig.model_name_excludes- For each serial number,
netbox.create_or_update_probe()creates or updates probe - Results logged to
logs/all.logandlogs/errors.log - If
--debug-dumpwas passed, raw SNMP data saved as JSON file
Adding a new device platform:
- Create new
snmp/newplatform.pywithSnmpNewPlatformclass extendingISnmp - Implement three abstract methods with platform-specific OIDs and parsing
- Add OID constants to
snmp/constants.py(e.g.NEW_PLATFORM_FIELD = {"SERIAL": "5", ...}) - Add to
PLATFORM_HANDLERSdict insnmp/__init__.py:"platform_name": SnmpNewPlatform - Re-export the class in
snmp/__init__.py - Test with device having matching platform in NetBox
Changing inventory priority:
- Modify
inventory_priority_classeslist inInventoryMonitor.prepare_device_sn_inventory()
Adjusting NetBox filters:
- Default filters (active status, has_primary_ip) are built inline in
collect_device_inventory()inmain.py - Use
NETBOX_TAGSandNETBOX_TAGS__Nenv vars (orconfig.netbox.tags/config.netbox.tags_exclude) for tag-based filtering
Adding a new CLI option:
- Add
@click.option()decorator tomain()inmain.py - Add the parameter to the
CliOverridesdataclass - Add merge logic in
_build_config()usingdataclasses.replace() - If it's a new config field, add it to the appropriate dataclass in
models.py
Using multiple environment files:
- Create environment-specific files:
.env.production,.env.staging,.env.dev - Use
--env-fileto specify which config to load - Combine with CLI overrides for additional flexibility
- Example:
python -m inventory_monitor.main --env-file .env.staging --device test-router
Custom logging:
- Adjust
setup_logger()inlogger.pyfor file rotation size, retention, format - Log level controlled via
LOG_LEVELenv var orconfig.log_level
Core dependencies (from setup.py):
- easysnmp: SNMP protocol library (requires net-snmp system package)
- pynetbox: NetBox API client
- python-dotenv: Environment variable loading
- joblib: Parallel job execution
- loguru: Structured logging
- click: CLI framework