package collectors import ( "bufio" "encoding/json" "errors" "os" "strconv" "strings" "time" cclog "github.com/ClusterCockpit/cc-metric-collector/internal/ccLogger" lp "github.com/ClusterCockpit/cc-metric-collector/internal/ccMetric" ) const NETSTATFILE = `/proc/net/dev` type NetstatCollectorConfig struct { IncludeDevices []string `json:"include_devices"` } type NetstatCollectorMetric struct { index int lastValue float64 } type NetstatCollector struct { metricCollector config NetstatCollectorConfig matches map[string]map[string]NetstatCollectorMetric devtags map[string]map[string]string lastTimestamp time.Time } func (m *NetstatCollector) Init(config json.RawMessage) error { m.name = "NetstatCollector" m.setup() m.lastTimestamp = time.Now() m.meta = map[string]string{"source": m.name, "group": "Network"} m.devtags = make(map[string]map[string]string) nameIndexMap := map[string]int{ "net_bytes_in": 1, "net_pkts_in": 2, "net_bytes_out": 9, "net_pkts_out": 10, } m.matches = make(map[string]map[string]NetstatCollectorMetric) if len(config) > 0 { err := json.Unmarshal(config, &m.config) if err != nil { cclog.ComponentError(m.name, "Error reading config:", err.Error()) return err } } file, err := os.Open(string(NETSTATFILE)) if err != nil { cclog.ComponentError(m.name, err.Error()) return err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { l := scanner.Text() if !strings.Contains(l, ":") { continue } f := strings.Fields(l) dev := strings.Trim(f[0], ": ") if _, ok := stringArrayContains(m.config.IncludeDevices, dev); ok { m.matches[dev] = make(map[string]NetstatCollectorMetric) for name, idx := range nameIndexMap { m.matches[dev][name] = NetstatCollectorMetric{ index: idx, lastValue: 0, } } m.devtags[dev] = map[string]string{"device": dev, "type": "node"} } } if len(m.devtags) == 0 { return errors.New("no devices to collector metrics found") } m.init = true return nil } func (m *NetstatCollector) Read(interval time.Duration, output chan lp.CCMetric) { if !m.init { return } now := time.Now() file, err := os.Open(string(NETSTATFILE)) if err != nil { cclog.ComponentError(m.name, err.Error()) return } defer file.Close() tdiff := now.Sub(m.lastTimestamp) scanner := bufio.NewScanner(file) for scanner.Scan() { l := scanner.Text() if !strings.Contains(l, ":") { continue } f := strings.Fields(l) dev := strings.Trim(f[0], ":") if devmetrics, ok := m.matches[dev]; ok { for name, data := range devmetrics { v, err := strconv.ParseFloat(f[data.index], 64) if err == nil { vdiff := v - data.lastValue value := vdiff / tdiff.Seconds() if data.lastValue == 0 { value = 0 } data.lastValue = v y, err := lp.New(name, m.devtags[dev], m.meta, map[string]interface{}{"value": value}, now) if err == nil { switch { case strings.Contains(name, "byte"): y.AddMeta("unit", "bytes/sec") case strings.Contains(name, "pkt"): y.AddMeta("unit", "packets/sec") } output <- y } devmetrics[name] = data } } } } m.lastTimestamp = time.Now() } func (m *NetstatCollector) Close() { m.init = false }