mirror of
https://github.com/prometheus/node_exporter.git
synced 2025-08-20 18:33:52 -07:00
netstat_freebsd: add support for TCP state metrics
Metric added (gauge): node_netstat_tcp_connections{state="CLOSED"} node_netstat_tcp_connections{state="CLOSE_WAIT"} node_netstat_tcp_connections{state="CLOSING"} node_netstat_tcp_connections{state="ESTABLISHED"} node_netstat_tcp_connections{state="FIN_WAIT_1"} node_netstat_tcp_connections{state="FIN_WAIT_2"} node_netstat_tcp_connections{state="LAST_ACK"} node_netstat_tcp_connections{state="LISTEN"} node_netstat_tcp_connections{state="SYN_RCVD"} node_netstat_tcp_connections{state="SYN_SENT"} node_netstat_tcp_connections{state="TIME_WAIT"} Signed-off-by: Danilo Egea Gondolfo <danilo@FreeBSD.org>
This commit is contained in:
parent
2db27ff2fa
commit
4bb3f0315d
|
@ -17,6 +17,7 @@
|
|||
package collector
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
@ -59,6 +60,16 @@ var (
|
|||
ipv6ForwardTotal = "bsdNetstatIPv6ForwardTotal"
|
||||
ipv6DeliveredTotal = "bsdNetstatIPv6DeliveredTotal"
|
||||
|
||||
tcpStates = []string{
|
||||
"CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD",
|
||||
"ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
|
||||
"LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
|
||||
}
|
||||
|
||||
tcpStatesMetric = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "netstat", "tcp_connections"),
|
||||
"Number of TCP connections per state", []string{"state"}, nil)
|
||||
|
||||
counterMetrics = map[string]*prometheus.Desc{
|
||||
// TCP stats
|
||||
tcpSendTotal: prometheus.NewDesc(
|
||||
|
@ -242,6 +253,30 @@ func getData(queryString string, expectedSize int) ([]byte, error) {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
func getTCPStates() ([]uint64, error) {
|
||||
|
||||
// This sysctl returns an array of uint64
|
||||
data, err := sysctlRaw("net.inet.tcp.states")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data)/8 != len(tcpStates) {
|
||||
return nil, fmt.Errorf("invalid TCP states data: expected %d entries, found %d", len(tcpStates), len(data)/8)
|
||||
}
|
||||
|
||||
states := make([]uint64, 0)
|
||||
|
||||
offset := 0
|
||||
for range len(tcpStates) {
|
||||
s := data[offset : offset+8]
|
||||
offset += 8
|
||||
states = append(states, binary.NativeEndian.Uint64(s))
|
||||
}
|
||||
return states, nil
|
||||
}
|
||||
|
||||
type netStatCollector struct {
|
||||
netStatMetric *prometheus.Desc
|
||||
}
|
||||
|
@ -309,6 +344,15 @@ func (c *netStatCollector) Update(ch chan<- prometheus.Metric) error {
|
|||
)
|
||||
}
|
||||
|
||||
tcpConnsPerStates, err := getTCPStates()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, value := range tcpConnsPerStates {
|
||||
ch <- prometheus.MustNewConstMetric(tcpStatesMetric, prometheus.GaugeValue, float64(value), tcpStates[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -323,6 +367,16 @@ func getFreeBSDDataMock(sysctl string) []byte {
|
|||
size := int(unsafe.Sizeof(C.struct_tcpstat{}))
|
||||
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(&tcpStats)), size)
|
||||
} else if sysctl == "net.inet.tcp.states" {
|
||||
tcpStatesSlice := make([]byte, 0, len(tcpStates)*8)
|
||||
tcpStatesValues := []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||
|
||||
for _, value := range tcpStatesValues {
|
||||
tcpStatesSlice = binary.NativeEndian.AppendUint64(tcpStatesSlice, value)
|
||||
}
|
||||
|
||||
return tcpStatesSlice
|
||||
|
||||
} else if sysctl == "net.inet.udp.stats" {
|
||||
udpStats := C.struct_udpstat{
|
||||
udps_opackets: 1234,
|
||||
|
|
|
@ -62,6 +62,24 @@ func TestGetTCPMetrics(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetTCPStatesMetrics(t *testing.T) {
|
||||
testSetup()
|
||||
|
||||
tcpData, err := getTCPStates()
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error:", err)
|
||||
}
|
||||
|
||||
expected := []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||
|
||||
for i, value := range tcpData {
|
||||
if got, want := float64(value), float64(expected[i]); got != want {
|
||||
t.Errorf("unexpected %s value: want %f, got %f", tcpStates[i], want, got)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetUDPMetrics(t *testing.T) {
|
||||
testSetup()
|
||||
|
||||
|
@ -143,20 +161,24 @@ func TestGetIPv6Metrics(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNetStatCollectorUpdate(t *testing.T) {
|
||||
ch := make(chan prometheus.Metric, len(counterMetrics))
|
||||
collector := &netStatCollector{
|
||||
netStatMetric: prometheus.NewDesc("netstat_metric", "NetStat Metric", nil, nil),
|
||||
}
|
||||
|
||||
totalMetrics := len(counterMetrics) + len(tcpStates)
|
||||
|
||||
ch := make(chan prometheus.Metric, totalMetrics)
|
||||
|
||||
err := collector.Update(ch)
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error:", err)
|
||||
}
|
||||
|
||||
if got, want := len(ch), len(counterMetrics); got != want {
|
||||
t.Errorf("metric count mismatch: want %d, got %d", want, got)
|
||||
if got, want := len(ch), totalMetrics; got != want {
|
||||
t.Fatalf("metric count mismatch: want %d, got %d", want, got)
|
||||
}
|
||||
|
||||
for range len(counterMetrics) {
|
||||
for range totalMetrics {
|
||||
<-ch
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue