Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Currently supported collectors:
- [LLDP collector](internal/collector/lldp_collector.go): collects LLDP neighbor information from SONiC Redis.
- [VLAN collector](internal/collector/vlan_collector.go): collects VLAN and VLAN member state from SONiC Redis.
- [LAG collector](internal/collector/lag_collector.go): collects PortChannel and member state from SONiC Redis.
- [FDB collector](internal/collector/fdb_collector.go): collects FDB summary metrics from SONiC ASIC DB.

# Usage

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

## Validated Platforms

Expand Down
4 changes: 4 additions & 0 deletions cmd/sonic-exporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func main() {
lldpCollector := collector.NewLldpCollector(logger)
vlanCollector := collector.NewVlanCollector(logger)
lagCollector := collector.NewLagCollector(logger)
fdbCollector := collector.NewFdbCollector(logger)
prometheus.MustRegister(interfaceCollector)
prometheus.MustRegister(hwCollector)
prometheus.MustRegister(crmCollector)
Expand All @@ -73,6 +74,9 @@ func main() {
if lagCollector.IsEnabled() {
prometheus.MustRegister(lagCollector)
}
if fdbCollector.IsEnabled() {
prometheus.MustRegister(fdbCollector)
}

// Node exporter collectors
nodeCollector, err := nodecollector.NewNodeCollector(logger,
Expand Down
37 changes: 37 additions & 0 deletions fixtures/test/asic_db_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"id": "ASIC_DB",
"data": {
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x260000000000111": {
"SAI_VLAN_ATTR_VLAN_ID": "1000"
},
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x260000000000222": {
"SAI_VLAN_ATTR_VLAN_ID": "2000"
},
"ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000001": {
"SAI_BRIDGE_PORT_ATTR_PORT_ID": "oid:0x1000000000002"
},
"ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000002": {
"SAI_BRIDGE_PORT_ATTR_PORT_ID": "oid:0x1000000000003"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000000111\",\"mac\":\"AA:BB:CC:00:00:01\",\"switch_id\":\"oid:0x21000000000000\"}": {
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000001",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000000111\",\"mac\":\"AA:BB:CC:00:00:02\",\"switch_id\":\"oid:0x21000000000000\"}": {
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000001",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000000222\",\"mac\":\"AA:BB:CC:00:00:03\",\"switch_id\":\"oid:0x21000000000000\"}": {
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000002",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_STATIC"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000000999\",\"mac\":\"AA:BB:CC:00:00:04\",\"switch_id\":\"oid:0x21000000000000\"}": {
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000002",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:not-json": {
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000000001",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
}
}
}
97 changes: 97 additions & 0 deletions internal/collector/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func populateRedisData() error {
"../../fixtures/test/counters_db_data.json",
"../../fixtures/test/config_db_data.json",
"../../fixtures/test/appl_db_data.json",
"../../fixtures/test/asic_db_data.json",
"../../fixtures/test/state_db_data.json",
}

Expand Down Expand Up @@ -79,6 +80,7 @@ func TestMain(m *testing.M) {
os.Setenv("LLDP_INCLUDE_MGMT", "true")
os.Setenv("VLAN_ENABLED", "true")
os.Setenv("LAG_ENABLED", "true")
os.Setenv("FDB_ENABLED", "true")
err = populateRedisData()
if err != nil {
slog.Error("failed to populate redis data", "error", err)
Expand All @@ -93,6 +95,7 @@ func TestMain(m *testing.M) {
os.Unsetenv("LLDP_INCLUDE_MGMT")
os.Unsetenv("VLAN_ENABLED")
os.Unsetenv("LAG_ENABLED")
os.Unsetenv("FDB_ENABLED")
os.Exit(exitCode)
}

Expand Down Expand Up @@ -375,3 +378,97 @@ func TestLagCollector(t *testing.T) {
t.Errorf("unexpected collecting result:\n%s", err)
}
}

func TestFdbCollector(t *testing.T) {
promslogConfig := &promslog.Config{}
logger := promslog.New(promslogConfig)

fdbCollector := NewFdbCollector(logger)

problems, err := testutil.CollectAndLint(fdbCollector)
if err != nil {
t.Error("metric lint completed with errors")
}

for _, problem := range problems {
t.Errorf("metric %v has a problem: %v", problem.Metric, problem.Text)
}

metadata := `
# HELP sonic_fdb_collector_success Whether FDB collector succeeded
# TYPE sonic_fdb_collector_success gauge
# HELP sonic_fdb_entries Number of FDB entries
# TYPE sonic_fdb_entries gauge
# HELP sonic_fdb_entries_unknown_vlan Number of FDB entries with unknown VLAN mapping
# TYPE sonic_fdb_entries_unknown_vlan gauge
`

expected := `
sonic_fdb_collector_success 1
sonic_fdb_entries 4
sonic_fdb_entries_unknown_vlan 1
`

if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(metadata+expected), "sonic_fdb_collector_success", "sonic_fdb_entries", "sonic_fdb_entries_unknown_vlan"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}

portMetadata := `
# HELP sonic_fdb_entries_by_port Number of FDB entries by port
# TYPE sonic_fdb_entries_by_port gauge
`

portExpected := `
sonic_fdb_entries_by_port{port="Ethernet0"} 2
sonic_fdb_entries_by_port{port="Ethernet39"} 2
`

if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(portMetadata+portExpected), "sonic_fdb_entries_by_port"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}

vlanMetadata := `
# HELP sonic_fdb_entries_by_vlan Number of FDB entries by VLAN
# TYPE sonic_fdb_entries_by_vlan gauge
`

vlanExpected := `
sonic_fdb_entries_by_vlan{vlan="1000"} 2
sonic_fdb_entries_by_vlan{vlan="2000"} 1
sonic_fdb_entries_by_vlan{vlan="unknown"} 1
`

if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(vlanMetadata+vlanExpected), "sonic_fdb_entries_by_vlan"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}

typeMetadata := `
# HELP sonic_fdb_entries_by_type Number of FDB entries by entry type
# TYPE sonic_fdb_entries_by_type gauge
`

typeExpected := `
sonic_fdb_entries_by_type{entry_type="dynamic"} 3
sonic_fdb_entries_by_type{entry_type="static"} 1
`

if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(typeMetadata+typeExpected), "sonic_fdb_entries_by_type"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}

statusMetadata := `
# HELP sonic_fdb_entries_skipped Number of FDB entries skipped during latest refresh
# TYPE sonic_fdb_entries_skipped gauge
# HELP sonic_fdb_entries_truncated Whether FDB collection hit max entries limit (1=yes, 0=no)
# TYPE sonic_fdb_entries_truncated gauge
`

statusExpected := `
sonic_fdb_entries_skipped 1
sonic_fdb_entries_truncated 0
`

if err := testutil.CollectAndCompare(fdbCollector, strings.NewReader(statusMetadata+statusExpected), "sonic_fdb_entries_skipped", "sonic_fdb_entries_truncated"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
Loading