# Inventory Monitor Scripts

A comprehensive Python package for gathering network device inventory via SNMP and storing it in NetBox using the inventory-monitor-plugin.

## Overview

This package performs the following key functions:

1. Loads inventory data from network devices via SNMP (v2c and v3 supported)
2. Processes and normalizes inventory data from multiple vendor platforms
   - Cisco (IOS, IOS-XE, IOS-XR)
   - Juniper (JunOS)
   - Nokia (SROS)
3. Merges inventory items with the same serial numbers based on priority class
4. Uploads inventory data to NetBox as Probes via the inventory-monitor-plugin

## Installation

### Prerequisites

- Python 3.7+
- NetBox instance with the inventory-monitor-plugin installed
- Network access to your devices via SNMP
- System SNMP libraries (required for easysnmp)

### System Dependencies

Install the net-snmp development libraries based on your OS:

```bash
# macOS (Homebrew)
brew install net-snmp

# Debian/Ubuntu
sudo apt-get install libsnmp-dev

# RHEL/CentOS
sudo yum install net-snmp-devel
```

#### ⚠️ Known Issue: macOS ARM64 (Apple Silicon M1/M2/M3)

The `easysnmp` dependency has known compilation issues on macOS with Apple Silicon processors. This is a limitation of the `easysnmp` package itself, not this project.

**Recommended Workarounds:**

1. **Use Docker (Recommended for Production)**
   ```bash
   # Use Linux-based container where easysnmp works reliably
   docker run -it --rm -v $(pwd):/app python:3.11-slim bash
   apt-get update && apt-get install -y libsnmp-dev gcc
   cd /app && pip install -e .
   ```

2. **Use Linux VM or Server** - Deploy on Linux where easysnmp has better support

3. **Development on macOS** - If you must develop locally on ARM64 Mac:
   - Install OpenSSL via Homebrew: `brew install openssl@3`
   - Try manual installation with build flags:
     ```bash
     export CFLAGS="-I/opt/homebrew/opt/openssl@3/include"
     export LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib"
     pip install easysnmp
     ```
   - **Note**: This may still fail due to upstream bugs in easysnmp

For production deployments, **we strongly recommend using Linux** (Docker, VM, or bare metal) where all dependencies install reliably.

See [Troubleshooting](#troubleshooting) section for more details on easysnmp installation issues.

### Python Package Installation

Clone the repository and install:

```bash
git clone https://github.com/yourusername/inventory_monitor_scripts.git
cd inventory_monitor_scripts
pip install -e .
```

Verify installation:

```bash
python -m inventory_monitor.main --help
```

## Configuration

Create a `.env` file in the repository root with required settings. Copy `.env.sample` as a starting point:

```bash
cp .env.sample .env
# Edit .env with your credentials
```

### Multiple Environment Configurations

You can maintain separate configuration files for different environments (production, staging, development) and specify which one to use with the `--env-file` flag:

```bash
# Create environment-specific files
cp .env.sample .env.production
cp .env.sample .env.staging

# Use production config
python -m inventory_monitor.main --env-file .env.production

# Use staging config with different device filter
python -m inventory_monitor.main --env-file .env.staging --netbox-tags staging
```

If `--env-file` is not specified, the default `.env` file is used. This allows you to keep separate SNMP credentials, NetBox instances, and other settings per environment without modifying the default configuration.

### Security Best Practices

**⚠️ IMPORTANT**: This tool handles sensitive credentials (SNMP community strings, SNMPv3 passwords, NetBox API tokens).

**Recommended approach**:
- ✅ **Use environment variables** (`.env` file or exported variables)
- ✅ **Use `--env-file` for different environments** (keeps credentials in files, not CLI)
- ❌ **Avoid CLI arguments** for secrets (visible in `ps aux`, shell history, logs)

While CLI arguments like `--snmp-community` and `--netbox-api-token` are supported for convenience, they expose credentials in process listings. Use them only for testing in secure environments.

### Required Settings

```bash
# NetBox API Configuration
NETBOX_API_TOKEN="your_netbox_api_token"
NETBOX_INSTANCE_URL="https://your-netbox-instance/"

# SNMP Protocol Version (comma-separated, tried in order)
SNMP_VERSION="3,2"  # Try SNMPv3 first, fall back to SNMPv2

# SNMPv2 Configuration
SNMP_COMMUNITY="public"  # SNMPv2 community string

# SNMPv3 Configuration (only needed if using SNMPv3)
SNMP_USER="snmp_user"
SNMP_AUTH="auth_password"
SNMP_AUTH_METHOD="SHA"  # MD5 or SHA
SNMP_PRIVACY="privacy_password"
SNMP_PRIVACY_METHOD="AES"  # DES or AES
SNMP_SECURITY="authPriv"  # noAuthNoPriv, authNoPriv, or authPriv
```

### Optional Settings

```bash
# Device Filtering
NETBOX_TAGS="tag1,tag2"  # Only process devices with these tags
NETBOX_TAGS__N="tag3,tag4"  # Exclude devices with these tags
entPhysicalModelName_excludes="ASR-9900-AC-PEM,ASR-9900-DC-PEM"  # Exclude specific models
# Note: the legacy spelling "entPhysicalModelName_exludes" is also accepted

# SNMP Connection Tuning
SNMP_TIMEOUT=1     # SNMP timeout in seconds (default: 1)
SNMP_RETRIES=1     # Number of retries per SNMP version (default: 1)

# NetBox SSL
NETBOX_VERIFY_SSL=true  # Set to "false" to skip SSL certificate verification

# Performance Tuning
PARALLEL_JOBS=50  # Number of parallel device processing jobs (default: 50)

# Logging
LOG_LEVEL="INFO"  # DEBUG, INFO, WARNING, ERROR, CRITICAL (default: INFO)
```

**Security Note**: The `.env` file contains sensitive credentials and is automatically ignored by git. The application loads config in priority order: `.env` file → environment variables (CLI and exported vars override `.env` values).

## Usage

### Process all devices

Run the script to process all active devices with primary IPs in NetBox:

```bash
python -m inventory_monitor.main
```

The script will:
1. Query NetBox for matching devices
2. Establish SNMP connections to each device in parallel
3. Retrieve inventory data
4. Merge inventory items by serial number
5. Create or update Probes in NetBox

### Process a single device

To process a specific device by name (useful for testing):

```bash
python -m inventory_monitor.main --device router-1
```

Single device processing runs sequentially without parallelization.

### CLI options for SNMP and NetBox

All SNMP and NetBox parameters can be overridden directly from the command line. CLI values take precedence over `.env` settings:

```bash
# Override SNMPv2 community string
python -m inventory_monitor.main --snmp-version 2 --snmp-community my_community

# Override SNMPv3 credentials
python -m inventory_monitor.main --snmp-user admin --snmp-auth MyPass --snmp-auth-method SHA

# Override SNMPv3 with full privacy
python -m inventory_monitor.main \
  --snmp-user admin \
  --snmp-auth AuthPass \
  --snmp-auth-method SHA \
  --snmp-privacy PrivPass \
  --snmp-privacy-method AES \
  --snmp-security authPriv

# Adjust timeout and retries
python -m inventory_monitor.main --snmp-timeout 30 --snmp-retries 3

# Combine with device selection
python -m inventory_monitor.main --device router-1 --snmp-version 2 --snmp-community secret

# Override NetBox settings
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

# Exclude tags via CLI
python -m inventory_monitor.main --netbox-tags-exclude decommissioned

# Adjust parallel jobs
python -m inventory_monitor.main --parallel-jobs 10
```

Full list of CLI options:

| Option | Description |
|--------|-------------|
| `--env-file` | Path to environment file (default: `.env`) |
| `-d`, `--device` | Process a specific device by name |
| `--snmp-version` | Comma-separated SNMP versions to try (e.g. `"3,2"`) |
| `--snmp-community` | SNMPv2 community string ⚠️ |
| `--snmp-user` | SNMPv3 username |
| `--snmp-auth` | SNMPv3 auth password ⚠️ |
| `--snmp-auth-method` | SNMPv3 auth protocol (`MD5` or `SHA`) |
| `--snmp-privacy` | SNMPv3 privacy password ⚠️ |
| `--snmp-privacy-method` | SNMPv3 privacy protocol (`DES` or `AES`) |
| `--snmp-security` | SNMPv3 security level (`noAuthNoPriv`, `authNoPriv`, `authPriv`) |
| `--snmp-timeout` | SNMP timeout per request (integer) |
| `--snmp-retries` | Number of retries per SNMP version (integer) |
| `--netbox-api-token` | NetBox API token ⚠️ |
| `--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 (integer) |
| `--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 for debugging |

⚠️ Options marked with warning expose credentials in process listings. Use `--env-file` with separate environment files instead.

### Override environment variables at runtime

You can also still use environment variable overrides:

```bash
# Force SNMPv2 only
SNMP_VERSION='2' python -m inventory_monitor.main

# Process only devices tagged 'production'
NETBOX_TAGS='production' python -m inventory_monitor.main

# Combine multiple overrides
LOG_LEVEL=DEBUG PARALLEL_JOBS=10 python -m inventory_monitor.main --device test-device
```

### Performance tuning

For large device sets, adjust parallelism:

```bash
# Reduce load on SNMP devices (10 parallel jobs)
PARALLEL_JOBS=10 python -m inventory_monitor.main

# Maximize throughput (200 parallel jobs, requires more system resources)
PARALLEL_JOBS=200 python -m inventory_monitor.main
```

## How to tell which SNMP version is being used

The application logs which SNMP version successfully connects to each device. Run with `LOG_LEVEL=DEBUG` (or at default `INFO`) and look for these log messages:

```
# Successful SNMPv3:
device-name (10.0.0.1): SNMPv3 connection successful

# SNMPv3 failed, fell back to SNMPv2:
device-name (10.0.0.1): Unable to use SNMPv3 - <reason>
device-name (10.0.0.1): SNMPv2 connection successful

# Both failed:
device-name (10.0.0.1): All SNMP connection attempts failed
```

The version try order is controlled by `SNMP_VERSION` (env) or `--snmp-version` (CLI). With the default `"3,2"`, SNMPv3 is tried first. If it fails, SNMPv2 is tried. To force a specific version:

```bash
# Force SNMPv2 only (skip v3 entirely)
python -m inventory_monitor.main --snmp-version 2

# Force SNMPv3 only (no v2 fallback)
python -m inventory_monitor.main --snmp-version 3

# Try v2 first, then v3
python -m inventory_monitor.main --snmp-version "2,3"
```

At `DEBUG` log level, you will also see retry attempts. The default `SNMP_RETRIES=1` means one attempt per version (no retries). With `--snmp-retries 3`:

```
device-name (10.0.0.1): SNMPv3 attempt 1/3 failed, retrying
device-name (10.0.0.1): SNMPv3 attempt 2/3 failed, retrying
device-name (10.0.0.1): SNMPv3 authentication failed - check username and password/keys
device-name (10.0.0.1): SNMPv2 connection successful
```

## Logging

Logs are created in the `logs/` directory with automatic rotation:

- **`logs/all.log`** - All messages at the configured log level (default: INFO)
- **`logs/errors.log`** - Error and critical messages with detailed traces

Both logs rotate at 10 MB and are retained for 1 week.

Set `LOG_LEVEL=DEBUG` for detailed troubleshooting:

```bash
LOG_LEVEL=DEBUG python -m inventory_monitor.main --device router-1
```

## Troubleshooting

### Installation Issues

**easysnmp on macOS ARM64**: The `easysnmp` dependency has known issues on Apple Silicon Macs. For production use, deploy on Linux (recommended) or use Docker.

### SNMP Connection Issues

**"All SNMP connection attempts failed"**
- Verify device IP is reachable: `ping <device-ip>`
- Confirm SNMP is enabled on the device
- Check SNMP credentials in `.env`
- Try forcing a specific version: `python -m inventory_monitor.main --device <name> --snmp-version 2`

**"SNMPv3 authentication failed"**
- Verify `SNMP_USER` matches device configuration
- Check `SNMP_AUTH` password and `SNMP_AUTH_METHOD` (SHA vs MD5)
- If using privacy, ensure `SNMP_PRIVACY` password and `SNMP_PRIVACY_METHOD` are correct
- Override from CLI to test: `--snmp-user myuser --snmp-auth mypass --snmp-auth-method SHA`

**"SNMPv2 timeout"**
- Device may not support SNMPv2
- Increase timeout: `--snmp-timeout 200`
- Increase retries: `--snmp-retries 3`
- Try SNMPv3: `--snmp-version 3`

### No Inventory Returned

- Device may not support Entity MIB (standard OIDs)
- Cisco devices fall back to legacy OIDs automatically
- Check device is actively reporting SNMP inventory

### NetBox API Errors

**"API token invalid"**
- Regenerate token in NetBox under Settings > API Tokens
- Verify token has write permissions to inventory-monitor plugin

**"Probe creation failed"**
- Ensure inventory-monitor-plugin is installed in NetBox
- Check device exists in NetBox with correct name
- Verify device has primary IPv4 address assigned

**"NetBox API error ... 500"**
- NetBox server encountered an internal error
- Check NetBox server logs for details
- Retry after the server recovers

**"NetBox API error ... 400 ... not one of the available choices"**
- A tag specified in `NETBOX_TAGS` or `--netbox-tags` does not exist in NetBox
- Verify tag names match exactly (case-sensitive)
- Create missing tags in NetBox under Organization > Tags

### Debug Data

Use `--debug-dump` to save raw SNMP data as a JSON file for inspection:

```bash
python -m inventory_monitor.main --device router-1 --debug-dump debug_output.json
```

Then inspect it in Python:

```python
import json
with open('debug_output.json') as f:
    data = json.load(f)
    # Inspect raw SNMP results
```

## Development

See `CLAUDE.md` for detailed architecture and development guidance.

### Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/your-feature`)
3. Commit your changes (`git commit -am 'Add feature'`)
4. Push to the branch (`git push origin feature/your-feature`)
5. Create a Pull Request

## License

[Insert your license information here]

## Credits

Developed by [Your Organization/Team Name]
