#!/usr/bin/python # -*- coding: utf-8 -*- # # Dell EMC OpenManage Ansible Modules # Version 2.0 # Copyright (C) 2018-2019 Dell Inc. or its subsidiaries. All Rights Reserved. # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import (absolute_import, division, print_function) __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = r''' --- module: idrac_firmware short_description: Firmware update from a repository on a network share (CIFS, NFS). description: - Update the Firmware by connecting to a network share (either CIFS or NFS) that contains a catalog of available updates. - Network share should contain a valid repository of Update Packages (DUPs) and a catalog file describing the DUPs. - All applicable updates contained in the repository are applied to the system. - This feature is available only with iDRAC Enterprise License. options: idrac_ip: description: iDRAC IP Address. type: str required: True idrac_user: description: iDRAC username. type: str required: True idrac_password: description: iDRAC user password. type: str required: True aliases: ['idrac_pwd'] idrac_port: description: iDRAC port. type: int default: 443 share_name: description: CIFS or NFS Network share. type: str required: True share_user: description: Network share user in the format 'user@domain' or 'domain\\user' if user is part of a domain else 'user'. This option is mandatory for CIFS Network Share. type: str share_password: description: Network share user password. This option is mandatory for CIFS Network Share. type: str aliases: ['share_pwd'] share_mnt: description: Local mount path of the network share with read-write permission for ansible user. This option is mandatory for Network Share. type: str required: True reboot: description: Whether to reboots after applying the updates or not. type: bool default: false job_wait: description: Whether to wait for job completion or not. type: bool default: true catalog_file_name: required: False description: Catalog file name relative to the I(share_name). type: str default: 'Catalog.xml' requirements: - "omsdk" - "python >= 2.7.5" author: "Rajeev Arakkal (@rajeevarakkal)" ''' EXAMPLES = """ --- - name: Update firmware from repository on a Network Share idrac_firmware: idrac_ip: "192.168.0.1" idrac_user: "user_name" idrac_password: "user_password" share_name: "192.168.0.0:/share" share_user: "share_user_name" share_password: "share_user_pwd" share_mnt: "/mnt/share" reboot: True job_wait: True catalog_file_name: "Catalog.xml" """ RETURN = """ --- msg: type: str description: Over all firmware update status. returned: always sample: "Successfully updated the firmware." update_status: type: dict description: Firmware Update job and progress details from the iDRAC. returned: success sample: { 'InstanceID': 'JID_XXXXXXXXXXXX', 'JobState': 'Completed', 'Message': 'Job completed successfully.', 'MessageId': 'REDXXX', 'Name': 'Repository Update', 'JobStartTime': 'NA', 'Status': 'Success', } """ from ansible_collections.community.general.plugins.module_utils.remote_management.dellemc.dellemc_idrac import iDRACConnection from ansible.module_utils.basic import AnsibleModule try: from omsdk.sdkcreds import UserCredentials from omsdk.sdkfile import FileOnShare HAS_OMSDK = True except ImportError: HAS_OMSDK = False def _validate_catalog_file(catalog_file_name): normilized_file_name = catalog_file_name.lower() if not normilized_file_name: raise ValueError('catalog_file_name should be a non-empty string.') elif not normilized_file_name.endswith("xml"): raise ValueError('catalog_file_name should be an XML file.') def update_firmware(idrac, module): """Update firmware from a network share and return the job details.""" msg = {} msg['changed'] = False msg['update_status'] = {} try: upd_share = FileOnShare(remote=module.params['share_name'] + "/" + module.params['catalog_file_name'], mount_point=module.params['share_mnt'], isFolder=False, creds=UserCredentials( module.params['share_user'], module.params['share_password']) ) idrac.use_redfish = True if '12' in idrac.ServerGeneration or '13' in idrac.ServerGeneration: idrac.use_redfish = False apply_update = True msg['update_status'] = idrac.update_mgr.update_from_repo(upd_share, apply_update, module.params['reboot'], module.params['job_wait']) except RuntimeError as e: module.fail_json(msg=str(e)) if "Status" in msg['update_status']: if msg['update_status']['Status'] == "Success": if module.params['job_wait']: msg['changed'] = True else: module.fail_json(msg='Failed to update firmware.', update_status=msg['update_status']) return msg def main(): module = AnsibleModule( argument_spec={ "idrac_ip": {"required": True, "type": 'str'}, "idrac_user": {"required": True, "type": 'str'}, "idrac_password": {"required": True, "type": 'str', "aliases": ['idrac_pwd'], "no_log": True}, "idrac_port": {"required": False, "default": 443, "type": 'int'}, "share_name": {"required": True, "type": 'str'}, "share_user": {"required": False, "type": 'str'}, "share_password": {"required": False, "type": 'str', "aliases": ['share_pwd'], "no_log": True}, "share_mnt": {"required": True, "type": 'str'}, "catalog_file_name": {"required": False, "type": 'str', "default": "Catalog.xml"}, "reboot": {"required": False, "type": 'bool', "default": False}, "job_wait": {"required": False, "type": 'bool', "default": True}, }, supports_check_mode=False) try: # Validate the catalog file _validate_catalog_file(module.params['catalog_file_name']) # Connect to iDRAC and update firmware with iDRACConnection(module.params) as idrac: update_status = update_firmware(idrac, module) except (ImportError, ValueError, RuntimeError) as e: module.fail_json(msg=str(e)) module.exit_json(msg='Successfully updated the firmware.', update_status=update_status) if __name__ == '__main__': main()