summaryrefslogtreecommitdiffstats
path: root/ansible_collections/purestorage/fusion/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/purestorage/fusion/plugins')
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/errors.py2
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py2
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py2
-rw-r--r--ansible_collections/purestorage/fusion/plugins/module_utils/snapshots.py29
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py11
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py28
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py8
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py6
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py7
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py8
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py37
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py35
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py13
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py8
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py8
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py9
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py9
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py8
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py8
-rw-r--r--ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py157
20 files changed, 310 insertions, 85 deletions
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/errors.py b/ansible_collections/purestorage/fusion/plugins/module_utils/errors.py
index 0edf364cf..f3d574edc 100644
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/errors.py
+++ b/ansible_collections/purestorage/fusion/plugins/module_utils/errors.py
@@ -162,7 +162,7 @@ def format_failed_fusion_operation_exception(exception):
if not code:
code = error.http_code
operation_name = op.request_type
- except Exception as e:
+ except Exception:
pass
output = ""
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py b/ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py
index a2cd75245..1bcb8b812 100644
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py
+++ b/ansible_collections/purestorage/fusion/plugins/module_utils/parsing.py
@@ -11,7 +11,7 @@ __metaclass__ = type
METRIC_SUFFIXES = ["K", "M", "G", "T", "P"]
duration_pattern = re.compile(
- r"^((?P<Y>[1-9]\d*)Y)?((?P<W>[1-9]\d*)W)?((?P<D>[1-9]\d*)D)?(((?P<H>[1-9]\d*)H)?((?P<M>[1-9]\d*)M)?)?$"
+ r"^((?P<Y>\d+)Y)?((?P<W>\d+)W)?((?P<D>\d+)D)?(((?P<H>\d+)H)?((?P<M>\d+)M)?)?$"
)
duration_transformation = {
"Y": 365 * 24 * 60,
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py b/ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py
index a4edaf341..db00a9c6f 100644
--- a/ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py
+++ b/ansible_collections/purestorage/fusion/plugins/module_utils/prerequisites.py
@@ -136,7 +136,7 @@ def _check_import(ansible_module, module, package=None, version_requirements=Non
:param version_requirements: a string, version requirements for 'package'
"""
try:
- mod = importlib.import_module(module)
+ importlib.import_module(module)
except ImportError:
ansible_module.fail_json(
msg="Error: Python package '{0}' required and missing".format(module)
diff --git a/ansible_collections/purestorage/fusion/plugins/module_utils/snapshots.py b/ansible_collections/purestorage/fusion/plugins/module_utils/snapshots.py
new file mode 100644
index 000000000..ed34c1c0e
--- /dev/null
+++ b/ansible_collections/purestorage/fusion/plugins/module_utils/snapshots.py
@@ -0,0 +1,29 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+try:
+ import fusion as purefusion
+except ImportError:
+ pass
+
+from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
+ await_operation,
+)
+
+
+def delete_snapshot(fusion, snap, snapshots_api):
+ patch = purefusion.SnapshotPatch(destroyed=purefusion.NullableBoolean(True))
+ op = snapshots_api.update_snapshot(
+ body=patch,
+ tenant_name=snap.tenant.name,
+ tenant_space_name=snap.tenant_space.name,
+ snapshot_name=snap.name,
+ )
+ await_operation(fusion, op)
+ op = snapshots_api.delete_snapshot(
+ tenant_name=snap.tenant.name,
+ tenant_space_name=snap.tenant_space.name,
+ snapshot_name=snap.name,
+ )
+ await_operation(fusion, op)
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py
index 39860449d..42254338f 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_api_client.py
@@ -99,14 +99,15 @@ def create_client(module, fusion):
id_api_instance = purefusion.IdentityManagerApi(fusion)
changed = True
+ id = None
if not module.check_mode:
client = purefusion.APIClientPost(
public_key=module.params["public_key"],
display_name=module.params["name"],
)
- id_api_instance.create_api_client(client)
-
- module.exit_json(changed=changed)
+ res = id_api_instance.create_api_client(client)
+ id = res.id
+ module.exit_json(changed=changed, id=id)
def main():
@@ -129,8 +130,8 @@ def main():
create_client(module, fusion)
elif client_id is not None and state == "absent":
delete_client(module, fusion, client_id)
- else:
- module.exit_json(changed=False)
+ if client_id is not None:
+ module.exit_json(changed=False, id=client_id)
module.exit_json(changed=False)
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py
index f7933eabe..ec94d616f 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_array.py
@@ -60,6 +60,10 @@ options:
description:
- Appliance ID of the array.
type: str
+ apartment_id:
+ description:
+ - The Apartment ID of the Array.
+ type: str
maintenance_mode:
description:
- "Switch the array into maintenance mode or back.
@@ -123,6 +127,7 @@ def create_array(module, fusion):
"""Create Array"""
array_api_instance = purefusion.ArraysApi(fusion)
+ id = None
if not module.check_mode:
if not module.params["display_name"]:
@@ -135,14 +140,17 @@ def create_array(module, fusion):
host_name=module.params["host_name"],
name=module.params["name"],
appliance_id=module.params["appliance_id"],
+ apartment_id=module.params["apartment_id"],
)
res = array_api_instance.create_array(
array,
availability_zone_name=module.params["availability_zone"],
region_name=module.params["region"],
)
- await_operation(fusion, res)
- return True
+ res_op = await_operation(fusion, res)
+ id = res_op.result.resource.id
+
+ return True, id
def update_array(module, fusion):
@@ -222,6 +230,7 @@ def main():
availability_zone=dict(type="str", required=True, aliases=["az"]),
display_name=dict(type="str"),
region=dict(type="str", required=True),
+ apartment_id=dict(type="str"),
appliance_id=dict(type="str"),
host_name=dict(type="str"),
hardware_type=dict(
@@ -246,17 +255,24 @@ def main():
array = get_array(module, fusion)
changed = False
+ id = None
+ if array is not None:
+ id = array.id
+
if not array and state == "present":
module.fail_on_missing_params(["hardware_type", "host_name", "appliance_id"])
- changed = create_array(module, fusion) | update_array(
+ changed, id = create_array(module, fusion)
+ update_array(
module, fusion
) # update is run to set properties which cannot be set on creation and instead use defaults
elif array and state == "present":
- changed = changed | update_array(module, fusion)
+ changed = update_array(module, fusion)
elif array and state == "absent":
changed = changed | delete_array(module, fusion)
- else:
- module.exit_json(changed=False)
+ module.exit_json(changed=changed)
+
+ if id is not None:
+ module.exit_json(changed=changed, id=id)
module.exit_json(changed=changed)
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py
index 02647d397..b4a493861 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_az.py
@@ -112,6 +112,7 @@ def create_az(module, fusion):
az_api_instance = purefusion.AvailabilityZonesApi(fusion)
changed = True
+ id = None
if not module.check_mode:
if not module.params["display_name"]:
display_name = module.params["name"]
@@ -125,9 +126,10 @@ def create_az(module, fusion):
op = az_api_instance.create_availability_zone(
azone, region_name=module.params["region"]
)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def main():
@@ -152,8 +154,6 @@ def main():
create_az(module, fusion)
elif azone and state == "absent":
delete_az(module, fusion)
- else:
- module.exit_json(changed=False)
module.exit_json(changed=False)
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py
index 3f45ea2dd..c4df0af49 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_hap.py
@@ -170,8 +170,10 @@ def create_hap(module, fusion):
display_name=display_name,
)
)
- await_operation(fusion, op)
- module.exit_json(changed=changed)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
+
+ module.exit_json(changed=changed, id=id)
def delete_hap(module, fusion):
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py
index 6816ed841..82c896fac 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ni.py
@@ -162,7 +162,7 @@ def update_ni(module, fusion, ni):
),
)
patches.append(patch)
-
+ id = None
if not module.check_mode:
for patch in patches:
op = ni_api_instance.update_network_interface(
@@ -172,11 +172,12 @@ def update_ni(module, fusion, ni):
array_name=module.params["array"],
net_intf_name=module.params["name"],
)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
changed = len(patches) != 0
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def main():
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py
index d6056fd5a..d40b813b9 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_nig.py
@@ -146,6 +146,7 @@ def create_nig(module, fusion):
):
module.fail_json(msg="`gateway` must be an address in subnet `prefix`")
+ id = None
if not module.check_mode:
display_name = module.params["display_name"] or module.params["name"]
if module.params["group_type"] == "eth":
@@ -171,13 +172,14 @@ def create_nig(module, fusion):
availability_zone_name=module.params["availability_zone"],
region_name=module.params["region"],
)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
changed = True
else:
# to prevent future unintended error
module.warn(f"group_type={module.params['group_type']} is not implemented")
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def delete_nig(module, fusion):
@@ -220,7 +222,7 @@ def update_nig(module, fusion, nig):
changed = len(patches) != 0
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=nig.id)
def main():
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py
index 57843d896..6d6f0eb94 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_pg.py
@@ -36,6 +36,11 @@ options:
type: str
default: present
choices: [ absent, present ]
+ destroy_snapshots_on_delete:
+ description:
+ - "Before deleting placement group, snapshots within the placement group will be deleted."
+ - "If `false` then any snapshots will need to be deleted as a separate step before removing the placement group."
+ type: bool
tenant:
description:
- The name of the tenant.
@@ -116,6 +121,9 @@ from ansible_collections.purestorage.fusion.plugins.module_utils.startup import
from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
await_operation,
)
+from ansible_collections.purestorage.fusion.plugins.module_utils.snapshots import (
+ delete_snapshot,
+)
def get_pg(module, fusion):
@@ -153,9 +161,10 @@ def create_pg(module, fusion):
tenant_name=module.params["tenant"],
tenant_space_name=module.params["tenant_space"],
)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- return True
+ return True, id
def update_display_name(module, fusion, patches, pg):
@@ -213,6 +222,16 @@ def delete_pg(module, fusion):
"""Delete Placement Group"""
pg_api_instance = purefusion.PlacementGroupsApi(fusion)
if not module.check_mode:
+ if module.params["destroy_snapshots_on_delete"]:
+ snapshots_api = purefusion.SnapshotsApi(fusion)
+ snapshots = snapshots_api.list_snapshots(
+ placement_group=module.params["name"],
+ tenant_name=module.params["tenant"],
+ tenant_space_name=module.params["tenant_space"],
+ )
+ for snap in snapshots.items:
+ delete_snapshot(fusion, snap, snapshots_api)
+
op = pg_api_instance.delete_placement_group(
placement_group_name=module.params["name"],
tenant_name=module.params["tenant"],
@@ -229,6 +248,7 @@ def main():
argument_spec.update(
dict(
name=dict(type="str", required=True),
+ destroy_snapshots_on_delete=dict(type="bool"),
display_name=dict(type="str"),
tenant=dict(type="str", required=True),
tenant_space=dict(type="str", required=True),
@@ -257,19 +277,28 @@ def main():
state = module.params["state"]
pgroup = get_pg(module, fusion)
+ id = None
+ if pgroup is not None:
+ id = pgroup.id
+
if state == "present" and not pgroup:
module.fail_on_missing_params(
["region", "availability_zone", "storage_service"]
)
- changed = create_pg(module, fusion) or changed
+ changed, id = create_pg(module, fusion) or changed
if module.params["array"]:
# changing placement requires additional update
pgroup = get_pg(module, fusion)
- changed = update_pg(module, fusion, pgroup) or changed
+ changedUpdate = update_pg(module, fusion, pgroup)
+ changed = changed | changedUpdate
elif state == "present" and pgroup:
changed = update_pg(module, fusion, pgroup) or changed
elif state == "absent" and pgroup:
changed = delete_pg(module, fusion) or changed
+ module.exit_json(changed=changed)
+
+ if id is not None:
+ module.exit_json(changed=changed, id=id)
module.exit_json(changed=changed)
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py
index abce9195c..216209d84 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_pp.py
@@ -31,6 +31,11 @@ options:
default: present
choices: [ present, absent ]
type: str
+ destroy_snapshots_on_delete:
+ description:
+ - "Before deleting protection policy, snapshots within the protection policy will be deleted."
+ - "If `false` then any snapshots will need to be deleted as a separate step before removing the protection policy."
+ type: bool
display_name:
description:
- The human name of the protection policy.
@@ -39,8 +44,10 @@ options:
local_rpo:
description:
- Recovery Point Objective for snapshots.
- - Value should be specified in minutes.
- Minimum value is 10 minutes.
+ - Value can be provided as m(inutes), h(ours),
+ d(ays), w(eeks), or y(ears).
+ - If no unit is provided, minutes are assumed.
type: str
local_retention:
description:
@@ -95,6 +102,9 @@ from ansible_collections.purestorage.fusion.plugins.module_utils.startup import
from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
await_operation,
)
+from ansible_collections.purestorage.fusion.plugins.module_utils.snapshots import (
+ delete_snapshot,
+)
def get_pp(module, fusion):
@@ -114,11 +124,13 @@ def create_pp(module, fusion):
pp_api_instance = purefusion.ProtectionPoliciesApi(fusion)
local_rpo = parse_minutes(module, module.params["local_rpo"])
local_retention = parse_minutes(module, module.params["local_retention"])
- if local_retention < 1:
- module.fail_json(msg="Local Retention must be a minimum of 1 minutes")
+ if local_retention < 10:
+ module.fail_json(msg="Local Retention must be a minimum of 10 minutes")
if local_rpo < 10:
module.fail_json(msg="Local RPO must be a minimum of 10 minutes")
+
changed = True
+ id = None
if not module.check_mode:
if not module.params["display_name"]:
display_name = module.params["name"]
@@ -136,9 +148,10 @@ def create_pp(module, fusion):
],
)
)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def delete_pp(module, fusion):
@@ -146,6 +159,15 @@ def delete_pp(module, fusion):
pp_api_instance = purefusion.ProtectionPoliciesApi(fusion)
changed = True
if not module.check_mode:
+ if module.params["destroy_snapshots_on_delete"]:
+ protection_policy = get_pp(module, fusion)
+ snapshots_api = purefusion.SnapshotsApi(fusion)
+ snapshots = snapshots_api.query_snapshots(
+ protection_policy_id=protection_policy.id
+ )
+ for snap in snapshots.items:
+ delete_snapshot(fusion, snap, snapshots_api)
+
op = pp_api_instance.delete_protection_policy(
protection_policy_name=module.params["name"],
)
@@ -160,6 +182,7 @@ def main():
argument_spec.update(
dict(
name=dict(type="str", required=True),
+ destroy_snapshots_on_delete=dict(type="bool"),
display_name=dict(type="str"),
local_rpo=dict(type="str"),
local_retention=dict(type="str"),
@@ -177,8 +200,6 @@ def main():
create_pp(module, fusion)
elif policy and state == "absent":
delete_pp(module, fusion)
- else:
- module.exit_json(changed=False)
module.exit_json(changed=False)
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py
index 7cfc7d866..c2ae2d5cf 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ra.py
@@ -43,7 +43,7 @@ options:
type: str
api_client_key:
description:
- - The key of API client to assign the role to.
+ - The issuer ID of the API client to assign the role to.
type: str
scope:
description:
@@ -127,7 +127,7 @@ def get_principal(module, fusion):
def user_to_principal(fusion, user_id):
- """Given a human readable Fusion user, such as a Pure 1 App ID
+ """Given a human-readable Fusion user, such as a Pure 1 App ID
return the associated principal
"""
id_api_instance = purefusion.IdentityManagerApi(fusion)
@@ -139,7 +139,7 @@ def user_to_principal(fusion, user_id):
def apiclient_to_principal(fusion, api_client_key):
- """Given an API client key, such as "pure1:apikey:123xXxyYyzYzASDF" (also known as issuer_id),
+ """Given an API client issuer ID, such as "pure1:apikey:123xXxyYyzYzASDF",
return the associated principal
"""
id_api_instance = purefusion.IdentityManagerApi(fusion)
@@ -189,6 +189,7 @@ def create_ra(module, fusion):
ra_api_instance = purefusion.RoleAssignmentsApi(fusion)
changed = True
+ id = None
if not module.check_mode:
principal = get_principal(module, fusion)
scope = get_scope(module.params)
@@ -196,8 +197,10 @@ def create_ra(module, fusion):
op = ra_api_instance.create_role_assignment(
assignment, role_name=module.params["role"]
)
- await_operation(fusion, op)
- module.exit_json(changed=changed)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
+
+ module.exit_json(changed=changed, id=id)
def delete_ra(module, fusion):
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py
index fbcbff4b0..de40e7dc2 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_region.py
@@ -96,6 +96,7 @@ def create_region(module, fusion):
reg_api_instance = purefusion.RegionsApi(fusion)
changed = True
+ id = None
if not module.check_mode:
if not module.params["display_name"]:
display_name = module.params["name"]
@@ -106,9 +107,10 @@ def create_region(module, fusion):
display_name=display_name,
)
op = reg_api_instance.create_region(region)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def delete_region(module, fusion):
@@ -144,7 +146,7 @@ def update_region(module, fusion, region):
)
await_operation(fusion, op)
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=region.id)
def main():
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py
index 2327b8d48..59fc0025e 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_sc.py
@@ -160,6 +160,7 @@ def create_sc(module, fusion):
module.fail_json(msg="Size limit is not within the required range")
changed = True
+ id = None
if not module.check_mode:
if not module.params["display_name"]:
display_name = module.params["name"]
@@ -175,9 +176,10 @@ def create_sc(module, fusion):
op = sc_api_instance.create_storage_class(
s_class, storage_service_name=module.params["storage_service"]
)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def update_sc(module, fusion, s_class):
@@ -201,7 +203,7 @@ def update_sc(module, fusion, s_class):
)
await_operation(fusion, op)
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=s_class.id)
def delete_sc(module, fusion):
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py
index 9eed4bea0..3a191a166 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_se.py
@@ -269,7 +269,7 @@ def get_se(module, fusion):
def create_se(module, fusion):
"""Create Storage Endpoint"""
se_api_instance = purefusion.StorageEndpointsApi(fusion)
-
+ id = None
if not module.check_mode:
endpoint_type = None
@@ -307,9 +307,10 @@ def create_se(module, fusion):
region_name=module.params["region"],
availability_zone_name=module.params["availability_zone"],
)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- module.exit_json(changed=True)
+ module.exit_json(changed=True, id=id)
def delete_se(module, fusion):
@@ -351,7 +352,7 @@ def update_se(module, fusion, se):
changed = len(patches) != 0
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=se.id)
def main():
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py
index 3fdbb07dd..4e6388249 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ss.py
@@ -106,6 +106,7 @@ def create_ss(module, fusion):
ss_api_instance = purefusion.StorageServicesApi(fusion)
changed = True
+ id = None
if not module.check_mode:
if not module.params["display_name"]:
display_name = module.params["name"]
@@ -117,9 +118,10 @@ def create_ss(module, fusion):
hardware_types=module.params["hardware_types"],
)
op = ss_api_instance.create_storage_service(s_service)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def delete_ss(module, fusion):
@@ -151,6 +153,7 @@ def update_ss(module, fusion, ss):
)
patches.append(patch)
+ id = None
if not module.check_mode:
for patch in patches:
op = ss_api_instance.update_storage_service(
@@ -161,7 +164,7 @@ def update_ss(module, fusion, ss):
changed = len(patches) != 0
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=ss.id)
def main():
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py
index 96e890a6b..85224a6c5 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_tenant.py
@@ -87,6 +87,7 @@ def create_tenant(module, fusion):
api_instance = purefusion.TenantsApi(fusion)
changed = True
+ id = None
if not module.check_mode:
if not module.params["display_name"]:
display_name = module.params["name"]
@@ -97,9 +98,10 @@ def create_tenant(module, fusion):
display_name=display_name,
)
op = api_instance.create_tenant(tenant)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def update_tenant(module, fusion, tenant):
@@ -122,7 +124,7 @@ def update_tenant(module, fusion, tenant):
)
await_operation(fusion, op)
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=tenant.id)
def delete_tenant(module, fusion):
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py
index 33fb0187a..ac60476bc 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_ts.py
@@ -95,6 +95,7 @@ def create_ts(module, fusion):
ts_api_instance = purefusion.TenantSpacesApi(fusion)
changed = True
+ id = None
if not module.check_mode:
if not module.params["display_name"]:
display_name = module.params["name"]
@@ -108,9 +109,10 @@ def create_ts(module, fusion):
tspace,
tenant_name=module.params["tenant"],
)
- await_operation(fusion, op)
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=id)
def update_ts(module, fusion, ts):
@@ -138,7 +140,7 @@ def update_ts(module, fusion, ts):
changed = len(patches) != 0
- module.exit_json(changed=changed)
+ module.exit_json(changed=changed, id=ts.id)
def delete_ts(module, fusion):
diff --git a/ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py b/ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py
index 5b19064f5..38dee8650 100644
--- a/ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py
+++ b/ansible_collections/purestorage/fusion/plugins/modules/fusion_volume.py
@@ -73,6 +73,21 @@ options:
To clear, assign empty list: host_access_policies: []'
type: list
elements: str
+ source_volume:
+ description:
+ - The source volume name. It must live within the same tenant space.
+ Cannot be used together with `source_snapshot` or `source_volume_snapshot`.
+ type: str
+ source_snapshot:
+ description:
+ - The source snapshot name. It must live within the same tenant space.
+ Cannot be used together with `source_volume`.
+ type: str
+ source_volume_snapshot:
+ description:
+ - The source volume snapshot name. It must live within the same tenant space.
+ Cannot be used together with `source_volume`.
+ type: str
rename:
description:
- New name for volume.
@@ -86,6 +101,7 @@ EXAMPLES = r"""
purestorage.fusion.fusion_volume:
name: foo
storage_class: fred
+ placement_group: pg
size: 1T
tenant: test
tenant_space: space_1
@@ -93,6 +109,31 @@ EXAMPLES = r"""
issuer_id: key_name
private_key_file: "az-admin-private-key.pem"
+- name: Create new volume based on a volume from the same tenant space
+ purestorage.fusion.fusion_volume:
+ name: foo
+ storage_class: fred
+ placement_group: pg
+ tenant: test
+ tenant_space: space_1
+ state: present
+ source_volume: "original_volume_name"
+ issuer_id: key_name
+ private_key_file: "az-admin-private-key.pem"
+
+- name: Create new volume based on a volume snapshot from the same tenant space
+ purestorage.fusion.fusion_volume:
+ name: foo
+ storage_class: fred
+ placement_group: pg
+ tenant: test
+ tenant_space: space_1
+ state: present
+ source_snapshot: "snap"
+ source_volume_snapshot: "vol_snap"
+ issuer_id: key_name
+ private_key_file: "az-admin-private-key.pem"
+
- name: Extend the size of an existing volume named foo
purestorage.fusion.fusion_volume:
name: foo
@@ -116,24 +157,24 @@ EXAMPLES = r"""
RETURN = r"""
"""
-try:
- import fusion as purefusion
-except ImportError:
- pass
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
- fusion_argument_spec,
-)
-from ansible_collections.purestorage.fusion.plugins.module_utils.parsing import (
- parse_number_with_metric_suffix,
+from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
+ await_operation,
)
from ansible_collections.purestorage.fusion.plugins.module_utils.startup import (
setup_fusion,
)
-from ansible_collections.purestorage.fusion.plugins.module_utils.operations import (
- await_operation,
+from ansible_collections.purestorage.fusion.plugins.module_utils.parsing import (
+ parse_number_with_metric_suffix,
)
+from ansible_collections.purestorage.fusion.plugins.module_utils.fusion import (
+ fusion_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+try:
+ import fusion as purefusion
+except ImportError:
+ pass
def get_volume(module, fusion):
@@ -166,28 +207,30 @@ def extract_current_haps(volume):
def create_volume(module, fusion):
"""Create Volume"""
-
- size = parse_number_with_metric_suffix(module, module.params["size"])
-
+ id = None
if not module.check_mode:
display_name = module.params["display_name"] or module.params["name"]
volume_api_instance = purefusion.VolumesApi(fusion)
+ source_link = get_source_link_from_parameters(module.params)
volume = purefusion.VolumePost(
- size=size,
+ size=None # when cloning a volume, size is not required
+ if source_link
+ else parse_number_with_metric_suffix(module, module.params["size"]),
storage_class=module.params["storage_class"],
placement_group=module.params["placement_group"],
name=module.params["name"],
display_name=display_name,
protection_policy=module.params["protection_policy"],
+ source_link=source_link,
)
op = volume_api_instance.create_volume(
volume,
tenant_name=module.params["tenant"],
tenant_space_name=module.params["tenant_space"],
)
- await_operation(fusion, op)
-
- return True
+ res_op = await_operation(fusion, op)
+ id = res_op.result.resource.id
+ return True, id
def update_host_access_policies(module, current, patches):
@@ -273,6 +316,17 @@ def update_protection_policy(module, current, patches):
patches.append(patch)
+def update_source_link(module, fusion, current, patches):
+ source_link = get_source_link_from_parameters(module.params)
+ if source_link is not None and (
+ current.source is None or current.source.self_link != source_link
+ ):
+ patch = purefusion.VolumePatch(
+ source_link=purefusion.NullableString(source_link)
+ )
+ patches.append(patch)
+
+
def apply_patches(module, fusion, patches):
volume_api_instance = purefusion.VolumesApi(fusion)
for patch in patches:
@@ -313,6 +367,7 @@ def update_volume(module, fusion):
update_storage_class(module, current, patches)
update_placement_group(module, current, patches)
update_host_access_policies(module, current, patches)
+ update_source_link(module, fusion, current, patches)
elif module.params["state"] == "absent" and not current.destroyed:
update_size(module, current, patches)
update_protection_policy(module, current, patches)
@@ -320,6 +375,7 @@ def update_volume(module, fusion):
update_storage_class(module, current, patches)
update_placement_group(module, current, patches)
update_host_access_policies(module, current, patches)
+ update_source_link(module, fusion, current, patches)
update_destroyed(module, current, patches)
if not module.check_mode:
@@ -355,16 +411,46 @@ def eradicate_volume(module, fusion):
return True
+def get_source_link_from_parameters(params):
+ tenant = params["tenant"]
+ tenant_space = params["tenant_space"]
+ volume = params["source_volume"]
+ snapshot = params["source_snapshot"]
+ volume_snapshot = params["source_volume_snapshot"]
+ if (
+ tenant is None or tenant_space is None
+ ): # should not happen as those parameters are always required by the ansible module
+ return None
+ if volume is not None:
+ return f"/tenants/{tenant}/tenant-spaces/{tenant_space}/volumes/{volume}"
+ if snapshot is not None and volume_snapshot is not None:
+ return f"/tenants/{tenant}/tenant-spaces/{tenant_space}/snapshots/{snapshot}/volume-snapshots/{volume_snapshot}"
+ return None
+
+
def validate_arguments(module, volume):
"""Validates most argument conditions and possible unacceptable argument combinations"""
state = module.params["state"]
if state == "present" and not volume:
- module.fail_on_missing_params(["placement_group", "storage_class", "size"])
+ module.fail_on_missing_params(["placement_group", "storage_class"])
+
+ if (
+ module.params["size"] is None
+ and module.params["source_volume"] is None
+ and module.params["source_snapshot"] is None
+ ):
+ module.fail_json(
+ msg="Either `size`, `source_volume` or `source_snapshot` parameter is required when creating a volume."
+ )
if module.params["state"] == "absent" and (
module.params["host_access_policies"]
- or (volume and volume.host_access_policies)
+ or (
+ module.params["host_access_policies"] is None
+ and volume
+ and volume.host_access_policies
+ )
):
module.fail_json(
msg=(
@@ -378,7 +464,7 @@ def validate_arguments(module, volume):
msg="'eradicate: true' cannot be used together with 'state: present'"
)
- if module.params["size"]:
+ if module.params["size"] is not None:
size = parse_number_with_metric_suffix(module, module.params["size"])
if size < 1048576 or size > 4503599627370496: # 1MB to 4PB
module.fail_json(
@@ -412,6 +498,9 @@ def main():
eradicate=dict(type="bool", default=False),
state=dict(type="str", default="present", choices=["absent", "present"]),
size=dict(type="str"),
+ source_volume=dict(type="str"),
+ source_snapshot=dict(type="str"),
+ source_volume_snapshot=dict(type="str"),
)
)
@@ -419,9 +508,22 @@ def main():
"placement_group": "storage_class",
}
+ mutually_exclusive = [
+ # a new volume cannot be based on a volume and a snapshot at the same time
+ # also, when cloning a volume, size of original volume is used
+ ("source_volume", "source_snapshot", "size"),
+ ]
+
+ required_together = [
+ # when creating a volume from snapshot, we need to know both snapshot name and snapshot volume name
+ ("source_snapshot", "source_volume_snapshot"),
+ ]
+
module = AnsibleModule(
argument_spec,
required_by=required_by,
+ mutually_exclusive=mutually_exclusive,
+ required_together=required_together,
supports_check_mode=True,
)
fusion = setup_fusion(module)
@@ -436,12 +538,19 @@ def main():
module.exit_json(changed=False)
changed = False
+ id = None
+ if volume is not None:
+ id = volume.id
if state == "present" and not volume:
- changed = changed | create_volume(module, fusion)
+ changed, id = create_volume(module, fusion)
# volume might exist even if soft-deleted, so we still have to update it
changed = changed | update_volume(module, fusion)
if module.params["eradicate"]:
changed = changed | eradicate_volume(module, fusion)
+ module.exit_json(changed=changed)
+
+ if id is not None:
+ module.exit_json(changed=changed, id=id)
module.exit_json(changed=changed)