diff --git a/cmd/exporter/main.go b/cmd/exporter/main.go index 052c7f8..288428c 100644 --- a/cmd/exporter/main.go +++ b/cmd/exporter/main.go @@ -1,70 +1,78 @@ package main import ( - "goprom/internal/metrics" + "goprom/internal/collector" "goprom/internal/redfish" "log" "net/http" - "time" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { - metrics.InitMetrics() - metrics.InitTemperatureMetrics() - go func() { - //time.Sleep(20 * time.Second) - ticker := time.NewTicker(60 * time.Second) - defer ticker.Stop() + // metrics.InitMetrics() + // metrics.InitTemperatureMetrics() + // go func() { + // //time.Sleep(20 * time.Second) + // ticker := time.NewTicker(60 * time.Second) + // defer ticker.Stop() - fetch := func() { - power, err := redfish.RetrievePowerValues() - if err != nil { - log.Printf("Error retrieving power values: %v", err) - metrics.UpdateHealth(false) - return - } - metrics.UpdateHealth(true) - metrics.UpdateMetrics(power) - } + // fetch := func() { + // power, err := redfish.RetrievePowerValues() + // if err != nil { + // log.Printf("Error retrieving power values: %v", err) + // metrics.UpdateHealth(false) + // return + // } + // metrics.UpdateHealth(true) + // metrics.UpdateMetrics(power) + // } - fetch2 := func() { - temperature, err := redfish.RetrieveTemperatureValues() - if err != nil { - log.Printf("Error retrieving temperature values: %v", err) - metrics.UpdateHealth(false) - return - } - metrics.UpdateTemperatureMetrics(temperature) - } + // fetch2 := func() { + // temperature, err := redfish.RetrieveTemperatureValues() + // if err != nil { + // log.Printf("Error retrieving temperature values: %v", err) + // metrics.UpdateHealth(false) + // return + // } + // metrics.UpdateTemperatureMetrics(temperature) + // } - fetch3 := func() { - client := redfish.NewClient( - redfish.BaseURL, - "root", - "calvin", - ) + // fetch3 := func() { + // client := redfish.NewClient( + // redfish.BaseURL, + // "root", + // "calvin", + // ) - temps, err := client.GetTemperatures(redfish.Temperature_Endpoints) - if err != nil { - log.Printf("Error retrieving temperature values: %v", err) - return - } + // temps, err := client.GetTemperatures(redfish.Temperature_Endpoints) + // if err != nil { + // log.Printf("Error retrieving temperature values: %v", err) + // return + // } - for _, t := range temps { - metrics.UpdateTemperatureMetrics(t) - } - } - fetch() - fetch2() - fetch3() - for range ticker.C { - fetch() - fetch2() - fetch3() - } - }() + // for _, t := range temps { + // metrics.UpdateTemperatureMetrics(t) + // } + // } + // fetch() + // fetch2() + // fetch3() + // for range ticker.C { + // fetch() + // fetch2() + // fetch3() + // } + // }() + + rfCollector := collector.NewRedfishCollector(redfish.NewClient( + redfish.BaseURL, + "root", + "calvin", + )) + + prometheus.MustRegister(rfCollector) http.Handle("/metrics", promhttp.Handler()) diff --git a/internal/collector/redfish_collector.go b/internal/collector/redfish_collector.go new file mode 100644 index 0000000..450f2b2 --- /dev/null +++ b/internal/collector/redfish_collector.go @@ -0,0 +1,251 @@ +package collector + +import ( + "goprom/internal/models" + "goprom/internal/redfish" + "net/http" + "strconv" + "time" + + "github.com/prometheus/client_golang/prometheus" +) + +type RedfishAPI interface { + Get(endpoint string, target interface{}) (int, error) +} + +type RedfishCollector struct { + client RedfishAPI + + powerConsumedDesc *prometheus.Desc + powerCapacityDesc *prometheus.Desc + + redfishUpDesc *prometheus.Desc + scrapeDurationDesc *prometheus.Desc + + readingCelsiusDesc *prometheus.Desc + lowerThresholdCriticalDesc *prometheus.Desc + lowerThresholdFatalDesc *prometheus.Desc + upperThresholdCriticalDesc *prometheus.Desc + upperThresholdFatalDesc *prometheus.Desc + lowerThresholdNonCriticalDesc *prometheus.Desc + upperThresholdNonCriticalDesc *prometheus.Desc +} + +func NewRedfishCollector(client RedfishAPI) *RedfishCollector { + return &RedfishCollector{ + client: client, + + powerConsumedDesc: prometheus.NewDesc( + "redfish_power_consumed_watts", + "Current PSU power consumption in watts", + nil, + nil, + ), + + powerCapacityDesc: prometheus.NewDesc( + "redfish_power_capacity_watts", + "PSU power capacity in watts", + nil, + nil, + ), + + redfishUpDesc: prometheus.NewDesc( + "redfish_up", + "Overall Redfish scrape status (1 = success, 0 = failure)", + nil, + nil, + ), + + scrapeDurationDesc: prometheus.NewDesc( + "redfish_scrape_duration_seconds", + "Time taken to scrape Redfish in seconds", + nil, + nil, + ), + + readingCelsiusDesc: prometheus.NewDesc( + "redfish_temperature_reading_celsius", + "Current temperature reading", + []string{"name", "physical_context", "sensor_number"}, + nil, + ), + + lowerThresholdCriticalDesc: prometheus.NewDesc( + "redfish_temperature_lower_threshold_critical", + "Lower critical temperature threshold", + []string{"name", "physical_context", "sensor_number"}, + nil, + ), + + lowerThresholdFatalDesc: prometheus.NewDesc( + "redfish_temperature_lower_threshold_fatal", + "Lower fatal temperature threshold", + []string{"name", "physical_context", "sensor_number"}, + nil, + ), + + upperThresholdCriticalDesc: prometheus.NewDesc( + "redfish_temperature_upper_threshold_critical", + "Upper critical temperature threshold", + []string{"name", "physical_context", "sensor_number"}, + nil, + ), + + upperThresholdFatalDesc: prometheus.NewDesc( + "redfish_temperature_upper_threshold_fatal", + "Upper fatal temperature threshold", + []string{"name", "physical_context", "sensor_number"}, + nil, + ), + + lowerThresholdNonCriticalDesc: prometheus.NewDesc( + "redfish_temperature_lower_threshold_non_critical", + "Lower non-critical temperature threshold", + []string{"name", "physical_context", "sensor_number"}, + nil, + ), + + upperThresholdNonCriticalDesc: prometheus.NewDesc( + "redfish_temperature_upper_threshold_non_critical", + "Upper non-critical temperature threshold", + []string{"name", "physical_context", "sensor_number"}, + nil, + ), + } +} + +func (c *RedfishCollector) Describe(ch chan<- *prometheus.Desc) { + + ch <- c.powerConsumedDesc + ch <- c.powerCapacityDesc + + ch <- c.redfishUpDesc + ch <- c.scrapeDurationDesc + + ch <- c.readingCelsiusDesc + ch <- c.lowerThresholdCriticalDesc + ch <- c.lowerThresholdFatalDesc + ch <- c.upperThresholdCriticalDesc + ch <- c.upperThresholdFatalDesc + ch <- c.lowerThresholdNonCriticalDesc + ch <- c.upperThresholdNonCriticalDesc +} + +func (c *RedfishCollector) Collect(ch chan<- prometheus.Metric) { + start := time.Now() + overallSuccess := true + + var power models.PowerControl + status, err := c.client.Get(redfish.Power_Endpoints[0], &power) + + if err != nil || (status != http.StatusOK && status != http.StatusNotFound) { + overallSuccess = false + } else if status == http.StatusOK { + ch <- prometheus.MustNewConstMetric( + c.powerConsumedDesc, + prometheus.GaugeValue, + float64(power.PowerConsumedWatts), + ) + ch <- prometheus.MustNewConstMetric( + c.powerCapacityDesc, + prometheus.GaugeValue, + float64(power.PowerCapacityWatts), + ) + } + + for _, endpoint := range redfish.Temperature_Endpoints { + var t models.Temperature + status, err := c.client.Get(endpoint, &t) + + if err != nil { + overallSuccess = false + continue + } + if status == http.StatusNotFound { + continue + } + if status != http.StatusOK { + overallSuccess = false + continue + } + + labels := []string{t.Name, t.PhysicalContext, strconv.Itoa(t.SensorNumber)} + + if t.ReadingCelsius != nil { + ch <- prometheus.MustNewConstMetric( + c.readingCelsiusDesc, + prometheus.GaugeValue, + float64(*t.ReadingCelsius), + labels..., + ) + } + + if t.LowerThresholdCritical != nil { + ch <- prometheus.MustNewConstMetric( + c.lowerThresholdCriticalDesc, + prometheus.GaugeValue, + float64(*t.LowerThresholdCritical), + labels..., + ) + } + + if t.LowerThresholdFatal != nil { + ch <- prometheus.MustNewConstMetric( + c.lowerThresholdFatalDesc, + prometheus.GaugeValue, + float64(*t.LowerThresholdFatal), + labels..., + ) + } + + if t.UpperThresholdCritical != nil { + ch <- prometheus.MustNewConstMetric( + c.upperThresholdCriticalDesc, + prometheus.GaugeValue, + float64(*t.UpperThresholdCritical), + labels..., + ) + } + + if t.UpperThresholdFatal != nil { + ch <- prometheus.MustNewConstMetric( + c.upperThresholdFatalDesc, + prometheus.GaugeValue, + float64(*t.UpperThresholdFatal), + labels..., + ) + } + + if t.LowerThresholdNonCritical != nil { + ch <- prometheus.MustNewConstMetric( + c.lowerThresholdNonCriticalDesc, + prometheus.GaugeValue, + float64(*t.LowerThresholdNonCritical), + labels..., + ) + } + + if t.UpperThresholdNonCritical != nil { + ch <- prometheus.MustNewConstMetric( + c.upperThresholdNonCriticalDesc, + prometheus.GaugeValue, + float64(*t.UpperThresholdNonCritical), + labels..., + ) + } + } + + if overallSuccess { + ch <- prometheus.MustNewConstMetric(c.redfishUpDesc, prometheus.GaugeValue, 1) + } else { + ch <- prometheus.MustNewConstMetric(c.redfishUpDesc, prometheus.GaugeValue, 0) + } + + duration := time.Since(start).Seconds() + ch <- prometheus.MustNewConstMetric( + c.scrapeDurationDesc, + prometheus.GaugeValue, + duration, + ) +}