SNMP Profile Authoring
Use this skill before editing files under:
src/go/plugin/go.d/config/go.d/snmp.profiles/src/go/plugin/go.d/collector/snmp/ddsnmp/src/go/plugin/go.d/collector/snmp/profile-format.mdsrc/go/plugin/go.d/collector/snmp_topology/
Boundary: this skill owns the profile topology: rows, which OIDs feed the SNMP topology producer and how they are
extracted. What the producer emits from those observations (actors, links, evidence, modals) is the payload contract
in src/plugins.d/FUNCTION_TOPOLOGY_DEVELOPER_GUIDE.md; the developer workflow for changing it is
.agents/skills/topology-authoring/SKILL.md.
Required Checks
- Identify the source MIB object for every profile field being added or changed.
- Check the object's
MAX-ACCESS. - If the object is
not-accessible, do not configure it as a readablesymbol.OIDunless it satisfies every device-scoped MIB-deviation requirement below. - If a
not-accessibleobject appears in the tableINDEX, derive it from the row OID index usingindexorindex_transform. - Keep index extraction and value formatting separate:
- use
indexfor one index component; - use
index_transformfor multiple components; - use
symbol.formatonly for final formatting such asip_address,mac_address, orhex.
- use
- Put SNMP topology rows under top-level
topology:with a required closedkind. Do not mark topology rows by naming metrics_topology_*. - Do not use chart/export-only value fields on topology row anchor symbols:
chart_meta,metric_type,mapping,transform,scale_factor, orformat. - Keep regular
systemUptimerows undermetrics:. Do not model uptime as a topology row kind; topology-specific uptime acquisition belongs in collector code, not profile topology schema. - Put SNMP licensing rows under top-level
licensing:. Do not model licensing telemetry as underscore-prefixed hidden metrics or_license_*tag protocols. - Licensing row value symbols may use
formatand exactmapping, but must not use chart/export fields, transforms, scale factors, or underscore-prefixed generated names. - For scalar licensing rows that combine multiple scalar signal OIDs into one
license row, declare an explicit stable
id:. For table licensing rows, keep every effective OID source (symbol.OID,from, or legacyOID) inside the same table OID and derivenot-accessibleINDEX values from the row index. - Put SNMP BGP rows under top-level
bgp:. Do not model BGP telemetry as vendor-specific raw metrics,virtual_metrics, or underscore-prefixed tag protocols when adding or migrating BGP coverage. - BGP peer-state mappings must use the six RFC 4271 canonical states
(
idle,connect,active,opensent,openconfirm,established). Usepartial: truepluspartial_statesonly when the source MIB is intentionally partial. - For BGP table rows, derive
not-accessibleINDEX objects with exactly one row-index selector:index,index_from_end, orindex_transform. Useindex_from_endfor trailing AFI/SAFI-like components after variable-length indexes such asInetAddress. - To constrain a topology table to a fixed structural index prefix, use one
readable anchor symbol and set
table.OIDto<anchor-column>.<prefix>. A simple same-index cross-table dependency inherits that prefix and is anchor-gated only when its target table has no symbol-bearing producer. A target route with a real symbol owner remains eager. Do not rely on propagation for ordinary metrics, multiple anchors,lookup_symbol,index_transform, or conflicting dependency scopes.
Device-Scoped MIB Deviations
A vendor profile MAY poll an object that the source MIB marks not-accessible only when all of these requirements hold:
- Scope: The polling row is declared in a selector-matched device or topology-role profile. A generic standards profile MUST NOT carry the deviation.
- Proof: A checked repository fixture or repeatable live capture proves that the matched device returns the object as a readable column and omits the standards-readable anchor needed to obtain the row otherwise.
- Isolation: The device row overrides the generic row identity instead of adding a second generic fallback or a second topology kind.
- Documentation: A short profile comment explains the vendor deviation and why index-only decoding cannot be used.
- Regression: Tests pin both the selector-scoped override and real fixture collection through the profile parser and consumer path.
Do not generalize a device deviation to related models or vendors without equivalent evidence.
Canonical Profile Syntax
Stock profiles MUST use symbol objects for OID/name pairs, including metric tags and typed licensing/BGP values.
Put metric type overrides on each affected symbol or symbols entry instead of the deprecated row-level metric_type.
Legacy custom profile aliases remain supported; typed OID precedence is symbol.OID, then from, then legacy OID.
Collection, boundary validation and inheritance identity use that same source. Prefer one OID source form per value.
Metric and topology column rows require table.OID; table.name is optional for those rows.
The retired constant_value_one key is ignored. Ordinary metric symbols still require OIDs; do not use the key to generate or suppress metrics.
Index Rules
indexis 1-based.index_transform.startandindex_transform.endare 0-based and inclusive.index_transform: [{start: N}]keeps index componentNthrough the last component whenN > 0.index_transform: [{start: 0, end: 0}]keeps only the first index component.drop_rightcan be used when the right side has fixed trailing components.
Common Patterns
Q-BRIDGE learned FDB MAC:
- tag: dot1q_fdb_mac
symbol:
format: mac_address
index_transform:
- start: 1
IP-MIB ipNetToPhysicalTable address:
- tag: arp_ip
symbol:
format: ip_address
index_transform:
- start: 3
The start: 3 skips ifIndex, address type, and the InetAddress length byte.
IP-MIB ipAddressTable IPv4 address:
table:
OID: 1.3.6.1.2.1.4.34.1.3.1.4
name: ipAddressTableIPv4
symbols:
- OID: 1.3.6.1.2.1.4.34.1.3
name: ip_if_index
metric_tags:
- tag: topo_ip_addr
symbol:
name: ipAddressAddr
index_transform:
- start: 2
The table root anchors on readable ipAddressIfIndex and appends index prefix
1.4 for IPv4 type and its required four-octet length. The address is the
not-accessible ipAddressAddr index component; start: 2 skips address type
and length. The open-ended slice is intentional: a fixed end would hide
malformed trailing components instead of letting the strict consumer reject
the row. Keep the transformed components raw and require the topology
consumer to decode exactly four decimal octets in 0..255; structural walk
scope alone does not validate the remaining instance suffix. A malformed
descendant can activate dependency walks, but it MUST NOT emit topology.
LLDP-MIB local management address:
- tag: lldp_loc_mgmt_addr
symbol:
name: lldpLocManAddr
format: hex
index_transform:
- start: 2
The start: 2 skips management-address subtype and length. Use hex, not
ip_address, because LLDP management addresses can carry non-IP subtypes; the
topology runtime normalizes IP-compatible bytes later.
Audit Recipe
When a profile reads a table column, verify that the MIB object is readable:
rg -n -C 4 'OBJECT-TYPE|MAX-ACCESS[[:space:]]+not-accessible|ACCESS[[:space:]]+not-accessible' path/to/MIB
For known topology-sensitive symbols, scan profile YAMLs before committing:
rg -n 'name:[[:space:]]*(dot1qTpFdbAddress|ipAddressAddr|ipNetToPhysicalIfIndex|ipNetToPhysicalNetAddressType|ipNetToPhysicalNetAddress|lldpLocManAddrSubtype|lldpLocManAddr|lldpRemManAddrSubtype|lldpRemManAddr)\b' src/go/plugin/go.d/config/go.d/snmp.profiles
Any hit must be reviewed. It is valid only when the tag is index-derived without a symbol.OID, or when it satisfies every
device-scoped MIB-deviation requirement above.
When adding a new topology kind, update all three parts together:
- profile YAML using
topology: - kind: <kind>; - the Go
TopologyKindenum and validation; - the topology cache handler registry and tests.
Verify that topology rows are delivered through ProfileMetrics.TopologyMetrics,
not through underscore-prefixed HiddenMetrics.
When adding or migrating licensing profile coverage, update all related parts together:
- profile YAML using
licensing:; - the closed licensing signal/state/sentinel enums and validation when adding new policy names;
- typed
ProfileMetrics.LicenseRowsproducer/consumer tests; - MIB evidence for every OID and every
not-accessibleindex-derived field.
Verify that licensing rows are delivered through ProfileMetrics.LicenseRows,
not through underscore-prefixed HiddenMetrics.
When adding or migrating BGP profile coverage, update all related parts together:
- profile YAML using
bgp:; - closed BGP row kind, peer-state, AFI/SAFI, and typed field validation when adding new policy names or value domains;
- typed
ProfileMetrics.BGPRowsproducer/consumer tests; - MIB evidence for every OID and every
not-accessibleindex-derived field; - SNMP integration metadata and generated docs when public BGP capability claims change.
Verify that BGP rows are delivered through ProfileMetrics.BGPRows, not
through metrics:, virtual_metrics:, or underscore-prefixed hidden metrics.
When adding or refactoring SNMP profile, parser, or topology tests, prefer
table-driven cases using map[string]struct{} keyed by test-case name when
the cases share setup and assertion shape. Use separate test functions only for
materially different setup or assertions.
Validation
Run the narrow suites for the changed area:
cd src/go
go test ./plugin/go.d/collector/snmp/ddsnmp/ddprofiledefinition
go test ./plugin/go.d/collector/snmp/ddsnmp/ddsnmpcollector
go test ./plugin/go.d/collector/snmp_topology
See src/go/plugin/go.d/collector/snmp/profile-format.md for the full profile syntax and the "Field accessibility" section.