Skip to content

Commit

Permalink
Add SEL Clear Support (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Sep 23, 2023
2 parents 426d0de + 8712fb6 commit ba1367e
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"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"

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "go version",

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
69 changes: 69 additions & 0 deletions bmc/sel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package bmc

import (
"context"
"fmt"
"time"

"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
)

// System Event Log Services for related services
type SystemEventLog interface {
ClearSystemEventLog(ctx context.Context) (err error)
}

type systemEventLogProviders struct {
name string
systemEventLogProvider SystemEventLog
}

func clearSystemEventLog(ctx context.Context, timeout time.Duration, s []systemEventLogProviders) (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 metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
selErr := elem.systemEventLogProvider.ClearSystemEventLog(ctx)
if selErr != nil {
err = multierror.Append(err, errors.WithMessagef(selErr, "provider: %v", elem.name))
continue
}
metadataLocal.SuccessfulProvider = elem.name
return metadataLocal, nil
}

}

return metadataLocal, multierror.Append(err, errors.New("failed to reset System Event Log"))
}

func ClearSystemEventLogFromInterfaces(ctx context.Context, timeout time.Duration, generic []interface{}) (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 metadata, multierror.Append(err, errors.New("no SystemEventLog implementations found"))
}
return clearSystemEventLog(ctx, timeout, selServices)
}
61 changes: 61 additions & 0 deletions bmc/sel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package bmc

import (
"context"
"testing"
"time"

"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

type mockSystemEventLogService struct {
name string
err error
}

func (m *mockSystemEventLogService) ClearSystemEventLog(ctx context.Context) error {
return m.err
}

func (m *mockSystemEventLogService) Name() string {
return m.name
}

func TestClearSystemEventLog(t *testing.T) {
ctx := context.Background()
timeout := 1 * time.Second

// Test with a mock SystemEventLogService that returns nil
mockService := &mockSystemEventLogService{name: "mock1", err: nil}
metadata, err := clearSystemEventLog(ctx, timeout, []systemEventLogProviders{{name: mockService.name, systemEventLogProvider: mockService}})
assert.Nil(t, err)
assert.Equal(t, mockService.name, metadata.SuccessfulProvider)

// Test with a mock SystemEventLogService that returns an error
mockService = &mockSystemEventLogService{name: "mock2", err: errors.New("mock error")}
metadata, err = clearSystemEventLog(ctx, timeout, []systemEventLogProviders{{name: mockService.name, systemEventLogProvider: mockService}})
assert.NotNil(t, err)
assert.NotEqual(t, mockService.name, metadata.SuccessfulProvider)
}

func TestClearSystemEventLogFromInterfaces(t *testing.T) {
ctx := context.Background()
timeout := 1 * time.Second

// Test with an empty slice
metadata, err := ClearSystemEventLogFromInterfaces(ctx, timeout, []interface{}{})
assert.NotNil(t, err)
assert.Empty(t, metadata.SuccessfulProvider)

// Test with a slice containing a non-SystemEventLog object
metadata, err = ClearSystemEventLogFromInterfaces(ctx, timeout, []interface{}{"not a SystemEventLog Service"})
assert.NotNil(t, err)
assert.Empty(t, metadata.SuccessfulProvider)

// Test with a slice containing a mock SystemEventLogService that returns nil
mockService := &mockSystemEventLogService{name: "mock1"}
metadata, err = ClearSystemEventLogFromInterfaces(ctx, timeout, []interface{}{mockService})
assert.Nil(t, err)
assert.Equal(t, mockService.name, metadata.SuccessfulProvider)
}
6 changes: 6 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,9 @@ func (c *Client) Screenshot(ctx context.Context) (image []byte, fileType string,

return image, fileType, err
}

func (c *Client) ClearSystemEventLog(ctx context.Context) (err error) {
metadata, err := bmc.ClearSystemEventLogFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces())
c.setMetadata(metadata)
return err
}
68 changes: 68 additions & 0 deletions examples/sel/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"context"
"crypto/x509"
"flag"
"io/ioutil"
"time"

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

func main() {
user := flag.String("user", "", "Username to login with")
pass := flag.String("password", "", "Username to login with")
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")
flag.Parse()

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

if *host == "" || *user == "" || *pass == "" {
l.Fatal("required host/user/pass parameters not defined")
}

clientOpts := []bmclib.Option{
bmclib.WithLogger(logger),
bmclib.WithRedfishUseBasicAuth(true),
}

if *withSecureTLS {
var pool *x509.CertPool
if *certPoolFile != "" {
pool = x509.NewCertPool()
data, err := ioutil.ReadFile(*certPoolFile)
if err != nil {
l.Fatal(err)
}
pool.AppendCertsFromPEM(data)
}
// a nil pool uses the system certs
clientOpts = append(clientOpts, bmclib.WithSecureTLS(pool))
}

cl := bmclib.NewClient(*host, *user, *pass, clientOpts...)
cl.Registry.Drivers = cl.Registry.Supports(providers.FeatureClearSystemEventLog)

ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()

err := cl.Open(ctx)
if err != nil {
l.WithError(err).Fatal(err, "BMC login failed")
}
defer cl.Close(ctx)

err = cl.ClearSystemEventLog(ctx)
if err != nil {
l.WithError(err).Fatal(err, "failed to clear System Event Log")
}
l.Info("System Event Log cleared")
}
6 changes: 6 additions & 0 deletions internal/ipmi/ipmi.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,9 @@ func (i *Ipmi) ReadUsers(ctx context.Context) (users []map[string]string, err er

return users, err
}

// ClearSystemEventLog clears the system event log
func (i *Ipmi) ClearSystemEventLog(ctx context.Context) (err error) {
_, err = i.run(ctx, []string{"sel", "clear"})
return err
}
36 changes: 36 additions & 0 deletions internal/redfishwrapper/sel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package redfishwrapper

import (
"context"

bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors"
"github.com/pkg/errors"
)

// ClearSystemEventLog clears all of the LogServices logs
func (c *Client) ClearSystemEventLog(ctx context.Context) (err error) {
if err := c.SessionActive(); err != nil {
return errors.Wrap(bmclibErrs.ErrNotAuthenticated, err.Error())
}

chassis, err := c.client.Service.Chassis()
if err != nil {
return err
}

for _, c := range chassis {
logServices, err := c.LogServices()
if err != nil {
return err
}

for _, logService := range logServices {
err = logService.ClearLog()
if err != nil {
return err
}
}
}

return nil
}
5 changes: 5 additions & 0 deletions providers/ipmitool/ipmitool.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (
providers.FeatureUserRead,
providers.FeatureBmcReset,
providers.FeatureBootDeviceSet,
providers.FeatureClearSystemEventLog,
}
)

Expand Down Expand Up @@ -180,3 +181,7 @@ func (c *Conn) PowerSet(ctx context.Context, state string) (ok bool, err error)

return ok, err
}

func (c *Conn) ClearSystemEventLog(ctx context.Context) (err error) {
return c.ipmitool.ClearSystemEventLog(ctx)
}
18 changes: 18 additions & 0 deletions providers/ipmitool/ipmitool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,21 @@ func TestBMCReset(t *testing.T) {
t.Log(state)
t.Fatal()
}

func TestSystemEventLogClear(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)
}
err = i.ClearSystemEventLog(context.Background())
if err != nil {
t.Fatal(err)
}
t.Log("System Event Log cleared")
t.Fatal()
}
2 changes: 2 additions & 0 deletions providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ const (
FeaturePostCodeRead registrar.Feature = "postcoderead"
// FeatureScreenshot means an implementation that returns a screenshot of the video.
FeatureScreenshot registrar.Feature = "screenshot"
// FeatureClearSystemEventLog means an implementation that clears the BMC System Event Log (SEL)
FeatureClearSystemEventLog registrar.Feature = "clearsystemeventlog"
)
1 change: 1 addition & 0 deletions providers/redfish/redfish.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
providers.FeatureFirmwareInstall,
providers.FeatureFirmwareInstallStatus,
providers.FeatureBmcReset,
providers.FeatureClearSystemEventLog,
}
)

Expand Down
7 changes: 7 additions & 0 deletions providers/redfish/sel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package redfish

import "context"

func (c *Conn) ClearSystemEventLog(ctx context.Context) (err error) {
return c.redfishwrapper.ClearSystemEventLog(ctx)
}

0 comments on commit ba1367e

Please sign in to comment.