Skip to content
Merged
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
26 changes: 19 additions & 7 deletions internal/redfishwrapper/network_boot.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,23 @@ const (
// and legacy PXE are separate capabilities that can be on or off in any combination. NetworkStack
// and BootModeSelect are prerequisites shared by both protocols, so they're only asserted on the
// enabled side of each set — disabling one protocol must not turn off the network stack the
// other protocol may still depend on.
// other protocol may still depend on. Not every BIOS carries every prerequisite, so the ones
// listed in optionalWhenAbsent are left out of the PATCH when the BIOS doesn't have them.
var networkBootFingerprintTables = []struct {
fingerprint string // attribute name unique enough to identify this BIOS/vendor
httpEnabled map[string]string
httpDisabled map[string]string
pxeEnabled map[string]string
pxeDisabled map[string]string
fingerprint string // attribute name unique enough to identify this BIOS/vendor
// optionalWhenAbsent names attributes of the enable sets that are dropped from the PATCH when
// the BIOS doesn't list them: a BMC rejects an unknown attribute with a 400 that fails the
// whole request (Supermicro AS-1015CS-TNR-EU has no BootModeSelect). The protocol switches
// themselves are never optional.
optionalWhenAbsent []string
httpEnabled map[string]string
httpDisabled map[string]string
pxeEnabled map[string]string
pxeDisabled map[string]string
}{
{
fingerprint: "IPv4HTTPSupport", // Supermicro H12SSW-NTR / AMI Aptio, confirmed live
fingerprint: "IPv4HTTPSupport", // Supermicro H12SSW-NTR / AMI Aptio, confirmed live
optionalWhenAbsent: []string{"NetworkStack", "BootModeSelect"},
httpEnabled: map[string]string{
"NetworkStack": attrEnabled,
"BootModeSelect": attrUEFI,
Expand Down Expand Up @@ -98,6 +105,11 @@ func networkBootAttributes(httpEnabled, pxeEnabled *bool, current map[string]str
if pxeEnabled != nil {
mergeNetworkBootAttrs(attrs, pickNetworkBootAttrs(*pxeEnabled, t.pxeEnabled, t.pxeDisabled))
}
for _, name := range t.optionalWhenAbsent {
if _, ok := current[name]; !ok {
delete(attrs, name)
}
}
return attrs, nil
}
return nil, fmt.Errorf("no known BIOS attribute mapping for this machine (fingerprint attributes not found in GetBiosConfiguration result)")
Expand Down
84 changes: 79 additions & 5 deletions internal/redfishwrapper/network_boot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestNetworkBootAttributes(t *testing.T) {
}{
"enable http only, pxe untouched": {
httpEnabled: networkBootBoolPtr(true),
current: map[string]string{"IPv4HTTPSupport": "Disabled", "IPv4PXESupport": "Enabled"},
current: map[string]string{"IPv4HTTPSupport": "Disabled", "IPv4PXESupport": "Enabled", "NetworkStack": "Enabled", "BootModeSelect": "UEFI"},
want: map[string]string{
"NetworkStack": "Enabled",
"BootModeSelect": "UEFI",
Expand All @@ -43,7 +43,7 @@ func TestNetworkBootAttributes(t *testing.T) {
},
"enable pxe only, http untouched": {
pxeEnabled: networkBootBoolPtr(true),
current: map[string]string{"IPv4HTTPSupport": "Enabled", "IPv4PXESupport": "Disabled"},
current: map[string]string{"IPv4HTTPSupport": "Enabled", "IPv4PXESupport": "Disabled", "NetworkStack": "Enabled", "BootModeSelect": "UEFI"},
want: map[string]string{
"NetworkStack": "Enabled",
"BootModeSelect": "UEFI",
Expand All @@ -60,7 +60,7 @@ func TestNetworkBootAttributes(t *testing.T) {
"enable both http and pxe": {
httpEnabled: networkBootBoolPtr(true),
pxeEnabled: networkBootBoolPtr(true),
current: map[string]string{"IPv4HTTPSupport": "Disabled"},
current: map[string]string{"IPv4HTTPSupport": "Disabled", "NetworkStack": "Enabled", "BootModeSelect": "UEFI"},
want: map[string]string{
"NetworkStack": "Enabled",
"BootModeSelect": "UEFI",
Expand All @@ -72,7 +72,7 @@ func TestNetworkBootAttributes(t *testing.T) {
"disable http while enabling pxe": {
httpEnabled: networkBootBoolPtr(false),
pxeEnabled: networkBootBoolPtr(true),
current: map[string]string{"IPv4HTTPSupport": "Enabled"},
current: map[string]string{"IPv4HTTPSupport": "Enabled", "NetworkStack": "Enabled", "BootModeSelect": "UEFI"},
want: map[string]string{
"NetworkStack": "Enabled",
"BootModeSelect": "UEFI",
Expand All @@ -81,6 +81,22 @@ func TestNetworkBootAttributes(t *testing.T) {
"IPv4PXESupport": "Enabled",
},
},
"enable http, BootModeSelect absent from the BIOS is left out": {
httpEnabled: networkBootBoolPtr(true),
current: map[string]string{"IPv4HTTPSupport": "Enabled", "NetworkStack": "Enabled", "CSMSupport": "Disabled"},
want: map[string]string{
"NetworkStack": "Enabled",
"IPv4HTTPSupport": "Enabled",
"IPv6HTTPSupport": "Enabled",
},
},
"enable pxe, both prerequisites absent from the BIOS are left out": {
pxeEnabled: networkBootBoolPtr(true),
current: map[string]string{"IPv4HTTPSupport": "Enabled"},
want: map[string]string{
"IPv4PXESupport": "Enabled",
},
},
"unknown fingerprint returns error": {
httpEnabled: networkBootBoolPtr(true),
current: map[string]string{"SomeUnrelatedAttribute": "value"},
Expand Down Expand Up @@ -124,7 +140,9 @@ func TestSetNetworkBootEnabled(t *testing.T) {
"Name": "BIOS Configuration",
"AttributeRegistry": "BiosAttributeRegistryU32.v1_0_0",
"Attributes": {
"IPv4HTTPSupport": "Disabled"
"IPv4HTTPSupport": "Disabled",
"NetworkStack": "Disabled",
"BootModeSelect": "Legacy"
}
}`))
case http.MethodPatch:
Expand Down Expand Up @@ -161,6 +179,62 @@ func TestSetNetworkBootEnabled(t *testing.T) {
}, patchBody)
}

func TestSetNetworkBootEnabled_PrerequisiteAttributesAbsent(t *testing.T) {
var patchBody map[string]string

mux := http.NewServeMux()
mux.HandleFunc("/redfish/v1/", endpointFunc(t, "/dell/serviceroot.json"))
mux.HandleFunc("/redfish/v1/Systems", endpointFunc(t, "/dell/systems.json"))
mux.HandleFunc("/redfish/v1/Systems/System.Embedded.1", endpointFunc(t, "/dell/system.embedded.1.json"))
mux.HandleFunc("/redfish/v1/Systems/System.Embedded.1/Bios", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
// A BIOS with the protocol switches but no BootModeSelect.
_, _ = w.Write([]byte(`{
"@odata.type": "#Bios.v1_2_3.Bios",
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Bios",
"Id": "Bios",
"Name": "BIOS Configuration",
"AttributeRegistry": "BiosAttributeRegistryU32.v1_0_0",
"Attributes": {
"IPv4HTTPSupport": "Disabled",
"IPv6HTTPSupport": "Disabled",
"NetworkStack": "Disabled"
}
}`))
case http.MethodPatch:
var body struct {
Attributes map[string]string `json:"Attributes"`
}
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
patchBody = body.Attributes
w.WriteHeader(http.StatusNoContent)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
})

server := httptest.NewTLSServer(mux)
defer server.Close()

parsedURL, err := url.Parse(server.URL)
require.NoError(t, err)

ctx := context.Background()
client := NewClient(parsedURL.Hostname(), parsedURL.Port(), "", "", WithBasicAuthEnabled(true))
require.NoError(t, client.Open(ctx))
defer client.Close(ctx)

ok, err := client.SetNetworkBootEnabled(ctx, networkBootBoolPtr(true), nil)
require.NoError(t, err)
assert.True(t, ok)
assert.Equal(t, map[string]string{
"NetworkStack": "Enabled",
"IPv4HTTPSupport": "Enabled",
"IPv6HTTPSupport": "Enabled",
}, patchBody)
}

func TestSetNetworkBootEnabled_NeitherSet(t *testing.T) {
client := NewClient("unused", "", "", "")

Expand Down
Loading