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

Add SEL Clear Support #349

Merged
merged 11 commits into from
Sep 23, 2023
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)
}