Info
License: Free — part of the universal collection tier. SNMP polling is the backbone of network monitoring and the agent ships it without a license key.
SNMP Poll Probe¶
The snmp_poll probe polls a network device over SNMP v2c or v3 and
turns standard MIB objects into typed metrics: system uptime,
per-interface traffic, errors, discards, speed and status. Custom OID
mappings cover the vendor-specific long tail.
The probe also reads the LLDP neighbor table of each polled device and reports its links. A link is reported when the neighbor advertises a named port (LLDP port ID subtype interface name or locally assigned); a port known only by its MAC address is not linked.
A discovery mode that crawls the network outward from seed devices is
accepted in the configuration but not active yet
(#953): the
discovery block is validated and ignored. The crawl profile is v2c
only.
One probe instance polls one device; declare one instance per device.
Quick start¶
# probes.d/10-snmp-poll.yaml — each file under probes.d/ is a YAML array of probes
- name: core-switch
type: snmp_poll
params:
target: 192.168.1.10
community: ${secret:core-switch.community} # OS secret store; inline plaintext is auto-sealed on install
mibs: [mib-2, if-mib]
interval: 60
This polls system and interface tables every 60 seconds and emits
one series per interface (if_index tag).
Parameters¶
| Parameter | Must set | Default | Description |
|---|---|---|---|
target |
Yes | - | Device address or hostname |
port |
No | 161 |
SNMP UDP port |
version |
In practice | v2c |
SNMP version; v1 is refused. One of v2c, v3, 2c, 3, 2 |
community |
If version is v2c, 2c, 2 |
public |
Community string (v2c). A secret: reference it with ${secret:…}, ${env:…} or ${file:…} rather than writing it in the file |
v3 |
If version is v3, 3 |
- | USM credentials, required with version v3 |
v3.username |
Yes | - | USM user |
v3.auth_protocol |
No | - | Authentication protocol; empty for none. One of MD5, SHA, SHA224, SHA256, SHA384, SHA512 |
v3.auth_passphrase |
No | - | Required with auth_protocol. A secret: reference it with ${secret:…}, ${env:…} or ${file:…} rather than writing it in the file |
v3.priv_protocol |
No | - | Privacy protocol; needs auth_protocol. One of DES, AES, AES192, AES256 |
v3.priv_passphrase |
No | - | Required with priv_protocol. A secret: reference it with ${secret:…}, ${env:…} or ${file:…} rather than writing it in the file |
retries |
No | 2 |
Retries per request |
timeout |
No | 5s |
Per-request timeout |
interval |
No | 60s |
Metric polling cadence |
topology_interval |
No | 10m |
Entity and topology sweep cadence |
mibs |
In practice | - | Built-in MIB modules to poll; this or custom_mappings is required. One of mib-2, if-mib |
mib_paths |
No | - | Local MIB files or folders used to name custom mappings |
custom_mappings |
No | - | OID to metric mappings |
custom_mappings[].oid |
Yes | - | OID, leading dot optional |
custom_mappings[].metric |
No | - | Metric name; resolved from mib_paths when omitted |
custom_mappings[].type |
No | gauge |
How the value is reported: a gauge as read, a counter as a monotonic total. One of gauge, counter |
custom_mappings[].index_label |
No | - | Walk the OID as a table and tag rows with this label |
discovery |
No | - | Topology crawl from seed devices. Not active yet (#953): the block is validated and ignored |
discovery.seeds |
Yes | - | Entry-point device addresses |
discovery.profile |
Yes | - | Credentials for crawled devices (v2c only) |
discovery.profile.version |
No | v2c |
SNMP version used to probe the discovered devices. One of v2c, 2c, 2 |
discovery.profile.community |
Yes | - | Community string used to probe the discovered devices. A secret: reference it with ${secret:…}, ${env:…} or ${file:…} rather than writing it in the file |
discovery.allowed_cidrs |
Yes | - | The crawl never leaves these ranges |
discovery.max_devices |
No | 200 |
Hard cap on the number of discovered devices |
discovery.max_hops |
No | 4 |
Crawl depth from the seeds |
discovery.interval |
No | - | Crawl cadence; topology_interval by default |
discovery.governance_rules |
No | - | Per-device governance by match |
discovery.governance_rules[].match |
No | - | Conditions a device must all meet; an empty match applies to every device |
discovery.governance_rules[].match.cidr |
No | - | Address range the polled address must fall in |
discovery.governance_rules[].match.vendor |
No | - | Vendor name from sysObjectID, compared case-insensitively |
discovery.governance_rules[].match.sysname |
No | - | Regular expression the device sysName must match |
discovery.governance_rules[].governance |
No | - | A block of settings |
discovery.governance_rules[].governance.owner |
No | - | Who owns what this instance observes |
discovery.governance_rules[].governance.owner.team |
No | - | Owning team |
discovery.governance_rules[].governance.owner.contact |
No | - | Contact for the team |
discovery.governance_rules[].governance.criticality |
No | - | Business criticality. One of critical, high, medium, low |
discovery.governance_rules[].governance.location |
No | - | Where it is |
discovery.governance_rules[].governance.location.site |
No | - | Site name |
discovery.governance_rules[].governance.location.datacenter |
No | - | Datacenter name |
discovery.governance_rules[].governance.location.rack |
No | - | Rack identifier |
discovery.governance_rules[].governance.location.room |
No | - | Room identifier |
discovery.governance_rules[].governance.lifecycle |
No | - | active, maintenance, decommissioning or retired |
discovery.governance_rules[].governance.labels |
No | - | Free-form labels, emitted as entity.label. |
governance |
No | - | Ownership, criticality and location of the device |
governance.owner |
No | - | Who owns what this instance observes |
governance.owner.team |
No | - | Owning team |
governance.owner.contact |
No | - | Contact for the team |
governance.criticality |
No | - | Business criticality. One of critical, high, medium, low |
governance.location |
No | - | Where it is |
governance.location.site |
No | - | Site name |
governance.location.datacenter |
No | - | Datacenter name |
governance.location.rack |
No | - | Rack identifier |
governance.location.room |
No | - | Room identifier |
governance.lifecycle |
No | - | active, maintenance, decommissioning or retired |
governance.labels |
No | - | Free-form labels, emitted as entity.label. |
At least one entry under mibs or custom_mappings is required.
Configuration errors are accumulated and reported together at
startup, not one at a time.
retries matters on a congested link: UDP loses packets, and each
attempt costs one timeout before the device counts as unanswered.
topology_interval is a slower rail, independent of interval, so a
dense topology sweep never delays the traffic counters. mib_paths is
read locally; the probe never fetches a MIB over the network.
SNMPv3 (USM)¶
params:
target: 192.168.1.10
version: "3"
mibs: [mib-2, if-mib]
v3:
username: monitoring
auth_protocol: SHA256
auth_passphrase: "${file:/etc/senhub-agent/snmp_auth}"
priv_protocol: AES256
priv_passphrase: "${file:/etc/senhub-agent/snmp_priv}"
The security level (noAuthNoPriv / authNoPriv / authPriv) is derived from which protocols are set — there is no separate field to contradict it. Unknown protocol names are startup errors, never a silent downgrade. The discovery crawl profile remains v2c-only.
Custom mappings¶
Map any OID — scalar or table column — to a named metric:
params:
target: 192.168.1.20
custom_mappings:
- oid: .1.3.6.1.4.1.318.1.1.1.2.2.1 # APC UPS battery capacity
metric: snmp.ups.battery_capacity
type: gauge
- oid: .1.3.6.1.2.1.2.2.1.10 # ifInOctets (table walk)
metric: snmp.interface.in_octets
type: counter
index_label: if_index
metric may be omitted only when mib_paths is set: the name is then
resolved from your MIB files at startup (for example
upsAdvBatteryCapacity), and an OID that cannot be resolved is a startup
error, never a silent gap. With index_label, the OID is walked as a
table and the row index becomes that tag.
Discovery¶
Not active yet
The discovery block is parsed and validated but not wired to
the poll lifecycle yet — configuring it does nothing today
beyond a startup warning. It is documented here because the
configuration shape is final. Per-device topology (LLDP
neighbors, routes, bridge tables of the polled target) is
active and independent of this block. Tracking:
#953.
When the crawl ships, a discovery block will make the probe crawl
outward from seed devices using LLDP neighbor tables, bounded by
CIDR ranges and depth/device caps, and report discovered devices and
links on the entity rail:
params:
target: 192.168.1.10
mibs: [mib-2, if-mib]
discovery:
seeds: [192.168.1.10, 192.168.1.11]
profile:
version: 2c
community: "${env:SNMP_COMMUNITY}"
allowed_cidrs: [192.168.0.0/16]
max_devices: 200
max_hops: 4
max_devices is a hard cap on discovered devices and max_hops bounds
the crawl depth from the seeds. governance_rules stamps governance on
devices as the crawl finds them (see below).
The governance parameter of this probe and the governance block every
probe entry accepts (see Governance per probe)
share one vocabulary. Both stamp the polled device; on a key present in both,
the probe-level parameter wins, and a matched discovery rule wins over either.
Governance for discovered devices¶
A crawl finds devices you never listed, so their ownership, criticality and
location cannot be written per device. governance_rules states them by
match instead: the first rule whose conditions all hold stamps its
governance block on the device's entity.
discovery:
seeds: ["10.0.0.1"]
allowed_cidrs: ["10.0.0.0/16"]
governance_rules:
- match:
cidr: "10.0.10.0/24"
governance:
criticality: critical
owner:
team: network
location:
site: paris
- match:
vendor: "cisco"
sysname: "^edge-" # regular expression
governance:
criticality: high
match accepts cidr, vendor and sysname — all optional, all must hold
for the rule to apply, and a rule with no match applies to every device.
The governance block takes the same keys as the per-agent one: criticality,
lifecycle, owner, location and free-form labels.
Metrics¶
One series per device (instance tag); interface metrics add
if_index.
| Metric | Type | Description |
|---|---|---|
senhub.snmp.up |
gauge | 1 when the device answered this cycle, 0 when not |
senhub.snmp.poll.duration |
gauge | Wall-clock poll time |
snmp.sys.uptime |
gauge | Device uptime |
snmp.interface.in_octets / out_octets |
counter | Interface traffic |
snmp.interface.in_errors / out_errors |
counter | Interface errors |
snmp.interface.in_discards / out_discards |
counter | Interface discards |
snmp.interface.speed |
gauge | Negotiated interface speed |
snmp.interface.admin_status / oper_status |
gauge | Interface status (up=1) |
An unreachable device is a measurement (senhub.snmp.up = 0), never
a probe failure — the agent keeps polling.
Operational notes¶
- v2c and v3. SNMPv1 is rejected at startup (no GETBULK). The SNMP trap receiver accepts v2c and v3 as well.
- No network MIB fetching. The probe never downloads MIB files
at runtime. Built-in modules cover MIB-2 and IF-MIB; everything
else goes through
custom_mappings. - Two rails, two cadences. Metrics poll at
interval; topology and entity sweeps run at the slowertopology_intervalso a dense sweep never delays traffic counters. - Counters are raw.
in_octetsand friends are emitted as counters; compute rates in the backend (rate(snmp_interface_in_octets[5m])in VictoriaMetrics).
Metric reference¶
Every metric this probe can emit. Metric is the OpenTelemetry name the
OTLP, Prometheus and Zabbix outputs derive theirs from. Name is what a
Nagios check and the API metrics= filter match.
PRTG channel is the label PRTG shows, placeholders filled from the
series' tags.
| Metric | Name | PRTG channel | Unit | Description |
|---|---|---|---|---|
senhub.snmp.up |
senhub.snmp.up |
SNMP {instance} Reachability | # | 1 when the last poll reached the SNMP target, 0 otherwise |
senhub.snmp.poll.duration |
senhub.snmp.poll.duration |
SNMP {instance} Poll Duration | s | Wall-clock time taken by the last poll cycle |
snmp.sys.uptime |
snmp.sys.uptime |
SNMP {instance} Uptime | centiseconds | sysUpTime — time since the network management portion of the system was last re-initialized (hundredths of a second) |
snmp.interface.in_octets |
snmp.interface.in_octets |
SNMP {instance} IF{if_index} In Octets | bytes | ifInOctets — total octets received on the interface |
snmp.interface.out_octets |
snmp.interface.out_octets |
SNMP {instance} IF{if_index} Out Octets | bytes | ifOutOctets — total octets transmitted on the interface |
snmp.interface.in_errors |
snmp.interface.in_errors |
SNMP {instance} IF{if_index} In Errors | # | ifInErrors — inbound packets containing errors |
snmp.interface.out_errors |
snmp.interface.out_errors |
SNMP {instance} IF{if_index} Out Errors | # | ifOutErrors — outbound packets that could not be transmitted due to errors |
snmp.interface.in_discards |
snmp.interface.in_discards |
SNMP {instance} IF{if_index} In Discards | # | ifInDiscards — inbound packets discarded though no error was detected |
snmp.interface.out_discards |
snmp.interface.out_discards |
SNMP {instance} IF{if_index} Out Discards | # | ifOutDiscards — outbound packets discarded though no error was detected |
snmp.interface.speed |
snmp.interface.speed |
SNMP {instance} IF{if_index} Speed | bits/s | ifSpeed — nominal bandwidth of the interface in bits per second |
snmp.interface.admin_status |
snmp.interface.admin_status |
SNMP {instance} IF{if_index} Admin Status | # | ifAdminStatus — desired interface state (1=up, 2=down, 3=testing) |
snmp.interface.oper_status |
snmp.interface.oper_status |
SNMP {instance} IF{if_index} Oper Status | # | ifOperStatus — current operational interface state (1=up, 2=down, 3=testing, ...) |