diff --git a/plugins/module_utils/storage/dell/utils.py b/plugins/module_utils/storage/dell/utils.py index 50f4166..5a9435d 100644 --- a/plugins/module_utils/storage/dell/utils.py +++ b/plugins/module_utils/storage/dell/utils.py @@ -223,7 +223,5 @@ def get_filter(name, id=None): def random_uuid_generation(): - generate_uuid = ''.join( - [random.choice(string.ascii_lowercase + string.digits) for n in range(32)]) - - return generate_uuid + """Generate a random UUID using lowercase letters and digits.""" + return ''.join(random.choices(string.ascii_lowercase + string.digits, k=32)) diff --git a/plugins/modules/device.py b/plugins/modules/device.py index e833531..8d179fe 100644 --- a/plugins/modules/device.py +++ b/plugins/modules/device.py @@ -780,69 +780,81 @@ def validate_input_parameters(self, device_name=None, device_id=None, # (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." + self.validate_current_pathname(current_pathname, sds_name, sds_id) + + self.validate_device_name(device_name, sds_name, sds_id) + + self.validate_sds_name(device_name, current_pathname, sds_name) + + self.validate_sds_id(device_name, current_pathname, sds_id) + + 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 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." + 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) - if sds_name: + def validate_sds_id(self, device_name, current_pathname, sds_id): + 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_name. Please enter a valid value." + "along with sds_id. 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." + 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 sds_id: + def validate_sds_name(self, device_name, current_pathname, sds_name): + 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_id. Please enter a valid value." + "along with sds_name. 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." + 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 device_id is not None and len(device_id.strip()) == 0: - error_msg = "Please provide valid device_id value to identify " \ - "a device." + def validate_device_name(self, device_name, sds_name, sds_id): + 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 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." + def validate_current_pathname(self, current_pathname, sds_name, sds_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) @@ -891,7 +903,6 @@ def perform_module_operation(self): state = self.module.params['state'] # result is a dictionary to contain end state and device details - changed = False result = dict( changed=False, device_details={} @@ -903,18 +914,13 @@ def perform_module_operation(self): # 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) + sds_id = self.get_sds_id(sds_name) # get device details device_details = self.get_device_details(current_pathname, sds_id, device_name, device_id) - + # Get device id if device_details: device_id = device_details['id'] msg = "Fetched the device details %s" % (str(device_details)) @@ -922,102 +928,139 @@ def perform_module_operation(self): # 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) + if state == 'present': + if device_details: + # modify operation + modify_dict = to_modify(device_details, media_type, + external_acceleration_type) + self.can_modify(modify_dict) + else: + # get Protection Domain ID from name + # it is needed to uniquely identify a storage pool or acceleration + # pool using name + device_id, add_changed = self.create_device( + current_pathname, device_name, device_id, sds_id, + storage_pool_id, storage_pool_name, acceleration_pool_id, + acceleration_pool_name, protection_domain_id, + protection_domain_name, external_acceleration_type, + media_type) - # 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) + device_details = self.show_output(device_id) + result['device_details'] = device_details # 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 + result['changed'] = add_changed or remove_changed self.module.exit_json(**result) + def create_device(self, current_pathname, device_name, device_id, sds_id, + storage_pool_id, storage_pool_name, acceleration_pool_id, + acceleration_pool_name, protection_domain_id, + protection_domain_name, external_acceleration_type, + media_type): + if protection_domain_name \ + and (storage_pool_name or acceleration_pool_name): + protection_domain_id = self.get_protection_domain_id( + protection_domain_name) + + # get storage pool ID from name + if storage_pool_name: + storage_pool_id = self.get_storage_pool_id( + storage_pool_name, protection_domain_id) + + # get acceleration pool ID from name + if acceleration_pool_name: + acceleration_pool_id = self.get_acceleration_pool_id( + acceleration_pool_name, protection_domain_id) + + # 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) + return device_id, add_changed + + def can_modify(self, modify_dict): + 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) + + def get_acceleration_pool_id(self, acceleration_pool_name, protection_domain_id): + 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) + return acceleration_pool_id + + def get_storage_pool_id(self, storage_pool_name, protection_domain_id): + 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) + return storage_pool_id + + def get_protection_domain_id(self, protection_domain_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) + return protection_domain_id + + def get_sds_id(self, 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) + return sds_id + def show_output(self, device_id): """Show device details :param device_id: ID of the device diff --git a/plugins/modules/info.py b/plugins/modules/info.py index a114d0c..13e4db3 100644 --- a/plugins/modules/info.py +++ b/plugins/modules/info.py @@ -2391,70 +2391,56 @@ def perform_module_operation(self): api_version = self.get_api_details() array_details = self.get_array_details() - sdc = [] - sds = [] - storage_pool = [] - vol = [] - snapshot_policy = [] - protection_domain = [] - device = [] - rcgs = [] - replication_pair = [] - fault_sets = [] - service_template = [] - managed_device = [] - deployment = [] - firmware_repository = [] - subset = self.module.params['gather_subset'] + subset_result_filter = {} + subset_result_wo_param = {} self.validate_subset(api_version, subset) - if subset is not None: - if 'sdc' in subset: - sdc = self.get_sdc_list(filter_dict=filter_dict) - if 'sds' in subset: - sds = self.get_sds_list(filter_dict=filter_dict) - if 'protection_domain' in subset: - protection_domain = self.get_pd_list(filter_dict=filter_dict) - if 'storage_pool' in subset: - storage_pool = self.get_storage_pool_list(filter_dict=filter_dict) - if 'vol' in subset: - vol = self.get_volumes_list(filter_dict=filter_dict) - if 'snapshot_policy' in subset: - snapshot_policy = self.get_snapshot_policy_list(filter_dict=filter_dict) - if 'device' in subset: - device = self.get_devices_list(filter_dict=filter_dict) - if 'rcg' in subset: - rcgs = self.get_replication_consistency_group_list(filter_dict=filter_dict) - if 'replication_pair' in subset: - replication_pair = self.get_replication_pair_list(filter_dict=filter_dict) - if 'fault_set' in subset: - fault_sets = self.get_fault_sets_list(filter_dict=filter_dict) - if 'managed_device' in subset: - managed_device = self.get_managed_devices_list() - if 'service_template' in subset: - service_template = self.get_service_templates_list() - if 'deployment' in subset: - deployment = self.get_deployments_list() - if 'firmware_repository' in subset: - firmware_repository = self.get_firmware_repository_list() + + subset_dict_with_filter = { + "sdc": self.get_sdc_list, + "sds": self.get_sds_list, + "protection_domain": self.get_pd_list, + "storage_pool": self.get_storage_pool_list, + "vol": self.get_volumes_list, + "snapshot_policy": self.get_snapshot_policy_list, + "device": self.get_devices_list, + "rcg": self.get_replication_consistency_group_list, + "replication_pair": self.get_replication_pair_list, + "fault_set": self.get_fault_sets_list, + } + + subset_wo_param = { + "managed_device": self.get_managed_devices_list, + "service_template": self.get_service_templates_list, + "deployment": self.get_deployments_list, + "firmware_repository": self.get_firmware_repository_list + } + if subset: + subset_result_filter = {key: subset_dict_with_filter[key]( + filter_dict=filter_dict) for key in subset if key in subset_dict_with_filter} + subset_result_wo_param = {key: subset_wo_param[key]( + ) for key in subset if key in subset_wo_param} self.module.exit_json( Array_Details=array_details, API_Version=api_version, - SDCs=sdc, - SDSs=sds, - Storage_Pools=storage_pool, - Volumes=vol, - Snapshot_Policies=snapshot_policy, - Protection_Domains=protection_domain, - Devices=device, - Replication_Consistency_Groups=rcgs, - Replication_Pairs=replication_pair, - Fault_Sets=fault_sets, - ManagedDevices=managed_device, - ServiceTemplates=service_template, - Deployments=deployment, - FirmwareRepository=firmware_repository + SDCs=subset_result_filter.get("sdc", []), + SDSs=subset_result_filter.get("sds", []), + Storage_Pools=subset_result_filter.get("storage_pool", []), + Volumes=subset_result_filter.get("vol", []), + Snapshot_Policies=subset_result_filter.get("snapshot_policy", []), + Protection_Domains=subset_result_filter.get( + "protection_domain", []), + Devices=subset_result_filter.get("device", []), + Replication_Consistency_Groups=subset_result_filter.get("rcg", []), + Replication_Pairs=subset_result_filter.get("replication_pair", []), + Fault_Sets=subset_result_filter.get("fault_set", []), + ManagedDevices=subset_result_wo_param.get("managed_device", []), + ServiceTemplates=subset_result_wo_param.get( + "service_template", []), + Deployments=subset_result_wo_param.get("deployment", []), + FirmwareRepository=subset_result_wo_param.get( + "firmware_repository", []) ) diff --git a/plugins/modules/replication_consistency_group.py b/plugins/modules/replication_consistency_group.py index b106dfb..05aafad 100644 --- a/plugins/modules/replication_consistency_group.py +++ b/plugins/modules/replication_consistency_group.py @@ -962,18 +962,18 @@ def modify_rcg(self, rcg_id, rcg_details): is_consistent = self.module.params['is_consistent'] activity_mode = self.module.params['activity_mode'] new_rcg_name = self.module.params['new_rcg_name'] - changed = False pause, freeze = self.get_pause_and_freeze_value() - if create_snapshot is True: - changed = self.create_rcg_snapshot(rcg_id) - if rpo and rcg_details['rpoInSeconds'] and \ - rpo != rcg_details['rpoInSeconds']: - changed = self.modify_rpo(rcg_id, rpo) + + changed = self.create_snap(rcg_id, create_snapshot) + + rpo_changed = self.rpo_mod(rcg_id, rcg_details, rpo) + if target_volume_access_mode and \ rcg_details['targetVolumeAccessMode'] != target_volume_access_mode: changed = \ - self.modify_target_volume_access_mode(rcg_id, target_volume_access_mode) + self.modify_target_volume_access_mode( + rcg_id, target_volume_access_mode) if activity_mode and \ self.modify_activity_mode(rcg_id, rcg_details, activity_mode): changed = True @@ -993,8 +993,20 @@ def modify_rcg(self, rcg_id, rcg_details): changed = True rcg_action_status = self.perform_rcg_action(rcg_id, rcg_details) - changed = changed or rcg_action_status + return rpo_changed or changed or rcg_action_status + + def rpo_mod(self, rcg_id, rcg_details, rpo): + changed = False + if rpo and rcg_details['rpoInSeconds'] and \ + rpo != rcg_details['rpoInSeconds']: + changed = self.modify_rpo(rcg_id, rpo) + return changed + + def create_snap(self, rcg_id, create_snapshot): + changed = False + if create_snapshot is True: + changed = self.create_rcg_snapshot(rcg_id) return changed def validate_input(self, rcg_params): diff --git a/plugins/modules/sdc.py b/plugins/modules/sdc.py index bb13a19..a97c2ca 100644 --- a/plugins/modules/sdc.py +++ b/plugins/modules/sdc.py @@ -296,7 +296,7 @@ def get_sdc(self, sdc_name=None, sdc_ip=None, sdc_id=None): LOG.error(errormsg) self.module.fail_json(msg=errormsg) - def validate_parameters(self, sdc_name=None, sdc_id=None, sdc_ip=None): + def validate_parameters(self): """Validate the input parameters""" sdc_identifiers = ['sdc_name', 'sdc_id', 'sdc_ip'] @@ -367,7 +367,7 @@ def perform_module_operation(self): sdc_details={} ) - self.validate_parameters(sdc_name, sdc_id, sdc_ip) + self.validate_parameters() sdc_details = self.get_sdc(sdc_name=sdc_name, sdc_id=sdc_id, sdc_ip=sdc_ip) id_ip_name = sdc_name or sdc_ip or sdc_id diff --git a/plugins/modules/snapshot.py b/plugins/modules/snapshot.py index 0cc41c5..74489e6 100644 --- a/plugins/modules/snapshot.py +++ b/plugins/modules/snapshot.py @@ -437,12 +437,11 @@ def get_snapshot(self, snapshot_name=None, snapshot_id=None): id_or_name = snapshot_id if snapshot_id else snapshot_name try: + filters = {'id': snapshot_id} if snapshot_name: - snapshot_details = self.powerflex_conn.volume.get( - filter_fields={'name': snapshot_name}) - else: - snapshot_details = self.powerflex_conn.volume.get( - filter_fields={'id': snapshot_id}) + filters = {'name': snapshot_name} + snapshot_details = self.powerflex_conn.volume.get( + filter_fields=filters) if len(snapshot_details) == 0: msg = "Snapshot with identifier %s is not found" % id_or_name @@ -455,39 +454,16 @@ def get_snapshot(self, snapshot_name=None, snapshot_id=None): self.module.fail_json(msg=errormsg) # Add ancestor volume name - if 'ancestorVolumeId' in snapshot_details[0] and \ - snapshot_details[0]['ancestorVolumeId']: - vol = self.get_volume( - vol_id=snapshot_details[0]['ancestorVolumeId']) - snapshot_details[0]['ancestorVolumeName'] = vol['name'] + self.add_ancestor(snapshot_details) # Add size in GB - if 'sizeInKb' in snapshot_details[0] and \ - snapshot_details[0]['sizeInKb']: - snapshot_details[0]['sizeInGb'] = utils.get_size_in_gb( - snapshot_details[0]['sizeInKb'], 'KB') + self.add_size_in_kb(snapshot_details) # Add storage pool name - if 'storagePoolId' in snapshot_details[0] and \ - snapshot_details[0]['storagePoolId']: - sp = self.get_storage_pool(snapshot_details[0]['storagePoolId']) - if len(sp) > 0: - snapshot_details[0]['storagePoolName'] = sp[0]['name'] + self.add_storage_pool_name(snapshot_details) # Add retention in hours - if 'secureSnapshotExpTime' in snapshot_details[0] and\ - 'creationTime' in snapshot_details[0]: - if snapshot_details[0]['secureSnapshotExpTime'] != 0: - expiry_obj = datetime.fromtimestamp( - snapshot_details[0]['secureSnapshotExpTime']) - creation_obj = datetime.fromtimestamp( - snapshot_details[0]['creationTime']) - - td = utils.dateutil.relativedelta.relativedelta( - expiry_obj, creation_obj) - snapshot_details[0]['retentionInHours'] = td.hours - else: - snapshot_details[0]['retentionInHours'] = 0 + self.add_retention_in_hours(snapshot_details) # Match volume details with snapshot details if any([self.module.params['vol_name'], @@ -500,6 +476,41 @@ def get_snapshot(self, snapshot_name=None, snapshot_id=None): LOG.error(errormsg) self.module.fail_json(msg=errormsg) + def add_retention_in_hours(self, snapshot_details): + if 'secureSnapshotExpTime' in snapshot_details[0] and\ + 'creationTime' in snapshot_details[0]: + if snapshot_details[0]['secureSnapshotExpTime'] != 0: + expiry_obj = datetime.fromtimestamp( + snapshot_details[0]['secureSnapshotExpTime']) + creation_obj = datetime.fromtimestamp( + snapshot_details[0]['creationTime']) + + td = utils.dateutil.relativedelta.relativedelta( + expiry_obj, creation_obj) + snapshot_details[0]['retentionInHours'] = td.hours + else: + snapshot_details[0]['retentionInHours'] = 0 + + def add_storage_pool_name(self, snapshot_details): + if 'storagePoolId' in snapshot_details[0] and \ + snapshot_details[0]['storagePoolId']: + sp = self.get_storage_pool(snapshot_details[0]['storagePoolId']) + if len(sp) > 0: + snapshot_details[0]['storagePoolName'] = sp[0]['name'] + + def add_size_in_kb(self, snapshot_details): + if 'sizeInKb' in snapshot_details[0] and \ + snapshot_details[0]['sizeInKb']: + snapshot_details[0]['sizeInGb'] = utils.get_size_in_gb( + snapshot_details[0]['sizeInKb'], 'KB') + + def add_ancestor(self, snapshot_details): + if 'ancestorVolumeId' in snapshot_details[0] and \ + snapshot_details[0]['ancestorVolumeId']: + vol = self.get_volume( + vol_id=snapshot_details[0]['ancestorVolumeId']) + snapshot_details[0]['ancestorVolumeName'] = vol['name'] + def match_vol_details(self, snapshot): """Match the given volume details with the response :param snapshot: The snapshot details @@ -764,13 +775,14 @@ def validate_desired_retention(self, desired_retention, retention_unit): :param retention_unit: Retention unit for snapshot """ - if retention_unit == 'hours' and (desired_retention < 1 or - desired_retention > 744): - self.module.fail_json(msg="Please provide a valid integer as the" + if desired_retention is not None: + if retention_unit == 'hours' and (desired_retention < 1 or + desired_retention > 744): + self.module.fail_json(msg="Please provide a valid integer as the" " desired retention between 1 and 744.") - elif retention_unit == 'days' and (desired_retention < 1 or - desired_retention > 31): - self.module.fail_json(msg="Please provide a valid integer as the" + elif retention_unit == 'days' and (desired_retention < 1 or + desired_retention > 31): + self.module.fail_json(msg="Please provide a valid integer as the" " desired retention between 1 and 31.") def unmap_snapshot_from_sdc(self, snapshot, sdc): @@ -822,40 +834,24 @@ def map_snapshot_to_sdc(self, snapshot, sdc): """ current_sdcs = snapshot['mappedSdcInfo'] - current_sdc_ids = [] sdc_id_list = [] sdc_map_list = [] sdc_modify_list1 = [] sdc_modify_list2 = [] - if current_sdcs: - for temp in current_sdcs: - current_sdc_ids.append(temp['sdcId']) + current_sdc_ids = self.populate_current_sdcs_ids(current_sdcs) for temp in sdc: - if 'sdc_name' in temp and temp['sdc_name']: - sdc_id = self.get_sdc_id(sdc_name=temp['sdc_name']) - elif 'sdc_ip' in temp and temp['sdc_ip']: - sdc_id = self.get_sdc_id(sdc_ip=temp['sdc_ip']) - else: - sdc_id = self.get_sdc_id(sdc_id=temp['sdc_id']) + sdc_id = self.get_sdc_id_from(temp) if sdc_id not in current_sdc_ids: sdc_id_list.append(sdc_id) - temp['sdc_id'] = sdc_id - if 'access_mode' in temp: - temp['access_mode'] = get_access_mode(temp['access_mode']) - if 'bandwidth_limit' not in temp: - temp['bandwidth_limit'] = None - if 'iops_limit' not in temp: - temp['iops_limit'] = None + self.update_sdc_details(temp, sdc_id) sdc_map_list.append(temp) else: access_mode_dict, limits_dict = check_for_sdc_modification( snapshot, sdc_id, temp) - if access_mode_dict: - sdc_modify_list1.append(access_mode_dict) - if limits_dict: - sdc_modify_list2.append(limits_dict) + self.update_sdc_modify_lists( + sdc_modify_list1, sdc_modify_list2, access_mode_dict, limits_dict) LOG.info("SDC to add: %s", sdc_map_list) @@ -892,6 +888,39 @@ def map_snapshot_to_sdc(self, snapshot, sdc): LOG.error(errormsg) self.module.fail_json(msg=errormsg) + def update_sdc_modify_lists(self, sdc_modify_list1, sdc_modify_list2, + access_mode_dict, limits_dict): + if access_mode_dict: + sdc_modify_list1.append(access_mode_dict) + if limits_dict: + sdc_modify_list2.append(limits_dict) + + def update_sdc_details(self, temp, sdc_id): + temp['sdc_id'] = sdc_id + if 'access_mode' in temp: + temp['access_mode'] = get_access_mode(temp['access_mode']) + if 'bandwidth_limit' not in temp: + temp['bandwidth_limit'] = None + if 'iops_limit' not in temp: + temp['iops_limit'] = None + + def get_sdc_id_from(self, temp): + sdc_id = None + if 'sdc_name' in temp and temp['sdc_name']: + sdc_id = self.get_sdc_id(sdc_name=temp['sdc_name']) + elif 'sdc_ip' in temp and temp['sdc_ip']: + sdc_id = self.get_sdc_id(sdc_ip=temp['sdc_ip']) + else: + sdc_id = self.get_sdc_id(sdc_id=temp['sdc_id']) + return sdc_id + + def populate_current_sdcs_ids(self, current_sdcs): + current_sdc_ids = [] + if current_sdcs: + for temp in current_sdcs: + current_sdc_ids.append(temp['sdcId']) + return current_sdc_ids + def validate_parameters(self): """Validate the input parameters""" @@ -954,148 +983,190 @@ def perform_module_operation(self): self.validate_parameters() - if size and not cap_unit: - cap_unit = 'GB' + cap_unit = self.get_cap_unit(size, cap_unit) - if desired_retention and not retention_unit: - retention_unit = 'hours' + retention_unit = self.get_retention_unit( + desired_retention, retention_unit) - if desired_retention is not None: - self.validate_desired_retention(desired_retention, retention_unit) + self.validate_desired_retention(desired_retention, retention_unit) snapshot_details = self.get_snapshot(snapshot_name, snapshot_id) if snapshot_details: snap_access_mode = None if read_only is not None: - if read_only: - snap_access_mode = 'ReadOnly' - else: - snap_access_mode = 'ReadWrite' + snap_access_mode = self.get_mode(read_only) is_modified, flag1, flag2, flag3 = check_snapshot_modified( snapshot_details, desired_retention, retention_unit, size, cap_unit, snap_access_mode) if state == 'present' and not snapshot_details: - if snapshot_id: - self.module.fail_json(msg="Creation of snapshot is allowed " - "using snapshot_name only, " - "snapshot_id given.") + self.validate_create(snapshot_name, snapshot_id, vol_name, + vol_id, snapshot_new_name, remove_mode) + changed = self.create_snapshot_with_detail(snapshot_name, vol_name, + read_only, size, + cap_unit, + desired_retention, + retention_unit) - if snapshot_name is None or len(snapshot_name.strip()) == 0: - self.module.fail_json(msg="Please provide valid snapshot " - "name.") + if is_modified: + changed = self.modify_val(size, cap_unit, desired_retention, + retention_unit, snapshot_details, + snap_access_mode, flag1, flag2, flag3) - if vol_name is None and vol_id is None: - self.module.fail_json(msg="Please provide volume details to " - "create new snapshot") + if state == 'present' and snapshot_details and sdc and sdc_state == 'mapped': + changed = self.sdc_state_mapped(sdc, snapshot_details) - if snapshot_new_name is not None: - self.module.fail_json(msg="snapshot_new_name is not required" - " while creating snapshot") + if state == 'present' and snapshot_details and sdc and \ + sdc_state == 'unmapped': + changed = self.unmap_snapshot_from_sdc(snapshot_details, sdc) - if remove_mode: - self.module.fail_json(msg="remove_mode is not required while " - "creating snapshot") + if state == 'present' and snapshot_details and \ + snapshot_new_name is not None: + self.validate_snap_shot_new_name(snapshot_new_name) + changed = self.rename_snapshot(snapshot_details['id'], + snapshot_new_name) + snapshot_name = self.assign_snapshot_name( + snapshot_new_name, changed) - if vol_name: - vol = self.get_volume(vol_name=vol_name) - vol_id = vol['id'] + if state == 'absent' and snapshot_details: + remove_mode = self.get_remove_mode(remove_mode) + changed = self.delete_snapshot(snapshot_details['id'], remove_mode) - retention = 0 - if desired_retention: - retention = calculate_retention(desired_retention, - retention_unit) + if state == 'present': + snapshot_details = self.get_snapshot(snapshot_name, snapshot_id) + result['snapshot_details'] = snapshot_details - system_id = self.get_system_id() - if read_only: - access_mode = 'ReadOnly' - else: - access_mode = 'ReadWrite' + result['changed'] = changed + self.module.exit_json(**result) - changed = self.create_snapshot(snapshot_name, vol_id, system_id, - access_mode, retention) - if changed: - snapshot_details = self.get_snapshot(snapshot_name) + def assign_snapshot_name(self, snapshot_new_name, changed): + if changed: + snapshot_name = snapshot_new_name + return snapshot_name - if size: - if cap_unit == 'GB': - new_size = size * 1024 * 1024 - else: - new_size = size * 1024 * 1024 * 1024 + def get_remove_mode(self, remove_mode): + if remove_mode is None: + remove_mode = "ONLY_ME" + return remove_mode - if new_size != snapshot_details['sizeInKb']: - if cap_unit == 'TB': - size = size * 1024 - changed = self.modify_size(snapshot_details['id'], size) + def get_retention_unit(self, desired_retention, retention_unit): + if desired_retention and not retention_unit: + retention_unit = 'hours' + return retention_unit - if is_modified: - if flag1: - retention = calculate_retention(desired_retention, - retention_unit) - changed = self.modify_retention(snapshot_details['id'], - retention) - - if flag2: - new_size = size - if cap_unit == 'TB': - new_size = size * 1024 - changed = self.modify_size(snapshot_details['id'], new_size) + def get_cap_unit(self, size, cap_unit): + if size and not cap_unit: + cap_unit = 'GB' + return cap_unit - if flag3: - changed = self.modify_snap_access_mode( - snapshot_details['id'], snap_access_mode) + def validate_snap_shot_new_name(self, snapshot_new_name): + if len(snapshot_new_name.strip()) == 0: + self.module.fail_json(msg="Please provide valid snapshot " + "name.") - if state == 'present' and snapshot_details and sdc and \ - sdc_state == 'mapped': + def sdc_state_mapped(self, sdc, snapshot_details): + changed_mode = False + changed_limits = False - changed_mode = False - changed_limits = False + _changed, access_mode_list, limits_list = \ + self.map_snapshot_to_sdc(snapshot_details, sdc) - changed, access_mode_list, limits_list = \ - self.map_snapshot_to_sdc(snapshot_details, sdc) + if len(access_mode_list) > 0: + changed_mode = self.modify_access_mode( + snapshot_details['id'], access_mode_list) - if len(access_mode_list) > 0: - changed_mode = self.modify_access_mode( - snapshot_details['id'], access_mode_list) + if len(limits_list) > 0: + for temp in limits_list: + payload = { + "volume_id": snapshot_details['id'], + "sdc_id": temp['sdc_id'], + "bandwidth_limit": temp['bandwidth_limit'], + "iops_limit": temp['iops_limit'] + } + changed_limits = self.modify_limits(payload) + return changed_mode or changed_limits + + def modify_val(self, size, cap_unit, desired_retention, retention_unit, + snapshot_details, snap_access_mode, flag1, flag2, flag3): + if flag1: + retention = calculate_retention(desired_retention, + retention_unit) + changed = self.modify_retention(snapshot_details['id'], + retention) + if flag2: + new_size = size + if cap_unit == 'TB': + new_size = size * 1024 + changed = self.modify_size(snapshot_details['id'], new_size) + + if flag3: + changed = self.modify_snap_access_mode( + snapshot_details['id'], snap_access_mode) + return changed + + def create_snapshot_with_detail(self, snapshot_name, vol_name, read_only, + size, cap_unit, desired_retention, + retention_unit): + if vol_name: + vol = self.get_volume(vol_name=vol_name) + vol_id = vol['id'] + + retention = 0 + if desired_retention: + retention = calculate_retention(desired_retention, + retention_unit) + + system_id = self.get_system_id() + access_mode = self.get_mode(read_only) + + changed = self.create_snapshot(snapshot_name, vol_id, system_id, + access_mode, retention) + if changed: + snapshot_details = self.get_snapshot(snapshot_name) + + if size: + if cap_unit == 'GB': + new_size = size * 1024 * 1024 + else: + new_size = size * 1024 * 1024 * 1024 - if len(limits_list) > 0: - for temp in limits_list: - payload = { - "volume_id": snapshot_details['id'], - "sdc_id": temp['sdc_id'], - "bandwidth_limit": temp['bandwidth_limit'], - "iops_limit": temp['iops_limit'] - } - changed_limits = self.modify_limits(payload) + if new_size != snapshot_details['sizeInKb']: + if cap_unit == 'TB': + size = size * 1024 + changed = self.modify_size(snapshot_details['id'], size) + return changed + + def get_mode(self, read_only): + ret_mode = None + if read_only: + ret_mode = 'ReadOnly' + else: + ret_mode = 'ReadWrite' + return ret_mode - if changed_mode or changed_limits: - changed = True + def validate_create(self, snapshot_name, snapshot_id, vol_name, vol_id, + snapshot_new_name, remove_mode): + if snapshot_id: + self.module.fail_json(msg="Creation of snapshot is allowed " + "using snapshot_name only, " + "snapshot_id given.") - if state == 'present' and snapshot_details and sdc and \ - sdc_state == 'unmapped': - changed = self.unmap_snapshot_from_sdc(snapshot_details, sdc) + if snapshot_name is None or len(snapshot_name.strip()) == 0: + self.module.fail_json(msg="Please provide valid snapshot " + "name.") - if state == 'present' and snapshot_details and \ - snapshot_new_name is not None: - if len(snapshot_new_name.strip()) == 0: - self.module.fail_json(msg="Please provide valid snapshot " - "name.") - changed = self.rename_snapshot(snapshot_details['id'], - snapshot_new_name) - if changed: - snapshot_name = snapshot_new_name + if vol_name is None and vol_id is None: + self.module.fail_json(msg="Please provide volume details to " + "create new snapshot") - if state == 'absent' and snapshot_details: - if remove_mode is None: - remove_mode = "ONLY_ME" - changed = self.delete_snapshot(snapshot_details['id'], remove_mode) + if snapshot_new_name is not None: + self.module.fail_json(msg="snapshot_new_name is not required" + " while creating snapshot") - if state == 'present': - snapshot_details = self.get_snapshot(snapshot_name, snapshot_id) - result['snapshot_details'] = snapshot_details - result['changed'] = changed - self.module.exit_json(**result) + if remove_mode: + self.module.fail_json(msg="remove_mode is not required while " + "creating snapshot") def check_snapshot_modified(snapshot=None, desired_retention=None, @@ -1111,27 +1182,18 @@ def check_snapshot_modified(snapshot=None, desired_retention=None, :return: Boolean indicating if modification is needed """ - snap_creation_timestamp = None expiration_timestamp = None is_timestamp_modified = False is_size_modified = False is_access_modified = False is_modified = False - if 'creationTime' in snapshot: - snap_creation_timestamp = snapshot['creationTime'] + snap_creation_timestamp = get_snap_creation_time(snapshot) if desired_retention: - if retention_unit == 'hours': - expiration_timestamp = \ - datetime.fromtimestamp(snap_creation_timestamp) + \ - timedelta(hours=desired_retention) - expiration_timestamp = time.mktime(expiration_timestamp.timetuple()) - else: - expiration_timestamp = \ - datetime.fromtimestamp(snap_creation_timestamp) + \ - timedelta(days=desired_retention) - expiration_timestamp = time.mktime(expiration_timestamp.timetuple()) + expiration_timestamp = get_expiration_timestamp(desired_retention, + retention_unit, + snap_creation_timestamp) if 'secureSnapshotExpTime' in snapshot and expiration_timestamp and \ snapshot['secureSnapshotExpTime'] != expiration_timestamp: @@ -1146,12 +1208,7 @@ def check_snapshot_modified(snapshot=None, desired_retention=None, existing_time_obj = datetime.fromtimestamp(existing_timestamp) new_time_obj = datetime.fromtimestamp(new_timestamp) - if existing_time_obj > new_time_obj: - td = utils.dateutil.relativedelta.relativedelta( - existing_time_obj, new_time_obj) - else: - td = utils.dateutil.relativedelta.relativedelta( - new_time_obj, existing_time_obj) + td = get_td(existing_time_obj, new_time_obj) LOG.info("Time difference: %s", td.minutes) @@ -1160,11 +1217,7 @@ def check_snapshot_modified(snapshot=None, desired_retention=None, is_timestamp_modified = True if size: - if cap_unit == 'GB': - new_size = size * 1024 * 1024 - else: - new_size = size * 1024 * 1024 * 1024 - + new_size = get_new_size(size, cap_unit) if new_size != snapshot['sizeInKb']: is_size_modified = True @@ -1176,6 +1229,47 @@ def check_snapshot_modified(snapshot=None, desired_retention=None, return is_modified, is_timestamp_modified, is_size_modified, is_access_modified +def get_td(existing_time_obj, new_time_obj): + if existing_time_obj > new_time_obj: + td = utils.dateutil.relativedelta.relativedelta( + existing_time_obj, new_time_obj) + else: + td = utils.dateutil.relativedelta.relativedelta( + new_time_obj, existing_time_obj) + + return td + + +def get_new_size(size, cap_unit): + if cap_unit == 'GB': + new_size = size * 1024 * 1024 + else: + new_size = size * 1024 * 1024 * 1024 + return new_size + + +def get_expiration_timestamp(desired_retention, retention_unit, + snap_creation_timestamp): + if retention_unit == 'hours': + expiration_timestamp = \ + datetime.fromtimestamp(snap_creation_timestamp) + \ + timedelta(hours=desired_retention) + expiration_timestamp = time.mktime(expiration_timestamp.timetuple()) + else: + expiration_timestamp = \ + datetime.fromtimestamp(snap_creation_timestamp) + \ + timedelta(days=desired_retention) + expiration_timestamp = time.mktime(expiration_timestamp.timetuple()) + return expiration_timestamp + + +def get_snap_creation_time(snapshot): + snap_creation_timestamp = None + if 'creationTime' in snapshot: + snap_creation_timestamp = snapshot['creationTime'] + return snap_creation_timestamp + + def calculate_retention(desired_retention=None, retention_unit=None): """ :param desired_retention: Desired retention of the snapshot @@ -1203,10 +1297,7 @@ def check_for_sdc_modification(snapshot, sdc_id, sdc_details): for sdc in snapshot['mappedSdcInfo']: if sdc['sdcId'] == sdc_id: - if sdc['accessMode'] != get_access_mode(sdc_details['access_mode']): - access_mode_dict['sdc_id'] = sdc_id - access_mode_dict['accessMode'] = get_access_mode( - sdc_details['access_mode']) + update_access_mode(sdc_id, sdc_details, access_mode_dict, sdc) if sdc['limitIops'] != sdc_details['iops_limit'] or \ sdc['limitBwInMbps'] != sdc_details['bandwidth_limit']: limits_dict['sdc_id'] = sdc_id @@ -1221,6 +1312,13 @@ def check_for_sdc_modification(snapshot, sdc_id, sdc_details): return access_mode_dict, limits_dict +def update_access_mode(sdc_id, sdc_details, access_mode_dict, sdc): + if sdc['accessMode'] != get_access_mode(sdc_details['access_mode']): + access_mode_dict['sdc_id'] = sdc_id + access_mode_dict['accessMode'] = get_access_mode( + sdc_details['access_mode']) + + def get_limits_in_mb(limits): """ :param limits: Limits in KB diff --git a/plugins/modules/volume.py b/plugins/modules/volume.py index 0fc3018..174b9b5 100644 --- a/plugins/modules/volume.py +++ b/plugins/modules/volume.py @@ -950,41 +950,24 @@ def map_volume_to_sdc(self, volume, sdc): """ current_sdcs = volume['mappedSdcInfo'] - current_sdc_ids = [] sdc_id_list = [] sdc_map_list = [] sdc_modify_list1 = [] sdc_modify_list2 = [] - if current_sdcs: - for temp in current_sdcs: - current_sdc_ids.append(temp['sdcId']) + current_sdc_ids = self.get_current_sdcs(current_sdcs) for temp in sdc: - if 'sdc_name' in temp and temp['sdc_name']: - sdc_id = self.get_sdc_id(sdc_name=temp['sdc_name']) - elif 'sdc_ip' in temp and temp['sdc_ip']: - sdc_id = self.get_sdc_id(sdc_ip=temp['sdc_ip']) - else: - sdc_id = self.get_sdc_id(sdc_id=temp['sdc_id']) + sdc_id = self.get_sdc_id_from_input(temp) if sdc_id not in current_sdc_ids: sdc_id_list.append(sdc_id) - temp['sdc_id'] = sdc_id - if 'access_mode' in temp: - temp['access_mode'] = \ - get_access_mode(temp['access_mode']) - if 'bandwidth_limit' not in temp: - temp['bandwidth_limit'] = None - if 'iops_limit' not in temp: - temp['iops_limit'] = None + self.update_temp_data(temp, sdc_id) sdc_map_list.append(temp) else: access_mode_dict, limits_dict = check_for_sdc_modification( volume, sdc_id, temp) - if access_mode_dict: - sdc_modify_list1.append(access_mode_dict) - if limits_dict: - sdc_modify_list2.append(limits_dict) + self.update_sdc_lists(sdc_modify_list1, sdc_modify_list2, + access_mode_dict, limits_dict) LOG.info("SDC to add: %s", sdc_map_list) @@ -1021,6 +1004,39 @@ def map_volume_to_sdc(self, volume, sdc): LOG.error(errormsg) self.module.fail_json(msg=errormsg) + def update_sdc_lists(self, sdc_modify_list1, sdc_modify_list2, + access_mode_dict, limits_dict): + if access_mode_dict: + sdc_modify_list1.append(access_mode_dict) + if limits_dict: + sdc_modify_list2.append(limits_dict) + + def get_sdc_id_from_input(self, temp): + if 'sdc_name' in temp and temp['sdc_name']: + sdc_id = self.get_sdc_id(sdc_name=temp['sdc_name']) + elif 'sdc_ip' in temp and temp['sdc_ip']: + sdc_id = self.get_sdc_id(sdc_ip=temp['sdc_ip']) + else: + sdc_id = self.get_sdc_id(sdc_id=temp['sdc_id']) + return sdc_id + + def update_temp_data(self, temp, sdc_id): + temp['sdc_id'] = sdc_id + if 'access_mode' in temp: + temp['access_mode'] = \ + get_access_mode(temp['access_mode']) + if 'bandwidth_limit' not in temp: + temp['bandwidth_limit'] = None + if 'iops_limit' not in temp: + temp['iops_limit'] = None + + def get_current_sdcs(self, current_sdcs): + current_sdc_ids = [] + if current_sdcs: + for temp in current_sdcs: + current_sdc_ids.append(temp['sdcId']) + return current_sdc_ids + def validate_parameters(self, auto_snap_remove_type, snap_pol_id, snap_pol_name, delete_snaps, state): """Validate the input parameters""" @@ -1145,32 +1161,17 @@ def to_modify(self, vol_details, new_size, use_rmcache, comp_type, pool_id = vol_details['storagePoolId'] pool_details = self.get_storage_pool(storage_pool_id=pool_id) pool_data_layout = pool_details['dataLayout'] - if pool_data_layout != "FineGranularity": - err_msg = "compression_type for volume can only be " \ - "mentioned when storage pools have Fine " \ - "Granularity layout. Storage Pool found" \ - " with {0}".format(pool_data_layout) - self.module.fail_json(msg=err_msg) - + self.pool_data_layout_fine(pool_data_layout) if comp_type != vol_details['compressionMethod']: modify_dict['comp_type'] = comp_type - if use_rmcache is not None and \ - vol_details['useRmcache'] != use_rmcache: - modify_dict['use_rmcache'] = use_rmcache + self.update_use_rmcache(vol_details, use_rmcache, modify_dict) vol_size_in_gb = utils.get_size_in_gb(vol_details['sizeInKb'], 'KB') - if new_size is not None and \ - not ((vol_size_in_gb - 8) < new_size <= vol_size_in_gb): - modify_dict['new_size'] = new_size + self.update_new_size(new_size, modify_dict, vol_size_in_gb) - if new_name is not None: - if new_name is None or len(new_name.strip()) == 0: - self.module.fail_json(msg="Please provide valid volume " - "name.") - if new_name != vol_details['name']: - modify_dict['new_name'] = new_name + self.update_new_name(vol_details, new_name, modify_dict) if snap_pol_id is not None and snap_pol_id == "" and \ auto_snap_remove_type and vol_details['snplIdOfSourceVolume']: @@ -1189,6 +1190,32 @@ def to_modify(self, vol_details, new_size, use_rmcache, comp_type, return modify_dict + def update_new_name(self, vol_details, new_name, modify_dict): + if new_name is not None: + if new_name is None or len(new_name.strip()) == 0: + self.module.fail_json(msg="Please provide valid volume " + "name.") + if new_name != vol_details['name']: + modify_dict['new_name'] = new_name + + def update_new_size(self, new_size, modify_dict, vol_size_in_gb): + if new_size is not None and \ + not ((vol_size_in_gb - 8) < new_size <= vol_size_in_gb): + modify_dict['new_size'] = new_size + + def update_use_rmcache(self, vol_details, use_rmcache, modify_dict): + if use_rmcache is not None and \ + vol_details['useRmcache'] != use_rmcache: + modify_dict['use_rmcache'] = use_rmcache + + def pool_data_layout_fine(self, pool_data_layout): + if pool_data_layout != "FineGranularity": + err_msg = "compression_type for volume can only be " \ + "mentioned when storage pools have Fine " \ + "Granularity layout. Storage Pool found" \ + " with {0}".format(pool_data_layout) + self.module.fail_json(msg=err_msg) + def verify_params(self, vol_details, snap_pol_name, snap_pol_id, pd_name, pd_id, pool_name, pool_id): """ @@ -1265,12 +1292,10 @@ def perform_module_operation(self): delete_snapshots = self.module.params['delete_snapshots'] state = self.module.params['state'] - if compression_type: - compression_type = compression_type.capitalize() - if vol_type: - vol_type = get_vol_type(vol_type) - if auto_snap_remove_type: - auto_snap_remove_type = auto_snap_remove_type.capitalize() + compression_type = self.check_null_capitalize(compression_type) + vol_type = get_vol_type(vol_type) + auto_snap_remove_type = self.check_null_capitalize( + auto_snap_remove_type) # result is a dictionary to contain end state and volume details changed = False @@ -1281,53 +1306,20 @@ def perform_module_operation(self): self.validate_parameters(auto_snap_remove_type, snap_pol_id, snap_pol_name, delete_snapshots, state) - if not auto_snap_remove_type and\ - (snap_pol_name == "" or snap_pol_id == ""): - auto_snap_remove_type = "Detach" - if size: - if not cap_unit: - cap_unit = 'GB' + auto_snap_remove_type = self.get_auto_snap_rt(snap_pol_name, + snap_pol_id, + auto_snap_remove_type) - if cap_unit == 'TB': - size = size * 1024 + size = self.get_size_data(size, cap_unit) - if pd_name: - pd_details = self.get_protection_domain(pd_name) - if pd_details: - pd_id = pd_details['id'] - msg = "Fetched the protection domain details with id {0}," \ - " name {1}".format(pd_id, pd_name) - LOG.info(msg) - - if sp_name: - sp_details = self.get_storage_pool(storage_pool_name=sp_name, - protection_domain_id=pd_id) - if sp_details: - sp_id = sp_details['id'] - msg = "Fetched the storage pool details id {0}," \ - " name {1}".format(sp_id, sp_name) - LOG.info(msg) + pd_id = self.get_pd_details(pd_name, pd_id) - if snap_pol_name is not None: - snap_pol_details = None - if snap_pol_name: - snap_pol_details = \ - self.get_snapshot_policy(snap_pol_name=snap_pol_name) - if snap_pol_details: - snap_pol_id = snap_pol_details['id'] + sp_id = self.get_sp_details(sp_name, pd_id, sp_id) - if snap_pol_name == "": - snap_pol_id = "" - msg = "Fetched the snapshot policy details with id {0}," \ - " name {1}".format(snap_pol_id, snap_pol_name) - LOG.info(msg) + snap_pol_id = self.get_snap_pol_details(snap_pol_name, snap_pol_id) # get volume details - volume_details = self.get_volume(vol_name, vol_id) - if volume_details: - vol_id = volume_details['id'] - msg = "Fetched the volume details {0}".format(str(volume_details)) - LOG.info(msg) + volume_details, vol_id = self.get_vol(vol_name, vol_id) if vol_name and volume_details: self.verify_params( @@ -1337,25 +1329,9 @@ def perform_module_operation(self): # create operation create_changed = False if state == 'present' and not volume_details: - if vol_id: - self.module.fail_json(msg="Creation of volume is allowed " - "using vol_name only, " - "vol_id given.") - - if vol_new_name: - self.module.fail_json( - msg="vol_new_name parameter is not supported during " - "creation of a volume. Try renaming the volume after" - " the creation.") - create_changed = self.create_volume(vol_name, sp_id, size, - vol_type, use_rmcache, - compression_type) - if create_changed: - volume_details = self.get_volume(vol_name) - vol_id = volume_details['id'] - msg = "Volume created successfully, fetched " \ - "volume details {0}".format(str(volume_details)) - LOG.info(msg) + vol_id, volume_details, create_changed = self.create_new_volume( + vol_name, vol_id, vol_type, compression_type, sp_id, + use_rmcache, size, vol_new_name, volume_details) # checking if basic volume parameters are modified or not. modify_dict = {} @@ -1368,25 +1344,13 @@ def perform_module_operation(self): LOG.info(msg) # Mapping the SDCs to a volume + map_changed = False mode_changed = False limits_changed = False - map_changed = False if state == 'present' and volume_details and sdc and \ sdc_state == 'mapped': - map_changed, access_mode_list, limits_list = \ - self.map_volume_to_sdc(volume_details, sdc) - if len(access_mode_list) > 0: - mode_changed = self.modify_access_mode(vol_id, - access_mode_list) - if len(limits_list) > 0: - for temp in limits_list: - payload = { - "volume_id": volume_details['id'], - "sdc_id": temp['sdc_id'], - "bandwidth_limit": temp['bandwidth_limit'], - "iops_limit": temp['iops_limit'] - } - limits_changed = self.modify_limits(payload) + mode_changed, limits_changed, map_changed = self.sdc_state_mapped( + vol_id, sdc, volume_details) # Unmap the SDCs to a volume unmap_changed = False @@ -1402,23 +1366,140 @@ def perform_module_operation(self): # delete operation del_changed = False if state == 'absent' and volume_details: - if delete_snapshots is True: - delete_snapshots = 'INCLUDING_DESCENDANTS' - if delete_snapshots is None or delete_snapshots is False: - delete_snapshots = 'ONLY_ME' - del_changed = \ - self.delete_volume(vol_id, delete_snapshots) + del_changed = self.delete_operation(vol_id, delete_snapshots) - if modify_changed or unmap_changed or map_changed or create_changed\ - or del_changed or mode_changed or limits_changed: - changed = True + changed = (modify_changed or unmap_changed or map_changed + or create_changed or del_changed or mode_changed + or limits_changed) # Returning the updated volume details + self.prepare_output(vol_id, state, result) + + result['changed'] = changed + self.module.exit_json(**result) + + def prepare_output(self, vol_id, state, result): if state == 'present': vol_details = self.show_output(vol_id) result['volume_details'] = vol_details - result['changed'] = changed - self.module.exit_json(**result) + + def get_vol(self, vol_name, vol_id): + volume_details = self.get_volume(vol_name, vol_id) + if volume_details: + vol_id = volume_details['id'] + msg = "Fetched the volume details {0}".format(str(volume_details)) + LOG.info(msg) + return volume_details, vol_id + + def get_auto_snap_rt(self, snap_pol_name, snap_pol_id, auto_snap_remove_type): + if not auto_snap_remove_type and\ + (snap_pol_name == "" or snap_pol_id == ""): + auto_snap_remove_type = "Detach" + return auto_snap_remove_type + + def get_size_data(self, size, cap_unit): + if size: + if not cap_unit: + cap_unit = 'GB' + + if cap_unit == 'TB': + size = size * 1024 + return size + + def get_pd_details(self, pd_name, pd_id): + if pd_name: + pd_details = self.get_protection_domain(pd_name) + if pd_details: + pd_id = pd_details['id'] + msg = "Fetched the protection domain details with id {0}," \ + " name {1}".format(pd_id, pd_name) + LOG.info(msg) + return pd_id + + def get_sp_details(self, sp_name, pd_id, sp_id): + if sp_name: + sp_details = self.get_storage_pool(storage_pool_name=sp_name, + protection_domain_id=pd_id) + if sp_details: + sp_id = sp_details['id'] + msg = "Fetched the storage pool details id {0}," \ + " name {1}".format(sp_id, sp_name) + LOG.info(msg) + return sp_id + + def get_snap_pol_details(self, snap_pol_name, snap_pol_id): + if snap_pol_name is not None: + snap_pol_details = None + if snap_pol_name: + snap_pol_details = \ + self.get_snapshot_policy(snap_pol_name=snap_pol_name) + if snap_pol_details: + snap_pol_id = snap_pol_details['id'] + + if snap_pol_name == "": + snap_pol_id = "" + msg = "Fetched the snapshot policy details with id {0}," \ + " name {1}".format(snap_pol_id, snap_pol_name) + LOG.info(msg) + return snap_pol_id + + def create_new_volume(self, vol_name, vol_id, vol_type, compression_type, + sp_id, use_rmcache, size, vol_new_name, + volume_details): + if vol_id: + self.module.fail_json(msg="Creation of volume is allowed " + "using vol_name only, " + "vol_id given.") + + if vol_new_name: + self.module.fail_json( + msg="vol_new_name parameter is not supported during " + "creation of a volume. Try renaming the volume after" + " the creation.") + create_changed = self.create_volume(vol_name, sp_id, size, + vol_type, use_rmcache, + compression_type) + if create_changed: + volume_details = self.get_volume(vol_name) + vol_id = volume_details['id'] + msg = "Volume created successfully, fetched " \ + "volume details {0}".format(str(volume_details)) + LOG.info(msg) + return vol_id, volume_details, create_changed + + def sdc_state_mapped(self, vol_id, sdc, volume_details): + mode_changed = False + limits_changed = False + map_changed = False + map_changed, access_mode_list, limits_list = \ + self.map_volume_to_sdc(volume_details, sdc) + if len(access_mode_list) > 0: + mode_changed = self.modify_access_mode(vol_id, + access_mode_list) + if len(limits_list) > 0: + for temp in limits_list: + payload = { + "volume_id": volume_details['id'], + "sdc_id": temp['sdc_id'], + "bandwidth_limit": temp['bandwidth_limit'], + "iops_limit": temp['iops_limit'] + } + limits_changed = self.modify_limits(payload) + return mode_changed, limits_changed, map_changed + + def delete_operation(self, vol_id, delete_snapshots): + if delete_snapshots is True: + delete_snapshots = 'INCLUDING_DESCENDANTS' + if delete_snapshots is None or delete_snapshots is False: + delete_snapshots = 'ONLY_ME' + del_changed = \ + self.delete_volume(vol_id, delete_snapshots) + return del_changed + + def check_null_capitalize(self, input_str): + if input_str: + input_str = input_str.capitalize() + return input_str def show_output(self, vol_id): """Show volume details @@ -1476,7 +1557,8 @@ def show_output(self, vol_id): volume_details[0]['snapshotsList'] = list_of_snaps # Append statistics - statistics = self.powerflex_conn.volume.get_statistics(volume_details[0]['id']) + statistics = self.powerflex_conn.volume.get_statistics( + volume_details[0]['id']) volume_details[0]['statistics'] = statistics if statistics else {} return volume_details[0] @@ -1500,11 +1582,7 @@ def check_for_sdc_modification(volume, sdc_id, sdc_details): for sdc in volume['mappedSdcInfo']: if sdc['sdcId'] == sdc_id: - if sdc['accessMode'] != \ - get_access_mode(sdc_details['access_mode']): - access_mode_dict['sdc_id'] = sdc_id - access_mode_dict['accessMode'] = get_access_mode( - sdc_details['access_mode']) + update_access_mode(sdc_id, sdc_details, access_mode_dict, sdc) if sdc['limitIops'] != sdc_details['iops_limit'] or \ sdc['limitBwInMbps'] != sdc_details['bandwidth_limit']: limits_dict['sdc_id'] = sdc_id @@ -1520,6 +1598,14 @@ def check_for_sdc_modification(volume, sdc_id, sdc_details): return access_mode_dict, limits_dict +def update_access_mode(sdc_id, sdc_details, access_mode_dict, sdc): + if sdc['accessMode'] != \ + get_access_mode(sdc_details['access_mode']): + access_mode_dict['sdc_id'] = sdc_id + access_mode_dict['accessMode'] = get_access_mode( + sdc_details['access_mode']) + + def get_limits_in_mb(limits): """ :param limits: Limits in KB @@ -1553,7 +1639,9 @@ def get_vol_type(vol_type): "THICK_PROVISIONED": "ThickProvisioned", "THIN_PROVISIONED": "ThinProvisioned", } - return vol_type_dict.get(vol_type) + if vol_type: + vol_type = vol_type_dict.get(vol_type) + return vol_type def get_powerflex_volume_parameters(): diff --git a/tests/unit/plugins/modules/test_info.py b/tests/unit/plugins/modules/test_info.py index 043fcd5..e759e76 100644 --- a/tests/unit/plugins/modules/test_info.py +++ b/tests/unit/plugins/modules/test_info.py @@ -358,7 +358,6 @@ def test_get_sds_details_exception(self, info_module_mock): "gather_subset": ['sds'] }) info_module_mock.module.params = self.get_module_args - sds_resp = MockInfoApi.INFO_SDS_GET_LIST info_module_mock.powerflex_conn.sds.get = MagicMock( side_effect=MockApiException ) @@ -399,7 +398,6 @@ def test_get_pd_details_exception(self, info_module_mock): "gather_subset": ['protection_domain'] }) info_module_mock.module.params = self.get_module_args - pd_resp = MockInfoApi.INFO_GET_PD_LIST info_module_mock.powerflex_conn.protection_domain.get = MagicMock( side_effect=MockApiException ) @@ -440,7 +438,6 @@ def test_get_device_details_exception(self, info_module_mock): "gather_subset": ['device'] }) info_module_mock.module.params = self.get_module_args - device_resp = MockInfoApi.INFO_GET_DEVICE_LIST info_module_mock.powerflex_conn.device.get = MagicMock( side_effect=MockApiException ) @@ -467,7 +464,6 @@ def test_get_fault_set_details_exception(self, info_module_mock): "gather_subset": ['fault_set'] }) info_module_mock.module.params = self.get_module_args - fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST info_module_mock.powerflex_conn.fault_set.get = MagicMock( side_effect=MockApiException ) @@ -484,7 +480,6 @@ def test_get_fault_set_details_invalid_filter_operator_exception(self, info_modu }] }) info_module_mock.module.params = self.get_module_args - fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST self.capture_fail_json_call(MockInfoApi.get_exception_response( 'invalid_filter_operator_exception'), info_module_mock) @@ -493,7 +488,6 @@ def test_get_fault_set_details_api_exception(self, info_module_mock): "gather_subset": ['fault_set'] }) info_module_mock.module.params = self.get_module_args - fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST info_module_mock.powerflex_conn.system.api_version = MagicMock( side_effect=MockApiException ) @@ -505,7 +499,6 @@ def test_get_fault_set_details_system_exception(self, info_module_mock): "gather_subset": ['fault_set'] }) info_module_mock.module.params = self.get_module_args - fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST info_module_mock.powerflex_conn.system.get = MagicMock( side_effect=MockApiException )