2020-06-19 03:16:49 +08:00
|
|
|
package redfish
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
2020-06-19 10:00:22 +08:00
|
|
|
"io/ioutil"
|
|
|
|
|
"net/http"
|
|
|
|
|
|
2020-06-19 03:16:49 +08:00
|
|
|
"github.com/influxdata/telegraf"
|
|
|
|
|
"github.com/influxdata/telegraf/internal"
|
2020-06-26 02:49:45 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/common/tls"
|
2020-06-19 03:16:49 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Cpu struct {
|
|
|
|
|
Name string
|
|
|
|
|
ReadingCelsius int
|
|
|
|
|
UpperThresholdCritical int
|
|
|
|
|
UpperThresholdFatal int
|
|
|
|
|
Status CpuStatus
|
|
|
|
|
}
|
|
|
|
|
type Payload struct {
|
|
|
|
|
Temperatures []Cpu `json:",omitempty"`
|
|
|
|
|
Fans []Speed `json:",omitempty"`
|
|
|
|
|
PowerSupplies []Watt `json:",omitempty"`
|
|
|
|
|
Hostname string `json:",omitempty"`
|
|
|
|
|
Voltages []Volt `json:",omitempty"`
|
|
|
|
|
Location *Address `json:",omitempty"`
|
|
|
|
|
}
|
|
|
|
|
type CpuStatus struct {
|
|
|
|
|
State string
|
|
|
|
|
Health string
|
|
|
|
|
}
|
|
|
|
|
type Speed struct {
|
|
|
|
|
Name string
|
|
|
|
|
Reading int
|
|
|
|
|
ReadingUnits string
|
|
|
|
|
UpperThresholdCritical int
|
|
|
|
|
UpperThresholdFatal int
|
|
|
|
|
Status FansStatus
|
|
|
|
|
}
|
|
|
|
|
type FansStatus struct {
|
|
|
|
|
State string
|
|
|
|
|
Health string
|
|
|
|
|
}
|
|
|
|
|
type Watt struct {
|
|
|
|
|
Name string
|
|
|
|
|
PowerInputWatts float64
|
|
|
|
|
PowerCapacityWatts float64
|
|
|
|
|
PowerOutputWatts float64
|
|
|
|
|
LastPowerOutputWatts float64
|
|
|
|
|
Status PowerStatus
|
|
|
|
|
LineInputVoltage float64
|
|
|
|
|
}
|
|
|
|
|
type PowerStatus struct {
|
|
|
|
|
State string
|
|
|
|
|
Health string
|
|
|
|
|
}
|
|
|
|
|
type Volt struct {
|
|
|
|
|
Name string
|
|
|
|
|
ReadingVolts float64
|
|
|
|
|
UpperThresholdCritical float64
|
|
|
|
|
UpperThresholdFatal float64
|
|
|
|
|
Status VoltStatus
|
|
|
|
|
}
|
|
|
|
|
type VoltStatus struct {
|
|
|
|
|
State string
|
|
|
|
|
Health string
|
|
|
|
|
}
|
|
|
|
|
type Address struct {
|
|
|
|
|
PostalAddress PostalAddress
|
|
|
|
|
Placement Placement
|
|
|
|
|
}
|
|
|
|
|
type PostalAddress struct {
|
|
|
|
|
DataCenter string
|
|
|
|
|
Room string
|
|
|
|
|
}
|
|
|
|
|
type Placement struct {
|
|
|
|
|
Rack string
|
|
|
|
|
Row string
|
|
|
|
|
}
|
|
|
|
|
type Redfish struct {
|
|
|
|
|
Address string `toml:"address"`
|
|
|
|
|
BasicAuthUsername string `toml:"username"`
|
|
|
|
|
BasicAuthPassword string `toml:"password"`
|
|
|
|
|
ComputerSystemId string `toml:"computer_system_id"`
|
|
|
|
|
client http.Client
|
|
|
|
|
tls.ClientConfig
|
|
|
|
|
Timeout internal.Duration `toml:"timeout"`
|
|
|
|
|
payload Payload
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Redfish) Description() string {
|
|
|
|
|
return "Read CPU, Fans, Powersupply and Voltage metrics of hardware server through redfish APIs"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var redfishConfig = `
|
2020-06-19 10:00:22 +08:00
|
|
|
## Redfish API Base URL.
|
|
|
|
|
address = "https://127.0.0.1:5000"
|
2020-06-19 03:16:49 +08:00
|
|
|
|
2020-06-19 10:00:22 +08:00
|
|
|
## Credentials for the Redfish API.
|
|
|
|
|
username = "root"
|
|
|
|
|
password = "password123456"
|
2020-06-19 03:16:49 +08:00
|
|
|
|
2020-06-19 10:00:22 +08:00
|
|
|
## System Id to collect data for in Redfish APIs.
|
2020-06-19 03:16:49 +08:00
|
|
|
computer_system_id="System.Embedded.1"
|
|
|
|
|
|
2020-06-19 10:00:22 +08:00
|
|
|
## Amount of time allowed to complete the HTTP request
|
2020-06-19 03:16:49 +08:00
|
|
|
# timeout = "5s"
|
2020-06-19 10:00:22 +08:00
|
|
|
|
|
|
|
|
## Optional TLS Config
|
|
|
|
|
# tls_ca = "/etc/telegraf/ca.pem"
|
|
|
|
|
# tls_cert = "/etc/telegraf/cert.pem"
|
|
|
|
|
# tls_key = "/etc/telegraf/key.pem"
|
|
|
|
|
## Use TLS but skip chain & host verification
|
|
|
|
|
# insecure_skip_verify = false
|
2020-06-19 03:16:49 +08:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
func (r *Redfish) SampleConfig() string {
|
|
|
|
|
return redfishConfig
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Redfish) Init() error {
|
|
|
|
|
if len(r.Address) == 0 || len(r.BasicAuthUsername) == 0 || len(r.BasicAuthPassword) == 0 {
|
|
|
|
|
return fmt.Errorf("did not provide IP or username and password")
|
|
|
|
|
}
|
|
|
|
|
if len(r.ComputerSystemId) == 0 {
|
|
|
|
|
return fmt.Errorf("did not provide the computer system ID of the resource")
|
|
|
|
|
}
|
|
|
|
|
tlsCfg, err := r.ClientConfig.TLSConfig()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
r.client = http.Client{
|
|
|
|
|
Transport: &http.Transport{
|
|
|
|
|
TLSClientConfig: tlsCfg,
|
|
|
|
|
Proxy: http.ProxyFromEnvironment,
|
|
|
|
|
},
|
|
|
|
|
Timeout: r.Timeout.Duration,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Redfish) GetData(url string) error {
|
|
|
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth(r.BasicAuthUsername, r.BasicAuthPassword)
|
|
|
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
resp, err := r.client.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
if resp.StatusCode == 200 {
|
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = json.Unmarshal(body, &r.payload)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error parsing input: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
return fmt.Errorf("received status code %d (%s), expected 200",
|
|
|
|
|
resp.StatusCode,
|
|
|
|
|
http.StatusText(resp.StatusCode))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Redfish) Gather(acc telegraf.Accumulator) error {
|
|
|
|
|
var url []string
|
|
|
|
|
url = append(url, fmt.Sprint(r.Address, "/redfish/v1/Chassis/", r.ComputerSystemId, "/Thermal"), fmt.Sprint(r.Address, "/redfish/v1/Chassis/", r.ComputerSystemId, "/Power"), fmt.Sprint(r.Address, "/redfish/v1/Systems/", r.ComputerSystemId), fmt.Sprint(r.Address, "/redfish/v1/Chassis/", r.ComputerSystemId, "/"))
|
|
|
|
|
for _, i := range url {
|
|
|
|
|
err := r.GetData(i)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if r.payload.Location != nil {
|
|
|
|
|
for _, j := range r.payload.Temperatures {
|
|
|
|
|
// Tags
|
|
|
|
|
tags := map[string]string{}
|
|
|
|
|
tags["address"] = r.Address
|
|
|
|
|
tags["name"] = j.Name
|
|
|
|
|
tags["source"] = r.payload.Hostname
|
|
|
|
|
tags["state"] = j.Status.State
|
|
|
|
|
tags["health"] = j.Status.Health
|
|
|
|
|
tags["datacenter"] = r.payload.Location.PostalAddress.DataCenter
|
|
|
|
|
tags["room"] = r.payload.Location.PostalAddress.Room
|
|
|
|
|
tags["rack"] = r.payload.Location.Placement.Rack
|
|
|
|
|
tags["row"] = r.payload.Location.Placement.Row
|
|
|
|
|
// Fields
|
|
|
|
|
fields := make(map[string]interface{})
|
|
|
|
|
fields["reading_celsius"] = j.ReadingCelsius
|
|
|
|
|
fields["upper_threshold_critical"] = j.UpperThresholdCritical
|
|
|
|
|
fields["upper_threshold_fatal"] = j.UpperThresholdFatal
|
|
|
|
|
acc.AddFields("redfish_thermal_temperatures", fields, tags)
|
|
|
|
|
}
|
|
|
|
|
for _, j := range r.payload.Fans {
|
|
|
|
|
// Tags
|
|
|
|
|
tags := map[string]string{}
|
|
|
|
|
fields := make(map[string]interface{})
|
|
|
|
|
tags["address"] = r.Address
|
|
|
|
|
tags["name"] = j.Name
|
|
|
|
|
tags["source"] = r.payload.Hostname
|
|
|
|
|
tags["state"] = j.Status.State
|
|
|
|
|
tags["health"] = j.Status.Health
|
|
|
|
|
tags["datacenter"] = r.payload.Location.PostalAddress.DataCenter
|
|
|
|
|
tags["room"] = r.payload.Location.PostalAddress.Room
|
|
|
|
|
tags["rack"] = r.payload.Location.Placement.Rack
|
|
|
|
|
tags["row"] = r.payload.Location.Placement.Row
|
|
|
|
|
|
|
|
|
|
// Fields
|
|
|
|
|
if j.ReadingUnits == "RPM" {
|
|
|
|
|
fields["upper_threshold_critical"] = j.UpperThresholdCritical
|
|
|
|
|
fields["upper_threshold_fatal"] = j.UpperThresholdFatal
|
|
|
|
|
fields["reading_rpm"] = j.Reading
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
fields["reading_percent"] = j.Reading
|
|
|
|
|
}
|
|
|
|
|
acc.AddFields("redfish_thermal_fans", fields, tags)
|
|
|
|
|
}
|
|
|
|
|
for _, j := range r.payload.PowerSupplies {
|
|
|
|
|
// Tags
|
|
|
|
|
tags := map[string]string{}
|
|
|
|
|
tags["address"] = r.Address
|
|
|
|
|
tags["name"] = j.Name
|
|
|
|
|
tags["source"] = r.payload.Hostname
|
|
|
|
|
tags["state"] = j.Status.State
|
|
|
|
|
tags["health"] = j.Status.Health
|
|
|
|
|
tags["datacenter"] = r.payload.Location.PostalAddress.DataCenter
|
|
|
|
|
tags["room"] = r.payload.Location.PostalAddress.Room
|
|
|
|
|
tags["rack"] = r.payload.Location.Placement.Rack
|
|
|
|
|
tags["row"] = r.payload.Location.Placement.Row
|
|
|
|
|
// Fields
|
|
|
|
|
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 r.payload.Voltages {
|
|
|
|
|
// Tags
|
|
|
|
|
tags := map[string]string{}
|
|
|
|
|
tags["address"] = r.Address
|
|
|
|
|
tags["name"] = j.Name
|
|
|
|
|
tags["source"] = r.payload.Hostname
|
|
|
|
|
tags["state"] = j.Status.State
|
|
|
|
|
tags["health"] = j.Status.Health
|
|
|
|
|
tags["datacenter"] = r.payload.Location.PostalAddress.DataCenter
|
|
|
|
|
tags["room"] = r.payload.Location.PostalAddress.Room
|
|
|
|
|
tags["rack"] = r.payload.Location.Placement.Rack
|
|
|
|
|
tags["row"] = r.payload.Location.Placement.Row
|
|
|
|
|
// Fields
|
|
|
|
|
fields := make(map[string]interface{})
|
|
|
|
|
fields["reading_volts"] = j.ReadingVolts
|
|
|
|
|
fields["upper_threshold_critical"] = j.UpperThresholdCritical
|
|
|
|
|
fields["upper_threshold_fatal"] = j.UpperThresholdFatal
|
|
|
|
|
acc.AddFields("redfish_power_voltages", fields, tags)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for _, j := range r.payload.Temperatures {
|
|
|
|
|
// Tags
|
|
|
|
|
tags := map[string]string{}
|
|
|
|
|
tags["address"] = r.Address
|
|
|
|
|
tags["name"] = j.Name
|
|
|
|
|
tags["source"] = r.payload.Hostname
|
|
|
|
|
tags["state"] = j.Status.State
|
|
|
|
|
tags["health"] = j.Status.Health
|
|
|
|
|
// Fields
|
|
|
|
|
fields := make(map[string]interface{})
|
|
|
|
|
fields["reading_celsius"] = j.ReadingCelsius
|
|
|
|
|
fields["upper_threshold_critical"] = j.UpperThresholdCritical
|
|
|
|
|
fields["upper_threshold_fatal"] = j.UpperThresholdFatal
|
|
|
|
|
acc.AddFields("redfish_thermal_temperatures", fields, tags)
|
|
|
|
|
}
|
|
|
|
|
for _, j := range r.payload.Fans {
|
|
|
|
|
// Tags
|
|
|
|
|
tags := map[string]string{}
|
|
|
|
|
fields := make(map[string]interface{})
|
|
|
|
|
tags["address"] = r.Address
|
|
|
|
|
tags["name"] = j.Name
|
|
|
|
|
tags["source"] = r.payload.Hostname
|
|
|
|
|
tags["state"] = j.Status.State
|
|
|
|
|
tags["health"] = j.Status.Health
|
|
|
|
|
// Fields
|
|
|
|
|
if j.ReadingUnits == "RPM" {
|
|
|
|
|
fields["upper_threshold_critical"] = j.UpperThresholdCritical
|
|
|
|
|
fields["upper_threshold_fatal"] = j.UpperThresholdFatal
|
|
|
|
|
fields["reading_rpm"] = j.Reading
|
|
|
|
|
} else {
|
|
|
|
|
fields["reading_percent"] = j.Reading
|
|
|
|
|
}
|
|
|
|
|
acc.AddFields("redfish_thermal_fans", fields, tags)
|
|
|
|
|
}
|
|
|
|
|
for _, j := range r.payload.PowerSupplies {
|
|
|
|
|
// Tags
|
|
|
|
|
tags := map[string]string{}
|
|
|
|
|
tags["address"] = r.Address
|
|
|
|
|
tags["name"] = j.Name //j.Name
|
|
|
|
|
tags["source"] = r.payload.Hostname
|
|
|
|
|
tags["state"] = j.Status.State
|
|
|
|
|
tags["health"] = j.Status.Health
|
|
|
|
|
// Fields
|
|
|
|
|
fields := make(map[string]interface{})
|
|
|
|
|
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 r.payload.Voltages {
|
|
|
|
|
// Tags
|
|
|
|
|
tags := map[string]string{}
|
|
|
|
|
tags["address"] = r.Address
|
|
|
|
|
tags["name"] = j.Name
|
|
|
|
|
tags["source"] = r.payload.Hostname
|
|
|
|
|
tags["state"] = j.Status.State
|
|
|
|
|
tags["health"] = j.Status.Health
|
|
|
|
|
// Fields
|
|
|
|
|
fields := make(map[string]interface{})
|
|
|
|
|
fields["reading_volts"] = j.ReadingVolts
|
|
|
|
|
fields["upper_threshold_critical"] = j.UpperThresholdCritical
|
|
|
|
|
fields["upper_threshold_fatal"] = j.UpperThresholdFatal
|
|
|
|
|
acc.AddFields("redfish_power_voltages", fields, tags)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
inputs.Add("redfish", func() telegraf.Input { return &Redfish{} })
|
|
|
|
|
}
|