Coverage for plugins/modules/purefb_s3acc_export.py: 97%
87 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# (c) 2026
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_s3acc_export
20version_added: '1.26.0'
21short_description: Manage FlashBlade Object Store Account exports
22description:
23- Create, update or delete a FlashBlade object-store account export.
24- An account export binds an object-store account to a server using an
25 S3 export policy, making the account's buckets visible for S3 through
26 that server.
27- The account export is uniquely identified by the (I(account), I(server))
28 pair.
29author:
30- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@everpuredata.com>
31options:
32 state:
33 description:
34 - Define whether the account export should exist or not.
35 default: present
36 choices: [ absent, present ]
37 type: str
38 account:
39 description:
40 - Name of the object store account to export.
41 type: str
42 required: true
43 server:
44 description:
45 - Name of the server the account will be exported on.
46 type: str
47 required: true
48 policy:
49 description:
50 - Name of the S3 export policy used for the export.
51 - Required when creating a new account export.
52 - When supplied on update, the export will be re-pointed at the
53 named policy.
54 type: str
55 enabled:
56 description:
57 - If C(true) the account export is enabled.
58 - Defaults to C(true) on creation if not specified.
59 type: bool
60 context:
61 description:
62 - Name of fleet member on which to perform the operation.
63 - This requires the array receiving the request is a member of a fleet
64 and the context name to be a member of the same fleet.
65 type: str
66 default: ""
67extends_documentation_fragment:
68- everpure.flashblade.everpure.fb
69"""
71EXAMPLES = r"""
72- name: Export account acme on server fb-01 using policy acme-export
73 everpure.flashblade.purefb_s3acc_export:
74 account: acme
75 server: fb-01
76 policy: acme-export
77 fb_url: 10.10.10.2
78 api_token: T-68618f31-0c9e-4e57-aa44-5306a2cf10e3
80- name: Disable an existing account export
81 everpure.flashblade.purefb_s3acc_export:
82 account: acme
83 server: fb-01
84 enabled: false
85 fb_url: 10.10.10.2
86 api_token: T-68618f31-0c9e-4e57-aa44-5306a2cf10e3
88- name: Re-point an existing account export at a different policy
89 everpure.flashblade.purefb_s3acc_export:
90 account: acme
91 server: fb-01
92 policy: acme-export-v2
93 fb_url: 10.10.10.2
94 api_token: T-68618f31-0c9e-4e57-aa44-5306a2cf10e3
96- name: Delete an account export
97 everpure.flashblade.purefb_s3acc_export:
98 account: acme
99 server: fb-01
100 state: absent
101 fb_url: 10.10.10.2
102 api_token: T-68618f31-0c9e-4e57-aa44-5306a2cf10e3
103"""
105RETURN = r"""
106"""
108HAS_PYPURECLIENT = True
109try:
110 from pypureclient.flashblade import (
111 ObjectStoreAccountExportPost,
112 ObjectStoreAccountExportPatch,
113 Reference,
114 )
115except ImportError:
116 HAS_PYPURECLIENT = False
118from ansible.module_utils.basic import AnsibleModule
119from ansible_collections.everpure.flashblade.plugins.module_utils.purefb import (
120 get_system,
121 purefb_argument_spec,
122)
123from ansible_collections.everpure.flashblade.plugins.module_utils.common import (
124 get_error_message,
125 get_rest_api_version,
126)
127from ansible_collections.everpure.flashblade.plugins.module_utils.version import (
128 LooseVersion,
129)
131MIN_REQUIRED_API_VERSION = "2.20"
134def _context_kwargs(module):
135 """Build context_names kwargs only when a context is supplied."""
136 if module.params["context"]:
137 return {"context_names": [module.params["context"]]}
138 return {}
141def _get_export(module, blade):
142 """Return the existing account export object or None."""
143 filter_string = "member.name='{0}' and server.name='{1}'".format(
144 module.params["account"], module.params["server"]
145 )
146 res = blade.get_object_store_account_exports(
147 filter=filter_string, **_context_kwargs(module)
148 )
149 if res.status_code != 200:
150 return None
151 items = list(res.items)
152 return items[0] if items else None
155def delete_export(module, blade, export):
156 """Delete an account export."""
157 changed = True
158 if not module.check_mode:
159 res = blade.delete_object_store_account_exports(
160 names=[getattr(export, "name", None)], **_context_kwargs(module)
161 )
162 if res.status_code != 200:
163 module.fail_json(
164 msg="Failed to delete account export for account {0} on server {1}. Error: {2}".format(
165 module.params["account"],
166 module.params["server"],
167 get_error_message(res),
168 )
169 )
170 module.exit_json(changed=changed)
173def create_export(module, blade):
174 """Create a new account export."""
175 if not module.params["policy"]:
176 module.fail_json(msg="policy is required when creating a new account export.")
177 changed = True
178 post_kwargs = {"server": Reference(name=module.params["server"])}
179 if module.params["enabled"] is not None:
180 post_kwargs["export_enabled"] = module.params["enabled"]
181 if not module.check_mode:
182 res = blade.post_object_store_account_exports(
183 member_names=[module.params["account"]],
184 policy_names=[module.params["policy"]],
185 object_store_account_export=ObjectStoreAccountExportPost(**post_kwargs),
186 **_context_kwargs(module),
187 )
188 if res.status_code != 200:
189 module.fail_json(
190 msg="Failed to create account export for account {0} on server {1}. Error: {2}".format(
191 module.params["account"],
192 module.params["server"],
193 get_error_message(res),
194 )
195 )
196 module.exit_json(changed=changed)
199def update_export(module, blade, export):
200 """Update an existing account export."""
201 changed = False
202 patch_kwargs = {}
204 if module.params["enabled"] is not None and module.params["enabled"] != getattr(
205 export, "enabled", None
206 ):
207 patch_kwargs["export_enabled"] = module.params["enabled"]
209 if module.params["policy"]:
210 current_policy = getattr(getattr(export, "policy", None), "name", None)
211 if module.params["policy"] != current_policy:
212 patch_kwargs["policy"] = Reference(name=module.params["policy"])
214 if patch_kwargs:
215 changed = True
216 if not module.check_mode:
217 res = blade.patch_object_store_account_exports(
218 names=[getattr(export, "name", None)],
219 object_store_account_export=ObjectStoreAccountExportPatch(
220 **patch_kwargs
221 ),
222 **_context_kwargs(module),
223 )
224 if res.status_code != 200:
225 module.fail_json(
226 msg="Failed to update account export for account {0} on server {1}. Error: {2}".format(
227 module.params["account"],
228 module.params["server"],
229 get_error_message(res),
230 )
231 )
233 module.exit_json(changed=changed)
236def main():
237 argument_spec = purefb_argument_spec()
238 argument_spec.update(
239 dict(
240 state=dict(type="str", default="present", choices=["absent", "present"]),
241 account=dict(type="str", required=True),
242 server=dict(type="str", required=True),
243 policy=dict(type="str"),
244 enabled=dict(type="bool"),
245 context=dict(type="str", default=""),
246 )
247 )
249 module = AnsibleModule(argument_spec, supports_check_mode=True)
251 if not HAS_PYPURECLIENT:
252 module.fail_json(msg="py-pure-client sdk is required for this module")
254 blade = get_system(module)
255 api_version = get_rest_api_version(blade)
256 if LooseVersion(MIN_REQUIRED_API_VERSION) > LooseVersion(api_version):
257 module.fail_json(
258 msg="FlashBlade REST version not supported. "
259 "Minimum version required: {0}".format(MIN_REQUIRED_API_VERSION)
260 )
262 if not module.params["context"]:
263 # Only default the context to the local array name when this array is a
264 # member of a fleet. A standalone array rejects any fleet context
265 # ("Cannot find array in fleet: not in a fleet"), so leave context
266 # unset - no context_names is then sent.
267 fleet_res = blade.get_fleets()
268 if fleet_res.status_code == 200 and list(fleet_res.items):
269 module.params["context"] = list(blade.get_arrays().items)[0].name
271 state = module.params["state"]
272 export = _get_export(module, blade)
274 if state == "present" and export is None:
275 create_export(module, blade)
276 elif state == "present" and export is not None:
277 update_export(module, blade, export)
278 elif state == "absent" and export is not None:
279 delete_export(module, blade, export)
281 module.exit_json(changed=False)
284if __name__ == "__main__":
285 main()