Skip to content

Commit

Permalink
examples: Update firmware install example
Browse files Browse the repository at this point in the history
  • Loading branch information
joelrebel committed Jun 22, 2023
1 parent 55bf86a commit 4294713
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 41 deletions.
70 changes: 52 additions & 18 deletions examples/install-firmware/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,57 @@
install-firmware is an example commmand that utilizes the 'v1' bmclib interface
methods to flash a firmware image to a BMC.
$ go run ./examples/v1/install-firmware/main.go -h
Usage of /tmp/go-build2950657412/b001/exe/main:
-cert-pool string
Path to an file containing x509 CAs. An empty string uses the system CAs. Only takes effect when --secure-tls=true
-firmware string
The local path of the firmware to install
-host string
BMC hostname to connect to
-password string
Username to login with
-port int
BMC port to connect to (default 443)
-secure-tls
Enable secure TLS
-user string
Username to login with
-version string
The firmware version being installed
Note: The example installs the firmware and polls until the status until the install is complete,
and if required by the install process - power cycles the host.
$ go run ./examples/v1/install-firmware/main.go -h
Usage of /tmp/go-build2950657412/b001/exe/main:
-cert-pool string
Path to an file containing x509 CAs. An empty string uses the system CAs. Only takes effect when --secure-tls=true
-firmware string
The local path of the firmware to install
-host string
BMC hostname to connect to
-password string
Username to login with
-port int
BMC port to connect to (default 443)
-secure-tls
Enable secure TLS
-user string
Username to login with
-version string
The firmware version being installed
# install bios firmware on a supermicro X11
#
$ go run . -host 192.168.1.1 -user ADMIN -password hunter2 -component bios -firmware BIOS_X11DPH-0981_20220208_3.6_STD.bin
INFO[0007] set firmware install mode component=BIOS ip="https://192.168.1.1" model=X11DPH-T
INFO[0011] uploading firmware component=BIOS ip="https://192.168.1.1" model=X11DPH-T
INFO[0091] verifying uploaded firmware component=BIOS ip="https://192.168.1.1" model=X11DPH-T
INFO[0105] initiating firmware install component=BIOS ip="https://192.168.1.1" model=X11DPH-T
INFO[0115] firmware install running component=bios state=running
INFO[0132] firmware install running component=bios state=running
...
...
INFO[0628] firmware install running component=bios state=running
INFO[0635] host powercycle required component=bios state=powercycle-host
INFO[0637] host power cycled, all done! component=bios state=powercycle-host
# install bmc firmware on a supermicro X11
#
$ go run . -host 192.168.1.1 -user ADMIN -password hunter2 -component bmc -firmware BMC_X11AST2500-4101MS_20220225_01.74.02_STD.bin
INFO[0007] setting device to firmware install mode component=BMC ip="https://192.168.1.1"
INFO[0009] uploading firmware ip="https://192.168.1.1"
INFO[0045] verifying uploaded firmware ip="https://192.168.1.1"
INFO[0047] initiating firmware install ip="https://192.168.1.1"
INFO[0079] firmware install running component=bmc state=running
INFO[0085] firmware install running component=bmc state=running
...
...
INFO[0233] firmware install running component=bmc state=running
INFO[0238] firmware install completed component=bmc state=complete
*/
package main
98 changes: 75 additions & 23 deletions examples/install-firmware/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ package main
import (
"context"
"crypto/x509"
"errors"
"flag"
"io/ioutil"
"log"
"os"
"strings"
"time"

bmclib "github.com/bmc-toolbox/bmclib/v2"
"github.com/bmc-toolbox/bmclib/v2/constants"
"github.com/bmc-toolbox/common"
bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors"
"github.com/bombsimon/logrusr/v2"
"github.com/sirupsen/logrus"
)
Expand All @@ -20,24 +22,33 @@ 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")
component := flag.String("component", "", "Component to be updated (bmc, bios.. etc)")
withSecureTLS := flag.Bool("secure-tls", false, "Enable secure TLS")
certPoolPath := 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")
firmwarePath := flag.String("firmware", "", "The local path of the firmware to install")
firmwareVersion := flag.String("version", "", "The firmware version being installed")
//firmwareVersion := flag.String("version", "", "The firmware version being installed")

flag.Parse()

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

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

if *host == "" || *user == "" || *pass == "" {
l.Fatal("required host/user/pass parameters not defined")
}
clientOpts := []bmclib.Option{bmclib.WithLogger(logger)}

if *component == "" {
l.Fatal("component parameter required (must be a component slug - bmc, bios etc)")
}

clientOpts := []bmclib.Option{
bmclib.WithLogger(logger),
bmclib.WithPerProviderTimeout(time.Minute * 30),
}

if *withSecureTLS {
var pool *x509.CertPool
Expand All @@ -53,40 +64,81 @@ func main() {
clientOpts = append(clientOpts, bmclib.WithSecureTLS(pool))
}

cl := bmclib.NewClient(*host, *user, *pass, clientOpts...)
cl := bmclib.NewClient(*host, *user, *pass, clientOpts...).PreferProvider("supermicro")
err := cl.Open(ctx)
if err != nil {
l.Fatal(err, "bmc login failed")
}

defer cl.Close(ctx)

// collect inventory
inventory, err := cl.Inventory(ctx)
if err != nil {
l.Fatal(err)
}

l.WithField("bmc-version", inventory.BMC.Firmware.Installed).Info()

// open file handle
fh, err := os.Open(*firmwarePath)
if err != nil {
l.Fatal(err)
}
defer fh.Close()

// SlugBMC hardcoded here, this can be any of the existing component slugs from devices/constants.go
// assuming that the BMC provider implements the required component firmware update support
taskID, err := cl.FirmwareInstall(ctx, common.SlugBMC, constants.FirmwareApplyOnReset, true, fh)
_, err = cl.FirmwareInstall(ctx, *component, constants.FirmwareApplyOnReset, true, fh)
if err != nil {
l.Error(err)
l.Fatal(err)
}

state, err := cl.FirmwareInstallStatus(ctx, taskID, common.SlugBMC, *firmwareVersion)
if err != nil {
log.Fatal(err)
}
for {
if ctx.Err() != nil {
l.Fatal(ctx.Err())
}

state, err := cl.FirmwareInstallStatus(ctx, "", *component, "")
if err != nil {
// when its under update a connection refused is returned
if strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "operation timed out") {
l.Info("BMC refused connection, BMC most likely resetting..")
time.Sleep(2 * time.Second)

continue
}

l.WithField("state", state).Info("BMC firmware install state")
if errors.Is(err, bmclibErrs.ErrSessionExpired) || strings.Contains(err.Error(), "session expired") {
err := cl.Open(ctx)
if err != nil {
l.Fatal(err, "bmc re-login failed")
}

l.WithFields(logrus.Fields{"state": state, "component": *component}).Info("BMC session expired, logging in..")

continue
}

log.Fatal(err)
}

switch state {
case constants.FirmwareInstallRunning:
l.WithFields(logrus.Fields{"state": state, "component": *component}).Info("firmware install running")

case constants.FirmwareInstallFailed:
l.WithFields(logrus.Fields{"state": state, "component": *component}).Info("firmware install failed")
os.Exit(1)

case constants.FirmwareInstallComplete:
l.WithFields(logrus.Fields{"state": state, "component": *component}).Info("firmware install completed")
os.Exit(0)

case constants.FirmwareInstallPowerCyleHost:
l.WithFields(logrus.Fields{"state": state, "component": *component}).Info("host powercycle required")

if _, err := cl.SetPowerState(ctx, "cycle"); err != nil {
l.WithFields(logrus.Fields{"state": state, "component": *component}).Info("error power cycling host for install")
os.Exit(1)
}

l.WithFields(logrus.Fields{"state": state, "component": *component}).Info("host power cycled, all done!")
os.Exit(0)
default:
l.WithFields(logrus.Fields{"state": state, "component": *component}).Info("unknown state returned")
}

time.Sleep(2 * time.Second)
}
}

0 comments on commit 4294713

Please sign in to comment.