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

Supermicro X13 inventory/firmware support FS-1671 #394

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ jobs:
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
with:
args: -v --config .golangci.yml --timeout=5m
version: latest
args: -v --config .golangci.yml --timeout=5m --out-format=colored-line-number
version: v1.55.2
- name: make all-checks
run: make all-checks
test:
Expand Down
9 changes: 9 additions & 0 deletions internal/redfishwrapper/firmware.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/bmc-toolbox/bmclib/v2/constants"
bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors"
redfish "github.com/stmcginnis/gofish/redfish"
)

type installMethod string
Expand Down Expand Up @@ -105,6 +106,14 @@ func (c *Client) FirmwareUpload(ctx context.Context, updateFile *os.File, params
)
}

// For X13 the full Task structure is returned in the body. If we can Unmarshall then we can safely assume
// that redfishTask.ID contains the ID.
redfishTask := &redfish.Task{}
err = json.Unmarshal(response, redfishTask)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note, if response is valid JSON but isn't a redfish.Task I believe you'll get no error and zero value for the Task. It might be worth a sanity check that the task id isn't an empty string here.

if err == nil {
return redfishTask.ID, nil
}

// The response contains a location header pointing to the task URI
// Location: /redfish/v1/TaskService/Tasks/JID_467696020275
var location = resp.Header.Get("Location")
Expand Down
3 changes: 2 additions & 1 deletion providers/supermicro/firmware.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (
"X11SSE-F",
"X12STH-SYS",
"X12SPO-NTF",
"X13DEM",
}

errUploadTaskIDExpected = errors.New("expected an firmware upload taskID")
Expand All @@ -46,7 +47,7 @@ func (c *Client) FirmwareUpload(ctx context.Context, component string, file *os.
return "", err
}

// // expect atleast 5 minutes left in the deadline to proceed with the upload
// expect atleast 5 minutes left in the deadline to proceed with the upload
d, _ := ctx.Deadline()
if time.Until(d) < 5*time.Minute {
return "", errors.New("remaining context deadline insufficient to perform update: " + time.Until(d).String())
Expand Down
49 changes: 39 additions & 10 deletions providers/supermicro/supermicro.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,14 @@ func (c *Client) Open(ctx context.Context) (err error) {
return err
}

if !bytes.Contains(body, []byte(`url_redirect.cgi?url_name=mainmenu`)) &&
!bytes.Contains(body, []byte(`url_redirect.cgi?url_name=topmenu`)) {
// X13 appears to have dropped the initial 'mainmenu' redirect
if !bytes.Contains(body, []byte(`url_redirect.cgi?url_name=topmenu`)) {
return closeWithError(ctx, errors.Wrap(bmclibErrs.ErrLoginFailed, "unexpected response contents"))
}
// if !bytes.Contains(body, []byte(`url_redirect.cgi?url_name=mainmenu`)) &&
// !bytes.Contains(body, []byte(`url_redirect.cgi?url_name=topmenu`)) {
// return closeWithError(ctx, errors.Wrap(bmclibErrs.ErrLoginFailed, "unexpected response contents"))
// }

contentsTopMenu, status, err := c.serviceClient.query(ctx, "cgi/url_redirect.cgi?url_name=topmenu", http.MethodGet, nil, nil, 0)
if err != nil {
Expand All @@ -197,6 +201,7 @@ func (c *Client) Open(ctx context.Context) (err error) {
c.serviceClient.setCsrfToken(csrfToken)

c.bmc, err = c.bmcQueryor(ctx)

if err != nil {
return closeWithError(ctx, errors.Wrap(bmclibErrs.ErrLoginFailed, err.Error()))
}
Expand Down Expand Up @@ -281,17 +286,36 @@ func (c *Client) ResetBiosConfiguration(ctx context.Context) (err error) {
}

func (c *Client) bmcQueryor(ctx context.Context) (bmcQueryor, error) {
x11 := newX11Client(c.serviceClient, c.log)
x12 := newX12Client(c.serviceClient, c.log)
x11bmc := newX11Client(c.serviceClient, c.log)
x12bmc := newX12Client(c.serviceClient, c.log)
x13bmc := newX13Client(c.serviceClient, c.log)

var queryor bmcQueryor

for _, bmc := range []bmcQueryor{x11, x12} {
expected := func(deviceModel string, bmc bmcQueryor) bool {
deviceModel = strings.ToLower(deviceModel)
switch bmc.(type) {
case *x11:
if strings.HasPrefix(deviceModel, "x11") {
return true
}
case *x12:
if strings.HasPrefix(deviceModel, "x12") {
return true
}
case *x13:
if strings.HasPrefix(deviceModel, "x13") {
return true
}
}

return false
}

for _, bmc := range []bmcQueryor{x11bmc, x12bmc, x13bmc} {
var err error

// Note to maintainers: x12 lacks support for the ipmi.cgi endpoint,
// which will lead to our graceful handling of ErrXMLAPIUnsupported below.
_, err = bmc.queryDeviceModel(ctx)
deviceModel, err := bmc.queryDeviceModel(ctx)
if err != nil {
if errors.Is(err, ErrXMLAPIUnsupported) {
continue
Expand All @@ -300,6 +324,11 @@ func (c *Client) bmcQueryor(ctx context.Context) (bmcQueryor, error) {
return nil, errors.Wrap(ErrModelUnknown, err.Error())
}

// ensure the device model matches the expected queryor
if !expected(deviceModel, bmc) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include a bmc.Close() before the continue here to ensure we don't leave a successful connection open

continue
}

queryor = bmc
break
}
Expand All @@ -309,8 +338,8 @@ func (c *Client) bmcQueryor(ctx context.Context) (bmcQueryor, error) {
}

model := strings.ToLower(queryor.deviceModel())
if !strings.HasPrefix(model, "x12") && !strings.HasPrefix(model, "x11") {
return nil, errors.Wrap(ErrModelUnsupported, "expected one of X11* or X12*, got:"+model)
if !strings.HasPrefix(model, "x13") && !strings.HasPrefix(model, "x12") && !strings.HasPrefix(model, "x11") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With your new expected function I don't think you'll actually get here if it's not x11, x12, or x13. I think the queryor nil check @ 301 covers you.

return nil, errors.Wrap(ErrModelUnsupported, "expected one of X11*, X12* or X13*, got:"+model)
}

return queryor, nil
Expand Down
2 changes: 1 addition & 1 deletion providers/supermicro/supermicro_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func TestOpen(t *testing.T) {
<title></title>
<script language="JavaScript" type="text/javascript">
<!--
self.location = "../cgi/url_redirect.cgi?url_name=mainmenu";
self.location = "../cgi/url_redirect.cgi?url_name=topmenu";
-->
</script>
</head>
Expand Down
2 changes: 1 addition & 1 deletion providers/supermicro/x11.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (c *x11) queryDeviceModel(ctx context.Context) (string, error) {
errBoardPartNumUnknown := errors.New("baseboard part number unknown")
data, err := c.fruInfo(ctx)
if err != nil {
if strings.Contains(err.Error(), "404") {
if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "<html>") {
return "", ErrXMLAPIUnsupported
}

Expand Down
6 changes: 5 additions & 1 deletion providers/supermicro/x12.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ func (c *x12) firmwareTaskActive(ctx context.Context, component string) error {

// noTasksRunning returns an error if a firmware related task was found active
func noTasksRunning(component string, t *redfish.Task) error {
if t.TaskState == "Killed" {
return nil
}

errTaskActive := errors.New("A firmware task was found active for component: " + component)

const (
Expand Down Expand Up @@ -222,7 +226,7 @@ func (c *x12) biosFwInstallParams() (map[string]bool, error) {
}, nil
default:
// ideally we never get in this position, since theres model number validation in parent callers.
return nil, errors.New("unsupported model for BIOS fw install: " + c.model)
return nil, errors.New("unsupported model for x12 BIOS fw install: " + c.model)
}
}

Expand Down
Loading
Loading