Add watchInterfaceEvents() listener
This commit is contained in:
@ -13,9 +13,9 @@ import (
|
|||||||
"github.com/posteo/go-agentx/value"
|
"github.com/posteo/go-agentx/value"
|
||||||
"go.fd.io/govpp/api"
|
"go.fd.io/govpp/api"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"govpp-snmp-agentx/logger"
|
"govpp-snmp-agentx/logger"
|
||||||
"govpp-snmp-agentx/vppstats"
|
"govpp-snmp-agentx/vpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IF-MIB OID bases:
|
// IF-MIB OID bases:
|
||||||
@ -72,8 +72,8 @@ type VPPConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VPPInterface struct {
|
type VPPInterface struct {
|
||||||
Description string `yaml:"description"`
|
Description string `yaml:"description"`
|
||||||
SubInterfaces map[string]VPPInterface `yaml:"sub-interfaces"`
|
SubInterfaces map[string]VPPInterface `yaml:"sub-interfaces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InterfaceMIB struct {
|
type InterfaceMIB struct {
|
||||||
@ -172,7 +172,7 @@ func (m *InterfaceMIB) UpdateStats(interfaceStats *api.InterfaceStats) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *InterfaceMIB) addInterfaceToMIB(iface *api.InterfaceCounters) {
|
func (m *InterfaceMIB) addInterfaceToMIB(iface *api.InterfaceCounters) {
|
||||||
idx := int(iface.InterfaceIndex) + *vppstats.IfIndexOffset
|
idx := int(iface.InterfaceIndex) + *vpp.IfIndexOffset
|
||||||
|
|
||||||
// Add ifEntry (classic interface table) entries
|
// Add ifEntry (classic interface table) entries
|
||||||
m.addIfEntry(iface, idx)
|
m.addIfEntry(iface, idx)
|
||||||
|
@ -11,27 +11,27 @@ import (
|
|||||||
|
|
||||||
func TestNewInterfaceMIB(t *testing.T) {
|
func TestNewInterfaceMIB(t *testing.T) {
|
||||||
mib := NewInterfaceMIB()
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
if mib == nil {
|
if mib == nil {
|
||||||
t.Fatal("NewInterfaceMIB returned nil")
|
t.Fatal("NewInterfaceMIB returned nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if mib.handler == nil {
|
if mib.handler == nil {
|
||||||
t.Error("Expected handler to be initialized")
|
t.Error("Expected handler to be initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
if mib.stats == nil {
|
if mib.stats == nil {
|
||||||
t.Error("Expected stats map to be initialized")
|
t.Error("Expected stats map to be initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
if mib.descriptions == nil {
|
if mib.descriptions == nil {
|
||||||
t.Error("Expected descriptions map to be initialized")
|
t.Error("Expected descriptions map to be initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(mib.stats) != 0 {
|
if len(mib.stats) != 0 {
|
||||||
t.Errorf("Expected stats map to be empty, got %d entries", len(mib.stats))
|
t.Errorf("Expected stats map to be empty, got %d entries", len(mib.stats))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(mib.descriptions) != 0 {
|
if len(mib.descriptions) != 0 {
|
||||||
t.Errorf("Expected descriptions map to be empty, got %d entries", len(mib.descriptions))
|
t.Errorf("Expected descriptions map to be empty, got %d entries", len(mib.descriptions))
|
||||||
}
|
}
|
||||||
@ -40,11 +40,11 @@ func TestNewInterfaceMIB(t *testing.T) {
|
|||||||
func TestGetHandler(t *testing.T) {
|
func TestGetHandler(t *testing.T) {
|
||||||
mib := NewInterfaceMIB()
|
mib := NewInterfaceMIB()
|
||||||
handler := mib.GetHandler()
|
handler := mib.GetHandler()
|
||||||
|
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
t.Error("GetHandler returned nil")
|
t.Error("GetHandler returned nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if handler != mib.handler {
|
if handler != mib.handler {
|
||||||
t.Error("GetHandler returned different handler than expected")
|
t.Error("GetHandler returned different handler than expected")
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ func TestGetHandler(t *testing.T) {
|
|||||||
|
|
||||||
func TestLoadVPPConfigValidYAML(t *testing.T) {
|
func TestLoadVPPConfigValidYAML(t *testing.T) {
|
||||||
mib := NewInterfaceMIB()
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
// Create a temporary YAML file
|
// Create a temporary YAML file
|
||||||
yamlContent := `interfaces:
|
yamlContent := `interfaces:
|
||||||
GigabitEthernet0/0/0:
|
GigabitEthernet0/0/0:
|
||||||
@ -64,39 +64,39 @@ loopbacks:
|
|||||||
loop0:
|
loop0:
|
||||||
description: 'Test: Loopback'
|
description: 'Test: Loopback'
|
||||||
`
|
`
|
||||||
|
|
||||||
tmpfile, err := os.CreateTemp("", "test_*.yaml")
|
tmpfile, err := os.CreateTemp("", "test_*.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpfile.Name())
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
if _, err := tmpfile.Write([]byte(yamlContent)); err != nil {
|
if _, err := tmpfile.Write([]byte(yamlContent)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := tmpfile.Close(); err != nil {
|
if err := tmpfile.Close(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test loading the config
|
// Test loading the config
|
||||||
err = mib.LoadVPPConfig(tmpfile.Name())
|
err = mib.LoadVPPConfig(tmpfile.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("LoadVPPConfig failed: %v", err)
|
t.Fatalf("LoadVPPConfig failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that descriptions were loaded
|
// Check that descriptions were loaded
|
||||||
if len(mib.descriptions) != 3 {
|
if len(mib.descriptions) != 3 {
|
||||||
t.Errorf("Expected 3 descriptions, got %d", len(mib.descriptions))
|
t.Errorf("Expected 3 descriptions, got %d", len(mib.descriptions))
|
||||||
}
|
}
|
||||||
|
|
||||||
if mib.descriptions["GigabitEthernet0/0/0"] != "Test: Interface" {
|
if mib.descriptions["GigabitEthernet0/0/0"] != "Test: Interface" {
|
||||||
t.Errorf("Unexpected interface description: %s", mib.descriptions["GigabitEthernet0/0/0"])
|
t.Errorf("Unexpected interface description: %s", mib.descriptions["GigabitEthernet0/0/0"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if mib.descriptions["GigabitEthernet0/0/0.100"] != "Test: Sub-interface" {
|
if mib.descriptions["GigabitEthernet0/0/0.100"] != "Test: Sub-interface" {
|
||||||
t.Errorf("Unexpected sub-interface description: %s", mib.descriptions["GigabitEthernet0/0/0.100"])
|
t.Errorf("Unexpected sub-interface description: %s", mib.descriptions["GigabitEthernet0/0/0.100"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if mib.descriptions["loop0"] != "Test: Loopback" {
|
if mib.descriptions["loop0"] != "Test: Loopback" {
|
||||||
t.Errorf("Unexpected loopback description: %s", mib.descriptions["loop0"])
|
t.Errorf("Unexpected loopback description: %s", mib.descriptions["loop0"])
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ loopbacks:
|
|||||||
|
|
||||||
func TestLoadVPPConfigNonExistentFile(t *testing.T) {
|
func TestLoadVPPConfigNonExistentFile(t *testing.T) {
|
||||||
mib := NewInterfaceMIB()
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
err := mib.LoadVPPConfig("/nonexistent/file.yaml")
|
err := mib.LoadVPPConfig("/nonexistent/file.yaml")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected error for non-existent file")
|
t.Error("Expected error for non-existent file")
|
||||||
@ -113,25 +113,25 @@ func TestLoadVPPConfigNonExistentFile(t *testing.T) {
|
|||||||
|
|
||||||
func TestLoadVPPConfigInvalidYAML(t *testing.T) {
|
func TestLoadVPPConfigInvalidYAML(t *testing.T) {
|
||||||
mib := NewInterfaceMIB()
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
// Create a temporary file with invalid YAML
|
// Create a temporary file with invalid YAML
|
||||||
invalidYAML := `interfaces:
|
invalidYAML := `interfaces:
|
||||||
test: [
|
test: [
|
||||||
`
|
`
|
||||||
|
|
||||||
tmpfile, err := os.CreateTemp("", "invalid_*.yaml")
|
tmpfile, err := os.CreateTemp("", "invalid_*.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpfile.Name())
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
if _, err := tmpfile.Write([]byte(invalidYAML)); err != nil {
|
if _, err := tmpfile.Write([]byte(invalidYAML)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := tmpfile.Close(); err != nil {
|
if err := tmpfile.Close(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mib.LoadVPPConfig(tmpfile.Name())
|
err = mib.LoadVPPConfig(tmpfile.Name())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected error for invalid YAML")
|
t.Error("Expected error for invalid YAML")
|
||||||
@ -140,7 +140,7 @@ func TestLoadVPPConfigInvalidYAML(t *testing.T) {
|
|||||||
|
|
||||||
func TestUpdateStatsBasic(t *testing.T) {
|
func TestUpdateStatsBasic(t *testing.T) {
|
||||||
mib := NewInterfaceMIB()
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
// Create mock interface stats
|
// Create mock interface stats
|
||||||
stats := &api.InterfaceStats{
|
stats := &api.InterfaceStats{
|
||||||
Interfaces: []api.InterfaceCounters{
|
Interfaces: []api.InterfaceCounters{
|
||||||
@ -158,15 +158,15 @@ func TestUpdateStatsBasic(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call UpdateStats (this will test the basic flow without AgentX sessions)
|
// Call UpdateStats (this will test the basic flow without AgentX sessions)
|
||||||
mib.UpdateStats(stats)
|
mib.UpdateStats(stats)
|
||||||
|
|
||||||
// Check that stats were stored
|
// Check that stats were stored
|
||||||
if len(mib.stats) != 1 {
|
if len(mib.stats) != 1 {
|
||||||
t.Errorf("Expected 1 interface in stats, got %d", len(mib.stats))
|
t.Errorf("Expected 1 interface in stats, got %d", len(mib.stats))
|
||||||
}
|
}
|
||||||
|
|
||||||
if storedStats, exists := mib.stats[0]; !exists {
|
if storedStats, exists := mib.stats[0]; !exists {
|
||||||
t.Error("Expected interface 0 to be stored in stats")
|
t.Error("Expected interface 0 to be stored in stats")
|
||||||
} else {
|
} else {
|
||||||
@ -177,4 +177,4 @@ func TestUpdateStatsBasic(t *testing.T) {
|
|||||||
t.Errorf("Expected RX packets 100, got %d", storedStats.Rx.Packets)
|
t.Errorf("Expected RX packets 100, got %d", storedStats.Rx.Packets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
"govpp-snmp-agentx/config"
|
"govpp-snmp-agentx/config"
|
||||||
"govpp-snmp-agentx/ifmib"
|
"govpp-snmp-agentx/ifmib"
|
||||||
"govpp-snmp-agentx/logger"
|
"govpp-snmp-agentx/logger"
|
||||||
"govpp-snmp-agentx/vppstats"
|
"govpp-snmp-agentx/vpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -41,7 +41,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start VPP stats routine with callback to update MIB
|
// Start VPP stats routine with callback to update MIB
|
||||||
vppstats.StartStatsRoutine(interfaceMIB.UpdateStats)
|
vpp.StartStatsRoutine(interfaceMIB.UpdateStats)
|
||||||
|
|
||||||
// Set up signal handling for graceful shutdown
|
// Set up signal handling for graceful shutdown
|
||||||
sigChan := make(chan os.Signal, 1)
|
sigChan := make(chan os.Signal, 1)
|
||||||
@ -50,7 +50,7 @@ func main() {
|
|||||||
// Wait for shutdown signal
|
// Wait for shutdown signal
|
||||||
<-sigChan
|
<-sigChan
|
||||||
logger.Printf("Shutting down...")
|
logger.Printf("Shutting down...")
|
||||||
|
|
||||||
// Flush any buffered log entries
|
// Flush any buffered log entries
|
||||||
logger.Sync()
|
logger.Sync()
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ func TestMainCompiles(t *testing.T) {
|
|||||||
// This would run main(), but we skip it in tests
|
// This would run main(), but we skip it in tests
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just test that we can access main package
|
// Just test that we can access main package
|
||||||
t.Log("Main package compiles successfully")
|
t.Log("Main package compiles successfully")
|
||||||
}
|
}
|
||||||
|
72
src/vpp/vpp_iface.go
Normal file
72
src/vpp/vpp_iface.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
||||||
|
|
||||||
|
package vpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.fd.io/govpp/api"
|
||||||
|
interfaces "go.fd.io/govpp/binapi/interface"
|
||||||
|
|
||||||
|
"govpp-snmp-agentx/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WatchInterfaceEvents(ch api.Channel) error {
|
||||||
|
logger.Debugf("WatchInterfaceEvents() called - starting interface event monitoring")
|
||||||
|
|
||||||
|
notifChan := make(chan api.Message, 100)
|
||||||
|
|
||||||
|
// subscribe for specific event message
|
||||||
|
logger.Debugf("Subscribing to interface events...")
|
||||||
|
sub, err := ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{})
|
||||||
|
if err != nil {
|
||||||
|
logger.Debugf("error subscribing to interface events: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Debugf("Successfully subscribed to interface events")
|
||||||
|
|
||||||
|
// enable interface events in VPP
|
||||||
|
logger.Debugf("Enabling interface events in VPP...")
|
||||||
|
err = ch.SendRequest(&interfaces.WantInterfaceEvents{
|
||||||
|
PID: uint32(os.Getpid()),
|
||||||
|
EnableDisable: 1,
|
||||||
|
}).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
|
||||||
|
if err != nil {
|
||||||
|
logger.Debugf("error enabling interface events: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debugf("Interface events enabled in VPP, starting event listener goroutine")
|
||||||
|
|
||||||
|
// receive notifications
|
||||||
|
go func() {
|
||||||
|
logger.Debugf("Interface event listener goroutine started")
|
||||||
|
defer func() {
|
||||||
|
logger.Debugf("Interface event listener goroutine shutting down")
|
||||||
|
// disable interface events in VPP
|
||||||
|
err = ch.SendRequest(&interfaces.WantInterfaceEvents{
|
||||||
|
PID: uint32(os.Getpid()),
|
||||||
|
EnableDisable: 0,
|
||||||
|
}).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
|
||||||
|
if err != nil {
|
||||||
|
logger.Debugf("error disabling interface events: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsubscribe from receiving events
|
||||||
|
err = sub.Unsubscribe()
|
||||||
|
if err != nil {
|
||||||
|
logger.Debugf("error unsubscribing from interface events: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
logger.Debugf("Interface event listener waiting for events...")
|
||||||
|
for notif := range notifChan {
|
||||||
|
e := notif.(*interfaces.SwInterfaceEvent)
|
||||||
|
logger.Debugf("interface event: SwIfIndex=%d, Flags=%d, Deleted=%t",
|
||||||
|
e.SwIfIndex, e.Flags, e.Deleted)
|
||||||
|
}
|
||||||
|
logger.Debugf("Interface event listener goroutine ended")
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
||||||
|
|
||||||
package vppstats
|
package vpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
@ -131,6 +131,21 @@ func statsRoutine(period time.Duration, callback StatsCallback) {
|
|||||||
logger.Printf("Connected to VPP (API: %s, Stats: %s)", *ApiAddr, *StatsAddr)
|
logger.Printf("Connected to VPP (API: %s, Stats: %s)", *ApiAddr, *StatsAddr)
|
||||||
connected = true
|
connected = true
|
||||||
wasConnected = true
|
wasConnected = true
|
||||||
|
|
||||||
|
// Start watching interface events
|
||||||
|
logger.Debugf("Creating API channel for interface events...")
|
||||||
|
ch, err := conn.NewAPIChannel()
|
||||||
|
if err != nil {
|
||||||
|
logger.Debugf("Failed to create API channel for interface events: %v", err)
|
||||||
|
} else {
|
||||||
|
logger.Debugf("API channel created successfully, calling WatchInterfaceEvents...")
|
||||||
|
if err := WatchInterfaceEvents(ch); err != nil {
|
||||||
|
logger.Debugf("Failed to start interface event watching: %v", err)
|
||||||
|
ch.Close()
|
||||||
|
} else {
|
||||||
|
logger.Printf("Interface event watching started successfully")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query stats if connected
|
// Query stats if connected
|
||||||
@ -231,4 +246,3 @@ func checkVPPLiveness(conn *core.Connection) bool {
|
|||||||
logger.Debugf("VPP liveness check passed (version: %s)", string(reply.Version))
|
logger.Debugf("VPP liveness check passed (version: %s)", string(reply.Version))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -1,163 +0,0 @@
|
|||||||
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
|
||||||
|
|
||||||
package vppstats
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.fd.io/govpp/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestVPPStatsFlags(t *testing.T) {
|
|
||||||
// Test default values
|
|
||||||
if *ApiAddr != "/var/run/vpp/api.sock" {
|
|
||||||
t.Errorf("Expected default API address to be '/var/run/vpp/api.sock', got '%s'", *ApiAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *StatsAddr != "/var/run/vpp/stats.sock" {
|
|
||||||
t.Errorf("Expected default stats address to be '/var/run/vpp/stats.sock', got '%s'", *StatsAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *IfIndexOffset != 1000 {
|
|
||||||
t.Errorf("Expected default interface index offset to be 1000, got %d", *IfIndexOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *Period != 10 {
|
|
||||||
t.Errorf("Expected default period to be 10, got %d", *Period)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlagRegistrations(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
flagName string
|
|
||||||
defValue string
|
|
||||||
}{
|
|
||||||
{"API address", "vppstats.api.addr", "/var/run/vpp/api.sock"},
|
|
||||||
{"Stats address", "vppstats.stats.addr", "/var/run/vpp/stats.sock"},
|
|
||||||
{"Index offset", "vppstats.ifindex-offset", "1000"},
|
|
||||||
{"Period", "vppstats.period", "10"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
f := flag.Lookup(tt.flagName)
|
|
||||||
if f == nil {
|
|
||||||
t.Errorf("Expected %s flag to be registered", tt.flagName)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.DefValue != tt.defValue {
|
|
||||||
t.Errorf("Expected %s flag default value to be '%s', got '%s'",
|
|
||||||
tt.flagName, tt.defValue, f.DefValue)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatsCallbackType(t *testing.T) {
|
|
||||||
// Test that we can create a valid callback function
|
|
||||||
var called bool
|
|
||||||
var receivedStats *api.InterfaceStats
|
|
||||||
|
|
||||||
callback := func(stats *api.InterfaceStats) {
|
|
||||||
called = true
|
|
||||||
receivedStats = stats
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create mock stats
|
|
||||||
mockStats := &api.InterfaceStats{
|
|
||||||
Interfaces: []api.InterfaceCounters{
|
|
||||||
{
|
|
||||||
InterfaceIndex: 1,
|
|
||||||
InterfaceName: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the callback
|
|
||||||
callback(mockStats)
|
|
||||||
|
|
||||||
if !called {
|
|
||||||
t.Error("Expected callback to be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
if receivedStats != mockStats {
|
|
||||||
t.Error("Expected callback to receive the same stats object")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedStats.Interfaces) != 1 {
|
|
||||||
t.Errorf("Expected 1 interface, got %d", len(receivedStats.Interfaces))
|
|
||||||
}
|
|
||||||
|
|
||||||
if receivedStats.Interfaces[0].InterfaceName != "test" {
|
|
||||||
t.Errorf("Expected interface name 'test', got '%s'", receivedStats.Interfaces[0].InterfaceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPeriodConversion(t *testing.T) {
|
|
||||||
// Test that period conversion works correctly
|
|
||||||
originalPeriod := *Period
|
|
||||||
defer func() { *Period = originalPeriod }()
|
|
||||||
|
|
||||||
testPeriods := []struct {
|
|
||||||
input int
|
|
||||||
expected time.Duration
|
|
||||||
}{
|
|
||||||
{1, time.Second},
|
|
||||||
{5, 5 * time.Second},
|
|
||||||
{10, 10 * time.Second},
|
|
||||||
{60, time.Minute},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range testPeriods {
|
|
||||||
t.Run(fmt.Sprintf("period_%d", tt.input), func(t *testing.T) {
|
|
||||||
*Period = tt.input
|
|
||||||
result := time.Duration(*Period) * time.Second
|
|
||||||
|
|
||||||
if result != tt.expected {
|
|
||||||
t.Errorf("Expected period %v, got %v", tt.expected, result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlagValues(t *testing.T) {
|
|
||||||
// Save original flag values
|
|
||||||
originalApiAddr := *ApiAddr
|
|
||||||
originalStatsAddr := *StatsAddr
|
|
||||||
originalOffset := *IfIndexOffset
|
|
||||||
originalPeriod := *Period
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
*ApiAddr = originalApiAddr
|
|
||||||
*StatsAddr = originalStatsAddr
|
|
||||||
*IfIndexOffset = originalOffset
|
|
||||||
*Period = originalPeriod
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Test setting flag values
|
|
||||||
*ApiAddr = "/custom/api.sock"
|
|
||||||
*StatsAddr = "/custom/stats.sock"
|
|
||||||
*IfIndexOffset = 2000
|
|
||||||
*Period = 30
|
|
||||||
|
|
||||||
if *ApiAddr != "/custom/api.sock" {
|
|
||||||
t.Errorf("Expected API address to be '/custom/api.sock', got '%s'", *ApiAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *StatsAddr != "/custom/stats.sock" {
|
|
||||||
t.Errorf("Expected stats address to be '/custom/stats.sock', got '%s'", *StatsAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *IfIndexOffset != 2000 {
|
|
||||||
t.Errorf("Expected interface index offset to be 2000, got %d", *IfIndexOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *Period != 30 {
|
|
||||||
t.Errorf("Expected period to be 30, got %d", *Period)
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user