Coverage for plugins/modules/purefb_resource_access.py: 85%
59 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-30 17:06 -0400
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-30 17:06 -0400
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
4# Copyright: (c) 2026, Simon Dodsley (simon@everpuredata.com)
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
7from __future__ import absolute_import, division, print_function
9__metaclass__ = type
11ANSIBLE_METADATA = {
12 "metadata_version": "1.1",
13 "status": ["preview"],
14 "supported_by": "community",
15}
17DOCUMENTATION = r"""
18---
19module: purefb_resource_access
20version_added: '1.28.0'
21short_description: Manage Resource Access on Everpure FlashBlades
22description:
23 - Create or delete resource access on Everpure FlashBlades.
24author:
25 - Everpure Ansible Team (@sdodsley) <pure-ansible-team@everpuredata.com>
26 - Leo Wahlandt (@valen98) <wahlandtleo@gmail.com>
27options:
28 resource_type:
29 description:
30 - The type of resource you want to add, given as the REST collection
31 name of the resource.
32 - The set of valid resource types is defined by the array and grows
33 with the REST API version, so it is not validated by this module.
34 - For a realm scope, Purity//FB 4.6 accepts the array-level resources
35 that can be delegated to a realm, C(subnets) and C(dns).
36 - Policies (for example NFS export policies) live at the array level
37 and cannot be shared with a realm through a resource access; the
38 array rejects them with C(Invalid resource type).
39 type: str
40 required: true
41 resource_name:
42 description:
43 - The name of the resource you want to target.
44 type: str
45 required: true
46 state:
47 description:
48 - Define whether the resource access should exist or not.
49 type: str
50 default: present
51 choices: [ absent, present ]
52 scope_type:
53 description:
54 - The type of scopes.
55 - When using Realms, this value will be 'realms'
56 - As with I(resource_type), the set of valid scope types is defined
57 by the array and is not validated by this module.
58 type: str
59 required: true
60 scope_name:
61 description:
62 - The name of the scope, that should be using the resource.
63 - You can not rename a resource access.
64 - Resource access are immutable. You have to delete it and then create a new one.
65 type: str
66 required: true
67extends_documentation_fragment:
68- everpure.flashblade.everpure.fb
69"""
71EXAMPLES = r"""
72- name: Delegate subnet subnet_foo to realm realm_foo
73 everpure.flashblade.purefb_resource_access:
74 resource_type: subnets
75 resource_name: subnet_foo
76 scope_type: realms
77 scope_name: realm_foo
78 fb_url: 10.10.10.2
79 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
81- name: Share the management DNS configuration with realm realm_foo
82 everpure.flashblade.purefb_resource_access:
83 resource_type: dns
84 resource_name: management
85 scope_type: realms
86 scope_name: realm_foo
87 fb_url: 10.10.10.2
88 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
90- name: Revoke the subnet delegation (the subnet itself is not deleted)
91 everpure.flashblade.purefb_resource_access:
92 resource_type: subnets
93 resource_name: subnet_foo
94 scope_type: realms
95 scope_name: realm_foo
96 state: absent
97 fb_url: 10.10.10.2
98 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
99"""
101RETURN = r"""
102"""
104HAS_PYPURECLIENT = True
105try:
106 from pypureclient.flashblade import ResourceAccessPost
107except ImportError:
108 HAS_PYPURECLIENT = False
110from ansible.module_utils.basic import AnsibleModule
111from ansible_collections.everpure.flashblade.plugins.module_utils.purefb import (
112 get_system,
113 purefb_argument_spec,
114)
115from ansible_collections.everpure.flashblade.plugins.module_utils.version import (
116 LooseVersion,
117)
118from ansible_collections.everpure.flashblade.plugins.module_utils.common import (
119 get_error_message,
120 get_rest_api_version,
121)
123MINIMUM_API_VERSION = "2.19"
126def get_resource_access(module, blade):
127 """Get resource access"""
128 filter_string = (
129 "resource.resource_type='"
130 + module.params["resource_type"]
131 + "' and scope.name='"
132 + module.params["scope_name"]
133 + "' and scope.resource_type='"
134 + module.params["scope_type"]
135 + "'"
136 )
137 res = blade.get_resource_accesses(filter=filter_string)
138 if res.status_code == 200 and res.total_item_count:
139 items = list(res.items)
140 for item in items:
141 if item.resource.name == module.params["resource_name"]:
142 return True, item.id
143 return False, False
146def create_resource_access(module, blade):
147 """Create a new resource_access"""
148 changed = True
149 if not module.check_mode:
150 resource = ResourceAccessPost(
151 resource={
152 "name": module.params["resource_name"],
153 "resource_type": module.params["resource_type"],
154 },
155 scope={
156 "name": module.params["scope_name"],
157 "resource_type": module.params["scope_type"],
158 },
159 )
160 res = blade.post_resource_accesses_batch(items=[resource])
161 if res.status_code != 200:
162 module.fail_json(
163 msg="Create resource access between {0} and {1} failed. Error: {2}".format(
164 module.params["scope_name"],
165 module.params["resource_type"],
166 get_error_message(res),
167 )
168 )
169 module.exit_json(changed=changed)
172def delete_resource_access(module, blade, access_id):
173 """Delete resource access"""
174 changed = True
175 if not module.check_mode:
176 res = blade.delete_resource_accesses(ids=[access_id])
177 if res.status_code != 200:
178 module.fail_json(
179 msg="Delete resource access between {0} and {1} failed. Error: {2}".format(
180 module.params["scope_name"],
181 module.params["resource_type"],
182 get_error_message(res),
183 )
184 )
185 module.exit_json(changed=changed)
188def main():
189 argument_spec = purefb_argument_spec()
190 argument_spec.update(
191 dict(
192 resource_type=dict(type="str", required=True),
193 resource_name=dict(type="str", required=True),
194 state=dict(type="str", default="present", choices=["absent", "present"]),
195 scope_type=dict(type="str", required=True),
196 scope_name=dict(type="str", required=True),
197 )
198 )
200 module = AnsibleModule(argument_spec, supports_check_mode=True)
201 if not HAS_PYPURECLIENT:
202 module.fail_json(msg="py-pure-client sdk is required for this module")
204 state = module.params["state"]
205 blade = get_system(module)
206 api_version = get_rest_api_version(blade)
207 if LooseVersion(MINIMUM_API_VERSION) > LooseVersion(api_version):
208 module.fail_json(
209 msg="Resource Access is not supported. Purity//FB 4.6.1, or higher, is required."
210 )
211 resource, access_id = get_resource_access(module, blade)
213 if not resource and state == "present":
214 create_resource_access(module, blade)
215 elif resource and state == "absent":
216 delete_resource_access(module, blade, access_id)
218 module.exit_json(changed=False)
221if __name__ == "__main__":
222 main()