#!/usr/bin/python
# -*- coding: utf-8 -*-

# 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 absolute_import, division, print_function
__metaclass__ = type

DOCUMENTATION = '''
---
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.
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 I(ioms).
    type: str
  ioms:
    description:
      - List of IOM FQDNs for the enclosure.  Must include this or I(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:
    required: false
    description:
      - ID of the component to modify, such as C(Enclosure), C(IOModuleAFRU), C(PowerSupplyBFRU), C(FanExternalFRU3), or C(FanInternalFRU).
    type: str
    version_added: 5.4.0
  update_image_uri:
    required: false
    description:
      - The URI of the image for the update.
    type: str
  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
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 = '''
- 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 }}"

'''

RETURN = '''
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.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native

CATEGORY_COMMANDS_ALL = {
    "Update": [
        "FWActivate",
        "UpdateAndActivate"
    ],
    "Chassis": [
        "IndicatorLedOn",
        "IndicatorLedOff"
    ]
}


def main():
    module = AnsibleModule(
        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)
        ),
        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=to_native("Invalid Category '%s'. Valid Categories = %s" % (category, 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=to_native("Invalid Command '%s'. Valid Commands = %s" % (cmd, CATEGORY_COMMANDS_ALL[category])))

    # Build root URI(s)
    if module.params.get("baseuri") is not None:
        root_uris = ["https://" + module.params['baseuri']]
    else:
        root_uris = [
            "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=to_native(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)

    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()
