#!/usr/bin/python # Copyright (c) 2022 Western Digital Corporation # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import annotations DOCUMENTATION = r""" module: wdc_redfish_command short_description: Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish APIs version_added: 5.4.0 description: - Builds Redfish URIs locally and sends them to remote OOB controllers to perform an action. - Manages OOB controller firmware. For example, Firmware Activate, Update and Activate. extends_documentation_fragment: - community.general.attributes - community.general.redfish attributes: check_mode: support: full diff_mode: support: none options: category: required: true description: - Category to execute on OOB controller. type: str command: required: true description: - List of commands to execute on OOB controller. type: list elements: str baseuri: description: - Base URI of OOB controller. Must include this or O(ioms). type: str ioms: description: - List of IOM FQDNs for the enclosure. Must include this or O(baseuri). type: list elements: str username: description: - User for authentication with OOB controller. type: str password: description: - Password for authentication with OOB controller. type: str auth_token: description: - Security token for authentication with OOB controller. type: str timeout: description: - Timeout in seconds for URL requests to OOB controller. default: 10 type: int resource_id: description: - ID of the component to modify, such as V(Enclosure), V(IOModuleAFRU), V(PowerSupplyBFRU), V(FanExternalFRU3), or V(FanInternalFRU). type: str version_added: 5.4.0 update_image_uri: description: - The URI of the image for the update. type: str update_creds: description: - The credentials for retrieving the update image. type: dict suboptions: username: description: - The username for retrieving the update image. type: str password: description: - The password for retrieving the update image. type: str validate_certs: version_added: 10.6.0 ca_path: version_added: 10.6.0 ciphers: version_added: 10.6.0 notes: - In the inventory, you can specify baseuri or ioms. See the EXAMPLES section. - Ioms is a list of FQDNs for the enclosure's IOMs. author: Mike Moerk (@mikemoerk) """ EXAMPLES = r""" - name: Firmware Activate (required after SimpleUpdate to apply the new firmware) community.general.wdc_redfish_command: category: Update command: FWActivate ioms: "{{ ioms }}" username: "{{ username }}" password: "{{ password }}" - name: Firmware Activate with individual IOMs specified community.general.wdc_redfish_command: category: Update command: FWActivate ioms: - iom1.wdc.com - iom2.wdc.com username: "{{ username }}" password: "{{ password }}" - name: Firmware Activate with baseuri specified community.general.wdc_redfish_command: category: Update command: FWActivate baseuri: "iom1.wdc.com" username: "{{ username }}" password: "{{ password }}" - name: Update and Activate (orchestrates firmware update and activation with a single command) community.general.wdc_redfish_command: category: Update command: UpdateAndActivate ioms: "{{ ioms }}" username: "{{ username }}" password: "{{ password }}" update_image_uri: "{{ update_image_uri }}" update_creds: username: operator password: supersecretpwd - name: Turn on enclosure indicator LED community.general.wdc_redfish_command: category: Chassis resource_id: Enclosure command: IndicatorLedOn username: "{{ username }}" password: "{{ password }}" - name: Turn off IOM A indicator LED community.general.wdc_redfish_command: category: Chassis resource_id: IOModuleAFRU command: IndicatorLedOff username: "{{ username }}" password: "{{ password }}" - name: Turn on Power Supply B indicator LED community.general.wdc_redfish_command: category: Chassis resource_id: PowerSupplyBFRU command: IndicatorLedOn username: "{{ username }}" password: "{{ password }}" - name: Turn on External Fan 3 indicator LED community.general.wdc_redfish_command: category: Chassis resource_id: FanExternalFRU3 command: IndicatorLedOn username: "{{ username }}" password: "{{ password }}" - name: Turn on Internal Fan indicator LED community.general.wdc_redfish_command: category: Chassis resource_id: FanInternalFRU command: IndicatorLedOn username: "{{ username }}" password: "{{ password }}" - name: Set chassis to Low Power Mode community.general.wdc_redfish_command: category: Chassis resource_id: Enclosure command: PowerModeLow - name: Set chassis to Normal Power Mode community.general.wdc_redfish_command: category: Chassis resource_id: Enclosure command: PowerModeNormal """ RETURN = r""" msg: description: Message with action result or error description. returned: always type: str sample: "Action was successful" """ from ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils import WdcRedfishUtils from ansible_collections.community.general.plugins.module_utils.redfish_utils import REDFISH_COMMON_ARGUMENT_SPEC from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.text.converters import to_native CATEGORY_COMMANDS_ALL = { "Update": ["FWActivate", "UpdateAndActivate"], "Chassis": [ "IndicatorLedOn", "IndicatorLedOff", "PowerModeLow", "PowerModeNormal", ], } def main(): argument_spec = dict( category=dict(required=True), command=dict(required=True, type="list", elements="str"), ioms=dict(type="list", elements="str"), baseuri=dict(), username=dict(), password=dict(no_log=True), auth_token=dict(no_log=True), update_creds=dict(type="dict", options=dict(username=dict(), password=dict(no_log=True))), resource_id=dict(), update_image_uri=dict(), timeout=dict(type="int", default=10), ) argument_spec.update(REDFISH_COMMON_ARGUMENT_SPEC) module = AnsibleModule( argument_spec, required_together=[ ("username", "password"), ], required_one_of=[("username", "auth_token"), ("baseuri", "ioms")], mutually_exclusive=[ ("username", "auth_token"), ], supports_check_mode=True, ) category = module.params["category"] command_list = module.params["command"] # admin credentials used for authentication creds = {"user": module.params["username"], "pswd": module.params["password"], "token": module.params["auth_token"]} # timeout timeout = module.params["timeout"] # Resource to modify resource_id = module.params["resource_id"] # Check that Category is valid if category not in CATEGORY_COMMANDS_ALL: module.fail_json( msg=f"Invalid Category '{category}'. Valid Categories = {sorted(CATEGORY_COMMANDS_ALL.keys())}" ) # Check that all commands are valid for cmd in command_list: # Fail if even one command given is invalid if cmd not in CATEGORY_COMMANDS_ALL[category]: module.fail_json(msg=f"Invalid Command '{cmd}'. Valid Commands = {CATEGORY_COMMANDS_ALL[category]}") # Build root URI(s) if module.params.get("baseuri") is not None: root_uris = [f"https://{module.params['baseuri']}"] else: root_uris = [f"https://{iom}" for iom in module.params["ioms"]] rf_utils = WdcRedfishUtils(creds, root_uris, timeout, module, resource_id=resource_id, data_modification=True) # Organize by Categories / Commands if 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"]) # update options update_opts = {"update_creds": module.params["update_creds"]} for command in command_list: if command == "FWActivate": if module.check_mode: result = {"ret": True, "changed": True, "msg": "FWActivate not performed in check mode."} else: result = rf_utils.firmware_activate(update_opts) elif command == "UpdateAndActivate": update_opts["update_image_uri"] = module.params["update_image_uri"] result = rf_utils.update_and_activate(update_opts) elif category == "Chassis": result = rf_utils._find_chassis_resource() if result["ret"] is False: module.fail_json(msg=result["msg"]) led_commands = ["IndicatorLedOn", "IndicatorLedOff"] # Check if more than one led_command is present num_led_commands = sum([command in led_commands for command in command_list]) if num_led_commands > 1: result = {"ret": False, "msg": "Only one IndicatorLed command should be sent at a time."} else: for command in command_list: if command.startswith("IndicatorLed"): result = rf_utils.manage_chassis_indicator_led(command) elif command.startswith("PowerMode"): result = rf_utils.manage_chassis_power_mode(command) if result["ret"] is False: module.fail_json(msg=to_native(result["msg"])) else: del result["ret"] changed = result.get("changed", True) session = result.get("session", dict()) module.exit_json( changed=changed, session=session, msg="Action was successful" if not module.check_mode else result.get("msg", "No action performed in check mode."), ) if __name__ == "__main__": main()