// SPDX-License-Identifier: GPL-3.0-or-later package upsd import ( "fmt" "strings" "github.com/netdata/netdata/go/go.d.plugin/agent/module" ) const ( prioUpsLoad = module.Priority + iota prioUpsLoadUsage prioUpsStatus prioUpsTemperature prioBatteryCharge prioBatteryEstimatedRuntime prioBatteryVoltage prioBatteryVoltageNominal prioInputVoltage prioInputVoltageNominal prioInputCurrent prioInputCurrentNominal prioInputFrequency prioInputFrequencyNominal prioOutputVoltage prioOutputVoltageNominal prioOutputCurrent prioOutputCurrentNominal prioOutputFrequency prioOutputFrequencyNominal ) var upsChartsTmpl = module.Charts{ upsLoadChartTmpl.Copy(), upsLoadUsageChartTmpl.Copy(), upsStatusChartTmpl.Copy(), upsTemperatureChartTmpl.Copy(), upsBatteryChargePercentChartTmpl.Copy(), upsBatteryEstimatedRuntimeChartTmpl.Copy(), upsBatteryVoltageChartTmpl.Copy(), upsBatteryVoltageNominalChartTmpl.Copy(), upsInputVoltageChartTmpl.Copy(), upsInputVoltageNominalChartTmpl.Copy(), upsInputCurrentChartTmpl.Copy(), upsInputCurrentNominalChartTmpl.Copy(), upsInputFrequencyChartTmpl.Copy(), upsInputFrequencyNominalChartTmpl.Copy(), upsOutputVoltageChartTmpl.Copy(), upsOutputVoltageNominalChartTmpl.Copy(), upsOutputCurrentChartTmpl.Copy(), upsOutputCurrentNominalChartTmpl.Copy(), upsOutputFrequencyChartTmpl.Copy(), upsOutputFrequencyNominalChartTmpl.Copy(), } var ( upsLoadChartTmpl = module.Chart{ IDSep: true, ID: "%s.load_percentage", Title: "UPS load", Units: "percentage", Fam: "ups", Ctx: "upsd.ups_load", Priority: prioUpsLoad, Type: module.Area, Dims: module.Dims{ {ID: "ups_%s_ups.load", Name: "load", Div: varPrecision}, }, } upsLoadUsageChartTmpl = module.Chart{ IDSep: true, ID: "%s.load_usage", Title: "UPS load usage (power output)", Units: "Watts", Fam: "ups", Ctx: "upsd.ups_load_usage", Priority: prioUpsLoadUsage, Dims: module.Dims{ {ID: "ups_%s_ups.load.usage", Name: "load_usage", Div: varPrecision}, }, } upsStatusChartTmpl = module.Chart{ IDSep: true, ID: "%s.status", Title: "UPS status", Units: "status", Fam: "ups", Ctx: "upsd.ups_status", Priority: prioUpsStatus, Dims: module.Dims{ {ID: "ups_%s_ups.status.OL", Name: "on_line"}, {ID: "ups_%s_ups.status.OB", Name: "on_battery"}, {ID: "ups_%s_ups.status.LB", Name: "low_battery"}, {ID: "ups_%s_ups.status.HB", Name: "high_battery"}, {ID: "ups_%s_ups.status.RB", Name: "replace_battery"}, {ID: "ups_%s_ups.status.CHRG", Name: "charging"}, {ID: "ups_%s_ups.status.DISCHRG", Name: "discharging"}, {ID: "ups_%s_ups.status.BYPASS", Name: "bypass"}, {ID: "ups_%s_ups.status.CAL", Name: "calibration"}, {ID: "ups_%s_ups.status.OFF", Name: "offline"}, {ID: "ups_%s_ups.status.OVER", Name: "overloaded"}, {ID: "ups_%s_ups.status.TRIM", Name: "trim_input_voltage"}, {ID: "ups_%s_ups.status.BOOST", Name: "boost_input_voltage"}, {ID: "ups_%s_ups.status.FSD", Name: "forced_shutdown"}, {ID: "ups_%s_ups.status.other", Name: "other"}, }, } upsTemperatureChartTmpl = module.Chart{ IDSep: true, ID: "%s.temperature", Title: "UPS temperature", Units: "Celsius", Fam: "ups", Ctx: "upsd.ups_temperature", Priority: prioUpsTemperature, Dims: module.Dims{ {ID: "ups_%s_ups.temperature", Name: "temperature", Div: varPrecision}, }, } ) var ( upsBatteryChargePercentChartTmpl = module.Chart{ IDSep: true, ID: "%s.battery_charge_percentage", Title: "UPS Battery charge", Units: "percentage", Fam: "battery", Ctx: "upsd.ups_battery_charge", Priority: prioBatteryCharge, Type: module.Area, Dims: module.Dims{ {ID: "ups_%s_battery.charge", Name: "charge", Div: varPrecision}, }, } upsBatteryEstimatedRuntimeChartTmpl = module.Chart{ IDSep: true, ID: "%s.battery_estimated_runtime", Title: "UPS Battery estimated runtime", Units: "seconds", Fam: "battery", Ctx: "upsd.ups_battery_estimated_runtime", Priority: prioBatteryEstimatedRuntime, Dims: module.Dims{ {ID: "ups_%s_battery.runtime", Name: "runtime", Div: varPrecision}, }, } upsBatteryVoltageChartTmpl = module.Chart{ IDSep: true, ID: "%s.battery_voltage", Title: "UPS Battery voltage", Units: "Volts", Fam: "battery", Ctx: "upsd.ups_battery_voltage", Priority: prioBatteryVoltage, Dims: module.Dims{ {ID: "ups_%s_battery.voltage", Name: "voltage", Div: varPrecision}, }, } upsBatteryVoltageNominalChartTmpl = module.Chart{ IDSep: true, ID: "%s.battery_voltage_nominal", Title: "UPS Battery voltage nominal", Units: "Volts", Fam: "battery", Ctx: "upsd.ups_battery_voltage_nominal", Priority: prioBatteryVoltageNominal, Dims: module.Dims{ {ID: "ups_%s_battery.voltage.nominal", Name: "nominal_voltage", Div: varPrecision}, }, } ) var ( upsInputVoltageChartTmpl = module.Chart{ IDSep: true, ID: "%s.input_voltage", Title: "UPS Input voltage", Units: "Volts", Fam: "input", Ctx: "upsd.ups_input_voltage", Priority: prioInputVoltage, Dims: module.Dims{ {ID: "ups_%s_input.voltage", Name: "voltage", Div: varPrecision}, }, } upsInputVoltageNominalChartTmpl = module.Chart{ IDSep: true, ID: "%s.input_voltage_nominal", Title: "UPS Input voltage nominal", Units: "Volts", Fam: "input", Ctx: "upsd.ups_input_voltage_nominal", Priority: prioInputVoltageNominal, Dims: module.Dims{ {ID: "ups_%s_input.voltage.nominal", Name: "nominal_voltage", Div: varPrecision}, }, } upsInputCurrentChartTmpl = module.Chart{ IDSep: true, ID: "%s.input_current", Title: "UPS Input current", Units: "Ampere", Fam: "input", Ctx: "upsd.ups_input_current", Priority: prioInputCurrent, Dims: module.Dims{ {ID: "ups_%s_input.current", Name: "current", Div: varPrecision}, }, } upsInputCurrentNominalChartTmpl = module.Chart{ IDSep: true, ID: "%s.input_current_nominal", Title: "UPS Input current nominal", Units: "Ampere", Fam: "input", Ctx: "upsd.ups_input_current_nominal", Priority: prioInputCurrentNominal, Dims: module.Dims{ {ID: "ups_%s_input.current.nominal", Name: "nominal_current", Div: varPrecision}, }, } upsInputFrequencyChartTmpl = module.Chart{ IDSep: true, ID: "%s.input_frequency", Title: "UPS Input frequency", Units: "Hz", Fam: "input", Ctx: "upsd.ups_input_frequency", Priority: prioInputFrequency, Dims: module.Dims{ {ID: "ups_%s_input.frequency", Name: "frequency", Div: varPrecision}, }, } upsInputFrequencyNominalChartTmpl = module.Chart{ IDSep: true, ID: "%s.input_frequency_nominal", Title: "UPS Input frequency nominal", Units: "Hz", Fam: "input", Ctx: "upsd.ups_input_frequency_nominal", Priority: prioInputFrequencyNominal, Dims: module.Dims{ {ID: "ups_%s_input.frequency.nominal", Name: "nominal_frequency", Div: varPrecision}, }, } ) var ( upsOutputVoltageChartTmpl = module.Chart{ IDSep: true, ID: "%s.output_voltage", Title: "UPS Output voltage", Units: "Volts", Fam: "output", Ctx: "upsd.ups_output_voltage", Priority: prioOutputVoltage, Dims: module.Dims{ {ID: "ups_%s_output.voltage", Name: "voltage", Div: varPrecision}, }, } upsOutputVoltageNominalChartTmpl = module.Chart{ IDSep: true, ID: "%s.output_voltage_nominal", Title: "UPS Output voltage nominal", Units: "Volts", Fam: "output", Ctx: "upsd.ups_output_voltage_nominal", Priority: prioOutputVoltageNominal, Dims: module.Dims{ {ID: "ups_%s_output.voltage.nominal", Name: "nominal_voltage", Div: varPrecision}, }, } upsOutputCurrentChartTmpl = module.Chart{ IDSep: true, ID: "%s.output_current", Title: "UPS Output current", Units: "Ampere", Fam: "output", Ctx: "upsd.ups_output_current", Priority: prioOutputCurrent, Dims: module.Dims{ {ID: "ups_%s_output.current", Name: "current", Div: varPrecision}, }, } upsOutputCurrentNominalChartTmpl = module.Chart{ IDSep: true, ID: "%s.output_current_nominal", Title: "UPS Output current nominal", Units: "Ampere", Fam: "output", Ctx: "upsd.ups_output_current_nominal", Priority: prioOutputCurrentNominal, Dims: module.Dims{ {ID: "ups_%s_output.current.nominal", Name: "nominal_current", Div: varPrecision}, }, } upsOutputFrequencyChartTmpl = module.Chart{ IDSep: true, ID: "%s.output_frequency", Title: "UPS Output frequency", Units: "Hz", Fam: "output", Ctx: "upsd.ups_output_frequency", Priority: prioOutputFrequency, Dims: module.Dims{ {ID: "ups_%s_output.frequency", Name: "frequency", Div: varPrecision}, }, } upsOutputFrequencyNominalChartTmpl = module.Chart{ IDSep: true, ID: "%s.output_frequency_nominal", Title: "UPS Output frequency nominal", Units: "Hz", Fam: "output", Ctx: "upsd.ups_output_frequency_nominal", Priority: prioOutputFrequencyNominal, Dims: module.Dims{ {ID: "ups_%s_output.frequency.nominal", Name: "nominal_frequency", Div: varPrecision}, }, } ) func (u *Upsd) addUPSCharts(ups upsUnit) { charts := upsChartsTmpl.Copy() var removed []string for _, v := range []struct{ v, id string }{ {varBatteryVoltage, upsBatteryVoltageChartTmpl.ID}, {varBatteryVoltageNominal, upsBatteryVoltageNominalChartTmpl.ID}, {varUpsTemperature, upsTemperatureChartTmpl.ID}, {varInputVoltage, upsInputVoltageChartTmpl.ID}, {varInputVoltageNominal, upsInputVoltageNominalChartTmpl.ID}, {varInputCurrent, upsInputCurrentChartTmpl.ID}, {varInputCurrentNominal, upsInputCurrentNominalChartTmpl.ID}, {varInputFrequency, upsInputFrequencyChartTmpl.ID}, {varInputFrequencyNominal, upsInputFrequencyNominalChartTmpl.ID}, {varOutputVoltage, upsOutputVoltageChartTmpl.ID}, {varOutputVoltageNominal, upsOutputVoltageNominalChartTmpl.ID}, {varOutputCurrent, upsOutputCurrentChartTmpl.ID}, {varOutputCurrentNominal, upsOutputCurrentNominalChartTmpl.ID}, {varOutputFrequency, upsOutputFrequencyChartTmpl.ID}, {varOutputFrequencyNominal, upsOutputFrequencyNominalChartTmpl.ID}, } { if !hasVar(ups.vars, v.v) { removed = append(removed, v.v) _ = charts.Remove(v.id) } } u.Debugf("UPS '%s' no metrics: %v", ups.name, removed) name := cleanUpsName(ups.name) for _, chart := range *charts { chart.ID = fmt.Sprintf(chart.ID, name) chart.Labels = []module.Label{ {Key: "ups_name", Value: ups.name}, {Key: "battery_type", Value: ups.vars[varBatteryType]}, {Key: "device_model", Value: ups.vars[varDeviceModel]}, {Key: "device_serial", Value: ups.vars[varDeviceSerial]}, {Key: "device_manufacturer", Value: ups.vars[varDeviceMfr]}, {Key: "device_type", Value: ups.vars[varDeviceType]}, } for _, dim := range chart.Dims { dim.ID = fmt.Sprintf(dim.ID, ups.name) } } if err := u.Charts().Add(*charts...); err != nil { u.Warning(err) } } func (u *Upsd) removeUPSCharts(name string) { name = cleanUpsName(name) for _, chart := range *u.Charts() { if strings.HasPrefix(chart.ID, name) { chart.MarkRemove() chart.MarkNotCreated() } } } func cleanUpsName(name string) string { name = strings.ReplaceAll(name, " ", "_") name = strings.ReplaceAll(name, ".", "_") return name }