From 34de7410e79392ccd6fb0afec6ef58ccf4895273 Mon Sep 17 00:00:00 2001 From: Matthew Burns Date: Wed, 29 Nov 2023 09:24:00 -0700 Subject: [PATCH 1/4] Retrieve SEL in both opinionated and raw formats --- .devcontainer/devcontainer.json | 15 +- bmc/sel.go | 128 ++++++++++++++++++ bmc/sel_test.go | 74 ++++++++++ client.go | 14 ++ examples/sel/main.go | 28 +++- go.sum | 9 -- internal/ipmi/ipmi.go | 37 +++++ internal/redfishwrapper/sel.go | 79 +++++++++++ internal/utils.go | 10 ++ providers/ipmitool/ipmitool.go | 10 ++ providers/ipmitool/ipmitool_test.go | 36 +++++ providers/providers.go | 5 +- .../redfish/fixtures/v1/dell/entries.json | 50 +++++++ .../redfish/fixtures/v1/dell/logservices.json | 13 ++ .../fixtures/v1/dell/logservices.sel.json | 22 +++ .../v1/dell/manager.idrac.embedded.1.json | 9 ++ .../redfish/fixtures/v1/dell/managers.json | 13 ++ .../fixtures/v1/dell/selentries/1.json | 20 +++ .../fixtures/v1/dell/selentries/2.json | 20 +++ providers/redfish/main_test.go | 8 ++ providers/redfish/sel.go | 8 ++ providers/redfish/sel_test.go | 29 ++++ 22 files changed, 621 insertions(+), 16 deletions(-) create mode 100644 providers/redfish/fixtures/v1/dell/entries.json create mode 100644 providers/redfish/fixtures/v1/dell/logservices.json create mode 100644 providers/redfish/fixtures/v1/dell/logservices.sel.json create mode 100644 providers/redfish/fixtures/v1/dell/manager.idrac.embedded.1.json create mode 100644 providers/redfish/fixtures/v1/dell/managers.json create mode 100644 providers/redfish/fixtures/v1/dell/selentries/1.json create mode 100644 providers/redfish/fixtures/v1/dell/selentries/2.json create mode 100644 providers/redfish/sel_test.go diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 893a422f..cf9ee8e3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,7 +3,10 @@ { "name": "Go", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/go:1-1.21-bullseye" + // "image": "mcr.microsoft.com/devcontainers/go:1-1.21-bullseye", + "build": { + "dockerfile": "Dockerfile" + }, // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, @@ -15,7 +18,15 @@ // "postCreateCommand": "go version", // Configure tool-specific properties. - // "customizations": {}, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.makefile-tools", + "zxh404.vscode-proto3", + "humao.rest-client" + ] + } + } // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" diff --git a/bmc/sel.go b/bmc/sel.go index 25c6cfc7..8135e06e 100644 --- a/bmc/sel.go +++ b/bmc/sel.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/bmc-toolbox/bmclib/v2/internal" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" ) @@ -12,6 +13,8 @@ import ( // System Event Log Services for related services type SystemEventLog interface { ClearSystemEventLog(ctx context.Context) (err error) + GetSystemEventLog(ctx context.Context) (entries [][]string, err error) + GetSystemEventLogRaw(ctx context.Context) (eventlog string, err error) } type systemEventLogProviders struct { @@ -19,6 +22,14 @@ type systemEventLogProviders struct { systemEventLogProvider SystemEventLog } +type SystemEventLogEntries []SystemEventLogEntry + +type SystemEventLogEntry struct { + ID int32 + Values []string + Raw string +} + func clearSystemEventLog(ctx context.Context, timeout time.Duration, s []systemEventLogProviders) (metadata Metadata, err error) { var metadataLocal Metadata @@ -67,3 +78,120 @@ func ClearSystemEventLogFromInterfaces(ctx context.Context, timeout time.Duratio } return clearSystemEventLog(ctx, timeout, selServices) } + +func getSystemEventLog(ctx context.Context, timeout time.Duration, s []systemEventLogProviders) (sel SystemEventLogEntries, metadata Metadata, err error) { + var selLocal SystemEventLogEntries + var metadataLocal Metadata + + for _, elem := range s { + if elem.systemEventLogProvider == nil { + continue + } + select { + case <-ctx.Done(): + err = multierror.Append(err, ctx.Err()) + + return sel, metadata, err + default: + metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + selRawEntries, selErr := elem.systemEventLogProvider.GetSystemEventLog(ctx) + if selErr != nil { + err = multierror.Append(err, errors.WithMessagef(selErr, "provider: %v", elem.name)) + continue + } + + for i, v := range selRawEntries { + + // In most cases, the first value is the ID, but not always + k, err := internal.ParseInt32(v[0]) + if err != nil { + k = int32(i) + } + + selLocal = append(selLocal, SystemEventLogEntry{ + ID: k, + Values: v, + }) + } + + metadataLocal.SuccessfulProvider = elem.name + return selLocal, metadataLocal, nil + } + + } + + return selLocal, metadataLocal, multierror.Append(err, errors.New("failed to get System Event Log")) +} + +func GetSystemEventLogFromInterfaces(ctx context.Context, timeout time.Duration, generic []interface{}) (sel SystemEventLogEntries, metadata Metadata, err error) { + selServices := make([]systemEventLogProviders, 0) + for _, elem := range generic { + temp := systemEventLogProviders{name: getProviderName(elem)} + switch p := elem.(type) { + case SystemEventLog: + temp.systemEventLogProvider = p + selServices = append(selServices, temp) + default: + e := fmt.Sprintf("not a SystemEventLog service implementation: %T", p) + err = multierror.Append(err, errors.New(e)) + } + } + if len(selServices) == 0 { + return sel, metadata, multierror.Append(err, errors.New("no SystemEventLog implementations found")) + } + return getSystemEventLog(ctx, timeout, selServices) +} + +func getSystemEventLogRaw(ctx context.Context, timeout time.Duration, s []systemEventLogProviders) (eventlog string, metadata Metadata, err error) { + var metadataLocal Metadata + + for _, elem := range s { + if elem.systemEventLogProvider == nil { + continue + } + select { + case <-ctx.Done(): + err = multierror.Append(err, ctx.Err()) + + return eventlog, metadata, err + default: + metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + eventlog, selErr := elem.systemEventLogProvider.GetSystemEventLogRaw(ctx) + if selErr != nil { + err = multierror.Append(err, errors.WithMessagef(selErr, "provider: %v", elem.name)) + continue + } + + metadataLocal.SuccessfulProvider = elem.name + return eventlog, metadataLocal, nil + } + + } + + return eventlog, metadataLocal, multierror.Append(err, errors.New("failed to get System Event Log")) +} + +func GetSystemEventLogRawFromInterfaces(ctx context.Context, timeout time.Duration, generic []interface{}) (eventlog string, metadata Metadata, err error) { + selServices := make([]systemEventLogProviders, 0) + for _, elem := range generic { + temp := systemEventLogProviders{name: getProviderName(elem)} + switch p := elem.(type) { + case SystemEventLog: + temp.systemEventLogProvider = p + selServices = append(selServices, temp) + default: + e := fmt.Sprintf("not a SystemEventLog service implementation: %T", p) + err = multierror.Append(err, errors.New(e)) + } + } + if len(selServices) == 0 { + return eventlog, metadata, multierror.Append(err, errors.New("no SystemEventLog implementations found")) + } + return getSystemEventLogRaw(ctx, timeout, selServices) +} diff --git a/bmc/sel_test.go b/bmc/sel_test.go index 88b10d13..1273e59a 100644 --- a/bmc/sel_test.go +++ b/bmc/sel_test.go @@ -18,6 +18,14 @@ func (m *mockSystemEventLogService) ClearSystemEventLog(ctx context.Context) err return m.err } +func (m *mockSystemEventLogService) GetSystemEventLog(ctx context.Context) (entries [][]string, err error) { + return nil, m.err +} + +func (m *mockSystemEventLogService) GetSystemEventLogRaw(ctx context.Context) (eventlog string, err error) { + return "", m.err +} + func (m *mockSystemEventLogService) Name() string { return m.name } @@ -59,3 +67,69 @@ func TestClearSystemEventLogFromInterfaces(t *testing.T) { assert.Nil(t, err) assert.Equal(t, mockService.name, metadata.SuccessfulProvider) } + +func TestGetSystemEventLog(t *testing.T) { + ctx := context.Background() + timeout := 1 * time.Second + + // Test with a mock SystemEventLogService that returns nil + mockService := &mockSystemEventLogService{name: "mock1", err: nil} + _, _, err := getSystemEventLog(ctx, timeout, []systemEventLogProviders{{name: mockService.name, systemEventLogProvider: mockService}}) + assert.Nil(t, err) + + // Test with a mock SystemEventLogService that returns an error + mockService = &mockSystemEventLogService{name: "mock2", err: errors.New("mock error")} + _, _, err = getSystemEventLog(ctx, timeout, []systemEventLogProviders{{name: mockService.name, systemEventLogProvider: mockService}}) + assert.NotNil(t, err) +} + +func TestGetSystemEventLogFromInterfaces(t *testing.T) { + ctx := context.Background() + timeout := 1 * time.Second + + // Test with an empty slice + _, _, err := GetSystemEventLogFromInterfaces(ctx, timeout, []interface{}{}) + assert.NotNil(t, err) + + // Test with a slice containing a non-SystemEventLog object + _, _, err = GetSystemEventLogFromInterfaces(ctx, timeout, []interface{}{"not a SystemEventLog Service"}) + assert.NotNil(t, err) + + // Test with a slice containing a mock SystemEventLogService that returns nil + mockService := &mockSystemEventLogService{name: "mock1"} + _, _, err = GetSystemEventLogFromInterfaces(ctx, timeout, []interface{}{mockService}) + assert.Nil(t, err) +} + +func TestGetSystemEventLogRaw(t *testing.T) { + ctx := context.Background() + timeout := 1 * time.Second + + // Test with a mock SystemEventLogService that returns nil + mockService := &mockSystemEventLogService{name: "mock1", err: nil} + _, _, err := getSystemEventLogRaw(ctx, timeout, []systemEventLogProviders{{name: mockService.name, systemEventLogProvider: mockService}}) + assert.Nil(t, err) + + // Test with a mock SystemEventLogService that returns an error + mockService = &mockSystemEventLogService{name: "mock2", err: errors.New("mock error")} + _, _, err = getSystemEventLogRaw(ctx, timeout, []systemEventLogProviders{{name: mockService.name, systemEventLogProvider: mockService}}) + assert.NotNil(t, err) +} + +func TestGetSystemEventLogRawFromInterfaces(t *testing.T) { + ctx := context.Background() + timeout := 1 * time.Second + + // Test with an empty slice + _, _, err := GetSystemEventLogRawFromInterfaces(ctx, timeout, []interface{}{}) + assert.NotNil(t, err) + + // Test with a slice containing a non-SystemEventLog object + _, _, err = GetSystemEventLogRawFromInterfaces(ctx, timeout, []interface{}{"not a SystemEventLog Service"}) + assert.NotNil(t, err) + + // Test with a slice containing a mock SystemEventLogService that returns nil + mockService := &mockSystemEventLogService{name: "mock1"} + _, _, err = GetSystemEventLogRawFromInterfaces(ctx, timeout, []interface{}{mockService}) + assert.Nil(t, err) +} diff --git a/client.go b/client.go index fe9b3fd7..dbc0d542 100644 --- a/client.go +++ b/client.go @@ -656,3 +656,17 @@ func (c *Client) FirmwareInstallUploaded(ctx context.Context, component, uploadV return installTaskID, err } + +// GetSystemEventLog queries for the SEL and returns the entries in an opinionated format. +func (c *Client) GetSystemEventLog(ctx context.Context) (entries bmc.SystemEventLogEntries, err error) { + entries, metadata, err := bmc.GetSystemEventLogFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces()) + c.setMetadata(metadata) + return entries, err +} + +// GetSystemEventLogRaw queries for the SEL and returns the raw response. +func (c *Client) GetSystemEventLogRaw(ctx context.Context) (eventlog string, err error) { + eventlog, metadata, err := bmc.GetSystemEventLogRawFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces()) + c.setMetadata(metadata) + return eventlog, err +} diff --git a/examples/sel/main.go b/examples/sel/main.go index 114d6496..031948ae 100644 --- a/examples/sel/main.go +++ b/examples/sel/main.go @@ -19,6 +19,7 @@ func main() { host := flag.String("host", "", "BMC hostname to connect to") withSecureTLS := flag.Bool("secure-tls", false, "Enable secure TLS") certPoolFile := flag.String("cert-pool", "", "Path to an file containing x509 CAs. An empty string uses the system CAs. Only takes effect when --secure-tls=true") + action := flag.String("action", "get", "Action to perform on the System Event Log (clear|get)") flag.Parse() l := logrus.New() @@ -60,9 +61,28 @@ func main() { } defer cl.Close(ctx) - err = cl.ClearSystemEventLog(ctx) - if err != nil { - l.WithError(err).Fatal(err, "failed to clear System Event Log") + if *action == "get" { + entries, err := cl.GetSystemEventLog(ctx) + if err != nil { + l.WithError(err).Fatal(err, "failed to get System Event Log") + } + l.Info("System Event Log entries", "entries", entries) + return + } else if *action == "get-raw" { + eventlog, err := cl.GetSystemEventLogRaw(ctx) + if err != nil { + l.WithError(err).Fatal(err, "failed to get System Event Log Raw") + } + l.Info("System Event Log", "eventlog", eventlog) + return + } else if *action == "clear" { + + err = cl.ClearSystemEventLog(ctx) + if err != nil { + l.WithError(err).Fatal(err, "failed to clear System Event Log") + } + l.Info("System Event Log cleared") + } else { + l.Fatal("invalid action") } - l.Info("System Event Log cleared") } diff --git a/go.sum b/go.sum index c3e42e8c..7f641714 100644 --- a/go.sum +++ b/go.sum @@ -73,16 +73,10 @@ go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -91,11 +85,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/internal/ipmi/ipmi.go b/internal/ipmi/ipmi.go index 5161b7dd..4bc0f94c 100644 --- a/internal/ipmi/ipmi.go +++ b/internal/ipmi/ipmi.go @@ -383,3 +383,40 @@ func (i *Ipmi) ClearSystemEventLog(ctx context.Context) (err error) { _, err = i.run(ctx, []string{"sel", "clear"}) return err } + +// GetSystemEventLog returns the system event log entries in ID, Timestamp, Description, Message format +func (i *Ipmi) GetSystemEventLog(ctx context.Context) (entries [][]string, err error) { + output, err := i.GetSystemEventLogRaw(ctx) + if err != nil { + return nil, errors.Wrap(err, "error getting system event log") + } + + scanner := bufio.NewScanner(strings.NewReader(output)) + for scanner.Scan() { + line := strings.Split(scanner.Text(), "|") + if len(line) < 3 { + continue + } + if line[0] == "ID" { + continue + } + for i := range line { + line[i] = strings.TrimSpace(line[i]) + } + + // ID, Timestamp (date time), Description, Message (message : assertion) + entries = append(entries, []string{line[0], fmt.Sprintf("%s %s", line[1], line[2]), line[3], fmt.Sprintf("%s : %s", line[4], line[5])}) + } + + return entries, nil +} + +// GetSystemEventLogRaw returns the raw SEL output +func (i *Ipmi) GetSystemEventLogRaw(ctx context.Context) (eventlog string, err error) { + output, err := i.run(ctx, []string{"sel", "list"}) + if err != nil { + return "", errors.Wrap(err, "error getting system event log") + } + + return output, nil +} diff --git a/internal/redfishwrapper/sel.go b/internal/redfishwrapper/sel.go index 78798735..41125421 100644 --- a/internal/redfishwrapper/sel.go +++ b/internal/redfishwrapper/sel.go @@ -2,9 +2,12 @@ package redfishwrapper import ( "context" + "encoding/json" + "log" bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors" "github.com/pkg/errors" + "github.com/stmcginnis/gofish/redfish" ) // ClearSystemEventLog clears all of the LogServices logs @@ -34,3 +37,79 @@ func (c *Client) ClearSystemEventLog(ctx context.Context) (err error) { return nil } + +// GetSystemEventLog returns the SystemEventLogEntries +func (c *Client) GetSystemEventLog(ctx context.Context) (entries [][]string, err error) { + if err := c.SessionActive(); err != nil { + return nil, errors.Wrap(bmclibErrs.ErrNotAuthenticated, err.Error()) + } + + managers, err := c.client.Service.Managers() + if err != nil { + return nil, err + } + + for _, m := range managers { + logServices, err := m.LogServices() + if err != nil { + return nil, err + } + + log.Println(len(logServices)) + + for _, logService := range logServices { + lentries, err := logService.Entries() + if err != nil { + return nil, err + } + + for _, entry := range lentries { + entries = append(entries, []string{ + entry.ID, + entry.Created, + entry.Description, + entry.Message, + }) + } + } + } + + return entries, nil +} + +// GetSystemEventLogRaw returns the raw SEL +func (c *Client) GetSystemEventLogRaw(ctx context.Context) (eventlog string, err error) { + var allEntries []*redfish.LogEntry + + if err := c.SessionActive(); err != nil { + return "", errors.Wrap(bmclibErrs.ErrNotAuthenticated, err.Error()) + } + + managers, err := c.client.Service.Managers() + if err != nil { + return "", err + } + + for _, m := range managers { + logServices, err := m.LogServices() + if err != nil { + return "", err + } + + for _, logService := range logServices { + lentries, err := logService.Entries() + if err != nil { + return "", err + } + + allEntries = append(allEntries, lentries...) + } + } + + rawEntries, err := json.Marshal(allEntries) + if err != nil { + return "", err + } + + return string(rawEntries), nil +} diff --git a/internal/utils.go b/internal/utils.go index a8ce848c..c73d6b19 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -1,6 +1,7 @@ package internal import ( + "fmt" "unicode" ) @@ -21,3 +22,12 @@ func StringInSlice(str string, sl []string) bool { } return false } + +func ParseInt32(i32 string) (int32, error) { + var i int32 + _, err := fmt.Sscanf(i32, "%d", &i) + if err != nil { + return 0, err + } + return i, nil +} diff --git a/providers/ipmitool/ipmitool.go b/providers/ipmitool/ipmitool.go index 90aedbd4..d8b5d9b2 100644 --- a/providers/ipmitool/ipmitool.go +++ b/providers/ipmitool/ipmitool.go @@ -28,6 +28,8 @@ var ( providers.FeatureBmcReset, providers.FeatureBootDeviceSet, providers.FeatureClearSystemEventLog, + providers.FeatureGetSystemEventLog, + providers.FeatureGetSystemEventLogRaw, } ) @@ -185,3 +187,11 @@ func (c *Conn) PowerSet(ctx context.Context, state string) (ok bool, err error) func (c *Conn) ClearSystemEventLog(ctx context.Context) (err error) { return c.ipmitool.ClearSystemEventLog(ctx) } + +func (c *Conn) GetSystemEventLog(ctx context.Context) (entries [][]string, err error) { + return c.ipmitool.GetSystemEventLog(ctx) +} + +func (c *Conn) GetSystemEventLogRaw(ctx context.Context) (eventlog string, err error) { + return c.ipmitool.GetSystemEventLogRaw(ctx) +} diff --git a/providers/ipmitool/ipmitool_test.go b/providers/ipmitool/ipmitool_test.go index 0e9589c2..241503f1 100644 --- a/providers/ipmitool/ipmitool_test.go +++ b/providers/ipmitool/ipmitool_test.go @@ -124,3 +124,39 @@ func TestSystemEventLogClear(t *testing.T) { t.Log("System Event Log cleared") t.Fatal() } + +func TestSystemEventLogGet(t *testing.T) { + t.Skip("need real ipmi server") + host := "127.0.0.1" + port := "623" + user := "ADMIN" + pass := "ADMIN" + i, err := New(host, user, pass, WithPort(port), WithLogger(logging.DefaultLogger())) + if err != nil { + t.Fatal(err) + } + entries, err := i.GetSystemEventLog(context.Background()) + if err != nil { + t.Fatal(err) + } + t.Log(entries) + t.Fatal() +} + +func TestSystemEventLogGetRaw(t *testing.T) { + t.Skip("need real ipmi server") + host := "127.0.0.1" + port := "623" + user := "ADMIN" + pass := "ADMIN" + i, err := New(host, user, pass, WithPort(port), WithLogger(logging.DefaultLogger())) + if err != nil { + t.Fatal(err) + } + eventlog, err := i.GetSystemEventLogRaw(context.Background()) + if err != nil { + t.Fatal(err) + } + t.Log(eventlog) + t.Fatal() +} diff --git a/providers/providers.go b/providers/providers.go index b7eb0518..c59e29cf 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -42,7 +42,10 @@ const ( FeatureScreenshot registrar.Feature = "screenshot" // FeatureClearSystemEventLog means an implementation that clears the BMC System Event Log (SEL) FeatureClearSystemEventLog registrar.Feature = "clearsystemeventlog" - + // FeatureGetSystemEventLog means an implementation that returns the BMC System Event Log (SEL) + FeatureGetSystemEventLog registrar.Feature = "getsystemeventlog" + // FeatureGetSystemEventLogRaw means an implementation that returns the BMC System Event Log (SEL) in raw format + FeatureGetSystemEventLogRaw registrar.Feature = "getsystemeventlograw" // FeatureFirmwareInstallSteps means an implementation returns the steps part of the firmware update process. FeatureFirmwareInstallSteps registrar.Feature = "firmwareinstallactions" diff --git a/providers/redfish/fixtures/v1/dell/entries.json b/providers/redfish/fixtures/v1/dell/entries.json new file mode 100644 index 00000000..abcbbf3a --- /dev/null +++ b/providers/redfish/fixtures/v1/dell/entries.json @@ -0,0 +1,50 @@ +{ + "@odata.context": "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries", + "@odata.type": "#LogEntryCollection.LogEntryCollection", + "Description": "System Event Logs for this manager", + "Members": [ + { + "@odata.context": "/redfish/v1/$metadata#LogEntry.LogEntry", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries/2", + "@odata.type": "#LogEntry.v1_6_1.LogEntry", + "Created": "2023-01-01T00:00:00-00:00", + "Description": "Log Entry 2", + "EntryCode": "Assert", + "EntryType": "SEL", + "GeneratorId": "0x0001", + "Id": "1", + "Links": {}, + "Message": "OEM software event.", + "MessageArgs": [], + "MessageArgs@odata.count": 0, + "MessageId": "d000000", + "Name": "Log Entry 2", + "SensorNumber": 999, + "SensorType": null, + "Severity": "OK" + }, + { + "@odata.context": "/redfish/v1/$metadata#LogEntry.LogEntry", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries/1", + "@odata.type": "#LogEntry.v1_6_1.LogEntry", + "Created": "2023-01-01T00:00:00-00:00", + "Description": "Log Entry 1", + "EntryCode": "Deassert", + "EntryType": "SEL", + "GeneratorId": "0x0001", + "Id": "1", + "Links": {}, + "Message": "OEM software event.", + "MessageArgs": [], + "MessageArgs@odata.count": 0, + "MessageId": "d000000", + "Name": "Log Entry 1", + "SensorNumber": 999, + "SensorType": null, + "Severity": "OK" + } + ], + "Members@odata.count": 2, + "Name": "Log Entry Collection" +} diff --git a/providers/redfish/fixtures/v1/dell/logservices.json b/providers/redfish/fixtures/v1/dell/logservices.json new file mode 100644 index 00000000..a4518c5c --- /dev/null +++ b/providers/redfish/fixtures/v1/dell/logservices.json @@ -0,0 +1,13 @@ +{ + "@odata.context": "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices", + "@odata.type": "#LogServiceCollection.LogServiceCollection", + "Description": "Collection of Log Services for this Manager", + "Members": [ + { + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel" + } + ], + "Members@odata.count": 3, + "Name": "Log Service Collection" +} diff --git a/providers/redfish/fixtures/v1/dell/logservices.sel.json b/providers/redfish/fixtures/v1/dell/logservices.sel.json new file mode 100644 index 00000000..e974aa17 --- /dev/null +++ b/providers/redfish/fixtures/v1/dell/logservices.sel.json @@ -0,0 +1,22 @@ +{ + "@odata.context": "/redfish/v1/$metadata#LogService.LogService", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel", + "@odata.type": "#LogService.v1_1_3.LogService", + "Actions": { + "#LogService.ClearLog": { + "target": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Actions/LogService.ClearLog" + } + }, + "DateTime": "2023-01-01T00:00:00-00:00", + "DateTimeLocalOffset": "00:00", + "Description": "SEL Log Service", + "Entries": { + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries" + }, + "Id": "Sel", + "LogEntryType": "SEL", + "MaxNumberOfRecords": 1024, + "Name": "SEL Log Service", + "OverWritePolicy": "WrapsWhenFull", + "ServiceEnabled": true +} diff --git a/providers/redfish/fixtures/v1/dell/manager.idrac.embedded.1.json b/providers/redfish/fixtures/v1/dell/manager.idrac.embedded.1.json new file mode 100644 index 00000000..a78d532b --- /dev/null +++ b/providers/redfish/fixtures/v1/dell/manager.idrac.embedded.1.json @@ -0,0 +1,9 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Manager.Manager", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1", + "@odata.type": "#Manager.v1_9_0.Manager", + "LogServices": { + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices" + }, + "Comment": "This isn't part of the schema - this file contains only the bare minimum to make the schema validator happy" +} diff --git a/providers/redfish/fixtures/v1/dell/managers.json b/providers/redfish/fixtures/v1/dell/managers.json new file mode 100644 index 00000000..df2fea27 --- /dev/null +++ b/providers/redfish/fixtures/v1/dell/managers.json @@ -0,0 +1,13 @@ +{ + "@odata.context": "/redfish/v1/$metadata#ManagerCollection.ManagerCollection", + "@odata.id": "/redfish/v1/Managers", + "@odata.type": "#ManagerCollection.ManagerCollection", + "Description": "BMC", + "Members": [ + { + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1" + } + ], + "Members@odata.count": 1, + "Name": "Manager" +} diff --git a/providers/redfish/fixtures/v1/dell/selentries/1.json b/providers/redfish/fixtures/v1/dell/selentries/1.json new file mode 100644 index 00000000..b6adc669 --- /dev/null +++ b/providers/redfish/fixtures/v1/dell/selentries/1.json @@ -0,0 +1,20 @@ +{ + "@odata.context": "/redfish/v1/$metadata#LogEntry.LogEntry", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries/1", + "@odata.type": "#LogEntry.v1_6_1.LogEntry", + "Created": "2023-01-01T00:00:00-00:00", + "Description": "Log Entry 1", + "EntryCode": "Deassert", + "EntryType": "SEL", + "GeneratorId": "0x0001", + "Id": "1", + "Links": {}, + "Message": "OEM software event.", + "MessageArgs": [], + "MessageArgs@odata.count": 0, + "MessageId": "d000000", + "Name": "Log Entry 1", + "SensorNumber": 999, + "SensorType": null, + "Severity": "OK" +} diff --git a/providers/redfish/fixtures/v1/dell/selentries/2.json b/providers/redfish/fixtures/v1/dell/selentries/2.json new file mode 100644 index 00000000..3812f7a6 --- /dev/null +++ b/providers/redfish/fixtures/v1/dell/selentries/2.json @@ -0,0 +1,20 @@ +{ + "@odata.context": "/redfish/v1/$metadata#LogEntry.LogEntry", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries/2", + "@odata.type": "#LogEntry.v1_6_1.LogEntry", + "Created": "2023-01-01T00:00:00-00:00", + "Description": "Log Entry 2", + "EntryCode": "Assert", + "EntryType": "SEL", + "GeneratorId": "0x0001", + "Id": "1", + "Links": {}, + "Message": "OEM software event.", + "MessageArgs": [], + "MessageArgs@odata.count": 0, + "MessageId": "d000000", + "Name": "Log Entry 2", + "SensorNumber": 999, + "SensorType": null, + "Severity": "OK" +} diff --git a/providers/redfish/main_test.go b/providers/redfish/main_test.go index 379292ba..53da2151 100644 --- a/providers/redfish/main_test.go +++ b/providers/redfish/main_test.go @@ -30,6 +30,14 @@ func jsonResponse(endpoint string) []byte { "/redfish/v1/UpdateService": fixturesDir + "/v1/updateservice.json", "/redfish/v1/Systems": fixturesDir + "/v1/systems.json", + "/redfish/v1/Managers": fixturesDir + "/v1/dell/managers.json", + "/redfish/v1/Managers/iDRAC.Embedded.1": fixturesDir + "/v1/dell/manager.idrac.embedded.1.json", + "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices": fixturesDir + "/v1/dell/logservices.json", + "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel": fixturesDir + "/v1/dell/logservices.sel.json", + "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries": fixturesDir + "/v1/dell/entries.json", + "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries/1": fixturesDir + "/v1/dell/selentries/1.json", + "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries/2": fixturesDir + "/v1/dell/selentries/2.json", + "/redfish/v1/Systems/System.Embedded.1": fixturesDir + "/v1/dell/system.embedded.1.json", "/redfish/v1/Systems/System.Embedded.1/Bios": fixturesDir + "/v1/dell/bios.json", "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/Jobs?$expand=*($levels=1)": fixturesDir + "/v1/dell/jobs.json", diff --git a/providers/redfish/sel.go b/providers/redfish/sel.go index 9d516243..82adda44 100644 --- a/providers/redfish/sel.go +++ b/providers/redfish/sel.go @@ -5,3 +5,11 @@ import "context" func (c *Conn) ClearSystemEventLog(ctx context.Context) (err error) { return c.redfishwrapper.ClearSystemEventLog(ctx) } + +func (c *Conn) GetSystemEventLog(ctx context.Context) (entries [][]string, err error) { + return c.redfishwrapper.GetSystemEventLog(ctx) +} + +func (c *Conn) GetSystemEventLogRaw(ctx context.Context) (eventlog string, err error) { + return c.redfishwrapper.GetSystemEventLogRaw(ctx) +} diff --git a/providers/redfish/sel_test.go b/providers/redfish/sel_test.go new file mode 100644 index 00000000..1f41ceb7 --- /dev/null +++ b/providers/redfish/sel_test.go @@ -0,0 +1,29 @@ +package redfish + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Write tests for GetSystemEventLog +func Test_GetSystemEventLog(t *testing.T) { + entries, err := mockClient.GetSystemEventLog(context.TODO()) + if err != nil { + t.Fatal(err) + } + + assert.NotNil(t, entries) + assert.Equal(t, 2, len(entries)) +} + +// Write tests for GetSystemEventLogRaw +func Test_GetSystemEventLogRaw(t *testing.T) { + eventlog, err := mockClient.GetSystemEventLogRaw(context.Background()) + if err != nil { + t.Fatal(err) + } + + assert.NotNil(t, eventlog) +} From d73562b91794094742dbbc48d40a993ceddf2027 Mon Sep 17 00:00:00 2001 From: Matthew Burns Date: Wed, 29 Nov 2023 09:39:24 -0700 Subject: [PATCH 2/4] Fix the conflict Clipped one line too high when fixing the conflicts earlier --- providers/redfish/main_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/providers/redfish/main_test.go b/providers/redfish/main_test.go index 60c7c8b3..2a71b818 100644 --- a/providers/redfish/main_test.go +++ b/providers/redfish/main_test.go @@ -32,6 +32,7 @@ func jsonResponse(endpoint string) []byte { "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel": fixturesDir + "/v1/dell/logservices.sel.json", "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries": fixturesDir + "/v1/dell/entries.json", "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries/1": fixturesDir + "/v1/dell/selentries/1.json", + "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries/2": fixturesDir + "/v1/dell/selentries/2.json", "/redfish/v1/": fixturesDir + "/v1/serviceroot.json", "/redfish/v1/UpdateService": fixturesDir + "/v1/updateservice.json", From ebab84a09ab3f841f179debb9d9b037d194be982 Mon Sep 17 00:00:00 2001 From: Matthew Burns Date: Wed, 29 Nov 2023 10:41:53 -0700 Subject: [PATCH 3/4] Updated devcontainer --- .devcontainer/Dockerfile | 4 ++++ bmc/sel.go | 30 ++++-------------------------- internal/utils.go | 10 ---------- 3 files changed, 8 insertions(+), 36 deletions(-) create mode 100644 .devcontainer/Dockerfile diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..11d9d578 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,4 @@ +FROM mcr.microsoft.com/devcontainers/go:1-1.21-bullseye +RUN apt update +RUN apt install ipmitool -y + diff --git a/bmc/sel.go b/bmc/sel.go index 8135e06e..002a25d1 100644 --- a/bmc/sel.go +++ b/bmc/sel.go @@ -5,7 +5,6 @@ import ( "fmt" "time" - "github.com/bmc-toolbox/bmclib/v2/internal" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" ) @@ -22,13 +21,7 @@ type systemEventLogProviders struct { systemEventLogProvider SystemEventLog } -type SystemEventLogEntries []SystemEventLogEntry - -type SystemEventLogEntry struct { - ID int32 - Values []string - Raw string -} +type SystemEventLogEntries [][]string func clearSystemEventLog(ctx context.Context, timeout time.Duration, s []systemEventLogProviders) (metadata Metadata, err error) { var metadataLocal Metadata @@ -80,7 +73,6 @@ func ClearSystemEventLogFromInterfaces(ctx context.Context, timeout time.Duratio } func getSystemEventLog(ctx context.Context, timeout time.Duration, s []systemEventLogProviders) (sel SystemEventLogEntries, metadata Metadata, err error) { - var selLocal SystemEventLogEntries var metadataLocal Metadata for _, elem := range s { @@ -97,33 +89,19 @@ func getSystemEventLog(ctx context.Context, timeout time.Duration, s []systemEve ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - selRawEntries, selErr := elem.systemEventLogProvider.GetSystemEventLog(ctx) + sel, selErr := elem.systemEventLogProvider.GetSystemEventLog(ctx) if selErr != nil { err = multierror.Append(err, errors.WithMessagef(selErr, "provider: %v", elem.name)) continue } - for i, v := range selRawEntries { - - // In most cases, the first value is the ID, but not always - k, err := internal.ParseInt32(v[0]) - if err != nil { - k = int32(i) - } - - selLocal = append(selLocal, SystemEventLogEntry{ - ID: k, - Values: v, - }) - } - metadataLocal.SuccessfulProvider = elem.name - return selLocal, metadataLocal, nil + return sel, metadataLocal, nil } } - return selLocal, metadataLocal, multierror.Append(err, errors.New("failed to get System Event Log")) + return nil, metadataLocal, multierror.Append(err, errors.New("failed to get System Event Log")) } func GetSystemEventLogFromInterfaces(ctx context.Context, timeout time.Duration, generic []interface{}) (sel SystemEventLogEntries, metadata Metadata, err error) { diff --git a/internal/utils.go b/internal/utils.go index c73d6b19..a8ce848c 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -1,7 +1,6 @@ package internal import ( - "fmt" "unicode" ) @@ -22,12 +21,3 @@ func StringInSlice(str string, sl []string) bool { } return false } - -func ParseInt32(i32 string) (int32, error) { - var i int32 - _, err := fmt.Sscanf(i32, "%d", &i) - if err != nil { - return 0, err - } - return i, nil -} From de61fa19a1eb6661ad4c505a75619abcda863cc1 Mon Sep 17 00:00:00 2001 From: Matthew Burns Date: Wed, 6 Dec 2023 09:02:34 -0700 Subject: [PATCH 4/4] Incorporated changes from review, added test --- client.go | 6 ++++++ examples/sel/main.go | 11 ++++++----- go.sum | 2 -- internal/ipmi/ipmi.go | 15 +++++++++++---- internal/redfishwrapper/sel.go | 3 --- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/client.go b/client.go index 6e5f6b7c..15712f77 100644 --- a/client.go +++ b/client.go @@ -670,6 +670,9 @@ func (c *Client) FirmwareInstallUploadAndInitiate(ctx context.Context, component // GetSystemEventLog queries for the SEL and returns the entries in an opinionated format. func (c *Client) GetSystemEventLog(ctx context.Context) (entries bmc.SystemEventLogEntries, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "GetSystemEventLog") + defer span.End() + entries, metadata, err := bmc.GetSystemEventLogFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces()) c.setMetadata(metadata) return entries, err @@ -677,6 +680,9 @@ func (c *Client) GetSystemEventLog(ctx context.Context) (entries bmc.SystemEvent // GetSystemEventLogRaw queries for the SEL and returns the raw response. func (c *Client) GetSystemEventLogRaw(ctx context.Context) (eventlog string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "GetSystemEventLogRaw") + defer span.End() + eventlog, metadata, err := bmc.GetSystemEventLogRawFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces()) c.setMetadata(metadata) return eventlog, err diff --git a/examples/sel/main.go b/examples/sel/main.go index 031948ae..e5bd97ca 100644 --- a/examples/sel/main.go +++ b/examples/sel/main.go @@ -61,28 +61,29 @@ func main() { } defer cl.Close(ctx) - if *action == "get" { + switch *action { + case "get": entries, err := cl.GetSystemEventLog(ctx) if err != nil { l.WithError(err).Fatal(err, "failed to get System Event Log") } l.Info("System Event Log entries", "entries", entries) return - } else if *action == "get-raw" { + case "get-raw": eventlog, err := cl.GetSystemEventLogRaw(ctx) if err != nil { l.WithError(err).Fatal(err, "failed to get System Event Log Raw") } l.Info("System Event Log", "eventlog", eventlog) return - } else if *action == "clear" { - + case "clear": err = cl.ClearSystemEventLog(ctx) if err != nil { l.WithError(err).Fatal(err, "failed to clear System Event Log") } l.Info("System Event Log cleared") - } else { + return + default: l.Fatal("invalid action") } } diff --git a/go.sum b/go.sum index dd41267a..67c09659 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,6 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stmcginnis/gofish v0.14.1-0.20231018151402-dddaff9168fb h1:+BpzUuFIEAs71bTshedsUHAAq21VZWvuokbN9ABEQeQ= -github.com/stmcginnis/gofish v0.14.1-0.20231018151402-dddaff9168fb/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCboggsu8CznI= github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91 h1:WmABtU8y6kTgzoVUn3FWCQGAfyodve3uz3xno28BrRs= github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCboggsu8CznI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/internal/ipmi/ipmi.go b/internal/ipmi/ipmi.go index 4bc0f94c..427a76ae 100644 --- a/internal/ipmi/ipmi.go +++ b/internal/ipmi/ipmi.go @@ -391,10 +391,18 @@ func (i *Ipmi) GetSystemEventLog(ctx context.Context) (entries [][]string, err e return nil, errors.Wrap(err, "error getting system event log") } - scanner := bufio.NewScanner(strings.NewReader(output)) + entries = parseSystemEventLog(output) + + return entries, nil +} + +// parseSystemEventLogRaw parses the raw output of the system event log. Helper +// function for GetSystemEventLog to make testing the parser easier. +func parseSystemEventLog(raw string) (entries [][]string) { + scanner := bufio.NewScanner(strings.NewReader(raw)) for scanner.Scan() { line := strings.Split(scanner.Text(), "|") - if len(line) < 3 { + if len(line) < 6 { continue } if line[0] == "ID" { @@ -403,12 +411,11 @@ func (i *Ipmi) GetSystemEventLog(ctx context.Context) (entries [][]string, err e for i := range line { line[i] = strings.TrimSpace(line[i]) } - // ID, Timestamp (date time), Description, Message (message : assertion) entries = append(entries, []string{line[0], fmt.Sprintf("%s %s", line[1], line[2]), line[3], fmt.Sprintf("%s : %s", line[4], line[5])}) } - return entries, nil + return entries } // GetSystemEventLogRaw returns the raw SEL output diff --git a/internal/redfishwrapper/sel.go b/internal/redfishwrapper/sel.go index 41125421..f663de01 100644 --- a/internal/redfishwrapper/sel.go +++ b/internal/redfishwrapper/sel.go @@ -3,7 +3,6 @@ package redfishwrapper import ( "context" "encoding/json" - "log" bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors" "github.com/pkg/errors" @@ -55,8 +54,6 @@ func (c *Client) GetSystemEventLog(ctx context.Context) (entries [][]string, err return nil, err } - log.Println(len(logServices)) - for _, logService := range logServices { lentries, err := logService.Entries() if err != nil {