diff --git a/ansible_collections/netapp/ontap/README.md b/ansible_collections/netapp/ontap/README.md index 99cfe1c5..ef6cc7f2 100644 --- a/ansible_collections/netapp/ontap/README.md +++ b/ansible_collections/netapp/ontap/README.md @@ -24,6 +24,11 @@ Join our Slack Channel at [Netapp.io](http://netapp.io/slack) ## 20.6.1 +### New Options: +- na_ontap_firmware_upgrade: `reboot_sp`: reboot service processor before downloading package. +- na_ontap_firmware_upgrade: `rename_package`: rename file when downloading service processor package. +- na_ontap_firmware_upgrade: `replace_package`: replace local file when downloading service processor package. + ### Bug Fixes - na_ontap_firmware_upgrade: images are not downloaded, but the module reports success. - na_ontap_user: fixed KeyError if password is not provided. diff --git a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_firmware_upgrade.py b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_firmware_upgrade.py index 11c3b1dd..4790148d 100644 --- a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_firmware_upgrade.py +++ b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_firmware_upgrade.py @@ -54,6 +54,7 @@ - URL of the package file containing the firmware to be downloaded. - Once the package file is downloaded to a node, the firmware update will happen automatically in background. - For SP, the upgrade will happen automatically when a node is rebooted. + - For SP, the upgrade will happen automatically if autoupdate is enabled (which is the recommended setting). version_added: "20.4.1" type: str force_disruptive_update: @@ -107,6 +108,25 @@ type: bool default: false version_added: "20.6.0" + rename_package: + description: + - Rename the package. + - Only available if 'firmware_type' is 'service-processor'. + type: str + version_added: "20.6.1" + replace_package: + description: + - Replace the local package. + - Only available if 'firmware_type' is 'service-processor'. + type: bool + version_added: "20.6.1" + reboot_sp: + description: + - Reboot service processor before downloading package. + - Only available if 'firmware_type' is 'service-processor'. + type: bool + default: true + version_added: "20.6.1" short_description: NetApp ONTAP firmware upgrade for SP, shelf, ACP, and disk. version_added: "2.9" ''' @@ -142,6 +162,35 @@ hostname: "{{ netapp_hostname }}" username: "{{ netapp_username }}" password: "{{ netapp_password }}" + - name: SP firmware download replace package + tags: + - sp_download + na_ontap_firmware_upgrade: + state: present + node: vsim1 + package_url: "{{ web_link }}" + firmware_type: service-processor + replace_package: true + reboot_sp: true + hostname: "{{ netapp_hostname }}" + username: "{{ netapp_username }}" + password: "{{ netapp_password }}" + https: true + validate_certs: false + - name: SP firmware download rename package + tags: + - sp_download + na_ontap_firmware_upgrade: + state: present + node: vsim1 + package_url: "{{ web_link }}" + firmware_type: service-processor + rename_package: SP_FW.zip + hostname: "{{ netapp_hostname }}" + username: "{{ netapp_username }}" + password: "{{ netapp_password }}" + https: true + validate_certs: false - name: ACP firmware download and upgrade na_ontap_firmware_upgrade: state: present @@ -218,6 +267,9 @@ def __init__(self): package_url=dict(required=False, type='str'), force_disruptive_update=dict(required=False, type='bool', default=False), fail_on_502_error=dict(required=False, type='bool', default=False), + rename_package=dict(required=False, type='str'), + replace_package=dict(required=False, type='bool'), + reboot_sp=dict(required=False, type='bool', default=True) )) self.module = AnsibleModule( @@ -225,7 +277,7 @@ def __init__(self): required_if=[ ('firmware_type', 'acp', ['node']), ('firmware_type', 'disk', ['node']), - ('firmware_type', 'service-processor', ['node', 'update_type']), + ('firmware_type', 'service-processor', ['node']), ('force_disruptive_update', True, ['firmware_type']), ], supports_check_mode=True @@ -511,6 +563,77 @@ def download_firmware(self): return msg + def download_sp_image(self): + fetch_package = netapp_utils.zapi.NaElement('system-image-fetch-package') + fetch_package.add_new_child('node', self.parameters['node']) + fetch_package.add_new_child('package', self.parameters['package_url']) + if self.parameters.get('rename_package'): + fetch_package.add_new_child('rename-package', self.parameters['rename_package']) + if self.parameters.get('replace_package'): + fetch_package.add_new_child('replace-package', str(self.parameters['replace_package'])) + try: + self.server.invoke_successfully(fetch_package, enable_tunneling=True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error fetching system image package from %s: %s' + % (self.parameters['package_url'], to_native(error)), + exception=traceback.format_exc()) + + def download_sp_image_progress(self): + progress = netapp_utils.zapi.NaElement('system-image-update-progress-get') + progress.add_new_child('node', self.parameters['node']) + progress_info = dict() + try: + result = self.server.invoke_successfully(progress, enable_tunneling=True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error fetching system image package download progress: %s' + % (to_native(error)), exception=traceback.format_exc()) + if result.get_child_by_name('phase'): + progress_info['phase'] = result.get_child_content('phase') + else: + progress_info['phase'] = None + if result.get_child_by_name('exit-message'): + progress_info['exit_message'] = result.get_child_content('exit-message') + else: + progress_info['exit_message'] = None + if result.get_child_by_name('exit-status'): + progress_info['exit_status'] = result.get_child_content('exit-status') + else: + progress_info['exit_status'] = None + if result.get_child_by_name('last-message'): + progress_info['last_message'] = result.get_child_content('last-message') + else: + progress_info['last_message'] = None + if result.get_child_by_name('run-status'): + progress_info['run_status'] = result.get_child_content('run-status') + else: + progress_info['run_status'] = None + return progress_info + + def reboot_sp(self): + reboot = netapp_utils.zapi.NaElement('service-processor-reboot') + reboot.add_new_child('node', self.parameters['node']) + try: + self.server.invoke_successfully(reboot, enable_tunneling=True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error rebooting service processor: %s' + % (to_native(error)), + exception=traceback.format_exc()) + + def download_sp_firmware(self): + if self.parameters.get('reboot_sp'): + self.reboot_sp() + self.download_sp_image() + progress = self.download_sp_image_progress() + # progress only show the current or most recent update/install operation. + if progress['phase'] == 'Download': + while progress['run_status'] is not None and progress['run_status'] != 'Exited': + time.sleep(10) + progress = self.download_sp_image_progress() + if progress['exit_status'] != 'Success': + self.module.fail_json(msg=progress['exit_message'], exception=traceback.format_exc()) + return MSGS['dl_completed'] + return MSGS['no_action'] + def autosupport_log(self): """ Autosupport log for software_update @@ -530,7 +653,10 @@ def apply(self): firmware_update_progress = dict() if self.parameters.get('package_url'): if not self.module.check_mode: - msg = self.download_firmware() + if self.parameters.get('firmware_type') == 'service-processor': + msg = self.download_sp_firmware() + else: + msg = self.download_firmware() changed = True if not self.parameters['force_disruptive_update']: # disk_qual, disk, shelf, and ACP are automatically updated in background