From fd1b34e85134855aa57bb24c89c082cf667ec123 Mon Sep 17 00:00:00 2001 From: Mike Raineri Date: Fri, 9 Feb 2024 13:15:03 -0500 Subject: [PATCH] Added option to set an apply time for update requests Signed-off-by: Mike Raineri --- README.md | 2 ++ redfish_utilities/__init__.py | 1 + redfish_utilities/update.py | 24 ++++++++++++++++++++++-- scripts/rf_update.py | 7 ++++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 00d67ae..b582570 100644 --- a/README.md +++ b/README.md @@ -608,6 +608,8 @@ optional arguments: -h, --help show this help message and exit --target TARGET, -t TARGET The target resource to apply the image + --applytime {Immediate,OnReset,AtMaintenanceWindowStart,InMaintenanceWindowOnReset,OnStartUpdateRequest}, -at {Immediate,OnReset,AtMaintenanceWindowStart,InMaintenanceWindowOnReset,OnStartUpdateRequest} + The apply time for the update --debug Creates debug file showing HTTP traces and exceptions ``` diff --git a/redfish_utilities/__init__.py b/redfish_utilities/__init__.py index 57f3ff4..018e99a 100644 --- a/redfish_utilities/__init__.py +++ b/redfish_utilities/__init__.py @@ -80,6 +80,7 @@ from .systems import set_system_bios from .systems import print_system_bios from .tasks import poll_task_monitor +from .update import operation_apply_times from .update import get_update_service from .update import get_simple_update_info from .update import simple_update diff --git a/redfish_utilities/update.py b/redfish_utilities/update.py index ea28b16..7c56a15 100644 --- a/redfish_utilities/update.py +++ b/redfish_utilities/update.py @@ -17,6 +17,7 @@ import errno import math from .messages import verify_response +from enum import Enum class RedfishUpdateServiceNotFoundError( Exception ): """ @@ -24,6 +25,19 @@ class RedfishUpdateServiceNotFoundError( Exception ): """ pass +class operation_apply_times( Enum ): + """ + Values for operation apply time settings + """ + IMMEDIATE = "Immediate" + ON_RESET = "OnReset" + AT_MAINTENANCE_WINDOW_START = "AtMaintenanceWindowStart" + AT_MAINTENANCE_WINDOW_ON_RESET = "InMaintenanceWindowOnReset" + ON_START_UPDATE_REQUEST = "OnStartUpdateRequest" + + def __str__( self ): + return self.value + def get_simple_update_info( context ): """ Locates the SimpleUpdate action and collects its information @@ -93,7 +107,7 @@ def get_simple_update_info( context ): return simple_update_uri, simple_update_parameters -def simple_update( context, image_uri, protocol = None, targets = None, username = None, password = None ): +def simple_update( context, image_uri, protocol = None, targets = None, username = None, password = None, apply_time = None ): """ Performs a SimpleUpdate request @@ -104,6 +118,7 @@ def simple_update( context, image_uri, protocol = None, targets = None, username targets: The targets receiving the update username: The username for retrieving the update for the given URI password: The password for retrieving the update for the given URI + apply_time: The apply time for the update Returns: The response from the request @@ -124,6 +139,8 @@ def simple_update( context, image_uri, protocol = None, targets = None, username body["Username"] = username if password is not None: body["Password"] = password + if apply_time is not None: + body["@Redfish.OperationApplyTime"] = apply_time.value response = context.post( uri, body = body ) verify_response( response ) @@ -149,7 +166,7 @@ def get_size( file_path, unit = 'bytes' ): size = file_size / 1024 ** exponents_map[unit] return round( size, 3 ) -def multipart_push_update( context, image_path, targets = None, timeout = None ): +def multipart_push_update( context, image_path, targets = None, timeout = None, apply_time = None ): """ Performs an HTTP Multipart push update request @@ -158,6 +175,7 @@ def multipart_push_update( context, image_path, targets = None, timeout = None ) image_path: The filepath to the image for the update targets: The targets receiving the update timeout: The timeout to apply to the update + apply_time: The apply time for the update Returns: The response from the request @@ -191,6 +209,8 @@ def multipart_push_update( context, image_path, targets = None, timeout = None ) update_parameters = {} if targets is not None: update_parameters["Targets"] = targets + if apply_time is not None: + update_parameters["@Redfish.OperationApplyTime"] = apply_time.value body = { "UpdateParameters": ( None, json.dumps( update_parameters ), "application/json" ), "UpdateFile": ( image_path.split( os.path.sep )[-1], open( image_path, "rb" ), "application/octet-stream" ) diff --git a/scripts/rf_update.py b/scripts/rf_update.py index c0b70b7..bcf857e 100644 --- a/scripts/rf_update.py +++ b/scripts/rf_update.py @@ -87,6 +87,7 @@ def print_error_payload( response ): argget.add_argument( "--rhost", "-r", type = str, required = True, help = "The address of the Redfish service (with scheme)" ) argget.add_argument( "--image", "-i", type = str, required = True, help = "The URI or filepath of the image" ) argget.add_argument( "--target", "-t", type = str, help = "The target resource to apply the image" ) +argget.add_argument( "--applytime", "-at", type = redfish_utilities.operation_apply_times, help = "The apply time for the update", choices = redfish_utilities.operation_apply_times ) argget.add_argument( "--debug", action = "store_true", help = "Creates debug file showing HTTP traces and exceptions" ) args = argget.parse_args() @@ -120,7 +121,7 @@ def print_error_payload( response ): if "MultipartHttpPushUri" in update_service.dict: # Perform a multipart push update print( "Pushing the image to the service directly; depending on the size of the image, this can take a few minutes..." ) - response = redfish_utilities.multipart_push_update( redfish_obj, args.image, targets = targets ) + response = redfish_utilities.multipart_push_update( redfish_obj, args.image, targets = targets, apply_time = args.applytime ) else: # Host a local web server and perform a SimpleUpdate for the local image web_server_thread = threading.Thread( target = local_web_server, args=( args.image, ) ) @@ -143,10 +144,10 @@ def print_error_payload( response ): s.connect( ( groups.group(2), int( remote_port ) ) ) image_uri = "http://{}:{}/{}".format( s.getsockname()[0], WEB_SERVER_PORT, args.image.rsplit( os.path.sep, 1 )[-1] ) s.close() - response = redfish_utilities.simple_update( redfish_obj, image_uri, targets = targets ) + response = redfish_utilities.simple_update( redfish_obj, image_uri, targets = targets, apply_time = args.applytime ) else: # Remote image; always use SimpleUpdate - response = redfish_utilities.simple_update( redfish_obj, args.image, targets = targets ) + response = redfish_utilities.simple_update( redfish_obj, args.image, targets = targets, apply_time = args.applytime ) # Monitor the response print( "Update initiated..." )