| 1 | --- |
| 2 | name: project-snmp-profiles-authoring |
| 3 | description: Use when editing Netdata SNMP profile YAMLs, topology SNMP profiles, ddsnmp profile parsing, or profile-format documentation. Requires checking source MIB field accessibility, especially MAX-ACCESS not-accessible INDEX objects, before adding or changing profile symbols. |
| 4 | --- |
| 5 | |
| 6 | # SNMP Profile Authoring |
| 7 | |
| 8 | Use this skill before editing files under: |
| 9 | |
| 10 | - `src/go/plugin/go.d/config/go.d/snmp.profiles/` |
| 11 | - `src/go/plugin/go.d/collector/snmp/ddsnmp/` |
| 12 | - `src/go/plugin/go.d/collector/snmp/profile-format.md` |
| 13 | - `src/go/plugin/go.d/collector/snmp_topology/` |
| 14 | |
| 15 | ## Required Checks |
| 16 | |
| 17 | 1. Identify the source MIB object for every profile field being added or changed. |
| 18 | 2. Check the object's `MAX-ACCESS`. |
| 19 | 3. If the object is `not-accessible`, do not configure it as a readable `symbol.OID`. |
| 20 | 4. If a `not-accessible` object appears in the table `INDEX`, derive it from the row OID index using `index` or `index_transform`. |
| 21 | 5. Keep index extraction and value formatting separate: |
| 22 | - use `index` for one index component; |
| 23 | - use `index_transform` for multiple components; |
| 24 | - use `symbol.format` only for final formatting such as `ip_address`, `mac_address`, or `hex`. |
| 25 | 6. Put SNMP topology rows under top-level `topology:` with a required closed |
| 26 | `kind`. Do not mark topology rows by naming metrics `_topology_*`. |
| 27 | 7. Do not use chart/export-only value fields on topology row anchor symbols: |
| 28 | `chart_meta`, `metric_type`, `mapping`, `transform`, `scale_factor`, |
| 29 | `format`, or `constant_value_one`. |
| 30 | 8. Keep regular `systemUptime` rows under `metrics:`. Do not model uptime as a |
| 31 | topology row kind; topology-specific uptime acquisition belongs in collector |
| 32 | code, not profile topology schema. |
| 33 | 9. Put SNMP licensing rows under top-level `licensing:`. Do not model licensing |
| 34 | telemetry as underscore-prefixed hidden metrics or `_license_*` tag |
| 35 | protocols. |
| 36 | 10. Licensing row value symbols may use `format` and exact `mapping`, but must |
| 37 | not use chart/export fields, transforms, scale factors, constant values, or |
| 38 | underscore-prefixed generated names. |
| 39 | 11. For scalar licensing rows that combine multiple scalar signal OIDs into one |
| 40 | license row, declare an explicit stable `id:`. For table licensing rows, |
| 41 | keep `from:` references inside the same table OID and derive |
| 42 | `not-accessible` INDEX values from the row index. |
| 43 | 12. Put SNMP BGP rows under top-level `bgp:`. Do not model BGP telemetry as |
| 44 | vendor-specific raw metrics, `virtual_metrics`, or underscore-prefixed tag |
| 45 | protocols when adding or migrating BGP coverage. |
| 46 | 13. BGP peer-state mappings must use the six RFC 4271 canonical states |
| 47 | (`idle`, `connect`, `active`, `opensent`, `openconfirm`, `established`). |
| 48 | Use `partial: true` plus `partial_states` only when the source MIB is |
| 49 | intentionally partial. |
| 50 | 14. For BGP table rows, derive `not-accessible` INDEX objects with exactly one |
| 51 | row-index selector: `index`, `index_from_end`, or `index_transform`. Use |
| 52 | `index_from_end` for trailing AFI/SAFI-like components after variable-length |
| 53 | indexes such as `InetAddress`. |
| 54 | |
| 55 | ## Index Rules |
| 56 | |
| 57 | - `index` is 1-based. |
| 58 | - `index_transform.start` and `index_transform.end` are 0-based and inclusive. |
| 59 | - `index_transform: [{start: N}]` keeps index component `N` through the last component when `N > 0`. |
| 60 | - `index_transform: [{start: 0, end: 0}]` keeps only the first index component. |
| 61 | - `drop_right` can be used when the right side has fixed trailing components. |
| 62 | |
| 63 | ## Common Patterns |
| 64 | |
| 65 | Q-BRIDGE learned FDB MAC: |
| 66 | |
| 67 | ```yaml |
| 68 | - tag: dot1q_fdb_mac |
| 69 | symbol: |
| 70 | format: mac_address |
| 71 | index_transform: |
| 72 | - start: 1 |
| 73 | ``` |
| 74 | |
| 75 | IP-MIB `ipNetToPhysicalTable` address: |
| 76 | |
| 77 | ```yaml |
| 78 | - tag: arp_ip |
| 79 | symbol: |
| 80 | format: ip_address |
| 81 | index_transform: |
| 82 | - start: 3 |
| 83 | ``` |
| 84 | |
| 85 | The `start: 3` skips `ifIndex`, address type, and the InetAddress length byte. |
| 86 | |
| 87 | LLDP-MIB local management address: |
| 88 | |
| 89 | ```yaml |
| 90 | - tag: lldp_loc_mgmt_addr |
| 91 | symbol: |
| 92 | name: lldpLocManAddr |
| 93 | format: hex |
| 94 | index_transform: |
| 95 | - start: 2 |
| 96 | ``` |
| 97 | |
| 98 | The `start: 2` skips management-address subtype and length. Use `hex`, not |
| 99 | `ip_address`, because LLDP management addresses can carry non-IP subtypes; the |
| 100 | topology runtime normalizes IP-compatible bytes later. |
| 101 | |
| 102 | ## Audit Recipe |
| 103 | |
| 104 | When a profile reads a table column, verify that the MIB object is readable: |
| 105 | |
| 106 | ```bash |
| 107 | rg -n -C 4 'OBJECT-TYPE|MAX-ACCESS[[:space:]]+not-accessible|ACCESS[[:space:]]+not-accessible' path/to/MIB |
| 108 | ``` |
| 109 | |
| 110 | For known topology-sensitive symbols, scan profile YAMLs before committing: |
| 111 | |
| 112 | ```bash |
| 113 | rg -n 'name:[[:space:]]*(dot1qTpFdbAddress|ipNetToPhysicalIfIndex|ipNetToPhysicalNetAddressType|ipNetToPhysicalNetAddress|lldpLocManAddrSubtype|lldpLocManAddr)\b' src/go/plugin/go.d/config/go.d/snmp.profiles |
| 114 | ``` |
| 115 | |
| 116 | Any hit must be reviewed. It is valid only when the tag is index-derived and does not declare `symbol.OID` for a `not-accessible` object. |
| 117 | |
| 118 | When adding a new topology kind, update all three parts together: |
| 119 | |
| 120 | - profile YAML using `topology: - kind: <kind>`; |
| 121 | - the Go `TopologyKind` enum and validation; |
| 122 | - the topology cache handler registry and tests. |
| 123 | |
| 124 | Verify that topology rows are delivered through `ProfileMetrics.TopologyMetrics`, |
| 125 | not through underscore-prefixed `HiddenMetrics`. |
| 126 | |
| 127 | When adding or migrating licensing profile coverage, update all related parts |
| 128 | together: |
| 129 | |
| 130 | - profile YAML using `licensing:`; |
| 131 | - the closed licensing signal/state/sentinel enums and validation when adding |
| 132 | new policy names; |
| 133 | - typed `ProfileMetrics.LicenseRows` producer/consumer tests; |
| 134 | - MIB evidence for every OID and every `not-accessible` index-derived field. |
| 135 | |
| 136 | Verify that licensing rows are delivered through `ProfileMetrics.LicenseRows`, |
| 137 | not through underscore-prefixed `HiddenMetrics`. |
| 138 | |
| 139 | When adding or migrating BGP profile coverage, update all related parts |
| 140 | together: |
| 141 | |
| 142 | - profile YAML using `bgp:`; |
| 143 | - closed BGP row kind, peer-state, AFI/SAFI, and typed field validation when |
| 144 | adding new policy names or value domains; |
| 145 | - typed `ProfileMetrics.BGPRows` producer/consumer tests; |
| 146 | - MIB evidence for every OID and every `not-accessible` index-derived field; |
| 147 | - SNMP integration metadata and generated docs when public BGP capability |
| 148 | claims change. |
| 149 | |
| 150 | Verify that BGP rows are delivered through `ProfileMetrics.BGPRows`, not |
| 151 | through `metrics:`, `virtual_metrics:`, or underscore-prefixed hidden metrics. |
| 152 | |
| 153 | When adding or refactoring SNMP profile, parser, or topology tests, prefer |
| 154 | table-driven cases using `map[string]struct{}` keyed by test-case name when |
| 155 | the cases share setup and assertion shape. Use separate test functions only for |
| 156 | materially different setup or assertions. |
| 157 | |
| 158 | ## Validation |
| 159 | |
| 160 | Run the narrow suites for the changed area: |
| 161 | |
| 162 | ```bash |
| 163 | cd src/go |
| 164 | go test ./plugin/go.d/collector/snmp/ddsnmp/ddprofiledefinition |
| 165 | go test ./plugin/go.d/collector/snmp/ddsnmp/ddsnmpcollector |
| 166 | go test ./plugin/go.d/collector/snmp_topology |
| 167 | ``` |
| 168 | |
| 169 | See `src/go/plugin/go.d/collector/snmp/profile-format.md` for the full profile syntax and the "Field accessibility" section. |