Skip to content

Commit 40e5197

Browse files
authored
Merge pull request #7 from rokernel/feat/fdb-summary-collector
feat: add protected FDB summary collector
2 parents 0f95ba8 + cff2280 commit 40e5197

6 files changed

Lines changed: 597 additions & 0 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Currently supported collectors:
1010
- [LLDP collector](internal/collector/lldp_collector.go): collects LLDP neighbor information from SONiC Redis.
1111
- [VLAN collector](internal/collector/vlan_collector.go): collects VLAN and VLAN member state from SONiC Redis.
1212
- [LAG collector](internal/collector/lag_collector.go): collects PortChannel and member state from SONiC Redis.
13+
- [FDB collector](internal/collector/fdb_collector.go): collects FDB summary metrics from SONiC ASIC DB.
1314

1415
# Usage
1516

@@ -45,6 +46,12 @@ Environment variables:
4546
- `LAG_TIMEOUT` - timeout for one LAG refresh cycle. Default: `2s`.
4647
- `LAG_MAX_LAGS` - maximum number of LAGs exported per refresh. Default: `512`.
4748
- `LAG_MAX_MEMBERS` - maximum number of LAG members exported per refresh. Default: `4096`.
49+
- `FDB_ENABLED` - enable FDB collector. Default: `false`.
50+
- `FDB_REFRESH_INTERVAL` - FDB cache refresh interval. Default: `60s`.
51+
- `FDB_TIMEOUT` - timeout for one FDB refresh cycle. Default: `2s`.
52+
- `FDB_MAX_ENTRIES` - maximum number of ASIC FDB entries processed per refresh. Default: `50000`.
53+
- `FDB_MAX_PORTS` - maximum number of per-port FDB series exported. Default: `1024`.
54+
- `FDB_MAX_VLANS` - maximum number of per-VLAN FDB series exported. Default: `4096`.
4855

4956
## Validated Platforms
5057

cmd/sonic-exporter/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func main() {
6060
lldpCollector := collector.NewLldpCollector(logger)
6161
vlanCollector := collector.NewVlanCollector(logger)
6262
lagCollector := collector.NewLagCollector(logger)
63+
fdbCollector := collector.NewFdbCollector(logger)
6364
prometheus.MustRegister(interfaceCollector)
6465
prometheus.MustRegister(hwCollector)
6566
prometheus.MustRegister(crmCollector)
@@ -73,6 +74,9 @@ func main() {
7374
if lagCollector.IsEnabled() {
7475
prometheus.MustRegister(lagCollector)
7576
}
77+
if fdbCollector.IsEnabled() {
78+
prometheus.MustRegister(fdbCollector)
79+
}
7680

7781
// Node exporter collectors
7882
nodeCollector, err := nodecollector.NewNodeCollector(logger,

fixtures/test/asic_db_data.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"id": "ASIC_DB",
3+
"data": {
4+
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x260000000000111": {
5+
"SAI_VLAN_ATTR_VLAN_ID": "1000"
6+
},
7+
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x260000000000222": {
8+
"SAI_VLAN_ATTR_VLAN_ID": "2000"
9+
},
10+
"ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000001": {
11+
"SAI_BRIDGE_PORT_ATTR_PORT_ID": "oid:0x1000000000002"
12+
},
13+
"ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000002": {
14+
"SAI_BRIDGE_PORT_ATTR_PORT_ID": "oid:0x1000000000003"
15+
},
16+
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000000111\",\"mac\":\"AA:BB:CC:00:00:01\",\"switch_id\":\"oid:0x21000000000000\"}": {
17+
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000001",
18+
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
19+
},
20+
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000000111\",\"mac\":\"AA:BB:CC:00:00:02\",\"switch_id\":\"oid:0x21000000000000\"}": {
21+
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000001",
22+
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
23+
},
24+
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000000222\",\"mac\":\"AA:BB:CC:00:00:03\",\"switch_id\":\"oid:0x21000000000000\"}": {
25+
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000002",
26+
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_STATIC"
27+
},
28+
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000000999\",\"mac\":\"AA:BB:CC:00:00:04\",\"switch_id\":\"oid:0x21000000000000\"}": {
29+
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000002",
30+
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
31+
},
32+
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:not-json": {
33+
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000001",
34+
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
35+
}
36+
}
37+
}

internal/collector/collector_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func populateRedisData() error {
2727
"../../fixtures/test/counters_db_data.json",
2828
"../../fixtures/test/config_db_data.json",
2929
"../../fixtures/test/appl_db_data.json",
30+
"../../fixtures/test/asic_db_data.json",
3031
"../../fixtures/test/state_db_data.json",
3132
}
3233

@@ -79,6 +80,7 @@ func TestMain(m *testing.M) {
7980
os.Setenv("LLDP_INCLUDE_MGMT", "true")
8081
os.Setenv("VLAN_ENABLED", "true")
8182
os.Setenv("LAG_ENABLED", "true")
83+
os.Setenv("FDB_ENABLED", "true")
8284
err = populateRedisData()
8385
if err != nil {
8486
slog.Error("failed to populate redis data", "error", err)
@@ -93,6 +95,7 @@ func TestMain(m *testing.M) {
9395
os.Unsetenv("LLDP_INCLUDE_MGMT")
9496
os.Unsetenv("VLAN_ENABLED")
9597
os.Unsetenv("LAG_ENABLED")
98+
os.Unsetenv("FDB_ENABLED")
9699
os.Exit(exitCode)
97100
}
98101

@@ -375,3 +378,97 @@ func TestLagCollector(t *testing.T) {
375378
t.Errorf("unexpected collecting result:\n%s", err)
376379
}
377380
}
381+
382+
func TestFdbCollector(t *testing.T) {
383+
promslogConfig := &promslog.Config{}
384+
logger := promslog.New(promslogConfig)
385+
386+
fdbCollector := NewFdbCollector(logger)
387+
388+
problems, err := testutil.CollectAndLint(fdbCollector)
389+
if err != nil {
390+
t.Error("metric lint completed with errors")
391+
}
392+
393+
for _, problem := range problems {
394+
t.Errorf("metric %v has a problem: %v", problem.Metric, problem.Text)
395+
}
396+
397+
metadata := `
398+
# HELP sonic_fdb_collector_success Whether FDB collector succeeded
399+
# TYPE sonic_fdb_collector_success gauge
400+
# HELP sonic_fdb_entries Number of FDB entries
401+
# TYPE sonic_fdb_entries gauge
402+
# HELP sonic_fdb_entries_unknown_vlan Number of FDB entries with unknown VLAN mapping
403+
# TYPE sonic_fdb_entries_unknown_vlan gauge
404+
`
405+
406+
expected := `
407+
sonic_fdb_collector_success 1
408+
sonic_fdb_entries 4
409+
sonic_fdb_entries_unknown_vlan 1
410+
`
411+
412+
if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(metadata+expected), "sonic_fdb_collector_success", "sonic_fdb_entries", "sonic_fdb_entries_unknown_vlan"); err != nil {
413+
t.Errorf("unexpected collecting result:\n%s", err)
414+
}
415+
416+
portMetadata := `
417+
# HELP sonic_fdb_entries_by_port Number of FDB entries by port
418+
# TYPE sonic_fdb_entries_by_port gauge
419+
`
420+
421+
portExpected := `
422+
sonic_fdb_entries_by_port{port="Ethernet0"} 2
423+
sonic_fdb_entries_by_port{port="Ethernet39"} 2
424+
`
425+
426+
if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(portMetadata+portExpected), "sonic_fdb_entries_by_port"); err != nil {
427+
t.Errorf("unexpected collecting result:\n%s", err)
428+
}
429+
430+
vlanMetadata := `
431+
# HELP sonic_fdb_entries_by_vlan Number of FDB entries by VLAN
432+
# TYPE sonic_fdb_entries_by_vlan gauge
433+
`
434+
435+
vlanExpected := `
436+
sonic_fdb_entries_by_vlan{vlan="1000"} 2
437+
sonic_fdb_entries_by_vlan{vlan="2000"} 1
438+
sonic_fdb_entries_by_vlan{vlan="unknown"} 1
439+
`
440+
441+
if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(vlanMetadata+vlanExpected), "sonic_fdb_entries_by_vlan"); err != nil {
442+
t.Errorf("unexpected collecting result:\n%s", err)
443+
}
444+
445+
typeMetadata := `
446+
# HELP sonic_fdb_entries_by_type Number of FDB entries by entry type
447+
# TYPE sonic_fdb_entries_by_type gauge
448+
`
449+
450+
typeExpected := `
451+
sonic_fdb_entries_by_type{entry_type="dynamic"} 3
452+
sonic_fdb_entries_by_type{entry_type="static"} 1
453+
`
454+
455+
if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(typeMetadata+typeExpected), "sonic_fdb_entries_by_type"); err != nil {
456+
t.Errorf("unexpected collecting result:\n%s", err)
457+
}
458+
459+
statusMetadata := `
460+
# HELP sonic_fdb_entries_skipped Number of FDB entries skipped during latest refresh
461+
# TYPE sonic_fdb_entries_skipped gauge
462+
# HELP sonic_fdb_entries_truncated Whether FDB collection hit max entries limit (1=yes, 0=no)
463+
# TYPE sonic_fdb_entries_truncated gauge
464+
`
465+
466+
statusExpected := `
467+
sonic_fdb_entries_skipped 1
468+
sonic_fdb_entries_truncated 0
469+
`
470+
471+
if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(statusMetadata+statusExpected), "sonic_fdb_entries_skipped", "sonic_fdb_entries_truncated"); err != nil {
472+
t.Errorf("unexpected collecting result:\n%s", err)
473+
}
474+
}

0 commit comments

Comments
 (0)