diff options
Diffstat (limited to 'ansible_collections/purestorage/flashblade/plugins')
5 files changed, 550 insertions, 12 deletions
diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py index 27cd7e317..11c8be479 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py @@ -118,13 +118,32 @@ options: description: - If set to true, adding bucket policies that grant public access to a bucket is not allowed. type: bool - version_added: 1.15.0 + version_added: '1.15.0' block_public_access: description: - If set to true, access to a bucket with a public policy is restricted to only authenticated users within the account that bucket belongs to. type: bool - version_added: 1.15.0 + version_added: '1.15.0' + eradication_mode: + description: + - The eradication mode of the bucket. + type: str + choices: [ "permission-based", "retention-based" ] + version_added: '1.17.0' + manual_eradication: + description: + - The manual eradication status of the bucket. If false, the bucket cannot be eradicated after + it has been destroyed, unless it is empty. If true, the bucket can be eradicated. + type: bool + version_added: '1.17.0' + eradication_delay: + description: + - Minimum eradication delay in days. Automatically eradicate destroyed buckets after + the delay time passes unless automatic eradication is delayed due to other configuration values. + - Valid values are integer days from 1 to 30. Default is 1. + type: int + version_added: '1.17.0' extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ @@ -194,11 +213,13 @@ from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb impo ) +SEC_PER_DAY = 86400000 MIN_REQUIRED_API_VERSION = "1.5" VERSIONING_VERSION = "1.9" VSO_VERSION = "2.5" QUOTA_VERSION = "2.8" MODE_VERSION = "2.12" +WORM_VERSION = "2.13" def get_s3acc(module, blade): @@ -354,6 +375,30 @@ def create_bucket(module, blade): msg="Failed to set Public Access config correctly for bucket {0}. " "Error: {1}".format(module.params["name"], res.errors[0].message) ) + if WORM_VERSION in api_version and module.params["eradication_mode"]: + if not module.params["eradication_delay"]: + module.params["eradication_delay"] = SEC_PER_DAY + else: + module.params["eradication_delay"] = ( + module.params["eradication_delay"] * SEC_PER_DAY + ) + if not module.params["manual_eradication"]: + module.params["manual_eradication"] = "disabled" + else: + module.params["manual_eradication"] = "enabled" + worm = BucketPatch( + eradication_config=flashblade.BucketEradicationConfig( + manual_eradication=module.params["manual_eradication"], + eradication_mode=module.params["eradication_mode"], + eradication_delay=module.params["eradication_delay"], + ) + ) + res = bladev2.patch_buckets(bucket=worm, names=[module.params["name"]]) + if res.status_code != 200: + module.warn( + msg="Failed to set Bucket Eradication config correctly for bucket {0}. " + "Error: {1}".format(module.params["name"], res.errors[0].message) + ) module.exit_json(changed=changed) @@ -416,6 +461,7 @@ def update_bucket(module, blade, bucket): """Update Bucket""" changed = False change_pac = False + change_worm = False bladev2 = get_system(module) bucket_detail = list(bladev2.get_buckets(names=[module.params["name"]]).items)[0] api_version = blade.api_version.list_versions().versions @@ -520,7 +566,52 @@ def update_bucket(module, blade, bucket): msg="Failed to update Public Access config correctly for bucket {0}. " "Error: {1}".format(module.params["name"], res.errors[0].message) ) - module.exit_json(changed=(changed or change_pac)) + if WORM_VERSION in api_version: + current_worm = { + "eradication_delay": bucket_detail.eradication_config.eradication_delay, + "manual_eradication": bucket_detail.eradication_config.manual_eradication, + "eradication_mode": bucket_detail.eradication_config.eradication_mode, + } + if module.params["eradication_delay"] is None: + new_delay = current_worm["eradication_delay"] + else: + new_delay = module.params["eradication_delay"] * SEC_PER_DAY + if module.params["manual_eradication"] is None: + new_manual = current_worm["manual_eradication"] + else: + if module.params["manual_eradication"]: + new_manual = "enabled" + else: + new_manual = "disabled" + if ( + module.params["eradication_mode"] + and module.params["eradication_mode"] != current_worm["eradication_mode"] + ): + new_mode = module.params["eradication_mode"] + else: + new_mode = current_worm["eradication_mode"] + new_worm = { + "eradication_delay": new_delay, + "manual_eradication": new_manual, + "eradication_mode": new_mode, + } + if current_worm != new_worm: + change_worm = True + worm = BucketPatch( + public_access_config=flashblade.BucketEradicationConfig( + eradication_delay=new_worm.eradication_delay, + manual_eradication=new_worm.manual_eradication, + eradication_mode=new_worm.eradication_mode, + ) + ) + if change_worm and not module.check_mode: + res = bladev2.patch_buckets(bucket=worm, names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Failed to update Eradication config correctly for bucket {0}. " + "Error: {1}".format(module.params["name"], res.errors[0].message) + ) + module.exit_json(changed=(changed or change_pac or change_worm)) def eradicate_bucket(module, blade): @@ -564,6 +655,11 @@ def main(): default="absent", choices=["enabled", "suspended", "absent"] ), state=dict(default="present", choices=["present", "absent"]), + eradication_delay=dict(type="int"), + eradication_mode=dict( + type="str", choices=["permission-based", "retention-based"] + ), + manual_eradication=dict(type="bool"), ) ) @@ -575,6 +671,12 @@ def main(): if not HAS_PYPURECLIENT: module.fail_json(msg="py-pure-client sdk is required to support VSO mode") + if ( + module.params["eradication_delay"] + and not 30 >= module.params["eradication_delay"] >= 1 + ): + module.fail_json(msg="Eradication Delay must be between 1 and 30 days.") + state = module.params["state"] blade = get_blade(module) api_version = blade.api_version.list_versions().versions diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py index 846351453..23e54845c 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py @@ -138,8 +138,8 @@ from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb impo ) -FAN_IN_MAXIMUM = 1 -FAN_OUT_MAXIMUM = 3 +FAN_IN_MAXIMUM = 5 +FAN_OUT_MAXIMUM = 5 MIN_REQUIRED_API_VERSION = "1.9" THROTTLE_API_VERSION = "2.3" @@ -215,7 +215,7 @@ def create_connection(module, blade): remote_conn_cnt = ( remote_system.array_connections.list_array_connections().pagination_info.total_item_count ) - if remote_conn_cnt == FAN_IN_MAXIMUM: + if remote_conn_cnt >= FAN_IN_MAXIMUM: module.fail_json( msg="Remote array {0} already connected to {1} other array. Fan-In not supported".format( remote_array, remote_conn_cnt @@ -244,7 +244,7 @@ def create_connection(module, blade): def create_v2_connection(module, blade): """Create connection between REST 2 capable arrays""" changed = True - if blade.get_array_connections().total_item_count == FAN_OUT_MAXIMUM: + if blade.get_array_connections().total_item_count >= FAN_OUT_MAXIMUM: module.fail_json( msg="FlashBlade fan-out maximum of {0} already reached".format( FAN_OUT_MAXIMUM @@ -262,7 +262,7 @@ def create_v2_connection(module, blade): ) remote_array = list(remote_system.get_arrays().items)[0].name remote_conn_cnt = remote_system.get_array_connections().total_item_count - if remote_conn_cnt == FAN_IN_MAXIMUM: + if remote_conn_cnt >= FAN_IN_MAXIMUM: module.fail_json( msg="Remote array {0} already connected to {1} other array. Fan-In not supported".format( remote_array, remote_conn_cnt diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py index 8d332e8b7..81c12d4b7 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py @@ -195,11 +195,17 @@ options: version_added: "1.12.0" continuous_availability: description: - - Deifines if the file system will be continuously available during + - Defines if the file system will be continuously available during disruptive scenarios such as network disruption, blades failover, etc type: bool default: true version_added: "1.15.0" + group_ownership: + description: + - The group ownership for new files and directories in a file system + type: str + choices: [ 'creator', 'parent-directory' ] + version_added: "1.17.0" extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ @@ -314,6 +320,7 @@ MULTIPROTOCOL_API_VERSION = "1.11" EXPORT_POLICY_API_VERSION = "2.3" SMB_POLICY_API_VERSION = "2.10" CA_API_VERSION = "2.12" +GOWNER_API_VERSION = "2.13" def get_fs(module, blade): @@ -567,6 +574,21 @@ def create_fs(module, blade): res.errors[0].message, ) ) + if GOWNER_API_VERSION in api_version and module.params["group_ownership"]: + go_attr = FileSystemPatch( + group_ownership=module.params["group_ownership"] + ) + res = system.patch_file_systems( + names=[module.params["name"]], file_system=go_attr + ) + if res.status_code != 200: + module.fail_json( + msg="Filesystem {0} created, but failed to set group ownership" + "Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) + ) module.exit_json(changed=changed) @@ -911,8 +933,29 @@ def modify_fs(module, blade): res.errors[0].message, ) ) + if GOWNER_API_VERSION in api_version: + change_go = False + if module.params["group_ownership"] != current_fs.group_ownership: + change_go = True + if not module.check_mode: + go_attr = FileSystemPatch( + group_ownership=module.params["group_ownership"] + ) + res = system.patch_file_systems( + names=[module.params["name"]], file_system=go_attr + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to modify group ownership for " + "filesystem {0}. Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) + ) - module.exit_json(changed=(changed or change_export or change_share or change_ca)) + module.exit_json( + changed=(changed or change_export or change_share or change_ca or change_go) + ) def _delete_fs(module, blade): @@ -1057,6 +1100,7 @@ def main(): smb_aclmode=dict( type="str", default="shared", choices=["shared", "native"] ), + group_ownership=dict(choices=["creator", "parent-directory"]), policy_state=dict(default="present", choices=["present", "absent"]), state=dict(default="present", choices=["present", "absent"]), delete_link=dict(default=False, type="bool"), diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py index 033312e82..418dacd5d 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py @@ -111,6 +111,7 @@ BUCKET_API_VERSION = "2.8" SMB_CLIENT_API_VERSION = "2.10" SPACE_API_VERSION = "2.11" PUBLIC_API_VERSION = "2.12" +NAP_API_VERSION = "2.13" def _millisecs_to_time(millisecs): @@ -218,6 +219,10 @@ def generate_default_dict(module, blade): default_info["security_update"] = getattr( blade_info, "security_update", None ) + if NAP_API_VERSION in api_version: + default_info["network_access_protocol"] = getattr( + blade_info.network_access_policy, "name", "None" + ) return default_info diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py index ebe70aa48..36ffee997 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py @@ -52,7 +52,7 @@ options: - Type of policy default: snapshot type: str - choices: [ snapshot, access, nfs, smb_share, smb_client ] + choices: [ snapshot, access, nfs, smb_share, smb_client, network ] version_added: "1.9.0" account: description: @@ -344,6 +344,14 @@ options: type: str default: "" version_added: '1.14.0' + interfaces: + description: + - Specifies which product interfaces the network access policy rule + applies to, whether it is permitting or denying access. + type: list + elements: str + choices: [ "management-ssh", "management-rest-api", "management-web-ui", "snmp", "local-network-superuser-password-access" ] + version_added: '1.17.0' extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ @@ -546,6 +554,8 @@ try: SmbClientPolicyRule, SmbClientPolicy, ObjectStoreAccessPolicyPost, + NetworkAccessPolicy, + NetworkAccessPolicyRule, ) except ImportError: HAS_PYPURECLIENT = False @@ -577,6 +587,7 @@ NFS_POLICY_API_VERSION = "2.3" NFS_RENAME_API_VERSION = "2.4" SMB_POLICY_API_VERSION = "2.10" SMB_ENCRYPT_API_VERSION = "2.11" +NET_POLICY_API_VERSION = "2.13" def _convert_to_millisecs(hour): @@ -1065,6 +1076,51 @@ def create_smb_client_policy(module, blade): module.exit_json(changed=changed) +def create_network_access_policy(module, blade): + """Create Network Access Policy""" + changed = True + versions = blade.api_version.list_versions().versions + if not module.check_mode: + res = blade.post_network_access_policies(names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Failed to create network access policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + if not module.params["enabled"]: + res = blade.patch_network_access_policies( + policy=SmbClientPolicy(enabled=False), names=[module.params["name"]] + ) + if res.status_code != 200: + blade.delete_network_access_policies(names=[module.params["name"]]) + module.fail_json( + msg="Failed to create network access policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + if not module.params["client"]: + module.fail_json(msg="client is required to create a new rule") + else: + rule = NetworkAccessPolicyRule( + client=module.params["client"], + effect=module.params["effect"], + interfaces=module.params["interfaces"], + ) + res = blade.post_network_access_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to rule for policy {0}. Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) + ) + module.exit_json(changed=changed) + + def update_smb_client_policy(module, blade): """Update SMB Client Policy Rule""" @@ -1341,6 +1397,268 @@ def delete_nfs_policy(module, blade): module.exit_json(changed=changed) +def update_network_access_policy(module, blade): + """Update Networkk Access Policy Rule""" + + changed = False + if module.params["client"]: + current_policy_rule = blade.get_network_access_policies_rules( + policy_names=[module.params["name"]], + filter="client='" + module.params["client"] + "'", + ) + if ( + current_policy_rule.status_code == 200 + and current_policy_rule.total_item_count == 0 + ): + rule = NetworkAccessPolicyRule( + client=module.params["client"], + effect=module.params["effect"], + interfaces=module.params["interfaces"], + ) + changed = True + if not module.check_mode: + if module.params["before_rule"]: + before_name = ( + module.params["name"] + "." + str(module.params["before_rule"]) + ) + res = blade.post_network_access_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + before_rule_name=before_name, + ) + else: + res = blade.post_network_access_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to create rule for client {0} " + "in policy {1}. Error: {2}".format( + module.params["client"], + module.params["name"], + res.errors[0].message, + ) + ) + else: + rules = list(current_policy_rule.items) + cli_count = None + done = False + if module.params["client"] == "*": + for cli in range(0, len(rules)): + if rules[cli].client == "*": + cli_count = cli + if not cli_count: + rule = NetworkAccessPolicyRule( + client=module.params["client"], + effect=module.params["effect"], + interfaces=module.params["interfaces"], + ) + done = True + changed = True + if not module.check_mode: + if module.params["before_rule"]: + res = blade.post_network_access_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + before_rule_name=( + module.params["name"] + + "." + + str(module.params["before_rule"]), + ), + ) + else: + res = blade.post_network_access_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to create rule for " + "client {0} in policy {1}. Error: {2}".format( + module.params["client"], + module.params["name"], + res.errors[0].message, + ) + ) + if not done: + old_policy_rule = rules[0] + current_rule = { + "client": sorted(old_policy_rule.client), + "effect": old_policy_rule.effect, + "interfaces": old_policy_rule.interfaces, + } + if module.params["interfaces"]: + new_interfaces = module.params["interfaces"] + else: + new_interfaces = current_rule["interfaces"] + if module.params["effect"]: + new_effect = module.params["effect"] + else: + new_effect = current_rule["effect"] + if module.params["client"]: + new_client = sorted(module.params["client"]) + else: + new_client = sorted(current_rule["client"]) + new_rule = { + "client": new_client, + "effect": new_effect, + "interfaces": new_interfaces, + } + if current_rule != new_rule: + changed = True + if not module.check_mode: + rule = NetworkAccessPolicyRule( + client=module.params["client"], + effect=module.params["effect"], + interfaces=module.params["interfaces"], + ) + res = blade.patch_network_access_policies_rules( + names=[ + module.params["name"] + "." + str(old_policy_rule.index) + ], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to update network access client rule {0}. Error: {1}".format( + module.params["name"] + + "." + + str(old_policy_rule.index), + res.errors[0].message, + ) + ) + if ( + module.params["before_rule"] + and module.params["before_rule"] != old_policy_rule.index + ): + changed = True + if not module.check_mode: + before_name = ( + module.params["name"] + + "." + + str(module.params["before_rule"]) + ) + res = blade.patch_network_access_policies_rules( + names=[ + module.params["name"] + "." + str(old_policy_rule.index) + ], + rule=NetworkAccessPolicyRule(), + before_rule_name=before_name, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to move network access client rule {0}. Error: {1}".format( + module.params["name"] + + "." + + str(old_policy_rule.index), + res.errors[0].message, + ) + ) + current_policy = list( + blade.get_network_access_policies(names=[module.params["name"]]).items + )[0] + if current_policy.enabled != module.params["enabled"]: + changed = True + if not module.check_mode: + res = blade.patch_network_access_policies( + policy=NetworkAccessPolicy(enabled=module.params["enabled"]), + names=[module.params["name"]], + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to change state of network access policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + module.exit_json(changed=changed) + + +def delete_network_access_policy(module, blade): + """Delete Network Access Policy, or Rule + + If client is provided then delete the client rule if it exists. + """ + + changed = False + policy_delete = True + if module.params["client"]: + policy_delete = False + res = blade.get_network_access_policies_rules( + policy_names=[module.params["name"]], + filter="client='" + module.params["client"] + "'", + ) + if res.status_code == 200: + if res.total_item_count == 0: + pass + elif res.total_item_count == 1: + rule = list(res.items)[0] + if module.params["client"] == rule.client: + changed = True + if not module.check_mode: + res = blade.delete_network_access_policies_rules( + names=[rule.name] + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete rule for client {0} in policy {1}. " + "Error: {2}".format( + module.params["client"], + module.params["name"], + res.errors[0].message, + ) + ) + else: + rules = list(res.items) + for cli in range(0, len(rules)): + if rules[cli].client == "*": + changed = True + if not module.check_mode: + res = blade.delete_network_access_policies_rules( + names=[rules[cli].name] + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete rule for client {0} in policy {1}. " + "Error: {2}".format( + module.params["client"], + module.params["name"], + res.errors[0].message, + ) + ) + if policy_delete: + changed = True + if not module.check_mode: + res = blade.delete_network_Access_policies(names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete network access policy {0}. Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + module.exit_json(changed=changed) + + +def rename_network_access_policy(module, blade): + """Rename Network Access Policy""" + + changed = True + if not module.check_mode: + res = blade.patch_network_access_policies( + names=[module.params["name"]], + policy=NfsExportPolicy(name=module.params["rename"]), + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to rename network access policy {0} to {1}. Error: {2}".format( + module.params["name"], + module.params["rename"], + res.errors[0].message, + ) + ) + module.exit_json(changed=changed) + + def rename_nfs_policy(module, blade): """Rename NFS Export Policy""" @@ -2544,7 +2862,14 @@ def main(): policy_type=dict( type="str", default="snapshot", - choices=["snapshot", "access", "nfs", "smb_share", "smb_client"], + choices=[ + "snapshot", + "access", + "nfs", + "smb_share", + "smb_client", + "network", + ], ), enabled=dict(type="bool", default=True), timezone=dict(type="str"), @@ -2635,6 +2960,17 @@ def main(): choices=["disabled", "optional", "required"], ), desc=dict(type="str", default=""), + interfaces=dict( + type="list", + elements="str", + choices=[ + "management-ssh", + "management-rest-api", + "management-web-ui", + "snmp", + "local-network-superuser-password-access", + ], + ), ) ) @@ -2644,6 +2980,7 @@ def main(): ["policy_type", "nfs", ["name"]], ["policy_type", "smb_client", ["name"]], ["policy_type", "smb_share", ["name"]], + ["policy_type", "network", ["interfaces"]], ] module = AnsibleModule( @@ -2857,6 +3194,56 @@ def main(): create_smb_share_policy(module, blade) elif state == "absent" and policy: delete_smb_share_policy(module, blade) + elif module.params["policy_type"] == "network": + if NET_POLICY_API_VERSION not in versions: + module.fail_json( + msg=( + "Minimum FlashBlade REST version required: {0}".format( + NET_POLICY_API_VERSION + ) + ) + ) + if not HAS_PYPURECLIENT: + module.fail_json(msg="py-pure-client sdk is required for this module") + blade = get_system(module) + try: + policy = list( + blade.get_network_access_policies(names=[module.params["name"]]).items + )[0] + except AttributeError: + policy = None + if module.params["rename"]: + try: + new_policy = list( + blade.get_network_access_policies( + names=[module.params["rename"]] + ).items + )[0] + except AttributeError: + new_policy = None + if policy and state == "present" and not module.params["rename"]: + if module.params["before_rule"]: + res = blade.get_network_access_policies_rules( + policy_names=[module.params["name"]], + names=[ + module.params["name"] + "." + str(module.params["before_rule"]) + ], + ) + if res.status_code != 200: + module.fail_json( + msg="Rule index {0} does not exist.".format( + module.params["before_rule"] + ) + ) + update_network_access_policy(module, blade) + elif ( + state == "present" and module.params["rename"] and policy and not new_policy + ): + rename_network_access_policy(module, blade) + elif state == "present" and not policy and not module.params["rename"]: + create_network_access_policy(module, blade) + elif state == "absent" and policy: + delete_network_access_policy(module, blade) elif SNAPSHOT_POLICY_API_VERSION in versions: if not HAS_PYPURECLIENT: module.fail_json(msg="py-pure-client sdk is required for this module") |