Add setBootOveride config support to redfishtool

The recent upgrade of redfishtool from 1.1.5 to 1.1.8 introduced
a change that added a BootSourceOverrideMode key:value pair to the
payload of the 'setBootOverride Once <target>' patch request.

An issue was found were some or all servers from a manufacturer
exhibit undesirable behavior when the 'setBootOverride Once <target>'
patch request payload includes a BootSourceOverrideMode key:value pair.

The undesirable behavior ranges from outright command failure or
triggers the BMC to run a time consuming BIOS reconfiguraiton job
followed by an extra reboot.

This update remedies that by introducing a configuration file and
source code patch to the opensource redfishtool package and uses the
contents of the new configuraiton file and patch to change the
behavior of the setBootOverride command handling for select servers.

The configuration file contains a setBootOverride_overrides
section with two BootSourceOverrideMode exclude string lists,
one of server models and one of server manufacturers.

[setBootOverride_overrides]
   exclude_BootSourceOverrideMode_Models = "<model>", "<model>"
   exclude_BootSourceOverrideMode_Manufacturers = "<manufacturer>"

This update allows the new configuration file to be populated with
server model or manufacturer names that should exclude (not include)
the BootSourceOverrideMode as part of the payload thereby avoiding
the undesirable behavior.

If current server is found in either the models or manufacturers lists
then it is excluded from getting the BootSourceOverrideMode key:value
pair added to the 'setBootOverride Once <target>' patch request.

Test Plan:

PASS: Verify build and install of patched opensource redfishtool pkg
PASS: Verify install of AIO SX/AIO DX
PASS: - with/without excluded servers
PASS: - IPV4 and IPV6
PASS: Verify DC system install with HPE servers
PASS: Verify handling defaults to no exclusions if
PASS: - the new config file is missing
PASS: - the setBootOverride_overrides section is missing
PASS: - both exclude_BootSourceOverrideMode lists are missing
PASS: Verify handling when exclude_BootSourceOverrideMode lists
PASS: - are missing or empty.
PASS: - contain a badly formatted string like a missing a quote
PASS: Verify handling if a server provides an empty
PASS: - model string
PASS: - manufacturer string
PASS: Verify newly added debug logging (level 4 with -vvvv)
PASS: Verify HPE server setBootOverride Once Pxe works

Closes-Bug: 2091879
Change-Id: Idcee7be6edc62438fe29a9e8ce031ee94328e508
Signed-off-by: Eric MacDonald <eric.macdonald@windriver.com>
This commit is contained in:
Eric MacDonald 2024-12-16 15:58:59 +00:00
parent 6705cfca37
commit 4217f4ba6d
6 changed files with 150 additions and 85 deletions

View File

@ -1,3 +1,4 @@
usr/bin/redfishtool
usr/bin/redfishtool.py
usr/lib/python3/dist-packages/redfishtoollib/*
etc/redfishtool/redfishtool.ini

View File

@ -9,4 +9,5 @@ export PBR_VERSION=1.0.0
dh $@ --with python3 --buildsystem=pybuild
override_dh_install:
# nothing to do here
mkdir -p $(CURDIR)/debian/redfishtool/etc/redfishtool
cp $(CURDIR)/redfishtool.ini $(CURDIR)/debian/redfishtool/etc/redfishtool

View File

@ -0,0 +1,15 @@
[setBootOverride_overrides]
# The following list of server models should not receive a
# BootSourceOverrideMode key:value pair in the
# 'setBootOverride Once' PATCH request. When present:
# - HP Proliant servers fail the request.
# - Dell PowerEdge R7xxx servers initiate a BIOS reconfig update/reboot
# For specific servers - first filter
exclude_BootSourceOverrideMode_Models = "R740XD vSAN Ready Node", "PowerEdge R750"
# For entire manufacturers - second filter
exclude_BootSourceOverrideMode_Manufacturers = "HPE", "Dell Inc."
[Settings]

View File

@ -0,0 +1,131 @@
From: Eric Macdonald <eric.macdonald@windriver.com>
Date: Sun, 15 Dec 2024 22:41:27 +0000
Subject: Add config file based BootSourceOverrideMode handling to
setBootOveride
This patch reads /etc/redfishtool/redfishtool.ini config file in
search for the setBootOverride_overrides section that has a
list of manufacturers and models that should not receive the
BootSourceOverrideMode key:value pair as part of the payload
to the 'setBootOverride Once <target>' patch request.
[setBootOverride_overrides]
exclude_BootSourceOverrideMode_Models = "<model>", "<model>"
exclude_BootSourceOverrideMode_Manufacturers = "<manufacturer>"
If current server is found in either the models or manufacturers lists
then it is excluded from getting the BootSourceOverrideMode key:value
pair added to the 'setBootOverride Once <target>' patch request.
Signed-off-by: Eric Macdonald <eric.macdonald@windriver.com>
---
redfishtoollib/Systems.py | 78 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 73 insertions(+), 5 deletions(-)
diff --git a/redfishtoollib/Systems.py b/redfishtoollib/Systems.py
index a0be4ad..6abee66 100644
--- a/redfishtoollib/Systems.py
+++ b/redfishtoollib/Systems.py
@@ -503,6 +503,65 @@ class RfSystemsOperations():
return op.iterate_op(op.setIndicatorLed_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
+ def get_exclude_BootSourceOverrideMode(self, rft, d):
+ """
+ Query redfishtool.ini for models or manufacturers that should not
+ not have the BootSourceOverrideMode key:value pair added to the
+ setBootOverride patch request.
+ """
+
+ # include the config file parser
+ import configparser
+
+ manufacturer=d["Manufacturer"]
+ model=d["Model"]
+ rft.printVerbose(4, "Manufacturer: {}".format(d["Manufacturer"]))
+ rft.printVerbose(4, "Model : {}".format(d["Model"]))
+
+ # the config file path
+ config_file="/etc/redfishtool/redfishtool.ini"
+ config = configparser.ConfigParser()
+ # read the configuration file
+ files_read = config.read(config_file)
+ if not files_read:
+ rft.printVerbose(0, "Configuration file '{}' not found or could not be read.".format(config_file))
+ return(False)
+
+ section="setBootOverride_overrides"
+ exclude_BootSourceOverrideMode_Models="exclude_BootSourceOverrideMode_Models"
+ exclude_BootSourceOverrideMode_Manufacturers="exclude_BootSourceOverrideMode_Manufacturers"
+
+ # get the model and manufacturers exclude lists
+ exclude_models_list=[]
+ exclude_manufacturers_list=[]
+ exclude_BootSourceOverrideMode=False
+
+ # don't fail based on missing file. Just run with default behavior.
+ if section in config:
+ if exclude_BootSourceOverrideMode_Models not in config[section]:
+ rft.printVerbose(4, "Option '{}' not found in section '{}' of the configuration file {}."\
+ .format(exclude_BootSourceOverrideMode_Models, section, config_file))
+ else:
+ exclude_models_list = config[section][exclude_BootSourceOverrideMode_Models]
+ rft.printVerbose(4, "Models Exclude List: {}".format(exclude_models_list))
+
+ if exclude_BootSourceOverrideMode_Manufacturers not in config[section]:
+ rft.printVerbose(4, "Option '{}' not found in section '{}' of the configuration file {}."\
+ .format(exclude_BootSourceOverrideMode_Manufacturers, section, config_file))
+ else:
+ exclude_manufacturers_list = config[section][exclude_BootSourceOverrideMode_Manufacturers]
+ rft.printVerbose(4, "Manufacturer Exclude List: {}".format(exclude_manufacturers_list))
+
+ exclude_BootSourceOverrideMode=False
+ if model in exclude_models_list:
+ rft.printVerbose(0, "Excluding BootSourceOverrideMode for server model: '{}'".format(model))
+ exclude_BootSourceOverrideMode = True
+ elif manufacturer in exclude_manufacturers_list:
+ rft.printVerbose(0, "Excluding BootSourceOverrideMode for server manufacturer: '{}'".format(manufacturer))
+ exclude_BootSourceOverrideMode = True
+ return (exclude_BootSourceOverrideMode)
+
+
def setBootOverride_single(self,sc,op,rft,cmdTop=False, prop=None):
# this operation has argument syntaxes below:
# ...setBootOverride <enabledVal> [<targetVal>]
@@ -533,11 +592,17 @@ class RfSystemsOperations():
#now read target,
# we will need to check that the properties we are patching are there, and chk for etag hdr
# and to see if the value specified is one of the allowable values for this rhost
- rc,r,j,d=op.get(sc,op,rft,prop="Boot")
+ rc,r,j,d=op.get(sc,op,rft)
if(rc != 0):
print("Error, can't read boot properties from remote service")
return(8,None,False,None)
-
+
+ # this call will open the redfishtool.ini file and search for server models or
+ # manufacturers that don't support or behave well when the 'setBootOverride Once <target>'
+ # key:value pair is part of the payload of the PATCH method.
+ # If found then a True (to exclude) is returned.
+ exclude_BootSourceOverrideMode = op.get_exclude_BootSourceOverrideMode(rft, d)
+
# verify that they have a BootSourceOverrideEnabled prop
bootRes=d["Boot"]
if( not "BootSourceOverrideEnabled" in bootRes ):
@@ -587,10 +652,13 @@ class RfSystemsOperations():
# Get the value of "BootSourceOverrideTarget" property and pass it in the patch request.
# Some HW vendors need this property to be passed explicitly.
- if "BootSourceOverrideMode" in d["Boot"]:
- patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal, "BootSourceOverrideTarget": targetVal, "BootSourceOverrideMode": d["Boot"]["BootSourceOverrideMode"] } }
+ if exclude_BootSourceOverrideMode is True:
+ patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal,\
+ "BootSourceOverrideTarget": targetVal } }
else:
- patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal, "BootSourceOverrideTarget": targetVal } }
+ patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal,\
+ "BootSourceOverrideTarget": targetVal,\
+ "BootSourceOverrideMode": d["Boot"]["BootSourceOverrideMode"] } }
#call the generic patch command to send the patch. This takes care of etag support
rc,r,j,d=rft.patchResource(rft, r, patchData)

View File

@ -1,83 +0,0 @@
From: Eric Macdonald <eric.macdonald@windriver.com>
Date: Tue, 10 Dec 2024 17:05:13 +0000
Subject: Add retry to BootSourceOverrideMode handling
Redfishtool version 1.1.8 added a condition where if
'BootSourceOverrideMode' exists in the boot query response
then it adds the following to the Boot Source Override command
json string for the setBootOverride_single command.
"BootSourceOverrideMode": d["Boot"]["BootSourceOverrideMode"]
This change is leading to this Bad Request error for some servers.
redfishtool: Transport: Response Error: status_code: 400 -- Bad Request
Servers known to fail in this way are HP Proliant e910t, e920t as well
as DL360 and DL380 server models with HP iLO versions 3.01, 3.04 and
3.6.
This update introduces a legacy mode retry to the setBootOverride
operation if the new method fails in a 400 - Bad Request.
Signed-off-by: Eric Macdonald <eric.macdonald@windriver.com>
---
redfishtoollib/Systems.py | 30 ++++++++++++++++++++++--------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/redfishtoollib/Systems.py b/redfishtoollib/Systems.py
index a0be4ad..e2cbe01 100644
--- a/redfishtoollib/Systems.py
+++ b/redfishtoollib/Systems.py
@@ -182,7 +182,7 @@ class RfSystemsOperations():
def __init__(self):
self.systemsPath=None
self.systemsCollectionDict=None
-
+ self.setBootOverride_legacy_mode=False
def hello(self,sc,op,rft,cmdTop=False):
rft.printVerbose(4,"in hello")
@@ -584,13 +584,19 @@ class RfSystemsOperations():
return(8,None,False,None)
#form the patch data
-
- # Get the value of "BootSourceOverrideTarget" property and pass it in the patch request.
- # Some HW vendors need this property to be passed explicitly.
- if "BootSourceOverrideMode" in d["Boot"]:
- patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal, "BootSourceOverrideTarget": targetVal, "BootSourceOverrideMode": d["Boot"]["BootSourceOverrideMode"] } }
- else:
+ if self.setBootOverride_legacy_mode:
+ # Some servers don't like the BootSourceOverrideMode added to the payload
+ # even when BootSourceOverrideMode is set in d["Boot"].
+ # The caller has the option to set self.setBootOverride_legacy_mode True
+ # and call this function to use the 'legacy' mode patchData.
patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal, "BootSourceOverrideTarget": targetVal } }
+ else:
+ # Get the value of "BootSourceOverrideTarget" property and pass it in the patch request.
+ # Some HW vendors need this property to be passed explicitly.
+ if "BootSourceOverrideMode" in d["Boot"]:
+ patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal, "BootSourceOverrideTarget": targetVal, "BootSourceOverrideMode": d["Boot"]["BootSourceOverrideMode"] } }
+ else:
+ patchData={"Boot": {"BootSourceOverrideEnabled": enabledVal, "BootSourceOverrideTarget": targetVal } }
#call the generic patch command to send the patch. This takes care of etag support
rc,r,j,d=rft.patchResource(rft, r, patchData)
@@ -605,7 +611,15 @@ class RfSystemsOperations():
def setBootOverride(self, sc, op, rft, cmdTop=False, prop=None):
- return op.iterate_op(op.setBootOverride_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
+ rc,r,j,d=op.iterate_op(op.setBootOverride_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
+ if ( rc == 5 and r is not None ):
+ # added a retry in legacyMode if the http response
+ # in 'r' is <Response [400]> - Bad Request
+ if r.status_code == 400:
+ rft.printVerbose(0," redfishtool: setBootOverride operation failed, retrying in legacy mode")
+ self.setBootOverride_legacy_mode = True
+ rc,r,j,d=op.iterate_op(op.setBootOverride_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
+ return(rc,r,j,d)
def getProcessors(self,sc,op, rft, cmdTop=False, prop=None):

View File

@ -1,2 +1,2 @@
0001-1.1.8-versioning.patch
0002-Add-retry-to-BootSourceOverrideMode-handling.patch
0002-Add-config-file-based-BootSourceOverrideMode-handlin.patch