Implement SNMP device auto-discovery with network scanning and automated inventory management

Intermediate 45 min Apr 09, 2026 1,246 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up automated SNMP device discovery using nmap network scanning and Python scripts for dynamic inventory management. Integrate with Zabbix and Grafana for comprehensive network monitoring and visualization.

Prerequisites

  • Root or sudo access
  • Network access to target devices
  • SNMP enabled on network devices
  • Python 3.8 or higher
  • At least 2GB free disk space

What this solves

SNMP device auto-discovery automates the process of finding and cataloging network devices across your infrastructure. Instead of manually maintaining device inventories, this system uses network scanning to detect SNMP-enabled devices and automatically adds them to your monitoring systems. This approach reduces operational overhead and ensures your network monitoring stays current as devices are added or removed.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions of all required components.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install SNMP tools and dependencies

Install the core SNMP utilities, nmap for network scanning, and Python dependencies for automation scripts.

sudo apt install -y snmp snmp-mibs-downloader nmap python3 python3-pip python3-venv curl jq
sudo dnf install -y net-snmp net-snmp-utils nmap python3 python3-pip curl jq

Create discovery environment

Set up a dedicated user and directory structure for the SNMP discovery system with proper permissions.

sudo useradd -r -s /bin/bash -d /opt/snmp-discovery snmp-discovery
sudo mkdir -p /opt/snmp-discovery/{scripts,logs,config,data}
sudo chown -R snmp-discovery:snmp-discovery /opt/snmp-discovery
sudo chmod 755 /opt/snmp-discovery
sudo chmod 775 /opt/snmp-discovery/{scripts,logs,config,data}

Install Python dependencies

Create a virtual environment and install required Python libraries for SNMP operations and database connectivity.

sudo -u snmp-discovery python3 -m venv /opt/snmp-discovery/venv
sudo -u snmp-discovery /opt/snmp-discovery/venv/bin/pip install --upgrade pip
sudo -u snmp-discovery /opt/snmp-discovery/venv/bin/pip install pysnmp requests sqlite3-python ipaddress netaddr

Configure SNMP client settings

Set up SNMP client configuration to enable MIB downloads and configure community strings.

# Allow MIB loading
mibs +ALL

# Default SNMP version
defVersion 2c

# Timeout settings
timeout 5
retries 3

Create discovery configuration

Configure the network ranges, SNMP communities, and discovery parameters for your environment.

# Network Discovery Configuration

[networks]
# Define network ranges to scan
network_ranges = 192.168.1.0/24,10.0.0.0/24,172.16.0.0/24

[snmp]
# SNMP communities to try
communities = public,private,community
versions = 2c,1
timeout = 5
retries = 3
port = 161

[discovery]
# Discovery intervals in minutes
scan_interval = 60
full_scan_interval = 1440

# Device identification OIDs
system_name_oid = 1.3.6.1.2.1.1.5.0
system_descr_oid = 1.3.6.1.2.1.1.1.0
system_uptime_oid = 1.3.6.1.2.1.1.3.0
system_contact_oid = 1.3.6.1.2.1.1.4.0
system_location_oid = 1.3.6.1.2.1.1.6.0

[output]
# Output formats and destinations
enable_json = true
enable_csv = true
enable_zabbix = true
enable_grafana = false

[database]
database_path = /opt/snmp-discovery/data/inventory.db

Create the main discovery script

Develop the core Python script that performs network scanning, SNMP queries, and inventory management.

#!/opt/snmp-discovery/venv/bin/python3

import os
import sys
import json
import sqlite3
import subprocess
import configparser
import logging
from datetime import datetime
from pysnmp.hlapi import *
from ipaddress import IPv4Network
import csv

class SNMPDiscovery:
    def __init__(self, config_file):
        self.config = configparser.ConfigParser()
        self.config.read(config_file)
        self.setup_logging()
        self.setup_database()
        
    def setup_logging(self):
        log_file = '/opt/snmp-discovery/logs/discovery.log'
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler(log_file),
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger(__name__)
        
    def setup_database(self):
        db_path = self.config.get('database', 'database_path')
        self.conn = sqlite3.connect(db_path)
        self.conn.execute('''
            CREATE TABLE IF NOT EXISTS devices (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                ip_address TEXT UNIQUE,
                hostname TEXT,
                system_name TEXT,
                system_descr TEXT,
                system_contact TEXT,
                system_location TEXT,
                uptime TEXT,
                snmp_community TEXT,
                snmp_version TEXT,
                first_seen TEXT,
                last_seen TEXT,
                status TEXT
            )
        ''')
        self.conn.commit()
        
    def scan_network(self, network_range):
        self.logger.info(f"Scanning network range: {network_range}")
        cmd = ['nmap', '-sn', network_range]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        active_ips = []
        for line in result.stdout.split('\n'):
            if 'Nmap scan report for' in line:
                ip = line.split()[-1].strip('()')
                active_ips.append(ip)
                
        return active_ips
        
    def snmp_query(self, ip, community, version, oid):
        try:
            if version == '2c':
                snmp_version = 1
            else:
                snmp_version = 0
                
            for (errorIndication, errorStatus, errorIndex, varBinds) in nextCmd(
                SnmpEngine(),
                CommunityData(community, mpModel=snmp_version),
                UdpTransportTarget((ip, 161), timeout=5),
                ContextData(),
                ObjectType(ObjectIdentity(oid)),
                lexicographicMode=False):
                
                if errorIndication:
                    break
                elif errorStatus:
                    break
                else:
                    for varBind in varBinds:
                        return str(varBind[1])
                        
        except Exception as e:
            self.logger.debug(f"SNMP query failed for {ip}: {e}")
            
        return None
        
    def discover_device(self, ip):
        communities = self.config.get('snmp', 'communities').split(',')
        versions = self.config.get('snmp', 'versions').split(',')
        
        for community in communities:
            community = community.strip()
            for version in versions:
                version = version.strip()
                
                # Try to get system name
                system_name = self.snmp_query(
                    ip, community, version, 
                    self.config.get('discovery', 'system_name_oid')
                )
                
                if system_name:
                    self.logger.info(f"Found SNMP device: {ip} ({system_name})")
                    
                    device_info = {
                        'ip_address': ip,
                        'system_name': system_name,
                        'snmp_community': community,
                        'snmp_version': version,
                        'system_descr': self.snmp_query(
                            ip, community, version,
                            self.config.get('discovery', 'system_descr_oid')
                        ),
                        'system_contact': self.snmp_query(
                            ip, community, version,
                            self.config.get('discovery', 'system_contact_oid')
                        ),
                        'system_location': self.snmp_query(
                            ip, community, version,
                            self.config.get('discovery', 'system_location_oid')
                        ),
                        'uptime': self.snmp_query(
                            ip, community, version,
                            self.config.get('discovery', 'system_uptime_oid')
                        )
                    }
                    
                    return device_info
                    
        return None
        
    def update_inventory(self, device_info):
        now = datetime.now().isoformat()
        
        # Check if device exists
        cursor = self.conn.execute(
            'SELECT id FROM devices WHERE ip_address = ?',
            (device_info['ip_address'],)
        )
        
        if cursor.fetchone():
            # Update existing device
            self.conn.execute('''
                UPDATE devices SET
                    system_name = ?, system_descr = ?, system_contact = ?,
                    system_location = ?, uptime = ?, snmp_community = ?,
                    snmp_version = ?, last_seen = ?, status = 'active'
                WHERE ip_address = ?
            ''', (
                device_info['system_name'], device_info['system_descr'],
                device_info['system_contact'], device_info['system_location'],
                device_info['uptime'], device_info['snmp_community'],
                device_info['snmp_version'], now, device_info['ip_address']
            ))
        else:
            # Insert new device
            self.conn.execute('''
                INSERT INTO devices (
                    ip_address, system_name, system_descr, system_contact,
                    system_location, uptime, snmp_community, snmp_version,
                    first_seen, last_seen, status
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active')
            ''', (
                device_info['ip_address'], device_info['system_name'],
                device_info['system_descr'], device_info['system_contact'],
                device_info['system_location'], device_info['uptime'],
                device_info['snmp_community'], device_info['snmp_version'],
                now, now
            ))
            
        self.conn.commit()
        
    def export_inventory(self):
        cursor = self.conn.execute('SELECT * FROM devices WHERE status = "active"')
        devices = cursor.fetchall()
        
        # Export to JSON
        if self.config.getboolean('output', 'enable_json'):
            json_data = []
            for device in devices:
                json_data.append({
                    'ip_address': device[1],
                    'hostname': device[2],
                    'system_name': device[3],
                    'system_descr': device[4],
                    'system_contact': device[5],
                    'system_location': device[6],
                    'uptime': device[7],
                    'snmp_community': device[8],
                    'snmp_version': device[9],
                    'first_seen': device[10],
                    'last_seen': device[11]
                })
                
            with open('/opt/snmp-discovery/data/inventory.json', 'w') as f:
                json.dump(json_data, f, indent=2)
                
        # Export to CSV
        if self.config.getboolean('output', 'enable_csv'):
            with open('/opt/snmp-discovery/data/inventory.csv', 'w', newline='') as f:
                writer = csv.writer(f)
                writer.writerow([
                    'ip_address', 'hostname', 'system_name', 'system_descr',
                    'system_contact', 'system_location', 'uptime',
                    'snmp_community', 'snmp_version', 'first_seen', 'last_seen'
                ])
                
                for device in devices:
                    writer.writerow(device[1:])
                    
    def run_discovery(self):
        self.logger.info("Starting SNMP device discovery")
        
        network_ranges = self.config.get('networks', 'network_ranges').split(',')
        
        for network_range in network_ranges:
            network_range = network_range.strip()
            active_ips = self.scan_network(network_range)
            
            for ip in active_ips:
                device_info = self.discover_device(ip)
                if device_info:
                    self.update_inventory(device_info)
                    
        self.export_inventory()
        self.logger.info("Discovery completed")
        
if __name__ == '__main__':
    config_file = '/opt/snmp-discovery/config/discovery.conf'
    discovery = SNMPDiscovery(config_file)
    discovery.run_discovery()

Create Zabbix integration script

Build a script to automatically add discovered devices to Zabbix monitoring system.

#!/opt/snmp-discovery/venv/bin/python3

import json
import requests
import sqlite3
import logging
import configparser

class ZabbixIntegration:
    def __init__(self, config_file):
        self.config = configparser.ConfigParser()
        self.config.read(config_file)
        self.setup_logging()
        
        # Zabbix API settings
        self.zabbix_url = "http://localhost/zabbix/api_jsonrpc.php"
        self.zabbix_user = "admin"
        self.zabbix_password = "zabbix"
        self.auth_token = None
        
    def setup_logging(self):
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s'
        )
        self.logger = logging.getLogger(__name__)
        
    def zabbix_authenticate(self):
        payload = {
            "jsonrpc": "2.0",
            "method": "user.login",
            "params": {
                "user": self.zabbix_user,
                "password": self.zabbix_password
            },
            "id": 1
        }
        
        response = requests.post(self.zabbix_url, json=payload)
        result = response.json()
        
        if 'result' in result:
            self.auth_token = result['result']
            self.logger.info("Zabbix authentication successful")
            return True
        else:
            self.logger.error(f"Zabbix authentication failed: {result}")
            return False
            
    def create_host(self, device_info):
        payload = {
            "jsonrpc": "2.0",
            "method": "host.create",
            "params": {
                "host": device_info['ip_address'],
                "name": device_info['system_name'] or device_info['ip_address'],
                "interfaces": [{
                    "type": 2,  # SNMP interface
                    "main": 1,
                    "useip": 1,
                    "ip": device_info['ip_address'],
                    "dns": "",
                    "port": "161",
                    "details": {
                        "version": 2 if device_info['snmp_version'] == '2c' else 1,
                        "community": device_info['snmp_community']
                    }
                }],
                "groups": [{"groupid": "4"}],  # Linux servers group
                "templates": [{"templateid": "10001"}]  # Template OS Linux
            },
            "auth": self.auth_token,
            "id": 1
        }
        
        response = requests.post(self.zabbix_url, json=payload)
        result = response.json()
        
        if 'result' in result:
            self.logger.info(f"Created Zabbix host: {device_info['ip_address']}")
            return True
        else:
            self.logger.error(f"Failed to create host: {result}")
            return False
            
    def sync_devices(self):
        if not self.zabbix_authenticate():
            return
            
        db_path = self.config.get('database', 'database_path')
        conn = sqlite3.connect(db_path)
        
        cursor = conn.execute('SELECT * FROM devices WHERE status = "active"')
        devices = cursor.fetchall()
        
        for device in devices:
            device_info = {
                'ip_address': device[1],
                'system_name': device[3],
                'snmp_community': device[8],
                'snmp_version': device[9]
            }
            
            self.create_host(device_info)
            
if __name__ == '__main__':
    config_file = '/opt/snmp-discovery/config/discovery.conf'
    integration = ZabbixIntegration(config_file)
    integration.sync_devices()

Create monitoring dashboard script

Develop a script to generate monitoring data for Grafana dashboards using discovered device information.

#!/opt/snmp-discovery/venv/bin/python3

import json
import sqlite3
import logging
import configparser
from datetime import datetime

class GrafanaExport:
    def __init__(self, config_file):
        self.config = configparser.ConfigParser()
        self.config.read(config_file)
        self.setup_logging()
        
    def setup_logging(self):
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s'
        )
        self.logger = logging.getLogger(__name__)
        
    def generate_dashboard_data(self):
        db_path = self.config.get('database', 'database_path')
        conn = sqlite3.connect(db_path)
        
        cursor = conn.execute('SELECT * FROM devices WHERE statu

Automated install script

Run this to automate the entire setup

Prefere não gerir isto sozinho?

Gerimos a infraestrutura de empresas que dependem do tempo de atividade. Totalmente gerida, com um contacto fixo que conhece o seu ambiente.

Tem um contacto fixo que conhece o seu ambiente

Roterdão 23:19 · acessível por mensagem, sem formulário de tickets