Skip to content

Commit

Permalink
Merge pull request #57 from DMTF/Fix55-Enhancing-Error-Reporting-for-…
Browse files Browse the repository at this point in the history
…BIOS

Added capability to perform workarounds for BIOS settings based on well-known possible settings URIs
  • Loading branch information
mraineri authored Aug 6, 2021
2 parents c602a85 + 120ff2a commit b29e992
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 12 deletions.
85 changes: 75 additions & 10 deletions redfish_utilities/systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
with the systems collection for a given Redfish service
"""

import warnings
from .messages import verify_response
from .resets import reset_types

Expand All @@ -23,7 +24,24 @@ class RedfishSystemNotFoundError( Exception ):

class RedfishSystemResetNotFoundError( Exception ):
"""
Raised when the Reset action cannot be found
Raised when the reset action cannot be found
"""
pass

class RedfishSystemBootNotFoundError( Exception ):
"""
Raised when the boot object cannot be found
"""
pass
class RedfishSystemBiosNotFoundError( Exception ):
"""
Raised when the BIOS resource cannot be found
"""
pass

class RedfishSystemBiosInvalidSettingsError( Exception ):
"""
Raised when the BIOS resource contains a settings object, but it's not rendered properly
"""
pass

Expand Down Expand Up @@ -115,6 +133,8 @@ def get_system_boot( context, system_id = None ):
"""

system = get_system( context, system_id )
if "Boot" not in system.dict:
raise RedfishSystemBootNotFoundError( "System '{}' does not contain the boot object".format( system.dict["Id"] ) )
return system.dict["Boot"]

def set_system_boot( context, system_id = None, ov_target = None, ov_enabled = None, ov_mode = None, ov_uefi_target = None, ov_boot_next = None ):
Expand Down Expand Up @@ -214,9 +234,9 @@ def get_system_reset_info( context, system_id = None ):

# Check that there is a Reset action
if "Actions" not in system.dict:
raise RedfishSystemResetNotFoundError( "System does not support Reset" )
raise RedfishSystemResetNotFoundError( "System '{}' does not support the reset action".format( system.dict["Id"] ) )
if "#ComputerSystem.Reset" not in system.dict["Actions"]:
raise RedfishSystemResetNotFoundError( "System does not support Reset" )
raise RedfishSystemResetNotFoundError( "System '{}' does not support the reset action".format( system.dict["Id"] ) )

# Extract the info about the Reset action
reset_action = system.dict["Actions"]["#ComputerSystem.Reset"]
Expand Down Expand Up @@ -487,18 +507,19 @@ def get_virtual_media_collection( context, system_id = None ):
virtual_media_uri = manager_resp.dict["VirtualMedia"]["@odata.id"]
break
if virtual_media_uri is None:
raise RedfishVirtualMediaNotFoundError( "System does not support virtual media" )
raise RedfishVirtualMediaNotFoundError( "System '{}' does not support virtual media".format( system.dict["Id"] ) )

# Get the VirtualMediaCollection
return context.get( virtual_media_uri )

def get_system_bios( context, system_id = None ):
def get_system_bios( context, system_id = None, workaround = False ):
"""
Finds a system matching the given ID and gets the BIOS settings
Args:
context: The Redfish client object with an open session
system_id: The system to locate; if None, perform on the only system
workaround: Indicates if workarounds should be attempted for non-conformant services
Returns:
A dictionary of the current BIOS attributes
Expand All @@ -509,25 +530,34 @@ def get_system_bios( context, system_id = None ):
system = get_system( context, system_id )

# Get the Bios resource
if "Bios" not in system.dict:
raise RedfishSystemBiosNotFoundError( "System '{}' does not support representing BIOS".format( system.dict["Id"] ) )
bios = context.get( system.dict["Bios"]["@odata.id"] )
current_settings = bios.dict["Attributes"]
future_settings = bios.dict["Attributes"]

# Get the Settings object if present
if "@Redfish.Settings" in bios.dict:
bios_settings = context.get( bios.dict["@Redfish.Settings"]["SettingsObject"]["@odata.id"] )
future_settings = bios_settings.dict["Attributes"]
try:
bios_settings = get_system_bios_settings( context, bios, system.dict["Id"], workaround )
future_settings = bios_settings.dict["Attributes"]
except:
if workaround:
warnings.warn( "System '{}' BIOS resource contains the settings term, but no 'SettingsObject'. Contact your vendor. Workarounds exhausted for reading the settings data and falling back on using the active attributes.".format( system_id ) )
else:
raise

return current_settings, future_settings

def set_system_bios( context, settings, system_id = None ):
def set_system_bios( context, settings, system_id = None, workaround = False ):
"""
Finds a system matching the given ID and sets the BIOS settings
Args:
context: The Redfish client object with an open session
settings: The settings to apply to the system
system_id: The system to locate; if None, perform on the only system
workaround: Indicates if workarounds should be attempted for non-conformant services
Returns:
The response of the PATCH
Expand All @@ -537,12 +567,14 @@ def set_system_bios( context, settings, system_id = None ):
system = get_system( context, system_id )

# Get the BIOS resource and determine if the settings need to be applied to the resource itself or the settings object
if "Bios" not in system.dict:
raise RedfishSystemBiosNotFoundError( "System '{}' does not support representing BIOS".format( system.dict["Id"] ) )
bios_uri = system.dict["Bios"]["@odata.id"]
bios = context.get( bios_uri )
etag = bios.getheader( "ETag" )
if "@Redfish.Settings" in bios.dict:
bios_uri = bios.dict["@Redfish.Settings"]["SettingsObject"]["@odata.id"]
bios_settings = context.get( bios_uri )
bios_settings = get_system_bios_settings( context, bios, system.dict["Id"], workaround )
bios_uri = bios_settings.dict["@odata.id"]
etag = bios_settings.getheader( "ETag" )

# Update the settings
Expand All @@ -554,6 +586,39 @@ def set_system_bios( context, settings, system_id = None ):
verify_response( response )
return response

def get_system_bios_settings( context, bios, system_id, workaround ):
"""
Gets the settings resource for BIOS and applies workarounds if needed
Args:
context: The Redfish client object with an open session
bios: The BIOS resource
system_id: The system identifier
workaround: Indicates if workarounds should be attempted for non-conformant services
Returns:
The Settings resource for BIOS
"""

if "SettingsObject" in bios.dict["@Redfish.Settings"]:
bios_settings = context.get( bios.dict["@Redfish.Settings"]["SettingsObject"]["@odata.id"] )
else:
if workaround:
warnings.warn( "System '{}' BIOS resource contains the settings term, but no 'SettingsObject'. Contact your vendor. Attempting workarounds...".format( system_id ) )
settings_uris = [ "Settings", "SD" ]
for setting_ext in settings_uris:
bios_settings = context.get( bios.dict["@odata.id"] + "/" + setting_ext )
if bios_settings.status == 200:
break
try:
verify_response( bios_settings )
except:
raise RedfishSystemBiosInvalidSettingsError( "System '{}' BIOS resource contains the settings term, but no 'SettingsObject'. Workarounds exhausted. Contact your vendor.".format( system_id ) ) from None
else:
raise RedfishSystemBiosInvalidSettingsError( "System '{}' BIOS resource contains the settings term, but no 'SettingsObject'. Contact your vendor, or retry with the 'workaround' flag.".format( system_id ) )

return bios_settings

def print_system_bios( current_settings, future_settings ):
"""
Prints the system BIOS settings into a table
Expand Down
5 changes: 3 additions & 2 deletions scripts/rf_bios_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
argget.add_argument( "--rhost", "-r", type = str, required = True, help = "The address of the Redfish service (with scheme)" )
argget.add_argument( "--system", "-s", type = str, help = "The ID of the system to manage" )
argget.add_argument( "--attribute", "-a", type = str, nargs = 2, metavar = ( "name", "value" ), action = "append", help = "Sets a BIOS attribute to a new value; can be supplied multiple times to set multiple attributes" )
argget.add_argument( "--workaround", "-workaround", action = "store_true", help = "Indicates if workarounds should be attempted for non-conformant services", default = False )
args = argget.parse_args()

# Set up the Redfish object
Expand All @@ -30,7 +31,7 @@

try:
# Get the BIOS settings
current_settings, future_settings = redfish_utilities.get_system_bios( redfish_obj, args.system )
current_settings, future_settings = redfish_utilities.get_system_bios( redfish_obj, args.system, args.workaround )

if args.attribute is not None:
new_settings = {}
Expand All @@ -54,7 +55,7 @@
# Set the specified attribute to the new value
new_settings[attribute[0]] = new_value
print( "Setting {} to {}...".format( attribute[0], attribute[1] ) )
redfish_utilities.set_system_bios( redfish_obj, new_settings, args.system )
redfish_utilities.set_system_bios( redfish_obj, new_settings, args.system, args.workaround )
else:
# Print the BIOS settings
redfish_utilities.print_system_bios( current_settings, future_settings )
Expand Down

0 comments on commit b29e992

Please sign in to comment.