docs: add SNMP profile format doc (#21201)
Ilya Mashchenko committed
Oct 26, 2025 at 18:17 UTC
b509bf311fe38de1883898adf61dc7e20b4f5b2f
1 file changed
+1557
src/go/plugin/go.d/collector/snmp/profile-format.md
new
+1557
@@ -0,0 +1,1557 @@
1
+# Profile Format
2
+
3
+## Overview
4
+
5
+An **SNMP profile** defines _how a specific class of devices is monitored through SNMP_.
6
+
7
+:::info
8
+
9
+SNMP profiles are reusable and declarative — you never need to modify the collector source code to support new devices.
10
+
11
+:::
12
+
13
+It tells the Netdata SNMP collector:
14
+
15
+- which **OIDs** to query
16
+- how to **interpret** the returned values
17
+- how to **transform** them into **metrics**, **dimensions**, **tags**, and **metadata**
18
+
19
+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.
20
+
21
+Each profile is a single YAML file that can be reused, extended, and combined.
22
+
23
+### How Profiles Work
24
+
25
+When Netdata connects to an SNMP device, the collector:
26
+
27
+1. Reads the device’s **sysObjectID** and **sysDescr**.
28
+2. Evaluates all available profiles.
29
+3. Applies every profile whose [selector](#1-selector) matches.
30
+4. Uses the combined configuration to:
31
+ - Collect [scalar metrics](#scalar-metrics-single-values) (single values like uptime or temperature).
32
+ - Collect [table metrics](#table-metrics-multiple-rows) (multi-row values like per-interface traffic).
33
+ - Build [virtual metrics](#virtual-metrics) (derived totals, fallbacks, or aggregates).
34
+ - Gather [metadata](#3-metadata) and [tags](#5-metric_tags) for labeling and grouping.
35
+
36
+**Profile Lifecycle**
37
+
38
+```text
39
+┌──────────────────────┐
40
+│ SNMP Device │ → provides sysObjectID/sysDescr
41
+└──────────┬───────────┘
42
+ ↓
43
+┌──────────────────────┐
44
+│ selector │ → matches device profile
45
+├──────────────────────┤
46
+│ extends │ → inherits base profiles
47
+├──────────────────────┤
48
+│ metadata │ → device info (vendor, model, etc.)
49
+├──────────────────────┤
50
+│ metrics │ → OIDs to collect
51
+├──────────────────────┤
52
+│ metric_tags │ → dynamic tags for all metrics
53
+├──────────────────────┤
54
+│ static_tags │ → fixed tags for all metrics
55
+├──────────────────────┤
56
+│ virtual_metrics │ → calculated or aggregated metrics
57
+└──────────┬───────────┘
58
+ ↓
59
+┌──────────────────────┐
60
+│ Netdata charts & UI │ → visualized in dashboard
61
+└──────────────────────┘
62
+```
63
+
64
+### Example: Complete SNMP Profile
65
+
66
+```yaml
67
+# example-device.yaml
68
+
69
+# selects which devices this profile applies to.
70
+selector:
71
+ - sysobjectid:
72
+ include: ["1.3.6.1.4.1.9.*"] # Cisco devices
73
+ sysdescr:
74
+ include: ["IOS"]
75
+
76
+# imports common base metrics
77
+extends:
78
+ - _system-base.yaml
79
+ - _std-if-mib.yaml
80
+
81
+# defines device-level labels (virtual node)
82
+metadata:
83
+ device:
84
+ fields:
85
+ vendor:
86
+ value: "Cisco"
87
+ model:
88
+ symbol:
89
+ OID: 1.3.6.1.2.1.47.1.1.1.1.2.1
90
+ name: entPhysicalModelName
91
+
92
+# specifies which OIDs to collect
93
+metrics:
94
+ - MIB: IF-MIB
95
+ table:
96
+ OID: 1.3.6.1.2.1.2.2
97
+ name: ifTable
98
+ symbols:
99
+ - OID: 1.3.6.1.2.1.2.2.1.10
100
+ name: ifInOctets
101
+ chart_meta:
102
+ description: Interface inbound traffic
103
+ family: 'Network/Interface/Traffic/In'
104
+ unit: "bit/s"
105
+ scale_factor: 8
106
+ metric_tags:
107
+ - tag: interface
108
+ symbol:
109
+ OID: 1.3.6.1.2.1.31.1.1.1.1
110
+ name: ifName
111
+
112
+# add dynamic tags to all metrics
113
+metric_tags:
114
+ - tag: fs_sys_version
115
+ symbol:
116
+ OID: 1.3.6.1.4.1.9.2.1.73.0
117
+ name: fsSysVersion
118
+
119
+# add fixed tags to all metrics
120
+static_tags:
121
+ - tag: region
122
+ value: "us-east-1"
123
+ - tag: environment
124
+ value: "production"
125
+
126
+# computes combined metrics
127
+virtual_metrics:
128
+ - name: ifTotalTraffic
129
+ sources:
130
+ - { metric: _ifHCInOctets, table: ifXTable, as: in }
131
+ - { metric: _ifHCOutOctets, table: ifXTable, as: out }
132
+ chart_meta:
133
+ description: Total traffic across all interfaces
134
+ family: 'Network/Total/Traffic'
135
+ unit: "bit/s"
136
+```
137
+
138
+## Profile Structure
139
+
140
+Each SNMP profile is a YAML file that defines **how Netdata collects, interprets, and labels SNMP metrics** from a device.
141
+
142
+Profiles are modular — you can extend others, define metadata, and specify what to collect.
143
+
144
+```yaml
145
+selector: <device matching pattern>
146
+extends: <base profiles to include>
147
+metadata: <device information>
148
+metrics: <what to collect>
149
+metric_tags: <global tags>
150
+static_tags: <static tags>
151
+virtual_metrics: <calculated metrics>
152
+```
153
+
154
+| Section | Purpose |
155
+|-------------------------------------------|-----------------------------------------------------------|
156
+| [**selector**](#1-selector) | Defines which devices the profile applies to. |
157
+| [**extends**](#2-extends) | Inherits and merges other base profiles. |
158
+| [**metadata**](#3-metadata) | Collects device-level information (host labels). |
159
+| [**metrics**](#4-metrics) | Defines which OIDs to collect and how to chart them. |
160
+| [**metric_tags**](#5-metric_tags) | Defines global dynamic tags collected once per device. |
161
+| [**static_tags**](#6-static_tags) | Defines fixed tags applied to all metrics. |
162
+| [**virtual_metrics**](#7-virtual_metrics) | Defines calculated or aggregated metrics based on others. |
163
+
164
+### 1. selector
165
+
166
+You use the selector to:
167
+
168
+- Target specific **device families** (e.g., Cisco, Juniper, HP)
169
+- Match devices supporting specific **MIBs**
170
+- Exclude unwanted devices
171
+
172
+During discovery, Netdata evaluates all profiles; any profile whose selector matches a device is **applied **.
173
+
174
+```yaml
175
+selector:
176
+ - sysobjectid:
177
+ include: ["1.3.6.1.4.1.9.*"] # regex: Cisco enterprise OID subtree
178
+ exclude: ["1.3.6.1.4.1.9.9.666"] # optional excludes
179
+ sysdescr:
180
+ include: ["IOS"] # substring (case-insensitive)
181
+ exclude: ["emulator", "lab"] # optional excludes
182
+```
183
+
184
+**How it works**:
185
+
186
+- Each selector rule is a set of conditions (`sysobjectid`, `sysdescr`, etc.).
187
+- For a rule to match, **all its conditions must pass**.
188
+- A profile is applied if **at least one rule** in the `selector` list matches the device.
189
+- If both `sysobjectid` and `sysdescr` are defined within the same rule, **both must succeed**.
190
+
191
+**Supported conditions**:
192
+
193
+| Key | What It Checks | Match Criteria (Pass) | Fails When... |
194
+|-----------------------|--------------------------------------|--------------------------------------------------|---------------------------------|
195
+| `sysobjectid.include` | Device `sysObjectID` | Matches **at least one** pattern in the list. | No items match. |
196
+| `sysobjectid.exclude` | Device `sysObjectID` | Matches **none** of the listed patterns. | Any item matches. |
197
+| `sysdescr.include` | Device `sysDescr` (case-insensitive) | Contains **at least one** substring in the list. | No listed substrings are found. |
198
+| `sysdescr.exclude` | Device `sysDescr` (case-insensitive) | Contains **none** of the listed substrings. | Any listed substring is found. |
199
+
200
+### 2. extends
201
+
202
+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.
203
+
204
+Most real profiles extend a few shared building blocks and then add device-specific definitions.
205
+
206
+```yaml
207
+extends:
208
+ - _system-base.yaml # System basics (uptime, contact, location)
209
+ - _std-if-mib.yaml # Network interfaces (IF-MIB)
210
+ - _std-ip-mib.yaml # IP statistics
211
+```
212
+
213
+The final profile is the **merged result** of all inherited profiles plus the content in the current file.
214
+
215
+**How inheritance works**:
216
+
217
+1. **Order matters** — profiles are loaded in the order listed.
218
+2. **Metrics are merged** — all metrics from all referenced profiles are included.
219
+3. **Later overrides earlier** — if the same field is defined multiple times, the last one wins.
220
+
221
+**Common base profiles**
222
+
223
+| Profile | Provides | Typical Use |
224
+|---------------------|-------------------------------------------|--------------------|
225
+| `_system-base.yaml` | Basic system info (uptime, name, contact) | All devices |
226
+| `_std-if-mib.yaml` | Interface statistics (IF-MIB) | Network devices |
227
+| `_std-ip-mib.yaml` | IP-level statistics (IP-MIB) | Routers, switches |
228
+| `_std-tcp-mib.yaml` | TCP statistics | Servers, firewalls |
229
+| `_std-udp-mib.yaml` | UDP statistics | Servers, firewalls |
230
+| `_std-ups-mib.yaml` | Power and UPS metrics | UPS devices |
231
+
232
+### 3. metadata
233
+
234
+The `metadata` section defines **device-level information** (not metric tags).
235
+
236
+It is collected **once per device** and populates the device’s **host labels** in Netdata (the “virtual node” labels shown on the device page).
237
+
238
+It always follows the structure `metadata → device → fields`, where each field defines a single label.
239
+
240
+Each field can be:
241
+
242
+- **Static** — `value:` is a fixed string.
243
+- **Dynamic** — `symbol:` reads the value from an SNMP OID.
244
+
245
+```yaml
246
+metadata:
247
+ device:
248
+ fields:
249
+ vendor:
250
+ value: "Cisco" # static label
251
+ model:
252
+ symbol: # dynamic label from SNMP
253
+ OID: 1.3.6.1.2.1.47.1.1.1.1.2.1
254
+ name: entPhysicalModelName
255
+```
256
+
257
+:::info
258
+
259
+You can define multiple symbols for fallback — the first valid one will be used.
260
+
261
+:::
262
+
263
+**How it works**:
264
+
265
+- `vendor` is set statically to `"Cisco"`.
266
+- `model` is collected from `entPhysicalModelName`.
267
+- These values appear as **device (virtual node) host labels** in the UI. They are **not** per-metric tags.
268
+
269
+:::tip
270
+
271
+See [**Tag Transformation**](#tag-transformation) for supported transformations and syntax examples.
272
+
273
+:::
274
+
275
+### 4. metrics
276
+
277
+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.
278
+
279
+**Metrics can be**:
280
+
281
+- **Scalars** — single values that apply to the entire device (for example, uptime).
282
+- **Tables** — repeating rows of related values (for example, interfaces, disks, sensors).
283
+
284
+:::note
285
+
286
+A metric is **either scalar** (single value) or **table-based** (multiple rows).
287
+Never mix both in the same metric entry.
288
+
289
+:::
290
+
291
+The collector automatically uses **SNMP GET** for scalars and **SNMP BULKWALK** for tables.
292
+
293
+```yaml
294
+metrics:
295
+ - MIB: HOST-RESOURCES-MIB
296
+ symbol:
297
+ OID: 1.3.6.1.2.1.1.3.0
298
+ name: systemUptime
299
+ scale_factor: 0.01 # Value is in hundredths of a second
300
+ chart_meta:
301
+ description: Time since the system was last rebooted or powered on
302
+ family: 'System/Uptime'
303
+ unit: "s"
304
+
305
+ - MIB: IF-MIB
306
+ table:
307
+ OID: 1.3.6.1.2.1.31.1.1
308
+ name: ifXTable
309
+ symbols:
310
+ - OID: 1.3.6.1.2.1.31.1.1.1.6
311
+ name: ifHCInOctets
312
+ chart_meta:
313
+ description: Traffic
314
+ family: 'Network/Interface/Traffic/In'
315
+ unit: "bit/s"
316
+ scale_factor: 8 # Octets → bits
317
+ metric_tags:
318
+ - tag: interface
319
+ symbol:
320
+ OID: 1.3.6.1.2.1.31.1.1.1.1
321
+ name: ifName
322
+```
323
+
324
+**How it works**:
325
+
326
+- Each entry defines a metric or table of metrics to collect via SNMP.
327
+- Scalars use a single `symbol`, while tables define a `table` and one or more `symbols`.
328
+- Metrics can include transformations (`extract_value`, `scale_factor`, etc.) and chart metadata.
329
+- Table metrics can include tags (`metric_tags`) to identify rows by interface, disk, or other attributes.
330
+
331
+:::tip
332
+
333
+See also
334
+
335
+- [Collecting Metrics](#collecting-metrics) — explains SNMP OIDs, scalars, and tables.
336
+- [Scalar Metrics](#scalar-metrics-single-values) — detailed syntax for single-value metrics.
337
+- [Table Metrics](#table-metrics-multiple-rows)— how tables and row indexes work.
338
+- [Adding Tags to Metrics](#adding-tags-to-metrics) — tag types and how to label table rows.
339
+- [Tag Transformations](#tag-transformation) — extract, match, or map tag values.
340
+- [Value Transformations](#value-transformation) — manipulate or scale collected values.
341
+- [Virtual Metrics](#virtual-metrics) — build new metrics from existing ones.
342
+
343
+:::
344
+
345
+### 5. metric_tags
346
+
347
+The `metric_tags` section defines **global dynamic tags** — values collected once from the device and applied to **every metric** in the profile.
348
+
349
+They are evaluated during collection, just like other SNMP symbols, and remain the same for all metrics within that device.
350
+
351
+**Typical uses**:
352
+
353
+- Attach device-wide metadata such as serial number, firmware version, or model.
354
+- Enable grouping and filtering by hardware, OS, or vendor attributes.
355
+- Complement per-metric tags (for example, per-interface or per-sensor tags in tables).
356
+
357
+```yaml
358
+metric_tags:
359
+ - tag: fs_sys_serial
360
+ symbol:
361
+ OID: 1.3.6.1.4.1.12356.106.1.1.1.0
362
+ name: fsSysSerial
363
+ - tag: fs_sys_version
364
+ symbol:
365
+ OID: 1.3.6.1.4.1.12356.106.4.1.1.0
366
+ name: fsSysVersion
367
+```
368
+
369
+**How it works**:
370
+
371
+- Each tag is collected once per device, not per metric or per table row.
372
+- The resulting tag values are attached to **all metrics** collected by the profile.
373
+- Tags can be transformed (for example, reformatted or mapped) using the same rules as per-metric tags.
374
+
375
+:::tip
376
+
377
+See [**Tag Transformation**](#tag-transformation) for supported transformations and syntax examples.
378
+
379
+:::
380
+
381
+### 6. static_tags
382
+
383
+The `static_tags` section defines **fixed key–value pairs** that are attached to every metric collected by the profile.
384
+
385
+They don’t depend on SNMP data and remain constant for all devices using the profile.
386
+
387
+**Typical uses**:
388
+
389
+- Add environment or deployment identifiers (for example, `environment`, `region`, or `service`).
390
+- Simplify filtering, grouping, and alerting across metrics from multiple devices.
391
+- Provide consistent context (for example, datacenter or team ownership).
392
+
393
+```yaml
394
+static_tags:
395
+ - tag: environment
396
+ value: production
397
+ - tag: region
398
+ value: us-east-1
399
+ - tag: service
400
+ value: network
401
+```
402
+
403
+**How it works**:
404
+
405
+- Each tag is added to **all metrics** collected by the profile.
406
+- Static tags are merged with any dynamic tags defined in `metric_tags`.
407
+- Device-specific or dynamic tags always take precedence if they overlap.
408
+
409
+### 7. virtual_metrics
410
+
411
+The `virtual_metrics` section defines **calculated metrics** built from other metrics already collected by the profile.
412
+
413
+They don’t query SNMP directly — instead, they reuse existing metric values to produce totals, sums, or fallbacks.
414
+
415
+:::tip
416
+
417
+See [**Virtual Metrics**](#virtual-metrics) for the complete reference, configuration options, and advanced examples.
418
+
419
+:::
420
+
421
+**Typical uses**:
422
+
423
+- Combine related counters (for example, `in` + `out` traffic or errors).
424
+- Create fallbacks (prefer 64-bit counters, fall back to 32-bit if missing).
425
+- Aggregate or group metrics per tag (for example, total per interface or per type).
426
+
427
+```yaml
428
+ - name: ifTotalTraffic
429
+ sources:
430
+ - { metric: ifHCInOctets, table: ifXTable, as: in }
431
+ - { metric: ifHCOutOctets, table: ifXTable, as: out }
432
+ chart_meta:
433
+ description: Total traffic across all interfaces
434
+ family: 'Network/Total/Traffic'
435
+ unit: "bit/s"
436
+```
437
+
438
+**How it works**:
439
+
440
+- Defines a new virtual metric named `ifTotalTraffic`.
441
+- Uses existing metrics (`ifHCInOctets`, `ifHCOutOctets`) as sources.
442
+- The `as` field names the resulting dimensions (`in`, `out`).
443
+- The resulting chart behaves like a regular metric — visible in dashboards, alertable, and included in exports.
444
+
445
+## Collecting Metrics
446
+
447
+This section explains how SNMP data is structured and how it maps to metrics in a Netdata profile.
448
+
449
+### Understanding SNMP Data
450
+
451
+SNMP data is organized as a **hierarchical tree** of numeric identifiers called **OIDs** (**Object Identifiers**).
452
+
453
+Each OID uniquely identifies a value on a device — similar to a file path in a filesystem.
454
+
455
+```text
456
+1.3.6.1.2.1.1.3.0
457
+│ │ │ │ │ │ │ └── Instance (0 = scalar)
458
+│ │ │ │ │ │ └──── Object (3 = sysUpTime)
459
+│ │ │ │ │ └────── Branch: system (MIB-2)
460
+│ │ │ └────────── MIB-2 root
461
+└─ SNMP global prefix
462
+```
463
+
464
+- **MIBs** (Management Information Bases) are named collections of related OIDs.
465
+
466
+ Examples: `IF-MIB` (interfaces), `IP-MIB` (IP statistics), `HOST-RESOURCES-MIB` (system info).
467
+- Each OID maps to a **typed value**, such as `Counter64`, `Gauge32`, `Integer`, or `TimeTicks`.
468
+- Some OIDs represent **single values** (scalars), while others represent **tables** of related values (rows).
469
+
470
+### Scalar Metrics (Single Values)
471
+
472
+Scalar metrics represent a **single value for the entire device**.
473
+
474
+Their OIDs always end with `.0`, which denotes the **instance number** for a scalar object.
475
+
476
+```yaml
477
+metrics:
478
+ - MIB: HOST-RESOURCES-MIB
479
+ symbol:
480
+ OID: 1.3.6.1.2.1.1.3.0
481
+ name: systemUptime
482
+ scale_factor: 0.01 # Value is in hundredths of a second
483
+ chart_meta:
484
+ description: Time since the system was last rebooted or powered on.
485
+ family: 'System/Uptime'
486
+ unit: "s"
487
+```
488
+
489
+**What this does**:
490
+
491
+- Collects the `sysUpTime` value once per device.
492
+- The `.0` at the end indicates there is only **one instance** of this value.
493
+- Common scalar metrics: device uptime, total memory, or overall temperature.
494
+
495
+### Table Metrics (Multiple Rows)
496
+
497
+Table metrics represent **lists of related values**, such as one entry per network interface, disk, or CPU.
498
+
499
+Each row in a table is identified by an **index** appended to the base OID — for example:
500
+
501
+```text
502
+ifHCInOctets.1 = 1024
503
+ifHCInOctets.2 = 2048
504
+```
505
+
506
+- `.1`, `.2`, … are `row indexes` that identify the instance (e.g., interface #1, interface #2).
507
+- Each column (symbol) in the table has its own OID pattern but shares the same row indexes.
508
+
509
+> Table metrics **must define at least one tag** (`metric_tags`) to identify each row.
510
+> Without tags, only a single row can be emitted.
511
+
512
+```yaml
513
+metrics:
514
+ - MIB: IF-MIB
515
+ table:
516
+ OID: 1.3.6.1.2.1.31.1.1
517
+ name: ifXTable
518
+ symbols:
519
+ - OID: 1.3.6.1.2.1.31.1.1.1.6
520
+ name: ifHCInOctets
521
+ chart_meta:
522
+ description: Traffic
523
+ family: 'Network/Interface/Traffic/In'
524
+ unit: "bit/s"
525
+ scale_factor: 8 # Octets → bits
526
+ metric_tags:
527
+ - tag: interface
528
+ symbol:
529
+ OID: 1.3.6.1.2.1.31.1.1.1.1
530
+ name: ifName
531
+```
532
+
533
+**How Table Metrics Expand into Rows**
534
+
535
+```text
536
+SNMP Table: ifTable
537
+───────────────────────────────────────────────
538
+Index | ifName | ifHCInOctets
539
+───────────────────────────────────────────────
540
+1 | eth0 | 1024
541
+2 | eth1 | 2048
542
+───────────────────────────────────────────────
543
+
544
+metric_tags:
545
+ - tag: interface
546
+ symbol:
547
+ OID: 1.3.6.1.2.1.31.1.1.1.1 # ifName
548
+
549
+Resulting metrics:
550
+───────────────────────────────────────────────
551
+ifHCInOctets{interface="eth0"} = 1024
552
+ifHCInOctets{interface="eth1"} = 2048
553
+───────────────────────────────────────────────
554
+```
555
+
556
+**How it works**:
557
+
558
+1. The collector reads both columns (`ifHCInOctets` and `ifName`) from the same table.
559
+2. It aligns rows using their shared SNMP index (`1`, `2`, …).
560
+3. Each metric is emitted with its corresponding tag from the same row.
561
+
562
+**What this does**:
563
+
564
+- Collects traffic (`ifHCInOctets`) from each interface.
565
+- Tags each row with its name (`ifName`) from the same index.
566
+- Produces metrics like:
567
+ ```text
568
+ ifHCInOctets{interface="eth0"} = 1024
569
+ ifHCInOctets{interface="eth1"} = 2048
570
+ ```
571
+
572
+### Metric Types
573
+
574
+Each SNMP value has a data type that determines **how Netdata interprets and displays it**.
575
+
576
+The collector automatically detects the appropriate **metric type** (e.g., `gauge` or `rate`), but you can override it manually.
577
+
578
+**Automatic Type Detection**
579
+
580
+| SNMP Type | Default Netdata Type | Typical Use |
581
+|--------------------------|----------------------|--------------------------------------|
582
+| `Counter32`, `Counter64` | `rate` | Network traffic, packet counters |
583
+| `Gauge32`, `Integer` | `gauge` | Temperatures, usage levels, statuses |
584
+| `TimeTicks` | `gauge` | Uptime, time-based values |
585
+
586
+**Overriding the Metric Type**
587
+
588
+You can explicitly set a metric’s type using the `metric_type` field inside a symbol definition.
589
+
590
+```yaml
591
+metrics:
592
+ - MIB: IF-MIB
593
+ table:
594
+ OID: 1.3.6.1.2.1.2.2
595
+ name: ifTable
596
+ symbols:
597
+ - OID: 1.3.6.1.2.1.2.2.1.10
598
+ name: ifInOctets
599
+ metric_type: gauge # Override default 'rate'
600
+```
601
+
602
+**What this does**:
603
+
604
+- Forces `ifInOctets` to be treated as a **gauge** (instantaneous value) instead of a rate.
605
+- Normally, `Counter` types are automatically converted to per-second rates.
606
+
607
+## Adding Tags to Metrics
608
+
609
+Tags add **context and identity** to SNMP metrics.
610
+
611
+They let you distinguish between instances (for example, which interface, disk, or IP) and allow filtering and grouping in the Netdata UI.
612
+
613
+**The collector**:
614
+
615
+- Attaches tags to each metric as labels.
616
+- Uses tags to differentiate rows when building charts.
617
+- Requires at least one tag for every **table metric** (to identify each row).
618
+- Ignores tags for **scalar metrics**, which represent a single value per device.
619
+
620
+**Key Concepts**:
621
+
622
+| Concept | Description |
623
+|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
624
+| **Table metrics must have tags** | Each table row must be uniquely identified by at least one tag (for example, interface name or index). Without tags, only one row is emitted. |
625
+| **Scalar metrics don’t need tags** | Scalars represent one value for the entire device, not per-instance data. |
626
+| **Static tags** | Fixed values that never change (for example, datacenter, environment). |
627
+| **Dynamic tags** | Extracted from SNMP data — from table columns, related tables, or row indexes. |
628
+| **Global tags** | Defined in the profile’s top-level `metric_tags` section and applied to all metrics. |
629
+
630
+**Tag Types and Available Transformations**:
631
+
632
+| Tag Type | Description | Supported Transformations |
633
+|---------------------|-----------------------------------------------------------------|-------------------------------------------------------------------------------|
634
+| **Static** | Fixed tags with constant values. | None (value is fixed). |
635
+| **Same-Table** | Values from columns in the same table as the metric. | `mapping`, `extract_value`, `match_pattern` + `match_value`, `match` + `tags` |
636
+| **Cross-Table** | Values from another table. | `mapping`, `extract_value`, `match_pattern` + `match_value`, `match` + `tags` |
637
+| **Index-Based** | Values derived from the OID index of each row. | `mapping` (optional) |
638
+| **Index Transform** | Adjusts multi-part indexes so cross-table tags align correctly. | — (structural, not a transformation) |
639
+
640
+**Summary**:
641
+
642
+- Each **table metric** must define at least one **tag source** (`metric_tags`) to distinguish rows.
643
+- Tags can come from **the same table**, **another table**, or the **row index** itself.
644
+- Tag transformations (`mapping`, `extract_value`, `match_pattern`, `match + tags`) can modify or extract parts of raw values.
645
+- **Static tags** apply globally and are not transformed.
646
+- **Index transformations** are a special mechanism used only for aligning multi-part indexes between tables.
647
+
648
+**How the Collector Matches Values and Tags**:
649
+
650
+```text
651
+SNMP Table (ifTable)
652
+───────────────────────────────────────────────
653
+Index | ifDescr | ifInOctets
654
+───────────────────────────────────────────────
655
+1 | eth0 | 1024
656
+2 | eth1 | 2048
657
+───────────────────────────────────────────────
658
+
659
+metric_tags:
660
+ - tag: interface
661
+ symbol:
662
+ OID: 1.3.6.1.2.1.2.2.1.2 # ifDescr
663
+
664
+Resulting metrics:
665
+───────────────────────────────────────────────
666
+ifInOctets{interface="eth0"} = 1024
667
+ifInOctets{interface="eth1"} = 2048
668
+───────────────────────────────────────────────
669
+```
670
+
671
+How it works:
672
+
673
+1. The collector walks the table and collects both columns: `ifInOctets` (value) and `ifDescr` (tag source).
674
+2. It aligns them by their shared SNMP index (`1`, `2`, …).
675
+3. Each metric row is tagged with the corresponding column value from the same index.
676
+
677
+**Cross-Table Example**:
678
+
679
+```text
680
+SNMP Tables: ifTable + ifXTable
681
+───────────────────────────────────────────────
682
+ifTable.ifInOctets.1 = 1024
683
+ifTable.ifInOctets.2 = 2048
684
+
685
+ifXTable.ifName.1 = "eth0"
686
+ifXTable.ifName.2 = "eth1"
687
+───────────────────────────────────────────────
688
+
689
+metric_tags:
690
+ - tag: interface
691
+ table: ifXTable
692
+ symbol:
693
+ OID: 1.3.6.1.2.1.31.1.1.1.1 # ifName
694
+
695
+Result:
696
+───────────────────────────────────────────────
697
+ifInOctets{interface="eth0"} = 1024
698
+ifInOctets{interface="eth1"} = 2048
699
+───────────────────────────────────────────────
700
+```
701
+
702
+How it works:
703
+
704
+- The collector collects metrics from `ifTable` but fetches tag values from `ifXTable`.
705
+- It matches rows from both tables using their shared index (`.1`, `.2`, …).
706
+- The `interface` tag is populated from `ifXTable.ifName` for each matching row
707
+
708
+### Static
709
+
710
+Static tags define **fixed key–value pairs** that are attached to metrics without being collected from SNMP.
711
+
712
+They are useful for identifying **environment**, **location**, or other context that applies to all collected data.
713
+
714
+#### Profile-level Static Tags
715
+
716
+Profile-level static tags apply to **all metrics** defined in the profile.
717
+
718
+```yaml
719
+# Global static tags (applied to all metrics)
720
+static_tags:
721
+ - tag: datacenter
722
+ value: "DC1"
723
+ - tag: environment
724
+ value: "production"
725
+```
726
+
727
+**What this does**:
728
+
729
+- These tags are injected into every metric reported by the profile.
730
+
731
+**Typical use cases**:
732
+
733
+- Identify the datacenter, region, or cluster where the device belongs.
734
+- Mark metrics from staging or production environments.
735
+- Add organization-wide context that doesn’t depend on SNMP data.
736
+
737
+#### Metric-level Static Tags
738
+
739
+Metric-level static tags apply to **specific metrics only**.
740
+
741
+```yaml
742
+# Metric-specific static tags
743
+metrics:
744
+ - MIB: IF-MIB
745
+ table:
746
+ OID: 1.3.6.1.2.1.2.2
747
+ name: ifTable
748
+ symbols:
749
+ - OID: 1.3.6.1.2.1.2.2.1.10
750
+ name: ifInOctets
751
+ static_tags:
752
+ - tag: "source"
753
+ value: "snmp"
754
+ - tag: "interface_type"
755
+ value: "physical"
756
+```
757
+
758
+**What this does**:
759
+
760
+- Adds the tags `source=snmp` and `interface_type=physical` only to the `ifInOctets` metric.
761
+- Does not affect other metrics in the same profile.
762
+
763
+> Metric-level static tags are technically supported but rarely needed.
764
+> In most cases, prefer profile-level `static_tags` for consistency and simplicity.
765
+
766
+### Same-Table
767
+
768
+Same-table tags extract values from **columns in the same SNMP table** as the metric.
769
+
770
+They are the most common way to label per-row metrics with identifiers like interface names or indexes.
771
+
772
+**The collector**:
773
+
774
+- Retrieves both the metric value and the tag column from the same table row.
775
+- Automatically aligns rows by their shared index.
776
+- Adds the tag to every metric collected from that row.
777
+
778
+```yaml
779
+metrics:
780
+ - MIB: IF-MIB
781
+ table:
782
+ OID: 1.3.6.1.2.1.2.2
783
+ name: ifTable
784
+ symbols:
785
+ - OID: 1.3.6.1.2.1.2.2.1.10
786
+ name: ifInOctets
787
+ metric_tags:
788
+ - tag: interface
789
+ symbol:
790
+ OID: 1.3.6.1.2.1.2.2.1.2
791
+ name: ifDescr
792
+```
793
+
794
+**What this does**:
795
+
796
+- Collects `ifInOctets` (input bytes) for each row in `ifTable`.
797
+- Reads the `ifDescr` column from the same table to label each row.
798
+- Produces metrics like:
799
+ ```text
800
+ ifInOctets{interface="eth0"} = 1000
801
+ ifInOctets{interface="eth1"} = 2000
802
+ ```
803
+
804
+### Cross-Table
805
+
806
+Cross-table tags let you **use data from another SNMP table** as a tag source.
807
+
808
+**The collector**:
809
+
810
+- Reads tag values from the specified `table:` instead of the current one.
811
+- Matches rows between tables by their **index**.
812
+- When index structures differ, an optional `index_transform` can modify the current table’s index to align it with the target.
813
+
814
+#### Same Index
815
+
816
+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.
817
+
818
+In practice, this means that the row number (index) in one table corresponds directly to the same row in another.
819
+
820
+For example:
821
+
822
+```text
823
+ifTable.ifInOctets.2 = 123456
824
+ifXTable.ifName.2 = "xe-0/0/1"
825
+```
826
+
827
+Both OIDs end with `.2`, so they refer to the same interface.
828
+
829
+This allows you to use `ifName` (from `ifXTable`) as a tag for metrics collected from `ifTable`.
830
+
831
+```yaml
832
+metrics:
833
+ - MIB: IF-MIB
834
+ table:
835
+ OID: 1.3.6.1.2.1.2.2
836
+ name: ifTable
837
+ symbols:
838
+ - OID: 1.3.6.1.2.1.2.2.1.10
839
+ name: ifInOctets
840
+ metric_tags:
841
+ - tag: interface
842
+ table: ifXTable
843
+ symbol:
844
+ OID: 1.3.6.1.2.1.31.1.1.1.1
845
+ name: ifName
846
+```
847
+
848
+**What this does**:
849
+
850
+- Collects `ifInOctets` from `ifTable`.
851
+- Finds the row with the same index in `ifXTable` (e.g., `.2`).
852
+- Uses `ifName` as the `interface` tag.
853
+- Produces metrics like:
854
+ ```text
855
+ ifInOctets{interface="xe-0/0/1"} = 123456
856
+ ```
857
+
858
+#### With Index Transformation
859
+
860
+Some tables describe related data but use **different index structures** — meaning their OID suffixes don’t line up directly.
861
+
862
+For example, in `ipIfStatsTable` the index contains **two parts**:
863
+
864
+```text
865
+ipIfStatsTable.ipIfStatsHCInOctets.2.1 = 38560
866
+ipIfStatsTable.ipIfStatsHCInOctets.2.2 = 44408
867
+```
868
+
869
+Here:
870
+
871
+- The first component (`2`) is the **IP version** (e.g., 2 = IPv4, 3 = IPv6).
872
+- The second component (`1`, `2`, `3`, …) is the **interface index**.
873
+- `ifXTable`, on the other hand, uses only the interface index (`1`, `2`, `3`, …).
874
+
875
+Because the indexes differ, they can’t be matched directly.
876
+
877
+To fix this, use `index_transform` to **select only the relevant part of the index** so it matches the target table’s format.
878
+
879
+```yaml
880
+metrics:
881
+ - MIB: IP-MIB
882
+ table:
883
+ OID: 1.3.6.1.2.1.4.31.3
884
+ name: ipIfStatsTable
885
+ symbols:
886
+ - OID: 1.3.6.1.2.1.4.31.3.1.6
887
+ name: ipIfStatsHCInOctets
888
+ chart_meta:
889
+ description: Total inbound IP octets (including errors)
890
+ family: 'Network/Interface/IP/Traffic/Total/In'
891
+ unit: "bit/s"
892
+ scale_factor: 8
893
+ metric_tags:
894
+ - tag: _interface
895
+ table: ifXTable
896
+ symbol:
897
+ OID: 1.3.6.1.2.1.31.1.1.1.1
898
+ name: ifName
899
+ index_transform:
900
+ - start: 1
901
+ end: 1
902
+```
903
+
904
+**What this does**:
905
+
906
+- Collects IP traffic metrics from `ipIfStatsTable`.
907
+- Keeps only the **second index element** (`start: 1`, `end: 1`) from `2.1` → becomes `1`.
908
+- Looks up that interface index in `ifXTable` to find the corresponding `ifName`.
909
+- Produces:
910
+ ```yaml
911
+ ipIfStatsHCInOctets{_interface="xe-0/0/1"} = 38560
912
+ ```
913
+
914
+##### How `index_transform` Works
915
+
916
+`index_transform` tells the collector which parts of the current table’s index to keep when matching rows across tables.
917
+
918
+| Concept | Example |
919
+|--------------------|-----------------------------------------------------------------------------------------------|
920
+| **Original index** | `2.1` (from `ipIfStatsTable`) → `[ipVersion, ifIndex]` |
921
+| **Target index** | `1` (from `ifXTable`) |
922
+| **Transform** | `index_transform: [ { start: 1, end: 1 } ]` |
923
+| **Result** | The collector keeps only the **second element** (`ifIndex = 1`), which now matches `ifXTable` |
924
+
925
+**In short**:
926
+
927
+- `start` and `end` positions are **zero-based** (0 = first index element).
928
+- Each range defines which parts of the index to keep.
929
+- You can list multiple ranges to combine non-contiguous parts.
930
+- The goal is to make the current table’s index **match** the target table’s index so tags align correctly.
931
+
932
+### Index-Based
933
+
934
+Index-based tags extract values directly from the **OID index** of the SNMP table rather than from a column.
935
+
936
+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.
937
+
938
+**The collector**:
939
+
940
+- Splits the table’s row index into numbered parts (1-based).
941
+- For each `index:` rule, assigns a tag using the specified position in the index.
942
+- Converts numeric index components to strings automatically.
943
+- Attaches all resulting tags to the metric collected from that row.
944
+
945
+```yaml
946
+metrics:
947
+ - MIB: SIP-COMMON-MIB
948
+ table:
949
+ name: sipCommonStatusCodeTable
950
+ OID: 1.3.6.1.2.1.149.1.5.1
951
+ symbols:
952
+ - OID: 1.3.6.1.2.1.149.1.5.1.1.3
953
+ name: sipCommonStatusCodeIns
954
+ chart_meta:
955
+ family: 'Network/VoIP/SIP/Response/StatusCode/In'
956
+ description: Total number of response messages received with the specified status code
957
+ unit: "{response}/s"
958
+ metric_tags:
959
+ - index: 1
960
+ tag: applIndex
961
+ - index: 2
962
+ tag: sipCommonStatusCodeMethod
963
+ - index: 3
964
+ tag: sipCommonStatusCodeValue
965
+```
966
+
967
+**What this does**:
968
+
969
+- Extracts the first three components of each row’s OID index and uses them as tags.
970
+- For example, if the full OID is:
971
+ ```text
972
+ 1.3.6.1.2.1.149.1.5.1.1.3.1.6.200
973
+ ```
974
+ The collector interprets:
975
+ ```ini
976
+ applIndex=1
977
+ sipCommonStatusCodeMethod=6
978
+ sipCommonStatusCodeValue=200
979
+ ```
980
+- Produces metrics like:
981
+ ```text
982
+ sipCommonStatusCodeIns{applIndex="1", sipCommonStatusCodeMethod="6", sipCommonStatusCodeValue="200"} = 42
983
+ ```
984
+
985
+## Tag Transformation
986
+
987
+Tag transformations let you **modify or extract parts of SNMP values** to produce clear, human-readable tags.
988
+
989
+They work the same in **both** places:
990
+
991
+- `metadata` (e.g., device model, OS name), and
992
+- `metric_tags` (e.g., per-row interface labels).
993
+
994
+**Available Tag Transformations**:
995
+
996
+| Transformation | Purpose | Example Input → Output |
997
+|----------------------------------|-------------------------------------------------|------------------------------------------------------------------|
998
+| `mapping` | Replace numeric/string codes with names. | `1 → "ethernet"`, `161 → "lag"` |
999
+| `extract_value` | Extract a substring via regex (first group). | `"RouterOS CCR2004-16G-2S+" → "CCR2004-16G-2S+"` |
1000
+| `match_pattern` + `match_value` | Replace the value using regex groups or static. | `"Palo Alto Networks VM-Series firewall" → "VM-Series firewall"` |
1001
+| `match` + `tags` (multiple tags) | Create **several** tags from one value. | `"xe-0/0/1" → if_family=xe, fpc=0, pic=0, port=1` |
1002
+
1003
+**Combination & Behavior**:
1004
+
1005
+| Rule | Description |
1006
+|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
1007
+| **Where** | Can be used inside `metadata.*.fields.*.symbols[]` and `metric_tags[]`. |
1008
+| **Order of application** | 1️⃣ `match_pattern` + `match_value` **or** `extract_value` (whichever is present) → 2️⃣ `mapping` → 3️⃣ `match` + `tags` (if defined). |
1009
+| **No match behavior** | • `extract_value`: keeps the original value.<br/>• `match_pattern`: skips the value (tag not emitted).<br/>• `match` + `tags`: emits no tags. |
1010
+| **Multiple symbols** | If multiple `symbols` are listed for the same tag, the **first non-empty result** is used. |
1011
+| **Mapping key consistency** | Keys in a `mapping` must all be the same type — all numeric or all string. |
1012
+| **Safety** | Keep regexes simple and, when possible, **anchor them** (e.g. `^pattern$`) to prevent unwanted matches. |
1013
+
1014
+**Quick Syntax Recap**:
1015
+
1016
+- `mapping`
1017
+ ```yaml
1018
+ mapping:
1019
+ 6: "ethernet"
1020
+ 161: "lag"
1021
+ ```
1022
+- `extract_value`
1023
+ ```yaml
1024
+ extract_value: 'RouterOS ([A-Za-z0-9-+]+)' # first capture group is used
1025
+ ```
1026
+
1027
+- `match_pattern` + `match_value`
1028
+ ```yaml
1029
+ match_pattern: 'Palo Alto Networks\s+(PA-\d+ series firewall|VM-Series firewall)'
1030
+ match_value: '$1' # or a static value like 'Router' when matched
1031
+ ```
1032
+- `match` + `tags` (multiple tags)
1033
+ ```yaml
1034
+ match: '^([A-Za-z]+)[-_]?(\d+)\/(\d+)\/(\d+)$'
1035
+ tags:
1036
+ if_family: $1
1037
+ fpc: $2
1038
+ pic: $3
1039
+ port: $4
1040
+ ```
1041
+
1042
+### Mapping
1043
+
1044
+Use `mapping` to replace raw tag values with **human-readable text labels**.
1045
+
1046
+**The collector**:
1047
+
1048
+- Looks up the raw value in the mapping table.
1049
+- Replaces it with the corresponding string.
1050
+- If the value is not found in the mapping, the **original value** is **kept**.
1051
+- Keys can be numeric or string, but must be consistent in type.
1052
+- Mapping is applied to tag values from `metadata` or `metric_tags`.
1053
+
1054
+```yaml
1055
+metrics:
1056
+ - MIB: IF-MIB
1057
+ table:
1058
+ OID: 1.3.6.1.2.1.2.2
1059
+ name: ifTable
1060
+ symbols:
1061
+ - OID: 1.3.6.1.2.1.2.2.1.10
1062
+ name: ifInOctets
1063
+ metric_tags:
1064
+ - tag: if_type
1065
+ symbol:
1066
+ OID: 1.3.6.1.2.1.2.2.1.3
1067
+ name: ifType
1068
+ mapping:
1069
+ 1: "other"
1070
+ 6: "ethernet"
1071
+ 24: "loopback"
1072
+ 131: "tunnel"
1073
+ 161: "lag"
1074
+```
1075
+
1076
+**What this does**:
1077
+
1078
+- Replaces numeric interface type codes (1, 6, 24, 131, 161) with readable names (`other`, `ethernet`, `loopback`, `tunnel`, `lag`).
1079
+- If a device reports an unknown type, the original numeric value is used.
1080
+- Works identically for `metadata` fields and `metric_tags`.
1081
+
1082
+### Extract Value
1083
+
1084
+Use `extract_value` to capture a part of a string using a **regular expression**.
1085
+
1086
+**The collector**:
1087
+
1088
+- Applies the pattern to the raw value.
1089
+- Replaces the value with the **first capture group** `( … )`.
1090
+- Keeps the **original value** if no match is found.
1091
+- Searches anywhere in the string unless you anchor the pattern with `^` or `$`.
1092
+- Uses the **first non-empty** result when multiple `symbols` are defined.
1093
+
1094
+```yaml
1095
+metadata:
1096
+ device:
1097
+ fields:
1098
+ model:
1099
+ symbols:
1100
+ # Example: 'RouterOS CCR2004-16G-2S+' → 'CCR2004-16G-2S+'
1101
+ - OID: 1.3.6.1.2.1.1.1.0
1102
+ name: sysDescr
1103
+ extract_value: 'RouterOS ([A-Za-z0-9-+]+)'
1104
+ # Example: 'CSS326-24G-2S+ SwOS v2.13' → 'CSS326-24G-2S+'
1105
+ - OID: 1.3.6.1.2.1.1.1.0
1106
+ name: sysDescr
1107
+ extract_value: '([A-Za-z0-9-+]+) SwOS'
1108
+```
1109
+
1110
+### Match Pattern
1111
+
1112
+Use `match_pattern` and `match_value` together to build a tag value using multiple **regex capture groups**.
1113
+
1114
+**The collector**:
1115
+
1116
+- Tests the value against the regular expression in `match_pattern`.
1117
+- If it **matches**, replaces the value with `match_value`.
1118
+- Within `match_value`, you can reference **capture groups** using `$1`, `$2`, `$3`, etc.
1119
+- If the value **does not match**, it is skipped (ignored).
1120
+- Works both for reformatting captured text and for assigning a static replacement when matched.
1121
+
1122
+**Example 1 — Reformat using capture groups**:
1123
+
1124
+```yaml
1125
+metadata:
1126
+ device:
1127
+ fields:
1128
+ product_name:
1129
+ symbol:
1130
+ OID: 1.3.6.1.2.1.1.1.0
1131
+ name: sysDescr
1132
+ match_pattern: 'Palo Alto Networks\s+(PA-\d+ series firewall|WildFire Appliance|VM-Series firewall)'
1133
+ match_value: "$1"
1134
+ # Examples:
1135
+ # - Palo Alto Networks VM-Series firewall → VM-Series firewall
1136
+ # - Palo Alto Networks PA-3200 series firewall → PA-3200 series firewall
1137
+ # - Palo Alto Networks WildFire Appliance → WildFire Appliance
1138
+```
1139
+
1140
+**Example 2 — Assign static value on match**:
1141
+
1142
+```yaml
1143
+metadata:
1144
+ device:
1145
+ fields:
1146
+ type:
1147
+ symbols:
1148
+ - OID: 1.3.6.1.2.1.1.1.0
1149
+ name: sysDescr
1150
+ # RouterOS devices
1151
+ match_pattern: 'RouterOS (CCR.*)'
1152
+ match_value: 'Router'
1153
+```
1154
+
1155
+### Match (Multiple Tags)
1156
+
1157
+Use `match` and `tags` to create **multiple tags** from a single SNMP value using a **regular expression** with capture groups.
1158
+
1159
+**The collector**:
1160
+
1161
+- Applies the regex in match to the raw value.
1162
+- If it `matches`, creates all tags listed under `tags`, substituting `$1`, `$2`, `$3`, etc. from the capture groups.
1163
+- If it **doesn’t match**, none of those tags are added.
1164
+- Tags that resolve to an **empty capture** are not added.
1165
+
1166
+**Example 1 — Split OS name and model from sysDescr (metadata)**:
1167
+
1168
+```yaml
1169
+metadata:
1170
+ device:
1171
+ fields:
1172
+ type:
1173
+ symbols:
1174
+ - OID: 1.3.6.1.2.1.1.1.0
1175
+ name: sysDescr
1176
+ match: '^(\S+)\s+(.*)$'
1177
+ tags:
1178
+ os_name: $1 # e.g. 'RouterOS'
1179
+ model: $2 # e.g. 'CCR2004-16G-2S+'
1180
+```
1181
+
1182
+> Input like `RouterOS CCR2004-16G-2S+` becomes: `os_name=RouterOS`, `model=CCR2004-16G-2S+`.
1183
+
1184
+**Example 2 — Derive multiple labels from interface names (metric_tags)**:
1185
+
1186
+```yaml
1187
+metric_tags:
1188
+ - symbol:
1189
+ OID: 1.3.6.1.2.1.2.2.1.2
1190
+ name: ifDescr
1191
+ match: '^([A-Za-z]+)[-_]?(\d+)\/(\d+)\/(\d+)$'
1192
+ tags:
1193
+ if_family: $1 # e.g. 'xe' or 'ge' or 'GigabitEthernet' → 'GigabitEthernet'
1194
+ fpc: $2 # '0'
1195
+ pic: $3 # '0'
1196
+ port: $4 # '1'
1197
+```
1198
+
1199
+- Handles common patterns like `xe-0/0/1`, `ge-0/0/0`, or `GigabitEthernet1/0/24`.
1200
+- Output tags might be: `if_family=xe`, `fpc=0`, `pic=0`, `port=1`.
1201
+
1202
+## Value Transformation
1203
+
1204
+Value transformations let you **process or normalize raw SNMP metric values** before they are stored and charted.
1205
+
1206
+They are applied **per symbol (per OID)** during SNMP data collection. They modify only **metric values**, not tags or metadata, and are **not applied to virtual metrics**.
1207
+
1208
+These transformations are typically used to:
1209
+
1210
+- Extract numeric substrings from mixed strings.
1211
+- Scale or convert units (bytes → bits, megabits → bits).
1212
+- Map discrete states (1 = up, 2 = down, etc.) into named dimensions.
1213
+
1214
+**Available Value Transformations**:
1215
+
1216
+| Transformation | Purpose | Example Input → Output |
1217
+|---------------------------------|-------------------------------------------------------------------|-------------------------------------|
1218
+| `mapping` | Convert numeric or string codes into state dimensions. | `1 → up`, `2 → down`, `3 → testing` |
1219
+| `extract_value` | Extract a numeric substring via regex. | `"23.8 °C" → "23"` |
1220
+| `scale_factor` | Multiply values by a constant to adjust units. | `"1.5" (MBps) × 8 → 12 (Mbps)` |
1221
+| `match_pattern` + `match_value` | *Not applicable* for metric values (use `extract_value` instead). | — |
1222
+
1223
+**Combination & Behavior**:
1224
+
1225
+| Rule | Description |
1226
+|---------------------------|---------------------------------------------------------------------------------------------------------------------------|
1227
+| **Where** | Value transformations are used inside `metrics[*].symbol` or `metrics[*].symbols[]`. |
1228
+| **Order of application** | 1️⃣ `extract_value` (if present) → 2️⃣ `mapping` → 3️⃣ `scale_factor`. |
1229
+| **Scale factor position** | `scale_factor` is always applied **last**, after all other transformations. |
1230
+| **Data type handling** | Transformations preserve numeric type (integer/float) unless the mapping converts it to a multi-value metric. |
1231
+| **Error handling** | If a transformation fails (e.g., regex doesn’t match), the collector keeps the original value. |
1232
+| **Applicability** | Transformations affect metric values only — not metadata or tags. |
1233
+| **Mapping behavior** | Always produces a multi-value metric where each mapped entry becomes a dimension; the active one reports `1`, others `0`. |
1234
+
1235
+**Quick Syntax Recap**:
1236
+
1237
+- `mapping`
1238
+ ```yaml
1239
+ mapping:
1240
+ 1: up
1241
+ 2: down
1242
+ 3: testing
1243
+ ```
1244
+
1245
+- `extract_value`
1246
+ ```yaml
1247
+ extract_value: '(\d+)' # First capture group is used
1248
+ ```
1249
+
1250
+- `scale_factor`
1251
+ ```yaml
1252
+ scale_factor: 8 # Octets → bits
1253
+ ```
1254
+
1255
+### Mapping
1256
+
1257
+Use `mapping` to convert raw metric values into **state dimensions**.
1258
+
1259
+Each mapping entry defines a **dimension name** and the numeric or string value that triggers it.
1260
+
1261
+**The collector**:
1262
+
1263
+- Evaluates the value against the mapping table.
1264
+- For each mapping entry, creates a **dimension** named after the mapped key.
1265
+- Sets that dimension to `1` if the current value matches the key, or `0` otherwise.
1266
+- If the value doesn’t match any key, all mapped dimensions are `0`.
1267
+- Works only for **metric values**, not for tags or metadata.
1268
+
1269
+```yaml
1270
+metrics:
1271
+ - OID: 1.3.6.1.2.1.2.2.1.7
1272
+ name: ifAdminStatus
1273
+ chart_meta:
1274
+ description: Current administrative state of the interface
1275
+ family: 'Network/Interface/Status/Admin'
1276
+ unit: "{status}"
1277
+ mapping:
1278
+ 1: up
1279
+ 2: down
1280
+ 3: testing
1281
+```
1282
+
1283
+**What this does**:
1284
+
1285
+- Converts SNMP integer values (1, 2, 3) into a **multi-value metric** with dimensions `up`, `down`, and `testing`.
1286
+- The dimension corresponding to the current value reports `1`; all others report `0`.
1287
+
1288
+### Extract Value
1289
+
1290
+Use `extract_value` to extract a **numeric or string portion** from the raw SNMP value using a **regular expression**.
1291
+
1292
+This is often used when a metric is encoded as a string that contains numeric data (e.g. `"23.8 °C"`).
1293
+
1294
+**The collector**:
1295
+
1296
+- Applies the regular expression to the raw SNMP value.
1297
+- Uses the **first capture group** `( … )` as the new metric value.
1298
+- If the pattern doesn’t match, the original value is kept.
1299
+- Works for any metric type (string or numeric).
1300
+- When multiple `symbols` are defined, the first non-empty result is used.
1301
+
1302
+```yaml
1303
+metrics:
1304
+ - MIB: CORIANT-GROOVE-MIB
1305
+ table:
1306
+ OID: 1.3.6.1.4.1.42229.1.2.3.1.1
1307
+ name: shelfTable
1308
+ symbols:
1309
+ - OID: 1.3.6.1.4.1.42229.1.2.3.1.1.1.3
1310
+ name: coriant.groove.shelfInletTemperature
1311
+ # Example: "23.8 °C" → "23"
1312
+ extract_value: '(\d+)'
1313
+ chart_meta:
1314
+ description: Shelf inlet temperature
1315
+ family: 'Hardware/Shelf/Temperature/Inlet'
1316
+ unit: "Cel"
1317
+```
1318
+
1319
+**What this does**:
1320
+
1321
+- Applies the regex `(\d+)` to the string `"23.8 °C"`.
1322
+- Extracts only the numeric part `"23"` and uses it as the metric value.
1323
+- If the value doesn’t match, the original string is retained.
1324
+- Ideal for string metrics that embed numbers, units, or labels.
1325
+
1326
+### Scale Factor
1327
+
1328
+Use `scale_factor` to **multiply collected metric values** by a constant.
1329
+
1330
+This transformation is typically used to convert between units (for example, bytes to bits).
1331
+
1332
+**The collector**:
1333
+
1334
+- Multiplies the raw SNMP value by the specified factor.
1335
+- Applies to both integer and floating-point values.
1336
+- Keeps the result in the same numeric type (integer or float).
1337
+- Works for **metric values only** (not for tags or metadata).
1338
+- Applies **after all other transformations** on the same metric (such as `extract_value`).
1339
+
1340
+```yaml
1341
+metrics:
1342
+ - MIB: IP-MIB
1343
+ table:
1344
+ OID: 1.3.6.1.2.1.4.31.1
1345
+ name: ipSystemStatsTable
1346
+ symbols:
1347
+ - OID: 1.3.6.1.2.1.4.31.1.1.6
1348
+ name: ipSystemStatsHCInOctets
1349
+ chart_meta:
1350
+ description: Octets received in input IP datagrams
1351
+ family: 'Network/IP/Traffic/Total/In'
1352
+ unit: "bit/s"
1353
+ scale_factor: 8 # Octets → bits
1354
+
1355
+ - MIB: IF-MIB
1356
+ symbol:
1357
+ OID: 1.3.6.1.2.1.31.1.1.1.15
1358
+ name: ifHighSpeed
1359
+ chart_meta:
1360
+ description: Estimate of the interface's current bandwidth
1361
+ family: 'Network/Interface/Speed'
1362
+ unit: "bit/s"
1363
+ scale_factor: 1000000 # Megabits → bits
1364
+```
1365
+
1366
+**What this does**:
1367
+
1368
+- Multiplies octet counters by `8`, reporting traffic in **bits per second** instead of bytes.
1369
+- Converts `ifHighSpeed` from **megabits** to **bits**.
1370
+- Ensures scaling happens **after** other transformations, such as value extraction or regex processing.
1371
+
1372
+## Virtual Metrics
1373
+
1374
+- Virtual metrics are **calculated metrics** built from other metrics in your profile (or inherited ones).
1375
+- They don’t query SNMP; they **reuse existing metric values** to create totals, fallbacks, or per-row aggregations.
1376
+- Once computed, they behave like normal metrics: charted, tagged, and alertable.
1377
+
1378
+Common use cases:
1379
+
1380
+- **Fallbacks**: prefer 64-bit counters, fall back to 32-bit if missing.
1381
+- **Sums/Combines**: add related metrics (e.g., in + out traffic).
1382
+- **Per-row totals**: aggregate multiple columns into one per-interface metric.
1383
+
1384
+### Structure
1385
+
1386
+```yaml
1387
+virtual_metrics:
1388
+ - name: <string>
1389
+ sources:
1390
+ - { metric: <metricName>, table: <tableName>, as: <dimensionName> }
1391
+ # Optional: direct primary source set
1392
+
1393
+ alternatives:
1394
+ - sources:
1395
+ - { metric: <metricNameA>, table: <tableName>, as: <dimensionName> }
1396
+ - { metric: <metricNameB>, table: <tableName>, as: <dimensionName> }
1397
+ - sources:
1398
+ - { metric: <fallbackMetricA>, table: <tableName>, as: <dimensionName> }
1399
+ - { metric: <fallbackMetricB>, table: <tableName>, as: <dimensionName> }
1400
+
1401
+ per_row: <true|false>
1402
+ group_by: <label | [labels]>
1403
+ chart_meta:
1404
+ description: ...
1405
+ family: ...
1406
+ unit: ...
1407
+```
1408
+
1409
+### Config reference
1410
+
1411
+| Item | Field | Type | Required | Default | Applies to | Description |
1412
+|--------------------|----------------|----------------------|----------|---------|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
1413
+| **Virtual Metric** | `name` | string | yes | — | all | Unique within the profile. Used as metric/chart base name. |
1414
+| | `sources` | array\<Source\> | no* | — | totals, per_row, grouped | Direct source set. Ignored if `alternatives` exist (alternatives take precedence). |
1415
+| | `alternatives` | array\<Alternative\> | no* | — | totals, per_row, grouped | Ordered fallback sets. The first alternative whose sources produce data is used. |
1416
+| | `per_row` | bool | no | false | per-row/grouped | When `true`, emits one output per input row; sources become dimensions; row tags attach. |
1417
+| | `group_by` | string / array | no | — | per-row/grouped | Label(s) used as row-key hints (in order). Missing/empty hints fall back to a full-tag stable key. With `per_row:false`, this acts like PromQL’s `sum by (...)`. |
1418
+| | `chart_meta` | object | no | — | all | Presentation metadata (`description`, `family`, `unit`, `type`). |
1419
+| **Source** | `metric` | string | yes | — | — | Name of an existing metric (scalar or table column metric). |
1420
+| | `table` | string | yes | — | — | Table name for the originating metric. Must match the metric’s table when used in per-row/grouped. |
1421
+| | `as` | string | yes | — | — | Dimension name within the composite (e.g., `in`, `out`). |
1422
+| **Alternative** | `sources` | array\<Source\> | yes | — | — | All sources in an alternative are evaluated together. If none produce data, the collector tries the next alternative. Per-row/group rules apply within the winning alternative. |
1423
+
1424
+> At least one of `sources` or `alternatives` **must be defined**.
1425
+
1426
+#### Rules & Constraints
1427
+
1428
+| Rule | Description |
1429
+|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
1430
+| **Precedence** | If both `sources` and `alternatives` exist, `alternatives` take precedence. |
1431
+| **Same-table requirement** | When `per_row` or `group_by` is used, all sources must originate from the same table. For alternatives, this rule applies within each alternative set. |
1432
+| **per_row: true** | One output per input row; multiple sources become chart dimensions (`as`); row tags attach automatically. |
1433
+| **group_by (with per_row:true)** | Acts as row-key hints (in order). Missing or empty hints fall back to a full-tag composite key. |
1434
+| **group_by (with per_row:false)** | Aggregates rows by the listed labels, similar to PromQL’s `sum by (...)`. |
1435
+| **Alternative evaluation** | Alternatives are checked in order. The first whose sources produce data becomes the “winner”; others are ignored. |
1436
+| **Parent metadata** | The virtual metric emits charts using its own `name` and `chart_meta`, even when data comes from an alternative. |
1437
+| **Dimensions** | Each `as` value defines a dimension in the resulting chart (e.g., `in`, `out`, `total`). |
1438
+| **Totals vs per-row** | Omitting both `per_row` and `group_by` produces a single total chart across all rows (device-wide view). |
1439
+
1440
+### Examples
1441
+
1442
+#### Per-row aggregation from one table (in + out traffic)
1443
+
1444
+```yaml
1445
+virtual_metrics:
1446
+ - name: ifTotalTraffic
1447
+ sources:
1448
+ - { metric: _ifHCInOctets, table: ifXTable, as: in }
1449
+ - { metric: _ifHCOutOctets, table: ifXTable, as: out }
1450
+ per_row: true
1451
+ group_by: ["interface"]
1452
+ chart_meta:
1453
+ description: Traffic per interface
1454
+ family: 'Network/Interface/Traffic'
1455
+ unit: "bit/s"
1456
+```
1457
+
1458
+**What this does**:
1459
+
1460
+- Creates **one output per input row** in `ifXTable`.
1461
+- Each chart represents one interface with two dimensions: `in` and `out`.
1462
+- `group_by: ["interface"]` provides key hints to keep per-interface charts stable.
1463
+- If a hint is missing or empty, a full-tag composite key is used instead.
1464
+- **Constraint**: `per_row` or `group_by` requires all sources to come from the same table.
1465
+
1466
+#### Total aggregation (sum across all interfaces)
1467
+
1468
+```yaml
1469
+virtual_metrics:
1470
+ - name: ifTotalTraffic
1471
+ sources:
1472
+ - { metric: _ifHCInOctets, table: ifXTable, as: in }
1473
+ - { metric: _ifHCOutOctets, table: ifXTable, as: out }
1474
+ chart_meta:
1475
+ description: Total traffic across all interfaces
1476
+ family: 'Network/Total/Traffic'
1477
+ unit: "bit/s"
1478
+```
1479
+
1480
+**What this does**:
1481
+
1482
+- Aggregates data from **all rows** in `ifXTable` into a **single chart**.
1483
+- Produces two dimensions (`in`, `out`) representing the total interface traffic for the entire device.
1484
+- No `per_row` or `group_by` fields → a single total chart (device-wide view).
1485
+
1486
+#### Grouped aggregation (sum by label)
1487
+
1488
+```yaml
1489
+virtual_metrics:
1490
+ - name: ifTypeTraffic
1491
+ sources:
1492
+ - { metric: _ifHCInOctets, table: ifXTable, as: in }
1493
+ - { metric: _ifHCOutOctets, table: ifXTable, as: out }
1494
+ per_row: false
1495
+ group_by: ["ifType"]
1496
+ chart_meta:
1497
+ description: Traffic aggregated by interface type
1498
+ family: 'Network/InterfaceType/Traffic'
1499
+ unit: "bit/s"
1500
+```
1501
+
1502
+**What this does**:
1503
+
1504
+- Performs **PromQL-like “sum by (ifType)” aggregation**.
1505
+- Combines all rows sharing the same `ifType` label into grouped totals.
1506
+- Result: one chart with `in` and `out` dimensions aggregated by interface type.
1507
+- **Constraint**: all sources must come from the same table.
1508
+
1509
+#### Alternatives (total; prefer 64-bit, fallback to 32-bit)
1510
+
1511
+```yaml
1512
+virtual_metrics:
1513
+ - name: ifTotalPacketsUcast
1514
+ alternatives:
1515
+ - sources:
1516
+ - { metric: _ifHCInUcastPkts, table: ifXTable, as: in }
1517
+ - { metric: _ifHCOutUcastPkts, table: ifXTable, as: out }
1518
+ - sources:
1519
+ - { metric: _ifInUcastPkts, table: ifTable, as: in }
1520
+ - { metric: _ifOutUcastPkts, table: ifTable, as: out }
1521
+ chart_meta:
1522
+ description: Total unicast packets across all interfaces (in/out)
1523
+ family: 'Network/Total/Packet/Unicast'
1524
+ unit: "{packet}/s"
1525
+```
1526
+
1527
+**What this does**:
1528
+
1529
+- Defines two **alternatives**, each as a list of sources.
1530
+- At runtime, the collector **picks the first alternative whose sources produce data** (HC first).
1531
+- Once a winner is found, **later alternatives are ignored**.
1532
+- The parent emits metrics using **its own** `name` and `chart_meta`, sourcing values from the selected child.
1533
+- If both `sources` and `alternatives` are present, `alternatives` take precedence.
1534
+
1535
+#### Composite (multi-source total: unicast/multicast/broadcast
1536
+
1537
+```yaml
1538
+virtual_metrics:
1539
+ - name: ifTotalPacketsByKind
1540
+ sources:
1541
+ - { metric: _ifHCInUcastPkts, table: ifXTable, as: in_ucast }
1542
+ - { metric: _ifHCOutUcastPkts, table: ifXTable, as: out_ucast }
1543
+ - { metric: _ifHCInMulticastPkts, table: ifXTable, as: in_mcast }
1544
+ - { metric: _ifHCOutMulticastPkts, table: ifXTable, as: out_mcast }
1545
+ - { metric: _ifHCInBroadcastPkts, table: ifXTable, as: in_bcast }
1546
+ - { metric: _ifHCOutBroadcastPkts, table: ifXTable, as: out_bcast }
1547
+ chart_meta:
1548
+ description: Total packets across all interfaces by kind (in/out)
1549
+ family: 'Network/Total/Packet/ByKind'
1550
+ unit: "{packet}/s"
1551
+```
1552
+
1553
+What this does
1554
+
1555
+- Builds a **single total chart** combining multiple related packet counters.
1556
+- Each `as` becomes a **dimension** (`in_ucast`, `out_ucast`, `in_mcast`, …).
1557
+- No `per_row`/`group_by` → totals aggregated across all interfaces.