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" "gitea.sinav-lab.com/sinav/keenetic-exporter-v2/internal/utils" "github.com/prometheus/client_golang/prometheus" ) type InternetCollector struct { enabledDesc *prometheus.Desc reliableDesc *prometheus.Desc gatewayAccessibleDesc *prometheus.Desc dnsAccessibleDesc *prometheus.Desc captiveAccessibleDesc *prometheus.Desc internetDesc *prometheus.Desc gatewayFailuresDesc *prometheus.Desc captiveFailuresDesc *prometheus.Desc } func NewInternetCollector() *InternetCollector { labels := []string{"device", "interface"} return &InternetCollector{ enabledDesc: prometheus.NewDesc("keenetic_internet_enabled", "Internet enabled", labels, nil), reliableDesc: prometheus.NewDesc("keenetic_internet_reliable", "Internet reliable", labels, nil), gatewayAccessibleDesc: prometheus.NewDesc("keenetic_gateway_accessible", "Gateway accessible", labels, nil), dnsAccessibleDesc: prometheus.NewDesc("keenetic_dns_accessible", "DNS accessible", labels, nil), captiveAccessibleDesc: prometheus.NewDesc("keenetic_captive_accessible", "Captive portal accessible", labels, nil), internetDesc: prometheus.NewDesc("keenetic_internet_available", "Internet available", labels, nil), gatewayFailuresDesc: prometheus.NewDesc("keenetic_gateway_failures", "Gateway access failure count", labels, nil), captiveFailuresDesc: prometheus.NewDesc("keenetic_captive_failures", "Captive portal failure count", labels, nil), } } func (c *InternetCollector) Name() string { return "internet" } func (c *InternetCollector) Describe(ch chan<- *prometheus.Desc) { ch <- c.enabledDesc ch <- c.reliableDesc ch <- c.gatewayAccessibleDesc ch <- c.dnsAccessibleDesc ch <- c.captiveAccessibleDesc ch <- c.internetDesc ch <- c.gatewayFailuresDesc ch <- c.captiveFailuresDesc } func (c *InternetCollector) Collect(dev *device.Device, ch chan<- prometheus.Metric) error { var internetInfo any var ok bool hostname := dev.Name client := dev.Client dev.CacheMutex.RLock() internetInfo, ok = dev.Cache["internet"] valid := time.Since(dev.LastUpdate) < dev.CacheTTL dev.CacheMutex.RUnlock() if !ok || !valid { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) fresh, err := client.GetInternetStatus(ctx) cancel() if err != nil { log.Printf("failed to get internet status from %s: %v", hostname, err) return err } internetInfo = fresh dev.CacheMutex.Lock() dev.Cache["internet"] = fresh dev.LastUpdate = time.Now() dev.CacheMutex.Unlock() } status, ok := internetInfo.(*keenetic.InternetStatus) if !ok || status == nil { log.Printf("invalid cache data type for internet status on %s", hostname) return fmt.Errorf("invalid cache data type for internet status") } iface := status.Gateway.Interface labels := []string{hostname, iface} ch <- prometheus.MustNewConstMetric(c.enabledDesc, prometheus.GaugeValue, utils.BoolToFloat(status.Enabled), labels...) ch <- prometheus.MustNewConstMetric(c.reliableDesc, prometheus.GaugeValue, utils.BoolToFloat(status.Reliable), labels...) ch <- prometheus.MustNewConstMetric(c.gatewayAccessibleDesc, prometheus.GaugeValue, utils.BoolToFloat(status.GatewayAccessible), labels...) ch <- prometheus.MustNewConstMetric(c.dnsAccessibleDesc, prometheus.GaugeValue, utils.BoolToFloat(status.DNSAccessible), labels...) ch <- prometheus.MustNewConstMetric(c.captiveAccessibleDesc, prometheus.GaugeValue, utils.BoolToFloat(status.CaptiveAccessible), labels...) ch <- prometheus.MustNewConstMetric(c.internetDesc, prometheus.GaugeValue, utils.BoolToFloat(status.Internet), labels...) ch <- prometheus.MustNewConstMetric(c.gatewayFailuresDesc, prometheus.GaugeValue, status.Gateway.Failures, labels...) ch <- prometheus.MustNewConstMetric(c.captiveFailuresDesc, prometheus.GaugeValue, status.Captive.Failures, labels...) return nil }