Back to Netdata

SNMP Profile Format

src/go/plugin/go.d/collector/snmp/profile-format.md

2.11.0101.8 KB
Original Source

SNMP Profile Format

Overview

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:

  • which OIDs to query
  • how to interpret the returned values
  • how to transform them into metrics, dimensions, tags, and metadata
  • which rows are regular metrics and which rows are SNMP topology observations

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.

How Profiles Work

When Netdata connects to an SNMP device, the collector:

  1. Reads the device’s sysObjectID and sysDescr.
  2. Evaluates all available profiles.
  3. Applies all profiles whose selectors match (multiple profiles can apply simultaneously).
  4. Uses the combined configuration to:

Profile Lifecycle

text
┌──────────────────────┐
│ 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: Complete SNMP Profile

yaml
# 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"

Profile Structure

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.

yaml
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>
SectionPurpose
selectorDefines which devices the profile applies to.
extendsInherits and merges other base profiles.
metadataCollects device-level information (host labels).
metricsDefines which OIDs to collect and how to chart them.
topologyDefines SNMP topology rows and their topology kind.
bgpDefines typed BGP device, peer, and peer-family rows.
licensingDefines typed license rows.
metric_tagsDefines global dynamic tags collected once per device and attached to all metrics.
static_tagsDefines fixed tags applied to all metrics.
virtual_metricsDefines calculated or aggregated metrics based on others.

1. selector

You use the selector to:

  • Target specific device families (e.g., Cisco, Juniper, HP)
  • Match devices supporting specific MIBs
  • Exclude unwanted devices

During discovery, Netdata evaluates all profiles; any profile whose selector matches a device is **applied **.

yaml
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:

  • Each selector rule is a set of conditions (sysobjectid, sysdescr, etc.).
  • For a rule to match, all its conditions must pass.
  • A profile is applied if at least one rule in the selector list matches the device.
  • If both sysobjectid and sysdescr are defined within the same rule, both must succeed.

Supported conditions:

KeyWhat It ChecksMatch Criteria (Pass)Fails When...
sysobjectid.includeDevice sysObjectIDMatches at least one pattern in the list.No items match.
sysobjectid.excludeDevice sysObjectIDMatches none of the listed patterns.Any item matches.
sysdescr.includeDevice sysDescr (case-insensitive)Contains at least one substring in the list.No listed substrings are found.
sysdescr.excludeDevice sysDescr (case-insensitive)Contains none of the listed substrings.Any listed substring is found.

2. extends

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.

yaml
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:

  1. Order matters — profiles are loaded in the order listed.
  2. Metrics are merged — all metrics from all referenced profiles are included.
  3. Later overrides earlier — if the same field is defined multiple times, the last one wins.

Metric override identity depends on metric type:

  • Scalar metrics use symbol.name + symbol.OID. This preserves same-name scalar fallback definitions that try alternative OIDs.
  • Table metrics use logical table identity (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

ProfileProvidesTypical Use
_system-base.yamlBasic system info (uptime, name, contact)All devices
_std-if-mib.yamlInterface statistics (IF-MIB)Network devices
_std-ip-mib.yamlIP-level statistics (IP-MIB)Routers, switches
_std-tcp-mib.yamlTCP statisticsServers, firewalls
_std-udp-mib.yamlUDP statisticsServers, firewalls
_std-ups-mib.yamlPower and UPS metricsUPS devices

3. metadata

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:

  • Staticvalue: is a fixed string.
  • Dynamic — the value is read from one or more SNMP OIDs using either:
    • symbol — a single OID to read from.
    • symbols — an ordered list of OIDs to try, first non-empty wins.
yaml
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.
  • These values appear as device (virtual node) host labels in the Netdata UI.
  • They are not per-metric tags and are applied to the device itself, not individual charts.
  • Metadata fields are available to both regular metrics and topology by default. Use 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.

:::

4. metrics

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:

  • Scalars — single values that apply to the entire device (for example, uptime).
  • Tables — repeating rows of related values (for example, interfaces, disks, sensors).

:::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.

yaml
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:

  • Each entry defines a metric or table of metrics to collect via SNMP.
  • Scalars use a single symbol, while tables define a table and one or more symbols.
  • Metrics can include transformations (extract_value, scale_factor, etc.) and chart metadata.
  • Table metrics can include tags (metric_tags) to identify rows by interface, disk, or other attributes.

:::tip

See also

:::

Underscore-prefixed metrics

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.

yaml
# 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 }

4.1 topology

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.

yaml
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.
  • Topology row symbol names must not start with _.
  • Topology rows do not use chart/export-only fields such as 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:

text
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.

Scalar symbol fallbacks

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.

yaml
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"

5. metric_tags

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:

  • Attach device-wide metadata such as serial number, firmware version, or model.
  • Enable grouping and filtering by hardware, OS, or vendor attributes.
  • Complement per-metric tags (for example, per-interface or per-sensor tags in tables).
yaml
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:

  • Each tag is collected once per device, not per metric or per table row.
  • The resulting tag values are attached to all metrics collected by the profile.
  • Tags can be transformed (for example, reformatted or mapped) using the same rules as per-metric tags.
  • Top-level 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.

:::

Underscore-prefixed tags

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.)

yaml
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"

Tag fallback (first non-empty wins)

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.)

yaml
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

6. static_tags

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:

  • Add environment or deployment identifiers (for example, environment, region, or service).
  • Simplify filtering, grouping, and alerting across metrics from multiple devices.
  • Provide consistent context (for example, datacenter or team ownership).
yaml
static_tags:
  - tag: environment
    value: production
  - tag: region
    value: us-east-1
  - tag: service
    value: network

How it works:

  • Each tag is added to all metrics collected by the profile.
  • Static tags are merged with any dynamic tags defined in metric_tags.
  • Device-specific or dynamic tags always take precedence if they overlap.

7. virtual_metrics

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:

  • Combine related counters (for example, in + out traffic or errors).
  • Create fallbacks (prefer 64-bit counters, fall back to 32-bit if missing).
  • Aggregate or group metrics per tag (for example, total per interface or per type).
yaml
  - 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:

  • Defines a new virtual metric named ifTotalTraffic.
  • Uses existing metrics (ifHCInOctets, ifHCOutOctets) as sources.
  • The as field names the resulting dimensions (in, out).
  • The resulting chart behaves like a regular metric — visible in dashboards, alertable, and included in exports.

Collecting Metrics

This section explains how SNMP data is structured and how it maps to metrics in a Netdata profile.

Understanding SNMP Data

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.

text
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 (Single Values)

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.

yaml
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:

  • Collects the sysUpTime value once per device.
  • The .0 at the end indicates there is only one instance of this value.
  • Common scalar metrics: device uptime, total memory, or overall temperature.

Table Metrics (Multiple Rows)

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:

text
ifHCInOctets.1 = 1024
ifHCInOctets.2 = 2048
  • .1, .2, … are row indexes that identify the instance (e.g., interface #1, interface #2).
  • Each column (symbol) in the table has its own OID pattern but shares the same row indexes.

Table metrics must define at least one tag (metric_tags) to identify each row. Without tags, only a single row can be emitted.

yaml
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

text
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:

  1. The collector reads both columns (ifHCInOctets and ifName) from the same table.
  2. It aligns rows using their shared SNMP index (1, 2, …).
  3. Each metric is emitted with its corresponding tag from the same row.

What this does:

  • Collects traffic (ifHCInOctets) from each interface.
  • Tags each row with its name (ifName) from the same index.
  • Produces metrics like:
    text
    ifHCInOctets{interface="eth0"} = 1024
    ifHCInOctets{interface="eth1"} = 2048
    

Metric Types

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 TypeDefault Netdata TypeTypical Use
Counter32, Counter64rateNetwork traffic, packet counters
Gauge32, IntegergaugeTemperatures, usage levels, statuses
TimeTicksgaugeUptime, time-based values

Overriding the Metric Type

You can explicitly set a metric’s type using the metric_type field inside a symbol definition.

yaml
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:

  • Forces ifInOctets to be treated as a gauge (instantaneous value) instead of a rate.
  • Normally, Counter types are automatically converted to per-second rates.

Chart Metadata

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.

yaml
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"
FieldTypeRequiredDescription
descriptionstringnoHuman-readable description shown in dashboards and alerts.
familystringnoChart grouping path (slashes / define hierarchy). Helps organize charts by system or subsystem.
unitstringnoDisplay unit, e.g. "bit/s", "%", "{status}", "Cel".
typestringnoOptional chart style override: line, area, or stacked. Defaults depend on metric type.

Adding Tags to Metrics

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:

  • Attaches tags to each metric as labels.
  • Uses tags to differentiate rows when building charts.
  • Requires at least one tag for every table metric (to identify each row).

Key Concepts:

ConceptDescription
Table metrics must have tagsEach 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 identityScalars represent one value for the entire device, so tags are not part of their normal public identity contract.
Static tagsFixed values that never change (for example, datacenter, environment).
Dynamic tagsExtracted from SNMP data — from table columns, related tables, or row indexes.
Global tagsDefined in the profile’s top-level metric_tags section and applied to all metrics.

Tag Types and Available Transformations:

Tag TypeDescriptionSupported Transformations
StaticFixed tags with constant values.None (value is fixed).
Same-TableValues from columns in the same table as the metric.mapping, extract_value, match_pattern + match_value, match + tags
Cross-TableValues from another table.mapping, extract_value, match_pattern + match_value, match + tags
Index-BasedValues derived from the OID index of each row.mapping (optional)
Index TransformAdjusts multi-part indexes so cross-table tags align correctly.— (structural, not a transformation)

Summary:

  • Each table metric must define at least one tag source (metric_tags) to distinguish rows.
  • Tags can come from the same table, another table, or the row index itself.
  • Scalar metrics do not use tags for identity. Internal bundled profiles may attach sidecar scalar values to hidden carrier metrics, but that is not a stable public authoring contract.
  • Tag transformations (mapping, extract_value, match_pattern, match + tags) can modify or extract parts of raw values.
  • Static tags apply globally and are not transformed.
  • Index transformations are a special mechanism used only for aligning multi-part indexes between tables.
  • Avoid reusing built-in device label names such as 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:

text
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:

  1. The collector walks the table and collects both columns: ifInOctets (value) and ifDescr (tag source).
  2. It aligns them by their shared SNMP index (1, 2, …).
  3. Each metric row is tagged with the corresponding column value from the same index.

Cross-Table Example:

text
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:

  • The collector collects metrics from ifTable but fetches tag values from ifXTable.
  • It matches rows from both tables using their shared index (.1, .2, …).
  • The interface tag is populated from ifXTable.ifName for each matching row

Static

Static 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

Profile-level static tags apply to all metrics defined in the profile.

yaml
# Global static tags (applied to all metrics)
static_tags:
  - tag: datacenter
    value: "DC1"
  - tag: environment
    value: "production"

What this does:

  • These tags are injected into every metric reported by the profile.

Typical use cases:

  • Identify the datacenter, region, or cluster where the device belongs.
  • Mark metrics from staging or production environments.
  • Add organization-wide context that doesn’t depend on SNMP data.

Metric-level Static Tags

Metric-level static tags apply to specific metrics only.

yaml
# 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:

  • Adds the tags source=snmp and interface_type=physical only to the ifInOctets metric.
  • Does not affect other metrics in the same profile.

Metric-level static tags are technically supported but rarely needed. In most cases, prefer profile-level static_tags for consistency and simplicity.

Same-Table

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:

  • Retrieves both the metric value and the tag column from the same table row.
  • Automatically aligns rows by their shared index.
  • Adds the tag to every metric collected from that row.
yaml
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:

  • Collects ifInOctets (input bytes) for each row in ifTable.
  • Reads the ifDescr column from the same table to label each row.
  • Produces metrics like:
    text
    ifInOctets{interface="eth0"} = 1000
    ifInOctets{interface="eth1"} = 2000
    

Cross-Table

Cross-table tags let you use data from another SNMP table as a tag source.

The collector:

  • Reads tag values from the specified table: instead of the current one.
  • Matches rows between tables by their index.
  • When index structures differ, an optional index_transform can modify the current table’s index to align it with the target.
  • When the target table is keyed differently, an optional lookup_symbol can match a transformed index value against a column in the target table and then read tags from the matched row.

Same Index

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:

text
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.

yaml
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:

  • Collects ifInOctets from ifTable.
  • Finds the row with the same index in ifXTable (e.g., .2).
  • Uses ifName as the interface tag.
  • Produces metrics like:
    text
    ifInOctets{interface="xe-0/0/1"} = 123456
    

With Index Transformation

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:

text
ipIfStatsTable.ipIfStatsHCInOctets.2.1 = 38560
ipIfStatsTable.ipIfStatsHCInOctets.2.2 = 44408

Here:

  • The first component (2) is the IP version (e.g., 2 = IPv4, 3 = IPv6).
  • The second component (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.

yaml
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:

  • Collects IP traffic metrics from ipIfStatsTable.
  • Keeps only the second index element (start: 1, end: 1) from 2.1 → becomes 1.
  • Looks up that interface index in ifXTable to find the corresponding ifName.
  • Produces:
    yaml
    ipIfStatsHCInOctets{_interface="xe-0/0/1"} = 38560
    
How index_transform Works

index_transform tells the collector which parts of the current table’s index to keep when matching rows across tables.

ConceptExample
Original index2.1 (from ipIfStatsTable) → [ipVersion, ifIndex]
Target index1 (from ifXTable)
Transformindex_transform: [ { start: 1, end: 1 } ]
ResultThe 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).
  • Each range defines which parts of the index to keep.
  • You can list multiple ranges to combine non-contiguous parts.
  • 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.
  • The goal is to make the current table’s index match the target table’s index so tags align correctly.

Example with drop_right:

yaml
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.

With Value Lookup (lookup_symbol)

Some vendor MIBs split related data across tables that do not share the same row index.

For example:

  • a prefix-counter table may be indexed by peerIndex.afi.safi
  • the peer table may be indexed by routingInstance.localAddr.remoteAddr
  • but the peer table still contains a peerIndex column

In that case, index_transform alone is not enough:

  • it can extract the current row's peerIndex
  • but it cannot guess which peer-table row owns that peerIndex

Use lookup_symbol to tell the collector:

  1. Extract a lookup value from the current row index
  2. Find the row in the target table whose lookup_symbol column matches that value
  3. Read the requested symbol from that matched row
yaml
metrics:
  - 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:

  • Collects accepted-prefix counters from the Juniper prefix table
  • Takes the first index element from the current row (peerIndex)
  • Scans jnxBgpM2PeerTable.jnxBgpM2PeerIndex for a matching value
  • Reads jnxBgpM2PeerRemoteAddr from the matched peer-table row
  • Produces metrics like:
    text
    bgpPeerPrefixesAccepted{neighbor="192.0.2.1"} = 1234
    

Important behavior:

  • lookup_symbol is used for cross-table tags and typed BGP value fields
  • it works together with index_transform, not instead of it
  • if no row matches, the tag lookup fails for that row
  • if one row matches, the collector reads the requested tag from that row
  • if multiple rows match, the lookup succeeds only when all matched rows resolve to the same final tag value; conflicting values fail the lookup

Index-Based

Index-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:

  • Splits the table’s row index into numbered parts (1-based).
  • For each index: rule, assigns a tag using the specified position in the index.
  • Converts numeric index components to strings automatically.
  • Attaches all resulting tags to the metric collected from that row.
  • Can also derive tags from the full row index using index_transform plus symbol.format, symbol.extract_value, symbol.match_pattern, or mapping, even when there is no column OID for that tag.
yaml
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:

  • Extracts the first three components of each row’s OID index and uses them as tags.
  • For example, if the full OID is:
    text
    1.3.6.1.2.1.149.1.5.1.1.3.1.6.200
    
    The collector interprets:
    ini
    applIndex=1
    sipCommonStatusCodeMethod=6
    sipCommonStatusCodeValue=200
    
  • Produces metrics like:
    text
    sipCommonStatusCodeIns{applIndex="1", sipCommonStatusCodeMethod="6", sipCommonStatusCodeValue="200"} = 42
    

Derived tag example from a transformed index:

yaml
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:

  • keeps the peer-address part only: 192.0.2.1
  • formats it as an IP address
  • emits neighbor="192.0.2.1"

Field Accessibility

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.
  • A not-accessible object that is part of a table INDEX can be derived from the row OID index using index or index_transform.
  • Keep SNMP index slicing in the profile YAML; keep format conversion in 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:

bash
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 Transformation

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), and
  • metric_tags (e.g., per-row interface labels).

Available Tag Transformations:

TransformationPurposeExample Input → Output
formatConvert the raw SNMP value before tag parsing.0x18fd74331a9c → "18fd74331a9c"
mappingReplace numeric/string codes with names.1 → "ethernet", 161 → "lag"
extract_valueExtract a substring via regex (first group)."RouterOS CCR2004-16G-2S+" → "CCR2004-16G-2S+"
match_pattern + match_valueReplace 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:

RuleDescription
WhereCan be used inside metadata.*.fields.*.symbols[] and metric_tags[].
Order of application1️⃣ match_pattern + match_value or extract_value (whichever is present) → 2️⃣ mapping → 3️⃣ match + tags (if defined).
No match behaviorextract_value: keeps the original value.
match_pattern: skips the value (tag not emitted).
match + tags: emits no tags.
Multiple symbolsIf multiple symbols are listed for the same tag, the first non-empty result is used.
Mapping key consistencyKeys in a mapping must all be the same type — all numeric or all string.
Mapping modesTags and metadata support only exact-match mapping. Use mapping.items; mapping.mode is optional and defaults to exact.
SafetyKeep regexes simple and, when possible, anchor them (e.g. ^pattern$) to prevent unwanted matches.

Quick Syntax Recap:

  • mapping

    yaml
    mapping:
      items:
        6: "ethernet"
        161: "lag"
    
  • extract_value

    yaml
     extract_value: 'RouterOS ([A-Za-z0-9-+]+)'   # first capture group is used
    
  • match_pattern + match_value

    yaml
    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)

    yaml
    match: '^([A-Za-z]+)[-_]?(\d+)\/(\d+)\/(\d+)$'
    tags:
      if_family: $1
      fpc: $2
      pic: $3
      port: $4
    
  • format

    yaml
    symbol:
      OID: 1.3.6.1.2.1.17.1.1
      name: dot1dBaseBridgeAddress
      format: hex
    

Format

Use format when the raw SNMP value must be converted before tag or metadata processing.

The collector:

  • Applies format when converting the raw SNMP value to a string.
  • Then applies the configured tag or metadata transformations to that formatted string.
  • Supports the same format values accepted by symbol definitions elsewhere in the profile.

Where it can be used:

  • metadata.device.fields.<field>.symbol
  • metadata.device.fields.<field>.symbols[]
  • metric_tags[].symbol

Currently used by this profile set:

  • format: hex for octet-string values such as:
    • MAC addresses
    • binary management-address values
    • capability bitmaps

Example:

yaml
metadata:
  device:
    fields:
      bridge_base_address:
        symbol:
          OID: 1.3.6.1.2.1.17.1.1
          name: dot1dBaseBridgeAddress
          format: hex

Mapping

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:

  • Looks up the raw value in the mapping table.
  • Replaces it with the corresponding string.
  • If the value is not found in the mapping, the original value is kept.
  • Keys can be numeric or string, but must be consistent in type.
  • Mapping is applied to tag values from metadata or metric_tags.
yaml
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:

  • Replaces numeric interface type codes (1, 6, 24, 131, 161) with readable names (other, ethernet, loopback, tunnel, lag).
  • If a device reports an unknown type, the original numeric value is used.
  • Works identically for metadata fields and metric_tags.
  • Legacy flat-map syntax remains supported for backward compatibility: mapping: { 6: "ethernet", 161: "lag" }

Extract Value

Use extract_value to capture a part of a string using a regular expression.

The collector:

  • Applies the pattern to the raw value.
  • Replaces the value with the first capture group ( … ).
  • Keeps the original value if no match is found.
  • Searches anywhere in the string unless you anchor the pattern with ^ or $.
  • Uses the first non-empty result when multiple symbols are defined.
yaml
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'

Match Pattern

Use match_pattern and match_value together to build a tag value using multiple regex capture groups.

The collector:

  • Tests the value against the regular expression in match_pattern.
  • If it matches, replaces the value with match_value.
  • Within match_value, you can reference capture groups using $1, $2, $3, etc.
  • If the value does not match, it is skipped (ignored).
  • Works both for reformatting captured text and for assigning a static replacement when matched.

Example 1 — Reformat using capture groups:

yaml
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:

yaml
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'

Match (Multiple Tags)

Use match and tags to create multiple tags from a single SNMP value using a regular expression with capture groups.

The collector:

  • Applies the regex in match to the raw value.
  • If it matches, creates all tags listed under tags, substituting $1, $2, $3, etc. from the capture groups.
  • If it doesn’t match, none of those tags are added.
  • Tags that resolve to an empty capture are not added.

Example 1 — Split OS name and model from sysDescr (metadata):

yaml
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):

yaml
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'
  • Handles common patterns like xe-0/0/1, ge-0/0/0, or GigabitEthernet1/0/24.
  • Output tags might be: if_family=xe, fpc=0, pic=0, port=1.

Value Transformation

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:

  • Extract numeric substrings from mixed strings.
  • Scale or convert units (bytes → bits, megabits → bits).
  • Map discrete states (1 = up, 2 = down, etc.) into named dimensions.

Available Value Transformations:

TransformationPurposeExample Input → Output
mappingConvert numeric or string codes into state dimensions.1 → up, 2 → down, 3 → testing
extract_valueExtract a numeric substring via regex."23.8 °C" → "23"
formatDecode raw SNMP data into a value shape before other processing.DateAndTime bytes → unix timestamp
scale_factorMultiply values by a constant to adjust units."1.5" (MBps) × 8 → 12 (Mbps)
match_pattern + match_valueReplace string metric values using regex groups or static text before numeric parsing."state=2" → "2"

Combination & Behavior:

RuleDescription
WhereMetric 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 applicationFor 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 → mappingscale_factor.
Scale factor positionscale_factor is always applied last, after all other metric value transformations. It cannot be combined with mapping.mode: bitmask.
String base parsingString-like values are parsed as base-10 by default. If format: hex is set, extracted values are parsed as base-16.
Data type handlingTransformations preserve numeric type (integer/float) unless the mapping converts it to a multi-value metric.
Error handlingextract_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.
ApplicabilityMetric value transformations affect metric values only; format also decodes tag and metadata symbol values.
Mapping syntaxmapping.items defines the lookup table. mapping.mode is optional and defaults to exact. Legacy flat-map syntax remains supported for backward compatibility.
Mapping behaviorExact 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

    yaml
    mapping:
      items:
        1: up
        2: down
        3: testing
    
  • mapping (bitmask mode)

    yaml
    mapping:
      mode: bitmask
      items:
        1: internalError
        128: processorPresent
        1024: processorThrottled
    
  • extract_value

    yaml
    extract_value: '(\d+)'   # First capture group is used
    
  • format: hex

    yaml
    format: hex
    extract_value: '^([0-9a-f]{2})'   # First byte of an OCTET STRING
    
  • format: snmp_dateandtime

    yaml
    format: snmp_dateandtime   # SNMPv2-TC DateAndTime OCTET STRING -> unix timestamp
    
  • format: text_date

    yaml
    format: text_date   # Textual dates such as "2026-12-31" -> unix timestamp
    
  • scale_factor

    yaml
    scale_factor: 8   # Octets → bits
    

Mapping

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:

  • Numeric key -> string value (1: up) emits dimensions named after the mapped string values (up, down, ...).
  • String key -> numeric value (OK: 0) first normalizes the value to the numeric target, then emits dimensions named after the original string keys (OK, WARNING, ...).
  • Numeric key -> numeric value is treated as value normalization only and does not emit multi-value dimensions.

The collector:

  • Evaluates the value against mapping.items.
  • In exact mode, creates dimensions using the mapping's string labels, following the rules above.
  • Sets that dimension to 1 if the current value matches the key, or 0 otherwise.
  • In bitmask mode, sets every mapped dimension whose bit is active to 1, and inactive mapped bits to 0.
  • If the value doesn’t match any exact key, all exact-mode dimensions are 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.
  • If multiple bit keys map to the same dimension, that dimension is active when any mapped bit is active.
yaml
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:

  • Converts SNMP integer values (1, 2, 3) into a multi-value metric with dimensions up, down, and testing.
  • The dimension corresponding to the current value reports 1; all others report 0.
  • Legacy flat-map syntax remains supported and is equivalent to exact mode.
  • For string -> numeric exact mappings, the emitted dimensions use the original string keys instead of the normalized numeric values.

Bitmask example:

yaml
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:

  • Treats the raw value as a bitmask where multiple bits can be active at once.
  • Emits a multi-value metric with one dimension per mapped bit.
  • Requires each declared key to represent either 0 or exactly one bit.
  • Preserves the raw numeric metric value alongside the decoded dimensions.
  • Ignores active bits that are not declared in mapping.items.

Extract Value

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:

  • Applies the regular expression to the raw SNMP value.
  • Uses the first capture group ( … ) as the new metric value.
  • If the pattern doesn’t match, the original value is kept.
  • Works for any metric type (string or numeric).
  • When multiple symbols are defined, the first non-empty result is used.
yaml
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:

  • Applies the regex (\d+) to the string "23.8 °C".
  • Extracts only the numeric part "23" and uses it as the metric value.
  • If the value doesn’t match, the original string is retained.
  • Ideal for string metrics that embed numbers, units, or labels.
  • If format: hex is also set, the extracted value is interpreted as hexadecimal before being stored as a metric.

Format

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.
yaml
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.

Scale Factor

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:

  • Multiplies the raw SNMP value by the specified factor.
  • Applies to both integer and floating-point values.
  • Keeps the result in the same numeric type (integer or float).
  • Works for metric values only (not for tags or metadata).
  • Applies after all other transformations on the same metric (such as extract_value).
yaml
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:

  • Multiplies octet counters by 8, reporting traffic in bits per second instead of bytes.
  • Converts ifHighSpeed from megabits to bits.
  • Ensures scaling happens after other transformations, such as value extraction or regex processing.

Virtual Metrics

  • Virtual metrics are calculated metrics built from other metrics in your profile (or inherited ones).
  • They don’t query SNMP; they reuse existing metric values to create totals, fallbacks, or per-row aggregations.
  • Once computed, they behave like normal metrics: charted, tagged, and alertable.
  • Virtual metrics are part of the regular metrics view. A virtual metric cannot depend on both regular metric rows and topology rows.

Common use cases:

  • Fallbacks: prefer 64-bit counters, fall back to 32-bit if missing.
  • Sums/Combines: add related metrics (e.g., in + out traffic).
  • Per-row totals: aggregate multiple columns into one per-interface metric.

Structure

yaml
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.

Config reference

ItemFieldTypeRequiredDefaultApplies toDescription
Virtual MetricnamestringyesallUnique within the profile. Used as metric/chart base name.
sourcesarray<Source>no*totals, per_row, groupedDirect source set. Ignored if alternatives exist (alternatives take precedence).
alternativesarray<Alternative>no*totals, per_row, groupedOrdered fallback sets. The first alternative whose sources produce data is used.
per_rowboolnofalseper-row/groupedWhen true, emits one output per input row; sources become dimensions; row tags attach.
group_byarray<string>noper-row/groupedLabel(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_tagsarray<EmitTag>noper-row/groupedRenames 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_metaobjectnoallPresentation metadata (description, family, unit, type).
SourcemetricstringyesName of an existing metric (scalar or table column metric).
tablestringno*Table name for the originating metric. Required for table-derived grouped/per-row virtual metrics. Scalar sources may omit it.
asstringnoOptional dimension name within a composite (e.g., in, out). Single-source virtual metrics do not need it.
dimstringnoSelects one dimension from a MultiValue source metric (for example start or established) before aggregation. Useful when composing virtual metrics from mapped status charts.
EmitTagtagstringyesOutput tag name to emit on the virtual metric.
fromstringyesExisting source-tag name to copy from the grouped source rows.
Alternativesourcesarray<Source>yesAll 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 sources or alternatives must be defined.

Rules & Constraints

RuleDescription
PrecedenceIf both sources and alternatives exist, alternatives take precedence.
Same-table requirementWhen 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: trueOne 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_tagsIf 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 evaluationAlternatives are checked in order. The first whose sources produce data becomes the “winner”; others are ignored.
Parent metadataThe virtual metric emits charts using its own name and chart_meta, even when data comes from an alternative.
DimensionsEach as value defines a dimension in the resulting chart (e.g., in, out, total).
Selected source dimensionWhen dim is set on a source, the collector reads only that MultiValue dimension from the source metric and ignores the rest.
Totals vs per-rowOmitting both per_row and group_by produces a single total chart across all rows (device-wide view).

Examples

Per-row aggregation from one table (in + out traffic)

yaml
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:

  • Creates one output per input row in ifXTable.
  • Each chart represents one interface with two dimensions: in and out.
  • group_by: ["interface"] provides key hints to keep per-interface charts stable.
  • If a hint is missing or empty, a stable key built from all non-underscore tags is used instead.
  • Constraint: per_row or group_by requires all sources to come from the same table.

Total aggregation (sum across all interfaces)

yaml
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:

  • Aggregates data from all rows in ifXTable into a single chart.
  • Produces two dimensions (in, out) representing the total interface traffic for the entire device.
  • No per_row or group_by fields → a single total chart (device-wide view).

Grouped aggregation (sum by label)

yaml
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"

Per-row availability from mapped status charts

yaml
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:

  • Reuses mapped one-hot status charts instead of adding duplicate raw-code metrics.
  • Reads only the start dimension from bgpPeerAdminStatus.
  • Reads only the established dimension from bgpPeerState.
  • Emits one per-peer chart row with stable dimensions admin_enabled and established.

Per-row grouping by private tags, but emitting normalized tags

yaml
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:

  • Groups rows using private helper tags that are not meant to appear in the final chart labels.
  • Emits standard operator-facing tags on the virtual metric (neighbor, address_family, subsequent_address_family).
  • Avoids collapsing multiple AFI/SAFI rows for the same peer into one output row.

What this does:

  • Performs PromQL-like “sum by (ifType)” aggregation.
  • Combines all rows sharing the same ifType label into grouped totals.
  • Result: one chart with in and out dimensions aggregated by interface type.
  • Constraint: all sources must come from the same table.

Alternatives (total; prefer 64-bit, fallback to 32-bit)

yaml
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:

  • Defines two alternatives, each as a list of sources.
  • At runtime, the collector evaluates alternatives in order and uses the first one where all source metrics exist and contain data (HC first).
  • Once a winner is found, later alternatives are ignored.
  • The parent emits metrics using its own name and chart_meta, sourcing values from the selected child.
  • If both sources and alternatives are present, alternatives take precedence.

Composite (multi-source total: unicast/multicast/broadcast

yaml
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

  • Builds a single total chart combining multiple related packet counters.
  • Each as becomes a dimension (in_ucast, out_ucast, in_mcast, …).
  • No per_row/group_by → totals aggregated across all interfaces.

BGP rows

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.

Authoring contract

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:

yaml
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:

yaml
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 }

Licensing rows

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.

Authoring contract

A licensing row describes one vendor license, entitlement, contract, or license pool. A row may be table-backed or scalar-backed:

  • A table-backed row declares table: and produces one typed license row per SNMP table row.
  • A scalar-backed row omits table: and produces one typed license row for the scalar values named in the row.
  • Scalar-backed rows that use only literal 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.remaining
    • authorization.timestamp / authorization.remaining
    • certificate.timestamp / certificate.remaining
    • grace.timestamp / grace.remaining
    • usage.used, usage.capacity, usage.available, usage.percent

Example table-backed row:

yaml
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:

yaml
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]

Parsing vendor expiry dates

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:

  • No format — for vendors that publish expiry as a plain integer unix epoch in 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_negative
  • timer_u32_max
  • timer_pre_1971

Identity and indexes

For 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:

yaml
identity:
  id:
    index: 1