From 195ab115258571b24ef875a8820683b0251b0866 Mon Sep 17 00:00:00 2001 From: Vincent Vilenchik Date: Thu, 5 Mar 2026 14:16:39 +0100 Subject: [PATCH 1/8] added missing metric for qfx5220 --- pkg/features/environment/collector.go | 118 +++++++++++++++++++++++++- pkg/features/environment/rpc.go | 99 +++++++++++++++++++++ 2 files changed, 213 insertions(+), 4 deletions(-) diff --git a/pkg/features/environment/collector.go b/pkg/features/environment/collector.go index 21f423e4..680dd101 100644 --- a/pkg/features/environment/collector.go +++ b/pkg/features/environment/collector.go @@ -9,8 +9,10 @@ import ( "strconv" "strings" - "github.com/czerwonk/junos_exporter/pkg/collector" + "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" + + "github.com/czerwonk/junos_exporter/pkg/collector" ) const prefix string = "junos_environment_" @@ -26,6 +28,7 @@ var ( dcCurrentDesc *prometheus.Desc dcPowerDesc *prometheus.Desc dcLoadDesc *prometheus.Desc + dcOutputDesc *prometheus.Desc ) func init() { @@ -40,6 +43,7 @@ func init() { dcCurrentDesc = prometheus.NewDesc(prefix+"pem_current", "PEM current value", l, nil) dcPowerDesc = prometheus.NewDesc(prefix+"pem_power_usage", "PEM power usage in W", l, nil) dcLoadDesc = prometheus.NewDesc(prefix+"pem_power_load_percent", "PEM power usage percent of total", l, nil) + dcOutputDesc = prometheus.NewDesc(prefix+"pem_dc_output", "PSM DC output status (1 OK, 0 not OK)", l, nil) l = []string{"target", "re_name", "item", "fan_name"} fanDesc = prometheus.NewDesc(prefix+"pem_fanspeed", "Fan speed in RPM", l, nil) @@ -63,13 +67,26 @@ func (*environmentCollector) Describe(ch chan<- *prometheus.Desc) { ch <- temperaturesDesc ch <- fanDesc ch <- dcPowerDesc + ch <- dcOutputDesc } // Collect collects metrics from JunOS func (c *environmentCollector) Collect(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - c.environmentItems(client, ch, labelValues) - c.environmentPEMItems(client, ch, labelValues) - + var v showVersionResult + err := client.RunCommandAndParse("show version", &v) + if err != nil { + //return errors.Wrap(err, "failed to run command 'show version'") + return errors.Wrap(err, "failed to run command 'show version'") + } + //QFX5220 have a slightly different xml for environment information, so we need to check the product model before collecting environment metrics + fmt.Printf("model is %s", v.SoftwareInformation.ProductModel) + if strings.Contains(strings.ToLower(v.SoftwareInformation.ProductModel), "qfx5220"){ + c.environmentItemsForSomeSwitchModels(client, ch, labelValues) + c.environmentPEMItemsForSomeSwitchModels(client, ch, labelValues) + } else { + c.environmentItems(client, ch, labelValues) + c.environmentPEMItems(client, ch, labelValues) + } return nil } @@ -134,6 +151,8 @@ func (c *environmentCollector) environmentItems(client collector.Client, ch chan return nil } + + func (c *environmentCollector) environmentPEMItems(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = multiEngineResult{} @@ -192,6 +211,97 @@ func (c *environmentCollector) environmentPEMItems(client collector.Client, ch c return nil } +func (c *environmentCollector) environmentItemsForSomeSwitchModels(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { + x := environmentResultSomeSwitches{} + + statusValues := map[string]int{ + "OK": 1, + "Testing": 2, + "Failed": 3, + "Absent": 4, + "Present": 5, + } + + err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { + return xml.Unmarshal(b, &x) + }) + if err != nil { + return nil + } + + reName := "N/A" + for _, item := range x.EnvironmentInformation.EnvironmentItem { + l := append(labelValues, reName) + + if strings.Contains(item.Name, "Power Supply") || strings.Contains(item.Name, "PEM") || strings.Contains(item.Name, "PSM") { + ch <- prometheus.MustNewConstMetric(powerSupplyDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) + } else if strings.Contains(item.Name, "Fan") { + if strings.Contains(item.Name, "Airflow") { + ch <- prometheus.MustNewConstMetric(fanAirflowDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) + } else { + ch <- prometheus.MustNewConstMetric(fanStatusDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) + } + } else if item.Temperature.Celsius != "" { + tempVal, err := strconv.ParseFloat(item.Temperature.Celsius, 64) + if err != nil { + return fmt.Errorf("could not parse temperature value to float: %s", item.Temperature.Celsius) + } + ch <- prometheus.MustNewConstMetric(temperaturesDesc, prometheus.GaugeValue, tempVal, append(l, item.Name)...) + } + } + + return nil +} + +func (c *environmentCollector) environmentPEMItemsForSomeSwitchModels(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { + x := multiEngineResultSomeSwitches{} + + stateValues := map[string]int{ + "Online": 1, + "Present": 2, + "Empty": 3, + "Offline": 4, + } + + err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { + return xml.Unmarshal(b, &x) + }) + if err != nil { + err := client.RunCommandAndParseWithParser("show chassis environment psm", func(b []byte) error { + return xml.Unmarshal(b, &x) + }) + if err != nil { + return err + } + } + + reName := "N/A" + for _, item := range x.EnvironmentComponentInformation.EnvironmentComponentItem { + l := append(labelValues, reName, item.Name) + + ch <- prometheus.MustNewConstMetric(pemDesc, prometheus.GaugeValue, float64(stateValues[item.State]), append(l, item.State)...) + + fan1Speed := item.PsmInformation.FanSpeedReadingPsm.Fan1Speed + if fan1Speed != "" { + rpms, err := strconv.ParseFloat(strings.TrimSuffix(fan1Speed, " RPM"), 64) + if err != nil { + return fmt.Errorf("could not parse fan speed value to float: %s", fan1Speed) + } + ch <- prometheus.MustNewConstMetric(fanDesc, prometheus.GaugeValue, rpms, append(l, item.PsmInformation.FanSpeedReadingPsm.Fan1Name)...) + } + + dcOutputVal := 0.0 + fmt.Printf("value of dc output is %s", item.PsmInformation.PsmStatus.DcOutput) + if strings.EqualFold(strings.ToLower(item.PsmInformation.PsmStatus.DcOutput), "ok") { + fmt.Printf("inside dc output metric") + dcOutputVal = 1.0 + } + ch <- prometheus.MustNewConstMetric(dcOutputDesc, prometheus.GaugeValue, dcOutputVal, l...) + } + + return nil +} + func parseXML(b []byte, res *multiEngineResult) error { if strings.Contains(string(b), "multi-routing-engine-results") { return xml.Unmarshal(b, res) diff --git a/pkg/features/environment/rpc.go b/pkg/features/environment/rpc.go index 745f3e2d..68270d14 100644 --- a/pkg/features/environment/rpc.go +++ b/pkg/features/environment/rpc.go @@ -59,3 +59,102 @@ type singleEngineResult struct { EnvironmentComponentInformation environmentComponentInformation `xml:"environment-component-information"` EnvironmentInformation environmentInformation `xml:"environment-information"` } + +type showVersionResult struct { + XMLName xml.Name `xml:"rpc-reply"` + Text string `xml:",chardata"` + Junos string `xml:"junos,attr"` + SoftwareInformation struct { + Text string `xml:",chardata"` + HostName string `xml:"host-name"` + ProductModel string `xml:"product-model"` + ProductName string `xml:"product-name"` + OsName string `xml:"os-name"` + JunosVersion string `xml:"junos-version"` + PackageInformation []struct { + Text string `xml:",chardata"` + Name string `xml:"name"` + PackageName string `xml:"package-name"` + Comment string `xml:"comment"` + } `xml:"package-information"` + } `xml:"software-information"` + Cli struct { + Text string `xml:",chardata"` + Banner string `xml:"banner"` + } `xml:"cli"` +} + +type environmentResultSomeSwitches struct { + XMLName xml.Name `xml:"rpc-reply"` + Text string `xml:",chardata"` + Junos string `xml:"junos,attr"` + EnvironmentInformation struct { + Text string `xml:",chardata"` + Xmlns string `xml:"xmlns,attr"` + EnvironmentItem []struct { + Text string `xml:",chardata"` + Name string `xml:"name"` + Status string `xml:"status"` + Class string `xml:"class"` + Temperature struct { + Text string `xml:",chardata"` + Celsius string `xml:"celsius,attr"` + } `xml:"temperature"` + Comment string `xml:"comment"` + } `xml:"environment-item"` + } `xml:"environment-information"` + Cli struct { + Text string `xml:",chardata"` + Banner string `xml:"banner"` + } `xml:"cli"` +} + +type multiEngineResultSomeSwitches struct { + XMLName xml.Name `xml:"rpc-reply"` + Text string `xml:",chardata"` + Junos string `xml:"junos,attr"` + EnvironmentComponentInformation struct { + Text string `xml:",chardata"` + Xmlns string `xml:"xmlns,attr"` + EnvironmentComponentItem []struct { + Text string `xml:",chardata"` + Name string `xml:"name"` + State string `xml:"state"` + PsmInformation struct { + Text string `xml:",chardata"` + TemperatureReading struct { + Text string `xml:",chardata"` + TemperatureName string `xml:"temperature-name"` + Temperature struct { + Text string `xml:",chardata"` + Celsius string `xml:"celsius,attr"` + } `xml:"temperature"` + } `xml:"temperature-reading"` + PsmStatus struct { + Text string `xml:",chardata"` + Fans string `xml:"fans"` + DcOutput string `xml:"dc-output"` + } `xml:"psm-status"` + FirmwareVersion string `xml:"firmware-version"` + FanSpeedReadingPsm struct { + Text string `xml:",chardata"` + Fan1Name string `xml:"fan1-name"` + Fan1Speed string `xml:"fan1-speed"` + Fan2Name string `xml:"fan2-name"` + } `xml:"fan-speed-reading-psm"` + } `xml:"psm-information"` + PsmHealthCheckDetail struct { + Text string `xml:",chardata"` + HealthCheckStatus string `xml:"health-check-status"` + HealthCheckStateStr string `xml:"health-check-state-str"` + HealthCheckLastResultStr string `xml:"health-check-last-result-str"` + HealthCheckLastExecStr string `xml:"health-check-last-exec-str"` + HealthCheckNextSchedRunStr string `xml:"health-check-next-sched-run-str"` + } `xml:"psm-health-check-detail"` + } `xml:"environment-component-item"` + } `xml:"environment-component-information"` + Cli struct { + Text string `xml:",chardata"` + Banner string `xml:"banner"` + } `xml:"cli"` +} From de7351043d809e69c3ea2b969ee16114c1e3322a Mon Sep 17 00:00:00 2001 From: Vincent Vilenchik Date: Thu, 5 Mar 2026 14:18:14 +0100 Subject: [PATCH 2/8] removed prints --- pkg/features/environment/collector.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/features/environment/collector.go b/pkg/features/environment/collector.go index 680dd101..e81a99ac 100644 --- a/pkg/features/environment/collector.go +++ b/pkg/features/environment/collector.go @@ -79,7 +79,6 @@ func (c *environmentCollector) Collect(client collector.Client, ch chan<- promet return errors.Wrap(err, "failed to run command 'show version'") } //QFX5220 have a slightly different xml for environment information, so we need to check the product model before collecting environment metrics - fmt.Printf("model is %s", v.SoftwareInformation.ProductModel) if strings.Contains(strings.ToLower(v.SoftwareInformation.ProductModel), "qfx5220"){ c.environmentItemsForSomeSwitchModels(client, ch, labelValues) c.environmentPEMItemsForSomeSwitchModels(client, ch, labelValues) @@ -291,9 +290,7 @@ func (c *environmentCollector) environmentPEMItemsForSomeSwitchModels(client col } dcOutputVal := 0.0 - fmt.Printf("value of dc output is %s", item.PsmInformation.PsmStatus.DcOutput) if strings.EqualFold(strings.ToLower(item.PsmInformation.PsmStatus.DcOutput), "ok") { - fmt.Printf("inside dc output metric") dcOutputVal = 1.0 } ch <- prometheus.MustNewConstMetric(dcOutputDesc, prometheus.GaugeValue, dcOutputVal, l...) From d6de273411e979d8a2080a1cae9a6d816cad1afb Mon Sep 17 00:00:00 2001 From: Vincent Vilenchik Date: Thu, 5 Mar 2026 15:06:58 +0100 Subject: [PATCH 3/8] added chassis environemnt pem metrics for ex4300 as well --- pkg/features/environment/collector.go | 107 +++++++++++++++++++++++--- pkg/features/environment/rpc.go | 66 +++++++++++++++- 2 files changed, 161 insertions(+), 12 deletions(-) diff --git a/pkg/features/environment/collector.go b/pkg/features/environment/collector.go index e81a99ac..9badfce7 100644 --- a/pkg/features/environment/collector.go +++ b/pkg/features/environment/collector.go @@ -78,13 +78,25 @@ func (c *environmentCollector) Collect(client collector.Client, ch chan<- promet //return errors.Wrap(err, "failed to run command 'show version'") return errors.Wrap(err, "failed to run command 'show version'") } - //QFX5220 have a slightly different xml for environment information, so we need to check the product model before collecting environment metrics - if strings.Contains(strings.ToLower(v.SoftwareInformation.ProductModel), "qfx5220"){ - c.environmentItemsForSomeSwitchModels(client, ch, labelValues) - c.environmentPEMItemsForSomeSwitchModels(client, ch, labelValues) + // QFX5220 and EX4300 have a slightly different xml for environment information, so we need to check the product model before collecting environment metrics + model := strings.ToLower(v.SoftwareInformation.ProductModel) + if model == "" { + // EX4300 returns chassis-inventory instead of software-information for 'show version' + var hw showChassisHardwareResult + if err := client.RunCommandAndParse("show chassis hardware", &hw); err == nil { + model = strings.ToLower(hw.ChassisInventory.Chassis.Description) + } + } + + if strings.Contains(model, "qfx5220") { + c.environmentItemsQFX5220(client, ch, labelValues) + c.environmentPEMItemsQFX5220(client, ch, labelValues) + } else if strings.Contains(model, "ex4300") { + c.environmentItemsEX4300(client, ch, labelValues) + c.environmentPEMItemsEX4300(client, ch, labelValues) } else { - c.environmentItems(client, ch, labelValues) - c.environmentPEMItems(client, ch, labelValues) + c.environmentItems(client, ch, labelValues) + c.environmentPEMItems(client, ch, labelValues) } return nil } @@ -210,8 +222,8 @@ func (c *environmentCollector) environmentPEMItems(client collector.Client, ch c return nil } -func (c *environmentCollector) environmentItemsForSomeSwitchModels(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - x := environmentResultSomeSwitches{} +func (c *environmentCollector) environmentItemsQFX5220(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { + x := environmentResultQFX5220{} statusValues := map[string]int{ "OK": 1, @@ -252,8 +264,8 @@ func (c *environmentCollector) environmentItemsForSomeSwitchModels(client collec return nil } -func (c *environmentCollector) environmentPEMItemsForSomeSwitchModels(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - x := multiEngineResultSomeSwitches{} +func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { + x := environmentPEMResultQFX5220{} stateValues := map[string]int{ "Online": 1, @@ -299,6 +311,81 @@ func (c *environmentCollector) environmentPEMItemsForSomeSwitchModels(client col return nil } +func (c *environmentCollector) environmentItemsEX4300(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { + x := environmentResultEX4300{} + + statusValues := map[string]int{ + "OK": 1, + "Testing": 2, + "Failed": 3, + "Absent": 4, + "Present": 5, + } + + err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { + return xml.Unmarshal(b, &x) + }) + if err != nil { + return nil + } + + reName := "N/A" + for _, item := range x.EnvironmentInformation.EnvironmentItem { + l := append(labelValues, reName) + + if strings.Contains(item.Name, "Power Supply") || strings.Contains(item.Name, "PEM") || strings.Contains(item.Name, "PSM") { + ch <- prometheus.MustNewConstMetric(powerSupplyDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) + } else if strings.Contains(item.Name, "Fan") { + if strings.Contains(item.Name, "Airflow") { + ch <- prometheus.MustNewConstMetric(fanAirflowDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) + } else { + ch <- prometheus.MustNewConstMetric(fanStatusDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) + } + } else if item.Temperature.Celsius != "" { + tempVal, err := strconv.ParseFloat(item.Temperature.Celsius, 64) + if err != nil { + return fmt.Errorf("could not parse temperature value to float: %s", item.Temperature.Celsius) + } + ch <- prometheus.MustNewConstMetric(temperaturesDesc, prometheus.GaugeValue, tempVal, append(l, item.Name)...) + } + } + + return nil +} + +func (c *environmentCollector) environmentPEMItemsEX4300(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { + x := environmentPEMResultEX4300{} + + stateValues := map[string]int{ + "Online": 1, + "Present": 2, + "Empty": 3, + "Offline": 4, + } + + err := client.RunCommandAndParseWithParser("show chassis environment power-supply-unit", func(b []byte) error { + return xml.Unmarshal(b, &x) + }) + if err != nil { + return err + } + + reName := "N/A" + for _, item := range x.EnvironmentComponentInformation.EnvironmentComponentItem { + pem := item.PemInformation + itemName := fmt.Sprintf("FPC %s PSU %s", pem.FpcSlot, pem.PemSlot) + l := append(labelValues, reName, itemName) + + ch <- prometheus.MustNewConstMetric(pemDesc, prometheus.GaugeValue, float64(stateValues[pem.PemState]), append(l, pem.PemState)...) + ch <- prometheus.MustNewConstMetric(temperaturesDesc, prometheus.GaugeValue, pem.PemTemperature, append(labelValues, reName, itemName)...) + ch <- prometheus.MustNewConstMetric(dcVoltageDesc, prometheus.GaugeValue, pem.OutputVolt, l...) + ch <- prometheus.MustNewConstMetric(dcCurrentDesc, prometheus.GaugeValue, pem.OutputCurrent, l...) + ch <- prometheus.MustNewConstMetric(dcPowerDesc, prometheus.GaugeValue, pem.OutputPower, l...) + } + + return nil +} + func parseXML(b []byte, res *multiEngineResult) error { if strings.Contains(string(b), "multi-routing-engine-results") { return xml.Unmarshal(b, res) diff --git a/pkg/features/environment/rpc.go b/pkg/features/environment/rpc.go index 68270d14..562b26eb 100644 --- a/pkg/features/environment/rpc.go +++ b/pkg/features/environment/rpc.go @@ -84,7 +84,7 @@ type showVersionResult struct { } `xml:"cli"` } -type environmentResultSomeSwitches struct { +type environmentResultQFX5220 struct { XMLName xml.Name `xml:"rpc-reply"` Text string `xml:",chardata"` Junos string `xml:"junos,attr"` @@ -109,7 +109,69 @@ type environmentResultSomeSwitches struct { } `xml:"cli"` } -type multiEngineResultSomeSwitches struct { +type showChassisHardwareResult struct { + XMLName xml.Name `xml:"rpc-reply"` + ChassisInventory struct { + Chassis struct { + Description string `xml:"description"` + } `xml:"chassis"` + } `xml:"chassis-inventory"` +} + +type environmentResultEX4300 struct { + XMLName xml.Name `xml:"rpc-reply"` + Text string `xml:",chardata"` + Junos string `xml:"junos,attr"` + EnvironmentInformation struct { + Text string `xml:",chardata"` + Xmlns string `xml:"xmlns,attr"` + EnvironmentItem []struct { + Text string `xml:",chardata"` + Name string `xml:"name"` + Status string `xml:"status"` + Class string `xml:"class"` + Temperature struct { + Text string `xml:",chardata"` + Celsius string `xml:"celsius,attr"` + } `xml:"temperature"` + Comment string `xml:"comment"` + } `xml:"environment-item"` + } `xml:"environment-information"` + Cli struct { + Text string `xml:",chardata"` + Banner string `xml:"banner"` + } `xml:"cli"` +} + +type environmentPEMResultEX4300 struct { + XMLName xml.Name `xml:"rpc-reply"` + Text string `xml:",chardata"` + Junos string `xml:"junos,attr"` + EnvironmentComponentInformation struct { + Text string `xml:",chardata"` + Xmlns string `xml:"xmlns,attr"` + EnvironmentComponentItem []struct { + PemInformation struct { + FpcSlot string `xml:"fpc-slot"` + PemSlot string `xml:"pem-slot"` + PemState string `xml:"pem-state"` + PemTemperature float64 `xml:"pem-temperature"` + InputCurrent float64 `xml:"input-current"` + InputVolt float64 `xml:"input-volt"` + InputPower float64 `xml:"input-power"` + OutputCurrent float64 `xml:"output-current"` + OutputVolt float64 `xml:"output-volt"` + OutputPower float64 `xml:"output-power"` + } `xml:"pem-information"` + } `xml:"environment-component-item"` + } `xml:"environment-component-information"` + Cli struct { + Text string `xml:",chardata"` + Banner string `xml:"banner"` + } `xml:"cli"` +} + +type environmentPEMResultQFX5220 struct { XMLName xml.Name `xml:"rpc-reply"` Text string `xml:",chardata"` Junos string `xml:"junos,attr"` From 9824fb74ebd8f176824d40b79aa1561b170f59b6 Mon Sep 17 00:00:00 2001 From: Vincent Vilenchik Date: Fri, 6 Mar 2026 09:24:19 +0100 Subject: [PATCH 4/8] added some more metrics for the ex4300 devices --- pkg/features/environment/collector.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pkg/features/environment/collector.go b/pkg/features/environment/collector.go index 9badfce7..fafbdde8 100644 --- a/pkg/features/environment/collector.go +++ b/pkg/features/environment/collector.go @@ -27,8 +27,11 @@ var ( dcVoltageDesc *prometheus.Desc dcCurrentDesc *prometheus.Desc dcPowerDesc *prometheus.Desc - dcLoadDesc *prometheus.Desc - dcOutputDesc *prometheus.Desc + dcLoadDesc *prometheus.Desc + dcOutputDesc *prometheus.Desc + inputVoltageDesc *prometheus.Desc + inputCurrentDesc *prometheus.Desc + inputPowerDesc *prometheus.Desc ) func init() { @@ -44,6 +47,9 @@ func init() { dcPowerDesc = prometheus.NewDesc(prefix+"pem_power_usage", "PEM power usage in W", l, nil) dcLoadDesc = prometheus.NewDesc(prefix+"pem_power_load_percent", "PEM power usage percent of total", l, nil) dcOutputDesc = prometheus.NewDesc(prefix+"pem_dc_output", "PSM DC output status (1 OK, 0 not OK)", l, nil) + inputVoltageDesc = prometheus.NewDesc(prefix+"pem_input_voltage", "PSU input voltage in V", l, nil) + inputCurrentDesc = prometheus.NewDesc(prefix+"pem_input_current", "PSU input current in A", l, nil) + inputPowerDesc = prometheus.NewDesc(prefix+"pem_input_power", "PSU input power in W", l, nil) l = []string{"target", "re_name", "item", "fan_name"} fanDesc = prometheus.NewDesc(prefix+"pem_fanspeed", "Fan speed in RPM", l, nil) @@ -68,6 +74,9 @@ func (*environmentCollector) Describe(ch chan<- *prometheus.Desc) { ch <- fanDesc ch <- dcPowerDesc ch <- dcOutputDesc + ch <- inputVoltageDesc + ch <- inputCurrentDesc + ch <- inputPowerDesc } // Collect collects metrics from JunOS @@ -75,7 +84,6 @@ func (c *environmentCollector) Collect(client collector.Client, ch chan<- promet var v showVersionResult err := client.RunCommandAndParse("show version", &v) if err != nil { - //return errors.Wrap(err, "failed to run command 'show version'") return errors.Wrap(err, "failed to run command 'show version'") } // QFX5220 and EX4300 have a slightly different xml for environment information, so we need to check the product model before collecting environment metrics @@ -381,6 +389,9 @@ func (c *environmentCollector) environmentPEMItemsEX4300(client collector.Client ch <- prometheus.MustNewConstMetric(dcVoltageDesc, prometheus.GaugeValue, pem.OutputVolt, l...) ch <- prometheus.MustNewConstMetric(dcCurrentDesc, prometheus.GaugeValue, pem.OutputCurrent, l...) ch <- prometheus.MustNewConstMetric(dcPowerDesc, prometheus.GaugeValue, pem.OutputPower, l...) + ch <- prometheus.MustNewConstMetric(inputVoltageDesc, prometheus.GaugeValue, pem.InputVolt, l...) + ch <- prometheus.MustNewConstMetric(inputCurrentDesc, prometheus.GaugeValue, pem.InputCurrent, l...) + ch <- prometheus.MustNewConstMetric(inputPowerDesc, prometheus.GaugeValue, pem.InputPower, l...) } return nil From 150ab288060f40685b6684fcbef8f72e6a4572bd Mon Sep 17 00:00:00 2001 From: Vincent Vilenchik Date: Fri, 6 Mar 2026 09:38:27 +0100 Subject: [PATCH 5/8] added some comments --- pkg/features/environment/collector.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/features/environment/collector.go b/pkg/features/environment/collector.go index fafbdde8..918d257f 100644 --- a/pkg/features/environment/collector.go +++ b/pkg/features/environment/collector.go @@ -309,6 +309,8 @@ func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Clien ch <- prometheus.MustNewConstMetric(fanDesc, prometheus.GaugeValue, rpms, append(l, item.PsmInformation.FanSpeedReadingPsm.Fan1Name)...) } + //it could be that the DCOutputValue has the same states as stateValues from above + //but I couldn't verify it for sure dcOutputVal := 0.0 if strings.EqualFold(strings.ToLower(item.PsmInformation.PsmStatus.DcOutput), "ok") { dcOutputVal = 1.0 From 01e5d8174b43cb7c6570d5b47e3df3a2c0c367ef Mon Sep 17 00:00:00 2001 From: Vincent Vilenchik Date: Fri, 6 Mar 2026 13:50:51 +0100 Subject: [PATCH 6/8] fixed comments from MR --- pkg/features/environment/collector.go | 105 ++++++++++++++------------ pkg/features/environment/rpc.go | 8 +- 2 files changed, 59 insertions(+), 54 deletions(-) diff --git a/pkg/features/environment/collector.go b/pkg/features/environment/collector.go index 918d257f..864d99d9 100644 --- a/pkg/features/environment/collector.go +++ b/pkg/features/environment/collector.go @@ -18,6 +18,14 @@ import ( const prefix string = "junos_environment_" var ( + statusValues = map[string]int{ + "OK": 1, + "Testing": 2, + "Failed": 3, + "Absent": 4, + "Present": 5, + } + temperaturesDesc *prometheus.Desc powerSupplyDesc *prometheus.Desc fanStatusDesc *prometheus.Desc @@ -27,11 +35,11 @@ var ( dcVoltageDesc *prometheus.Desc dcCurrentDesc *prometheus.Desc dcPowerDesc *prometheus.Desc - dcLoadDesc *prometheus.Desc - dcOutputDesc *prometheus.Desc - inputVoltageDesc *prometheus.Desc - inputCurrentDesc *prometheus.Desc - inputPowerDesc *prometheus.Desc + dcLoadDesc *prometheus.Desc + dcOutputDesc *prometheus.Desc + inputVoltageDesc *prometheus.Desc + inputCurrentDesc *prometheus.Desc + inputPowerDesc *prometheus.Desc ) func init() { @@ -79,21 +87,33 @@ func (*environmentCollector) Describe(ch chan<- *prometheus.Desc) { ch <- inputPowerDesc } +// deviceModel returns the lowercase product model string for the device. +// Most devices expose it via 'show version', but some (e.g. EX4300) return +// chassis-inventory instead, so we fall back to 'show chassis hardware'. +func (c *environmentCollector) deviceModel(client collector.Client) (string, error) { + var v showVersionResult + if err := client.RunCommandAndParse("show version", &v); err != nil { + return "", errors.Wrap(err, "failed to run command 'show version'") + } + + if model := strings.ToLower(v.SoftwareInformation.ProductModel); model != "" { + return model, nil + } + + // Fallback: EX4300 returns chassis-inventory instead of software-information + var hw showChassisHardwareResult + if err := client.RunCommandAndParse("show chassis hardware", &hw); err == nil { + return strings.ToLower(hw.ChassisInventory.Chassis.Description), nil + } + + return "", nil +} + // Collect collects metrics from JunOS func (c *environmentCollector) Collect(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - var v showVersionResult - err := client.RunCommandAndParse("show version", &v) + model, err := c.deviceModel(client) if err != nil { - return errors.Wrap(err, "failed to run command 'show version'") - } - // QFX5220 and EX4300 have a slightly different xml for environment information, so we need to check the product model before collecting environment metrics - model := strings.ToLower(v.SoftwareInformation.ProductModel) - if model == "" { - // EX4300 returns chassis-inventory instead of software-information for 'show version' - var hw showChassisHardwareResult - if err := client.RunCommandAndParse("show chassis hardware", &hw); err == nil { - model = strings.ToLower(hw.ChassisInventory.Chassis.Description) - } + return err } if strings.Contains(model, "qfx5220") { @@ -112,13 +132,6 @@ func (c *environmentCollector) Collect(client collector.Client, ch chan<- promet func (c *environmentCollector) environmentItems(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { x := multiEngineResult{} - statusValues := map[string]int{ - "OK": 1, - "Testing": 2, - "Failed": 3, - "Absent": 4, - "Present": 5, - } err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { return parseXML(b, &x) @@ -150,7 +163,7 @@ func (c *environmentCollector) environmentItems(client collector.Client, ch chan l := labelValues for _, item := range re.EnvironmentInformation.Items { l = append(labelValues, re.Name) - if strings.Contains(item.Name, "Power Supply") || strings.Contains(item.Name, "PEM") || strings.Contains(item.Name, "PSM") { + if containsAny(item.Name, []string{"Power Supply", "PEM", "PSM"}) { l = append(l, item.Name, item.Status) ch <- prometheus.MustNewConstMetric(powerSupplyDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), l...) } else if strings.Contains(item.Name, "Fan") { @@ -170,8 +183,6 @@ func (c *environmentCollector) environmentItems(client collector.Client, ch chan return nil } - - func (c *environmentCollector) environmentPEMItems(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = multiEngineResult{} @@ -231,15 +242,8 @@ func (c *environmentCollector) environmentPEMItems(client collector.Client, ch c } func (c *environmentCollector) environmentItemsQFX5220(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - x := environmentResultQFX5220{} + x := environmentResultModelQFX5220{} - statusValues := map[string]int{ - "OK": 1, - "Testing": 2, - "Failed": 3, - "Absent": 4, - "Present": 5, - } err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { return xml.Unmarshal(b, &x) @@ -251,8 +255,7 @@ func (c *environmentCollector) environmentItemsQFX5220(client collector.Client, reName := "N/A" for _, item := range x.EnvironmentInformation.EnvironmentItem { l := append(labelValues, reName) - - if strings.Contains(item.Name, "Power Supply") || strings.Contains(item.Name, "PEM") || strings.Contains(item.Name, "PSM") { + if containsAny(item.Name, []string{"Power Supply", "PEM", "PSM"}) { ch <- prometheus.MustNewConstMetric(powerSupplyDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) } else if strings.Contains(item.Name, "Fan") { if strings.Contains(item.Name, "Airflow") { @@ -273,7 +276,7 @@ func (c *environmentCollector) environmentItemsQFX5220(client collector.Client, } func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - x := environmentPEMResultQFX5220{} + x := environmentPEMResultModelQFX5220{} stateValues := map[string]int{ "Online": 1, @@ -309,8 +312,8 @@ func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Clien ch <- prometheus.MustNewConstMetric(fanDesc, prometheus.GaugeValue, rpms, append(l, item.PsmInformation.FanSpeedReadingPsm.Fan1Name)...) } - //it could be that the DCOutputValue has the same states as stateValues from above - //but I couldn't verify it for sure + //it could be that the DCOutputValue has the same states as stateValues from above + //but I couldn't verify it for sure dcOutputVal := 0.0 if strings.EqualFold(strings.ToLower(item.PsmInformation.PsmStatus.DcOutput), "ok") { dcOutputVal = 1.0 @@ -322,15 +325,8 @@ func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Clien } func (c *environmentCollector) environmentItemsEX4300(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - x := environmentResultEX4300{} + x := environmentResultModelEX4300{} - statusValues := map[string]int{ - "OK": 1, - "Testing": 2, - "Failed": 3, - "Absent": 4, - "Present": 5, - } err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { return xml.Unmarshal(b, &x) @@ -343,7 +339,7 @@ func (c *environmentCollector) environmentItemsEX4300(client collector.Client, c for _, item := range x.EnvironmentInformation.EnvironmentItem { l := append(labelValues, reName) - if strings.Contains(item.Name, "Power Supply") || strings.Contains(item.Name, "PEM") || strings.Contains(item.Name, "PSM") { + if containsAny(item.Name, []string{"Power Supply", "PEM", "PSM"}) { ch <- prometheus.MustNewConstMetric(powerSupplyDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) } else if strings.Contains(item.Name, "Fan") { if strings.Contains(item.Name, "Airflow") { @@ -364,7 +360,7 @@ func (c *environmentCollector) environmentItemsEX4300(client collector.Client, c } func (c *environmentCollector) environmentPEMItemsEX4300(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - x := environmentPEMResultEX4300{} + x := environmentPEMResultModelEX4300{} stateValues := map[string]int{ "Online": 1, @@ -399,6 +395,15 @@ func (c *environmentCollector) environmentPEMItemsEX4300(client collector.Client return nil } +func containsAny(s string, items []string) bool { + for _, item := range items { + if strings.Contains(s, item) { + return true + } + } + return false +} + func parseXML(b []byte, res *multiEngineResult) error { if strings.Contains(string(b), "multi-routing-engine-results") { return xml.Unmarshal(b, res) diff --git a/pkg/features/environment/rpc.go b/pkg/features/environment/rpc.go index 562b26eb..d078b63d 100644 --- a/pkg/features/environment/rpc.go +++ b/pkg/features/environment/rpc.go @@ -84,7 +84,7 @@ type showVersionResult struct { } `xml:"cli"` } -type environmentResultQFX5220 struct { +type environmentResultModelQFX5220 struct { XMLName xml.Name `xml:"rpc-reply"` Text string `xml:",chardata"` Junos string `xml:"junos,attr"` @@ -118,7 +118,7 @@ type showChassisHardwareResult struct { } `xml:"chassis-inventory"` } -type environmentResultEX4300 struct { +type environmentResultModelEX4300 struct { XMLName xml.Name `xml:"rpc-reply"` Text string `xml:",chardata"` Junos string `xml:"junos,attr"` @@ -143,7 +143,7 @@ type environmentResultEX4300 struct { } `xml:"cli"` } -type environmentPEMResultEX4300 struct { +type environmentPEMResultModelEX4300 struct { XMLName xml.Name `xml:"rpc-reply"` Text string `xml:",chardata"` Junos string `xml:"junos,attr"` @@ -171,7 +171,7 @@ type environmentPEMResultEX4300 struct { } `xml:"cli"` } -type environmentPEMResultQFX5220 struct { +type environmentPEMResultModelQFX5220 struct { XMLName xml.Name `xml:"rpc-reply"` Text string `xml:",chardata"` Junos string `xml:"junos,attr"` From b4bd3bbb7999ba510ecea07ee0a0be4ea229eef3 Mon Sep 17 00:00:00 2001 From: Vincent Vilenchik Date: Fri, 6 Mar 2026 14:04:24 +0100 Subject: [PATCH 7/8] fixed comments from MR --- pkg/features/environment/collector.go | 72 +-------------------------- pkg/features/environment/rpc.go | 48 ------------------ 2 files changed, 2 insertions(+), 118 deletions(-) diff --git a/pkg/features/environment/collector.go b/pkg/features/environment/collector.go index 864d99d9..9d88b2b9 100644 --- a/pkg/features/environment/collector.go +++ b/pkg/features/environment/collector.go @@ -116,14 +116,13 @@ func (c *environmentCollector) Collect(client collector.Client, ch chan<- promet return err } + c.environmentItems(client, ch, labelValues) + if strings.Contains(model, "qfx5220") { - c.environmentItemsQFX5220(client, ch, labelValues) c.environmentPEMItemsQFX5220(client, ch, labelValues) } else if strings.Contains(model, "ex4300") { - c.environmentItemsEX4300(client, ch, labelValues) c.environmentPEMItemsEX4300(client, ch, labelValues) } else { - c.environmentItems(client, ch, labelValues) c.environmentPEMItems(client, ch, labelValues) } return nil @@ -241,39 +240,6 @@ func (c *environmentCollector) environmentPEMItems(client collector.Client, ch c return nil } -func (c *environmentCollector) environmentItemsQFX5220(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - x := environmentResultModelQFX5220{} - - - err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { - return xml.Unmarshal(b, &x) - }) - if err != nil { - return nil - } - - reName := "N/A" - for _, item := range x.EnvironmentInformation.EnvironmentItem { - l := append(labelValues, reName) - if containsAny(item.Name, []string{"Power Supply", "PEM", "PSM"}) { - ch <- prometheus.MustNewConstMetric(powerSupplyDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) - } else if strings.Contains(item.Name, "Fan") { - if strings.Contains(item.Name, "Airflow") { - ch <- prometheus.MustNewConstMetric(fanAirflowDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) - } else { - ch <- prometheus.MustNewConstMetric(fanStatusDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) - } - } else if item.Temperature.Celsius != "" { - tempVal, err := strconv.ParseFloat(item.Temperature.Celsius, 64) - if err != nil { - return fmt.Errorf("could not parse temperature value to float: %s", item.Temperature.Celsius) - } - ch <- prometheus.MustNewConstMetric(temperaturesDesc, prometheus.GaugeValue, tempVal, append(l, item.Name)...) - } - } - - return nil -} func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { x := environmentPEMResultModelQFX5220{} @@ -324,40 +290,6 @@ func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Clien return nil } -func (c *environmentCollector) environmentItemsEX4300(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - x := environmentResultModelEX4300{} - - - err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { - return xml.Unmarshal(b, &x) - }) - if err != nil { - return nil - } - - reName := "N/A" - for _, item := range x.EnvironmentInformation.EnvironmentItem { - l := append(labelValues, reName) - - if containsAny(item.Name, []string{"Power Supply", "PEM", "PSM"}) { - ch <- prometheus.MustNewConstMetric(powerSupplyDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) - } else if strings.Contains(item.Name, "Fan") { - if strings.Contains(item.Name, "Airflow") { - ch <- prometheus.MustNewConstMetric(fanAirflowDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) - } else { - ch <- prometheus.MustNewConstMetric(fanStatusDesc, prometheus.GaugeValue, float64(statusValues[item.Status]), append(l, item.Name, item.Status)...) - } - } else if item.Temperature.Celsius != "" { - tempVal, err := strconv.ParseFloat(item.Temperature.Celsius, 64) - if err != nil { - return fmt.Errorf("could not parse temperature value to float: %s", item.Temperature.Celsius) - } - ch <- prometheus.MustNewConstMetric(temperaturesDesc, prometheus.GaugeValue, tempVal, append(l, item.Name)...) - } - } - - return nil -} func (c *environmentCollector) environmentPEMItemsEX4300(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { x := environmentPEMResultModelEX4300{} diff --git a/pkg/features/environment/rpc.go b/pkg/features/environment/rpc.go index d078b63d..c2332c1b 100644 --- a/pkg/features/environment/rpc.go +++ b/pkg/features/environment/rpc.go @@ -84,30 +84,6 @@ type showVersionResult struct { } `xml:"cli"` } -type environmentResultModelQFX5220 struct { - XMLName xml.Name `xml:"rpc-reply"` - Text string `xml:",chardata"` - Junos string `xml:"junos,attr"` - EnvironmentInformation struct { - Text string `xml:",chardata"` - Xmlns string `xml:"xmlns,attr"` - EnvironmentItem []struct { - Text string `xml:",chardata"` - Name string `xml:"name"` - Status string `xml:"status"` - Class string `xml:"class"` - Temperature struct { - Text string `xml:",chardata"` - Celsius string `xml:"celsius,attr"` - } `xml:"temperature"` - Comment string `xml:"comment"` - } `xml:"environment-item"` - } `xml:"environment-information"` - Cli struct { - Text string `xml:",chardata"` - Banner string `xml:"banner"` - } `xml:"cli"` -} type showChassisHardwareResult struct { XMLName xml.Name `xml:"rpc-reply"` @@ -118,30 +94,6 @@ type showChassisHardwareResult struct { } `xml:"chassis-inventory"` } -type environmentResultModelEX4300 struct { - XMLName xml.Name `xml:"rpc-reply"` - Text string `xml:",chardata"` - Junos string `xml:"junos,attr"` - EnvironmentInformation struct { - Text string `xml:",chardata"` - Xmlns string `xml:"xmlns,attr"` - EnvironmentItem []struct { - Text string `xml:",chardata"` - Name string `xml:"name"` - Status string `xml:"status"` - Class string `xml:"class"` - Temperature struct { - Text string `xml:",chardata"` - Celsius string `xml:"celsius,attr"` - } `xml:"temperature"` - Comment string `xml:"comment"` - } `xml:"environment-item"` - } `xml:"environment-information"` - Cli struct { - Text string `xml:",chardata"` - Banner string `xml:"banner"` - } `xml:"cli"` -} type environmentPEMResultModelEX4300 struct { XMLName xml.Name `xml:"rpc-reply"` From 4496ed8f90b23219878fc8d4b2bdf8312fe68836 Mon Sep 17 00:00:00 2001 From: Vincent Vilenchik Date: Mon, 9 Mar 2026 10:13:42 +0100 Subject: [PATCH 8/8] merged status and state maps into one --- pkg/features/environment/collector.go | 43 +++++++-------------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/pkg/features/environment/collector.go b/pkg/features/environment/collector.go index 9d88b2b9..a4bda240 100644 --- a/pkg/features/environment/collector.go +++ b/pkg/features/environment/collector.go @@ -24,6 +24,9 @@ var ( "Failed": 3, "Absent": 4, "Present": 5, + "Online": 1, + "Empty": 4, + "Offline": 3, } temperaturesDesc *prometheus.Desc @@ -45,11 +48,11 @@ var ( func init() { l := []string{"target", "re_name", "item"} temperaturesDesc = prometheus.NewDesc(prefix+"item_temp", "Temperature of the air flowing past", l, nil) - powerSupplyDesc = prometheus.NewDesc(prefix+"power_up", "Status of power supplies (1 OK, 2 Testing, 3 Failed, 4 Absent, 5 Present)", append(l, "status"), nil) - fanStatusDesc = prometheus.NewDesc(prefix+"fan_up", "Status of fans (1 OK, 2 Testing, 3 Failed, 4 Absent, 5 Present)", append(l, "status"), nil) - fanAirflowDesc = prometheus.NewDesc(prefix+"fan_airflow_up", "Status of fan airflows (1 OK, 2 Testing, 3 Failed, 4 Absent, 5 Present)", append(l, "status"), nil) + powerSupplyDesc = prometheus.NewDesc(prefix+"power_up", "Status of power supplies (1 OK/Online, 2 Testing, 3 Failed/Offline, 4 Absent/Empty, 5 Present)", append(l, "status"), nil) + fanStatusDesc = prometheus.NewDesc(prefix+"fan_up", "Status of fans (1 OK/Online, 2 Testing, 3 Failed/Offline, 4 Absent/Empty, 5 Present)", append(l, "status"), nil) + fanAirflowDesc = prometheus.NewDesc(prefix+"fan_airflow_up", "Status of fan airflows (1 OK/Online, 2 Testing, 3 Failed/Offline, 4 Absent/Empty, 5 Present)", append(l, "status"), nil) - pemDesc = prometheus.NewDesc(prefix+"pem_state", "State of PEM module. 1 - Online, 2 - Present, 3 - Empty", append(l, "state"), nil) + pemDesc = prometheus.NewDesc(prefix+"pem_state", "State of PEM module (1 OK/Online, 2 Testing, 3 Failed/Offline, 4 Absent/Empty, 5 Present)", append(l, "state"), nil) dcVoltageDesc = prometheus.NewDesc(prefix+"pem_voltage", "PEM voltage value", l, nil) dcCurrentDesc = prometheus.NewDesc(prefix+"pem_current", "PEM current value", l, nil) dcPowerDesc = prometheus.NewDesc(prefix+"pem_power_usage", "PEM power usage in W", l, nil) @@ -131,7 +134,6 @@ func (c *environmentCollector) Collect(client collector.Client, ch chan<- promet func (c *environmentCollector) environmentItems(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { x := multiEngineResult{} - err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { return parseXML(b, &x) }) @@ -185,13 +187,6 @@ func (c *environmentCollector) environmentItems(client collector.Client, ch chan func (c *environmentCollector) environmentPEMItems(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = multiEngineResult{} - stateValues := map[string]int{ - "Online": 1, - "Present": 2, - "Empty": 3, - "Offline": 4, - } - err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { return parseXML(b, &x) }) @@ -208,7 +203,7 @@ func (c *environmentCollector) environmentPEMItems(client collector.Client, ch c for _, e := range re.EnvironmentComponentInformation.EnvironmentComponentItem { l := append(labelValues, re.Name, e.Name) - ch <- prometheus.MustNewConstMetric(pemDesc, prometheus.GaugeValue, float64(stateValues[e.State]), append(l, e.State)...) + ch <- prometheus.MustNewConstMetric(pemDesc, prometheus.GaugeValue, float64(statusValues[e.State]), append(l, e.State)...) for _, f := range e.FanSpeedReading { rpms, err := strconv.ParseFloat(strings.TrimSuffix(f.FanSpeed, " RPM"), 64) @@ -240,17 +235,9 @@ func (c *environmentCollector) environmentPEMItems(client collector.Client, ch c return nil } - func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { x := environmentPEMResultModelQFX5220{} - stateValues := map[string]int{ - "Online": 1, - "Present": 2, - "Empty": 3, - "Offline": 4, - } - err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { return xml.Unmarshal(b, &x) }) @@ -267,7 +254,7 @@ func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Clien for _, item := range x.EnvironmentComponentInformation.EnvironmentComponentItem { l := append(labelValues, reName, item.Name) - ch <- prometheus.MustNewConstMetric(pemDesc, prometheus.GaugeValue, float64(stateValues[item.State]), append(l, item.State)...) + ch <- prometheus.MustNewConstMetric(pemDesc, prometheus.GaugeValue, float64(statusValues[item.State]), append(l, item.State)...) fan1Speed := item.PsmInformation.FanSpeedReadingPsm.Fan1Speed if fan1Speed != "" { @@ -278,7 +265,7 @@ func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Clien ch <- prometheus.MustNewConstMetric(fanDesc, prometheus.GaugeValue, rpms, append(l, item.PsmInformation.FanSpeedReadingPsm.Fan1Name)...) } - //it could be that the DCOutputValue has the same states as stateValues from above + //it could be that the DCOutputValue has the same states as statusValues from above //but I couldn't verify it for sure dcOutputVal := 0.0 if strings.EqualFold(strings.ToLower(item.PsmInformation.PsmStatus.DcOutput), "ok") { @@ -290,17 +277,9 @@ func (c *environmentCollector) environmentPEMItemsQFX5220(client collector.Clien return nil } - func (c *environmentCollector) environmentPEMItemsEX4300(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { x := environmentPEMResultModelEX4300{} - stateValues := map[string]int{ - "Online": 1, - "Present": 2, - "Empty": 3, - "Offline": 4, - } - err := client.RunCommandAndParseWithParser("show chassis environment power-supply-unit", func(b []byte) error { return xml.Unmarshal(b, &x) }) @@ -314,7 +293,7 @@ func (c *environmentCollector) environmentPEMItemsEX4300(client collector.Client itemName := fmt.Sprintf("FPC %s PSU %s", pem.FpcSlot, pem.PemSlot) l := append(labelValues, reName, itemName) - ch <- prometheus.MustNewConstMetric(pemDesc, prometheus.GaugeValue, float64(stateValues[pem.PemState]), append(l, pem.PemState)...) + ch <- prometheus.MustNewConstMetric(pemDesc, prometheus.GaugeValue, float64(statusValues[pem.PemState]), append(l, pem.PemState)...) ch <- prometheus.MustNewConstMetric(temperaturesDesc, prometheus.GaugeValue, pem.PemTemperature, append(labelValues, reName, itemName)...) ch <- prometheus.MustNewConstMetric(dcVoltageDesc, prometheus.GaugeValue, pem.OutputVolt, l...) ch <- prometheus.MustNewConstMetric(dcCurrentDesc, prometheus.GaugeValue, pem.OutputCurrent, l...)