Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Sensors data #238

Open
wants to merge 9 commits into
base: v1
Choose a base branch
from
210 changes: 210 additions & 0 deletions bmc/sensors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package bmc

import (
"context"
"errors"
"fmt"

"github.com/bmc-toolbox/bmclib/devices"
"github.com/hashicorp/go-multierror"
)

// PowerSensorsGetter retrieves power consumption values
type PowerSensorsGetter interface {
PowerSensors(ctx context.Context) ([]*devices.PowerSensor, error)
}

// TemperatureSensorGetter retrieves temperature values
type TemperatureSensorsGetter interface {
TemperatureSensors(ctx context.Context) ([]*devices.TemperatureSensor, error)
}

// FanSensorsGetter retrieves fan speed data
type FanSensorsGetter interface {
FanSensors(ctx context.Context) ([]*devices.FanSensor, error)
}

// ChassisHealthGetter retrieves chassis health data
type ChassisHealthGetter interface {
ChassisHealth(ctx context.Context) ([]*devices.ChassisHealth, error)
}

// PowerGetter interface implementation identifier and passthrough methods

// GetPowerSensors returns power draw data, trying all interface implementations passed in
func GetPowerSensors(ctx context.Context, p []PowerSensorsGetter) (power []*devices.PowerSensor, err error) {
Loop:
for _, elem := range p {
if elem == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())
break Loop
default:
power, vErr := elem.PowerSensors(ctx)
if vErr != nil {
err = multierror.Append(err, vErr)
continue
}
return power, nil
}
}

return power, multierror.Append(err, errors.New("failed to get power sensor data"))
}

// GetPowerSensorsFromInterfaces identfies implementations of the PowerSensorGetter interface and acts as a pass through method
func GetPowerSensorsFromInterfaces(ctx context.Context, generic []interface{}) (power []*devices.PowerSensor, err error) {
powerDrawGetter := make([]PowerSensorsGetter, 0)
for _, elem := range generic {
switch p := elem.(type) {
case PowerSensorsGetter:
powerDrawGetter = append(powerDrawGetter, p)
default:
e := fmt.Sprintf("not a PowerSensorGetter implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(powerDrawGetter) == 0 {
return power, multierror.Append(err, errors.New("no PowerSensorGetter implementations found"))
}

return GetPowerSensors(ctx, powerDrawGetter)
}

// TemperatureSensorsGetter interface identifier and passthrough methods

// GetTemperatureSensors returns temperature data, trying all interface implementations passed in
func GetTemperatureSensors(ctx context.Context, p []TemperatureSensorsGetter) (temps []*devices.TemperatureSensor, err error) {
Loop:
for _, elem := range p {
if elem == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())
break Loop
default:
temps, vErr := elem.TemperatureSensors(ctx)
if vErr != nil {
err = multierror.Append(err, vErr)
continue
}
return temps, nil
}
}

return temps, multierror.Append(err, errors.New("failed to get temperature sensor data"))
}

// GetTemperatureSensorsFromInterfaces identfies implementations of the TemperatureGetter interface and acts as a pass through method
func GetTemperatureSensorsFromInterfaces(ctx context.Context, generic []interface{}) (temps []*devices.TemperatureSensor, err error) {
gets := make([]TemperatureSensorsGetter, 0)
for _, elem := range generic {
switch p := elem.(type) {
case TemperatureSensorsGetter:
gets = append(gets, p)
default:
e := fmt.Sprintf("not a TemperatureGetter implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(gets) == 0 {
return temps, multierror.Append(err, errors.New("no TemperatureGetter implementations found"))
}

return GetTemperatureSensors(ctx, gets)
}

// FanSensorsGetter interface identifier and passthrough methods

// GetFanSensors returns fan speed data, trying all interface implementations passed in
func GetFanSensors(ctx context.Context, p []FanSensorsGetter) (fanSensors []*devices.FanSensor, err error) {
Loop:
for _, elem := range p {
if elem == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())
break Loop
default:
fanSensors, vErr := elem.FanSensors(ctx)
if vErr != nil {
err = multierror.Append(err, vErr)
continue
}
return fanSensors, nil
}
}

return fanSensors, multierror.Append(err, errors.New("failed to get fan sensor data"))
}

// GetFanSensorsFromInterfaces identfies implementations of the FanSpeedGetter interface and acts as a pass through method
func GetFanSensorsFromInterfaces(ctx context.Context, generic []interface{}) (fanSensors []*devices.FanSensor, err error) {
gets := make([]FanSensorsGetter, 0)
for _, elem := range generic {
switch p := elem.(type) {
case FanSensorsGetter:
gets = append(gets, p)
default:
e := fmt.Sprintf("not a FanSensorGetter implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(gets) == 0 {
return fanSensors, multierror.Append(err, errors.New("no FanSensorGetter implementations found"))
}

return GetFanSensors(ctx, gets)
}

// ChassisHealthGetter interface identifier and passthrough methods

// GetChassisHealth gets all chassis health data, trying all interface implementations passed in
func GetChassisHealth(ctx context.Context, p []ChassisHealthGetter) (health []*devices.ChassisHealth, err error) {
Loop:
for _, elem := range p {
if elem == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())
break Loop
default:
health, vErr := elem.ChassisHealth(ctx)
if vErr != nil {
err = multierror.Append(err, vErr)
continue
}
return health, nil
}
}

return health, multierror.Append(err, errors.New("failed to get chassis health"))
}

// GetChassisHealthFromInterfaces identfies implementations of the ChassisHealthGetter interface and acts as a pass through method
func GetChassisHealthFromInterfaces(ctx context.Context, generic []interface{}) (health []*devices.ChassisHealth, err error) {
gets := make([]ChassisHealthGetter, 0)
for _, elem := range generic {
switch p := elem.(type) {
case ChassisHealthGetter:
gets = append(gets, p)
default:
e := fmt.Sprintf("not a ChassisHealthGetter implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(gets) == 0 {
return health, multierror.Append(err, errors.New("no FanSensorGetter implementations found"))
}

return GetChassisHealth(ctx, gets)
}
21 changes: 21 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"sync"

"github.com/bmc-toolbox/bmclib/bmc"
"github.com/bmc-toolbox/bmclib/devices"
"github.com/bmc-toolbox/bmclib/providers/asrockrack"
"github.com/bmc-toolbox/bmclib/providers/dell/idrac9"
"github.com/bmc-toolbox/bmclib/providers/goipmi"
Expand Down Expand Up @@ -221,3 +222,23 @@ func (c *Client) GetBIOSVersion(ctx context.Context) (version string, err error)
func (c *Client) UpdateBIOSFirmware(ctx context.Context, fileReader io.Reader, fileSize int64) (err error) {
return bmc.UpdateBIOSFirmwareFromInterfaces(ctx, fileReader, fileSize, c.Registry.GetDriverInterfaces())
}

// GetPowerSensors pass through library function
func (c *Client) GetPowerSensors(ctx context.Context) (sensors []*devices.PowerSensor, err error) {
return bmc.GetPowerSensorsFromInterfaces(ctx, c.Registry.GetDriverInterfaces())
}

// GetTemperatureSensors pass through library function
func (c *Client) GetTemperatureSensors(ctx context.Context) (sensors []*devices.TemperatureSensor, err error) {
return bmc.GetTemperatureSensorsFromInterfaces(ctx, c.Registry.GetDriverInterfaces())
}

// GetFanSensors pass through library function
func (c *Client) GetFanSensors(ctx context.Context) (sensors []*devices.FanSensor, err error) {
return bmc.GetFanSensorsFromInterfaces(ctx, c.Registry.GetDriverInterfaces())
}

// GetChassisHealth pass through library function
func (c *Client) GetChassisHealth(ctx context.Context) (health []*devices.ChassisHealth, err error) {
return bmc.GetChassisHealthFromInterfaces(ctx, c.Registry.GetDriverInterfaces())
}
27 changes: 27 additions & 0 deletions devices/sensors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package devices

type PowerSensor struct {
Name string
ID string
InputWatts float32
OutputWatts float32
LastOutputWatts float32
}

type TemperatureSensor struct {
ID string
ReadingCelsius float32
PhysicalContext string
}

type FanSensor struct {
ID string
Reading float32
PhysicalContext string
}

type ChassisHealth struct {
ID string
State string
Health string
}
63 changes: 63 additions & 0 deletions examples/v1/sensors/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"context"
"fmt"
"log"
"time"

"github.com/bmc-toolbox/bmclib"
"github.com/bombsimon/logrusr"
"github.com/sirupsen/logrus"
)

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
host := ""
port := ""
user := ""
pass := ""

l := logrus.New()
l.Level = logrus.DebugLevel
logger := logrusr.NewLogger(l)

var err error

cl := bmclib.NewClient(host, port, user, pass, bmclib.WithLogger(logger))
cl.Registry.Drivers = cl.Registry.Using("redfish")
err = cl.Open(ctx)
if err != nil {
log.Fatal(err, "bmc login failed")
}

p, err := cl.GetPowerSensors(ctx)
if err != nil {
fmt.Println(err, "unable to retrieve power sensor data")
}

fmt.Printf("%+v\n", p)

t, err := cl.GetTemperatureSensors(ctx)
if err != nil {
fmt.Println(err, "unable to retrieve temperature sensor data")
}

fmt.Printf("%+v\n", t)

f, err := cl.GetFanSensors(ctx)
if err != nil {
fmt.Println(err, "unable to retrieve fan sensor data")
}

fmt.Printf("%+v\n", f)

c, err := cl.GetChassisHealth(ctx)
if err != nil {
fmt.Println(err, "unable to retrieve chassis health data")
}

fmt.Printf("%+v\n", c)

}
8 changes: 8 additions & 0 deletions providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,12 @@ const (
FeatureBmcFirmwareUpdate registrar.Feature = "bmcfirwareupdate"
// FeatureBiosFirmwareUpdate means an implementation that updates the BIOS firmware
FeatureBiosFirmwareUpdate registrar.Feature = "biosfirwareupdate"
// FeaturePowersensors indicates an implementation that returns Power sensor information
FeaturePowersensors registrar.Feature = "powersensors"
// FeatureTemperatureSensors indicates an implementation that returns Temperature sensor information
FeatureTemperatureSensors registrar.Feature = "temperaturesensors"
// FeatureFanSensors indicates an implementation that returns Fan sensor information
FeatureFanSensors registrar.Feature = "fansensors"
// FeatureChassisHealth indicates an implementation that returns ChassisHealth information
FeatureChassisHealth registrar.Feature = "chassishealth"
)
30 changes: 24 additions & 6 deletions providers/redfish/redfish.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,35 @@ var (
Features = registrar.Features{
providers.FeaturePowerSet,
providers.FeaturePowerState,
providers.FeaturePowersensors,
joelrebel marked this conversation as resolved.
Show resolved Hide resolved
providers.FeatureTemperatureSensors,
providers.FeatureFanSensors,
providers.FeatureChassisHealth,
}

// Supported Chassis Odata IDs
chassisOdataIdURLs = []string{
// Dells
"/redfish/v1/Chassis/System.Embedded.1",
// Supermicro
"/redfish/v1/Chassis/1",
// MegaRAC
"/redfish/v1/Chassis/Self",
}

ErrRedfishChassisOdataID = errors.New("no compatible chassis Odata IDs identified")
ErrRedfishServiceNil = errors.New("redfish connection returned a nil redfish Service object")
)

// Conn details for redfish client
type Conn struct {
Host string
Port string
User string
Pass string
conn *gofish.APIClient
Log logr.Logger
Host string
Port string
User string
Pass string
Vendor string
conn *gofish.APIClient
Log logr.Logger
}

// Open a connection to a BMC via redfish
Expand Down
Loading