package collector import ( "context" "fmt" "log" "time" "gitea.sinav-lab.com/sinav/keenetic-exporter-v2/internal/device" "gitea.sinav-lab.com/sinav/keenetic-exporter-v2/internal/keenetic" "github.com/prometheus/client_golang/prometheus" ) type InterfaceStatsCollector struct { rxBytesDesc *prometheus.Desc txBytesDesc *prometheus.Desc rxPacketsDesc *prometheus.Desc txPacketsDesc *prometheus.Desc rxErrorsDesc *prometheus.Desc txErrorsDesc *prometheus.Desc rxDroppedDesc *prometheus.Desc txDroppedDesc *prometheus.Desc rxBroadcastDesc *prometheus.Desc txBroadcastDesc *prometheus.Desc rxMulticastDesc *prometheus.Desc txMulticastDesc *prometheus.Desc rxSpeedBpsDesc *prometheus.Desc txSpeedBpsDesc *prometheus.Desc } func NewInterfaceStatsCollector() *InterfaceStatsCollector { labels := []string{"device", "interface"} return &InterfaceStatsCollector{ rxBytesDesc: prometheus.NewDesc("keenetic_interface_rx_bytes", "Received bytes", labels, nil), txBytesDesc: prometheus.NewDesc("keenetic_interface_tx_bytes", "Transmitted bytes", labels, nil), rxPacketsDesc: prometheus.NewDesc("keenetic_interface_rx_packets", "Received packets", labels, nil), txPacketsDesc: prometheus.NewDesc("keenetic_interface_tx_packets", "Transmitted packets", labels, nil), rxErrorsDesc: prometheus.NewDesc("keenetic_interface_rx_errors", "Receive errors", labels, nil), txErrorsDesc: prometheus.NewDesc("keenetic_interface_tx_errors", "Transmit errors", labels, nil), rxDroppedDesc: prometheus.NewDesc("keenetic_interface_rx_dropped", "Dropped received packets", labels, nil), txDroppedDesc: prometheus.NewDesc("keenetic_interface_tx_dropped", "Dropped transmitted packets", labels, nil), rxBroadcastDesc: prometheus.NewDesc("keenetic_interface_rx_broadcast_packets", "Received broadcast packets", labels, nil), txBroadcastDesc: prometheus.NewDesc("keenetic_interface_tx_broadcast_packets", "Transmitted broadcast packets", labels, nil), rxMulticastDesc: prometheus.NewDesc("keenetic_interface_rx_multicast_packets", "Received multicast packets", labels, nil), txMulticastDesc: prometheus.NewDesc("keenetic_interface_tx_multicast_packets", "Transmitted multicast packets", labels, nil), rxSpeedBpsDesc: prometheus.NewDesc("keenetic_interface_rx_speed_bps", "Receive speed in bits per second", labels, nil), txSpeedBpsDesc: prometheus.NewDesc("keenetic_interface_tx_speed_bps", "Transmit speed in bits per second", labels, nil), } } func (c *InterfaceStatsCollector) Name() string { return "interface_stats" } func (c *InterfaceStatsCollector) Describe(ch chan<- *prometheus.Desc) { ch <- c.rxBytesDesc ch <- c.txBytesDesc ch <- c.rxPacketsDesc ch <- c.txPacketsDesc ch <- c.rxErrorsDesc ch <- c.txErrorsDesc ch <- c.rxDroppedDesc ch <- c.txDroppedDesc ch <- c.rxBroadcastDesc ch <- c.txBroadcastDesc ch <- c.rxMulticastDesc ch <- c.txMulticastDesc ch <- c.rxSpeedBpsDesc ch <- c.txSpeedBpsDesc } func (c *InterfaceStatsCollector) Collect(dev *device.Device, ch chan<- prometheus.Metric) error { var statsInfo any var ok bool hostname := dev.Name client := dev.Client dev.CacheMutex.RLock() statsInfo, ok = dev.Cache["interface_stats"] dev.CacheMutex.RUnlock() if !ok { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) fresh, err := client.GetConnectedInterfaceStats(ctx) cancel() if err != nil { log.Printf("failed to get interface stats from %s: %v", hostname, err) return err } statsInfo = fresh dev.CacheMutex.Lock() dev.Cache["interface_stats"] = fresh dev.CacheMutex.Unlock() } stats, ok := statsInfo.(map[string]*keenetic.InterfaceStats) if !ok { log.Printf("invalid cache data type for interface stats on %s", hostname) return fmt.Errorf("invalid cache data type for interface stats") } for name, s := range stats { labels := []string{hostname, name} ch <- prometheus.MustNewConstMetric(c.rxBytesDesc, prometheus.CounterValue, s.RxBytes, labels...) ch <- prometheus.MustNewConstMetric(c.txBytesDesc, prometheus.CounterValue, s.TxBytes, labels...) ch <- prometheus.MustNewConstMetric(c.rxPacketsDesc, prometheus.CounterValue, s.RxPackets, labels...) ch <- prometheus.MustNewConstMetric(c.txPacketsDesc, prometheus.CounterValue, s.TxPackets, labels...) ch <- prometheus.MustNewConstMetric(c.rxErrorsDesc, prometheus.CounterValue, s.RxErrors, labels...) ch <- prometheus.MustNewConstMetric(c.txErrorsDesc, prometheus.CounterValue, s.TxErrors, labels...) ch <- prometheus.MustNewConstMetric(c.rxDroppedDesc, prometheus.CounterValue, s.RxDropped, labels...) ch <- prometheus.MustNewConstMetric(c.txDroppedDesc, prometheus.CounterValue, s.TxDropped, labels...) ch <- prometheus.MustNewConstMetric(c.rxBroadcastDesc, prometheus.CounterValue, s.RxBroadcastPackets, labels...) ch <- prometheus.MustNewConstMetric(c.txBroadcastDesc, prometheus.CounterValue, s.TxBroadcastPackets, labels...) ch <- prometheus.MustNewConstMetric(c.rxMulticastDesc, prometheus.CounterValue, s.RxMulticastPackets, labels...) ch <- prometheus.MustNewConstMetric(c.txMulticastDesc, prometheus.CounterValue, s.TxMulticastPackets, labels...) ch <- prometheus.MustNewConstMetric(c.rxSpeedBpsDesc, prometheus.GaugeValue, s.RxSpeed, labels...) ch <- prometheus.MustNewConstMetric(c.txSpeedBpsDesc, prometheus.GaugeValue, s.TxSpeed, labels...) } return nil }