diff --git a/changelogs/fragments/691_vol_fleet.yaml b/changelogs/fragments/691_vol_fleet.yaml new file mode 100644 index 00000000..5fc849e8 --- /dev/null +++ b/changelogs/fragments/691_vol_fleet.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefa_volume - Added ``context`` parameter to support fleet operations diff --git a/plugins/modules/purefa_volume.py b/plugins/modules/purefa_volume.py index 6cd64e7b..7bcac4f3 100644 --- a/plugins/modules/purefa_volume.py +++ b/plugins/modules/purefa_volume.py @@ -157,6 +157,13 @@ type: str choices: [ promoted, demoted ] version_added: '1.16.0' + context: + description: + - Name of fleet member on which to perform the volume operation. + - This requires the array receiving the request is a member of a fleet + and the context name to be a member of the same fleet. + type: str + version_added: '1.33.0' extends_documentation_fragment: - purestorage.flasharray.purestorage.fa """ @@ -315,7 +322,14 @@ HAS_PURESTORAGE = True try: - from pypureclient import flasharray + from pypureclient.flasharray import ( + Qos, + VolumePost, + VolumePatch, + PriorityAdjustment, + Reference, + FixedReference, + ) except ImportError: HAS_PURESTORAGE = False @@ -324,7 +338,6 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.purestorage.flasharray.plugins.module_utils.purefa import ( get_array, - get_system, purefa_argument_spec, ) from ansible_collections.purestorage.flasharray.plugins.module_utils.common import ( @@ -335,24 +348,23 @@ LooseVersion, ) -QOS_API_VERSION = "1.14" -VGROUPS_API_VERSION = "1.13" -POD_API_VERSION = "1.13" -AC_QOS_VERSION = "1.16" -OFFLOAD_API_VERSION = "1.16" -IOPS_API_VERSION = "1.17" -MULTI_VOLUME_VERSION = "2.2" -PROMOTE_API_VERSION = "1.19" PURE_OUI = "naa.624a9370" PRIORITY_API_VERSION = "2.11" DEFAULT_API_VERSION = "2.16" -VOLUME_PROMOTION_API_VERSION = "2.2" +CONTEXT_API_VERSION = "2.38" def _volfact(module, array, volume_name): api_version = array.get_rest_version() if not module.check_mode: - volume_data = list(array.get_volumes(names=[volume_name]).items)[0] + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + volume_data = list( + array.get_volumes( + names=[volume_name], context_names=[module.params["context"]] + ).items + )[0] + else: + volume_data = list(array.get_volumes(names=[volume_name]).items)[0] volfact = { volume_name: { "size": volume_data.provisioned, @@ -379,6 +391,8 @@ def _volfact(module, array, volume_name): volfact[volume_name][ "priority_value" ] = volume_data.priority_adjustment.priority_adjustment_value + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + volfact[volume_name]["context"] = volume_data.context.name else: volfact = {} return volfact @@ -391,63 +405,51 @@ def _create_nguid(serial): def get_pod(module, array): """Get ActiveCluster Pod""" + api_version = array.get_rest_version() pod_name = module.params["pgroup"].split("::")[0] - try: - return array.get_pod(pod=pod_name) - except Exception: - return None + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_pods(names=[pod_name], context_names=[module.params["context"]]) + else: + res = array.get_pods(names=[pod_name]) + if res.status_code == 200: + return list(res.items)[0] + return None def get_pending_pgroup(module, array): """Get Protection Group""" - pgroup = None - if ":" in module.params["pgroup"]: - if "::" not in module.params["pgroup"]: - for pgrp in array.list_pgroups(pending=True, on="*"): - if pgrp["name"] == module.params["pgroup"] and pgrp["time_remaining"]: - pgroup = pgrp - break - else: - for pgrp in array.list_pgroups(pending=True): - if pgrp["name"] == module.params["pgroup"] and pgrp["time_remaining"]: - pgroup = pgrp - break + api_version = array.get_rest_version() + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_protection_groups( + names=[module.params["pgroup"]], context_names=[module.params["context"]] + ) else: - for pgrp in array.list_pgroups(pending=True): - if pgrp["name"] == module.params["pgroup"] and pgrp["time_remaining"]: - pgroup = pgrp - break - - return pgroup + res = array.get_protection_groups(names=[module.params["pgroup"]]) + if res.status_code == 200: + pgroup = list(res.items)[0] + if pgroup.destroyed: + return list(res.items)[0] + return None def get_pgroup(module, array): """Get Protection Group""" - pgroup = None - if ":" in module.params["pgroup"]: - if "::" not in module.params["pgroup"]: - for pgrp in array.list_pgroups(on="*"): - if pgrp["name"] == module.params["pgroup"]: - pgroup = pgrp - break - else: - for pgrp in array.list_pgroups(): - if pgrp["name"] == module.params["pgroup"]: - pgroup = pgrp - break + api_version = array.get_rest_version() + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_protection_groups( + names=[module.params["pgroup"]], context_names=[module.params["context"]] + ) else: - for pgrp in array.list_pgroups(): - if pgrp["name"] == module.params["pgroup"]: - pgroup = pgrp - break - - return pgroup + res = array.get_protection_groups(names=[module.params["pgroup"]]) + if res.status_code == 200: + return list(res.items)[0] + return None -def get_multi_volumes(module, destroyed=False): +def get_multi_volumes(module, array): """Return True is all volumes exist or None""" names = [] - array = get_array(module) + api_version = array.get_rest_version() for vol_num in range( module.params["start"], module.params["count"] + module.params["start"] ): @@ -456,101 +458,117 @@ def get_multi_volumes(module, destroyed=False): + str(vol_num).zfill(module.params["digits"]) + module.params["suffix"] ) - return bool(array.get_volumes(names=names, destroyed=destroyed).status_code == 200) + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_volumes( + names=names, destroyed=False, context_names=[module.params["context"]] + ) + else: + res = array.get_volumes(names=names, destroyed=False) + if res.status_code == 200: + return list(res.items)[0] + return None def get_volume(module, array): """Return Volume or None""" - try: - return array.get_volume(module.params["name"]) - except Exception: - return None + api_version = array.get_rest_version() + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_volumes( + names=[module.params["name"]], context_names=[module.params["context"]] + ) + else: + res = array.get_volumes(names=[module.params["name"]]) + if res.status_code == 200: + return list(res.items)[0] + return None -def get_endpoint(name, array): +def get_endpoint(module, name, array): """Return Endpoint or None""" - try: - return array.get_volume(name, pending=True, protocol_endpoint=True) - except Exception: - return None + api_version = array.get_rest_version() + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_volumes(names=[name], context_names=[module.params["context"]]) + else: + res = array.get_volumes(names=[name]) + if res.status_code == 200: + volume = list(res.items)[0] + if volume.protocol_endpoint: + return list(res.items)[0] + return None -def get_destroyed_volume(vol, array): +def get_destroyed_volume(module, array): """Return Destroyed Volume or None""" - try: - return bool(array.get_volume(vol, pending=True)["time_remaining"] != "") - except Exception: - return False - - -def get_destroyed_endpoint(vol, array): - """Return Destroyed Endpoint or None""" - try: - return bool( - array.get_volume(vol, protocol_endpoint=True, pending=True)[ - "time_remaining" - ] - != "" + api_version = array.get_rest_version() + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_volumes( + names=[module.params["name"]], context_names=[module.params["context"]] ) - except Exception: - return False + else: + res = array.get_volumes(names=[module.params["name"]]) + if res.status_code == 200: + volume = list(res.items)[0] + if volume.destroyed: + return list(res.items)[0] + return None def get_target(module, array): """Return Volume or None""" - try: - return array.get_volume(module.params["target"]) - except Exception: - return None + api_version = array.get_rest_version() + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_volumes( + names=[module.params["target"]], context_names=[module.params["context"]] + ) + else: + res = array.get_volumes(names=[module.params["target"]]) + if res.status_code == 200: + return list(res.items)[0] + return None def check_vgroup(module, array): """Check is the requested VG to create volume in exists""" - vg_exists = False - api_version = array._list_available_rest_versions() - if VGROUPS_API_VERSION in api_version: - vg_name = module.params["name"].split("/")[0] - try: - vgs = array.list_vgroups() - except Exception: - module.fail_json(msg="Failed to get volume groups list. Check array.") - for vgroup in range(0, len(vgs)): - if vg_name == vgs[vgroup]["name"]: - vg_exists = True - break - else: - module.fail_json( - msg="VG volumes are not supported. Please upgrade your FlashArray." + api_version = array.get_rest_version() + vg_name = module.params["name"].split("/")[0] + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_volume_groups( + names=[vg_name], context_names=[module.params["context"]] ) - return vg_exists + else: + res = array.get_volume_groups(names=[vg_name]) + return bool(res.status_code == 200) def check_pod(module, array): """Check is the requested pod to create volume in exists""" pod_exists = False - api_version = array._list_available_rest_versions() - if POD_API_VERSION in api_version: - pod_name = module.params["name"].split("::")[0] - try: - pods = array.list_pods() - except Exception: - module.fail_json(msg="Failed to get pod list. Check array.") - for pod in range(0, len(pods)): - if pod_name == pods[pod]["name"]: - pod_exists = True - break + api_version = array.get_rest_version() + pod_name = module.params["name"].split("::")[0] + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.get_pods(names=[pod_name], context_names=[module.params["context"]]) else: - module.fail_json( - msg="Pod volumes are not supported. Please upgrade your FlashArray." - ) - return pod_exists + res = array.get_pods(names=[pod_name]) + return bool(res.status_code == 200) def create_volume(module, array): """Create Volume""" changed = False - api_version = array._list_available_rest_versions() - pg_check(module) + context = False + api_version = array.get_rest_version() + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + context = True + if ( + LooseVersion(api_version) >= LooseVersion(DEFAULT_API_VERSION) + and module.params["pgroup"] + ): + module.fail_json(msg="For Purity//FA 6.3.4 or higher, use add_to_pgs parameter") + elif ( + LooseVersion(api_version) <= LooseVersion(DEFAULT_API_VERSION) + and module.params["add_to_pgs"] + ): + module.fail_json(msg="For Purity//FA 6.3.4 or lower, use pgroup parameter") if "/" in module.params["name"] and not check_vgroup(module, array): module.fail_json( msg="Failed to create volume {0}. Volume Group does not exist.".format( @@ -565,174 +583,189 @@ def create_volume(module, array): ) ) pod_name = module.params["name"].split("::")[0] - if PROMOTE_API_VERSION in api_version: - if array.get_pod(pod_name)["promotion_status"] == "demoted": - module.fail_json(msg="Volume cannot be created in a demoted pod") - if module.params["bw_qos"] or module.params["iops_qos"]: - if AC_QOS_VERSION not in api_version: - module.warn( - "Pods cannot cannot contain volumes with QoS settings. Ignoring..." - ) - module.params["bw_qos"] = module.params["iops_qos"] = None + if ( + list(array.get_pods(names=[pod_name]).items)[0].promotion_status + == "demoted" + ): + module.fail_json(msg="Volume cannot be created in a demoted pod") if not module.params["size"]: module.fail_json(msg="Size for a new volume must be specified") if module.params["bw_qos"] or module.params["iops_qos"]: - if module.params["bw_qos"] and QOS_API_VERSION in api_version: - if module.params["iops_qos"] and IOPS_API_VERSION in api_version: - if module.params["bw_qos"] and not module.params["iops_qos"]: - if int(human_to_bytes(module.params["bw_qos"])) in range( - 1048576, 549755813888 - ): - changed = True - if not module.check_mode: - try: - array.create_volume( - module.params["name"], - module.params["size"], - bandwidth_limit=module.params["bw_qos"], - ) - except Exception: - module.fail_json( - msg="Volume {0} creation failed.".format( - module.params["name"] - ) - ) - else: - module.fail_json( - msg="Bandwidth QoS value {0} out of range.".format( - module.params["bw_qos"] - ) - ) - elif module.params["iops_qos"] and not module.params["bw_qos"]: - if ( - 100000000 - >= int(human_to_real(module.params["iops_qos"])) - >= 100 - ): - changed = True - if not module.check_mode: - try: - array.create_volume( - module.params["name"], - module.params["size"], - iops_limit=module.params["iops_qos"], - ) - except Exception: - module.fail_json( - msg="Volume {0} creation failed.".format( - module.params["name"] - ) - ) - else: - module.fail_json( - msg="IOPs QoS value {0} out of range.".format( - module.params["iops_qos"] - ) - ) + if module.params["bw_qos"] and not module.params["iops_qos"]: + changed = True + if not module.check_mode: + if context: + res = array.post_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePost( + provisioned=module.params["size"], + qos=Qos(bandwidth_limit=module.params["bw_qos"]), + ), + ) else: - bw_qos_size = int(human_to_bytes(module.params["bw_qos"])) - if int(human_to_real(module.params["iops_qos"])) in range( - 100, 100000000 - ) and bw_qos_size in range(1048576, 549755813888): - changed = True - if not module.check_mode: - try: - array.create_volume( - module.params["name"], - module.params["size"], - iops_limit=module.params["iops_qos"], - bandwidth_limit=module.params["bw_qos"], - ) - except Exception: - module.fail_json( - msg="Volume {0} creation failed.".format( - module.params["name"] - ) - ) - else: - module.fail_json( - msg="IOPs or Bandwidth QoS value out of range." + res = array.post_volumes( + names=[module.params["name"]], + volume=VolumePost( + provisioned=module.params["size"], + qos=Qos(bandwidth_limit=module.params["bw_qos"]), + ), + ) + if res.status_code != 200: + module.fail_json( + msg="Volume {0} creation failed. Error: {1}".format( + module.params["name"], + res.errors[0].message, ) - else: - if module.params["bw_qos"]: - if int(human_to_bytes(module.params["bw_qos"])) in range( - 1048576, 549755813888 - ): - changed = True - if not module.check_mode: - try: - array.create_volume( - module.params["name"], - module.params["size"], - bandwidth_limit=module.params["bw_qos"], - ) - except Exception: - module.fail_json( - msg="Volume {0} creation failed.".format( - module.params["name"] - ) - ) - else: - module.fail_json( - msg="Bandwidth QoS value {0} out of range.".format( - module.params["bw_qos"] - ) + ) + elif module.params["iops_qos"] and not module.params["bw_qos"]: + changed = True + if not module.check_mode: + if context: + res = array.post_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePost( + provisioned=module.params["size"], + qos=Qos(iops_limit=module.params["iops_qos"]), + ), + ) + else: + res = array.post_volumes( + names=[module.params["name"]], + volume=VolumePost( + provisioned=module.params["size"], + qos=Qos(iops_limit=module.params["iops_qos"]), + ), + ) + if res.status_code != 200: + module.fail_json( + msg="Volume {0} creation failed. Error: {1}".format( + module.params["name"], + res.errors[0].message, ) + ) + else: + changed = True + if not module.check_mode: + if context: + res = array.post_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePost( + provisioned=module.params["size"], + qos=Qos(iops_limit=module.params["iops_qos"]), + bandwidth_limit=module.params["bw_qos"], + ), + ) else: - changed = True - if not module.check_mode: - try: - array.create_volume( - module.params["name"], module.params["size"] - ) - except Exception: - module.fail_json( - msg="Volume {0} creation failed.".format( - module.params["name"] - ) - ) + res = array.post_volumes( + names=[module.params["name"]], + volume=VolumePost( + provisioned=module.params["size"], + qos=Qos(iops_limit=module.params["iops_qos"]), + bandwidth_limit=module.params["bw_qos"], + ), + ) + if res.status_code != 200: + module.fail_json( + msg="Volume {0} creation failed. Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) + ) else: changed = True if not module.check_mode: - try: - array.create_volume(module.params["name"], module.params["size"]) - except Exception: + if context: + res = array.post_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePost(provisioned=module.params["size"]), + ) + else: + res = array.post_volumes( + names=[module.params["name"]], + volume=VolumePost(provisioned=module.params["size"]), + ) + if res.status_code != 200: module.fail_json( - msg="Volume {0} creation failed.".format(module.params["name"]) + msg="Volume {0} creation failed. Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) ) - arrayv6 = get_array(module) - if VOLUME_PROMOTION_API_VERSION in api_version and module.params["promotion_state"]: - volume = flasharray.VolumePatch( - requested_promotion_state=module.params["promotion_state"] - ) + if module.params["promotion_state"]: + volume = VolumePatch(requested_promotion_state=module.params["promotion_state"]) changed = True if not module.check_mode: - res = arrayv6.patch_volumes(names=[module.params["name"]], volume=volume) - if res.status_code != 200: - arrayv6.patch_volumes( + if context: + res = array.patch_volumes( names=[module.params["name"]], - volume=flasharray.VolumePatch(destroyed=True), + volume=volume, + context_names=[module.params["context"]], ) - arrayv6.delete_volumes(names=[module.params["name"]]) + else: + res = array.patch_volumes(names=[module.params["name"]], volume=volume) + if res.status_code != 200: + if module.params["context"] and context: + array.patch_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePatch(destroyed=True), + ) + else: + array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(destroyed=True), + ) + if module.params["context"] and context: + array.delete_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + ) + else: + array.delete_volumes(names=[module.params["name"]]) module.fail_json( - msg="Failed to set Promotion State for volume {0}.".format( - module.params["name"] + msg="Failed to set Promotion State for volume {0}. Error: {1}".format( + module.params["name"], + res.errors[0].message, ) ) - if PRIORITY_API_VERSION in api_version and module.params["priority_operator"]: - volume = flasharray.VolumePatch( - priority_adjustment=flasharray.PriorityAdjustment( + if module.params["priority_operator"]: + volume = VolumePatch( + priority_adjustment=PriorityAdjustment( priority_adjustment_operator=module.params["priority_operator"], priority_adjustment_value=module.params["priority_value"], ) ) - res = arrayv6.patch_volumes(names=[module.params["name"]], volume=volume) - if res.status_code != 200: - arrayv6.patch_volumes( + if context: + res = array.patch_volumes( names=[module.params["name"]], - volume=flasharray.VolumePatch(destroyed=True), + volume=volume, + context_names=[module.params["context"]], ) - arrayv6.delete_volumes(names=[module.params["name"]]) + else: + res = array.patch_volumes(names=[module.params["name"]], volume=volume) + if res.status_code != 200: + if context: + array.patch_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePatch(destroyed=True), + ) + else: + array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(destroyed=True), + ) + if context: + array.delete_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + ) + else: + array.delete_volumes(names=[module.params["name"]]) module.fail_json( msg="Failed to set DMM Priority Adjustment on volume {0}. Error: {1}".format( module.params["name"], res.errors[0].message @@ -741,28 +774,51 @@ def create_volume(module, array): if module.params["pgroup"] and DEFAULT_API_VERSION not in api_version: changed = True if not module.check_mode: - try: - array.set_pgroup( - module.params["pgroup"], addvollist=[module.params["name"]] + if context: + res = array.patch_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + add_to_protection_groups=FixedReference( + name=module.params["pgroup"] + ), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + add_to_protection_groups=FixedReference( + name=module.params["pgroup"] + ), ) - except Exception: + if res.status_code != 200: module.warn_json( - "Failed to add {0} to protection group {1}.".format( - module.params["name"], module.params["pgroup"] + "Failed to add {0} to protection group {1}. Error: {2}".format( + module.params["name"], + module.params["pgroup"], + res.errors[0].message, ) ) module.exit_json( - changed=changed, volume=_volfact(module, arrayv6, module.params["name"]) + changed=changed, volume=_volfact(module, array, module.params["name"]) ) def create_multi_volume(module, array, single=False): """Create Volume""" volfact = {} + vols = VolumePost() changed = True - api_version = array._list_available_rest_versions() - pg_check(module) + api_version = array.get_rest_version() + if ( + LooseVersion(api_version) >= LooseVersion(DEFAULT_API_VERSION) + and module.params["pgroup"] + ): + module.fail_json(msg="For Purity//FA 6.3.4 or higher, use add_to_pgs parameter") + elif ( + LooseVersion(api_version) <= LooseVersion(DEFAULT_API_VERSION) + and module.params["add_to_pgs"] + ): + module.fail_json(msg="For Purity//FA 6.3.4 or lower, use pgroup parameter") bw_qos_size = iops_qos_size = 0 names = [] if "/" in module.params["name"] and not check_vgroup(module, array): @@ -779,9 +835,8 @@ def create_multi_volume(module, array, single=False): ) ) pod_name = module.params["name"].split("::")[0] - if PROMOTE_API_VERSION in api_version: - if array.get_pod(pod_name)["promotion_status"] == "demoted": - module.fail_json(msg="Volume cannot be created in a demoted pod") + if array.get_pod(pod_name)["promotion_status"] == "demoted": + module.fail_json(msg="Volume cannot be created in a demoted pod") array = get_array(module) if not single: for vol_num in range( @@ -794,38 +849,26 @@ def create_multi_volume(module, array, single=False): ) else: names.append(module.params["name"]) - if module.params["bw_qos"]: - bw_qos = int(human_to_bytes(module.params["bw_qos"])) - if bw_qos in range(1048576, 549755813888): - bw_qos_size = bw_qos - else: - module.fail_json(msg="Bandwidth QoS value out of range.") - if module.params["iops_qos"]: - iops_qos = int(human_to_real(module.params["iops_qos"])) - if iops_qos in range(100, 100000000): - iops_qos_size = iops_qos - else: - module.fail_json(msg="IOPs QoS value out of range.") if bw_qos_size != 0 and iops_qos_size != 0: - vols = flasharray.VolumePost( + vols = VolumePost( provisioned=human_to_bytes(module.params["size"]), - qos=flasharray.Qos(bandwidth_limit=bw_qos_size, iops_limit=iops_qos_size), + qos=Qos(bandwidth_limit=bw_qos_size, iops_limit=iops_qos_size), subtype="regular", ) elif bw_qos_size == 0 and iops_qos_size == 0: - vols = flasharray.VolumePost( + vols = VolumePost( provisioned=human_to_bytes(module.params["size"]), subtype="regular" ) elif bw_qos_size == 0 and iops_qos_size != 0: - vols = flasharray.VolumePost( + vols = VolumePost( provisioned=human_to_bytes(module.params["size"]), - qos=flasharray.Qos(iops_limit=iops_qos_size), + qos=Qos(iops_limit=iops_qos_size), subtype="regular", ) elif bw_qos_size != 0 and iops_qos_size == 0: - vols = flasharray.VolumePost( + vols = VolumePost( provisioned=human_to_bytes(module.params["size"]), - qos=flasharray.Qos(bandwidth_limit=bw_qos_size), + qos=Qos(bandwidth_limit=bw_qos_size), subtype="regular", ) if not module.check_mode: @@ -834,69 +877,116 @@ def create_multi_volume(module, array, single=False): add_to_pgs = [] for add_pg in range(0, len(module.params["add_to_pgs"])): add_to_pgs.append( - flasharray.FixedReference( - name=module.params["add_to_pgs"][add_pg] - ) + FixedReference(name=module.params["add_to_pgs"][add_pg]) + ) + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.post_volumes( + names=names, + volume=vols, + context_names=[module.params["context"]], + with_default_protection=module.params[ + "with_default_protection" + ], + add_to_protection_groups=add_to_pgs, + ) + else: + res = array.post_volumes( + names=names, + volume=vols, + with_default_protection=module.params[ + "with_default_protection" + ], + add_to_protection_groups=add_to_pgs, ) - res = array.post_volumes( - names=names, - volume=vols, - with_default_protection=module.params["with_default_protection"], - add_to_protection_groups=add_to_pgs, - ) else: - res = array.post_volumes( - names=names, - volume=vols, - with_default_protection=module.params["with_default_protection"], - ) + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.post_volumes( + names=names, + context_names=[module.params["context"]], + volume=vols, + with_default_protection=module.params[ + "with_default_protection" + ], + ) + else: + res = array.post_volumes( + names=names, + volume=vols, + with_default_protection=module.params[ + "with_default_protection" + ], + ) else: res = array.post_volumes(names=names, volume=vols) if res.status_code != 200: module.fail_json( - msg="Multi-Volume {0}#{1} creation failed: {2}".format( + msg="Multi-Volume {0}#{1} creation failed. Error: {2}".format( module.params["name"], module.params["suffix"], res.errors[0].message, ) ) else: - if ( - VOLUME_PROMOTION_API_VERSION in api_version - and module.params["promotion_state"] - ): - volume = flasharray.VolumePatch( + if module.params["promotion_state"]: + volume = VolumePatch( requested_promotion_state=module.params["promotion_state"] ) - prom_res = array.patch_volumes(names=names, volume=volume) - if prom_res.status_code != 200: - array.patch_volumes( + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + prom_res = array.patch_volumes( names=names, - volume=flasharray.VolumePatch(destroyed=True), + volume=volume, + context_names=[module.params["context"]], ) - array.delete_volumes(names=names) + else: + prom_res = array.patch_volumes(names=names, volume=volume) + if prom_res.status_code != 200: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + array.patch_volumes( + names=names, + context_names=[module.params["context"]], + volume=VolumePatch(destroyed=True), + ) + array.delete_volumes(names=names) + else: + array.patch_volumes( + names=names, + volume=VolumePatch(destroyed=True), + ) + array.delete_volumes(names=names) module.warn( "Failed to set promotion status on volumes. Error: {0}".format( prom_res.errors[0].message ) ) - if ( - PRIORITY_API_VERSION in api_version - and module.params["priority_operator"] - ): - volume = flasharray.VolumePatch( - priority_adjustment=flasharray.PriorityAdjustment( + if module.params["priority_operator"]: + volume = VolumePatch( + priority_adjustment=PriorityAdjustment( priority_adjustment_operator=module.params["priority_operator"], priority_adjustment_value=module.params["priority_value"], ) ) - prio_res = array.patch_volumes(names=names, volume=volume) - if prio_res.status_code != 200: - array.patch_volumes( + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + prio_res = array.patch_volumes( names=names, - volume=flasharray.VolumePatch(destroyed=True), + volume=volume, + context_names=[module.params["context"]], ) - array.delete_volumes(names=names) + else: + prio_res = array.patch_volumes(names=names, volume=volume) + if prio_res.status_code != 200: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + array.patch_volumes( + names=names, + context_names=[module.params["context"]], + volume=VolumePatch(destroyed=True), + ) + array.delete_volumes(names=names) + else: + array.patch_volumes( + names=names, + volume=VolumePatch(destroyed=True), + ) + array.delete_volumes(names=names) module.fail_json( msg="Failed to set DMM Priority Adjustment on volumes. Error: {0}".format( prio_res.errors[0].message @@ -921,17 +1011,11 @@ def create_multi_volume(module, array, single=False): ].qos.bandwidth_limit if iops_qos_size != 0: volfact[vol_name]["iops_limit"] = temp[count].qos.iops_limit - if ( - VOLUME_PROMOTION_API_VERSION in api_version - and module.params["promotion_state"] - ): + if module.params["promotion_state"]: volfact[vol_name]["promotion_status"] = prio_temp[ count ].promotion_status - if ( - PRIORITY_API_VERSION in api_version - and module.params["priority_operator"] - ): + if module.params["priority_operator"]: volfact[vol_name]["priority_operator"] = prio_temp[ count ].priority_adjustment.priority_adjustment_operator @@ -941,9 +1025,16 @@ def create_multi_volume(module, array, single=False): if module.params["pgroup"] and DEFAULT_API_VERSION not in api_version: if not module.check_mode: - res = array.post_protection_groups_volumes( - group_names=[module.params["pgroup"]], member_names=names - ) + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.post_protection_groups_volumes( + group_names=[module.params["pgroup"]], member_names=names + ) + else: + res = array.post_protection_groups_volumes( + context_names=[module.params["context"]], + group_names=[module.params["pgroup"]], + member_names=names, + ) if res.status_code != 200: module.warn( "Failed to add {0} to protection group {1}.".format( @@ -958,8 +1049,7 @@ def copy_from_volume(module, array): """Create Volume Clone""" changed = False tgt = get_target(module, array) - api_version = array._list_available_rest_versions() - arrayv6 = get_array(module) + api_version = array.get_rest_version() if tgt is None: changed = True if not module.check_mode: @@ -968,29 +1058,38 @@ def copy_from_volume(module, array): add_to_pgs = [] for add_pg in range(0, len(module.params["add_to_pgs"])): add_to_pgs.append( - flasharray.FixedReference( - name=module.params["add_to_pgs"][add_pg] - ) + FixedReference(name=module.params["add_to_pgs"][add_pg]) + ) + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.post_volumes( + with_default_protection=module.params[ + "with_default_protection" + ], + add_to_protection_groups=add_to_pgs, + context_names=[module.params["context"]], + names=[module.params["target"]], + volume=VolumePost( + source=Reference(name=module.params["name"]) + ), + ) + else: + res = array.post_volumes( + with_default_protection=module.params[ + "with_default_protection" + ], + add_to_protection_groups=add_to_pgs, + names=[module.params["target"]], + volume=VolumePost( + source=Reference(name=module.params["name"]) + ), ) - res = arrayv6.post_volumes( - with_default_protection=module.params[ - "with_default_protection" - ], - add_to_protection_groups=add_to_pgs, - names=[module.params["target"]], - volume=flasharray.VolumePost( - source=flasharray.Reference(name=module.params["name"]) - ), - ) else: - res = arrayv6.post_volumes( + res = array.post_volumes( with_default_protection=module.params[ "with_default_protection" ], names=[module.params["target"]], - volume=flasharray.VolumePost( - source=flasharray.Reference(name=module.params["name"]) - ), + volume=VolumePost(source=Reference(name=module.params["name"])), ) if res.status_code != 200: @@ -1002,39 +1101,52 @@ def copy_from_volume(module, array): ) ) else: - try: - array.copy_volume(module.params["name"], module.params["target"]) - changed = True - except Exception: + res = array.post_volumes( + names=[module.params["name"]], + volume=VolumePost(source=Reference(name=module.params["target"])), + ) + if res.status_code != 200: module.fail_json( - msg="Copy volume {0} to volume {1} failed.".format( - module.params["name"], module.params["target"] + msg="Copy volume {0} to volume {1} failed. Error: {2}".format( + module.params["name"], + module.params["target"], + res.errors[0].message, ) ) elif tgt is not None and module.params["overwrite"]: changed = True if not module.check_mode: if DEFAULT_API_VERSION not in api_version: - try: - array.copy_volume( - module.params["name"], - module.params["target"], + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.post_volumes( + names=[module.params["name"]], + volume=VolumePost( + source=Reference(name=module.params["target"]) + ), + context_names=[module.params["context"]], + overwrite=module.params["overwrite"], + ) + else: + res = array.post_volumes( + names=[module.params["name"]], + volume=VolumePost( + source=Reference(name=module.params["target"]) + ), overwrite=module.params["overwrite"], ) - changed = True - except Exception: + if res.status_code != 200: module.fail_json( - msg="Copy volume {0} to volume {1} failed.".format( - module.params["name"], module.params["target"] + msg="Copy volume {0} to volume {1} failed. Error: {2}".format( + module.params["name"], + module.params["target"], + res.errors[0].message, ) ) else: - res = arrayv6.post_volumes( + res = array.post_volumes( overwrite=module.params["overwrite"], names=[module.params["target"]], - volume=flasharray.VolumePost( - source=flasharray.Reference(name=module.params["name"]) - ), + volume=VolumePost(source=Reference(name=module.params["name"])), ) if res.status_code != 200: module.fail_json( @@ -1046,261 +1158,293 @@ def copy_from_volume(module, array): ) module.exit_json( - changed=changed, volume=_volfact(module, arrayv6, module.params["target"]) + changed=changed, volume=_volfact(module, array, module.params["target"]) ) -def pg_check(module): - array = get_array(module) - rest_version = array.get_rest_version() +def update_volume(module, array): + """Update Volume size and/or QoS""" + changed = False + api_version = array.get_rest_version() if ( - LooseVersion(rest_version) >= LooseVersion(DEFAULT_API_VERSION) + LooseVersion(api_version) >= LooseVersion(DEFAULT_API_VERSION) and module.params["pgroup"] ): module.fail_json(msg="For Purity//FA 6.3.4 or higher, use add_to_pgs parameter") elif ( - LooseVersion(rest_version) <= LooseVersion(DEFAULT_API_VERSION) + LooseVersion(api_version) <= LooseVersion(DEFAULT_API_VERSION) and module.params["add_to_pgs"] ): module.fail_json(msg="For Purity//FA 6.3.4 or lower, use pgroup parameter") - - -def update_volume(module, array): - """Update Volume size and/or QoS""" - changed = False - arrayv6 = None - api_version = array._list_available_rest_versions() - pg_check(module) - if MULTI_VOLUME_VERSION in api_version: - arrayv6 = get_array(module) - vol = array.get_volume(module.params["name"]) - vol_qos = array.get_volume(module.params["name"], qos=True) - if QOS_API_VERSION in api_version: - if vol_qos["bandwidth_limit"] is None: - vol_qos["bandwidth_limit"] = 0 - if IOPS_API_VERSION in api_version: - if vol_qos["iops_limit"] is None: - vol_qos["iops_limit"] = 0 - if "::" in module.params["name"]: - if module.params["bw_qos"] or module.params["iops_qos"]: - if AC_QOS_VERSION not in api_version: - module.warn( - "Pods cannot cannot contain volumes with QoS settings. Ignoring..." - ) - module.params["bw_qos"] = module.params["iops_qos"] = None + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + vol = list( + array.get_volumes( + names=[module.params["name"]], ccontext_names=[module.params["context"]] + ).items + )[0] + else: + vol = list(array.get_volumes(names=[module.params["name"]]).items)[0] + vol_qos = vol.qos + if not hasattr(vol_qos, "bandwidth_limit"): + vol.qos.bandwidth_limit = 0 + if not hasattr(vol_qos, "iops_limit"): + vol.qos.iops_limit = 0 if module.params["size"]: if human_to_bytes(module.params["size"]) != vol["size"]: if human_to_bytes(module.params["size"]) > vol["size"]: changed = True if not module.check_mode: - try: - array.extend_volume( - module.params["name"], module.params["size"] + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePatch(provisioned=module.params["size"]), ) - except Exception: - module.fail_json( - msg="Volume {0} resize failed.".format( - module.params["name"] - ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(provisioned=module.params["size"]), ) - if module.params["bw_qos"] and QOS_API_VERSION in api_version: - if int(human_to_bytes(module.params["bw_qos"])) != int( - vol_qos["bandwidth_limit"] - ): - if module.params["bw_qos"] == "0": - changed = True - if not module.check_mode: - try: - array.set_volume(module.params["name"], bandwidth_limit="") - except Exception: + if res.status_code != 200: module.fail_json( - msg="Volume {0} Bandwidth QoS removal failed.".format( - module.params["name"] + msg="Volume {0} resize failed. Error: {1}".format( + module.params["name"], + res.errors[0].message, ) ) - elif int(human_to_bytes(module.params["bw_qos"])) in range( - 1048576, 549755813888 - ): - changed = True - if not module.check_mode: - try: - array.set_volume( + if module.params["bw_qos"] and int(human_to_bytes(module.params["bw_qos"])) != int( + vol_qos.bandwidth_limit + ): + if module.params["bw_qos"] == "0": + changed = True + if not module.check_mode: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=VolumePatch(qos=Qos(bandwidth_limit=549755813888)), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(qos=Qos(bandwidth_limit=549755813888)), + ) + if res.status_code != 200: + module.fail_json( + msg="Volume {0} Bandwidth QoS removal failed. Error: {1}".format( module.params["name"], - bandwidth_limit=module.params["bw_qos"], - ) - except Exception: - module.fail_json( - msg="Volume {0} Bandwidth QoS change failed.".format( - module.params["name"] - ) + res.errors[0].message, ) - else: - module.fail_json( - msg="Bandwidth QoS value {0} out of range.".format( - module.params["bw_qos"] ) - ) - if module.params["iops_qos"] and IOPS_API_VERSION in api_version: - if int(human_to_real(module.params["iops_qos"])) != int(vol_qos["iops_limit"]): - if module.params["iops_qos"] == "0": - changed = True - if not module.check_mode: - try: - array.set_volume(module.params["name"], iops_limit="") - except Exception: - module.fail_json( - msg="Volume {0} IOPs QoS removal failed.".format( - module.params["name"] - ) + else: + changed = True + if not module.check_mode: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePatch( + qos=Qos(bandwidth_limit=module.params["bw_qos"]) + ), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch( + qos=Qos(bandwidth_limit=module.params["bw_qos"]) + ), + ) + if res.status_code != 200: + module.fail_json( + msg="Volume {0} Bandwidth QoS change failed. Error: {1}".format( + module.params["name"], + res.errors[0].message, ) - elif int(human_to_real(module.params["iops_qos"])) in range(100, 100000000): - changed = True - if not module.check_mode: - try: - array.set_volume( - module.params["name"], iops_limit=module.params["iops_qos"] + ) + if module.params["iops_qos"] and int( + human_to_real(module.params["iops_qos"]) + ) != int(vol_qos["iops_limit"]): + if module.params["iops_qos"] == "0": + changed = True + if not module.check_mode: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=VolumePatch(qos=Qos(iops_limit=100000000)), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(qos=Qos(iops_limit=100000000)), + ) + if res.status_code != 200: + module.fail_json( + msg="Volume {0} IOPs QoS removal failed. Error: {1}".format( + module.params["name"], res.errors[0].message ) - except Exception: - module.fail_json( - msg="Volume {0} IOPs QoS change failed.".format( - module.params["name"] - ) + ) + else: + changed = True + if not module.check_mode: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=VolumePatch( + qos=Qos(iops_limit=module.params["iops_qos"]) + ), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch( + qos=Qos(iops_limit=module.params["iops_qos"]) + ), + ) + if res.status_code != 200: + module.fail_json( + msg="Volume {0} IOPs QoS change failed. Error: {1}".format( + module.params["name"], res.errors[0].message ) - else: - module.fail_json( - msg="Bandwidth QoS value {0} out of range.".format( - module.params["bw_qos"] ) - ) - if VOLUME_PROMOTION_API_VERSION in api_version and module.params["promotion_state"]: - vol6 = list(arrayv6.get_volumes(names=[module.params["name"]]).items)[0] - if module.params["promotion_state"] != vol6.promotion_status: - volume_patch = flasharray.VolumePatch( + if module.params["promotion_state"]: + if module.params["promotion_state"] != vol.promotion_status: + volume_patch = VolumePatch( requested_promotion_state=module.params["promotion_state"] ) changed = True if not module.check_mode: - prom_res = arrayv6.patch_volumes( - names=[module.params["name"]], volume=volume_patch - ) - if prom_res.status_code != 200: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=volume_patch, + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], volume=volume_patch + ) + if res.status_code != 200: module.fail_json( - msg="Failed to change promotion status for volume {0}.".format( - module.params["name"] + msg="Failed to change promotion status for volume {0}. Error: {1}".format( + module.params["name"], + res.errors[0].message, ) ) - if PRIORITY_API_VERSION in api_version and module.params["priority_operator"]: - volv6 = list(arrayv6.get_volumes(names=[module.params["name"]]).items)[0] + if module.params["priority_operator"]: change_prio = False if ( module.params["priority_operator"] - != volv6.priority_adjustment.priority_adjustment_operator + != vol.priority_adjustment.priority_adjustment_operator ): change_prio = True newop = module.params["priority_operator"] else: - newop = volv6.priority_adjustment.priority_adjustment_operator + newop = vol.priority_adjustment.priority_adjustment_operator if ( module.params["priority_value"] and module.params["priority_value"] - != volv6.priority_adjustment.priority_adjustment_value + != vol.priority_adjustment.priority_adjustment_value ): change_prio = True newval = module.params["priority_value"] elif ( not module.params["priority_value"] - and volv6.priority_adjustment.priority_adjustment_value != 0 + and vol.priority_adjustment.priority_adjustment_value != 0 ): change_prio = True newval = 0 else: - newval = volv6.priority_adjustment.priority_adjustment_value - volumepatch = flasharray.VolumePatch( - priority_adjustment=flasharray.PriorityAdjustment( + newval = vol.priority_adjustment.priority_adjustment_value + volumepatch = VolumePatch( + priority_adjustment=PriorityAdjustment( priority_adjustment_operator=newop, priority_adjustment_value=newval, ) ) - if change_prio and not module.check_mode: + if change_prio: changed = True - prio_res = arrayv6.patch_volumes( - names=[module.params["name"]], volume=volumepatch - ) - if prio_res.status_code != 200: - module.fail_json( - msg="Failed to change DMM Priority Adjustment for {0}. Error: {1}".format( - module.params["name"], prio_res.errors[0].message + if not module.check_mode: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + prio_res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=volumepatch, + ) + else: + prio_res = array.patch_volumes( + names=[module.params["name"]], volume=volumepatch + ) + if prio_res.status_code != 200: + module.fail_json( + msg="Failed to change DMM Priority Adjustment for {0}. Error: {1}".format( + module.params["name"], prio_res.errors[0].message + ) ) - ) module.exit_json( - changed=changed, volume=_volfact(module, arrayv6, module.params["name"]) + changed=changed, volume=_volfact(module, array, module.params["name"]) ) def rename_volume(module, array): """Rename volume within a container, ie pod, vgroup or local array""" changed = False - arrayv6 = get_array(module) pod_name = "" vgroup_name = "" - target_name = module.params["rename"] target_exists = False + api_version = array.get_rest_version() if "::" in module.params["name"]: pod_name = module.params["name"].split("::")[0] target_name = pod_name + "::" + module.params["rename"] - try: - array.get_volume(target_name, pending=True) - target_exists = True - except Exception: - target_exists = False elif "/" in module.params["name"]: vgroup_name = module.params["name"].split("/")[0] target_name = vgroup_name + "/" + module.params["rename"] - try: - array.get_volume(target_name, pending=True) - target_exists = True - except Exception: - target_exists = False else: - try: - array.get_volume(target_name, pending=True) - target_exists = True - except Exception: - target_exists = False - if target_exists and get_endpoint(target_name, array): - module.fail_json( - msg="Target volume {0} is a protocol-endpoinnt".format(target_name) + target_name = module.params["rename"] + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + target_exists = bool( + array.get_volumes( + names=[target_name], context_names=[module.params["context"]] + ).status_code + == 200 ) - if not target_exists: - if get_destroyed_endpoint(target_name, array): - module.fail_json( - msg="Target volume {0} is a destroyed protocol-endpoinnt".format( - target_name - ) - ) - else: - changed = True - if not module.check_mode: - try: - array.rename_volume(module.params["name"], module.params["rename"]) - except Exception: - module.fail_json( - msg="Rename volume {0} to {1} failed.".format( - module.params["name"], module.params["rename"] - ) - ) else: + target_exists = bool(array.get_volumes(names=[target_name]).status_code == 200) + if target_exists: module.fail_json(msg="Target volume {0} already exists.".format(target_name)) + else: + changed = True + if not module.check_mode: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + volume=VolumePatch(name=module.params["rename"]), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(name=module.params["rename"]), + ) + if res.status_code != 200: + module.fail_json( + msg="Rename volume {0} to {1} failed. Error: {2}".format( + module.params["name"], + module.params["rename"], + res.errors[0].message, + ) + ) module.exit_json( - changed=changed, volume=_volfact(module, arrayv6, module.params["rename"]) + changed=changed, volume=_volfact(module, array, module.params["rename"]) ) def move_volume(module, array): """Move volume between pods, vgroups or local array""" + api_version = array.get_rest_version() changed = vgroup_exists = target_exists = pod_exists = False - api_version = array._list_available_rest_versions() pod_name = "" vgroup_name = "" volume_name = module.params["name"] @@ -1317,59 +1461,92 @@ def move_volume(module, array): module.fail_json( msg="Source and destination [local] cannot be the same." ) - try: - target_exists = array.get_volume(volume_name, pending=True) - except Exception: - target_exists = False + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + target_exists = bool( + array.get_volumes( + names=[volume_name], context_names=[module.params["context"]] + ).status_code + == 200 + ) + else: + target_exists = bool( + array.get_volumes(names=[volume_name]).status_code == 200 + ) if target_exists: module.fail_json(msg="Target volume {0} already exists".format(volume_name)) else: - try: - pod_exists = array.get_pod(module.params["move"]) - if len(pod_exists["arrays"]) > 1: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + pod_exists = bool( + array.get_pods( + names=[module.params["move"]], + context_names=[module.params["context"]], + ).status_code + == 200 + ) + else: + pod_exists = bool( + array.get_pods(names=[module.params["move"]]).status_code == 200 + ) + if pod_exists: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + pod = list( + array.get_pods( + names=[module.params["move"]], + context_names=[module.params["context"]], + ).items + )[0] + else: + pod = list(array.get_pods(names=[module.params["move"]]).items)[0] + if pod.array_count > 1: module.fail_json(msg="Volume cannot be moved into a stretched pod") - if pod_exists["link_target_count"] != 0: + if pod.link_target_count != 0: module.fail_json(msg="Volume cannot be moved into a linked source pod") - if PROMOTE_API_VERSION in api_version: - if pod_exists["promotion_status"] == "demoted": - module.fail_json(msg="Volume cannot be moved into a demoted pod") - pod_exists = bool(pod_exists) - except Exception: - pod_exists = False - if pod_exists: - try: - pg_info = array.get_volume(volume_name, pending=True, protect=True) - pgs = [ - item["protection_group"] - for item in pg_info - if "protection_group" in item - ] - if pgs: - module.fail_json( - msg="Cannot move volume in protection groups {0}, disassociate to proceed".format( - pgs - ) - ) + if pod.promotion_status == "demoted": + module.fail_json(msg="Volume cannot be moved into a demoted pod") + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): target_exists = bool( - array.get_volume( - module.params["move"] + "::" + volume_name, pending=True - ) + array.get_volumes( + names=[module.params["move"] + "::" + volume_name], + context_names=[module.params["context"]], + ).status_code + == 200 + ) + else: + target_exists = bool( + array.get_volumes( + names=[module.params["move"] + "::" + volume_name] + ).status_code + == 200 ) - except Exception: - target_exists = False - try: - vgroup_exists = bool(array.get_vgroup(module.params["move"])) - except Exception: - vgroup_exists = False + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + vgroup_exists = bool( + array.get_volume_groups( + names=[module.params["move"]], + context_names=[module.params["context"]], + ).status_code + == 200 + ) + else: + vgroup_exists = bool( + array.get_volume_groups(names=[module.params["move"]]).status_code + == 200 + ) if vgroup_exists: - try: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): target_exists = bool( - array.get_volume( - module.params["move"] + "/" + volume_name, pending=True - ) + array.get_volumes( + names=[module.params["move"] + "/" + volume_name], + context_names=[module.params["context"]], + ).status_code + == 200 + ) + else: + target_exists = bool( + array.get_volumes( + names=[module.params["move"] + "/" + volume_name] + ).status_code + == 200 ) - except Exception: - target_exists = False if target_exists: module.fail_json(msg="Volume of same name already exists in move location") if pod_exists and vgroup_exists: @@ -1383,100 +1560,192 @@ def move_volume(module, array): msg="Move location {0} does not exist.".format(module.params["move"]) ) if "::" in module.params["name"] and not vgroup_exists: - pod = array.get_pod(module.params["move"]) - if len(pod["arrays"]) > 1: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + pod = list(array.get_pods(names=[pod_name]).items)[0] + else: + pod = list( + array.get_pods( + names=[pod_name], context_names=[module.params["context"]] + ).items + )[0] + if pod.array_count > 1: module.fail_json(msg="Volume cannot be moved out of a stretched pod") - if pod["linked_target_count"] != 0: + if pod.linked_target_count != 0: module.fail_json( msg="Volume cannot be moved out of a linked source pod" ) - if PROMOTE_API_VERSION in api_version: - if pod["promotion_status"] == "demoted": - module.fail_json(msg="Volume cannot be moved out of a demoted pod") + if pod.promotion_status == "demoted": + module.fail_json(msg="Volume cannot be moved out of a demoted pod") if "/" in module.params["name"]: if ( vgroup_name == module.params["move"] or pod_name == module.params["move"] ): module.fail_json(msg="Source and destination cannot be the same") - target_location = module.params["move"] - if get_endpoint(target_location, array): + if get_endpoint(module, module.params["move"], array): module.fail_json( - msg="Target volume {0} is a protocol-endpoinnt".format(target_location) + msg="Target volume {0} is a protocol-endpoinnt".format( + module.params["move"] + ) ) changed = True if not module.check_mode: - try: - volume = array.move_volume(module.params["name"], target_location) - volume_name = volume["name"] - except Exception: - if target_location == "": - target_location = "[local]" + if pod_exists: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=VolumePatch(pod=Reference(name=module.params["move"])), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(pod=Reference(name=module.params["move"])), + ) + elif vgroup_exists: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=VolumePatch( + volume_group=Reference(name=module.params["move"]) + ), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch( + volume_group=Reference(name=module.params["move"]) + ), + ) + elif module.params["move"] == "local": + if "/" in module.params["name"]: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=VolumePatch(volume_group=Reference(name="")), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(volume_group=Reference(name="")), + ) + else: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + context_names=[module.params["context"]], + names=[module.params["name"]], + volume=VolumePatch(pod=Reference(name="")), + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(pod=Reference(name="")), + ) + if res.status_code != 200: module.fail_json( - msg="Move of volume {0} to {1} failed.".format( - module.params["name"], target_location + msg="Move of volume {0} to {1} failed. Error: {2}".format( + module.params["name"], + module.params["move"], + res.errors[0].message, ) ) + else: + volume_name = list(res.items)[0].name else: volume_name = "" - arrayv6 = get_array(module) - module.exit_json(changed=changed, volume=_volfact(module, arrayv6, volume_name)) + module.exit_json(changed=changed, volume=_volfact(module, array, volume_name)) def delete_volume(module, array): """Delete Volume""" + api_version = array.get_rest_version() changed = True if not module.check_mode: - try: - array.destroy_volume(module.params["name"]) - if module.params["eradicate"]: - try: - array.eradicate_volume(module.params["name"]) - except Exception: - module.fail_json( - msg="Eradicate volume {0} failed.".format(module.params["name"]) + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(destroyed=True), + context_names=[module.params["context"]], + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], volume=VolumePatch(destroyed=True) + ) + if res.status_code == 200 and module.params["eradicate"]: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.delete_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + ) + else: + res = array.delete_volumes(names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Eradicate volume {0} failed. Error: {1}".format( + module.params["name"], res.errors[0].message ) - module.exit_json(changed=changed, volume=[]) - except Exception: + ) + module.exit_json(changed=changed, volume=[]) + else: module.fail_json( - msg="Delete volume {0} failed.".format(module.params["name"]) + msg="Delete volume {0} failed. Error: {1}".format( + module.params["name"], res.errors[0].message + ) ) - arrayv6 = get_array(module) module.exit_json( - changed=changed, volume=_volfact(module, arrayv6, module.params["name"]) + changed=changed, volume=_volfact(module, array, module.params["name"]) ) def eradicate_volume(module, array): """Eradicate Deleted Volume""" + api_version = array.get_rest_version() changed = False volfact = [] if module.params["eradicate"]: changed = True if not module.check_mode: - try: - array.eradicate_volume(module.params["name"]) - except Exception: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.delete_volumes( + names=[module.params["name"]], + context_names=[module.params["context"]], + ) + else: + res = array.delete_volumes(names=[module.params["name"]]) + if res.status_code != 200: module.fail_json( - msg="Eradication of volume {0} failed".format(module.params["name"]) + msg="Eradication of volume {0} failed. Erro: {1}".format( + module.params["name"], res.errors[0].message + ) ) module.exit_json(changed=changed, volume=volfact) def recover_volume(module, array): """Recover Deleted Volume""" + api_version = array.get_rest_version() changed = True if not module.check_mode: - try: - array.recover_volume(module.params["name"]) - except Exception: + if LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version): + res = array.patch_volumes( + names=[module.params["name"]], + volume=VolumePatch(destroyed=False), + context_names=[module.params["context"]], + ) + else: + res = array.patch_volumes( + names=[module.params["name"]], volume=VolumePatch(destroyed=False) + ) + if res.status_code != 200: module.fail_json( - msg="Recovery of volume {0} failed".format(module.params["name"]) + msg="Recovery of volume {0} failed. Error: {1}".format( + module.params["name"], res.errors[0].message + ) ) - array.get_volume(module.params["name"]) - arrayv6 = get_array(module) module.exit_json( - changed=changed, volume=_volfact(module, arrayv6, module.params["name"]) + changed=changed, volume=_volfact(module, array, module.params["name"]) ) @@ -1504,6 +1773,7 @@ def main(): with_default_protection=dict(type="bool", default=True), add_to_pgs=dict(type="list", elements="str"), promotion_state=dict(type="str", choices=["promoted", "demoted"]), + context=dict(type="str"), ) ) @@ -1526,10 +1796,31 @@ def main(): iops_qos = module.params["iops_qos"] state = module.params["state"] destroyed = False - array = get_system(module) + array = get_array(module) volume = get_volume(module, array) - api_version = array._list_available_rest_versions() - endpoint = get_endpoint(module.params["name"], array) + api_version = array.get_rest_version() + endpoint = get_endpoint(module, module.params["name"], array) + if not HAS_PURESTORAGE: + module.fail_json(msg="py-pure-client sdk is required for this mudule") + if ( + LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(api_version) + and not module.params["context"] + ): + # If no context is provided set the context to the local array name + module.params["context"] = list(array.get_arrays().items)[0].name + + if module.params["bw_qos"]: + bw_qos = int(human_to_bytes(module.params["bw_qos"])) + if bw_qos in range(1048576, 549755813888): + bw_qos_size = bw_qos + else: + module.fail_json(msg="Bandwidth QoS value out of range.") + if module.params["iops_qos"]: + iops_qos = int(human_to_real(module.params["iops_qos"])) + if iops_qos in range(100, 100000000): + iops_qos_size = iops_qos + else: + module.fail_json(msg="IOPs QoS value out of range.") if endpoint: module.fail_json( @@ -1540,14 +1831,6 @@ def main(): if module.params["pgroup"]: pattern = re.compile("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$") - if ":" in module.params["pgroup"] and OFFLOAD_API_VERSION not in api_version: - module.fail_json( - msg="API version does not support offload protection groups." - ) - if "::" in module.params["pgroup"] and POD_API_VERSION not in api_version: - module.fail_json( - msg="API version does not support ActiveCluster protection groups." - ) if ":" in module.params["pgroup"]: if "::" in module.params["pgroup"]: pgname = module.params["pgroup"].split("::")[1] @@ -1590,22 +1873,14 @@ def main(): ) if not volume: - destroyed = get_destroyed_volume(module.params["name"], array) + destroyed = get_destroyed_volume(module, array) target = get_target(module, array) if module.params["count"]: - if not HAS_PURESTORAGE: - module.fail_json( - msg="py-pure-client sdk is required to support 'count' parameter" - ) - if MULTI_VOLUME_VERSION not in api_version: - module.fail_json( - msg="'count' parameter is not supported until Purity//FA 6.0.0 or higher" - ) if module.params["digits"] and module.params["digits"] not in range(1, 10): module.fail_json(msg="'digits' must be in the range of 1 to 10") if module.params["start"] < 0: module.fail_json(msg="'start' must be a positive number") - volume = get_multi_volumes(module) + volume = get_multi_volumes(module, array) if state == "present" and not volume and size: create_multi_volume(module, array) elif state == "present" and not volume and not size: