summaryrefslogtreecommitdiffstats
path: root/ansible_collections/dellemc/powerflex/plugins/modules/device.py
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/dellemc/powerflex/plugins/modules/device.py')
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/device.py1105
1 files changed, 1105 insertions, 0 deletions
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/device.py b/ansible_collections/dellemc/powerflex/plugins/modules/device.py
new file mode 100644
index 000000000..a321315e3
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/device.py
@@ -0,0 +1,1105 @@
+#!/usr/bin/python
+
+# Copyright: (c) 2021, Dell Technologies
+# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+""" Ansible module for managing device on Dell Technologies (Dell) PowerFlex"""
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: device
+version_added: '1.1.0'
+short_description: Manage device on Dell PowerFlex
+description:
+- Managing device on PowerFlex storage system includes
+ adding new device, getting details of device, and removing a device.
+author:
+- Rajshree Khare (@khareRajshree) <ansible.team@dell.com>
+extends_documentation_fragment:
+ - dellemc.powerflex.powerflex
+options:
+ current_pathname:
+ description:
+ - Full path of the device to be added.
+ - Required while adding a device.
+ type: str
+ device_name:
+ description:
+ - Device name.
+ - Mutually exclusive with I(device_id).
+ type: str
+ device_id:
+ description:
+ - Device ID.
+ - Mutually exclusive with I(device_name).
+ type: str
+ sds_name:
+ description:
+ - The name of the SDS.
+ - Required while adding a device.
+ - Mutually exclusive with I(sds_id).
+ type: str
+ sds_id:
+ description:
+ - The ID of the SDS.
+ - Required while adding a device.
+ - Mutually exclusive with I(sds_name).
+ type: str
+ storage_pool_name:
+ description:
+ - Storage Pool name.
+ - Used while adding a storage device.
+ - Mutually exclusive with I(storage_pool_id), I(acceleration_pool_id) and
+ I(acceleration_pool_name).
+ type: str
+ storage_pool_id:
+ description:
+ - Storage Pool ID.
+ - Used while adding a storage device.
+ - Media type supported are C(SSD) and C(HDD).
+ - Mutually exclusive with I(storage_pool_name), I(acceleration_pool_id) and
+ I(acceleration_pool_name).
+ type: str
+ acceleration_pool_name:
+ description:
+ - Acceleration Pool Name.
+ - Used while adding an acceleration device.
+ - Media type supported are C(SSD) and C(NVDIMM).
+ - Mutually exclusive with I(storage_pool_id), I(storage_pool_name) and
+ I(acceleration_pool_name).
+ type: str
+ acceleration_pool_id:
+ description:
+ - Acceleration Pool ID.
+ - Used while adding an acceleration device.
+ - Media type supported are C(SSD) and C(NVDIMM).
+ - Mutually exclusive with I(acceleration_pool_name), I(storage_pool_name) and
+ I(storage_pool_id).
+ type: str
+ protection_domain_name:
+ description:
+ - Protection domain name.
+ - Used while identifying a storage pool along with I(storage_pool_name).
+ - Mutually exclusive with I(protection_domain_id).
+ type: str
+ protection_domain_id:
+ description:
+ - Protection domain ID.
+ - Used while identifying a storage pool along with I(storage_pool_name).
+ - Mutually exclusive with I(protection_domain_name).
+ type: str
+ external_acceleration_type:
+ description:
+ - Device external acceleration types.
+ - Used while adding a device.
+ type: str
+ choices: ['Invalid', 'None', 'Read', 'Write', 'ReadAndWrite']
+ media_type:
+ description:
+ - Device media types.
+ - Required while adding a device.
+ type: str
+ choices: ['HDD', 'SSD', 'NVDIMM']
+ state:
+ description:
+ - State of the device.
+ choices: ['present', 'absent']
+ required: true
+ type: str
+notes:
+ - The value for device_id is generated only after successful addition of the
+ device.
+ - To uniquely identify a device, either I(device_id) can be passed or one of
+ I(current_pathname) or I(device_name) must be passed with I(sds_id) or I(sds_name).
+ - It is recommended to install Rfcache driver for SSD device on SDS in
+ order to add it to an acceleration pool.
+ - The I(check_mode) is not supported.
+'''
+
+EXAMPLES = r'''
+- name: Add a device
+ dellemc.powerflex.device:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ media_type: "HDD"
+ device_name: "device2"
+ storage_pool_name: "pool1"
+ protection_domain_name: "domain1"
+ external_acceleration_type: "ReadAndWrite"
+ state: "present"
+- name: Get device details using device_id
+ dellemc.powerflex.device:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ device_id: "d7fe088900000000"
+ state: "present"
+- name: Get device details using (current_pathname, sds_name)
+ dellemc.powerflex.device:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node0"
+ state: "present"
+- name: Get device details using (current_pathname, sds_id)
+ dellemc.powerflex.device:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ current_pathname: "/dev/sdb"
+ sds_id: "5717d71800000000"
+ state: "present"
+- name: Remove a device using device_id
+ dellemc.powerflex.device:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ device_id: "76eb7e2f00010000"
+ state: "absent"
+- name: Remove a device using (current_pathname, sds_id)
+ dellemc.powerflex.device:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ state: "absent"
+'''
+
+RETURN = r'''
+changed:
+ description: Whether or not the resource has changed.
+ returned: always
+ type: bool
+ sample: 'false'
+device_details:
+ description: Details of the device.
+ returned: When device exists
+ type: dict
+ contains:
+ accelerationPoolId:
+ description: Acceleration pool ID.
+ type: str
+ accelerationPoolName:
+ description: Acceleration pool name.
+ type: str
+ accelerationProps:
+ description: Indicates acceleration props.
+ type: str
+ aggregatedState:
+ description: Indicates aggregated state.
+ type: str
+ ataSecurityActive:
+ description: Indicates ATA security active state.
+ type: bool
+ autoDetectMediaType:
+ description: Indicates auto detection of media type.
+ type: str
+ cacheLookAheadActive:
+ description: Indicates cache look ahead active state.
+ type: bool
+ capacity:
+ description: Device capacity.
+ type: int
+ capacityLimitInKb:
+ description: Device capacity limit in KB.
+ type: int
+ deviceCurrentPathName:
+ description: Device current path name.
+ type: str
+ deviceOriginalPathName:
+ description: Device original path name.
+ type: str
+ deviceState:
+ description: Indicates device state.
+ type: str
+ deviceType:
+ description: Indicates device type.
+ type: str
+ errorState:
+ description: Indicates error state.
+ type: str
+ externalAccelerationType:
+ description: Indicates external acceleration type.
+ type: str
+ fglNvdimmMetadataAmortizationX100:
+ description: Indicates FGL NVDIMM meta data amortization value.
+ type: int
+ fglNvdimmWriteCacheSize:
+ description: Indicates FGL NVDIMM write cache size.
+ type: int
+ firmwareVersion:
+ description: Indicates firmware version.
+ type: str
+ id:
+ description: Device ID.
+ type: str
+ ledSetting:
+ description: Indicates LED setting.
+ type: str
+ links:
+ description: Device links.
+ type: list
+ contains:
+ href:
+ description: Device instance URL.
+ type: str
+ rel:
+ description: Relationship of device with different
+ entities.
+ type: str
+ logicalSectorSizeInBytes:
+ description: Logical sector size in bytes.
+ type: int
+ longSuccessfulIos:
+ description: Indicates long successful IOs.
+ type: list
+ maxCapacityInKb:
+ description: Maximum device capacity limit in KB.
+ type: int
+ mediaFailing:
+ description: Indicates media failing.
+ type: bool
+ mediaType:
+ description: Indicates media type.
+ type: str
+ modelName:
+ description: Indicates model name.
+ type: str
+ name:
+ description: Device name.
+ type: str
+ persistentChecksumState:
+ description: Indicates persistent checksum state.
+ type: str
+ physicalSectorSizeInBytes:
+ description: Physical sector size in bytes.
+ type: int
+ protectionDomainId:
+ description: Protection domain ID.
+ type: str
+ protectionDomainName:
+ description: Protection domain name.
+ type: str
+ raidControllerSerialNumber:
+ description: RAID controller serial number.
+ type: str
+ rfcacheErrorDeviceDoesNotExist:
+ description: Indicates RF cache error device does not exist.
+ type: bool
+ rfcacheProps:
+ description: RF cache props.
+ type: str
+ sdsId:
+ description: SDS ID.
+ type: str
+ sdsName:
+ description: SDS name.
+ type: str
+ serialNumber:
+ description: Indicates Serial number.
+ type: str
+ spSdsId:
+ description: Indicates SPs SDS ID.
+ type: str
+ ssdEndOfLifeState:
+ description: Indicates SSD end of life state.
+ type: str
+ storagePoolId:
+ description: Storage Pool ID.
+ type: str
+ storagePoolName:
+ description: Storage Pool name.
+ type: str
+ storageProps:
+ description: Storage props.
+ type: list
+ temperatureState:
+ description: Indicates temperature state.
+ type: str
+ vendorName:
+ description: Indicates vendor name.
+ type: str
+ writeCacheActive:
+ description: Indicates write cache active.
+ type: bool
+ sample: {
+ "accelerationPoolId": null,
+ "accelerationProps": null,
+ "aggregatedState": "NeverFailed",
+ "ataSecurityActive": false,
+ "autoDetectMediaType": "SSD",
+ "cacheLookAheadActive": false,
+ "capacity": 0,
+ "capacityLimitInKb": 365772800,
+ "deviceCurrentPathName": "/dev/sdb",
+ "deviceOriginalPathName": "/dev/sdb",
+ "deviceState": "Normal",
+ "deviceType": "Unknown",
+ "errorState": "None",
+ "externalAccelerationType": "None",
+ "fglNvdimmMetadataAmortizationX100": 150,
+ "fglNvdimmWriteCacheSize": 16,
+ "firmwareVersion": null,
+ "id": "b6efa59900000000",
+ "ledSetting": "Off",
+ "links": [
+ {
+ "href": "/api/instances/Device::b6efa59900000000",
+ "rel": "self"
+ },
+ {
+ "href": "/api/instances/Device::b6efa59900000000/relationships
+ /Statistics",
+ "rel": "/api/Device/relationship/Statistics"
+ },
+ {
+ "href": "/api/instances/Sds::8f3bb0ce00000000",
+ "rel": "/api/parent/relationship/sdsId"
+ },
+ {
+ "href": "/api/instances/StoragePool::e0d8f6c900000000",
+ "rel": "/api/parent/relationship/storagePoolId"
+ },
+ {
+ "href": "/api/instances/SpSds::fedf6f2000000000",
+ "rel": "/api/parent/relationship/spSdsId"
+ }
+ ],
+ "logicalSectorSizeInBytes": 0,
+ "longSuccessfulIos": {
+ "longWindow": null,
+ "mediumWindow": null,
+ "shortWindow": null
+ },
+ "maxCapacityInKb": 365772800,
+ "mediaFailing": false,
+ "mediaType": "HDD",
+ "modelName": null,
+ "name": "device230",
+ "persistentChecksumState": "Protected",
+ "physicalSectorSizeInBytes": 0,
+ "protectionDomainId": "9300c1f900000000",
+ "protectionDomainName": "domain1",
+ "raidControllerSerialNumber": null,
+ "rfcacheErrorDeviceDoesNotExist": false,
+ "rfcacheProps": null,
+ "sdsId": "8f3bb0ce00000000",
+ "sdsName": "node1",
+ "serialNumber": null,
+ "slotNumber": null,
+ "spSdsId": "fedf6f2000000000",
+ "ssdEndOfLifeState": "NeverFailed",
+ "storagePoolId": "e0d8f6c900000000",
+ "storagePoolName": "pool1",
+ "storageProps": {
+ "destFglAccDeviceId": null,
+ "destFglNvdimmSizeMb": 0,
+ "fglAccDeviceId": null,
+ "fglNvdimmSizeMb": 0
+ },
+ "temperatureState": "NeverFailed",
+ "vendorName": null,
+ "writeCacheActive": false
+ }
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell\
+ import utils
+
+LOG = utils.get_logger('device')
+
+
+class PowerFlexDevice(object):
+ """Class with device operations"""
+
+ def __init__(self):
+ """ Define all parameters required by this module"""
+ self.module_params = utils.get_powerflex_gateway_host_parameters()
+ self.module_params.update(get_powerflex_device_parameters())
+
+ mut_ex_args = [['sds_name', 'sds_id'],
+ ['device_name', 'device_id'],
+ ['protection_domain_name',
+ 'protection_domain_id'],
+ ['storage_pool_name', 'storage_pool_id'],
+ ['acceleration_pool_name', 'acceleration_pool_id'],
+ ['acceleration_pool_id', 'storage_pool_id'],
+ ['acceleration_pool_name', 'storage_pool_name'],
+ ['device_id', 'sds_name'],
+ ['device_id', 'sds_id'],
+ ['device_id', 'current_pathname']]
+
+ # initialize the Ansible module
+ self.module = AnsibleModule(
+ argument_spec=self.module_params,
+ supports_check_mode=False,
+ mutually_exclusive=mut_ex_args)
+
+ utils.ensure_required_libs(self.module)
+
+ try:
+ self.powerflex_conn = utils.get_powerflex_gateway_host_connection(
+ self.module.params)
+ LOG.info("Got the PowerFlex system connection object instance")
+ except Exception as e:
+ LOG.error(str(e))
+ self.module.fail_json(msg=str(e))
+
+ def get_device_details(self, current_pathname=None, sds_id=None,
+ device_name=None, device_id=None):
+ """Get device details
+ :param current_pathname: Device path name
+ :type current_pathname: str
+ :param sds_id: ID of the SDS
+ :type sds_id: str
+ :param device_name: Name of the device
+ :type device_name: str
+ :param device_id: ID of the device
+ :type device_id: str
+ :return: Details of device if it exist
+ :rtype: dict
+ """
+
+ try:
+ if current_pathname and sds_id:
+ device_details = self.powerflex_conn.device.get(
+ filter_fields={'deviceCurrentPathName': current_pathname,
+ 'sdsId': sds_id})
+ elif device_name and sds_id:
+ device_details = self.powerflex_conn.device.get(
+ filter_fields={'name': device_name,
+ 'sdsId': sds_id})
+ else:
+ device_details = self.powerflex_conn.device.get(
+ filter_fields={'id': device_id})
+
+ if len(device_details) == 0:
+ msg = "Device not found"
+ LOG.info(msg)
+ return None
+
+ return device_details[0]
+
+ except Exception as e:
+ error_msg = "Failed to get the device with error '%s'" % str(e)
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def get_sds(self, sds_name=None, sds_id=None):
+ """Get SDS details
+ :param sds_name: Name of the SDS
+ :param sds_id: ID of the SDS
+ :return: SDS details
+ :rtype: dict
+ """
+ name_or_id = sds_id if sds_id else sds_name
+ try:
+ sds_details = None
+ if sds_id:
+ sds_details = self.powerflex_conn.sds.get(
+ filter_fields={'id': sds_id})
+
+ if sds_name:
+ sds_details = self.powerflex_conn.sds.get(
+ filter_fields={'name': sds_name})
+
+ if not sds_details:
+ error_msg = "Unable to find the SDS with '%s'. Please " \
+ "enter a valid SDS name/id." % name_or_id
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ return sds_details[0]
+
+ except Exception as e:
+ error_msg = "Failed to get the SDS '%s' with error '%s'" \
+ % (name_or_id, str(e))
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def get_protection_domain(self, protection_domain_name=None,
+ protection_domain_id=None):
+ """Get protection domain details
+ :param protection_domain_name: Name of the protection domain
+ :param protection_domain_id: ID of the protection domain
+ :return: Protection domain details
+ :rtype: dict
+ """
+ name_or_id = protection_domain_id if protection_domain_id \
+ else protection_domain_name
+ try:
+ pd_details = None
+ if protection_domain_id:
+ pd_details = self.powerflex_conn.protection_domain.get(
+ filter_fields={'id': protection_domain_id})
+
+ if protection_domain_name:
+ pd_details = self.powerflex_conn.protection_domain.get(
+ filter_fields={'name': protection_domain_name})
+
+ if not pd_details:
+ error_msg = "Unable to find the protection domain with " \
+ "'%s'. Please enter a valid protection domain " \
+ "name/id." % name_or_id
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ return pd_details[0]
+
+ except Exception as e:
+ error_msg = "Failed to get the protection domain '%s' with " \
+ "error '%s'" % (name_or_id, str(e))
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def get_storage_pool(self, storage_pool_name=None,
+ storage_pool_id=None,
+ protection_domain_id=None):
+ """Get storage pool details
+ :param storage_pool_name: Name of the storage pool
+ :param storage_pool_id: ID of the storage pool
+ :param protection_domain_id: ID of the protection domain
+ :return: Storage pool details
+ :rtype: dict
+ """
+ name_or_id = storage_pool_id if storage_pool_id else storage_pool_name
+ try:
+ storage_pool_details = None
+ if storage_pool_id:
+ storage_pool_details = self.powerflex_conn.storage_pool.get(
+ filter_fields={'id': storage_pool_id})
+
+ if storage_pool_name:
+ storage_pool_details = self.powerflex_conn.storage_pool.get(
+ filter_fields={'name': storage_pool_name,
+ 'protectionDomainId': protection_domain_id}
+ )
+
+ if not storage_pool_details:
+ error_msg = "Unable to find the storage pool with " \
+ "'%s'. Please enter a valid storage pool " \
+ "name/id." % name_or_id
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ return storage_pool_details[0]
+
+ except Exception as e:
+ error_msg = "Failed to get the storage_pool '%s' with " \
+ "error '%s'" % (name_or_id, str(e))
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def get_acceleration_pool(self, acceleration_pool_name=None,
+ acceleration_pool_id=None,
+ protection_domain_id=None):
+ """Get acceleration pool details
+ :param acceleration_pool_name: Name of the acceleration pool
+ :param acceleration_pool_id: ID of the acceleration pool
+ :param protection_domain_id: ID of the protection domain
+ :return: Acceleration pool details
+ :rtype: dict
+ """
+ name_or_id = acceleration_pool_id \
+ if acceleration_pool_id else acceleration_pool_name
+ try:
+ acceleration_pool_details = None
+ if acceleration_pool_id:
+ acceleration_pool_details = self.powerflex_conn.\
+ acceleration_pool.get(filter_fields={
+ 'id': acceleration_pool_id})
+
+ if acceleration_pool_name:
+ acceleration_pool_details = self.powerflex_conn.\
+ acceleration_pool.get(filter_fields={
+ 'name': acceleration_pool_name,
+ 'protectionDomainId': protection_domain_id})
+
+ if not acceleration_pool_details:
+ error_msg = "Unable to find the acceleration pool with " \
+ "'%s'. Please enter a valid acceleration pool " \
+ "name/id." % name_or_id
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ return acceleration_pool_details[0]
+
+ except Exception as e:
+ error_msg = "Failed to get the acceleration pool '%s' with " \
+ "error '%s'" % (name_or_id, str(e))
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def add_device(self, device_name, current_pathname, sds_id,
+ storage_pool_id, media_type, acceleration_pool_id,
+ external_acceleration_type):
+ """Add device
+ :param device_name: Device name
+ :type device_name: str
+ :param current_pathname: Current pathname of device
+ :type current_pathname: str
+ :param sds_id: SDS ID
+ :type sds_id: str
+ :param storage_pool_id: Storage Pool ID
+ :type storage_pool_id: str
+ :param media_type: Media type of device
+ :type media_type: str
+ :param acceleration_pool_id: Acceleration pool ID
+ :type acceleration_pool_id: str
+ :param external_acceleration_type: External acceleration type
+ :type external_acceleration_type: str
+ return: Boolean indicating if add device operation is successful
+ """
+ try:
+ if device_name is None or len(device_name.strip()) == 0:
+ error_msg = "Please provide valid device_name value for " \
+ "adding a device."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if current_pathname is None or len(current_pathname.strip()) == 0:
+ error_msg = "Current pathname of device is a mandatory " \
+ "parameter for adding a device. Please enter a " \
+ "valid value."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if sds_id is None or len(sds_id.strip()) == 0:
+ error_msg = "Please provide valid sds_id value " \
+ "for adding a device."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if storage_pool_id is None and acceleration_pool_id is None:
+ error_msg = "Please provide either storage pool name/ID " \
+ "or acceleration pool name/ID for adding a " \
+ "device."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ add_params = ("current_pathname: %s, "
+ "sds_id: %s, "
+ "acceleration_pool_id: %s,"
+ "external_acceleration_type: %s,"
+ "media_type: %s,"
+ "device_name: %s,"
+ "storage_pool_id: %s,"
+ % (current_pathname, sds_id,
+ acceleration_pool_id,
+ external_acceleration_type,
+ media_type,
+ device_name,
+ storage_pool_id))
+ LOG.info("Adding device with params: %s", add_params)
+
+ self.powerflex_conn.device.create(
+ current_pathname=current_pathname,
+ sds_id=sds_id,
+ acceleration_pool_id=acceleration_pool_id,
+ external_acceleration_type=external_acceleration_type,
+ media_type=media_type,
+ name=device_name,
+ storage_pool_id=storage_pool_id)
+ return True
+ except Exception as e:
+ error_msg = "Adding device %s operation failed with " \
+ "error '%s'" % (device_name, str(e))
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def remove_device(self, device_id):
+ """Remove device
+ :param device_id: Device ID
+ :type device_id: str
+ return: Boolean indicating if remove device operation is
+ successful
+ """
+ try:
+ LOG.info("Device to be removed: %s", device_id)
+ self.powerflex_conn.device.delete(device_id=device_id)
+ return True
+ except Exception as e:
+ error_msg = "Remove device '%s' operation failed with " \
+ "error '%s'" % (device_id, str(e))
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def validate_input_parameters(self, device_name=None, device_id=None,
+ current_pathname=None, sds_name=None,
+ sds_id=None):
+ """Validate the input parameters"""
+
+ # Unique ways to identify a device:
+ # (current_pathname , sds_id)
+ # (current_pathname , sds_name)
+ # (device_name , sds_name)
+ # (device_name , sds_id)
+ # device_id.
+
+ if current_pathname:
+ if (sds_name is None or len(sds_name.strip()) == 0) \
+ and (sds_id is None or len(sds_id.strip()) == 0):
+ error_msg = "sds_name or sds_id is mandatory along with " \
+ "current_pathname. Please enter a valid value."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+ elif current_pathname is not None \
+ and len(current_pathname.strip()) == 0:
+ error_msg = "Please enter a valid value for current_pathname."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if device_name:
+ if (sds_name is None or len(sds_name.strip()) == 0) \
+ and (sds_id is None or len(sds_id.strip()) == 0):
+ error_msg = "sds_name or sds_id is mandatory along with " \
+ "device_name. Please enter a valid value."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+ elif device_name is not None and len(device_name.strip()) == 0:
+ error_msg = "Please enter a valid value for device_name."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if sds_name:
+ if (current_pathname is None
+ or len(current_pathname.strip()) == 0) \
+ and (device_name is None
+ or len(device_name.strip()) == 0):
+ error_msg = "current_pathname or device_name is mandatory " \
+ "along with sds_name. Please enter a valid value."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+ elif sds_name is not None and len(sds_name.strip()) == 0:
+ error_msg = "Please enter a valid value for sds_name."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if sds_id:
+ if (current_pathname is None
+ or len(current_pathname.strip()) == 0) \
+ and (device_name is None
+ or len(device_name.strip()) == 0):
+ error_msg = "current_pathname or device_name is mandatory " \
+ "along with sds_id. Please enter a valid value."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+ elif sds_id is not None and len(sds_id.strip()) == 0:
+ error_msg = "Please enter a valid value for sds_id."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if device_id is not None and len(device_id.strip()) == 0:
+ error_msg = "Please provide valid device_id value to identify " \
+ "a device."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if current_pathname is None and device_name is None \
+ and device_id is None:
+ error_msg = "Please specify a valid parameter combination to " \
+ "identify a device."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def validate_add_parameters(self, device_id=None,
+ external_acceleration_type=None,
+ storage_pool_id=None,
+ storage_pool_name=None,
+ acceleration_pool_id=None,
+ acceleration_pool_name=None):
+ """Validate the add device parameters"""
+
+ if device_id:
+ error_msg = "Addition of device is allowed using " \
+ "device_name only, device_id given."
+ LOG.info(error_msg)
+ self.module.fail_json(msg=error_msg)
+ if external_acceleration_type and storage_pool_id is None \
+ and storage_pool_name is None \
+ and acceleration_pool_id is None \
+ and acceleration_pool_name is None:
+ error_msg = "Storage Pool ID/name or Acceleration Pool " \
+ "ID/name is mandatory along with " \
+ "external_acceleration_type."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def perform_module_operation(self):
+ """
+ Perform different actions on device based on parameters passed in
+ the playbook
+ """
+ current_pathname = self.module.params['current_pathname']
+ device_name = self.module.params['device_name']
+ device_id = self.module.params['device_id']
+ sds_name = self.module.params['sds_name']
+ sds_id = self.module.params['sds_id']
+ storage_pool_name = self.module.params['storage_pool_name']
+ storage_pool_id = self.module.params['storage_pool_id']
+ acceleration_pool_id = self.module.params['acceleration_pool_id']
+ acceleration_pool_name = self.module.params['acceleration_pool_name']
+ protection_domain_name = self.module.params['protection_domain_name']
+ protection_domain_id = self.module.params['protection_domain_id']
+ external_acceleration_type = self.module.params[
+ 'external_acceleration_type']
+ media_type = self.module.params['media_type']
+ state = self.module.params['state']
+
+ # result is a dictionary to contain end state and device details
+ changed = False
+ result = dict(
+ changed=False,
+ device_details={}
+ )
+
+ # validate input parameters
+ self.validate_input_parameters(device_name, device_id,
+ current_pathname, sds_name, sds_id)
+
+ # get SDS ID from name
+ if sds_name:
+ sds_details = self.get_sds(sds_name)
+ if sds_details:
+ sds_id = sds_details['id']
+ msg = "Fetched the SDS details with id '%s', name '%s'" \
+ % (sds_id, sds_name)
+ LOG.info(msg)
+
+ # get device details
+ device_details = self.get_device_details(current_pathname,
+ sds_id, device_name,
+ device_id)
+
+ if device_details:
+ device_id = device_details['id']
+ msg = "Fetched the device details %s" % (str(device_details))
+ LOG.info(msg)
+
+ # add operation
+ add_changed = False
+ if state == 'present' and not device_details:
+ # get Protection Domain ID from name
+ # it is needed to uniquely identify a storage pool or acceleration
+ # pool using name
+ if protection_domain_name \
+ and (storage_pool_name or acceleration_pool_name):
+ pd_details = self.get_protection_domain(
+ protection_domain_name)
+ if pd_details:
+ protection_domain_id = pd_details['id']
+ msg = "Fetched the protection domain details with id " \
+ "'%s', name '%s'" % (protection_domain_id,
+ protection_domain_name)
+ LOG.info(msg)
+
+ # get storage pool ID from name
+ if storage_pool_name:
+ if protection_domain_id:
+ storage_pool_details = self.get_storage_pool(
+ storage_pool_name=storage_pool_name,
+ protection_domain_id=protection_domain_id)
+ if storage_pool_details:
+ storage_pool_id = storage_pool_details['id']
+ msg = "Fetched the storage pool details with id '%s', " \
+ "name '%s'" % (storage_pool_id, storage_pool_name)
+ LOG.info(msg)
+ else:
+ error_msg = "Protection domain name/id is required to " \
+ "uniquely identify a storage pool, only " \
+ "storage_pool_name is given."
+ LOG.info(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ # get acceleration pool ID from name
+ if acceleration_pool_name:
+ if protection_domain_id:
+ acceleration_pool_details = self.get_acceleration_pool(
+ acceleration_pool_name=acceleration_pool_name,
+ protection_domain_id=protection_domain_id)
+ if acceleration_pool_details:
+ acceleration_pool_id = acceleration_pool_details['id']
+ msg = "Fetched the acceleration pool details with id " \
+ "'%s', name '%s'" % (acceleration_pool_id,
+ acceleration_pool_name)
+ LOG.info(msg)
+ else:
+ error_msg = "Protection domain name/id is required to " \
+ "uniquely identify a acceleration pool, " \
+ "only acceleration_pool_name is given."
+ LOG.info(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ # validate input parameters
+ self.validate_add_parameters(device_id,
+ external_acceleration_type,
+ storage_pool_id,
+ storage_pool_name,
+ acceleration_pool_id,
+ acceleration_pool_name)
+ add_changed = self.add_device(device_name, current_pathname,
+ sds_id, storage_pool_id, media_type,
+ acceleration_pool_id,
+ external_acceleration_type)
+ if add_changed:
+ device_details = self.get_device_details(
+ device_name=device_name, sds_id=sds_id)
+ device_id = device_details['id']
+ msg = "Device created successfully, fetched device details " \
+ "%s" % (str(device_details))
+ LOG.info(msg)
+
+ # remove operation
+ remove_changed = False
+ if state == 'absent' and device_details:
+ remove_changed = self.remove_device(device_id)
+
+ if add_changed or remove_changed:
+ changed = True
+
+ # modify operation
+ if device_details and state == 'present':
+ modify_dict = to_modify(device_details, media_type,
+ external_acceleration_type)
+ if modify_dict:
+ error_msg = "Modification of device attributes is " \
+ "currently not supported by Ansible modules."
+ LOG.info(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ # Returning the updated device details
+ if state == 'present':
+ device_details = self.show_output(device_id)
+ result['device_details'] = device_details
+ result['changed'] = changed
+ self.module.exit_json(**result)
+
+ def show_output(self, device_id):
+ """Show device details
+ :param device_id: ID of the device
+ :type device_id: str
+ :return: Details of device
+ :rtype: dict
+ """
+
+ try:
+ device_details = self.powerflex_conn.device.get(
+ filter_fields={'id': device_id})
+
+ if len(device_details) == 0:
+ msg = "Device with identifier '%s' not found" % device_id
+ LOG.error(msg)
+ return None
+
+ # Append SDS name
+ if 'sdsId' in device_details[0] and device_details[0]['sdsId']:
+ sds_details = self.get_sds(sds_id=device_details[0]['sdsId'])
+ device_details[0]['sdsName'] = sds_details['name']
+
+ # Append storage pool name and its protection domain name and ID
+ if 'storagePoolId' in device_details[0] \
+ and device_details[0]['storagePoolId']:
+ sp_details = self.get_storage_pool(
+ storage_pool_id=device_details[0]['storagePoolId'])
+ device_details[0]['storagePoolName'] = sp_details['name']
+ pd_id = sp_details['protectionDomainId']
+ device_details[0]['protectionDomainId'] = pd_id
+ pd_details = self.get_protection_domain(
+ protection_domain_id=pd_id)
+ device_details[0]['protectionDomainName'] = pd_details['name']
+
+ # Append acceleration pool name and its protection domain name
+ # and ID
+ if 'accelerationPoolId' in device_details[0] \
+ and device_details[0]['accelerationPoolId']:
+ ap_details = self.get_acceleration_pool(
+ acceleration_pool_id=device_details[0][
+ 'accelerationPoolId'])
+ device_details[0]['accelerationPoolName'] = ap_details['name']
+ pd_id = ap_details['protectionDomainId']
+ device_details[0]['protectionDomainId'] = pd_id
+ pd_details = self.get_protection_domain(
+ protection_domain_id=pd_id)
+ device_details[0]['protectionDomainName'] = pd_details['name']
+
+ return device_details[0]
+
+ except Exception as e:
+ error_msg = "Failed to get the device '%s' with error '%s'"\
+ % (device_id, str(e))
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+
+def to_modify(device_details, media_type, external_acceleration_type):
+ """Identify device attributes to be modified"""
+
+ modify_dict = {}
+
+ if media_type is not None and \
+ device_details['mediaType'] != media_type:
+ modify_dict['mediaType'] = media_type
+
+ if external_acceleration_type is not None and \
+ device_details['externalAccelerationType'] \
+ != external_acceleration_type:
+ modify_dict['externalAccelerationType'] \
+ = external_acceleration_type
+
+ if len(modify_dict) != 0:
+ LOG.info("Attributes to be modified: %s", modify_dict)
+ return modify_dict
+
+
+def get_powerflex_device_parameters():
+ """This method provide parameter required for the device module on
+ PowerFlex"""
+ return dict(
+ current_pathname=dict(),
+ device_name=dict(),
+ device_id=dict(),
+ sds_name=dict(),
+ sds_id=dict(),
+ storage_pool_name=dict(),
+ storage_pool_id=dict(),
+ acceleration_pool_id=dict(),
+ acceleration_pool_name=dict(),
+ protection_domain_name=dict(),
+ protection_domain_id=dict(),
+ external_acceleration_type=dict(choices=['Invalid', 'None', 'Read',
+ 'Write', 'ReadAndWrite']),
+ media_type=dict(choices=['HDD', 'SSD', 'NVDIMM']),
+ state=dict(required=True, type='str', choices=['present', 'absent'])
+ )
+
+
+def main():
+ """ Create PowerFlex device object and perform actions on it
+ based on user input from playbook"""
+ obj = PowerFlexDevice()
+ obj.perform_module_operation()
+
+
+if __name__ == '__main__':
+ main()