src/go/plugin/go.d/collector/snmp/profile-format.md
An SNMP profile defines how a specific class of devices is monitored through SNMP.
:::info
SNMP profiles are reusable and declarative — you never need to modify the collector source code to support new devices.
:::
It tells the Netdata SNMP collector:
Profiles make it possible to describe entire device families (switches, routers, UPSes, firewalls, printers, etc.) declaratively — so you don’t need to hard-code logic in Go or manually define metrics for each device.
Each profile is a single YAML file that can be reused, extended, and combined.
When Netdata connects to an SNMP device, the collector:
Profile Lifecycle
┌──────────────────────┐
│ SNMP Device │ → provides sysObjectID/sysDescr
└──────────┬───────────┘
↓
┌──────────────────────┐
│ selector │ → matches device profile
├──────────────────────┤
│ extends │ → inherits base profiles
├──────────────────────┤
│ metadata │ → device info (vendor, model, etc.)
├──────────────────────┤
│ metrics │ → OIDs to collect
├──────────────────────┤
│ topology │ → OIDs to collect for SNMP topology
├──────────────────────┤
│ metric_tags │ → dynamic tags for all metrics
├──────────────────────┤
│ static_tags │ → fixed tags for all metrics
├──────────────────────┤
│ virtual_metrics │ → calculated or aggregated metrics
└──────────┬───────────┘
↓
┌──────────────────────┐
│ Netdata charts & UI │ → visualized in dashboard
└──────────────────────┘
# example-device.yaml
# selects which devices this profile applies to.
selector:
- sysobjectid:
include: ["1.3.6.1.4.1.9.*"] # Cisco devices
sysdescr:
include: ["IOS"]
# imports common base metrics
extends:
- _system-base.yaml
- _std-if-mib.yaml
# defines device-level labels (virtual node)
metadata:
device:
fields:
vendor:
value: "Cisco"
model:
symbol:
OID: 1.3.6.1.2.1.47.1.1.1.1.2.1
name: entPhysicalModelName
# specifies which OIDs to collect
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.2.2
name: ifTable
symbols:
- OID: 1.3.6.1.2.1.2.2.1.10
name: ifInOctets
chart_meta:
description: Interface inbound traffic
family: 'Network/Interface/Traffic/In'
unit: "bit/s"
scale_factor: 8
metric_tags:
- tag: interface
symbol:
OID: 1.3.6.1.2.1.31.1.1.1.1
name: ifName
# add dynamic tags to all metrics
metric_tags:
- tag: fs_sys_version
symbol:
OID: 1.3.6.1.4.1.9.2.1.73.0
name: fsSysVersion
# add fixed tags to all metrics
static_tags:
- tag: region
value: "us-east-1"
- tag: environment
value: "production"
# computes combined metrics
virtual_metrics:
- name: ifTotalTraffic
sources:
- { metric: _ifHCInOctets, table: ifXTable, as: in }
- { metric: _ifHCOutOctets, table: ifXTable, as: out }
chart_meta:
description: Total traffic across all interfaces
family: 'Network/Total/Traffic'
unit: "bit/s"
Each SNMP profile is a YAML file that defines how Netdata collects, interprets, and labels SNMP metrics from a device.
Profiles are modular — you can extend others, define metadata, and specify what to collect.
selector: <device matching pattern>
extends: <base profiles to include>
metadata: <device information>
metrics: <what to collect>
topology: <what to collect for topology>
bgp: <what to collect for typed BGP monitoring>
licensing: <what to collect for typed licensing>
metric_tags: <global tags>
static_tags: <static tags>
virtual_metrics: <calculated metrics>
| Section | Purpose |
|---|---|
| selector | Defines which devices the profile applies to. |
| extends | Inherits and merges other base profiles. |
| metadata | Collects device-level information (host labels). |
| metrics | Defines which OIDs to collect and how to chart them. |
| topology | Defines SNMP topology rows and their topology kind. |
| bgp | Defines typed BGP device, peer, and peer-family rows. |
| licensing | Defines typed license rows. |
| metric_tags | Defines global dynamic tags collected once per device and attached to all metrics. |
| static_tags | Defines fixed tags applied to all metrics. |
| virtual_metrics | Defines calculated or aggregated metrics based on others. |
You use the selector to:
During discovery, Netdata evaluates all profiles; any profile whose selector matches a device is **applied **.
selector:
- sysobjectid:
include: ["1.3.6.1.4.1.9.*"] # regex: Cisco enterprise OID subtree
exclude: ["1.3.6.1.4.1.9.9.666"] # optional excludes
sysdescr:
include: ["IOS"] # substring (case-insensitive)
exclude: ["emulator", "lab"] # optional excludes
How it works:
sysobjectid, sysdescr, etc.).selector list matches the device.sysobjectid and sysdescr are defined within the same rule, both must succeed.Supported conditions:
| Key | What It Checks | Match Criteria (Pass) | Fails When... |
|---|---|---|---|
sysobjectid.include | Device sysObjectID | Matches at least one pattern in the list. | No items match. |
sysobjectid.exclude | Device sysObjectID | Matches none of the listed patterns. | Any item matches. |
sysdescr.include | Device sysDescr (case-insensitive) | Contains at least one substring in the list. | No listed substrings are found. |
sysdescr.exclude | Device sysDescr (case-insensitive) | Contains none of the listed substrings. | Any listed substring is found. |
Use extends to inherit metrics, tags, and metadata from another profile instead of duplicating common metrics — perfect for vendor-specific variations of a base MIB.
Most real profiles extend a few shared building blocks and then add device-specific definitions.
extends:
- _system-base.yaml # System basics (uptime, contact, location)
- _std-if-mib.yaml # Network interfaces (IF-MIB)
- _std-ip-mib.yaml # IP statistics
The final profile is the merged result of all inherited profiles plus the content in the current file.
How inheritance works:
Metric override identity depends on metric type:
symbol.name + symbol.OID. This preserves same-name scalar fallback definitions that try alternative OIDs.table.name when set, otherwise table.OID) + symbol.name. If two inherited profiles define the same table metric name, the later profile wins even when the table or symbol OID differs. If the same logical table name is inherited with a different table OID, the later table definition replaces the earlier table definition; symbols from the earlier table OID are not merged into the later table. Different metric names can still read from the same column OID when separate transformations are needed.Common base profiles
| Profile | Provides | Typical Use |
|---|---|---|
_system-base.yaml | Basic system info (uptime, name, contact) | All devices |
_std-if-mib.yaml | Interface statistics (IF-MIB) | Network devices |
_std-ip-mib.yaml | IP-level statistics (IP-MIB) | Routers, switches |
_std-tcp-mib.yaml | TCP statistics | Servers, firewalls |
_std-udp-mib.yaml | UDP statistics | Servers, firewalls |
_std-ups-mib.yaml | Power and UPS metrics | UPS devices |
The metadata section defines device-level information (not metric tags).
It is collected once per device and populates the device’s host labels in Netdata (the “virtual node” labels shown on the device page).
It always follows the structure metadata → device → fields, where each field defines a single label.
Each field can be:
value: is a fixed string.symbol — a single OID to read from.symbols — an ordered list of OIDs to try, first non-empty wins.metadata:
device:
fields:
vendor:
value: "Cisco" # static label
model:
symbols: # dynamic label with fallback
- OID: 1.3.6.1.4.1.9.3.6.3.0
name: ciscoModelA
- OID: 1.3.6.1.2.1.47.1.1.1.1.13.1
name: entPhysicalModelName
How it works:
vendor is set statically to "Cisco".model is collected dynamically. The collector tries the listed OIDs in order and uses the first one that returns a non-empty value.consumers: [metrics] or consumers: [topology] only when a field is
intentionally limited to one view.:::tip
See Tag Transformation for supported transformations and syntax examples.
:::
The metrics section defines what data to collect from the device — which OIDs to query, how to interpret them, and how to display them as charts in Netdata.
Metrics can be:
:::note
A metric is either scalar (single value) or table-based (multiple rows). Never mix both in the same metric entry.
:::
The collector automatically uses SNMP GET for scalars and SNMP BULKWALK for tables.
metrics:
- MIB: HOST-RESOURCES-MIB
symbol:
OID: 1.3.6.1.2.1.1.3.0
name: systemUptime
scale_factor: 0.01 # Value is in hundredths of a second
chart_meta:
description: Time since the system was last rebooted or powered on
family: 'System/Uptime'
unit: "s"
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.31.1.1
name: ifXTable
symbols:
- OID: 1.3.6.1.2.1.31.1.1.1.6
name: ifHCInOctets
chart_meta:
description: Traffic
family: 'Network/Interface/Traffic/In'
unit: "bit/s"
scale_factor: 8 # Octets → bits
metric_tags:
- tag: interface
symbol:
OID: 1.3.6.1.2.1.31.1.1.1.1
name: ifName
How it works:
symbol, while tables define a table and one or more symbols.extract_value, scale_factor, etc.) and chart metadata.metric_tags) to identify rows by interface, disk, or other attributes.:::tip
See also
:::
Metric names that start with an underscore (e.g., _ifHCInOctets) are private: they’re collected but not propagated to the SNMP collector output. Use them as internal building blocks (typically as inputs for virtual_metrics) so the final metric set remains clean. After virtual metrics are computed, the collector drops underscored metrics from the exported set, while preserving them in the internal hidden metric set for collector-level consumers.
# IF-MIB::ifXTable
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.31.1.1
name: ifXTable
symbols:
- { OID: 1.3.6.1.2.1.31.1.1.1.6, name: _ifHCInOctets, scale_factor: 8 }
- { OID: 1.3.6.1.2.1.31.1.1.1.10, name: _ifHCOutOctets, scale_factor: 8 }
virtual_metrics:
- name: ifTraffic
per_row: true
group_by: ["interface"]
sources:
- { metric: _ifHCInOctets, table: ifXTable, as: in }
- { metric: _ifHCOutOctets, table: ifXTable, as: out }
The topology section defines SNMP rows consumed by the SNMP topology collector.
Topology rows are collected through the same scalar and table mechanics as
regular metrics, but they are not exported as charts. Instead, each row is routed
to a topology handler through its closed kind value.
Use top-level topology: when the row describes a topology actor, link, VLAN,
bridge, FDB, ARP, LLDP, CDP, STP, VTP, or interface-mapping observation.
topology:
- kind: lldp_rem
MIB: LLDP-MIB
table:
OID: 1.0.8802.1.1.2.1.4.1
name: lldpRemTable
symbols:
- OID: 1.0.8802.1.1.2.1.4.1.1.6
name: lldp_rem
metric_tags:
- tag: lldp_loc_port_num
index: 2
- tag: lldp_rem_index
index: 3
- tag: lldp_rem_sys_name
symbol:
OID: 1.0.8802.1.1.2.1.4.1.1.9
name: lldpRemSysName
Rules:
kind is required and must be one of the closed topology kinds below._.chart_meta,
metric_type, mapping, transform, scale_factor, format, or
constant_value_one on the row value symbol.metric_tags inside a topology row work like table metric tags and identify
or enrich the topology row.systemUptime stays under metrics: for regular SNMP collection. It is not a
topology kind and should not be declared under topology:.Valid topology kinds:
lldp_loc_port
lldp_loc_man_addr
lldp_rem
lldp_rem_man_addr
lldp_rem_man_addr_compat
cdp_cache
if_name
if_status
if_duplex
ip_if_index
bridge_port_if_index
fdb_entry
qbridge_fdb_entry
qbridge_vlan_entry
stp_port
vtp_vlan
arp_entry
arp_legacy_entry
Topology mixins can be inherited through extends just like metric mixins. When
two inherited topology rows collide, the identity is kind + table identity + symbol name, matching regular table metric merge behavior.
You can express “try this OID, otherwise try that OID” by declaring multiple scalar metrics with the same symbol.name, each pointing to a different OID. At runtime the collector GETs all declared scalar OIDs, marks missing ones, and emits the metric from whichever OID returns data. Missing OIDs are skipped cleanly.
metrics:
- MIB: HOST-RESOURCES-MIB
symbol:
OID: 1.3.6.1.2.1.25.1.1.0
name: systemUptime
scale_factor: 0.01
chart_meta:
description: Time since the system was last rebooted or powered on.
family: 'System/Uptime'
unit: "s"
- MIB: HOST-RESOURCES-MIB
symbol:
OID: 1.3.6.1.2.1.1.3.0
name: systemUptime
scale_factor: 0.01
chart_meta:
description: Time since the system was last rebooted or powered on.
family: 'System/Uptime'
unit: "s"
The metric_tags section defines global dynamic tags — values collected once from the device and applied to every metric in the profile.
They are evaluated during collection, just like other SNMP symbols, and remain the same for all metrics within that device.
Typical uses:
metric_tags:
- tag: fs_sys_serial
symbol:
OID: 1.3.6.1.4.1.12356.106.1.1.1.0
name: fsSysSerial
- tag: fs_sys_version
symbol:
OID: 1.3.6.1.4.1.12356.106.4.1.1.0
name: fsSysVersion
How it works:
metric_tags are available to both regular metrics and topology by
default. In topology they become device/profile labels, not per-row dispatch
keys. Use consumers: [metrics] or consumers: [topology] only when a tag is
intentionally limited to one view.:::tip
See Tag Transformation for supported transformations and syntax examples.
:::
Tag names that start with an underscore (e.g., _if_type) are emitted as labels but are ignored for chart-ID composition by the SNMP collector that consumes these metrics. Use underscore tags to keep chart IDs short when another tag already guarantees uniqueness (for example, interface). (You still get the underscore-tag value as a chart label.)
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.31.1.1
name: ifXTable
symbols:
- OID: 1.3.6.1.2.1.31.1.1.1.6
name: ifHCInOctets
chart_meta:
description: Traffic
family: 'Network/Interface/Traffic/In'
unit: "bit/s"
scale_factor: 8
metric_tags:
- tag: interface
symbol: { OID: 1.3.6.1.2.1.31.1.1.1.1, name: ifName }
- tag: _if_type
table: ifTable
symbol: { OID: 1.3.6.1.2.1.2.2.1.3, name: ifType }
mapping:
1: "other"
6: "ethernet"
24: "loopback"
131: "tunnel"
161: "lag"
If you declare the same tag name multiple times, tags are evaluated in order and the first non-empty value is kept. This lets you fall back from a preferred column to an alternative. (Internally, the tag adder only sets a tag if it isn’t already set or is empty.)
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.31.1.1
name: ifXTable
symbols:
- OID: 1.3.6.1.2.1.31.1.1.1.6
name: ifHCInOctets
chart_meta:
description: Traffic
family: 'Network/Interface/Traffic/In'
unit: "bit/s"
scale_factor: 8 # Octets → bits
metric_tags:
- tag: interface
symbol: { OID: 1.3.6.1.2.1.31.1.1.1.1, name: ifName } # preferred
- tag: interface
table: ifTable
symbol: { OID: 1.3.6.1.2.1.2.2.1.2, name: ifDescr } # fallback
The static_tags section defines fixed key–value pairs that are attached to every metric collected by the profile.
They don’t depend on SNMP data and remain constant for all devices using the profile.
Typical uses:
environment, region, or service).static_tags:
- tag: environment
value: production
- tag: region
value: us-east-1
- tag: service
value: network
How it works:
metric_tags.The virtual_metrics section defines calculated metrics built from other metrics already collected by the profile.
They don’t query SNMP directly — instead, they reuse existing metric values to produce totals, sums, or fallbacks.
:::tip
See Virtual Metrics for the complete reference, configuration options, and advanced examples.
:::
Typical uses:
in + out traffic or errors). - name: ifTotalTraffic
sources:
- { metric: ifHCInOctets, table: ifXTable, as: in }
- { metric: ifHCOutOctets, table: ifXTable, as: out }
chart_meta:
description: Total traffic across all interfaces
family: 'Network/Total/Traffic'
unit: "bit/s"
How it works:
ifTotalTraffic.ifHCInOctets, ifHCOutOctets) as sources.as field names the resulting dimensions (in, out).This section explains how SNMP data is structured and how it maps to metrics in a Netdata profile.
SNMP data is organized as a hierarchical tree of numeric identifiers called OIDs (Object Identifiers).
Each OID uniquely identifies a value on a device — similar to a file path in a filesystem.
1.3.6.1.2.1.1.3.0
│ │ │ │ │ │ │ └── Instance (0 = scalar)
│ │ │ │ │ │ └──── Object (3 = sysUpTime)
│ │ │ │ │ └────── Branch: system (MIB-2)
│ │ │ └────────── MIB-2 root
└─ SNMP global prefix
MIBs (Management Information Bases) are named collections of related OIDs.
Examples: IF-MIB (interfaces), IP-MIB (IP statistics), HOST-RESOURCES-MIB (system info).
Each OID maps to a typed value, such as Counter64, Gauge32, Integer, or TimeTicks.
Some OIDs represent single values (scalars), while others represent tables of related values (rows).
Scalar metrics represent a single value for the entire device.
Their OIDs always end with .0, which denotes the instance number for a scalar object.
metrics:
- MIB: HOST-RESOURCES-MIB
symbol:
OID: 1.3.6.1.2.1.1.3.0
name: systemUptime
scale_factor: 0.01 # Value is in hundredths of a second
chart_meta:
description: Time since the system was last rebooted or powered on.
family: 'System/Uptime'
unit: "s"
What this does:
sysUpTime value once per device..0 at the end indicates there is only one instance of this value.Table metrics represent lists of related values, such as one entry per network interface, disk, or CPU.
Each row in a table is identified by an index appended to the base OID — for example:
ifHCInOctets.1 = 1024
ifHCInOctets.2 = 2048
.1, .2, … are row indexes that identify the instance (e.g., interface #1, interface #2).Table metrics must define at least one tag (
metric_tags) to identify each row. Without tags, only a single row can be emitted.
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.31.1.1
name: ifXTable
symbols:
- OID: 1.3.6.1.2.1.31.1.1.1.6
name: ifHCInOctets
chart_meta:
description: Traffic
family: 'Network/Interface/Traffic/In'
unit: "bit/s"
scale_factor: 8 # Octets → bits
metric_tags:
- tag: interface
symbol:
OID: 1.3.6.1.2.1.31.1.1.1.1
name: ifName
How Table Metrics Expand into Rows
SNMP Table: ifTable
───────────────────────────────────────────────
Index | ifName | ifHCInOctets
───────────────────────────────────────────────
1 | eth0 | 1024
2 | eth1 | 2048
───────────────────────────────────────────────
metric_tags:
- tag: interface
symbol:
OID: 1.3.6.1.2.1.31.1.1.1.1 # ifName
Resulting metrics:
───────────────────────────────────────────────
ifHCInOctets{interface="eth0"} = 1024
ifHCInOctets{interface="eth1"} = 2048
───────────────────────────────────────────────
How it works:
ifHCInOctets and ifName) from the same table.1, 2, …).What this does:
ifHCInOctets) from each interface.ifName) from the same index.ifHCInOctets{interface="eth0"} = 1024
ifHCInOctets{interface="eth1"} = 2048
Each SNMP value has a data type that determines how Netdata interprets and displays it.
The collector automatically detects the appropriate metric type (e.g., gauge or rate), but you can override it manually.
Automatic Type Detection
| SNMP Type | Default Netdata Type | Typical Use |
|---|---|---|
Counter32, Counter64 | rate | Network traffic, packet counters |
Gauge32, Integer | gauge | Temperatures, usage levels, statuses |
TimeTicks | gauge | Uptime, time-based values |
Overriding the Metric Type
You can explicitly set a metric’s type using the metric_type field inside a symbol definition.
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.2.2
name: ifTable
symbols:
- OID: 1.3.6.1.2.1.2.2.1.10
name: ifInOctets
metric_type: gauge # Override default 'rate'
What this does:
ifInOctets to be treated as a gauge (instantaneous value) instead of a rate.Counter types are automatically converted to per-second rates.Each metric or virtual metric can include an optional chart_meta block that defines how it appears in Netdata charts.
This metadata does not affect data collection — it only controls how the chart is named and grouped in the Netdata dashboard.
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.2.2
name: ifTable
symbols:
- OID: 1.3.6.1.2.1.2.2.1.10
name: ifInOctets
chart_meta:
description: Inbound network traffic
family: 'Network/Interface/Traffic/In'
unit: "bit/s"
| Field | Type | Required | Description |
|---|---|---|---|
description | string | no | Human-readable description shown in dashboards and alerts. |
family | string | no | Chart grouping path (slashes / define hierarchy). Helps organize charts by system or subsystem. |
unit | string | no | Display unit, e.g. "bit/s", "%", "{status}", "Cel". |
type | string | no | Optional chart style override: line, area, or stacked. Defaults depend on metric type. |
Tags add context and identity to SNMP metrics.
They let you distinguish between instances (for example, which interface, disk, or IP) and allow filtering and grouping in the Netdata UI.
The collector:
Key Concepts:
| Concept | Description |
|---|---|
| Table metrics must have tags | Each table row must be uniquely identified by at least one tag (for example, interface name or index). Without tags, only one row is emitted. |
| Scalar metrics don’t need tags for identity | Scalars represent one value for the entire device, so tags are not part of their normal public identity contract. |
| Static tags | Fixed values that never change (for example, datacenter, environment). |
| Dynamic tags | Extracted from SNMP data — from table columns, related tables, or row indexes. |
| Global tags | Defined in the profile’s top-level metric_tags section and applied to all metrics. |
Tag Types and Available Transformations:
| Tag Type | Description | Supported Transformations |
|---|---|---|
| Static | Fixed tags with constant values. | None (value is fixed). |
| Same-Table | Values from columns in the same table as the metric. | mapping, extract_value, match_pattern + match_value, match + tags |
| Cross-Table | Values from another table. | mapping, extract_value, match_pattern + match_value, match + tags |
| Index-Based | Values derived from the OID index of each row. | mapping (optional) |
| Index Transform | Adjusts multi-part indexes so cross-table tags align correctly. | — (structural, not a transformation) |
Summary:
metric_tags) to distinguish rows.mapping, extract_value, match_pattern, match + tags) can modify or extract parts of raw values.sysName, address,
vendor, model, and device_type for row tags. Those keys are reserved
for collector-provided device metadata labels.How the Collector Matches Values and Tags:
SNMP Table (ifTable)
───────────────────────────────────────────────
Index | ifDescr | ifInOctets
───────────────────────────────────────────────
1 | eth0 | 1024
2 | eth1 | 2048
───────────────────────────────────────────────
metric_tags:
- tag: interface
symbol:
OID: 1.3.6.1.2.1.2.2.1.2 # ifDescr
Resulting metrics:
───────────────────────────────────────────────
ifInOctets{interface="eth0"} = 1024
ifInOctets{interface="eth1"} = 2048
───────────────────────────────────────────────
How it works:
ifInOctets (value) and ifDescr (tag source).1, 2, …).Cross-Table Example:
SNMP Tables: ifTable + ifXTable
───────────────────────────────────────────────
ifTable.ifInOctets.1 = 1024
ifTable.ifInOctets.2 = 2048
ifXTable.ifName.1 = "eth0"
ifXTable.ifName.2 = "eth1"
───────────────────────────────────────────────
metric_tags:
- tag: interface
table: ifXTable
symbol:
OID: 1.3.6.1.2.1.31.1.1.1.1 # ifName
Result:
───────────────────────────────────────────────
ifInOctets{interface="eth0"} = 1024
ifInOctets{interface="eth1"} = 2048
───────────────────────────────────────────────
How it works:
ifTable but fetches tag values from ifXTable..1, .2, …).interface tag is populated from ifXTable.ifName for each matching rowStatic tags define fixed key–value pairs that are attached to metrics without being collected from SNMP.
They are useful for identifying environment, location, or other context that applies to all collected data.
Profile-level static tags apply to all metrics defined in the profile.
# Global static tags (applied to all metrics)
static_tags:
- tag: datacenter
value: "DC1"
- tag: environment
value: "production"
What this does:
Typical use cases:
Metric-level static tags apply to specific metrics only.
# Metric-specific static tags
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.2.2
name: ifTable
symbols:
- OID: 1.3.6.1.2.1.2.2.1.10
name: ifInOctets
static_tags:
- tag: "source"
value: "snmp"
- tag: "interface_type"
value: "physical"
What this does:
source=snmp and interface_type=physical only to the ifInOctets metric.Metric-level static tags are technically supported but rarely needed. In most cases, prefer profile-level
static_tagsfor consistency and simplicity.
Same-table tags extract values from columns in the same SNMP table as the metric.
They are the most common way to label per-row metrics with identifiers like interface names or indexes.
The collector:
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.2.2
name: ifTable
symbols:
- OID: 1.3.6.1.2.1.2.2.1.10
name: ifInOctets
metric_tags:
- tag: interface
symbol:
OID: 1.3.6.1.2.1.2.2.1.2
name: ifDescr
What this does:
ifInOctets (input bytes) for each row in ifTable.ifDescr column from the same table to label each row.ifInOctets{interface="eth0"} = 1000
ifInOctets{interface="eth1"} = 2000
Cross-table tags let you use data from another SNMP table as a tag source.
The collector:
table: instead of the current one.index_transform can modify the current table’s index to align it with the target.lookup_symbol can match a transformed index value against a column in the target table and then read tags from the matched row.Two tables are said to have the same index when their row identifiers (OID suffixes after the base OID) are identical — meaning they describe the same entity.
In practice, this means that the row number (index) in one table corresponds directly to the same row in another.
For example:
ifTable.ifInOctets.2 = 123456
ifXTable.ifName.2 = "xe-0/0/1"
Both OIDs end with .2, so they refer to the same interface.
This allows you to use ifName (from ifXTable) as a tag for metrics collected from ifTable.
metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.2.2
name: ifTable
symbols:
- OID: 1.3.6.1.2.1.2.2.1.10
name: ifInOctets
metric_tags:
- tag: interface
table: ifXTable
symbol:
OID: 1.3.6.1.2.1.31.1.1.1.1
name: ifName
What this does:
ifInOctets from ifTable.ifXTable (e.g., .2).ifName as the interface tag.ifInOctets{interface="xe-0/0/1"} = 123456
Some tables describe related data but use different index structures — meaning their OID suffixes don’t line up directly.
For example, in ipIfStatsTable the index contains two parts:
ipIfStatsTable.ipIfStatsHCInOctets.2.1 = 38560
ipIfStatsTable.ipIfStatsHCInOctets.2.2 = 44408
Here:
2) is the IP version (e.g., 2 = IPv4, 3 = IPv6).1, 2, 3, …) is the interface index.ifXTable, on the other hand, uses only the interface index (1, 2, 3, …).Because the indexes differ, they can’t be matched directly.
To fix this, use index_transform to select only the relevant part of the index so it matches the target table’s format.
metrics:
- MIB: IP-MIB
table:
OID: 1.3.6.1.2.1.4.31.3
name: ipIfStatsTable
symbols:
- OID: 1.3.6.1.2.1.4.31.3.1.6
name: ipIfStatsHCInOctets
chart_meta:
description: Total inbound IP octets (including errors)
family: 'Network/Interface/IP/Traffic/Total/In'
unit: "bit/s"
scale_factor: 8
metric_tags:
- tag: _interface
table: ifXTable
symbol:
OID: 1.3.6.1.2.1.31.1.1.1.1
name: ifName
index_transform:
- start: 1
end: 1
What this does:
ipIfStatsTable.start: 1, end: 1) from 2.1 → becomes 1.ifXTable to find the corresponding ifName.ipIfStatsHCInOctets{_interface="xe-0/0/1"} = 38560
index_transform Worksindex_transform tells the collector which parts of the current table’s index to keep when matching rows across tables.
| Concept | Example |
|---|---|
| Original index | 2.1 (from ipIfStatsTable) → [ipVersion, ifIndex] |
| Target index | 1 (from ifXTable) |
| Transform | index_transform: [ { start: 1, end: 1 } ] |
| Result | The collector keeps only the second element (ifIndex = 1), which now matches ifXTable |
In short:
start and end positions are zero-based (0 = first index element).drop_right can be used instead of end when you need to keep a variable-length prefix of the index and trim a fixed number of trailing elements.Example with drop_right:
metric_tags:
- tag: peer_index
table: peerTable
symbol:
OID: 1.2.3.4.5
name: peerRemoteAs
index_transform:
- start: 0
drop_right: 2
If the current row index is 1.192.0.2.1.1.128, the transform keeps 1.192.0.2.1 and drops the trailing AFI / SAFI pair.
lookup_symbol)Some vendor MIBs split related data across tables that do not share the same row index.
For example:
peerIndex.afi.safiroutingInstance.localAddr.remoteAddrpeerIndex columnIn that case, index_transform alone is not enough:
peerIndexpeerIndexUse lookup_symbol to tell the collector:
lookup_symbol column matches that valuesymbol from that matched rowmetrics:
- MIB: BGP4-V2-MIB-JUNIPER
table:
OID: 1.3.6.1.4.1.2636.5.1.1.2.6.2
name: jnxBgpM2PrefixCountersTable
symbols:
- OID: 1.3.6.1.4.1.2636.5.1.1.2.6.2.1.8
name: bgpPeerPrefixesAccepted
metric_tags:
- tag: neighbor
table: jnxBgpM2PeerTable
symbol:
OID: 1.3.6.1.4.1.2636.5.1.1.2.1.1.1.11
name: jnxBgpM2PeerRemoteAddr
lookup_symbol:
OID: 1.3.6.1.4.1.2636.5.1.1.2.1.1.1.14
name: jnxBgpM2PeerIndex
index_transform:
- start: 0
end: 0
What this does:
peerIndex)jnxBgpM2PeerTable.jnxBgpM2PeerIndex for a matching valuejnxBgpM2PeerRemoteAddr from the matched peer-table rowbgpPeerPrefixesAccepted{neighbor="192.0.2.1"} = 1234
Important behavior:
lookup_symbol is used for cross-table tags and typed BGP value fieldsindex_transform, not instead of itIndex-based tags extract values directly from the OID index of the SNMP table rather than from a column.
This is useful when a table encodes identifiers (like method, code, or port number) as part of the OID itself instead of storing them in separate columns.
The collector:
index: rule, assigns a tag using the specified position in the index.index_transform plus symbol.format, symbol.extract_value, symbol.match_pattern, or mapping, even when there is no column OID for that tag.metrics:
- MIB: SIP-COMMON-MIB
table:
name: sipCommonStatusCodeTable
OID: 1.3.6.1.2.1.149.1.5.1
symbols:
- OID: 1.3.6.1.2.1.149.1.5.1.1.3
name: sipCommonStatusCodeIns
chart_meta:
family: 'Network/VoIP/SIP/Response/StatusCode/In'
description: Total number of response messages received with the specified status code
unit: "{response}/s"
metric_tags:
- index: 1
tag: applIndex
- index: 2
tag: sipCommonStatusCodeMethod
- index: 3
tag: sipCommonStatusCodeValue
What this does:
1.3.6.1.2.1.149.1.5.1.1.3.1.6.200
applIndex=1
sipCommonStatusCodeMethod=6
sipCommonStatusCodeValue=200
sipCommonStatusCodeIns{applIndex="1", sipCommonStatusCodeMethod="6", sipCommonStatusCodeValue="200"} = 42
Derived tag example from a transformed index:
metric_tags:
- tag: neighbor
symbol:
name: peerRemoteAddrIndex
format: ip_address
index_transform:
- start: 1
drop_right: 2
If the current row index is 1.192.0.2.1.1.128, the collector:
192.0.2.1neighbor="192.0.2.1"SNMP profile symbols must only read objects that the source MIB exposes as
readable columns. Before adding or changing a symbol.OID, check the source
MIB object's MAX-ACCESS (SMIv2) or ACCESS (SMIv1).
Rules:
read-only, read-write, and read-create objects can be read as
symbol.OID values.not-accessible objects must not be read as symbol.OID values.not-accessible object that is part of a table INDEX can be derived from
the row OID index using index or index_transform.symbol.format.The two index extraction mechanisms use different counting bases:
index: N is 1-based: index: 1 selects the first index component,
index: 2 the second, and so on. Use this to pick a single component.index_transform: [{start: M, end: K}] is 0-based over the row index
parts. start: 0 is the first component. end is inclusive. Setting
end: 0 together with start: N > 0 slices to the tail (start: N to the
last index part) - useful for length-prefixed OCTET STRING index columns
whose width depends on a sibling index component (e.g.
LLDP-MIB::lldpLocManAddr, IP-MIB::ipNetToPhysicalNetAddress).So index: 1 and index_transform: [{start: 0, end: 0}] both extract the
first index component.
Typed BGP value fields also support index_from_end: N, where N is
1-based from the right side of the row index. Use it only when the target
INDEX component is a trailing component after a variable-length field, such as
AFI/SAFI after an InetAddress peer address.
Use exactly one row-index selector per typed BGP value: index,
index_from_end, or index_transform. Profile validation rejects typed BGP
values that set more than one of these selectors.
Typed BGP cross-table value fields can also use lookup_symbol with
table: and index_transform:. This is needed when a BGP peer-family table is
indexed by a compact peer ID, but peer identity fields such as neighbor and
remote AS live in a peer table keyed by a different composite index. The
collector extracts the lookup value from the current row index, finds the row
in the referenced table whose lookup_symbol column has that value, and then
reads the requested typed value symbol from the matched row.
Examples:
Q-BRIDGE-MIB::dot1qTpFdbAddress is not-accessible and is part of the
dot1qTpFdbEntry index. Derive it from the row index and use
format: mac_address.IP-MIB::ipNetToPhysicalIfIndex,
IP-MIB::ipNetToPhysicalNetAddressType, and
IP-MIB::ipNetToPhysicalNetAddress are not-accessible index components.
Derive them from the row index. The physical MAC value,
ipNetToPhysicalPhysAddress, is readable and can stay as a column symbol.LLDP-MIB::lldpLocManAddrSubtype and LLDP-MIB::lldpLocManAddr are
not-accessible index components. Anchor the row on a readable column such as
lldpLocManAddrLen, then derive subtype and address from the row index. Use
format: hex for the address bytes so non-IP management-address subtypes are
preserved; topology normalization converts IP-compatible bytes later.Audit recipe:
rg -n -C 4 'OBJECT-TYPE|MAX-ACCESS[[:space:]]+not-accessible|ACCESS[[:space:]]+not-accessible' path/to/MIB
rg -n 'name:[[:space:]]*(dot1qTpFdbAddress|ipNetToPhysicalIfIndex|ipNetToPhysicalNetAddressType|ipNetToPhysicalNetAddress|lldpLocManAddrSubtype|lldpLocManAddr)\b' src/go/plugin/go.d/config/go.d/snmp.profiles
Any profile hit for a not-accessible object is valid only when the tag is
index-derived and does not declare a symbol.OID for that object.
Tag transformations let you modify or extract parts of SNMP values to produce clear, human-readable tags.
They work the same in both places:
metadata (e.g., device model, OS name), andmetric_tags (e.g., per-row interface labels).Available Tag Transformations:
| Transformation | Purpose | Example Input → Output |
|---|---|---|
format | Convert the raw SNMP value before tag parsing. | 0x18fd74331a9c → "18fd74331a9c" |
mapping | Replace numeric/string codes with names. | 1 → "ethernet", 161 → "lag" |
extract_value | Extract a substring via regex (first group). | "RouterOS CCR2004-16G-2S+" → "CCR2004-16G-2S+" |
match_pattern + match_value | Replace the value using regex groups or static. | "Palo Alto Networks VM-Series firewall" → "VM-Series firewall" |
match + tags (multiple tags) | Create several tags from one value. | "xe-0/0/1" → if_family=xe, fpc=0, pic=0, port=1 |
Combination & Behavior:
| Rule | Description |
|---|---|
| Where | Can be used inside metadata.*.fields.*.symbols[] and metric_tags[]. |
| Order of application | 1️⃣ match_pattern + match_value or extract_value (whichever is present) → 2️⃣ mapping → 3️⃣ match + tags (if defined). |
| No match behavior | • extract_value: keeps the original value. |
• match_pattern: skips the value (tag not emitted). | |
• match + tags: emits no tags. | |
| Multiple symbols | If multiple symbols are listed for the same tag, the first non-empty result is used. |
| Mapping key consistency | Keys in a mapping must all be the same type — all numeric or all string. |
| Mapping modes | Tags and metadata support only exact-match mapping. Use mapping.items; mapping.mode is optional and defaults to exact. |
| Safety | Keep regexes simple and, when possible, anchor them (e.g. ^pattern$) to prevent unwanted matches. |
Quick Syntax Recap:
mapping
mapping:
items:
6: "ethernet"
161: "lag"
extract_value
extract_value: 'RouterOS ([A-Za-z0-9-+]+)' # first capture group is used
match_pattern + match_value
match_pattern: 'Palo Alto Networks\s+(PA-\d+ series firewall|VM-Series firewall)'
match_value: '$1' # or a static value like 'Router' when matched
match + tags (multiple tags)
match: '^([A-Za-z]+)[-_]?(\d+)\/(\d+)\/(\d+)$'
tags:
if_family: $1
fpc: $2
pic: $3
port: $4
format
symbol:
OID: 1.3.6.1.2.1.17.1.1
name: dot1dBaseBridgeAddress
format: hex
Use format when the raw SNMP value must be converted before tag or metadata processing.
The collector:
format when converting the raw SNMP value to a string.format values accepted by symbol definitions elsewhere in the profile.Where it can be used:
metadata.device.fields.<field>.symbolmetadata.device.fields.<field>.symbols[]metric_tags[].symbolCurrently used by this profile set:
format: hex for octet-string values such as:
Example:
metadata:
device:
fields:
bridge_base_address:
symbol:
OID: 1.3.6.1.2.1.17.1.1
name: dot1dBaseBridgeAddress
format: hex
Use mapping to replace raw tag values with human-readable text labels.
mapping.mode is optional here and defaults to exact. bitmask mode is not supported for tags or metadata.
The collector:
metadata or metric_tags.metrics:
- MIB: IF-MIB
table:
OID: 1.3.6.1.2.1.2.2
name: ifTable
symbols:
- OID: 1.3.6.1.2.1.2.2.1.10
name: ifInOctets
metric_tags:
- tag: if_type
symbol:
OID: 1.3.6.1.2.1.2.2.1.3
name: ifType
mapping:
items:
1: "other"
6: "ethernet"
24: "loopback"
131: "tunnel"
161: "lag"
What this does:
other, ethernet, loopback, tunnel, lag).metadata fields and metric_tags.mapping: { 6: "ethernet", 161: "lag" }Use extract_value to capture a part of a string using a regular expression.
The collector:
( … ).^ or $.symbols are defined.metadata:
device:
fields:
model:
symbols:
# Example: 'RouterOS CCR2004-16G-2S+' → 'CCR2004-16G-2S+'
- OID: 1.3.6.1.2.1.1.1.0
name: sysDescr
extract_value: 'RouterOS ([A-Za-z0-9-+]+)'
# Example: 'CSS326-24G-2S+ SwOS v2.13' → 'CSS326-24G-2S+'
- OID: 1.3.6.1.2.1.1.1.0
name: sysDescr
extract_value: '([A-Za-z0-9-+]+) SwOS'
Use match_pattern and match_value together to build a tag value using multiple regex capture groups.
The collector:
match_pattern.match_value.match_value, you can reference capture groups using $1, $2, $3, etc.Example 1 — Reformat using capture groups:
metadata:
device:
fields:
product_name:
symbol:
OID: 1.3.6.1.2.1.1.1.0
name: sysDescr
match_pattern: 'Palo Alto Networks\s+(PA-\d+ series firewall|WildFire Appliance|VM-Series firewall)'
match_value: "$1"
# Examples:
# - Palo Alto Networks VM-Series firewall → VM-Series firewall
# - Palo Alto Networks PA-3200 series firewall → PA-3200 series firewall
# - Palo Alto Networks WildFire Appliance → WildFire Appliance
Example 2 — Assign static value on match:
metadata:
device:
fields:
type:
symbols:
- OID: 1.3.6.1.2.1.1.1.0
name: sysDescr
# RouterOS devices
match_pattern: 'RouterOS (CCR.*)'
match_value: 'Router'
Use match and tags to create multiple tags from a single SNMP value using a regular expression with capture groups.
The collector:
matches, creates all tags listed under tags, substituting $1, $2, $3, etc. from the capture groups.Example 1 — Split OS name and model from sysDescr (metadata):
metadata:
device:
fields:
type:
symbols:
- OID: 1.3.6.1.2.1.1.1.0
name: sysDescr
match: '^(\S+)\s+(.*)$'
tags:
os_name: $1 # e.g. 'RouterOS'
model: $2 # e.g. 'CCR2004-16G-2S+'
Input like
RouterOS CCR2004-16G-2S+becomes:os_name=RouterOS,model=CCR2004-16G-2S+.
Example 2 — Derive multiple labels from interface names (metric_tags):
metric_tags:
- symbol:
OID: 1.3.6.1.2.1.2.2.1.2
name: ifDescr
match: '^([A-Za-z]+)[-_]?(\d+)\/(\d+)\/(\d+)$'
tags:
if_family: $1 # e.g. 'xe' or 'ge' or 'GigabitEthernet' → 'GigabitEthernet'
fpc: $2 # '0'
pic: $3 # '0'
port: $4 # '1'
xe-0/0/1, ge-0/0/0, or GigabitEthernet1/0/24.if_family=xe, fpc=0, pic=0, port=1.Value transformations let you decode, process, or normalize raw SNMP symbol values before they are stored and charted.
For metrics, they are applied per symbol (per OID) during SNMP data collection and are not applied to virtual metrics.
format is the exception to the "metric values only" rule: it is symbol
decoding, so it also applies when the same symbol is used for metric tags or
device metadata. After decoding, metric tags and device metadata follow their
own supported transformation rules.
These transformations are typically used to:
Available Value Transformations:
| Transformation | Purpose | Example Input → Output |
|---|---|---|
mapping | Convert numeric or string codes into state dimensions. | 1 → up, 2 → down, 3 → testing |
extract_value | Extract a numeric substring via regex. | "23.8 °C" → "23" |
format | Decode raw SNMP data into a value shape before other processing. | DateAndTime bytes → unix timestamp |
scale_factor | Multiply values by a constant to adjust units. | "1.5" (MBps) × 8 → 12 (Mbps) |
match_pattern + match_value | Replace string metric values using regex groups or static text before numeric parsing. | "state=2" → "2" |
Combination & Behavior:
| Rule | Description |
|---|---|
| Where | Metric value transformations are used inside metrics[*].symbol or metrics[*].symbols[]; format also applies when symbols are used for metric tags or device metadata. |
| Order of application | For string-decoded metric values: 1️⃣ format (if present) → 2️⃣ extract_value (if present) → 3️⃣ match_pattern + match_value (if present) → 4️⃣ mapping → 5️⃣ numeric parsing → 6️⃣ scale_factor. Ordinary numeric PDUs skip the string-only extract_value and match_pattern steps and use numeric parsing → mapping → scale_factor. |
| Scale factor position | scale_factor is always applied last, after all other metric value transformations. It cannot be combined with mapping.mode: bitmask. |
| String base parsing | String-like values are parsed as base-10 by default. If format: hex is set, extracted values are parsed as base-16. |
| Data type handling | Transformations preserve numeric type (integer/float) unless the mapping converts it to a multi-value metric. |
| Error handling | extract_value keeps the original value when it does not match; match_pattern fails the metric value when it does not match; no-value format sentinels are treated as missing. |
| Applicability | Metric value transformations affect metric values only; format also decodes tag and metadata symbol values. |
| Mapping syntax | mapping.items defines the lookup table. mapping.mode is optional and defaults to exact. Legacy flat-map syntax remains supported for backward compatibility. |
| Mapping behavior | Exact mode preserves the legacy exact-match/remap behavior. bitmask mode is metric-value only: every mapped bit becomes a dimension, the raw numeric value is preserved, key 0 matches only raw value 0, and unknown bits are ignored. |
Quick Syntax Recap:
mapping
mapping:
items:
1: up
2: down
3: testing
mapping (bitmask mode)
mapping:
mode: bitmask
items:
1: internalError
128: processorPresent
1024: processorThrottled
extract_value
extract_value: '(\d+)' # First capture group is used
format: hex
format: hex
extract_value: '^([0-9a-f]{2})' # First byte of an OCTET STRING
format: snmp_dateandtime
format: snmp_dateandtime # SNMPv2-TC DateAndTime OCTET STRING -> unix timestamp
format: text_date
format: text_date # Textual dates such as "2026-12-31" -> unix timestamp
scale_factor
scale_factor: 8 # Octets → bits
Use mapping to convert raw metric values into state dimensions or decoded bitmask dimensions.
mapping.mode defaults to exact. Use mapping.mode: bitmask when the raw metric value is a flag field where multiple bits may be set at the same time.
In exact mode, the emitted dimension names come from the string side of the mapping:
1: up) emits dimensions named after the mapped string values (up, down, ...).OK: 0) first normalizes the value to the numeric target, then emits dimensions named after the original string keys (OK, WARNING, ...).The collector:
mapping.items.1 if the current value matches the key, or 0 otherwise.1, and inactive mapped bits to 0.0.mapping.mode: bitmask works only for metric values, not for tags or metadata.mapping.mode: bitmask keys must be 0 or a single power-of-two bit (1, 2, 4, 8, ...).scale_factor cannot be combined with mapping.mode: bitmask.metrics:
- OID: 1.3.6.1.2.1.2.2.1.7
name: ifAdminStatus
chart_meta:
description: Current administrative state of the interface
family: 'Network/Interface/Status/Admin'
unit: "{status}"
mapping:
items:
1: up
2: down
3: testing
What this does:
up, down, and testing.1; all others report 0.Bitmask example:
metrics:
- table:
OID: 1.3.6.1.4.1.674.10892.1.1100.32
name: processorDeviceStatusTable
symbols:
- OID: 1.3.6.1.4.1.674.10892.1.1100.32.1.6
name: processorDeviceStatusReading
mapping:
mode: bitmask
items:
1: internalError
2: thermalTrip
32: configurationError
128: processorPresent
256: processorDisabled
512: terminatorPresent
1024: processorThrottled
What this does:
0 or exactly one bit.mapping.items.Use extract_value to extract a numeric or string portion from the raw SNMP value using a regular expression.
This is often used when a metric is encoded as a string that contains numeric data (e.g. "23.8 °C").
The collector:
( … ) as the new metric value.symbols are defined, the first non-empty result is used.metrics:
- MIB: CORIANT-GROOVE-MIB
table:
OID: 1.3.6.1.4.1.42229.1.2.3.1.1
name: shelfTable
symbols:
- OID: 1.3.6.1.4.1.42229.1.2.3.1.1.1.3
name: coriant.groove.shelfInletTemperature
# Example: "23.8 °C" → "23"
extract_value: '(\d+)'
chart_meta:
description: Shelf inlet temperature
family: 'Hardware/Shelf/Temperature/Inlet'
unit: "Cel"
What this does:
(\d+) to the string "23.8 °C"."23" and uses it as the metric value.format: hex is also set, the extracted value is interpreted as hexadecimal before being stored as a metric.Use format to decode raw SNMP values as a symbol is converted into its
textual or numeric representation.
For metric values, format runs before the rest of the value-processing
pipeline. For metric tags and device metadata, format runs before their own
supported extraction, match, and mapping rules. scale_factor remains
metric-value-only. Ordinary numeric PDUs do not pass through string-only
processing such as extract_value or match_pattern unless an explicit
string-decoding format routes them through the string processor first.
If a format yields no value (for example, text_date encounters a vendor
sentinel such as 0, 4294967295, never, or n/a), the result is treated
as missing. For metrics, no metric value is produced from that symbol. For
metric tags and device metadata, no tag or metadata value is produced from that
symbol.
Supported formats:
hex: decodes OCTET STRING bytes to lowercase hexadecimal text.ip_address: decodes IP address values.mac_address: decodes MAC address values.snmp_dateandtime: decodes SNMPv2-TC DateAndTime OCTET STRING values
into unix timestamps. The 11-octet form uses its embedded UTC offset. The
8-octet form has no timezone fields, so the collector interprets it as UTC
because the device timezone is unavailable.text_date: parses common textual date strings and epoch strings into
unix timestamps. Vendor no-value sentinels such as 0, 4294967295,
never, and n/a are treated as missing values.uint32: interprets integer values as unsigned 32-bit values.metrics:
- MIB: EXAMPLE-MIB
symbol:
OID: 1.3.6.1.4.1.99999.1.1.0
name: example.expiry_timestamp
format: snmp_dateandtime
The decoded value becomes the metric value seen by later processing steps,
such as extract_value, mapping, scale_factor, or transform.
Use scale_factor to multiply collected metric values by a constant.
This transformation is typically used to convert between units (for example, bytes to bits).
The collector:
extract_value).metrics:
- MIB: IP-MIB
table:
OID: 1.3.6.1.2.1.4.31.1
name: ipSystemStatsTable
symbols:
- OID: 1.3.6.1.2.1.4.31.1.1.6
name: ipSystemStatsHCInOctets
chart_meta:
description: Octets received in input IP datagrams
family: 'Network/IP/Traffic/Total/In'
unit: "bit/s"
scale_factor: 8 # Octets → bits
- MIB: IF-MIB
symbol:
OID: 1.3.6.1.2.1.31.1.1.1.15
name: ifHighSpeed
chart_meta:
description: Estimate of the interface's current bandwidth
family: 'Network/Interface/Speed'
unit: "bit/s"
scale_factor: 1000000 # Megabits → bits
What this does:
8, reporting traffic in bits per second instead of bytes.ifHighSpeed from megabits to bits.Common use cases:
virtual_metrics:
- name: <string>
# Option 1 — Direct sources (no fallback)
sources:
- { metric: <metricName>, table: <tableName>, as: <dimensionName> }
# Option 2 — Alternatives (with fallback sets)
alternatives:
- sources: # Try this first (preferred)
- { metric: <metricNameA>, table: <tableName>, as: <dimensionName> }
- { metric: <metricNameB>, table: <tableName>, as: <dimensionName> }
- sources: # Fallback if the first set is missing
- { metric: <fallbackMetricA>, table: <tableName>, as: <dimensionName> }
- { metric: <fallbackMetricB>, table: <tableName>, as: <dimensionName> }
per_row: <true|false>
group_by: [<labels>]
emit_tags:
- { tag: <outputTag>, from: <sourceTag> }
chart_meta:
description: ...
family: ...
unit: ...
Sources vs. Alternatives:
sources: defines the primary or default input set — used when there is only one way to compute the metric.alternatives: defines ordered fallback sets, each containing its own sources: block.The collector evaluates alternatives in order and uses the first set that successfully produces data.
| Item | Field | Type | Required | Default | Applies to | Description |
|---|---|---|---|---|---|---|
| Virtual Metric | name | string | yes | — | all | Unique within the profile. Used as metric/chart base name. |
sources | array<Source> | no* | — | totals, per_row, grouped | Direct source set. Ignored if alternatives exist (alternatives take precedence). | |
alternatives | array<Alternative> | no* | — | totals, per_row, grouped | Ordered fallback sets. The first alternative whose sources produce data is used. | |
per_row | bool | no | false | per-row/grouped | When true, emits one output per input row; sources become dimensions; row tags attach. | |
group_by | array<string> | no | — | per-row/grouped | Label(s) used as row-key hints (in order). With per_row:true, missing/empty hints fall back to a stable key built from all non-underscore tags. With per_row:false, this acts like PromQL’s sum by (...). | |
emit_tags | array<EmitTag> | no | — | per-row/grouped | Renames or selects which source tags are emitted on the resulting virtual metric. Useful when grouping by private tags such as _neighbor but exporting standard tags such as neighbor. | |
chart_meta | object | no | — | all | Presentation metadata (description, family, unit, type). | |
| Source | metric | string | yes | — | — | Name of an existing metric (scalar or table column metric). |
table | string | no* | — | — | Table name for the originating metric. Required for table-derived grouped/per-row virtual metrics. Scalar sources may omit it. | |
as | string | no | — | — | Optional dimension name within a composite (e.g., in, out). Single-source virtual metrics do not need it. | |
dim | string | no | — | — | Selects one dimension from a MultiValue source metric (for example start or established) before aggregation. Useful when composing virtual metrics from mapped status charts. | |
| EmitTag | tag | string | yes | — | — | Output tag name to emit on the virtual metric. |
from | string | yes | — | — | Existing source-tag name to copy from the grouped source rows. | |
| Alternative | sources | array<Source> | yes | — | — | All sources in an alternative are evaluated together. If none produce data, the collector tries the next alternative. Per-row/group rules apply within the winning alternative. |
At least one of
sourcesoralternativesmust be defined.
| Rule | Description |
|---|---|
| Precedence | If both sources and alternatives exist, alternatives take precedence. |
| Same-table requirement | When per_row or group_by is used, all sources must originate from the same table. For alternatives, this rule applies within each alternative set. |
| per_row: true | One output per input row; multiple sources become chart dimensions (as); row tags attach automatically. |
| group_by (with per_row:true) | Acts as row-key hints (in order). Missing or empty hints fall back to a stable key built from all non-underscore tags. |
| group_by (with per_row:false) | Aggregates rows by the listed labels, similar to PromQL’s sum by (...). |
| emit_tags | If omitted, per_row:true emits the winning row tags as-is. Grouped non-per_row metrics emit the group_by labels by default. When set, only the listed tags are emitted, using the from source-tag names. |
| Alternative evaluation | Alternatives are checked in order. The first whose sources produce data becomes the “winner”; others are ignored. |
| Parent metadata | The virtual metric emits charts using its own name and chart_meta, even when data comes from an alternative. |
| Dimensions | Each as value defines a dimension in the resulting chart (e.g., in, out, total). |
| Selected source dimension | When dim is set on a source, the collector reads only that MultiValue dimension from the source metric and ignores the rest. |
| Totals vs per-row | Omitting both per_row and group_by produces a single total chart across all rows (device-wide view). |
virtual_metrics:
- name: ifTotalTraffic
sources:
- { metric: _ifHCInOctets, table: ifXTable, as: in }
- { metric: _ifHCOutOctets, table: ifXTable, as: out }
per_row: true
group_by: ["interface"]
chart_meta:
description: Traffic per interface
family: 'Network/Interface/Traffic'
unit: "bit/s"
What this does:
ifXTable.in and out.group_by: ["interface"] provides key hints to keep per-interface charts stable.per_row or group_by requires all sources to come from the same table.virtual_metrics:
- name: ifTotalTraffic
sources:
- { metric: _ifHCInOctets, table: ifXTable, as: in }
- { metric: _ifHCOutOctets, table: ifXTable, as: out }
chart_meta:
description: Total traffic across all interfaces
family: 'Network/Total/Traffic'
unit: "bit/s"
What this does:
ifXTable into a single chart.in, out) representing the total interface traffic for the entire device.per_row or group_by fields → a single total chart (device-wide view).virtual_metrics:
- name: ifTypeTraffic
sources:
- { metric: _ifHCInOctets, table: ifXTable, as: in }
- { metric: _ifHCOutOctets, table: ifXTable, as: out }
per_row: false
group_by: ["ifType"]
chart_meta:
description: Traffic aggregated by interface type
family: 'Network/InterfaceType/Traffic'
unit: "bit/s"
virtual_metrics:
- name: bgpPeerAvailability
per_row: true
sources:
- { metric: bgpPeerAdminStatus, table: bgpPeerTable, as: admin_enabled, dim: start }
- { metric: bgpPeerState, table: bgpPeerTable, as: established, dim: established }
chart_meta:
description: BGP peer administrative and established availability
family: 'Network/Routing/BGP/Peer/Availability'
unit: "{status}"
What this does:
start dimension from bgpPeerAdminStatus.established dimension from bgpPeerState.admin_enabled and established.virtual_metrics:
- name: bgpPeerAvailability
per_row: true
group_by: ["_neighbor", "_address_family", "_subsequent_address_family"]
emit_tags:
- { tag: neighbor, from: _neighbor }
- { tag: address_family, from: _address_family }
- { tag: subsequent_address_family, from: _subsequent_address_family }
sources:
- { metric: hwBgpPeerAdminStatus, table: hwBgpPeerRouteTable, as: admin_enabled, dim: start }
- { metric: hwBgpPeerState, table: hwBgpPeerRouteTable, as: established, dim: established }
chart_meta:
description: BGP peer availability
family: 'Network/Routing/BGP/Peer/Availability'
unit: "{status}"
What this does:
neighbor, address_family, subsequent_address_family).What this does:
ifType label into grouped totals.in and out dimensions aggregated by interface type.virtual_metrics:
- name: ifTotalPacketsUcast
alternatives:
- sources:
- { metric: _ifHCInUcastPkts, table: ifXTable, as: in }
- { metric: _ifHCOutUcastPkts, table: ifXTable, as: out }
- sources:
- { metric: _ifInUcastPkts, table: ifTable, as: in }
- { metric: _ifOutUcastPkts, table: ifTable, as: out }
chart_meta:
description: Total unicast packets across all interfaces (in/out)
family: 'Network/Total/Packet/Unicast'
unit: "{packet}/s"
What this does:
name and chart_meta, sourcing values from the selected child.sources and alternatives are present, alternatives take precedence.virtual_metrics:
- name: ifTotalPacketsByKind
sources:
- { metric: _ifHCInUcastPkts, table: ifXTable, as: in_ucast }
- { metric: _ifHCOutUcastPkts, table: ifXTable, as: out_ucast }
- { metric: _ifHCInMulticastPkts, table: ifXTable, as: in_mcast }
- { metric: _ifHCOutMulticastPkts, table: ifXTable, as: out_mcast }
- { metric: _ifHCInBroadcastPkts, table: ifXTable, as: in_bcast }
- { metric: _ifHCOutBroadcastPkts, table: ifXTable, as: out_bcast }
chart_meta:
description: Total packets across all interfaces by kind (in/out)
family: 'Network/Total/Packet/ByKind'
unit: "{packet}/s"
What this does
as becomes a dimension (in_ucast, out_ucast, in_mcast, …).per_row/group_by → totals aggregated across all interfaces.The SNMP collector ships a shared BGP pipeline that turns vendor-specific BGP
MIB rows into typed device, peer, and peer-family rows. Profiles describe this
telemetry in a top-level bgp: section. The collector emits typed BGP rows from
that section; underscore-prefixed helper tags and virtual_metrics: aliases
are legacy migration mechanisms, not the preferred BGP transport.
BGP row kind values are closed:
device — device-level BGP summaries with no peer identity.peer — peer-level rows identified by neighbor and remote_as.peer_family — address-family rows identified by neighbor, remote_as,
address_family, and subsequent_address_family.Peer-state mappings must use the six RFC 4271 state names: idle, connect,
active, opensent, openconfirm, and established. A complete source must
map all six states. If a source MIB is intentionally partial, set
partial: true and use partial_states: [...] to record which canonical
states the source can represent.
When a peer or peer-family row does not provide a routing instance, the public
chart/function label defaults to default. Profiles may still set
identity.routing_instance explicitly when a vendor MIB exposes VRF or routing
instance identity.
Device rows support device_counts.peers, device_counts.ibgp_peers,
device_counts.ebgp_peers, and per-state counters under
device_counts.states. Peer and peer-family rows support typed groups such as
admin, state, connection, traffic, transitions, timers,
last_error, last_notifications, reasons, graceful_restart, routes,
and route_limits.
For table-backed rows, readable columns use symbol:. not-accessible index
objects must be derived from the row index with index, index_from_end, or
index_transform. Values from related tables use the first-class table:
field on the BGP value, optionally with lookup_symbol: when the current row
must join to another table by value.
Example device-level counts:
bgp:
- id: vendor-bgp-device-counts
MIB: VENDOR-BGP-MIB
kind: device
device_counts:
peers:
symbol: { OID: 1.3.6.1.4.1.99999.1, name: vendor.bgpPeerSessionNum }
ibgp_peers:
symbol: { OID: 1.3.6.1.4.1.99999.2, name: vendor.iBgpPeerSessionNum }
ebgp_peers:
symbol: { OID: 1.3.6.1.4.1.99999.3, name: vendor.eBgpPeerSessionNum }
Example peer-family row:
bgp:
- id: vendor-bgp-peer-family
MIB: VENDOR-BGP-MIB
kind: peer_family
table:
OID: 1.3.6.1.4.1.99999.10
name: vendorBgpPeerTable
identity:
routing_instance: { value: default }
neighbor:
symbol: { OID: 1.3.6.1.4.1.99999.10.1.4, name: vendorBgpPeerRemoteAddr, format: ip_address }
remote_as:
symbol: { OID: 1.3.6.1.4.1.99999.10.1.5, name: vendorBgpPeerRemoteAs, format: uint32 }
address_family:
index: 1
mapping: { 1: ipv4, 2: ipv6, 25: l2vpn }
subsequent_address_family:
index: 2
mapping: { 1: unicast, 128: vpn }
state:
symbol:
OID: 1.3.6.1.4.1.99999.10.1.6
name: vendorBgpPeerState
mapping:
1: idle
2: connect
3: active
4: opensent
5: openconfirm
6: established
traffic:
updates:
received:
symbol: { OID: 1.3.6.1.4.1.99999.10.1.7, name: vendorBgpPeerInUpdates }
sent:
symbol: { OID: 1.3.6.1.4.1.99999.10.1.8, name: vendorBgpPeerOutUpdates }
The SNMP collector ships a shared device-level licensing pipeline that
turns vendor-specific licensing telemetry into six common contexts
(snmp.license.remaining_time, snmp.license.authorization_remaining_time,
snmp.license.certificate_remaining_time, snmp.license.grace_remaining_time,
snmp.license.usage_percent, snmp.license.state) plus an interactive
snmp:licenses drill-down function. Profiles describe licensing telemetry in
a top-level licensing: section. The collector emits typed license rows from
that section; regular metrics: rows are not used as a licensing transport.
A licensing row describes one vendor license, entitlement, contract, or license pool. A row may be table-backed or scalar-backed:
table: and produces one typed license row per
SNMP table row.table: and produces one typed license row for the
scalar values named in the row.value: fields must declare an
explicit stable id: because there is no signal OID to use as structural
identity.Each row has:
identity: fields used by the drill-down: id, name, feature,
component.descriptors: fields: type, impact, perpetual, unlimited.state: for a normalized state severity (0 healthy, 1 degraded, 2
broken) plus the raw vendor value.signals: for timers and usage:
expiry.timestamp / expiry.remainingauthorization.timestamp / authorization.remainingcertificate.timestamp / certificate.remaininggrace.timestamp / grace.remainingusage.used, usage.capacity, usage.available, usage.percentExample table-backed row:
licensing:
- id: licensing_blades
MIB: CHECKPOINT-MIB
table:
OID: 1.3.6.1.4.1.2620.1.6.18.1
name: licensingTable
identity:
id: { OID: 1.3.6.1.4.1.2620.1.6.18.1.1.2, name: licensingID }
name: { OID: 1.3.6.1.4.1.2620.1.6.18.1.1.4, name: licensingBladeName }
component: { value: blade }
descriptors:
type: { value: subscription }
state:
OID: 1.3.6.1.4.1.2620.1.6.18.1.1.5
name: licensingState
mapping:
valid: "0"
"about-to-expire": "1"
expired: "2"
signals:
expiry:
timestamp:
OID: 1.3.6.1.4.1.2620.1.6.18.1.1.6
name: licensingExpirationDate
sentinel: [timer_u32_max]
usage:
used: { OID: 1.3.6.1.4.1.2620.1.6.18.1.1.10, name: licensingUsedQuota }
capacity: { OID: 1.3.6.1.4.1.2620.1.6.18.1.1.9, name: licensingTotalQuota }
Example scalar-backed row:
licensing:
- id: routeros_upgrade
MIB: MIKROTIK-MIB
identity:
id: { value: routeros_upgrade }
name: { value: RouterOS upgrade entitlement }
component: { value: routeros }
descriptors:
type: { value: upgrade_entitlement }
signals:
expiry:
timestamp:
OID: 1.3.6.1.4.1.14988.1.1.4.2.0
name: mtxrLicUpgrUntil
format: snmp_dateandtime
sentinel: [timer_pre_1971]
The value-processor format mechanism handles licensing expiry dates directly. On each poll, the table collector re-fetches value columns even on table-cache hits, so expiry values are decoded from the current poll's PDU instead of from cached row metadata.
This does not disable the generic SNMP table cache for the surrounding
table. Same-table metric_tags can still come from cached row metadata on
cache hits. For live licensing state, prefer symbol-based severity and
timestamp values over same-table text tags whenever the device exposes both.
Three options are available:
Gauge32 / Counter32 / Unsigned32. The numeric value
processor reads it directly. Check Point's licensingExpirationDate is one
example.format: snmp_dateandtime — for SNMPv2-TC DateAndTime octet strings (8
or 11 byte fixed binary). Used by vendors like Blue Coat ProxySG and Cisco
CISCO-LICENSE-MGMT-MIB.format: text_date — for textual date strings (e.g., 2026-12-31,
Mon Jan 2 2030, epoch seconds/milliseconds embedded as text). Accepts
the same layouts as the licensing pipeline's internal date parser. Used
by Fortinet's DisplayString expiry columns.The decoded unix timestamp is stored in the typed timer. The licensing projection can drop known "no expiry" sentinels before the consumer sees them. Supported sentinel policies are:
timer_zero_or_negativetimer_u32_maxtimer_pre_1971For table rows, the collector keeps structural identity from the profile,
table OID, and SNMP row index. Human-readable identity fields are for display
and grouping in the drill-down. For not-accessible index objects, derive the
identity from the row index:
identity:
id:
index: 1