From 341efcb89d7caecfbfb338f106b557ffba6967c8 Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Sat, 16 Nov 2024 17:47:30 +0100 Subject: [PATCH] LED States --- cli/board.go | 2 +- cli/led.go | 53 +++++++- internal/redfish/redfish.go | 141 ++++++++++++---------- internal/vendors/dell/dell.go | 2 +- internal/vendors/lenovo/lenovo.go | 2 +- internal/vendors/supermicro/supermicro.go | 2 +- internal/vendors/vagrant/vagrant.go | 2 +- pkg/api/types.go | 27 +++-- 8 files changed, 140 insertions(+), 91 deletions(-) diff --git a/cli/board.go b/cli/board.go index 6863457..304f2ee 100644 --- a/cli/board.go +++ b/cli/board.go @@ -14,7 +14,7 @@ var boardCmd = &cli.Command{ return err } board := c.Board() - log.Infow("board", "bandtype", bandtype, "host", host, "result", board.String(), "bios", board.BIOS, "powermetric", board.PowerMetric, "powersupplies", board.PowerSupplies, "ledstate", board.IndicatorLED) + log.Infow("board", "bandtype", bandtype, "host", host, "result", board.String(), "redfish version", board.RedfishVersion, "bios", board.BiosVersion, "powermetric", board.PowerMetric, "powersupplies", board.PowerSupplies, "ledstate", board.IndicatorLED) bmc, err := outBandBMCConnection.BMC() if err != nil { return err diff --git a/cli/led.go b/cli/led.go index 15e73f4..0ee4960 100644 --- a/cli/led.go +++ b/cli/led.go @@ -1,6 +1,7 @@ package main import ( + "github.com/metal-stack/go-hal" "github.com/urfave/cli/v2" ) @@ -9,12 +10,52 @@ var ledCmd = &cli.Command{ Usage: "gather machine led state", Flags: flags, Action: func(ctx *cli.Context) error { - // c, err := getHalConnection(log) - // if err != nil { - // return err - // } - // ledstate := c.IdentifyLEDState() - // log.Infow("board", "bandtype", bandtype, "host", host, "result", board.String(), "bios", board.BIOS, "powermetric", board.PowerMetric, "powersupplies", board.PowerSupplies) + c, err := getHalConnection(log) + if err != nil { + return err + } + board := c.Board() + log.Infow("lead", "state", board.IndicatorLED) return nil }, + Subcommands: []*cli.Command{ + { + Name: "on", + Usage: "identify led on", + Flags: flags, + Action: func(ctx *cli.Context) error { + c, err := getHalConnection(log) + if err != nil { + return err + } + err = c.IdentifyLEDState(hal.IdentifyLEDStateOn) + if err != nil { + return err + } + log.Infow("led state set to on") + board := c.Board() + log.Infow("lead", "state", board.IndicatorLED) + return nil + }, + }, + { + Name: "off", + Usage: "identify led off", + Flags: flags, + Action: func(ctx *cli.Context) error { + c, err := getHalConnection(log) + if err != nil { + return err + } + err = c.IdentifyLEDState(hal.IdentifyLEDStateOff) + if err != nil { + return err + } + log.Infow("led state set to off") + board := c.Board() + log.Infow("lead", "state", board.IndicatorLED) + return nil + }, + }, + }, } diff --git a/internal/redfish/redfish.go b/internal/redfish/redfish.go index ff0b7ad..94dea1e 100644 --- a/internal/redfish/redfish.go +++ b/internal/redfish/redfish.go @@ -20,13 +20,14 @@ import ( ) type APIClient struct { - *gofish.APIClient + Gofish *gofish.APIClient *http.Client - urlPrefix string - user string - password string - basicAuth string - log logger.Logger + urlPrefix string + user string + password string + basicAuth string + log logger.Logger + redfishVersion string } func New(url, user, password string, insecure bool, log logger.Logger) (*APIClient, error) { @@ -41,20 +42,22 @@ func New(url, user, password string, insecure bool, log logger.Logger) (*APIClie if err != nil { return nil, err } + return &APIClient{ - APIClient: c, - Client: c.HTTPClient, - user: user, - password: password, - basicAuth: base64.StdEncoding.EncodeToString([]byte(user + ":" + password)), - urlPrefix: fmt.Sprintf("%s/redfish/v1", url), - log: log, + Gofish: c, + Client: c.HTTPClient, + redfishVersion: c.Service.RedfishVersion, + user: user, + password: password, + basicAuth: base64.StdEncoding.EncodeToString([]byte(user + ":" + password)), + urlPrefix: fmt.Sprintf("%s/redfish/v1", url), + log: log, }, nil } func (c *APIClient) BoardInfo() (*api.Board, error) { // Query the chassis data using the session token - if c.Service == nil { + if c.Gofish.Service == nil { return nil, fmt.Errorf("gofish service root is not available most likely due to missing username") } @@ -62,7 +65,7 @@ func (c *APIClient) BoardInfo() (*api.Board, error) { manufacturer := "" model := "" - systems, err := c.Service.Systems() + systems, err := c.Gofish.Service.Systems() if err != nil { c.log.Warnw("ignore system query", "error", err.Error()) } @@ -85,7 +88,7 @@ func (c *APIClient) BoardInfo() (*api.Board, error) { } } - chassis, err := c.Service.Chassis() + chassis, err := c.Gofish.Service.Chassis() if err != nil { c.log.Warnw("ignore system query", "error", err.Error()) } @@ -122,14 +125,15 @@ func (c *APIClient) BoardInfo() (*api.Board, error) { "PartNumber", chass.PartNumber, "SerialNumber", chass.SerialNumber, "BiosVersion", biosVersion, "led", chass.IndicatorLED) return &api.Board{ - VendorString: manufacturer, - Model: model, - PartNumber: chass.PartNumber, - SerialNumber: chass.SerialNumber, - BiosVersion: biosVersion, - IndicatorLED: toMetalLEDState(chass.IndicatorLED), - PowerMetric: powerMetric, - PowerSupplies: powerSupplies, + VendorString: manufacturer, + Model: model, + PartNumber: chass.PartNumber, + SerialNumber: chass.SerialNumber, + BiosVersion: biosVersion, + RedfishVersion: c.redfishVersion, + IndicatorLED: toMetalLEDState(chass.IndicatorLED), + PowerMetric: powerMetric, + PowerSupplies: powerSupplies, }, nil } } @@ -149,7 +153,7 @@ func toMetalLEDState(state common.IndicatorLED) string { // MachineUUID retrieves a unique uuid for this (hardware) machine func (c *APIClient) MachineUUID() (string, error) { - systems, err := c.Service.Systems() + systems, err := c.Gofish.Service.Systems() if err != nil { c.log.Errorw("error during system query, unable to detect uuid", "error", err.Error()) return "", err @@ -163,7 +167,7 @@ func (c *APIClient) MachineUUID() (string, error) { } func (c *APIClient) PowerState() (hal.PowerState, error) { - systems, err := c.Service.Systems() + systems, err := c.Gofish.Service.Systems() if err != nil { c.log.Warnw("ignore system query", "error", err.Error()) } @@ -206,7 +210,7 @@ func (c *APIClient) PowerCycle() error { } func (c *APIClient) setPower(resetType redfish.ResetType) error { - systems, err := c.Service.Systems() + systems, err := c.Gofish.Service.Systems() if err != nil { c.log.Warnw("ignore system query", "error", err.Error()) } @@ -312,7 +316,7 @@ func (c *APIClient) addHeadersAndAuth(req *http.Request) { } func (c *APIClient) setNextBootBIOS() error { - systems, err := c.Service.Systems() + systems, err := c.Gofish.Service.Systems() if err != nil { c.log.Warnw("ignore system query", "error", err.Error()) } @@ -329,12 +333,12 @@ func (c *APIClient) setNextBootBIOS() error { } func (c *APIClient) BMC() (*api.BMC, error) { - systems, err := c.Service.Systems() + systems, err := c.Gofish.Service.Systems() if err != nil { c.log.Warnw("ignore system query", "error", err.Error()) } - chassis, err := c.Service.Chassis() + chassis, err := c.Gofish.Service.Chassis() if err != nil { c.log.Warnw("ignore system query", "error", err.Error()) } @@ -364,51 +368,54 @@ func (c *APIClient) BMC() (*api.BMC, error) { } func (c *APIClient) IdentifyLEDState(state hal.IdentifyLEDState) error { - chassis, err := c.Service.Chassis() + var payload map[string]any + + switch state { + case hal.IdentifyLEDStateOff: + payload = map[string]any{"LocationIndicatorActive": false} + case hal.IdentifyLEDStateOn: + payload = map[string]any{"LocationIndicatorActive": true} + case hal.IdentifyLEDStateBlinking: + payload = map[string]any{"LocationIndicatorActive": true} + case hal.IdentifyLEDStateUnknown: + return fmt.Errorf("unknown LED state:%s", state) + } + resp, err := c.Gofish.Patch("/redfish/v1/Chassis/System.Embedded.1", payload) if err != nil { - c.log.Warnw("ignore system query", "error", err.Error()) + c.log.Errorw("unable to set led", "error", err) } + c.log.Infow("set led", "response", resp.Body) - systems, err := c.Service.Systems() + return nil +} + +func (c *APIClient) GetIdentifyLED() (hal.IdentifyLEDState, error) { + + resp, err := c.Gofish.Get("/redfish/v1/Chassis/System.Embedded.1") if err != nil { - return err + c.log.Errorw("unable to get led", "error", err) + return hal.IdentifyLEDStateUnknown, err } - // Not sure if system or chassis is responsible for LED - for _, system := range systems { - c.log.Infow("setting indicator led via system", "system", system.ID, "state", state) - switch state { - case hal.IdentifyLEDStateOff: - system.LocationIndicatorActive = false - system.IndicatorLED = common.OffIndicatorLED - case hal.IdentifyLEDStateOn: - system.LocationIndicatorActive = true - system.IndicatorLED = common.LitIndicatorLED - case hal.IdentifyLEDStateBlinking: - system.IndicatorLED = common.BlinkingIndicatorLED - case hal.IdentifyLEDStateUnknown: - return fmt.Errorf("unknown LED state:%s", state) - } + c.log.Infow("set led", "response", resp.Body) + var ledstate map[string]string + err = json.NewDecoder(resp.Body).Decode(ledstate) + if err != nil { + c.log.Errorw("unable to parse led state", "error", err) + return hal.IdentifyLEDStateUnknown, err } - for _, chass := range chassis { - if chass.ChassisType != redfish.RackMountChassisType { - continue - } - c.log.Infow("setting indicator led via chassis", "chassis", chass.ID, "state", state) - switch state { - case hal.IdentifyLEDStateOff: - chass.LocationIndicatorActive = false - chass.IndicatorLED = common.OffIndicatorLED - case hal.IdentifyLEDStateOn: - chass.LocationIndicatorActive = true - chass.IndicatorLED = common.LitIndicatorLED - case hal.IdentifyLEDStateBlinking: - chass.IndicatorLED = common.BlinkingIndicatorLED - case hal.IdentifyLEDStateUnknown: - return fmt.Errorf("unknown LED state:%s", state) - } + state, ok := ledstate["LocationIndicatorActive"] + if !ok { + return hal.IdentifyLEDStateUnknown, fmt.Errorf("ledstate does not contain a LocationIndicatorActive key") } - return nil + + switch state { + case "true": + return hal.IdentifyLEDStateOn, nil + case "false": + return hal.IdentifyLEDStateOff, nil + } + return hal.IdentifyLEDStateUnknown, fmt.Errorf("unknown state:%s", state) } func (c *APIClient) IdentifyLEDOn() error { diff --git a/internal/vendors/dell/dell.go b/internal/vendors/dell/dell.go index fce6b7e..6c446f6 100644 --- a/internal/vendors/dell/dell.go +++ b/internal/vendors/dell/dell.go @@ -41,7 +41,7 @@ func (ob *outBand) BMCConnection() api.OutBandBMCConnection { } func (ob *outBand) Close() { - ob.Redfish.APIClient.Logout() + ob.Redfish.Gofish.Logout() } func (c *bmcConnectionOutBand) BMC() (*api.BMC, error) { diff --git a/internal/vendors/lenovo/lenovo.go b/internal/vendors/lenovo/lenovo.go index 6bba51d..969f872 100644 --- a/internal/vendors/lenovo/lenovo.go +++ b/internal/vendors/lenovo/lenovo.go @@ -57,7 +57,7 @@ func OutBand(r *redfish.APIClient, board *api.Board) hal.OutBand { } func (ob *outBand) Close() { - ob.Redfish.APIClient.Logout() + ob.Redfish.Gofish.Logout() } // InBand diff --git a/internal/vendors/supermicro/supermicro.go b/internal/vendors/supermicro/supermicro.go index b2094ad..67ad35b 100644 --- a/internal/vendors/supermicro/supermicro.go +++ b/internal/vendors/supermicro/supermicro.go @@ -157,7 +157,7 @@ func (c *bmcConnection) User() api.BMCUser { } func (ob *outBand) Close() { - ob.Redfish.APIClient.Logout() + ob.Redfish.Gofish.Logout() } func (c *bmcConnection) Present() bool { diff --git a/internal/vendors/vagrant/vagrant.go b/internal/vendors/vagrant/vagrant.go index 62af4cd..edf93fd 100644 --- a/internal/vendors/vagrant/vagrant.go +++ b/internal/vendors/vagrant/vagrant.go @@ -55,7 +55,7 @@ func OutBand(board *api.Board, ip string, ipmiPort int, user, password string) h } func (ob *outBand) Close() { - ob.Redfish.APIClient.Logout() + ob.Redfish.Gofish.Logout() } // InBand diff --git a/pkg/api/types.go b/pkg/api/types.go index a534467..e4d5f9c 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -36,19 +36,20 @@ const ( // Board raw dmi board information type Board struct { - VM bool - VendorString string - Vendor Vendor - Model string - PartNumber string - SerialNumber string - BiosVersion string - BMC *BMC - BIOS *BIOS - Firmware kernel.FirmwareMode - IndicatorLED string - PowerMetric *PowerMetric - PowerSupplies []PowerSupply + VM bool + VendorString string + Vendor Vendor + Model string + PartNumber string + SerialNumber string + BiosVersion string + RedfishVersion string + BMC *BMC + BIOS *BIOS + Firmware kernel.FirmwareMode + IndicatorLED string + PowerMetric *PowerMetric + PowerSupplies []PowerSupply } type PowerMetric struct {