Skip to content

Commit

Permalink
Support Firmware Interface
Browse files Browse the repository at this point in the history
* Driver updated to support
- default_firmware_interface and enabled_firmware_interfaces

* Node updated to support
- firmware_interface field can be specified
- List the Firmware Components requires API 1.86

Acceptance tests added
- show that the node has the field FirmwareInterface and that returns
no Firmware Components since is ipmi.
  • Loading branch information
iurygregory committed Oct 13, 2023
1 parent fed1858 commit ac919b4
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 0 deletions.
20 changes: 20 additions & 0 deletions internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,23 @@ func TestNodesRAIDConfig(t *testing.T) {
}).ExtractErr()
th.AssertNoErr(t, err)
}

func TestNodesFirmwareInterface(t *testing.T) {
clients.SkipReleasesBelow(t, "stable/2023.2")
clients.RequireLong(t)
clients.RequireIronicHTTPBasic(t)

client, err := clients.NewBareMetalV1HTTPBasic()
th.AssertNoErr(t, err)
client.Microversion = "1.86"

node, err := v1.CreateNode(t, client)
th.AssertNoErr(t, err)
defer v1.DeleteNode(t, client, node)

th.AssertEquals(t, node.FirmwareInterface, "no-firmware")

nodeFirmwareCmps, err := nodes.ListFirmware(client, node.UUID).Extract()
th.AssertNoErr(t, err)
th.AssertDeepEquals(t, nodeFirmwareCmps, []nodes.FirmwareComponent{})
}
19 changes: 19 additions & 0 deletions internal/acceptance/openstack/baremetal/v1/nodes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,22 @@ func TestNodesRAIDConfig(t *testing.T) {
}).ExtractErr()
th.AssertNoErr(t, err)
}

func TestNodesFirmwareInterface(t *testing.T) {
clients.SkipReleasesBelow(t, "stable/2023.2")
clients.RequireLong(t)

client, err := clients.NewBareMetalV1Client()
th.AssertNoErr(t, err)
client.Microversion = "1.86"

node, err := CreateNode(t, client)
th.AssertNoErr(t, err)
defer DeleteNode(t, client, node)

th.AssertEquals(t, node.FirmwareInterface, "no-firmware")

nodeFirmwareCmps, err := nodes.ListFirmware(client, node.UUID).Extract()
th.AssertNoErr(t, err)
th.AssertDeepEquals(t, nodeFirmwareCmps, []nodes.FirmwareComponent{})
}
7 changes: 7 additions & 0 deletions openstack/baremetal/v1/drivers/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ type Driver struct {
// if no deploy interface is specified for the node.
DefaultDeployInterface string `json:"default_deploy_interface"`

// The default firmware interface used for a node with a dynamic driver,
// if no firmware interface is specified for the node.
DefaultFirmwareInterface string `json:"default_firmware_interface"`

// The default inspection interface used for a node with a dynamic driver,
// if no inspection interface is specified for the node.
DefaultInspectInterface string `json:"default_inspect_interface"`
Expand Down Expand Up @@ -95,6 +99,9 @@ type Driver struct {
// The enabled deploy interfaces for this driver.
EnabledDeployInterfaces []string `json:"enabled_deploy_interfaces"`

// The enabled firmware interfaces for this driver.
EnabledFirmwareInterfaces []string `json:"enabled_firmware_interfaces"`

// The enabled inspection interfaces for this driver.
EnabledInspectInterfaces []string `json:"enabled_inspect_interfaces"`

Expand Down
6 changes: 6 additions & 0 deletions openstack/baremetal/v1/drivers/testing/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const SingleDriverDetails = `
"default_boot_interface": "pxe",
"default_console_interface": "no-console",
"default_deploy_interface": "iscsi",
"default_firmware_interface": "no-firmware",
"default_inspect_interface": "no-inspect",
"default_management_interface": "ipmitool",
"default_network_interface": "flat",
Expand All @@ -125,6 +126,9 @@ const SingleDriverDetails = `
"iscsi",
"direct"
],
"enabled_firmware_interfaces": [
"no-firmware"
],
"enabled_inspect_interfaces": [
"no-inspect"
],
Expand Down Expand Up @@ -281,6 +285,7 @@ var (
DefaultBootInterface: "pxe",
DefaultConsoleInterface: "no-console",
DefaultDeployInterface: "iscsi",
DefaultFirmwareInterface: "no-firmware",
DefaultInspectInterface: "no-inspect",
DefaultManagementInterface: "ipmitool",
DefaultNetworkInterface: "flat",
Expand All @@ -293,6 +298,7 @@ var (
EnabledBootInterfaces: []string{"pxe"},
EnabledConsoleInterface: []string{"no-console"},
EnabledDeployInterfaces: []string{"iscsi", "direct"},
EnabledFirmwareInterfaces: []string{"no-firmware"},
EnabledInspectInterfaces: []string{"no-inspect"},
EnabledManagementInterfaces: []string{"ipmitool"},
EnabledNetworkInterfaces: []string{"flat", "noop"},
Expand Down
13 changes: 13 additions & 0 deletions openstack/baremetal/v1/nodes/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ type CreateOpts struct {
// A set of one or more arbitrary metadata key and value pairs.
Extra map[string]interface{} `json:"extra,omitempty"`

// The firmware interface for a node, e.g. "redfish"
FirmwareInterface string `json:"firmware_interface,omitempty"`

// The interface used for node inspection, e.g. “no-inspect”.
InspectInterface string `json:"inspect_interface,omitempty"`

Expand Down Expand Up @@ -411,6 +414,7 @@ type StepInterface string
const (
InterfaceBIOS StepInterface = "bios"
InterfaceDeploy StepInterface = "deploy"
InterfaceFirmware StepInterface = "firmware"
InterfaceManagement StepInterface = "management"
InterfacePower StepInterface = "power"
InterfaceRAID StepInterface = "raid"
Expand Down Expand Up @@ -889,3 +893,12 @@ func GetInventory(client *gophercloud.ServiceClient, id string) (r InventoryResu
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}

// ListFirmware return the list of Firmware components for the given Node.
func ListFirmware(client *gophercloud.ServiceClient, id string) (r ListFirmwareResult) {
resp, err := client.Get(firmwareListURL(client, id), &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
36 changes: 36 additions & 0 deletions openstack/baremetal/v1/nodes/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ type Node struct {
// Deploy interface for a node, e.g. “iscsi”.
DeployInterface string `json:"deploy_interface"`

// Firmware interface for a node, e.g. “redfish”.
FirmwareInterface string `json:"firmware_interface"`

// Interface used for node inspection, e.g. “no-inspect”.
InspectInterface string `json:"inspect_interface"`

Expand Down Expand Up @@ -405,6 +408,7 @@ type NodeValidation struct {
Boot DriverValidation `json:"boot"`
Console DriverValidation `json:"console"`
Deploy DriverValidation `json:"deploy"`
Firmware DriverValidation `json:"firmware"`
Inspect DriverValidation `json:"inspect"`
Management DriverValidation `json:"management"`
Network DriverValidation `json:"network"`
Expand Down Expand Up @@ -606,3 +610,35 @@ func (r InventoryResult) Extract() (*InventoryData, error) {
err := r.ExtractInto(&data)
return &data, err
}

// ListFirmwareResult is the response from a ListFirmware operation. Call its Extract method
// to interpret it as an array of FirmwareComponent structs.
type ListFirmwareResult struct {
gophercloud.Result
}

// A particular Firmware Component for a node
type FirmwareComponent struct {
// The UTC date and time when the resource was created, ISO 8601 format.
CreatedAt time.Time `json:"created_at"`
// The UTC date and time when the resource was updated, ISO 8601 format. May be “null”.
UpdatedAt *time.Time `json:"updated_at"`
// The Component name
Component string `json:"component"`
// The initial version of the firmware component.
InitialVersion string `json:"initial_version"`
// The current version of the firmware component.
CurrentVersion string `json:"current_version"`
// The last firmware version updated for the component.
LastVersionFlashed string `json:"last_version_flashed,omitempty"`
}

// Extract interprets a ListFirmwareResult as an array of FirmwareComponent structs, if possible.
func (r ListFirmwareResult) Extract() ([]FirmwareComponent, error) {
var s struct {
Components []FirmwareComponent `json:"firmware"`
}

err := r.ExtractInto(&s)
return s.Components, err
}
71 changes: 71 additions & 0 deletions openstack/baremetal/v1/nodes/testing/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const NodeListDetailBody = `
"driver_internal_info": {},
"extra": {},
"fault": null,
"firmware_interface": "no-firmware",
"inspect_interface": "no-inspect",
"inspection_finished_at": null,
"inspection_started_at": null,
Expand Down Expand Up @@ -197,6 +198,7 @@ const NodeListDetailBody = `
"driver_internal_info": {},
"extra": {},
"fault": null,
"firmware_interface": "no-firmware",
"inspect_interface": "no-inspect",
"inspection_finished_at": "2023-02-02T14:45:59.705249Z",
"inspection_started_at": "2023-02-02T14:35:59.682403Z",
Expand Down Expand Up @@ -294,6 +296,7 @@ const NodeListDetailBody = `
"driver_internal_info": {},
"extra": {},
"fault": null,
"firmware_interface": "no-firmware",
"inspect_interface": "no-inspect",
"inspection_finished_at": null,
"inspection_started_at": null,
Expand Down Expand Up @@ -404,6 +407,7 @@ const SingleNodeBody = `
"driver_internal_info": {},
"extra": {},
"fault": null,
"firmware_interface": "no-firmware",
"inspect_interface": "no-inspect",
"inspection_finished_at": null,
"inspection_started_at": null,
Expand Down Expand Up @@ -504,6 +508,10 @@ const NodeValidationBody = `
"reason": "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']",
"result": false
},
"firmware": {
"reason": "Driver ipmi does not support firmware (disabled or not implemented).",
"result": false
},
"inspect": {
"reason": "Driver ipmi does not support inspect (disabled or not implemented).",
"result": false
Expand Down Expand Up @@ -828,6 +836,29 @@ var NodeInventoryBody = fmt.Sprintf(`
}
`, inventorytest.InventorySample)

const NodeFirmwareListBody = `
{
"firmware": [
{
"created_at": "2023-10-03T18:30:00+00:00",
"updated_at": null,
"component": "bios",
"initial_version": "U30 v2.36 (07/16/2020)",
"current_version": "U30 v2.36 (07/16/2020)",
"last_version_flashed": null
},
{
"created_at": "2023-10-03T18:30:00+00:00",
"updated_at": "2023-10-03T18:45:54+00:00",
"component": "bmc",
"initial_version": "iLO 5 v2.78",
"current_version": "iLO 5 v2.81",
"last_version_flashed": "iLO 5 v2.81"
}
]
}
`

var (
createdAtFoo, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:28+00:00")
createdAtBar, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:29+00:00")
Expand Down Expand Up @@ -872,6 +903,7 @@ var (
BootInterface: "pxe",
ConsoleInterface: "no-console",
DeployInterface: "iscsi",
FirmwareInterface: "no-firmware",
InspectInterface: "no-inspect",
ManagementInterface: "ipmitool",
NetworkInterface: "flat",
Expand Down Expand Up @@ -906,6 +938,10 @@ var (
Result: false,
Reason: "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']",
},
Firmware: nodes.DriverValidation{
Result: false,
Reason: "Driver ipmi does not support firmware (disabled or not implemented).",
},
Inspect: nodes.DriverValidation{
Result: false,
Reason: "Driver ipmi does not support inspect (disabled or not implemented).",
Expand Down Expand Up @@ -975,6 +1011,7 @@ var (
BootInterface: "pxe",
ConsoleInterface: "no-console",
DeployInterface: "iscsi",
FirmwareInterface: "no-firmware",
InspectInterface: "no-inspect",
ManagementInterface: "ipmitool",
NetworkInterface: "flat",
Expand Down Expand Up @@ -1023,6 +1060,7 @@ var (
BootInterface: "pxe",
ConsoleInterface: "no-console",
DeployInterface: "iscsi",
FirmwareInterface: "no-firmware",
InspectInterface: "no-inspect",
ManagementInterface: "ipmitool",
NetworkInterface: "flat",
Expand Down Expand Up @@ -1192,6 +1230,28 @@ var (
NodeInventoryData = nodes.InventoryData{
Inventory: inventorytest.Inventory,
}

createdAtFirmware, _ = time.Parse(time.RFC3339, "2023-10-03T18:30:00+00:00")
updatedAtFirmware, _ = time.Parse(time.RFC3339, "2023-10-03T18:45:54+00:00")
lastVersion = "iLO 5 v2.81"
NodeFirmwareList = []nodes.FirmwareComponent{
{
CreatedAt: createdAtFirmware,
UpdatedAt: nil,
Component: "bios",
InitialVersion: "U30 v2.36 (07/16/2020)",
CurrentVersion: "U30 v2.36 (07/16/2020)",
LastVersionFlashed: "",
},
{
CreatedAt: createdAtFirmware,
UpdatedAt: &updatedAtFirmware,
Component: "bmc",
InitialVersion: "iLO 5 v2.78",
CurrentVersion: "iLO 5 v2.81",
LastVersionFlashed: lastVersion,
},
}
)

// HandleNodeListSuccessfully sets up the test server to respond to a server List request.
Expand Down Expand Up @@ -1244,6 +1304,7 @@ func HandleNodeCreationSuccessfully(t *testing.T, response string) {
"ipmi_port": "6230",
"ipmi_username": "admin"
},
"firmware_interface": "no-firmware",
"name": "foo"
}`)

Expand Down Expand Up @@ -1597,3 +1658,13 @@ func HandleGetInventorySuccessfully(t *testing.T) {
fmt.Fprintf(w, NodeInventoryBody)
})
}

// HandleListFirmware
func HandleListFirmwareSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/nodes/1234asdf/firmware", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, NodeFirmwareListBody)
})
}
12 changes: 12 additions & 0 deletions openstack/baremetal/v1/nodes/testing/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func TestCreateNode(t *testing.T) {
"deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz",
"ipmi_password": "admin",
},
FirmwareInterface: "no-firmware",
}).Extract()
th.AssertNoErr(t, err)

Expand Down Expand Up @@ -730,3 +731,14 @@ func TestGetInventory(t *testing.T) {
th.AssertNoErr(t, err)
th.AssertEquals(t, "x86_64", compatData.CPUArch)
}

func TestListFirmware(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleListFirmwareSuccessfully(t)

c := client.ServiceClient()
actual, err := nodes.ListFirmware(c, "1234asdf").Extract()
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, NodeFirmwareList, actual)
}
4 changes: 4 additions & 0 deletions openstack/baremetal/v1/nodes/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,7 @@ func maintenanceURL(client *gophercloud.ServiceClient, id string) string {
func inventoryURL(client *gophercloud.ServiceClient, id string) string {
return client.ServiceURL("nodes", id, "inventory")
}

func firmwareListURL(client *gophercloud.ServiceClient, id string) string {
return client.ServiceURL("nodes", id, "firmware")
}

0 comments on commit ac919b4

Please sign in to comment.