mirror of
https://github.com/henrygd/beszel.git
synced 2025-12-17 02:36:17 +01:00
add pattern matching and blacklist functionality to NICS env var. (#1190)
This commit is contained in:
@@ -3,6 +3,7 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -13,6 +14,69 @@ import (
|
|||||||
|
|
||||||
var netInterfaceDeltaTracker = deltatracker.NewDeltaTracker[string, uint64]()
|
var netInterfaceDeltaTracker = deltatracker.NewDeltaTracker[string, uint64]()
|
||||||
|
|
||||||
|
// NicConfig controls inclusion/exclusion of network interfaces via the NICS env var
|
||||||
|
//
|
||||||
|
// Behavior mirrors SensorConfig's matching logic:
|
||||||
|
// - Leading '-' means blacklist mode; otherwise whitelist mode
|
||||||
|
// - Supports '*' wildcards using path.Match
|
||||||
|
// - In whitelist mode with an empty list, no NICs are selected
|
||||||
|
// - In blacklist mode with an empty list, all NICs are selected
|
||||||
|
type NicConfig struct {
|
||||||
|
nics map[string]struct{}
|
||||||
|
isBlacklist bool
|
||||||
|
hasWildcards bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNicConfig(nicsEnvVal string) *NicConfig {
|
||||||
|
cfg := &NicConfig{
|
||||||
|
nics: make(map[string]struct{}),
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(nicsEnvVal, "-") {
|
||||||
|
cfg.isBlacklist = true
|
||||||
|
nicsEnvVal = nicsEnvVal[1:]
|
||||||
|
}
|
||||||
|
for nic := range strings.SplitSeq(nicsEnvVal, ",") {
|
||||||
|
nic = strings.TrimSpace(nic)
|
||||||
|
if nic != "" {
|
||||||
|
cfg.nics[nic] = struct{}{}
|
||||||
|
if strings.Contains(nic, "*") {
|
||||||
|
cfg.hasWildcards = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidNic determines if a NIC should be included based on NicConfig rules
|
||||||
|
func isValidNic(nicName string, cfg *NicConfig) bool {
|
||||||
|
// Empty list behavior differs by mode: blacklist: allow all; whitelist: allow none
|
||||||
|
if len(cfg.nics) == 0 {
|
||||||
|
return cfg.isBlacklist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exact match: return true if whitelist, false if blacklist
|
||||||
|
if _, exactMatch := cfg.nics[nicName]; exactMatch {
|
||||||
|
return !cfg.isBlacklist
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no wildcards, return true if blacklist, false if whitelist
|
||||||
|
if !cfg.hasWildcards {
|
||||||
|
return cfg.isBlacklist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for wildcard patterns
|
||||||
|
for pattern := range cfg.nics {
|
||||||
|
if !strings.Contains(pattern, "*") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if match, _ := path.Match(pattern, nicName); match {
|
||||||
|
return !cfg.isBlacklist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.isBlacklist
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Agent) updateNetworkStats(systemStats *system.Stats) {
|
func (a *Agent) updateNetworkStats(systemStats *system.Stats) {
|
||||||
// network stats
|
// network stats
|
||||||
if len(a.netInterfaces) == 0 {
|
if len(a.netInterfaces) == 0 {
|
||||||
@@ -89,14 +153,11 @@ func (a *Agent) initializeNetIoStats() {
|
|||||||
// reset valid network interfaces
|
// reset valid network interfaces
|
||||||
a.netInterfaces = make(map[string]struct{}, 0)
|
a.netInterfaces = make(map[string]struct{}, 0)
|
||||||
|
|
||||||
// map of network interface names passed in via NICS env var
|
// parse NICS env var for whitelist / blacklist
|
||||||
var nicsMap map[string]struct{}
|
nicsEnvVal, nicsEnvExists := GetEnv("NICS")
|
||||||
nics, nicsEnvExists := GetEnv("NICS")
|
var nicCfg *NicConfig
|
||||||
if nicsEnvExists {
|
if nicsEnvExists {
|
||||||
nicsMap = make(map[string]struct{}, 0)
|
nicCfg = newNicConfig(nicsEnvVal)
|
||||||
for nic := range strings.SplitSeq(nics, ",") {
|
|
||||||
nicsMap[nic] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset network I/O stats
|
// reset network I/O stats
|
||||||
@@ -107,17 +168,11 @@ func (a *Agent) initializeNetIoStats() {
|
|||||||
if netIO, err := psutilNet.IOCounters(true); err == nil {
|
if netIO, err := psutilNet.IOCounters(true); err == nil {
|
||||||
a.netIoStats.Time = time.Now()
|
a.netIoStats.Time = time.Now()
|
||||||
for _, v := range netIO {
|
for _, v := range netIO {
|
||||||
switch {
|
if nicsEnvExists && !isValidNic(v.Name, nicCfg) {
|
||||||
// skip if nics exists and the interface is not in the list
|
continue
|
||||||
case nicsEnvExists:
|
}
|
||||||
if _, nameInNics := nicsMap[v.Name]; !nameInNics {
|
if a.skipNetworkInterface(v) {
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
// otherwise run the interface name through the skipNetworkInterface function
|
|
||||||
default:
|
|
||||||
if a.skipNetworkInterface(v) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
slog.Info("Detected network interface", "name", v.Name, "sent", v.BytesSent, "recv", v.BytesRecv)
|
slog.Info("Detected network interface", "name", v.Name, "sent", v.BytesSent, "recv", v.BytesRecv)
|
||||||
a.netIoStats.BytesSent += v.BytesSent
|
a.netIoStats.BytesSent += v.BytesSent
|
||||||
|
|||||||
259
agent/network_test.go
Normal file
259
agent/network_test.go
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
//go:build testing
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsValidNic(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
nicName string
|
||||||
|
config *NicConfig
|
||||||
|
expectedValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Whitelist - NIC in list",
|
||||||
|
nicName: "eth0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
},
|
||||||
|
expectedValid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Whitelist - NIC not in list",
|
||||||
|
nicName: "wlan0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
},
|
||||||
|
expectedValid: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Blacklist - NIC in list",
|
||||||
|
nicName: "eth0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}},
|
||||||
|
isBlacklist: true,
|
||||||
|
},
|
||||||
|
expectedValid: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Blacklist - NIC not in list",
|
||||||
|
nicName: "wlan0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}},
|
||||||
|
isBlacklist: true,
|
||||||
|
},
|
||||||
|
expectedValid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Whitelist with wildcard - matching pattern",
|
||||||
|
nicName: "eth1",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth*": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
expectedValid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Whitelist with wildcard - non-matching pattern",
|
||||||
|
nicName: "wlan0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth*": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
expectedValid: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Blacklist with wildcard - matching pattern",
|
||||||
|
nicName: "eth1",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth*": {}},
|
||||||
|
isBlacklist: true,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
expectedValid: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Blacklist with wildcard - non-matching pattern",
|
||||||
|
nicName: "wlan0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth*": {}},
|
||||||
|
isBlacklist: true,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
expectedValid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty whitelist config - no NICs allowed",
|
||||||
|
nicName: "eth0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{},
|
||||||
|
isBlacklist: false,
|
||||||
|
},
|
||||||
|
expectedValid: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty blacklist config - all NICs allowed",
|
||||||
|
nicName: "eth0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{},
|
||||||
|
isBlacklist: true,
|
||||||
|
},
|
||||||
|
expectedValid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple patterns - exact match",
|
||||||
|
nicName: "eth0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}, "wlan*": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
},
|
||||||
|
expectedValid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple patterns - wildcard match",
|
||||||
|
nicName: "wlan1",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}, "wlan*": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
expectedValid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple patterns - no match",
|
||||||
|
nicName: "bond0",
|
||||||
|
config: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}, "wlan*": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
expectedValid: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := isValidNic(tt.nicName, tt.config)
|
||||||
|
assert.Equal(t, tt.expectedValid, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewNicConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
nicsEnvVal string
|
||||||
|
expectedCfg *NicConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Empty string",
|
||||||
|
nicsEnvVal: "",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Single NIC whitelist",
|
||||||
|
nicsEnvVal: "eth0",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple NICs whitelist",
|
||||||
|
nicsEnvVal: "eth0,wlan0",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}, "wlan0": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Blacklist mode",
|
||||||
|
nicsEnvVal: "-eth0,wlan0",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}, "wlan0": {}},
|
||||||
|
isBlacklist: true,
|
||||||
|
hasWildcards: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "With wildcards",
|
||||||
|
nicsEnvVal: "eth*,wlan0",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth*": {}, "wlan0": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Blacklist with wildcards",
|
||||||
|
nicsEnvVal: "-eth*,wlan0",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth*": {}, "wlan0": {}},
|
||||||
|
isBlacklist: true,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "With whitespace",
|
||||||
|
nicsEnvVal: "eth0, wlan0 , eth1",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}, "wlan0": {}, "eth1": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Only wildcards",
|
||||||
|
nicsEnvVal: "eth*,wlan*",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth*": {}, "wlan*": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Leading dash only",
|
||||||
|
nicsEnvVal: "-",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{},
|
||||||
|
isBlacklist: true,
|
||||||
|
hasWildcards: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mixed exact and wildcard",
|
||||||
|
nicsEnvVal: "eth0,br-*",
|
||||||
|
expectedCfg: &NicConfig{
|
||||||
|
nics: map[string]struct{}{"eth0": {}, "br-*": {}},
|
||||||
|
isBlacklist: false,
|
||||||
|
hasWildcards: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cfg := newNicConfig(tt.nicsEnvVal)
|
||||||
|
require.NotNil(t, cfg)
|
||||||
|
assert.Equal(t, tt.expectedCfg.isBlacklist, cfg.isBlacklist)
|
||||||
|
assert.Equal(t, tt.expectedCfg.hasWildcards, cfg.hasWildcards)
|
||||||
|
assert.Equal(t, tt.expectedCfg.nics, cfg.nics)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
- Adjust calculation of cached memory (fixes #1187, #1196)
|
- Adjust calculation of cached memory (fixes #1187, #1196)
|
||||||
|
|
||||||
|
- Add pattern matching and blacklist functionality to `NICS` env var. (#1190)
|
||||||
|
|
||||||
- Update Intel GPU collector to parse plain text (`-l`) instead of JSON output (#1150)
|
- Update Intel GPU collector to parse plain text (`-l`) instead of JSON output (#1150)
|
||||||
|
|
||||||
## 0.12.10
|
## 0.12.10
|
||||||
|
|||||||
Reference in New Issue
Block a user