Closed sanverm2 closed 2 years ago
Hey @sanverm2, I believe others are using the UpdateService
you pointed to successfully. Is there an issue you are running in to with it?
Access would be something like:
package main
import (
"github.com/stmcginnis/gofish"
)
func main() {
// Create a new instance of gofish client, ignoring self-signed certs
config := gofish.ClientConfig{
Endpoint: "https://bmc-ip",
Username: "my-username",
Password: "my-password",
Insecure: true,
}
c, err := gofish.Connect(config)
if err != nil {
panic(err)
}
defer c.Logout()
// Retrieve the service root
service := c.Service
updateService, err := service.UpdateService()
if err != nil {
panic(err)
}
}
Hi @sanverm2, I submitted PR #132 which solves an issue with the multipart method that for some reason was preventing from uploading FW packages correctly to the FW inventory.
Now, to use SImpleUpdate action you need to use custom headers to get Etag from the FW inventory and later on send the PostMultipart with custom headers using the if-match for that purpose.
I submitted another PR that implements all CRUD methods with WithCustomHeaders that allows to do that. Unfortunately the library version that comes with those features is not yet published, so you cannot get it from Go. Still, it is on the master branch for you to check.
Thanks @mikeletux!
It would probably be a good idea to implement a helper method (something like UpdateService.SubmitUpdate(data []bytes)
) to hide some of the plumbing from the consumer. If anyone has the time to take a crack at that, I think it would be a great addition to gofish.
Hey @stmcginnis ,
Yeah I agree, it would be awesome to implement a helper method like that to hide using Post call directly. I take note of that and try to make it real as soon as I can๐
Awesome - thanks again @mikeletux!
Thanks @stmcginnis @mikeletux .
"Now, to use SImpleUpdate action you need to use custom headers to get Etag from the FW inventory and later on send the PostMultipart with custom headers using the if-match for that purpose."
If you have sample code to use it, it would be great help.
@sanverm2 I'm writing a terraform provider for redfish. The code I'm writing for SimpleUpdate can be found @ https://github.com/mikeletux/terraform-provider-redfish/blob/master/redfish/resource_simple_update.go
Hope it helps (Some of the functions are not yet available on gofish v0.9.0 but it does on master branch)
func createRedfishSimpleUpdate(service *gofish.Service, d *schema.ResourceData) diag.Diagnostics {
var diags diag.Diagnostics
transferProtocol := d.Get("transfer_protocol").(string)
targetFirmwareImage := d.Get("target_firmware_image").(string)
// Get update service from root
updateService, err := service.UpdateService()
if err != nil {
return diag.Errorf("error while retrieving UpdateService - %s", err)
}
//Check if the transfer protocol is available in the redfish instance
err = checkTransferProtocol(transferProtocol, updateService)
if err != nil {
return diag.Errorf("%s", err)
}
switch transferProtocol {
case "HTTP":
// Get ETag from FW inventory
response, err := service.Client.Get(updateService.FirmwareInventory)
if err != nil {
diag.Errorf("error while retrieving Etag from FirmwareInventory")
}
response.Body.Close()
etag := response.Header.Get("ETag")
// Set custom headers
customHeaders := map[string]string{
"if-match": etag,
}
// Open file to upload
file, err := openFile(targetFirmwareImage)
if err != nil {
return diag.Errorf("couldn't open FW file to upload - %s", err)
}
defer file.Close()
// Set payload
payload := map[string]io.Reader{
"file": file,
}
// Upload FW Package to FW inventory
response, err = service.Client.PostMultipartWithHeaders(updateService.HTTPPushURI, payload, customHeaders)
if err != nil {
return diag.Errorf("there was an issue when uploading FW package to redfish - %s", err)
}
response.Body.Close()
packageLocation := response.Header.Get("Location")
// Get package information ( SoftwareID - Version )
packageInformation, err := redfish.GetSoftwareInventory(service.Client, packageLocation)
if err != nil {
return diag.Errorf("there was an issue when retrieving uploaded package information - %s", err)
}
// Set payload for POST call that'll trigger the update job scheduling
triggerUpdatePayload := struct {
ImageURI string
}{
ImageURI: packageLocation,
}
// Do the POST call agains Simple.Update service
response, err = service.Client.Post(updateService.UpdateServiceTarget, triggerUpdatePayload)
if err != nil {
// Delete uploaded package - TBD
return diag.Errorf("there was an issue when scheduling the update job - %s", err)
}
response.Body.Close()
// AT THIS POINT FW UPDATE WILL BE SCHEDULED
d.Set("software_id", packageInformation.SoftwareID)
d.Set("version", packageInformation.Version)
d.SetId("test")
default:
return diag.Errorf("Transfer protocol not available in this implementation")
}
return diags
}
And also some helper functions that I use:
// openFile is a simple function that opens a file
func openFile(filePath string) (*os.File, error) {
if f, err := os.Open(filePath); err != nil {
return nil, fmt.Errorf("error when opening %s file - %s", filePath, err)
} else {
return f, nil
}
}
// checkTransferProtocol checks if the chosen transfer protocol is available in the redfish instance
func checkTransferProtocol(transferProtocol string, updateService *redfish.UpdateService) error {
for _, v := range updateService.TransferProtocol {
if transferProtocol == v {
return nil
}
}
return fmt.Errorf("This transfer protocol is not available in this redfish instance")
}
Hope it helps ๐
Awesome! Thanks @mikeletux
I believe this is addressed. If not, please reopen or file a new issue with current specific details. Thanks!
Hi Sean,
We are trying to use gofish for management and monitoring our servers. However, we are able to use monitoring APIs but we do not see any implementation of firmware update as of now in version 0.9.0.
We checked checked APIs:
https://pkg.go.dev/github.com/stmcginnis/gofish/redfish#UpdateService
Is it under implementation?