pkg/exporter/probe/procipvs/ipvsservicestats.go (115 lines of code) (raw):

package procipvs import ( "bytes" "context" "errors" "io" "os" "strconv" "strings" "github.com/alibaba/kubeskoop/pkg/exporter/probe" ) const maxBufferSize = 1024 * 1024 var ( probeName = "ipvs" statf = "/proc/net/ip_vs_stats" Connections = "connections" IncomingPackets = "incomingpackets" OutgoingPackets = "outgoingpackets" IncomingBytes = "incomingbytes" OutgoingBytes = "outgoingbytes" IPVSMetrics = []probe.LegacyMetric{ {Name: Connections, Help: "The total number of connections handled by the IPVS (IP Virtual Server)"}, {Name: IncomingPackets, Help: "The total number of incoming packets processed by the IPVS"}, {Name: OutgoingBytes, Help: "The total number of bytes sent out by the IPVS"}, {Name: IncomingBytes, Help: "The total number of bytes received by the IPVS"}, {Name: OutgoingPackets, Help: "The total number of outgoing packets processed by the IPVS"}, } ) func init() { probe.MustRegisterMetricsProbe(probeName, ipvsProbeCreator) } func ipvsProbeCreator() (probe.MetricsProbe, error) { p := &ProcIPVS{} batchMetrics := probe.NewLegacyBatchMetrics(probeName, IPVSMetrics, p.CollectOnce) return probe.NewMetricsProbe(probeName, p, batchMetrics), nil } type ProcIPVS struct { } func (p *ProcIPVS) Start(_ context.Context) error { return nil } func (p *ProcIPVS) Stop(_ context.Context) error { return nil } func (p *ProcIPVS) CollectOnce() (map[string]map[uint32]uint64, error) { resMap := make(map[string]map[uint32]uint64) f, err := os.Open(statf) if err != nil { return resMap, err } defer f.Close() reader := io.LimitReader(f, maxBufferSize) data, err := io.ReadAll(reader) if err != nil { return resMap, err } stats, err := parseIPVSStats(bytes.NewReader(data)) if err != nil { return resMap, err } // only handle stats in default netns resMap[Connections] = map[uint32]uint64{0: stats.Connections} resMap[IncomingPackets] = map[uint32]uint64{0: stats.IncomingBytes} resMap[IncomingBytes] = map[uint32]uint64{0: stats.IncomingBytes} resMap[OutgoingPackets] = map[uint32]uint64{0: stats.OutgoingPackets} resMap[OutgoingBytes] = map[uint32]uint64{0: stats.OutgoingBytes} return resMap, nil } // IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`. type IPVSStats struct { // Total count of connections. Connections uint64 // Total incoming packages processed. IncomingPackets uint64 // Total outgoing packages processed. OutgoingPackets uint64 // Total incoming traffic. IncomingBytes uint64 // Total outgoing traffic. OutgoingBytes uint64 } // parseIPVSStats performs the actual parsing of `ip_vs_stats`. func parseIPVSStats(r io.Reader) (IPVSStats, error) { var ( statContent []byte statLines []string statFields []string stats IPVSStats ) statContent, err := io.ReadAll(r) if err != nil { return IPVSStats{}, err } statLines = strings.SplitN(string(statContent), "\n", 4) if len(statLines) != 4 { return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short") } statFields = strings.Fields(statLines[2]) if len(statFields) != 5 { return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields") } stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64) if err != nil { return IPVSStats{}, err } stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64) if err != nil { return IPVSStats{}, err } stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64) if err != nil { return IPVSStats{}, err } stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64) if err != nil { return IPVSStats{}, err } stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64) if err != nil { return IPVSStats{}, err } return stats, nil }