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

# Copyright (c) 2017-2018 Dell EMC Inc.
# 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: redfish_config
short_description: Manages Out-Of-Band controllers using Redfish APIs
description:
  - Builds Redfish URIs locally and sends them to remote OOB controllers to
    set or update a configuration attribute.
  - Manages BIOS configuration settings.
  - Manages OOB controller configuration settings.
extends_documentation_fragment:
  - community.general.attributes
attributes:
  check_mode:
    support: none
  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:
    required: true
    description:
      - Base URI of OOB controller.
    type: str
  username:
    description:
      - Username for authenticating to OOB controller.
    type: str
  password:
    description:
      - Password for authenticating to OOB controller.
    type: str
  auth_token:
    description:
      - Security token for authenticating to OOB controller.
    type: str
    version_added: 2.3.0
  bios_attributes:
    required: false
    description:
      - Dictionary of BIOS attributes to update.
    default: {}
    type: dict
    version_added: '0.2.0'
  timeout:
    description:
      - Timeout in seconds for HTTP requests to OOB controller.
      - The default value for this parameter changed from V(10) to V(60)
        in community.general 9.0.0.
    type: int
    default: 60
  boot_order:
    required: false
    description:
      - List of BootOptionReference strings specifying the BootOrder.
    default: []
    type: list
    elements: str
    version_added: '0.2.0'
  network_protocols:
    required: false
    description:
      - Setting dict of manager services to update.
    type: dict
    default: {}
    version_added: '0.2.0'
  resource_id:
    required: false
    description:
      - ID of the System, Manager or Chassis to modify.
    type: str
    version_added: '0.2.0'
  service_id:
    required: false
    description:
      - ID of the manager to update.
    type: str
    version_added: '8.4.0'
  nic_addr:
    required: false
    description:
      - EthernetInterface Address string on OOB controller.
    default: 'null'
    type: str
    version_added: '0.2.0'
  nic_config:
    required: false
    description:
      - Setting dict of EthernetInterface on OOB controller.
    type: dict
    default: {}
    version_added: '0.2.0'
  strip_etag_quotes:
    description:
      - Removes surrounding quotes of etag used in C(If-Match) header
        of C(PATCH) requests.
      - Only use this option to resolve bad vendor implementation where
        C(If-Match) only matches the unquoted etag string.
    type: bool
    default: false
    version_added: 3.7.0
  hostinterface_config:
    required: false
    description:
      - Setting dict of HostInterface on OOB controller.
    type: dict
    default: {}
    version_added: '4.1.0'
  hostinterface_id:
    required: false
    description:
      - Redfish HostInterface instance ID if multiple HostInterfaces are present.
    type: str
    version_added: '4.1.0'
  sessions_config:
    required: false
    description:
      - Setting dict of Sessions.
    type: dict
    default: {}
    version_added: '5.7.0'
  storage_subsystem_id:
    required: false
    description:
      - Id of the Storage Subsystem on which the volume is to be created.
    type: str
    default: ''
    version_added: '7.3.0'
  storage_none_volume_deletion:
    required: false
    description:
      - Indicates if all non-RAID volumes are automatically deleted prior to creating the new volume.
    type: bool
    default: false
    version_added: '9.5.0'
  volume_ids:
    required: false
    description:
      - List of IDs of volumes to be deleted.
    type: list
    default: []
    elements: str
    version_added: '7.3.0'
  secure_boot_enable:
    required: false
    description:
      - Setting parameter to enable or disable SecureBoot.
    type: bool
    default: True
    version_added: '7.5.0'
  volume_details:
    required: false
    description:
      - Setting dict of volume to be created.
      - If C(CapacityBytes) key is not specified in this dictionary, the size of
        the volume will be determined by the Redfish service. It is possible the
        size will not be the maximum available size.
    type: dict
    default: {}
    version_added: '7.5.0'
  ciphers:
    required: false
    description:
      - SSL/TLS Ciphers to use for the request.
      - 'When a list is provided, all ciphers are joined in order with V(:).'
      - See the L(OpenSSL Cipher List Format,https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html#CIPHER-LIST-FORMAT)
        for more details.
      - The available ciphers is dependent on the Python and OpenSSL/LibreSSL versions.
    type: list
    elements: str
    version_added: 9.2.0

author:
  - "Jose Delarosa (@jose-delarosa)"
  - "T S Kushal (@TSKushal)"
'''

EXAMPLES = '''
  - name: Set BootMode to UEFI
    community.general.redfish_config:
      category: Systems
      command: SetBiosAttributes
      resource_id: 437XR1138R2
      bios_attributes:
        BootMode: "Uefi"
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Set multiple BootMode attributes
    community.general.redfish_config:
      category: Systems
      command: SetBiosAttributes
      resource_id: 437XR1138R2
      bios_attributes:
        BootMode: "Bios"
        OneTimeBootMode: "Enabled"
        BootSeqRetry: "Enabled"
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Enable PXE Boot for NIC1
    community.general.redfish_config:
      category: Systems
      command: SetBiosAttributes
      resource_id: 437XR1138R2
      bios_attributes:
        PxeDev1EnDis: Enabled
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Set BIOS default settings with a timeout of 20 seconds
    community.general.redfish_config:
      category: Systems
      command: SetBiosDefaultSettings
      resource_id: 437XR1138R2
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"
      timeout: 20

  - name: Set boot order
    community.general.redfish_config:
      category: Systems
      command: SetBootOrder
      boot_order:
        - Boot0002
        - Boot0001
        - Boot0000
        - Boot0003
        - Boot0004
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Set boot order to the default
    community.general.redfish_config:
      category: Systems
      command: SetDefaultBootOrder
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Set Manager Network Protocols
    community.general.redfish_config:
      category: Manager
      command: SetNetworkProtocols
      network_protocols:
        SNMP:
          ProtocolEnabled: true
          Port: 161
        HTTP:
          ProtocolEnabled: false
          Port: 8080
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Set Manager NIC
    community.general.redfish_config:
      category: Manager
      command: SetManagerNic
      nic_config:
        DHCPv4:
          DHCPEnabled: false
        IPv4StaticAddresses:
          Address: 192.168.1.3
          Gateway: 192.168.1.1
          SubnetMask: 255.255.255.0
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Disable Host Interface
    community.general.redfish_config:
      category: Manager
      command: SetHostInterface
      hostinterface_config:
        InterfaceEnabled: false
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Enable Host Interface for HostInterface resource ID '2'
    community.general.redfish_config:
      category: Manager
      command: SetHostInterface
      hostinterface_config:
        InterfaceEnabled: true
      hostinterface_id: "2"
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Set SessionService Session Timeout to 30 minutes
    community.general.redfish_config:
      category: Sessions
      command: SetSessionService
      sessions_config:
        SessionTimeout: 1800
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Enable SecureBoot
    community.general.redfish_config:
      category: Systems
      command: EnableSecureBoot
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Set SecureBoot
    community.general.redfish_config:
      category: Systems
      command: SetSecureBoot
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"
      secure_boot_enable: True

  - name: Delete All Volumes
    community.general.redfish_config:
      category: Systems
      command: DeleteVolumes
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"
      storage_subsystem_id: "DExxxxxx"
      volume_ids: ["volume1", "volume2"]

  - name: Create Volume
    community.general.redfish_config:
      category: Systems
      command: CreateVolume
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"
      storage_subsystem_id: "DExxxxxx"
      volume_details:
        Name: "MR Volume"
        RAIDType: "RAID0"
        Drives:
          - "/redfish/v1/Systems/1/Storage/DE00B000/Drives/1"

  - name: Set service identification to {{ service_id }}
    community.general.redfish_config:
      category: Manager
      command: SetServiceIdentification
      service_id: "{{ service_id }}"
      baseuri: "{{ baseuri }}"
      username: "{{ username }}"
      password: "{{ password }}"
'''

RETURN = '''
msg:
    description: Message with action result or error description
    returned: always
    type: str
    sample: "Action was successful"
'''

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.redfish_utils import RedfishUtils
from ansible.module_utils.common.text.converters import to_native


# More will be added as module features are expanded
CATEGORY_COMMANDS_ALL = {
    "Systems": ["SetBiosDefaultSettings", "SetBiosAttributes", "SetBootOrder",
                "SetDefaultBootOrder", "EnableSecureBoot", "SetSecureBoot", "DeleteVolumes", "CreateVolume"],
    "Manager": ["SetNetworkProtocols", "SetManagerNic", "SetHostInterface", "SetServiceIdentification"],
    "Sessions": ["SetSessionService"],
}


def main():
    result = {}
    module = AnsibleModule(
        argument_spec=dict(
            category=dict(required=True),
            command=dict(required=True, type='list', elements='str'),
            baseuri=dict(required=True),
            username=dict(),
            password=dict(no_log=True),
            auth_token=dict(no_log=True),
            bios_attributes=dict(type='dict', default={}),
            timeout=dict(type='int', default=60),
            boot_order=dict(type='list', elements='str', default=[]),
            network_protocols=dict(
                type='dict',
                default={}
            ),
            resource_id=dict(),
            service_id=dict(),
            nic_addr=dict(default='null'),
            nic_config=dict(
                type='dict',
                default={}
            ),
            strip_etag_quotes=dict(type='bool', default=False),
            hostinterface_config=dict(type='dict', default={}),
            hostinterface_id=dict(),
            sessions_config=dict(type='dict', default={}),
            storage_subsystem_id=dict(type='str', default=''),
            storage_none_volume_deletion=dict(type='bool', default=False),
            volume_ids=dict(type='list', default=[], elements='str'),
            secure_boot_enable=dict(type='bool', default=True),
            volume_details=dict(type='dict', default={}),
            ciphers=dict(type='list', elements='str'),
        ),
        required_together=[
            ('username', 'password'),
        ],
        required_one_of=[
            ('username', 'auth_token'),
        ],
        mutually_exclusive=[
            ('username', 'auth_token'),
        ],
        supports_check_mode=False
    )

    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']

    # BIOS attributes to update
    bios_attributes = module.params['bios_attributes']

    # boot order
    boot_order = module.params['boot_order']

    # System, Manager or Chassis ID to modify
    resource_id = module.params['resource_id']

    # manager nic
    nic_addr = module.params['nic_addr']
    nic_config = module.params['nic_config']

    # Etag options
    strip_etag_quotes = module.params['strip_etag_quotes']

    # HostInterface config options
    hostinterface_config = module.params['hostinterface_config']

    # HostInterface instance ID
    hostinterface_id = module.params['hostinterface_id']

    # Service Identification
    service_id = module.params['service_id']

    # Sessions config options
    sessions_config = module.params['sessions_config']

    # Volume deletion options
    storage_subsystem_id = module.params['storage_subsystem_id']
    volume_ids = module.params['volume_ids']

    # Set SecureBoot options
    secure_boot_enable = module.params['secure_boot_enable']

    # Volume creation options
    volume_details = module.params['volume_details']
    storage_subsystem_id = module.params['storage_subsystem_id']
    storage_none_volume_deletion = module.params['storage_none_volume_deletion']

    # ciphers
    ciphers = module.params['ciphers']

    # Build root URI
    root_uri = "https://" + module.params['baseuri']
    rf_utils = RedfishUtils(creds, root_uri, timeout, module,
                            resource_id=resource_id, data_modification=True, strip_etag_quotes=strip_etag_quotes,
                            ciphers=ciphers)

    # 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, list(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])))

    # Organize by Categories / Commands
    if category == "Systems":
        # execute only if we find a System resource
        result = rf_utils._find_systems_resource()
        if result['ret'] is False:
            module.fail_json(msg=to_native(result['msg']))

        for command in command_list:
            if command == "SetBiosDefaultSettings":
                result = rf_utils.set_bios_default_settings()
            elif command == "SetBiosAttributes":
                result = rf_utils.set_bios_attributes(bios_attributes)
            elif command == "SetBootOrder":
                result = rf_utils.set_boot_order(boot_order)
            elif command == "SetDefaultBootOrder":
                result = rf_utils.set_default_boot_order()
            elif command == "EnableSecureBoot":
                result = rf_utils.enable_secure_boot()
            elif command == "SetSecureBoot":
                result = rf_utils.set_secure_boot(secure_boot_enable)
            elif command == "DeleteVolumes":
                result = rf_utils.delete_volumes(storage_subsystem_id, volume_ids)
            elif command == "CreateVolume":
                result = rf_utils.create_volume(volume_details, storage_subsystem_id, storage_none_volume_deletion)

    elif category == "Manager":
        # execute only if we find a Manager service resource
        result = rf_utils._find_managers_resource()
        if result['ret'] is False:
            module.fail_json(msg=to_native(result['msg']))

        for command in command_list:
            if command == "SetNetworkProtocols":
                result = rf_utils.set_network_protocols(module.params['network_protocols'])
            elif command == "SetManagerNic":
                result = rf_utils.set_manager_nic(nic_addr, nic_config)
            elif command == "SetHostInterface":
                result = rf_utils.set_hostinterface_attributes(hostinterface_config, hostinterface_id)
            elif command == "SetServiceIdentification":
                result = rf_utils.set_service_identification(service_id)

    elif category == "Sessions":
        # execute only if we find a Sessions resource
        result = rf_utils._find_sessionservice_resource()
        if result['ret'] is False:
            module.fail_json(msg=to_native(result['msg']))

        for command in command_list:
            if command == "SetSessionService":
                result = rf_utils.set_session_service(sessions_config)

    # Return data back or fail with proper message
    if result['ret'] is True:
        if result.get('warning'):
            module.warn(to_native(result['warning']))

        module.exit_json(changed=result['changed'], msg=to_native(result['msg']))
    else:
        module.fail_json(msg=to_native(result['msg']))


if __name__ == '__main__':
    main()
