diff --git a/plugins/inputs/redfish/README.md b/plugins/inputs/redfish/README.md index 0f3120435..cf0ab5cb0 100644 --- a/plugins/inputs/redfish/README.md +++ b/plugins/inputs/redfish/README.md @@ -31,6 +31,10 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. ## System Id to collect data for in Redfish APIs. computer_system_id="System.Embedded.1" + ## Metrics to collect + ## The metric collects to gather. Choose from "power" and "thermal". + # include_metrics = ["power", "thermal"] + ## Tag sets allow you to include redfish OData link parent data ## For Example. ## Thermal data is an OData link with parent Chassis which has a link of Location. diff --git a/plugins/inputs/redfish/redfish.go b/plugins/inputs/redfish/redfish.go index b2af61f13..444527f23 100644 --- a/plugins/inputs/redfish/redfish.go +++ b/plugins/inputs/redfish/redfish.go @@ -26,6 +26,7 @@ type Redfish struct { Username string `toml:"username"` Password string `toml:"password"` ComputerSystemID string `toml:"computer_system_id"` + IncludeMetrics []string `toml:"include_metrics"` IncludeTagSets []string `toml:"include_tag_sets"` Timeout config.Duration `toml:"timeout"` @@ -163,6 +164,17 @@ func (r *Redfish) Init() error { return fmt.Errorf("did not provide the computer system ID of the resource") } + if len(r.IncludeMetrics) == 0 { + return fmt.Errorf("no metrics specified to collect") + } + for _, metric := range r.IncludeMetrics { + switch metric { + case "thermal", "power": + default: + return fmt.Errorf("unknown metric requested: %s", metric) + } + } + r.tagSet = make(map[string]bool, len(r.IncludeTagSets)) for _, setLabel := range r.IncludeTagSets { r.tagSet[setLabel] = true @@ -295,159 +307,181 @@ func (r *Redfish) Gather(acc telegraf.Accumulator) error { return err } - thermal, err := r.getThermal(chassis.Thermal.Ref) - if err != nil { - return err + for _, metric := range r.IncludeMetrics { + var err error + switch metric { + case "thermal": + err = r.gatherThermal(acc, address, system, chassis) + case "power": + err = r.gatherPower(acc, address, system, chassis) + default: + return fmt.Errorf("unknown metric requested: %s", metric) + } + if err != nil { + return err + } + } + } + return nil +} + +func (r *Redfish) gatherThermal(acc telegraf.Accumulator, address string, system *System, chassis *Chassis) error { + thermal, err := r.getThermal(chassis.Thermal.Ref) + if err != nil { + return err + } + + for _, j := range thermal.Temperatures { + tags := map[string]string{} + tags["member_id"] = j.MemberID + tags["address"] = address + tags["name"] = j.Name + tags["source"] = system.Hostname + tags["state"] = j.Status.State + tags["health"] = j.Status.Health + if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { + tags["datacenter"] = chassis.Location.PostalAddress.DataCenter + tags["room"] = chassis.Location.PostalAddress.Room + tags["rack"] = chassis.Location.Placement.Rack + tags["row"] = chassis.Location.Placement.Row + } + if _, ok := r.tagSet[tagSetChassis]; ok { + setChassisTags(chassis, tags) } - for _, j := range thermal.Temperatures { - tags := map[string]string{} - tags["member_id"] = j.MemberID - tags["address"] = address - tags["name"] = j.Name - tags["source"] = system.Hostname - tags["state"] = j.Status.State - tags["health"] = j.Status.Health - if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { - tags["datacenter"] = chassis.Location.PostalAddress.DataCenter - tags["room"] = chassis.Location.PostalAddress.Room - tags["rack"] = chassis.Location.Placement.Rack - tags["row"] = chassis.Location.Placement.Row - } - if _, ok := r.tagSet[tagSetChassis]; ok { - setChassisTags(chassis, tags) - } + fields := make(map[string]interface{}) + fields["reading_celsius"] = j.ReadingCelsius + fields["upper_threshold_critical"] = j.UpperThresholdCritical + fields["upper_threshold_fatal"] = j.UpperThresholdFatal + fields["lower_threshold_critical"] = j.LowerThresholdCritical + fields["lower_threshold_fatal"] = j.LowerThresholdFatal + acc.AddFields("redfish_thermal_temperatures", fields, tags) + } - fields := make(map[string]interface{}) - fields["reading_celsius"] = j.ReadingCelsius + for _, j := range thermal.Fans { + tags := map[string]string{} + fields := make(map[string]interface{}) + tags["member_id"] = j.MemberID + tags["address"] = address + tags["name"] = j.Name + tags["source"] = system.Hostname + tags["state"] = j.Status.State + tags["health"] = j.Status.Health + if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { + tags["datacenter"] = chassis.Location.PostalAddress.DataCenter + tags["room"] = chassis.Location.PostalAddress.Room + tags["rack"] = chassis.Location.Placement.Rack + tags["row"] = chassis.Location.Placement.Row + } + if _, ok := r.tagSet[tagSetChassis]; ok { + setChassisTags(chassis, tags) + } + + if j.ReadingUnits != nil && *j.ReadingUnits == "RPM" { fields["upper_threshold_critical"] = j.UpperThresholdCritical fields["upper_threshold_fatal"] = j.UpperThresholdFatal fields["lower_threshold_critical"] = j.LowerThresholdCritical fields["lower_threshold_fatal"] = j.LowerThresholdFatal - acc.AddFields("redfish_thermal_temperatures", fields, tags) + fields["reading_rpm"] = j.Reading + } else { + fields["reading_percent"] = j.Reading + } + acc.AddFields("redfish_thermal_fans", fields, tags) + } + + return nil +} + +func (r *Redfish) gatherPower(acc telegraf.Accumulator, address string, system *System, chassis *Chassis) error { + power, err := r.getPower(chassis.Power.Ref) + if err != nil { + return err + } + + for _, j := range power.PowerControl { + tags := map[string]string{ + "member_id": j.MemberID, + "address": address, + "name": j.Name, + "source": system.Hostname, + } + if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { + tags["datacenter"] = chassis.Location.PostalAddress.DataCenter + tags["room"] = chassis.Location.PostalAddress.Room + tags["rack"] = chassis.Location.Placement.Rack + tags["row"] = chassis.Location.Placement.Row + } + if _, ok := r.tagSet[tagSetChassis]; ok { + setChassisTags(chassis, tags) } - for _, j := range thermal.Fans { - tags := map[string]string{} - fields := make(map[string]interface{}) - tags["member_id"] = j.MemberID - tags["address"] = address - tags["name"] = j.Name - tags["source"] = system.Hostname - tags["state"] = j.Status.State - tags["health"] = j.Status.Health - if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { - tags["datacenter"] = chassis.Location.PostalAddress.DataCenter - tags["room"] = chassis.Location.PostalAddress.Room - tags["rack"] = chassis.Location.Placement.Rack - tags["row"] = chassis.Location.Placement.Row - } - if _, ok := r.tagSet[tagSetChassis]; ok { - setChassisTags(chassis, tags) - } - - if j.ReadingUnits != nil && *j.ReadingUnits == "RPM" { - fields["upper_threshold_critical"] = j.UpperThresholdCritical - fields["upper_threshold_fatal"] = j.UpperThresholdFatal - fields["lower_threshold_critical"] = j.LowerThresholdCritical - fields["lower_threshold_fatal"] = j.LowerThresholdFatal - fields["reading_rpm"] = j.Reading - } else { - fields["reading_percent"] = j.Reading - } - acc.AddFields("redfish_thermal_fans", fields, tags) + fields := map[string]interface{}{ + "power_allocated_watts": j.PowerAllocatedWatts, + "power_available_watts": j.PowerAvailableWatts, + "power_capacity_watts": j.PowerCapacityWatts, + "power_consumed_watts": j.PowerConsumedWatts, + "power_requested_watts": j.PowerRequestedWatts, + "average_consumed_watts": j.PowerMetrics.AverageConsumedWatts, + "interval_in_min": j.PowerMetrics.IntervalInMin, + "max_consumed_watts": j.PowerMetrics.MaxConsumedWatts, + "min_consumed_watts": j.PowerMetrics.MinConsumedWatts, } - power, err := r.getPower(chassis.Power.Ref) - if err != nil { - return err + acc.AddFields("redfish_power_powercontrol", fields, tags) + } + + for _, j := range power.PowerSupplies { + tags := map[string]string{} + tags["member_id"] = j.MemberID + tags["address"] = address + tags["name"] = j.Name + tags["source"] = system.Hostname + tags["state"] = j.Status.State + tags["health"] = j.Status.Health + if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { + tags["datacenter"] = chassis.Location.PostalAddress.DataCenter + tags["room"] = chassis.Location.PostalAddress.Room + tags["rack"] = chassis.Location.Placement.Rack + tags["row"] = chassis.Location.Placement.Row + } + if _, ok := r.tagSet[tagSetChassis]; ok { + setChassisTags(chassis, tags) } - for _, j := range power.PowerControl { - tags := map[string]string{ - "member_id": j.MemberID, - "address": address, - "name": j.Name, - "source": system.Hostname, - } - if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { - tags["datacenter"] = chassis.Location.PostalAddress.DataCenter - tags["room"] = chassis.Location.PostalAddress.Room - tags["rack"] = chassis.Location.Placement.Rack - tags["row"] = chassis.Location.Placement.Row - } - if _, ok := r.tagSet[tagSetChassis]; ok { - setChassisTags(chassis, tags) - } + fields := make(map[string]interface{}) + fields["power_input_watts"] = j.PowerInputWatts + fields["power_output_watts"] = j.PowerOutputWatts + fields["line_input_voltage"] = j.LineInputVoltage + fields["last_power_output_watts"] = j.LastPowerOutputWatts + fields["power_capacity_watts"] = j.PowerCapacityWatts + acc.AddFields("redfish_power_powersupplies", fields, tags) + } - fields := map[string]interface{}{ - "power_allocated_watts": j.PowerAllocatedWatts, - "power_available_watts": j.PowerAvailableWatts, - "power_capacity_watts": j.PowerCapacityWatts, - "power_consumed_watts": j.PowerConsumedWatts, - "power_requested_watts": j.PowerRequestedWatts, - "average_consumed_watts": j.PowerMetrics.AverageConsumedWatts, - "interval_in_min": j.PowerMetrics.IntervalInMin, - "max_consumed_watts": j.PowerMetrics.MaxConsumedWatts, - "min_consumed_watts": j.PowerMetrics.MinConsumedWatts, - } - - acc.AddFields("redfish_power_powercontrol", fields, tags) + for _, j := range power.Voltages { + tags := map[string]string{} + tags["member_id"] = j.MemberID + tags["address"] = address + tags["name"] = j.Name + tags["source"] = system.Hostname + tags["state"] = j.Status.State + tags["health"] = j.Status.Health + if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { + tags["datacenter"] = chassis.Location.PostalAddress.DataCenter + tags["room"] = chassis.Location.PostalAddress.Room + tags["rack"] = chassis.Location.Placement.Rack + tags["row"] = chassis.Location.Placement.Row + } + if _, ok := r.tagSet[tagSetChassis]; ok { + setChassisTags(chassis, tags) } - for _, j := range power.PowerSupplies { - tags := map[string]string{} - tags["member_id"] = j.MemberID - tags["address"] = address - tags["name"] = j.Name - tags["source"] = system.Hostname - tags["state"] = j.Status.State - tags["health"] = j.Status.Health - if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { - tags["datacenter"] = chassis.Location.PostalAddress.DataCenter - tags["room"] = chassis.Location.PostalAddress.Room - tags["rack"] = chassis.Location.Placement.Rack - tags["row"] = chassis.Location.Placement.Row - } - if _, ok := r.tagSet[tagSetChassis]; ok { - setChassisTags(chassis, tags) - } - - fields := make(map[string]interface{}) - fields["power_input_watts"] = j.PowerInputWatts - fields["power_output_watts"] = j.PowerOutputWatts - fields["line_input_voltage"] = j.LineInputVoltage - fields["last_power_output_watts"] = j.LastPowerOutputWatts - fields["power_capacity_watts"] = j.PowerCapacityWatts - acc.AddFields("redfish_power_powersupplies", fields, tags) - } - - for _, j := range power.Voltages { - tags := map[string]string{} - tags["member_id"] = j.MemberID - tags["address"] = address - tags["name"] = j.Name - tags["source"] = system.Hostname - tags["state"] = j.Status.State - tags["health"] = j.Status.Health - if _, ok := r.tagSet[tagSetChassisLocation]; ok && chassis.Location != nil { - tags["datacenter"] = chassis.Location.PostalAddress.DataCenter - tags["room"] = chassis.Location.PostalAddress.Room - tags["rack"] = chassis.Location.Placement.Rack - tags["row"] = chassis.Location.Placement.Row - } - if _, ok := r.tagSet[tagSetChassis]; ok { - setChassisTags(chassis, tags) - } - - fields := make(map[string]interface{}) - fields["reading_volts"] = j.ReadingVolts - fields["upper_threshold_critical"] = j.UpperThresholdCritical - fields["upper_threshold_fatal"] = j.UpperThresholdFatal - fields["lower_threshold_critical"] = j.LowerThresholdCritical - fields["lower_threshold_fatal"] = j.LowerThresholdFatal - acc.AddFields("redfish_power_voltages", fields, tags) - } + fields := make(map[string]interface{}) + fields["reading_volts"] = j.ReadingVolts + fields["upper_threshold_critical"] = j.UpperThresholdCritical + fields["upper_threshold_fatal"] = j.UpperThresholdFatal + fields["lower_threshold_critical"] = j.LowerThresholdCritical + fields["lower_threshold_fatal"] = j.LowerThresholdFatal + acc.AddFields("redfish_power_voltages", fields, tags) } return nil @@ -458,6 +492,7 @@ func init() { return &Redfish{ // default tag set of chassis.location required for backwards compatibility IncludeTagSets: []string{tagSetChassisLocation}, + IncludeMetrics: []string{"power", "thermal"}, } }) } diff --git a/plugins/inputs/redfish/redfish_test.go b/plugins/inputs/redfish/redfish_test.go index 099cd720f..b6a29f379 100644 --- a/plugins/inputs/redfish/redfish_test.go +++ b/plugins/inputs/redfish/redfish_test.go @@ -425,6 +425,7 @@ func TestDellApis(t *testing.T) { Username: "test", Password: "test", ComputerSystemID: "System.Embedded.1", + IncludeMetrics: []string{"thermal", "power"}, } require.NoError(t, plugin.Init()) var acc testutil.Accumulator @@ -603,6 +604,7 @@ func TestHPApis(t *testing.T) { Username: "test", Password: "test", ComputerSystemID: "1", + IncludeMetrics: []string{"thermal", "power"}, } require.NoError(t, hpPlugin.Init()) var hpAcc testutil.Accumulator @@ -643,6 +645,7 @@ func TestInvalidUsernameorPassword(t *testing.T) { Username: "test", Password: "test", ComputerSystemID: "System.Embedded.1", + IncludeMetrics: []string{"thermal", "power"}, } var acc testutil.Accumulator @@ -671,6 +674,7 @@ func TestNoUsernameorPasswordConfiguration(t *testing.T) { r := &Redfish{ Address: ts.URL, ComputerSystemID: "System.Embedded.1", + IncludeMetrics: []string{"thermal", "power"}, } err := r.Init() @@ -743,6 +747,7 @@ func TestInvalidDellJSON(t *testing.T) { Username: "test", Password: "test", ComputerSystemID: "System.Embedded.1", + IncludeMetrics: []string{"thermal", "power"}, } require.NoError(t, plugin.Init()) @@ -813,6 +818,7 @@ func TestInvalidHPJSON(t *testing.T) { Username: "test", Password: "test", ComputerSystemID: "System.Embedded.2", + IncludeMetrics: []string{"thermal", "power"}, } require.NoError(t, plugin.Init()) @@ -1097,6 +1103,7 @@ func TestIncludeTagSetsConfiguration(t *testing.T) { Password: "test", ComputerSystemID: "1", IncludeTagSets: []string{"chassis", "chassis.location"}, + IncludeMetrics: []string{"thermal", "power"}, } require.NoError(t, hpPlugin.Init()) var hpAcc testutil.Accumulator diff --git a/plugins/inputs/redfish/sample.conf b/plugins/inputs/redfish/sample.conf index d8610f741..661169ec2 100644 --- a/plugins/inputs/redfish/sample.conf +++ b/plugins/inputs/redfish/sample.conf @@ -10,6 +10,10 @@ ## System Id to collect data for in Redfish APIs. computer_system_id="System.Embedded.1" + ## Metrics to collect + ## The metric collects to gather. Choose from "power" and "thermal". + # include_metrics = ["power", "thermal"] + ## Tag sets allow you to include redfish OData link parent data ## For Example. ## Thermal data is an OData link with parent Chassis which has a link of Location.