Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
ansible / dellemc / openmanage / plugins / modules / idrac_redfish_storage_controller.py
Size: Mime:
#!/usr/bin/python
# -*- coding: utf-8 -*-

#
# Dell EMC OpenManage Ansible Modules
# Version 5.2.0
# Copyright (C) 2019-2022 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

DOCUMENTATION = r'''
---
module: idrac_redfish_storage_controller
short_description: Configures the physical disk, virtual disk, and storage controller settings
version_added: "2.1.0"
description:
  - This module allows the users to configure the settings of the physical disk, virtual disk,
    and storage controller.
extends_documentation_fragment:
  - dellemc.openmanage.redfish_auth_options
options:
  command:
    description:
      - These actions may require a system reset, depending on the capabilities of the controller.
      - C(ResetConfig) - Deletes all the virtual disks and unassigns all hot spares on physical disks.
        I(controller_id) is required for this operation.
      - C(AssignSpare) - Assigns a physical disk as a dedicated or global hot spare for a virtual disk.
        I(target) is required for this operation.
      - C(SetControllerKey) - Sets the key on controllers, which is used to encrypt the drives in Local
        Key Management(LKM). I(controller_id), I(key), and I(key_id) are required for this operation.
      - C(RemoveControllerKey) - Deletes the encryption key on the controller.
        I(controller_id) is required for this operation.
      - C(ReKey) - Resets the key on the controller and it always reports as changes found when check mode is enabled.
        I(controller_id), I(old_key), I(key_id), and I(key) is required for this operation.
      - C(UnassignSpare) - To unassign the Global or Dedicated hot spare. I(target) is required for this operation.
      - C(EnableControllerEncryption) - To enable Local Key Management (LKM) or Secure Enterprise Key Manager (SEKM)
        on controllers that support encryption of the drives. I(controller_id), I(key), and I(key_id) are required
        for this operation.
      - C(BlinkTarget) - Blinks the target virtual drive or physical disk and it always reports as changes found
        when check mode is enabled. I(target) or I(volume_id) is required for this operation.
      - C(UnBlinkTarget) - Unblink the target virtual drive or physical disk and and it always reports as changes
        found when check mode is enabled. I(target) or I(volume_id) is required for this operation.
      - C(ConvertToRAID) - Converts the disk form non-Raid to Raid. I(target) is required for this operation.
      - C(ConvertToNonRAID) - Converts the disk form Raid to non-Raid. I(target) is required for this operation.
      - C(ChangePDStateToOnline) - To set the disk status to online. I(target) is required for this operation.
      - C(ChangePDStateToOffline) - To set the disk status to offline. I(target) is required for this operation.
    choices: [ResetConfig, AssignSpare, SetControllerKey, RemoveControllerKey, ReKey, UnassignSpare,
      EnableControllerEncryption, BlinkTarget, UnBlinkTarget, ConvertToRAID, ConvertToNonRAID,
      ChangePDStateToOnline, ChangePDStateToOffline]
    default: AssignSpare
    type: str
  target:
    description:
      - Fully Qualified Device Descriptor (FQDD) of the target physical drive.
      - This is mandatory when I(command) is C(AssignSpare), C(UnassisgnSpare),
        C(ChangePDStateToOnline), C(ChangePDStateToOffline), C(ConvertToRAID), or C(ConvertToNonRAID).
      - If I(volume_id) is not specified or empty, this physical drive will be
        assigned as a global hot spare when I(command) is C(AssignSpare).
      - "Notes: Global or Dedicated hot spare can be assigned only once for a physical disk,
        Re-assign cannot be done when I(command) is C(AssignSpare)."
    type: list
    elements: str
    aliases: [drive_id]
  volume_id:
    description:
      - Fully Qualified Device Descriptor (FQDD) of the volume.
      - Applicable if I(command) is C(AssignSpare), C(BlinkTarget), and C(UnBlinkTarget).
      - I(volume_id) or I(target) is required when the I(command) is C(BlinkTarget) or C(UnBlinkTarget),
        if both are specified I(target) is considered.
      - To know the number of volumes to which a hot spare can be assigned, refer iDRAC Redfish API documentation.
    type: list
    elements: str
  controller_id:
    description:
      - Fully Qualified Device Descriptor (FQDD) of the storage controller. For example-'RAID.Slot.1-1'.
      - This option is mandatory when I(command) is C(ResetConfig), C(SetControllerKey),
        C(RemoveControllerKey), C(ReKey), or C(EnableControllerEncryption).
    type: str
  key:
    description:
      - A new security key passphrase that the encryption-capable controller uses to create the
        encryption key. The controller uses the encryption key to lock or unlock access to the
        Self-Encrypting Drive (SED). Only one encryption key can be created for each controller.
      - This is mandatory when I(command) is C(SetControllerKey), C(ReKey), or C(EnableControllerEncryption)
        and when I(mode) is C(LKM).
      - The length of the key can be a maximum of 32 characters in length, where the expanded form of
        the special character is counted as a single character.
      - "The key must contain at least one character from each of the character classes: uppercase,
        lowercase, number, and special character."
    type: str
  key_id:
    description:
      - This is a user supplied text label associated with the passphrase.
      - This is mandatory when I(command) is C(SetControllerKey), C(ReKey), or C(EnableControllerEncryption)
        and when I(mode) is C(LKM).
      - The length of I(key_id) can be a maximum of 32 characters in length and should not have any spaces.
    type: str
  old_key:
    description:
      - Security key passphrase used by the encryption-capable controller.
      - This option is mandatory when I(command) is C(ReKey) and I(mode) is C(LKM).
    type: str
  mode:
    description:
      - Encryption mode of the encryption capable controller.
      - This option is applicable only when I(command) is C(ReKey) or C(EnableControllerEncryption).
      - C(SEKM) requires secure enterprise key manager license on the iDRAC.
      - C(LKM) to choose mode as local key mode.
    choices: [LKM, SEKM]
    default: LKM
    type: str
  job_wait:
    description:
      - Provides the option if the module has to wait for the job to be completed.
    type: bool
    default: False
  job_wait_timeout:
    description:
      - The maximum wait time of job completion in seconds before the job tracking is stopped.
      - This option is applicable when I(job_wait) is C(True).
    type: int
    default: 120
requirements:
  - "python >= 3.8.6"
author:
  - "Jagadeesh N V (@jagadeeshnv)"
  - "Felix Stephen (@felixs88)"
notes:
    - Run this module from a system that has direct access to Dell EMC iDRAC.
    - This module always reports as changes found when C(ReKey), C(BlinkTarget), and C(UnBlinkTarget).
    - This module supports C(check_mode).
'''

EXAMPLES = r'''
---
- name: Assign dedicated hot spare
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    volume_id:
      - "Disk.Virtual.0:RAID.Slot.1-1"
    target: "Disk.Bay.0:Enclosure.Internal.0-1:RAID.Slot.1-1"
  tags:
    - assign_dedicated_hot_spare

- name: Assign global hot spare
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    target: "Disk.Bay.0:Enclosure.Internal.0-1:RAID.Slot.1-1"
  tags:
    - assign_global_hot_spare

- name: Unassign hot spare
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    target: "Disk.Bay.0:Enclosure.Internal.0-1:RAID.Slot.1-1"
    command: UnassignSpare
  tags:
    - un-assign-hot-spare

- name: Set controller encryption key
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "SetControllerKey"
    controller_id: "RAID.Slot.1-1"
    key: "PassPhrase@123"
    key_id: "mykeyid123"
  tags:
    - set_controller_key

- name: Rekey in LKM mode
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "ReKey"
    controller_id: "RAID.Slot.1-1"
    key: "NewPassPhrase@123"
    key_id: "newkeyid123"
    old_key: "OldPassPhrase@123"
  tags:
    - rekey_lkm

- name: Rekey in SEKM mode
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "ReKey"
    controller_id: "RAID.Slot.1-1"
    mode: "SEKM"
  tags:
    - rekey_sekm

- name: Remove controller key
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "RemoveControllerKey"
    controller_id: "RAID.Slot.1-1"
  tags:
    - remove_controller_key

- name: Reset controller configuration
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "ResetConfig"
    controller_id: "RAID.Slot.1-1"
  tags:
    - reset_config

- name: Enable controller encryption
  idrac_redfish_storage_controller:
    baseuri: "{{ baseuri }}"
    username: "{{ username }}"
    password: "{{ password }}"
    ca_path: "/path/to/ca_cert.pem"
    command: "EnableControllerEncryption"
    controller_id: "RAID.Slot.1-1"
    mode: "LKM"
    key: "your_Key@123"
    key_id: "your_Keyid@123"
  tags:
    - enable-encrypt

- name: Blink physical disk.
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: BlinkTarget
    target: "Disk.Bay.0:Enclosure.Internal.0-1:RAID.Slot.1-1"
  tags:
    - blink-target

- name: Blink virtual drive.
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: BlinkTarget
    volume_id: "Disk.Virtual.0:RAID.Slot.1-1"
  tags:
    - blink-volume

- name: Unblink physical disk.
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: UnBlinkTarget
    target: "Disk.Bay.0:Enclosure.Internal.0-1:RAID.Slot.1-1"
  tags:
    - unblink-target

- name: Unblink virtual drive.
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: UnBlinkTarget
    volume_id: "Disk.Virtual.0:RAID.Slot.1-1"
  tags:
    - unblink-drive

- name: Convert physical disk to RAID
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "ConvertToRAID"
    target: "Disk.Bay.0:Enclosure.Internal.0-1:RAID.Slot.1-1"
  tags:
    - convert-raid

- name: Convert physical disk to non-RAID
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "ConvertToNonRAID"
    target: "Disk.Bay.0:Enclosure.Internal.0-1:RAID.Slot.1-1"
  tags:
    - convert-non-raid

- name: Change physical disk state to online.
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "ChangePDStateToOnline"
    target: "Disk.Bay.1:Enclosure.Internal.0-1:RAID.Slot.1-1"
  tags:
    - pd-state-online

- name: Change physical disk state to offline.
  dellemc.openmanage.idrac_redfish_storage_controller:
    baseuri: "192.168.0.1:443"
    username: "user_name"
    password: "user_password"
    ca_path: "/path/to/ca_cert.pem"
    command: "ChangePDStateToOnline"
    target: "Disk.Bay.1:Enclosure.Internal.0-1:RAID.Slot.1-1"
  tags:
    - pd-state-offline
'''

RETURN = r'''
---
msg:
  type: str
  description: Overall status of the storage controller configuration operation.
  returned: always
  sample: "Successfully submitted the job that performs the AssignSpare operation"
task:
  type: dict
  description: ID and URI resource of the job created.
  returned: success
  sample: {
    "id": "JID_XXXXXXXXXXXXX",
    "uri": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/JID_XXXXXXXXXXXXX"
  }
status:
  type: dict
  description: status of the submitted job.
  returned: always
  sample: {
    "ActualRunningStartTime": "2022-02-09T04:42:41",
    "ActualRunningStopTime": "2022-02-09T04:44:00",
    "CompletionTime": "2022-02-09T04:44:00",
    "Description": "Job Instance",
    "EndTime": "TIME_NA",
    "Id": "JID_444033604418",
    "JobState": "Completed",
    "JobType": "RealTimeNoRebootConfiguration",
    "Message": "Job completed successfully.",
    "MessageArgs":[],
    "MessageId": "PR19",
    "Name": "Configure: RAID.Integrated.1-1",
    "PercentComplete": 100,
    "StartTime": "2022-02-09T04:42:40",
    "TargetSettingsURI": null
  }
error_info:
  type: dict
  description: Details of a http error.
  returned: on http error
  sample:  {
    "error": {
      "@Message.ExtendedInfo": [
        {
          "Message": "Unable to run the method because the requested HTTP method is not allowed.",
          "MessageArgs": [],
          "MessageArgs@odata.count": 0,
          "MessageId": "iDRAC.1.6.SYS402",
          "RelatedProperties": [],
          "RelatedProperties@odata.count": 0,
          "Resolution": "Enter a valid HTTP method and retry the operation. For information about
          valid methods, see the Redfish Users Guide available on the support site.",
          "Severity": "Informational"
        }
      ],
      "code": "Base.1.0.GeneralError",
      "message": "A general error has occurred. See ExtendedInfo for more information"
    }
  }
'''


import json
from ansible_collections.dellemc.openmanage.plugins.module_utils.redfish import Redfish, redfish_auth_params
from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import wait_for_job_completion, strip_substr_dict
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError
from ansible.module_utils.urls import ConnectionError, SSLValidationError


SYSTEM_ID = "System.Embedded.1"
MANAGER_ID = "iDRAC.Embedded.1"
RAID_ACTION_URI = "/redfish/v1/Systems/{system_id}/Oem/Dell/DellRaidService/Actions/DellRaidService.{action}"
CONTROLLER_URI = "/redfish/v1/Dell/Systems/{system_id}/Storage/DellController/{controller_id}"
VOLUME_URI = "/redfish/v1/Systems/{system_id}/Storage/{controller_id}/Volumes"
PD_URI = "/redfish/v1/Systems/System.Embedded.1/Storage/{controller_id}/Drives/{drive_id}"

JOB_SUBMISSION = "Successfully submitted the job that performs the '{0}' operation."
JOB_COMPLETION = "Successfully performed the '{0}' operation."
CHANGES_FOUND = "Changes found to be applied."
NO_CHANGES_FOUND = "No changes found to be applied."
TARGET_ERR_MSG = "The Fully Qualified Device Descriptor (FQDD) of the target {0} must be only one."
PD_ERROR_MSG = "Unable to locate the physical disk with the ID: {0}"
ENCRYPT_ERR_MSG = "The storage controller '{0}' does not support encryption."


def check_id_exists(module, redfish_obj, key, item_id, uri):
    msg = "{0} with id '{1}' not found in system".format(key, item_id)
    try:
        resp = redfish_obj.invoke_request("GET", uri.format(system_id=SYSTEM_ID, controller_id=item_id))
        if not resp.success:
            module.fail_json(msg=msg)
    except HTTPError as err:
        module.fail_json(msg=msg, error_info=json.load(err))


def ctrl_key(module, redfish_obj):
    resp, job_uri, job_id, payload = None, None, None, {}
    controller_id = module.params.get("controller_id")
    command, mode = module.params["command"], module.params["mode"]
    key, key_id = module.params.get("key"), module.params.get("key_id")
    check_id_exists(module, redfish_obj, "controller_id", controller_id, CONTROLLER_URI)
    ctrl_resp = redfish_obj.invoke_request("GET", CONTROLLER_URI.format(system_id=SYSTEM_ID,
                                                                        controller_id=controller_id))
    security_status = ctrl_resp.json_data.get("SecurityStatus")
    if security_status == "EncryptionNotCapable":
        module.fail_json(msg=ENCRYPT_ERR_MSG.format(controller_id))
    ctrl_key_id = ctrl_resp.json_data.get("KeyID")
    if command == "SetControllerKey":
        if module.check_mode and ctrl_key_id is None:
            module.exit_json(msg=CHANGES_FOUND, changed=True)
        elif (module.check_mode and ctrl_key_id is not None) or (not module.check_mode and ctrl_key_id is not None):
            module.exit_json(msg=NO_CHANGES_FOUND)
        payload = {"TargetFQDD": controller_id, "Key": key, "Keyid": key_id}
    elif command == "ReKey":
        if module.check_mode:
            module.exit_json(msg=CHANGES_FOUND, changed=True)
        if mode == "LKM":
            payload = {"TargetFQDD": controller_id, "Mode": mode, "NewKey": key,
                       "Keyid": key_id, "OldKey": module.params.get("old_key")}
        else:
            payload = {"TargetFQDD": controller_id, "Mode": mode}
    elif command == "RemoveControllerKey":
        if module.check_mode and ctrl_key_id is not None:
            module.exit_json(msg=CHANGES_FOUND, changed=True)
        elif (module.check_mode and ctrl_key_id is None) or (not module.check_mode and ctrl_key_id is None):
            module.exit_json(msg=NO_CHANGES_FOUND)
        payload = {"TargetFQDD": controller_id}
    elif command == "EnableControllerEncryption":
        if module.check_mode and not security_status == "SecurityKeyAssigned":
            module.exit_json(msg=CHANGES_FOUND, changed=True)
        elif (module.check_mode and security_status == "SecurityKeyAssigned") or \
                (not module.check_mode and security_status == "SecurityKeyAssigned"):
            module.exit_json(msg=NO_CHANGES_FOUND)
        payload = {"TargetFQDD": controller_id, "Mode": mode}
        if mode == "LKM":
            payload["Key"] = key
            payload["Keyid"] = key_id
    resp = redfish_obj.invoke_request("POST", RAID_ACTION_URI.format(system_id=SYSTEM_ID, action=command),
                                      data=payload)
    job_uri = resp.headers.get("Location")
    job_id = job_uri.split("/")[-1]
    return resp, job_uri, job_id


def ctrl_reset_config(module, redfish_obj):
    resp, job_uri, job_id = None, None, None
    controller_id = module.params.get("controller_id")
    check_id_exists(module, redfish_obj, "controller_id", controller_id, CONTROLLER_URI)
    member_resp = redfish_obj.invoke_request("GET", VOLUME_URI.format(system_id=SYSTEM_ID, controller_id=controller_id))
    members = member_resp.json_data.get("Members")
    if module.check_mode and members:
        module.exit_json(msg=CHANGES_FOUND, changed=True)
    elif (module.check_mode and not members) or (not module.check_mode and not members):
        module.exit_json(msg=NO_CHANGES_FOUND)
    else:
        resp = redfish_obj.invoke_request("POST", RAID_ACTION_URI.format(system_id=SYSTEM_ID,
                                                                         action=module.params["command"]),
                                          data={"TargetFQDD": controller_id})
        job_uri = resp.headers.get("Location")
        job_id = job_uri.split("/")[-1]
    return resp, job_uri, job_id


def hot_spare_config(module, redfish_obj):
    target, command = module.params.get("target"), module.params["command"]
    resp, job_uri, job_id = None, None, None
    volume = module.params.get("volume_id")
    controller_id = target[0].split(":")[-1]
    drive_id = target[0]
    try:
        pd_resp = redfish_obj.invoke_request("GET", PD_URI.format(controller_id=controller_id, drive_id=drive_id))
    except HTTPError:
        module.fail_json(msg=PD_ERROR_MSG.format(drive_id))
    else:
        hot_spare = pd_resp.json_data.get("HotspareType")
        if module.check_mode and hot_spare == "None" and command == "AssignSpare" or \
                (module.check_mode and not hot_spare == "None" and command == "UnassignSpare"):
            module.exit_json(msg=CHANGES_FOUND, changed=True)
        elif (module.check_mode and hot_spare in ["Dedicated", "Global"] and command == "AssignSpare") or \
                (not module.check_mode and hot_spare in ["Dedicated", "Global"] and command == "AssignSpare") or \
                (module.check_mode and hot_spare == "None" and command == "UnassignSpare") or \
                (not module.check_mode and hot_spare == "None" and command == "UnassignSpare"):
            module.exit_json(msg=NO_CHANGES_FOUND)
        else:
            payload = {"TargetFQDD": drive_id}
            if volume is not None and command == "AssignSpare":
                payload["VirtualDiskArray"] = volume
            resp = redfish_obj.invoke_request("POST", RAID_ACTION_URI.format(system_id=SYSTEM_ID,
                                                                             action=command),
                                              data=payload)
            job_uri = resp.headers.get("Location")
            job_id = job_uri.split("/")[-1]
    return resp, job_uri, job_id


def change_pd_status(module, redfish_obj):
    resp, job_uri, job_id = None, None, None
    command, target = module.params["command"], module.params.get("target")
    controller_id = target[0].split(":")[-1]
    drive_id = target[0]
    state = "Online" if command == "ChangePDStateToOnline" else "Offline"
    try:
        pd_resp = redfish_obj.invoke_request("GET", PD_URI.format(controller_id=controller_id, drive_id=drive_id))
        raid_status = pd_resp.json_data["Oem"]["Dell"]["DellPhysicalDisk"]["RaidStatus"]
    except HTTPError:
        module.fail_json(msg=PD_ERROR_MSG.format(drive_id))
    else:
        if module.check_mode and not state == raid_status:
            module.exit_json(msg=CHANGES_FOUND, changed=True)
        elif (module.check_mode and state == raid_status) or (not module.check_mode and state == raid_status):
            module.exit_json(msg=NO_CHANGES_FOUND)
        else:
            resp = redfish_obj.invoke_request("POST", RAID_ACTION_URI.format(system_id=SYSTEM_ID,
                                                                             action="ChangePDState"),
                                              data={"TargetFQDD": drive_id, "State": state})
            job_uri = resp.headers.get("Location")
            job_id = job_uri.split("/")[-1]
    return resp, job_uri, job_id


def convert_raid_status(module, redfish_obj):
    resp, job_uri, job_id = None, None, None
    command, target = module.params["command"], module.params.get("target")
    ctrl, pd_ready_state = None, []
    try:
        for ctrl in target:
            controller_id = ctrl.split(":")[-1]
            pd_resp = redfish_obj.invoke_request("GET", PD_URI.format(controller_id=controller_id, drive_id=ctrl))
            raid_status = pd_resp.json_data["Oem"]["Dell"]["DellPhysicalDisk"]["RaidStatus"]
            pd_ready_state.append(raid_status)
    except HTTPError:
        module.fail_json(msg=PD_ERROR_MSG.format(ctrl))
    else:
        if (command == "ConvertToRAID" and module.check_mode and 0 < pd_ready_state.count("NonRAID")) or \
                (command == "ConvertToNonRAID" and module.check_mode and 0 < pd_ready_state.count("Ready")):
            module.exit_json(msg=CHANGES_FOUND, changed=True)
        elif (command == "ConvertToRAID" and module.check_mode and
              len(pd_ready_state) == pd_ready_state.count("Ready")) or \
                (command == "ConvertToRAID" and not module.check_mode and
                 len(pd_ready_state) == pd_ready_state.count("Ready")) or \
                (command == "ConvertToNonRAID" and module.check_mode and
                 len(pd_ready_state) == pd_ready_state.count("NonRAID")) or \
                (command == "ConvertToNonRAID" and not module.check_mode and
                 len(pd_ready_state) == pd_ready_state.count("NonRAID")):
            module.exit_json(msg=NO_CHANGES_FOUND)
        else:
            resp = redfish_obj.invoke_request("POST", RAID_ACTION_URI.format(system_id=SYSTEM_ID,
                                                                             action=command),
                                              data={"PDArray": target})
            job_uri = resp.headers.get("Location")
            job_id = job_uri.split("/")[-1]
    return resp, job_uri, job_id


def target_identify_pattern(module, redfish_obj):
    target, volume = module.params.get("target"), module.params.get("volume_id")
    command = module.params.get("command")
    payload = {"TargetFQDD": None}

    if target is not None and volume is None:
        payload = {"TargetFQDD": target[0]}
    elif volume is not None and target is None:
        payload = {"TargetFQDD": volume[0]}
    elif target is not None and volume is not None:
        payload = {"TargetFQDD": target[0]}

    if module.check_mode:
        module.exit_json(msg=CHANGES_FOUND, changed=True)
    resp = redfish_obj.invoke_request("POST", RAID_ACTION_URI.format(system_id=SYSTEM_ID,
                                                                     action=command),
                                      data=payload)
    return resp


def validate_inputs(module):
    module_params = module.params
    command = module_params.get("command")
    mode = module_params.get("mode")
    if command == "ReKey" and mode == "LKM":
        key = module_params.get("key")
        key_id = module_params.get("key_id")
        old_key = module_params.get("old_key")
        if not all([key, key_id, old_key]):
            module.fail_json(msg="All of the following: key, key_id and old_key are "
                                 "required for '{0}' operation.".format(command))
    elif command == "EnableControllerEncryption" and mode == "LKM":
        key = module_params.get("key")
        key_id = module_params.get("key_id")
        if not all([key, key_id]):
            module.fail_json(msg="All of the following: key, key_id are "
                                 "required for '{0}' operation.".format(command))
    elif command in ["AssignSpare", "UnassignSpare", "BlinkTarget", "UnBlinkTarget"]:
        target, volume = module_params.get("target"), module_params.get("volume")
        if target is not None and not 1 >= len(target):
            module.fail_json(msg=TARGET_ERR_MSG.format("physical disk"))
        if volume is not None and not 1 >= len(volume):
            module.fail_json(msg=TARGET_ERR_MSG.format("virtual drive"))
    elif command in ["ChangePDStateToOnline", "ChangePDStateToOffline"]:
        target = module.params.get("target")
        if target is not None and not 1 >= len(target):
            module.fail_json(msg=TARGET_ERR_MSG.format("physical disk"))


def main():
    specs = {
        "command": {"required": False, "default": "AssignSpare",
                    "choices": ["ResetConfig", "AssignSpare", "SetControllerKey", "RemoveControllerKey",
                                "ReKey", "UnassignSpare", "EnableControllerEncryption", "BlinkTarget",
                                "UnBlinkTarget", "ConvertToRAID", "ConvertToNonRAID", "ChangePDStateToOnline",
                                "ChangePDStateToOffline"]},
        "controller_id": {"required": False, "type": "str"},
        "volume_id": {"required": False, "type": "list", "elements": "str"},
        "target": {"required": False, "type": "list", "elements": "str", "aliases": ["drive_id"]},
        "key": {"required": False, "type": "str", "no_log": True},
        "key_id": {"required": False, "type": "str"},
        "old_key": {"required": False, "type": "str", "no_log": True},
        "mode": {"required": False, "choices": ["LKM", "SEKM"], "default": "LKM"},
        "job_wait": {"required": False, "type": "bool", "default": False},
        "job_wait_timeout": {"required": False, "type": "int", "default": 120}
    }
    specs.update(redfish_auth_params)
    module = AnsibleModule(
        argument_spec=specs,
        required_if=[
            ["command", "SetControllerKey", ["controller_id", "key", "key_id"]],
            ["command", "ReKey", ["controller_id", "mode"]], ["command", "ResetConfig", ["controller_id"]],
            ["command", "RemoveControllerKey", ["controller_id"]], ["command", "AssignSpare", ["target"]],
            ["command", "UnassignSpare", ["target"]], ["command", "EnableControllerEncryption", ["controller_id"]],
            ["command", "BlinkTarget", ["target", "volume_id"], True],
            ["command", "UnBlinkTarget", ["target", "volume_id"], True], ["command", "ConvertToRAID", ["target"]],
            ["command", "ConvertToNonRAID", ["target"]], ["command", "ChangePDStateToOnline", ["target"]],
            ["command", "ChangePDStateToOffline", ["target"]]
        ],
        supports_check_mode=True)
    validate_inputs(module)
    try:
        command = module.params["command"]
        with Redfish(module.params, req_session=True) as redfish_obj:
            if command == "ResetConfig":
                resp, job_uri, job_id = ctrl_reset_config(module, redfish_obj)
            elif command == "SetControllerKey" or command == "ReKey" or \
                    command == "RemoveControllerKey" or command == "EnableControllerEncryption":
                resp, job_uri, job_id = ctrl_key(module, redfish_obj)
            elif command == "AssignSpare" or command == "UnassignSpare":
                resp, job_uri, job_id = hot_spare_config(module, redfish_obj)
            elif command == "BlinkTarget" or command == "UnBlinkTarget":
                resp = target_identify_pattern(module, redfish_obj)
                if resp.success and resp.status_code == 200:
                    module.exit_json(msg=JOB_COMPLETION.format(command), changed=True)
            elif command == "ConvertToRAID" or command == "ConvertToNonRAID":
                resp, job_uri, job_id = convert_raid_status(module, redfish_obj)
            elif command == "ChangePDStateToOnline" or command == "ChangePDStateToOffline":
                resp, job_uri, job_id = change_pd_status(module, redfish_obj)

            job_wait = module.params["job_wait"]
            if job_wait:
                resp, msg = wait_for_job_completion(redfish_obj, job_uri, job_wait=job_wait,
                                                    wait_timeout=module.params["job_wait_timeout"])
                job_data = strip_substr_dict(resp.json_data)
                if job_data["JobState"] == "Failed":
                    changed, failed = False, True
                else:
                    changed, failed = True, False
                module.exit_json(msg=JOB_COMPLETION.format(command), task={"id": job_id, "uri": job_uri},
                                 status=job_data, changed=changed, failed=failed)
            else:
                resp, msg = wait_for_job_completion(redfish_obj, job_uri, job_wait=job_wait,
                                                    wait_timeout=module.params["job_wait_timeout"])
                job_data = strip_substr_dict(resp.json_data)
            module.exit_json(msg=JOB_SUBMISSION.format(command), task={"id": job_id, "uri": job_uri},
                             status=job_data)
    except HTTPError as err:
        module.fail_json(msg=str(err), error_info=json.load(err))
    except URLError as err:
        module.exit_json(msg=str(err), unreachable=True)
    except (RuntimeError, SSLValidationError, ConnectionError, KeyError,
            ImportError, ValueError, TypeError, AttributeError) as e:
        module.fail_json(msg=str(e))


if __name__ == '__main__':
    main()