diff --git a/cmd/update.go b/cmd/update.go index 6d89e76..4894e7d 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -8,9 +8,13 @@ import ( ) var ( + host string + port int firmwarePath string firmwareVersion string component string + transferProtocol string + status bool ) var updateCmd = &cobra.Command{ @@ -22,17 +26,39 @@ var updateCmd = &cobra.Command{ FirmwarePath: firmwarePath, FirmwareVersion: firmwareVersion, Component: component, + TransferProtocol: transferProtocol, QueryParams: magellan.QueryParams{ + Drivers: []string{"redfish"}, + Preferred: "redfish", + Host: host, User: user, Pass: pass, Timeout: timeout, + Port: port, + WithSecureTLS: withSecureTLS, }, } - client, err := magellan.NewClient(l, &q.QueryParams) - if err != nil { - l.Log.Errorf("could not make client: %v", err) + + // check if required params are set + if host == "" || user == "" || pass == "" { + l.Log.Fatal("requires host, user, and pass to be set") } - err = magellan.UpdateFirmware(client, l, q) + + // get status if flag is set and exit + if status { + err := magellan.GetUpdateStatus(q) + if err != nil { + l.Log.Errorf("could not get update status: %v", err) + } + return + } + + // client, err := magellan.NewClient(l, &q.QueryParams) + // if err != nil { + // l.Log.Errorf("could not make client: %v", err) + // } + // err = magellan.UpdateFirmware(client, l, q) + err := magellan.UpdateFirmwareRemote(q) if err != nil { l.Log.Errorf("could not update firmware: %v", err) } @@ -40,10 +66,15 @@ var updateCmd = &cobra.Command{ } func init() { - updateCmd.PersistentFlags().StringVar(&user, "user", "", "set the BMC user") - updateCmd.PersistentFlags().StringVar(&pass, "pass", "", "set the BMC password") - updateCmd.PersistentFlags().StringVar(&firmwarePath, "firmware-path", "", "set the path to the firmware") - updateCmd.PersistentFlags().StringVar(&firmwareVersion, "firmware-version", "", "set the version of firmware to be installed") - updateCmd.PersistentFlags().StringVar(&component, "component", "", "set the component to upgrade") + updateCmd.Flags().StringVar(&host, "host", "", "set the BMC host") + updateCmd.Flags().IntVar(&port, "port", 443, "set the BMC port") + updateCmd.Flags().StringVar(&user, "user", "", "set the BMC user") + updateCmd.Flags().StringVar(&pass, "pass", "", "set the BMC password") + updateCmd.Flags().StringVar(&transferProtocol, "protocol", "HTTP", "set the transfer protocol") + updateCmd.Flags().StringVar(&firmwarePath, "firmware-path", "", "set the path to the firmware") + updateCmd.Flags().StringVar(&firmwareVersion, "firmware-version", "", "set the version of firmware to be installed") + updateCmd.Flags().StringVar(&component, "component", "", "set the component to upgrade") + updateCmd.Flags().BoolVar(&withSecureTLS, "secure-tls", false, "enable secure TLS") + updateCmd.Flags().BoolVar(&status, "status", false, "get the status of the update") rootCmd.AddCommand(updateCmd) } \ No newline at end of file diff --git a/internal/update.go b/internal/update.go index 30c61b2..7881449 100644 --- a/internal/update.go +++ b/internal/update.go @@ -24,9 +24,16 @@ type UpdateParams struct { FirmwarePath string FirmwareVersion string Component string + TransferProtocol string } +// NOTE: Does not work since OpenBMC, whic bmclib uses underneath, does not +// support multipart updates. See issue: https://github.com/bmc-toolbox/bmclib/issues/341 func UpdateFirmware(client *bmclib.Client, l *log.Logger, q *UpdateParams) error { + if q.Component == "" { + return fmt.Errorf("component is required") + } + // open BMC session and update driver registry ctx, ctxCancel := context.WithTimeout(context.Background(), time.Second*time.Duration(q.Timeout)) client.Registry.FilterForCompatible(ctx) @@ -118,22 +125,36 @@ func UpdateFirmware(client *bmclib.Client, l *log.Logger, q *UpdateParams) error return nil } -func UpdateFirmwareV2(serverIP string, imageURI string, component string, q *QueryParams) error { - url := baseUrl(q) + "UpdateService/Actions/SimpleUpdate" +func UpdateFirmwareRemote(q *UpdateParams) error { + url := baseRedfishUrl(&q.QueryParams) + "/UpdateService/Actions/SimpleUpdate" + headers := map[string]string { + "Content-Type": "application/json", + "cache-control": "no-cache", + } b := map[string]any{ - "UpdateComponent": component, // BMC, BIOS - "TransferProtocol": "HTTP", - "ImageURI": "http://" + serverIP + "/" + imageURI, + "UpdateComponent": q.Component, // BMC, BIOS + "TransferProtocol": q.TransferProtocol, + "ImageURI": q.FirmwarePath, } data, err := json.Marshal(b) if err != nil { return fmt.Errorf("could not marshal data: %v", err) } - headers := map[string]string{ - "Content-Type": "application/json", - "cache-control": "no-cache", + res, body, err := util.MakeRequest(url, "POST", data, headers) + if err != nil { + return fmt.Errorf("something went wrong: %v", err) + } else if res == nil { + return fmt.Errorf("no response returned (url: %s)", url) } - res, _, err := util.MakeRequest(url, "POST", data, headers) + if len(body) > 0 { + fmt.Printf("%d: %v\n", res.StatusCode, string(body)) + } + return nil +} + +func GetUpdateStatus(q *UpdateParams) error { + url := baseRedfishUrl(&q.QueryParams) + "/UpdateService" + res, body, err := util.MakeRequest(url, "GET", nil, nil) if err != nil { return fmt.Errorf("something went wrong: %v", err) } else if res == nil { @@ -141,5 +162,34 @@ func UpdateFirmwareV2(serverIP string, imageURI string, component string, q *Que } else if res.StatusCode != http.StatusOK { return fmt.Errorf("returned status code %d", res.StatusCode) } + if len(body) > 0 { + fmt.Printf("%d: %v\n", res.StatusCode, string(body)) + } return nil -} \ No newline at end of file +} + +// func UpdateFirmwareLocal(q *UpdateParams) error { +// fwUrl := baseUrl(&q.QueryParams) + "" +// url := baseUrl(&q.QueryParams) + "UpdateService/Actions/" +// headers := map[string]string { + +// } + +// // get etag from FW inventory +// response, err := util.MakeRequest() + +// // load file from disk +// file, err := os.ReadFile(q.FirmwarePath) +// if err != nil { +// return fmt.Errorf("could not read file: %v", err) +// } + + + +// switch q.TransferProtocol { +// case "HTTP": +// default: +// return fmt.Errorf("transfer protocol not supported") +// } +// return nil +// } \ No newline at end of file