From b5b23efdcc8fc9f0feef00f247013ac131525bf8 Mon Sep 17 00:00:00 2001 From: Bill Dodd Date: Mon, 17 Feb 2020 15:19:47 -0600 Subject: [PATCH] Add SimpleUpdate command (#65074) * add SimpleUpdate command * add AllowableValues check for Targets property --- lib/ansible/module_utils/redfish_utils.py | 87 ++++++++++++++++++ .../redfish/redfish_command.py | 91 ++++++++++++++++++- 2 files changed, 177 insertions(+), 1 deletion(-) diff --git a/lib/ansible/module_utils/redfish_utils.py b/lib/ansible/module_utils/redfish_utils.py index b16ddf07bf8..8fc6b42e4d3 100644 --- a/lib/ansible/module_utils/redfish_utils.py +++ b/lib/ansible/module_utils/redfish_utils.py @@ -1255,6 +1255,93 @@ class RedfishUtils(object): else: return self._software_inventory(self.software_uri) + def _get_allowable_values(self, action, name, default_values=None): + if default_values is None: + default_values = [] + allowable_values = None + # get Allowable values from ActionInfo + if '@Redfish.ActionInfo' in action: + action_info_uri = action.get('@Redfish.ActionInfo') + response = self.get_request(self.root_uri + action_info_uri) + if response['ret'] is True: + data = response['data'] + if 'Parameters' in data: + params = data['Parameters'] + for param in params: + if param.get('Name') == name: + allowable_values = param.get('AllowableValues') + break + # fallback to @Redfish.AllowableValues annotation + if allowable_values is None: + prop = '%s@Redfish.AllowableValues' % name + if prop in action: + allowable_values = action[prop] + # fallback to default values + if allowable_values is None: + allowable_values = default_values + return allowable_values + + def simple_update(self, update_opts): + image_uri = update_opts.get('update_image_uri') + protocol = update_opts.get('update_protocol') + targets = update_opts.get('update_targets') + creds = update_opts.get('update_creds') + + if not image_uri: + return {'ret': False, 'msg': + 'Must specify update_image_uri for the SimpleUpdate command'} + + response = self.get_request(self.root_uri + self.update_uri) + if response['ret'] is False: + return response + data = response['data'] + if 'Actions' not in data: + return {'ret': False, 'msg': 'Service does not support SimpleUpdate'} + if '#UpdateService.SimpleUpdate' not in data['Actions']: + return {'ret': False, 'msg': 'Service does not support SimpleUpdate'} + action = data['Actions']['#UpdateService.SimpleUpdate'] + if 'target' not in action: + return {'ret': False, 'msg': 'Service does not support SimpleUpdate'} + update_uri = action['target'] + if protocol: + default_values = ['CIFS', 'FTP', 'SFTP', 'HTTP', 'HTTPS', 'NSF', + 'SCP', 'TFTP', 'OEM', 'NFS'] + allowable_values = self._get_allowable_values(action, + 'TransferProtocol', + default_values) + if protocol not in allowable_values: + return {'ret': False, + 'msg': 'Specified update_protocol (%s) not supported ' + 'by service. Supported protocols: %s' % + (protocol, allowable_values)} + if targets: + allowable_values = self._get_allowable_values(action, 'Targets') + if allowable_values: + for target in targets: + if target not in allowable_values: + return {'ret': False, + 'msg': 'Specified target (%s) not supported ' + 'by service. Supported targets: %s' % + (target, allowable_values)} + + payload = { + 'ImageURI': image_uri + } + if protocol: + payload["TransferProtocol"] = protocol + if targets: + payload["Targets"] = targets + if creds: + if creds.get('username'): + payload["Username"] = creds.get('username') + if creds.get('password'): + payload["Password"] = creds.get('password') + response = self.post_request(self.root_uri + update_uri, payload) + if response['ret'] is False: + return response + return {'ret': True, 'changed': True, + 'msg': "SimpleUpdate requested"} + def get_bios_attributes(self, systems_uri): result = {} bios_attributes = {} diff --git a/lib/ansible/modules/remote_management/redfish/redfish_command.py b/lib/ansible/modules/remote_management/redfish/redfish_command.py index e490043a2a1..4fbd8e3f9bd 100644 --- a/lib/ansible/modules/remote_management/redfish/redfish_command.py +++ b/lib/ansible/modules/remote_management/redfish/redfish_command.py @@ -119,6 +119,42 @@ options: - The ID of the System, Manager or Chassis to modify type: str version_added: "2.10" + update_image_uri: + required: false + description: + - The URI of the image for the update + type: str + version_added: "2.10" + update_protocol: + required: false + description: + - The protocol for the update + type: str + version_added: "2.10" + update_targets: + required: false + description: + - The list of target resource URIs to apply the update to + type: list + elements: str + version_added: "2.10" + update_creds: + required: false + description: + - The credentials for retrieving the update image + type: dict + suboptions: + username: + required: false + description: + - The username for retrieving the update image + type: str + password: + required: false + description: + - The password for retrieving the update image + type: str + version_added: "2.10" author: "Jose Delarosa (@jose-delarosa)" ''' @@ -302,6 +338,30 @@ EXAMPLES = ''' baseuri: "{{ baseuri }}" username: "{{ username }}" password: "{{ password }}" + + - name: Simple update + redfish_command: + category: Update + command: SimpleUpdate + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + update_image_uri: https://example.com/myupdate.img + + - name: Simple update with additional options + redfish_command: + category: Update + command: SimpleUpdate + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + update_image_uri: //example.com/myupdate.img + update_protocol: FTP + update_targets: + - /redfish/v1/UpdateService/FirmwareInventory/BMC + update_creds: + username: operator + password: supersecretpwd ''' RETURN = ''' @@ -327,6 +387,7 @@ CATEGORY_COMMANDS_ALL = { "UpdateAccountServiceProperties"], "Sessions": ["ClearSessions"], "Manager": ["GracefulRestart", "ClearLogs"], + "Update": ["SimpleUpdate"] } @@ -349,7 +410,17 @@ def main(): timeout=dict(type='int', default=10), uefi_target=dict(), boot_next=dict(), - resource_id=dict() + resource_id=dict(), + update_image_uri=dict(), + update_protocol=dict(), + update_targets=dict(type='list', elements='str', default=[]), + update_creds=dict( + type='dict', + options=dict( + username=dict(), + password=dict() + ) + ) ), supports_check_mode=False ) @@ -375,6 +446,14 @@ def main(): # System, Manager or Chassis ID to modify resource_id = module.params['resource_id'] + # update options + update_opts = { + 'update_image_uri': module.params['update_image_uri'], + 'update_protocol': module.params['update_protocol'], + 'update_targets': module.params['update_targets'], + 'update_creds': module.params['update_creds'] + } + # Build root URI root_uri = "https://" + module.params['baseuri'] rf_utils = RedfishUtils(creds, root_uri, timeout, module, @@ -466,6 +545,16 @@ def main(): for command in command_list: result = MANAGER_COMMANDS[command]() + elif category == "Update": + # execute only if we find UpdateService resources + resource = rf_utils._find_updateservice_resource() + if resource['ret'] is False: + module.fail_json(msg=resource['msg']) + + for command in command_list: + if command == "SimpleUpdate": + result = rf_utils.simple_update(update_opts) + # Return data back or fail with proper message if result['ret'] is True: del result['ret']