Coverage for plugins/modules/purefb_policy.py: 29%
1422 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-30 17:07 -0400
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-30 17:07 -0400
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
4# (c) 2020, 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_policy
20version_added: '1.0.0'
21short_description: Manage FlashBlade policies
22description:
23- Manage policies for filesystem, file replica links and object store access.
24- To update an existing snapshot policy rule, you must first delete the
25 original rule and then add the new rule to replace it. Purity's best-fit
26 will try to ensure that any required snapshots deleted on the deletion of
27 the first rule will be recovered as long replacement rule is added before
28 the snapshot eradication period is exceeded (usuually 24 hours).
29- Network access policies cannot be created or deleted on FlashBlade.
30 I(policy_type=network) manages the rules of an existing network access
31 policy (normally C(default-network-access-policy)).
32- Rule fields that are not set in a task keep their current value when an
33 existing rule is updated; the documented defaults are only applied when a
34 new rule is created.
35author:
36- Everpure Ansible Team (@sdodsley) <pure-ansible-team@everpuredata.com>
37options:
38 state:
39 description:
40 - Create or delete policy.
41 - I(copy) copies a single rule for Object Store Access Policies
42 (I(rule) and I(target) in C(account/policy) form are required).
43 - For snapshot, NFS, SMB and WORM policies I(copy) creates I(target) as a
44 new policy with the same settings and rules as I(name).
45 - Network access policies cannot be copied.
46 default: present
47 type: str
48 choices: [ absent, present, copy ]
49 target:
50 description:
51 - Name of policy to copy rule to, or name of the new policy to create
52 when copying a whole policy.
53 type: str
54 version_added: "1.9.0"
55 target_rule:
56 description:
57 - Name of the rule to copy the exisitng rule to.
58 - If not defined the existing rule name is used.
59 type: str
60 version_added: "1.9.0"
61 policy_type:
62 description:
63 - Type of policy
64 default: snapshot
65 type: str
66 choices: [ snapshot, access, nfs, smb_share, smb_client, network, worm ]
67 version_added: "1.9.0"
68 account:
69 description:
70 - Name of Object Store account policy applies to.
71 - B(Special Case) I(pure policy) is used for the system-wide S3 policies
72 type: str
73 version_added: "1.9.0"
74 rule:
75 description:
76 - Name of the rule for the Object Store Access Policy
77 - Rules in system wide policies cannot be deleted or modified
78 type: str
79 version_added: "1.9.0"
80 effect:
81 description:
82 - Allow S3 requests that match all of the I(actions) item selected.
83 Rules are additive.
84 - Also the effect of a network access policy rule.
85 - Defaults to I(allow) when a new rule is created.
86 type: str
87 choices: [ allow, deny ]
88 version_added: "1.9.0"
89 actions:
90 description:
91 - List of permissions to grant.
92 - System-wide policy rules cannot be deleted or modified
93 type: list
94 elements: str
95 choices:
96 - s3:*
97 - s3:AbortMultipartUpload
98 - s3:BypassGovernanceRetention
99 - s3:CreateBucket
100 - s3:DeleteBucket
101 - s3:DeleteObject
102 - s3:DeleteObjectVersion
103 - s3:ExtendSafemodeRetentionPeriod
104 - s3:GetBucketAcl
105 - s3:GetBucketLocation
106 - s3:GetBucketVersioning
107 - s3:GetLifecycleConfiguration
108 - s3:GetObject
109 - s3:GetObjectAcl
110 - s3:GetObjectLegalHold
111 - s3:GetObjectLockConfiguration
112 - s3:GetObjectRetention
113 - s3:GetObjectTagging
114 - s3:GetObjectVersion
115 - s3:GetObjectVersionTagging
116 - s3:ListAllMyBuckets
117 - s3:ListBucket
118 - s3:ListBucketMultipartUploads
119 - s3:ListBucketVersions
120 - s3:ListMultipartUploadParts
121 - s3:PutBucketVersioning
122 - s3:PutLifecycleConfiguration
123 - s3:PutObject
124 - s3:PutObjectLegalHold
125 - s3:PutObjectLockConfiguration
126 - s3:PutObjectRetention
127 - s3:ResolveSafemodeConflicts
128 version_added: "1.9.0"
129 object_resources:
130 description:
131 - List of bucket names and object paths, with a wildcard (*) to
132 specify objects in a bucket; e.g., bucket1, bucket1/*, bucket2,
133 bucket2/*.
134 - System-wide policy rules cannot be deleted or modified
135 type: list
136 elements: str
137 version_added: "1.9.0"
138 source_ips:
139 description:
140 - List of IPs and subnets from which this rule should allow requests;
141 e.g., 10.20.30.40, 10.20.30.0/24, 2001:DB8:1234:5678::/64.
142 - System-wide policy rules cannot be deleted or modified
143 type: list
144 elements: str
145 version_added: "1.9.0"
146 s3_prefixes:
147 description:
148 - List of 'folders' (object key prefixes) for which object listings
149 may be requested.
150 - System-wide policy rules cannot be deleted or modified
151 type: list
152 elements: str
153 version_added: "1.9.0"
154 s3_delimiters:
155 description:
156 - List of delimiter characters allowed in object list requests.
157 - Grants permissions to list 'folder names' (prefixes ending in a
158 delimiter) instead of object keys.
159 - System-wide policy rules cannot be deleted or modified
160 type: list
161 elements: str
162 version_added: "1.9.0"
163 ignore_enforcement:
164 description:
165 - Certain combinations of actions and other rule elements are inherently
166 ignored if specified together in a rule.
167 - If set to true, operations which attempt to set these combinations will fail.
168 - If set to false, such operations will instead be allowed.
169 type: bool
170 default: true
171 version_added: "1.9.0"
172 user:
173 description:
174 - User in the I(account) that the policy is granted to.
175 type: str
176 version_added: "1.9.0"
177 force_delete:
178 description:
179 - Force the deletion of a Object Store Access Policy is this
180 has attached users.
181 - WARNING This can have undesired side-effects.
182 - System-wide policies cannot be deleted
183 type: bool
184 default: false
185 version_added: "1.9.0"
186 name:
187 description:
188 - Name of the policy
189 type: str
190 enabled:
191 description:
192 - State of policy
193 type: bool
194 default: true
195 every:
196 description:
197 - Interval between snapshots in seconds
198 - Range available 300 - 31536000 (equates to 5m to 365d)
199 type: int
200 keep_for:
201 description:
202 - How long to keep snapshots for
203 - Range available 300 - 31536000 (equates to 5m to 365d)
204 - Must not be set less than I(every)
205 type: int
206 at:
207 description:
208 - Provide a time in 12-hour AM/PM format, eg. 11AM
209 type: str
210 timezone:
211 description:
212 - Time Zone used for the I(at) parameter
213 - If not provided, the module will attempt to get the current local timezone from the server
214 type: str
215 filesystem:
216 description:
217 - List of filesystems to add to a policy on creation
218 - To amend policy members use the I(everpure.flashblade.purefb_fs) module
219 type: list
220 elements: str
221 replica_link:
222 description:
223 - List of filesystem replica links to add to a policy on creation
224 - To amend policy members use the I(everpure.flashblade.purefb_fs_replica) module
225 type: list
226 elements: str
227 access:
228 description:
229 - Specifies access control for the export policy rule
230 - Defaults to I(root-squash) when a new rule is created.
231 type: str
232 choices: [ root-squash, all-squash, no-squash ]
233 version_added: "1.9.0"
234 anonuid:
235 description:
236 - Any user whose UID is affected by an I(access) of `root_squash` or `all_squash`
237 will have their UID mapped to anonuid.
238 The default is null, which means 65534.
239 type: int
240 version_added: "1.9.0"
241 anongid:
242 description:
243 - Any user whose GID is affected by an I(access) of `root_squash` or `all_squash`
244 will have their GID mapped to anongid.
245 The default anongid is null, which means 65534.
246 type: int
247 version_added: "1.9.0"
248 atime:
249 description:
250 - After a read operation has occurred, the inode access time is updated only if any
251 of the following conditions is true; the previous access time is less than the
252 inode modify time, the previous access time is less than the inode change time,
253 or the previous access time is more than 24 hours ago.
254 - If set to false, disables the update of inode access times after read operations.
255 - Defaults to true when a new rule is created.
256 type: bool
257 version_added: "1.9.0"
258 client:
259 description:
260 - Specifies the clients that will be permitted to access the export.
261 - Accepted notation is a single IP address, subnet in CIDR notation, netgroup, or
262 anonymous (*).
263 type: str
264 version_added: "1.9.0"
265 fileid_32bit:
266 description:
267 - Whether the file id is 32 bits or not.
268 - Defaults to false when a new rule is created.
269 type: bool
270 version_added: "1.9.0"
271 permission:
272 description:
273 - Specifies which read-write client access permissions are allowed for the export.
274 - Defaults to I(ro) when a new rule is created.
275 type: str
276 choices: [ rw, ro ]
277 version_added: "1.9.0"
278 secure:
279 description:
280 - If true, this prevents NFS access to client connections coming from non-reserved ports.
281 - If false, allows NFS access to client connections coming from non-reserved ports.
282 - Applies to NFSv3, NFSv4.1, and auxiliary protocols MOUNT and NLM.
283 - Defaults to false when a new rule is created.
284 type: bool
285 version_added: "1.9.0"
286 security:
287 description:
288 - The security flavors to use for accessing files on this mount point.
289 - If the server does not support the requested flavor, the mount operation fails.
290 - I(sys) trusts the client to specify users identity.
291 - I(krb) provides cryptographic proof of a users identity in each RPC request.
292 - I(krb5i) adds integrity checking to krb5, to ensure the data has not been tampered with.
293 - I(krb5p) adds integrity checking and encryption to krb5.
294 - Defaults to C([sys]) when a new rule is created.
295 type: list
296 elements: str
297 choices: [ sys, krb5, krb5i, krb5p ]
298 version_added: "1.9.0"
299 before_rule:
300 description:
301 - The index of the client rule to insert or move a client rule before.
302 - Not supported for SMB share policies, whose rules are unordered.
303 type: int
304 version_added: "1.9.0"
305 rename:
306 description:
307 - New name for policy
308 - Applies to snapshot, NFS, SMB and network access policies.
309 - WORM data policies cannot be renamed.
310 - Purity//FB up to at least 4.6.4 (REST 2.21) ignores a new name for
311 snapshot policies; the module fails instead of reporting a rename
312 the array did not perform.
313 type: str
314 version_added: "1.10.0"
315 destroy_snapshots:
316 description:
317 - This parameter must be set to true in order to modify a policy such that local or remote snapshots would be destroyed.
318 type: bool
319 version_added: '1.11.0'
320 default: false
321 principal:
322 description:
323 - The user or group who is the subject of this rule, and their domain
324 type: str
325 version_added: '1.12.0'
326 change:
327 description:
328 - The state of the SMB share principals Change access permission.
329 - Setting to "" will clear the current setting
330 - Share permissions form the ladder I(read), I(change), I(full_control)
331 with a single effect per rule. Setting a level applies its effect to
332 the levels below it; a level set to the opposite effect above the
333 granted ones, or to "", is not granted, as the array stores it.
334 type: str
335 choices: [ allow, deny, "" ]
336 version_added: '1.12.0'
337 read:
338 description:
339 - The state of the SMB share principals Read access permission.
340 - Setting to "" will clear the current setting
341 - See I(change) for how the share permission levels interact.
342 type: str
343 choices: [ allow, deny, "" ]
344 version_added: '1.12.0'
345 full_control:
346 description:
347 - The state of the SMB share principals Full Control access permission.
348 - Setting to "" will clear the current setting
349 - See I(change) for how the share permission levels interact.
350 type: str
351 choices: [ allow, deny, "" ]
352 version_added: '1.12.0'
353 smb_encryption:
354 description:
355 - The status of SMB encryption in a client policy rule
356 - Defaults to I(optional) when a new rule is created.
357 type: str
358 choices: [ disabled, optional, required ]
359 version_added: '1.12.0'
360 desc:
361 description:
362 - A description of an object store policy,
363 optionally specified when the policy is created.
364 - Cannot be modified for an existing policy.
365 type: str
366 default: ""
367 version_added: '1.14.0'
368 interfaces:
369 description:
370 - Specifies which product interfaces the network access policy rule
371 applies to, whether it is permitting or denying access.
372 - Required for I(policy_type=network); only used when a rule is created
373 or updated.
374 type: list
375 elements: str
376 choices: [ "management-ssh", "management-rest-api", "management-web-ui", "snmp", "local-network-superuser-password-access" ]
377 version_added: '1.17.0'
378 max_retention:
379 description:
380 - The maximum retention period of the WORM file system.
381 - Between 1 second and 100 years.
382 - Cannot be less than the I(min_retention).
383 - Valid values are weeks (w), days(d), hours(h), minutes(m) and seconds(s).
384 type: str
385 version_added: '1.19.0'
386 min_retention:
387 description:
388 - The minimum retention period of the WORM file system.
389 - Between 1 second and 100 years.
390 - Cannot be greater than the I(max_retention).
391 - Valid values are weeks (w), days(d), hours(h), minutes(m) and seconds(s).
392 type: str
393 version_added: '1.19.0'
394 default_retention:
395 description:
396 - The retention period used for committing files to WORM status.
397 Will be applied if no access time is provided, or the access time is less than the current server time.
398 Between I(min_retention) and I(max_retention) periods.
399 - Valid values are weeks (w), days(d), hours(h), minutes(m) and seconds(s).
400 type: str
401 version_added: '1.19.0'
402 retention_lock:
403 description:
404 - State of policy attributes after creation.
405 - If set to I(locked) then values of the policy attributes are not allowed to change.
406 - If set to I(locked) then values of the policy attributes can be changed.
407 - Changing from I(unlocked) to I(locked) is allowed, but to change from I(locked) to I(unlocked)
408 will require support from Everpure Technical Services.
409 type: str
410 choices: [ locked, unlocked ]
411 version_added: '1.19.0'
412 context:
413 description:
414 - Name of fleet member on which to perform the operation.
415 - This requires the array receiving the request is a member of a fleet
416 and the context name to be a member of the same fleet.
417 type: str
418 default: ""
419 version_added: "1.22.0"
420extends_documentation_fragment:
421- everpure.flashblade.everpure.fb
422"""
424EXAMPLES = r"""
425- name: Create a simple snapshot policy with no rules
426 everpure.flashblade.purefb_policy:
427 name: test_policy
428 policy_type: snapshot
429 fb_url: 10.10.10.2
430 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
431- name: Create a snapshot policy and connect to existing filesystems and filesystem replica links
432 everpure.flashblade.purefb_policy:
433 name: test_policy_with_members
434 policy_type: snapshot
435 filesystem:
436 - fs1
437 - fs2
438 replica_link:
439 - rl1
440 - rl2
441 fb_url: 10.10.10.2
442 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
443- name: Create a snapshot policy with rules
444 everpure.flashblade.purefb_policy:
445 name: test_policy2
446 policy_type: snapshot
447 at: 11AM
448 keep_for: 86400
449 every: 86400
450 timezone: Asia/Shanghai
451 fb_url: 10.10.10.2
452 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
453- name: Delete filesystem foo from snapshot policy
454 everpure.flashblade.purefb_policy:
455 name: test_policy
456 policy_type: snapshot
457 filesystem: foo
458 state: absent
459 fb_url: 10.10.10.2
460 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
461- name: Delete a snapshot policy
462 everpure.flashblade.purefb_policy:
463 name: test_policy
464 policy_type: snapshot
465 state: absent
466 fb_url: 10.10.10.2
467 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
468- name: Create an empty object store access policy
469 everpure.flashblade.purefb_policy:
470 name: test_os_policy
471 account: test
472 policy_type: access
473 fb_url: 10.10.10.2
474 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
475- name: Create an empty object store access policy and assign user
476 everpure.flashblade.purefb_policy:
477 name: test_os_policy
478 account: test
479 policy_type: access
480 user: fred
481 fb_url: 10.10.10.2
482 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
483- name: Create a object store access policy with simple rule
484 everpure.flashblade.purefb_policy:
485 name: test_os_policy_rule
486 policy_type: access
487 account: test
488 rule: rule1
489 actions: "s3:*"
490 object_resources: "*"
491 fb_url: 10.10.10.2
492 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
493- name: Create an empty SMB client policy
494 everpure.flashblade.purefb_policy:
495 name: test_smb_client
496 policy_type: smb_client
497 fb_url: 10.10.10.2
498 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
499- name: Create an SMB client policy with a client rule
500 everpure.flashblade.purefb_policy:
501 name: test_smb_client
502 policy_type: smb_client
503 client: "10.0.1.0/24"
504 permission: rw
505 fb_url: 10.10.10.2
506 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
507- name: Create an empty NFS export policy
508 everpure.flashblade.purefb_policy:
509 name: test_nfs_export
510 policy_type: nfs
511 fb_url: 10.10.10.2
512 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
513- name: Create an NFS export policy with a client rule
514 everpure.flashblade.purefb_policy:
515 name: test_nfs_export
516 policy_type: nfs
517 atime: true
518 client: "10.0.1.0/24"
519 secure: true
520 security: [sys, krb5]
521 permission: rw
522 fb_url: 10.10.10.2
523 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
524- name: Create a new rule for an existing NFS export policy
525 everpure.flashblade.purefb_policy:
526 name: test_nfs_export
527 policy_type: nfs
528 atime: true
529 client: "10.0.2.0/24"
530 security: sys
531 permission: ro
532 fb_url: 10.10.10.2
533 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
534- name: Delete a client rule from an NFS export policy
535 everpure.flashblade.purefb_policy:
536 name: test_nfs_export
537 client: "10.0.1.0/24"
538 policy_type: nfs
539 state: absent
540 fb_url: 10.10.10.2
541 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
542- name: Delete an NFS export policy and all associated rules
543 everpure.flashblade.purefb_policy:
544 name: test_nfs_export
545 state: absent
546 policy_type: nfs
547 fb_url: 10.10.10.2
548 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
549- name: Delete a rule from an object store access policy
550 everpure.flashblade.purefb_policy:
551 name: test_os_policy_rule
552 account: test
553 policy_type: access
554 rule: rule1
555 state: absent
556 fb_url: 10.10.10.2
557 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
558- name: Delete a user from an object store access policy
559 everpure.flashblade.purefb_policy:
560 name: test_os_policy_rule
561 account: test
562 user: fred
563 policy_type: access
564 state: absent
565 fb_url: 10.10.10.2
566 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
567- name: Delete an object store access policy with attached users (USE WITH CAUTION)
568 everpure.flashblade.purefb_policy:
569 name: test_os_policy_rule
570 account: test
571 policy_type: access
572 force_delete: true
573 state: absent
574 fb_url: 10.10.10.2
575 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
576- name: Delete an object store access policy with no attached users
577 everpure.flashblade.purefb_policy:
578 name: test_os_policy_rule
579 account: test
580 policy_type: access
581 state: absent
582 fb_url: 10.10.10.2
583 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
584- name: Copy an object store access policy rule to another exisitng policy
585 everpure.flashblade.purefb_policy:
586 name: test_os_policy_rule
587 policy_type: access
588 account: test
589 target: "account2/anotherpolicy"
590 target_rule: new_rule1
591 state: copy
592 fb_url: 10.10.10.2
593 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
594- name: Rename an NFS Export Policy
595 everpure.flashblade.purefb_policy:
596 name: old_name
597 policy_type: nfs
598 rename: new_name
599 fb_url: 10.10.10.2
600 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
601- name: Copy an NFS Export Policy, including its rules, to a new policy
602 everpure.flashblade.purefb_policy:
603 name: test_nfs_export
604 policy_type: nfs
605 target: test_nfs_export_copy
606 state: copy
607 fb_url: 10.10.10.2
608 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
609- name: Add a rule to the default network access policy
610 everpure.flashblade.purefb_policy:
611 name: default-network-access-policy
612 policy_type: network
613 client: 10.0.1.0/24
614 effect: deny
615 interfaces:
616 - management-ssh
617 fb_url: 10.10.10.2
618 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
619- name: Create a WORM Data Policy
620 everpure.flashblade.purefb_policy:
621 name: worm1
622 policy_type: worm
623 default_retention: 5d
624 min_rentetion: 20h
625 max_retention: 1y
626 fb_url: 10.10.10.2
627 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6
628"""
630RETURN = r"""
631"""
633HAS_PYPURECLIENT = True
634try:
635 from pypureclient.flashblade import (
636 PolicyRuleObjectAccessCondition,
637 PolicyRuleObjectAccessPost,
638 NfsExportPolicy,
639 NfsExportPolicyRule,
640 Policy,
641 SnapshotPolicyPatch,
642 PolicyRule,
643 SmbSharePolicyRule,
644 SmbSharePolicy,
645 SmbClientPolicyRule,
646 SmbClientPolicy,
647 ObjectStoreAccessPolicyPost,
648 NetworkAccessPolicy,
649 NetworkAccessPolicyRule,
650 WormDataPolicy,
651 )
652except ImportError:
653 HAS_PYPURECLIENT = False
655HAS_PYTZ = True
656try:
657 import pytz
658except ImportError:
659 HAS_PYTZ = False
661import os
662import re
663import platform
665from ansible.module_utils.common.process import get_bin_path
666from ansible.module_utils.facts.utils import get_file_content
667from ansible.module_utils.basic import AnsibleModule
668from ansible_collections.everpure.flashblade.plugins.module_utils.purefb import (
669 get_system,
670 purefb_argument_spec,
671)
672from ansible_collections.everpure.flashblade.plugins.module_utils.version import (
673 LooseVersion,
674)
675from ansible_collections.everpure.flashblade.plugins.module_utils.common import (
676 _findstr,
677 get_error_message,
678 get_rest_api_version,
679)
680from ansible_collections.everpure.flashblade.plugins.module_utils.time_utils import (
681 time_to_milliseconds,
682)
684NFS_POLICY_API_VERSION = "2.3"
685NFS_RENAME_API_VERSION = "2.4"
686SMB_POLICY_API_VERSION = "2.10"
687SMB_ENCRYPT_API_VERSION = "2.11"
688NET_POLICY_API_VERSION = "2.13"
689WORM_POLICY_API_VERSION = "2.15"
690CONTEXT_API_VERSION = "2.17"
691# From REST 2.22 the NFS export rule anonuid/anongid are integers on the wire;
692# older arrays declare and accept them only as strings.
693NFS_ANON_INT_API_VERSION = "2.22"
696def _get_local_tz(module, timezone="UTC"):
697 """
698 We will attempt to get the local timezone of the server running the module and use that.
699 If we can't get the timezone then we will set the default to be UTC
701 Linnux has been tested and other opersting systems should be OK.
702 Failures cause assumption of UTC
704 Windows is not supported and will assume UTC
705 """
706 if platform.system() == "Linux":
707 timedatectl = get_bin_path("timedatectl")
708 if timedatectl is not None:
709 rcode, stdout, stderr = module.run_command(timedatectl)
710 if rcode == 0 and stdout:
711 line = _findstr(stdout, "Time zone")
712 full_tz = line.split(":", 1)[1].rstrip()
713 timezone = full_tz.split()[0]
714 return timezone
715 else:
716 module.warn("Incorrect timedatectl output. Timezone will be set to UTC")
717 else:
718 if os.path.exists("/etc/timezone"):
719 timezone = get_file_content("/etc/timezone")
720 else:
721 module.warn("Could not find /etc/timezone. Assuming UTC")
723 elif platform.system() == "SunOS":
724 if os.path.exists("/etc/default/init"):
725 for line in get_file_content("/etc/default/init", "").splitlines():
726 if line.startswith("TZ="):
727 timezone = line.split("=", 1)[1]
728 return timezone
729 else:
730 module.warn("Could not find /etc/default/init. Assuming UTC")
732 elif re.match("^Darwin", platform.platform()):
733 systemsetup = get_bin_path("systemsetup")
734 if systemsetup is not None:
735 rcode, stdout, stderr = module.execute(systemsetup, "-gettimezone")
736 if rcode == 0 and stdout:
737 timezone = stdout.split(":", 1)[1].lstrip()
738 else:
739 module.warn("Could not run systemsetup. Assuming UTC")
740 else:
741 module.warn("Could not find systemsetup. Assuming UTC")
743 elif re.match("^(Free|Net|Open)BSD", platform.platform()):
744 if os.path.exists("/etc/timezone"):
745 timezone = get_file_content("/etc/timezone")
746 else:
747 module.warn("Could not find /etc/timezone. Assuming UTC")
749 elif platform.system() == "AIX":
750 aix_oslevel = int(platform.version() + platform.release())
751 if aix_oslevel >= 61:
752 if os.path.exists("/etc/environment"):
753 for line in get_file_content("/etc/environment", "").splitlines():
754 if line.startswith("TZ="):
755 timezone = line.split("=", 1)[1]
756 return timezone
757 else:
758 module.warn("Could not find /etc/environment. Assuming UTC")
759 else:
760 module.warn(
761 "Cannot determine timezone when AIX os level < 61. Assuming UTC"
762 )
764 else:
765 module.warn("Could not find /etc/timezone. Assuming UTC")
767 return timezone
770def _context_kwargs(module, versions):
771 """Return the ``context_names`` keyword for fleet-aware SDK calls.
773 An empty dict is returned when the array is below the context API version
774 or no context is set, so callers can always splat the result.
775 """
776 if (
777 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
778 and module.params["context"]
779 ):
780 return {"context_names": [module.params["context"]]}
781 return {}
784def _default(value, default):
785 """Return I(default) when the task did not set I(value)."""
786 return default if value is None else value
789def _blank_to_none(value):
790 """Treat "" (used by the task to clear a setting) the same as unset."""
791 return None if value == "" else value
794def _sorted_or_none(values):
795 """Sorted copy of a list for order-insensitive comparison; [] is unset."""
796 return sorted(values) if values else None
799# SMB share permissions, lowest level first.
800_SHARE_LEVELS = ("read", "change", "full_control")
803def _share_rule_perms(current, requested):
804 """Return the permissions the array stores once I(requested) is applied.
806 Purity//FB keeps an SMB share rule as one access control entry: the
807 permissions form the ladder read < change < full_control, every granted
808 level carries the single effect of the rule and the levels above the
809 highest granted one are reported as None. Setting a level grants (or
810 denies) it together with every level below it. A level set to the
811 opposite effect above the granted ones (``full_control=deny`` next to
812 ``read``/``change`` ``allow``), or set to "", is simply not granted, like
813 everything above it. Levels the task leaves unset keep their current
814 value.
815 """
816 merged = {}
817 for level in _SHARE_LEVELS:
818 value = requested.get(level)
819 if value is None:
820 value = current.get(level)
821 merged[level] = _blank_to_none(value)
822 # The task's own values decide the effect; unset levels only inherit.
823 effect = None
824 explicit_top = -1
825 for index, level in enumerate(_SHARE_LEVELS):
826 value = requested.get(level)
827 if value is None:
828 continue
829 if effect is None and value in ("allow", "deny"):
830 effect = value
831 if value != effect:
832 break
833 explicit_top = index
834 if effect is None:
835 for level in _SHARE_LEVELS:
836 if merged[level] in ("allow", "deny"):
837 effect = merged[level]
838 break
839 result = {}
840 granted = effect is not None
841 for index, level in enumerate(_SHARE_LEVELS):
842 value = effect if index <= explicit_top else merged[level]
843 granted = granted and value == effect
844 result[level] = effect if granted else None
845 return result
848def _share_rule_desired(module, live_perms):
849 """Permissions the task asks for, applied over I(live_perms)."""
850 perms = _share_rule_perms(
851 live_perms, {level: module.params[level] for level in _SHARE_LEVELS}
852 )
853 if not any(perms.values()):
854 module.fail_json(
855 msg="SMB share rule for principal {0} must allow or deny at least "
856 "read; use state=absent to remove the rule".format(
857 module.params["principal"]
858 )
859 )
860 return perms
863def _share_rule_top(perms):
864 """Return (effect, index of the highest granted level) of a rule."""
865 top = max(
866 index for index, level in enumerate(_SHARE_LEVELS) if perms[level] is not None
867 )
868 return perms["read"], top
871def _share_rule_patch(current, desired):
872 """Return the one-field PATCH body that turns I(current) into I(desired).
874 The array grants (or denies) a level together with every level below it
875 and drops the levels above it when the effect changes, so one field is
876 enough: the new top level with its effect, or "" on the level above the
877 new top when the rule keeps its effect and only loses levels.
878 """
879 current_effect, current_top = _share_rule_top(current)
880 new_effect, new_top = _share_rule_top(desired)
881 if new_effect != current_effect or new_top > current_top:
882 return {_SHARE_LEVELS[new_top]: new_effect}
883 return {_SHARE_LEVELS[new_top + 1]: ""}
886def _anon_id(value):
887 """Normalise anonuid/anongid for comparison.
889 The array reports the ids as integers, but a PATCH body carries them as
890 strings (see _nfs_rule), so compare them as int. None and "" both mean
891 "not set".
892 """
893 if value is None or value == "":
894 return None
895 return int(value)
898def _nfs_rule(versions, patch=False, **fields):
899 """Build the request body of an NFS export policy rule.
901 The array accepts anonuid/anongid as integers on POST and reports them as
902 integers, but rejects integers on PATCH ("invalid value for anonuid") and
903 takes only strings there. This holds on every REST version tested (2.21
904 and 2.24), so a PATCH body is always sent as a plain dict, which the SDK
905 passes through unchanged, with the ids rendered as strings.
907 A POST body uses the SDK's ``NfsExportPolicyRule`` from REST 2.22, where
908 the model declares the ids as integers; below that the model still
909 declares strings and rejects integers, so a dict with string ids is sent
910 instead. Fields set to None are left out either way.
911 """
912 fields = {key: value for key, value in fields.items() if value is not None}
913 if not patch and LooseVersion(NFS_ANON_INT_API_VERSION) <= LooseVersion(versions):
914 return NfsExportPolicyRule(**fields)
915 for key in ("anonuid", "anongid"):
916 if key in fields:
917 fields[key] = str(fields[key])
918 return fields
921def _nfs_rule_from_params(module, versions):
922 """Build a new NFS export rule from the task.
924 The array defaults are applied to any rule field the task did not set.
925 """
926 return _nfs_rule(
927 versions,
928 client=module.params["client"],
929 permission=_default(module.params["permission"], "ro"),
930 access=_default(module.params["access"], "root-squash"),
931 anonuid=_anon_id(module.params["anonuid"]),
932 anongid=_anon_id(module.params["anongid"]),
933 fileid_32bit=_default(module.params["fileid_32bit"], False),
934 atime=_default(module.params["atime"], True),
935 secure=_default(module.params["secure"], False),
936 security=_default(module.params["security"], ["sys"]),
937 )
940def _smb_client_rule_from_params(module, versions):
941 """Build a new SMB client rule from the task.
943 The array defaults are applied to any rule field the task did not set.
944 """
945 if LooseVersion(SMB_ENCRYPT_API_VERSION) <= LooseVersion(versions):
946 return SmbClientPolicyRule(
947 client=module.params["client"],
948 permission=_default(module.params["permission"], "ro"),
949 encryption=_default(module.params["smb_encryption"], "optional"),
950 )
951 return SmbClientPolicyRule(
952 client=module.params["client"],
953 permission=_default(module.params["permission"], "ro"),
954 )
957def delete_smb_share_policy(module, blade):
958 """Delete SMB Share Policy, or Rule
960 If principal is provided then delete the principal rule if it exists.
961 """
963 changed = False
964 versions = get_rest_api_version(blade)
965 policy_delete = True
966 if module.params["principal"]:
967 policy_delete = False
968 if (
969 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
970 and module.params["context"]
971 ):
972 prin_rule = blade.get_smb_share_policies_rules(
973 policy_names=[module.params["name"]],
974 filter="principal='" + module.params["principal"] + "'",
975 context_names=[module.params["context"]],
976 )
977 else:
978 prin_rule = blade.get_smb_share_policies_rules(
979 policy_names=[module.params["name"]],
980 filter="principal='" + module.params["principal"] + "'",
981 )
982 # A filter that matches nothing is a 200 with no items, so guard the
983 # lookup or the second delete of the same principal raises IndexError.
984 if prin_rule.status_code == 200 and prin_rule.total_item_count != 0:
985 rule = list(prin_rule.items)[0]
986 changed = True
987 if not module.check_mode:
988 # Share rule names are the principal, unique only within the
989 # policy, so the policy must be named as well.
990 res = blade.delete_smb_share_policies_rules(
991 policy_names=[module.params["name"]],
992 names=[rule.name],
993 **_context_kwargs(module, versions),
994 )
995 if res.status_code != 200:
996 module.fail_json(
997 msg="Failed to delete rule for principal {0} in policy {1}. "
998 "Error: {2}".format(
999 module.params["principal"],
1000 module.params["name"],
1001 get_error_message(res),
1002 )
1003 )
1004 if policy_delete:
1005 changed = True
1006 if not module.check_mode:
1007 if (
1008 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1009 and module.params["context"]
1010 ):
1011 res = blade.delete_smb_share_policies(
1012 names=[module.params["name"]],
1013 context_names=[module.params["context"]],
1014 )
1015 else:
1016 res = blade.delete_smb_share_policies(names=[module.params["name"]])
1017 if res.status_code != 200:
1018 module.fail_json(
1019 msg="Failed to delete SMB share policy {0}. Error: {1}".format(
1020 module.params["name"], get_error_message(res)
1021 )
1022 )
1023 module.exit_json(changed=changed)
1026def rename_smb_share_policy(module, blade):
1027 """Rename SMB Share Policy"""
1029 changed = True
1030 versions = get_rest_api_version(blade)
1031 if not module.check_mode:
1032 if (
1033 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1034 and module.params["context"]
1035 ):
1036 res = blade.patch_smb_share_policies(
1037 names=[module.params["name"]],
1038 policy=SmbSharePolicy(name=module.params["rename"]),
1039 context_names=[module.params["context"]],
1040 )
1041 else:
1042 res = blade.patch_smb_share_policies(
1043 names=[module.params["name"]],
1044 policy=SmbSharePolicy(name=module.params["rename"]),
1045 )
1046 if res.status_code != 200:
1047 module.fail_json(
1048 msg="Failed to rename SMB share policy {0} to {1}. Error: {2}".format(
1049 module.params["name"],
1050 module.params["rename"],
1051 get_error_message(res),
1052 )
1053 )
1054 module.exit_json(changed=changed)
1057def create_smb_share_policy(module, blade):
1058 """Create SMB Share Policy"""
1059 changed = True
1060 versions = get_rest_api_version(blade)
1061 if not module.check_mode:
1062 if (
1063 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1064 and module.params["context"]
1065 ):
1066 res = blade.post_smb_share_policies(
1067 names=[module.params["name"]], context_names=[module.params["context"]]
1068 )
1069 else:
1070 res = blade.post_smb_share_policies(names=[module.params["name"]])
1071 if res.status_code != 200:
1072 module.fail_json(
1073 msg="Failed to create SMB share policy {0}.Error: {1}".format(
1074 module.params["name"], get_error_message(res)
1075 )
1076 )
1077 if not module.params["enabled"]:
1078 if (
1079 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1080 and module.params["context"]
1081 ):
1082 res = blade.patch_smb_share_policies(
1083 policy=SmbSharePolicy(enabled=False),
1084 names=[module.params["name"]],
1085 context_names=[module.params["context"]],
1086 )
1087 else:
1088 res = blade.patch_smb_share_policies(
1089 policy=SmbSharePolicy(enabled=False), names=[module.params["name"]]
1090 )
1091 if res.status_code != 200:
1092 if (
1093 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1094 and module.params["context"]
1095 ):
1096 blade.delete_smb_share_policies(
1097 names=[module.params["name"]],
1098 context_names=[module.params["context"]],
1099 )
1100 else:
1101 blade.delete_smb_share_policies(names=[module.params["name"]])
1102 module.fail_json(
1103 msg="Failed to create SMB share policy {0}.Error: {1}".format(
1104 module.params["name"], get_error_message(res)
1105 )
1106 )
1107 if not module.params["principal"]:
1108 module.fail_json(msg="principal is required to create a new rule")
1109 else:
1110 # Send the permissions as the array stores them (see
1111 # _share_rule_perms); the raw task values can mix allow and deny,
1112 # which POST rejects.
1113 perms = _share_rule_desired(module, {})
1114 rule = SmbSharePolicyRule(
1115 principal=module.params["principal"],
1116 **{level: value for level, value in perms.items() if value},
1117 )
1118 if (
1119 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1120 and module.params["context"]
1121 ):
1122 res = blade.post_smb_share_policies_rules(
1123 policy_names=[module.params["name"]],
1124 rule=rule,
1125 context_names=[module.params["context"]],
1126 )
1127 else:
1128 res = blade.post_smb_share_policies_rules(
1129 policy_names=[module.params["name"]],
1130 rule=rule,
1131 )
1132 if res.status_code != 200:
1133 module.fail_json(
1134 msg="Failed to create rule for policy {0}. Error: {1}".format(
1135 module.params["name"],
1136 get_error_message(res),
1137 )
1138 )
1139 module.exit_json(changed=changed)
1142def update_smb_share_policy(module, blade):
1143 """Update SMB Share Policy Rule"""
1145 changed = False
1146 versions = get_rest_api_version(blade)
1147 if module.params["principal"]:
1148 if (
1149 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1150 and module.params["context"]
1151 ):
1152 current_policy_rule = blade.get_smb_share_policies_rules(
1153 policy_names=[module.params["name"]],
1154 filter="principal='" + module.params["principal"] + "'",
1155 context_names=[module.params["context"]],
1156 )
1157 else:
1158 current_policy_rule = blade.get_smb_share_policies_rules(
1159 policy_names=[module.params["name"]],
1160 filter="principal='" + module.params["principal"] + "'",
1161 )
1162 if current_policy_rule.status_code != 200:
1163 module.fail_json(
1164 msg="Failed to get SMB export policy rules for {0}. Error: {1}".format(
1165 module.params["name"],
1166 current_policy_rule.errors[0].message,
1167 )
1168 )
1169 elif (
1170 current_policy_rule.status_code == 200
1171 and current_policy_rule.total_item_count == 0
1172 ):
1173 perms = _share_rule_desired(module, {})
1174 rule = SmbSharePolicyRule(
1175 principal=module.params["principal"],
1176 **{level: value for level, value in perms.items() if value},
1177 )
1178 changed = True
1179 if not module.check_mode:
1180 # Share rules are unordered; the SDK has no before_rule_name
1181 # for them.
1182 res = blade.post_smb_share_policies_rules(
1183 policy_names=[module.params["name"]],
1184 rule=rule,
1185 **_context_kwargs(module, versions),
1186 )
1187 if res.status_code != 200:
1188 module.fail_json(
1189 msg="Failed to create rule for principal {0} "
1190 "in policy {1}. Error: {2}".format(
1191 module.params["principal"],
1192 module.params["name"],
1193 get_error_message(res),
1194 )
1195 )
1196 else:
1197 old_policy_rule = list(current_policy_rule.items)[0]
1198 # The array reports a rule as its ladder of granted levels (see
1199 # _share_rule_perms), so both sides are normalised: the task's
1200 # full_control=deny next to read/change=allow equals the None the
1201 # array stores for "not granted". The principal is not compared;
1202 # the filter above matched it (case-insensitively) and PATCH
1203 # cannot change it.
1204 live_perms = {
1205 level: getattr(old_policy_rule, level) for level in _SHARE_LEVELS
1206 }
1207 current_perms = _share_rule_perms(live_perms, {})
1208 new_perms = _share_rule_desired(module, live_perms)
1209 if new_perms != current_perms:
1210 changed = True
1211 if not module.check_mode:
1212 # PATCH smb-share-policies/rules rejects `principal`
1213 # ("Invalid body parameter: principal") and bodies that
1214 # mix allow and deny, so send the single field that moves
1215 # the ladder (see _share_rule_patch).
1216 rule = SmbSharePolicyRule(
1217 **_share_rule_patch(current_perms, new_perms)
1218 )
1219 # Share rules are unordered and carry no index: the rule
1220 # is named after its principal, which is only unique
1221 # within the policy, so address it by name and policy.
1222 res = blade.patch_smb_share_policies_rules(
1223 policy_names=[module.params["name"]],
1224 names=[old_policy_rule.name],
1225 rule=rule,
1226 **_context_kwargs(module, versions),
1227 )
1228 if res.status_code != 200:
1229 module.fail_json(
1230 msg="Failed to update SMB share rule {0} in policy {1}. "
1231 "Error: {2}".format(
1232 old_policy_rule.name,
1233 module.params["name"],
1234 get_error_message(res),
1235 )
1236 )
1237 if (
1238 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1239 and module.params["context"]
1240 ):
1241 current_policy = list(
1242 blade.get_smb_share_policies(
1243 names=[module.params["name"]], context_names=[module.params["context"]]
1244 ).items
1245 )[0]
1246 else:
1247 current_policy = list(
1248 blade.get_smb_share_policies(names=[module.params["name"]]).items
1249 )[0]
1250 if current_policy.enabled != module.params["enabled"]:
1251 changed = True
1252 if not module.check_mode:
1253 if (
1254 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1255 and module.params["context"]
1256 ):
1257 res = blade.patch_smb_share_policies(
1258 policy=SmbSharePolicy(enabled=module.params["enabled"]),
1259 names=[module.params["name"]],
1260 context_names=[module.params["context"]],
1261 )
1262 else:
1263 res = blade.patch_smb_share_policies(
1264 policy=SmbSharePolicy(enabled=module.params["enabled"]),
1265 names=[module.params["name"]],
1266 )
1267 if res.status_code != 200:
1268 module.fail_json(
1269 msg="Failed to change state of SMB share policy {0}.Error: {1}".format(
1270 module.params["name"], get_error_message(res)
1271 )
1272 )
1273 module.exit_json(changed=changed)
1276def delete_smb_client_policy(module, blade):
1277 """Delete SMB CLient Policy, or Rule
1279 If client is provided then delete the client rule if it exists.
1280 """
1282 changed = False
1283 versions = get_rest_api_version(blade)
1284 policy_delete = True
1285 if module.params["client"]:
1286 policy_delete = False
1287 if (
1288 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1289 and module.params["context"]
1290 ):
1291 res = blade.get_smb_client_policies_rules(
1292 policy_names=[module.params["name"]],
1293 filter="client='" + module.params["client"] + "'",
1294 context_names=[module.params["context"]],
1295 )
1296 else:
1297 res = blade.get_smb_client_policies_rules(
1298 policy_names=[module.params["name"]],
1299 filter="client='" + module.params["client"] + "'",
1300 )
1301 if res.status_code == 200:
1302 if res.total_item_count == 0:
1303 pass
1304 elif res.total_item_count == 1:
1305 rule = list(res.items)[0]
1306 if module.params["client"] == rule.client:
1307 changed = True
1308 if not module.check_mode:
1309 if (
1310 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1311 and module.params["context"]
1312 ):
1313 res = blade.delete_smb_client_policies_rules(
1314 names=[rule.name],
1315 context_names=[module.params["context"]],
1316 )
1317 else:
1318 res = blade.delete_smb_client_policies_rules(
1319 names=[rule.name]
1320 )
1321 if res.status_code != 200:
1322 module.fail_json(
1323 msg="Failed to delete rule for client {0} in policy {1}. "
1324 "Error: {2}".format(
1325 module.params["client"],
1326 module.params["name"],
1327 get_error_message(res),
1328 )
1329 )
1330 else:
1331 rules = list(res.items)
1332 for cli in rules:
1333 if cli.client == "*":
1334 changed = True
1335 if not module.check_mode:
1336 if (
1337 LooseVersion(CONTEXT_API_VERSION)
1338 <= LooseVersion(versions)
1339 and module.params["context"]
1340 ):
1341 res = blade.delete_smb_client_policies_rules(
1342 names=[cli.name],
1343 context_names=[module.params["context"]],
1344 )
1345 else:
1346 res = blade.delete_smb_client_policies_rules(
1347 names=[cli.name]
1348 )
1349 if res.status_code != 200:
1350 module.fail_json(
1351 msg="Failed to delete rule for client {0} in policy {1}. "
1352 "Error: {2}".format(
1353 module.params["client"],
1354 module.params["name"],
1355 get_error_message(res),
1356 )
1357 )
1358 if policy_delete:
1359 changed = True
1360 if not module.check_mode:
1361 if (
1362 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1363 and module.params["context"]
1364 ):
1365 res = blade.delete_smb_client_policies(
1366 names=[module.params["name"]],
1367 context_names=[module.params["context"]],
1368 )
1369 else:
1370 res = blade.delete_smb_client_policies(names=[module.params["name"]])
1371 if res.status_code != 200:
1372 module.fail_json(
1373 msg="Failed to delete SMB client policy {0}. Error: {1}".format(
1374 module.params["name"], get_error_message(res)
1375 )
1376 )
1377 module.exit_json(changed=changed)
1380def rename_smb_client_policy(module, blade):
1381 """Rename SMB Client Policy"""
1383 changed = True
1384 versions = get_rest_api_version(blade)
1385 if not module.check_mode:
1386 if (
1387 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1388 and module.params["context"]
1389 ):
1390 res = blade.patch_smb_client_policies(
1391 names=[module.params["name"]],
1392 policy=SmbClientPolicy(name=module.params["rename"]),
1393 context_names=[module.params["context"]],
1394 )
1395 else:
1396 res = blade.patch_smb_client_policies(
1397 names=[module.params["name"]],
1398 policy=SmbClientPolicy(name=module.params["rename"]),
1399 )
1400 if res.status_code != 200:
1401 module.fail_json(
1402 msg="Failed to rename SMB client policy {0} to {1}. Error: {2}".format(
1403 module.params["name"],
1404 module.params["rename"],
1405 get_error_message(res),
1406 )
1407 )
1408 module.exit_json(changed=changed)
1411def create_smb_client_policy(module, blade):
1412 """Create SMB Client Policy"""
1413 changed = True
1414 versions = get_rest_api_version(blade)
1415 if not module.check_mode:
1416 if (
1417 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1418 and module.params["context"]
1419 ):
1420 res = blade.post_smb_client_policies(
1421 names=[module.params["name"]], context_names=[module.params["context"]]
1422 )
1423 else:
1424 res = blade.post_smb_client_policies(names=[module.params["name"]])
1425 if res.status_code != 200:
1426 module.fail_json(
1427 msg="Failed to create SMB client policy {0}.Error: {1}".format(
1428 module.params["name"], get_error_message(res)
1429 )
1430 )
1431 if not module.params["enabled"]:
1432 if (
1433 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1434 and module.params["context"]
1435 ):
1436 res = blade.patch_smb_client_policies(
1437 policy=SmbClientPolicy(enabled=False),
1438 names=[module.params["name"]],
1439 context_names=[module.params["context"]],
1440 )
1441 else:
1442 res = blade.patch_smb_client_policies(
1443 policy=SmbClientPolicy(enabled=False), names=[module.params["name"]]
1444 )
1445 if res.status_code != 200:
1446 if (
1447 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1448 and module.params["context"]
1449 ):
1450 blade.delete_smb_client_policies(
1451 names=[module.params["name"]],
1452 context_names=[module.params["context"]],
1453 )
1454 else:
1455 blade.delete_smb_client_policies(names=[module.params["name"]])
1456 module.fail_json(
1457 msg="Failed to create SMB client policy {0}.Error: {1}".format(
1458 module.params["name"], get_error_message(res)
1459 )
1460 )
1461 if not module.params["client"]:
1462 module.fail_json(msg="client is required to create a new rule")
1463 else:
1464 rule = _smb_client_rule_from_params(module, versions)
1465 if (
1466 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1467 and module.params["context"]
1468 ):
1469 res = blade.post_smb_client_policies_rules(
1470 policy_names=[module.params["name"]],
1471 rule=rule,
1472 context_names=[module.params["context"]],
1473 )
1474 else:
1475 res = blade.post_smb_client_policies_rules(
1476 policy_names=[module.params["name"]],
1477 rule=rule,
1478 )
1479 if res.status_code != 200:
1480 module.fail_json(
1481 msg="Failed to rule for policy {0}. Error: {1}".format(
1482 module.params["name"],
1483 get_error_message(res),
1484 )
1485 )
1486 module.exit_json(changed=changed)
1489def create_network_access_policy(module, blade):
1490 """Create Network Access Policy
1492 The FlashBlade REST API has no POST for network-access-policies (only the
1493 rules endpoint can be posted to), so a named policy can never be created.
1494 Fail clearly instead of raising an AttributeError from the SDK.
1495 """
1496 module.fail_json(
1497 msg="Network access policy {0} does not exist and network access "
1498 "policies cannot be created. Only the rules of an existing policy "
1499 "(normally default-network-access-policy) can be managed.".format(
1500 module.params["name"]
1501 )
1502 )
1505def create_worm_data_policy(module, blade):
1506 """Create WORM Data Policy"""
1507 changed = True
1508 versions = get_rest_api_version(blade)
1509 if not module.check_mode:
1510 min_retention = time_to_milliseconds(module.params["min_retention"])
1511 max_retention = time_to_milliseconds(module.params["max_retention"])
1512 default_retention = time_to_milliseconds(module.params["default_retention"])
1513 if (
1514 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1515 and module.params["context"]
1516 ):
1517 res = blade.post_worm_data_policies(
1518 policy=WormDataPolicy(
1519 enabled=module.params["enabled"],
1520 mode="compliance",
1521 min_retention=min_retention,
1522 max_retention=max_retention,
1523 default_retention=default_retention,
1524 retention_lock=module.params["retention_lock"],
1525 ),
1526 names=[module.params["name"]],
1527 context_names=[module.params["context"]],
1528 )
1529 else:
1530 res = blade.post_worm_data_policies(
1531 policy=WormDataPolicy(
1532 enabled=module.params["enabled"],
1533 mode="compliance",
1534 min_retention=min_retention,
1535 max_retention=max_retention,
1536 default_retention=default_retention,
1537 retention_lock=module.params["retention_lock"],
1538 ),
1539 names=[module.params["name"]],
1540 )
1541 if res.status_code != 200:
1542 if (
1543 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1544 and module.params["context"]
1545 ):
1546 blade.delete_worm_data_policies(
1547 names=[module.params["name"]],
1548 context_names=[module.params["context"]],
1549 )
1550 else:
1551 blade.delete_worm_data_policies(names=[module.params["name"]])
1552 module.fail_json(
1553 msg="Failed to create WORM data policy {0}.Error: {1}".format(
1554 module.params["name"], get_error_message(res)
1555 )
1556 )
1557 module.exit_json(changed=changed)
1560def update_smb_client_policy(module, blade):
1561 """Update SMB Client Policy Rule"""
1563 changed = False
1564 versions = get_rest_api_version(blade)
1565 if module.params["client"]:
1566 if (
1567 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1568 and module.params["context"]
1569 ):
1570 current_policy_rule = blade.get_smb_client_policies_rules(
1571 policy_names=[module.params["name"]],
1572 filter="client='" + module.params["client"] + "'",
1573 context_names=[module.params["context"]],
1574 )
1575 else:
1576 current_policy_rule = blade.get_smb_client_policies_rules(
1577 policy_names=[module.params["name"]],
1578 filter="client='" + module.params["client"] + "'",
1579 )
1580 # If the rule lookup fails or returns nothing then no rule exists for
1581 # this client yet - some Purity//FB versions return an error rather
1582 # than an empty result set - so create it. Otherwise reconcile the
1583 # existing rule.
1584 if (
1585 current_policy_rule.status_code != 200
1586 or current_policy_rule.total_item_count == 0
1587 ):
1588 rule = _smb_client_rule_from_params(module, versions)
1589 changed = True
1590 if not module.check_mode:
1591 if module.params["before_rule"]:
1592 before_name = (
1593 module.params["name"] + "." + str(module.params["before_rule"])
1594 )
1595 if (
1596 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1597 and module.params["context"]
1598 ):
1599 res = blade.post_smb_client_policies_rules(
1600 policy_names=[module.params["name"]],
1601 rule=rule,
1602 before_rule_name=before_name,
1603 context_names=[module.params["context"]],
1604 )
1605 else:
1606 res = blade.post_smb_client_policies_rules(
1607 policy_names=[module.params["name"]],
1608 rule=rule,
1609 before_rule_name=before_name,
1610 )
1611 else:
1612 if (
1613 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1614 and module.params["context"]
1615 ):
1616 res = blade.post_smb_client_policies_rules(
1617 policy_names=[module.params["name"]],
1618 rule=rule,
1619 context_names=[module.params["context"]],
1620 )
1621 else:
1622 res = blade.post_smb_client_policies_rules(
1623 policy_names=[module.params["name"]],
1624 rule=rule,
1625 )
1626 if res.status_code != 200:
1627 module.fail_json(
1628 msg="Failed to create rule for client {0} "
1629 "in policy {1}. Error: {2}".format(
1630 module.params["client"],
1631 module.params["name"],
1632 get_error_message(res),
1633 )
1634 )
1635 else:
1636 rules = list(current_policy_rule.items)
1637 cli_count = None
1638 done = False
1639 if module.params["client"] == "*":
1640 for cli in rules:
1641 if cli.client == "*":
1642 cli_count = cli
1643 if not cli_count:
1644 rule = _smb_client_rule_from_params(module, versions)
1645 done = True
1646 changed = True
1647 if not module.check_mode:
1648 if module.params["before_rule"]:
1649 if (
1650 LooseVersion(CONTEXT_API_VERSION)
1651 <= LooseVersion(versions)
1652 and module.params["context"]
1653 ):
1654 res = blade.post_smb_client_policies_rules(
1655 policy_names=[module.params["name"]],
1656 rule=rule,
1657 before_rule_name=(
1658 module.params["name"]
1659 + "."
1660 + str(module.params["before_rule"]),
1661 ),
1662 context_names=[module.params["context"]],
1663 )
1664 else:
1665 res = blade.post_smb_client_policies_rules(
1666 policy_names=[module.params["name"]],
1667 rule=rule,
1668 before_rule_name=(
1669 module.params["name"]
1670 + "."
1671 + str(module.params["before_rule"]),
1672 ),
1673 )
1674 else:
1675 if (
1676 LooseVersion(CONTEXT_API_VERSION)
1677 <= LooseVersion(versions)
1678 and module.params["context"]
1679 ):
1680 res = blade.post_smb_client_policies_rules(
1681 policy_names=[module.params["name"]],
1682 rule=rule,
1683 context_names=[module.params["context"]],
1684 )
1685 else:
1686 res = blade.post_smb_client_policies_rules(
1687 policy_names=[module.params["name"]],
1688 rule=rule,
1689 )
1690 if res.status_code != 200:
1691 module.fail_json(
1692 msg="Failed to create rule for "
1693 "client {0} in policy {1}. Error: {2}".format(
1694 module.params["client"],
1695 module.params["name"],
1696 get_error_message(res),
1697 )
1698 )
1699 if not done:
1700 old_policy_rule = rules[0]
1701 # Fields the task did not set keep their current value.
1702 current_rule = {
1703 "client": old_policy_rule.client,
1704 "permission": old_policy_rule.permission,
1705 }
1706 new_rule = {
1707 "client": module.params["client"],
1708 "permission": _default(
1709 module.params["permission"], current_rule["permission"]
1710 ),
1711 }
1712 if LooseVersion(SMB_ENCRYPT_API_VERSION) <= LooseVersion(versions):
1713 current_rule["encryption"] = old_policy_rule.encryption
1714 new_rule["encryption"] = _default(
1715 module.params["smb_encryption"], current_rule["encryption"]
1716 )
1717 if current_rule != new_rule:
1718 changed = True
1719 if not module.check_mode:
1720 if LooseVersion(SMB_ENCRYPT_API_VERSION) <= LooseVersion(
1721 versions
1722 ):
1723 rule = SmbClientPolicyRule(
1724 client=new_rule["client"],
1725 permission=new_rule["permission"],
1726 encryption=new_rule["encryption"],
1727 )
1728 else:
1729 rule = SmbClientPolicyRule(
1730 client=new_rule["client"],
1731 permission=new_rule["permission"],
1732 )
1733 if (
1734 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1735 and module.params["context"]
1736 ):
1737 res = blade.patch_smb_client_policies_rules(
1738 names=[
1739 module.params["name"]
1740 + "."
1741 + str(old_policy_rule.index)
1742 ],
1743 rule=rule,
1744 context_names=[module.params["context"]],
1745 )
1746 else:
1747 res = blade.patch_smb_client_policies_rules(
1748 names=[
1749 module.params["name"]
1750 + "."
1751 + str(old_policy_rule.index)
1752 ],
1753 rule=rule,
1754 )
1755 if res.status_code != 200:
1756 module.fail_json(
1757 msg="Failed to update SMB client rule {0}. Error: {1}".format(
1758 module.params["name"]
1759 + "."
1760 + str(old_policy_rule.index),
1761 get_error_message(res),
1762 )
1763 )
1764 if (
1765 module.params["before_rule"]
1766 and module.params["before_rule"] != old_policy_rule.index
1767 ):
1768 changed = True
1769 if not module.check_mode:
1770 before_name = (
1771 module.params["name"]
1772 + "."
1773 + str(module.params["before_rule"])
1774 )
1775 if (
1776 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1777 and module.params["context"]
1778 ):
1779 res = blade.patch_smb_client_policies_rules(
1780 names=[
1781 module.params["name"]
1782 + "."
1783 + str(old_policy_rule.index)
1784 ],
1785 rule=SmbClientPolicyRule(),
1786 before_rule_name=before_name,
1787 context_names=[module.params["context"]],
1788 )
1789 else:
1790 res = blade.patch_smb_client_policies_rules(
1791 names=[
1792 module.params["name"]
1793 + "."
1794 + str(old_policy_rule.index)
1795 ],
1796 rule=SmbClientPolicyRule(),
1797 before_rule_name=before_name,
1798 )
1799 if res.status_code != 200:
1800 module.fail_json(
1801 msg="Failed to move SMB client rule {0}. Error: {1}".format(
1802 module.params["name"]
1803 + "."
1804 + str(old_policy_rule.index),
1805 get_error_message(res),
1806 )
1807 )
1808 if (
1809 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1810 and module.params["context"]
1811 ):
1812 current_policy = list(
1813 blade.get_smb_client_policies(
1814 names=[module.params["name"]], context_names=[module.params["context"]]
1815 ).items
1816 )[0]
1817 else:
1818 current_policy = list(
1819 blade.get_smb_client_policies(names=[module.params["name"]]).items
1820 )[0]
1821 if current_policy.enabled != module.params["enabled"]:
1822 changed = True
1823 if not module.check_mode:
1824 if (
1825 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1826 and module.params["context"]
1827 ):
1828 res = blade.patch_smb_client_policies(
1829 policy=SmbClientPolicy(enabled=module.params["enabled"]),
1830 names=[module.params["name"]],
1831 context_names=[module.params["context"]],
1832 )
1833 else:
1834 res = blade.patch_smb_client_policies(
1835 policy=SmbClientPolicy(enabled=module.params["enabled"]),
1836 names=[module.params["name"]],
1837 )
1838 if res.status_code != 200:
1839 module.fail_json(
1840 msg="Failed to change state of SMB client policy {0}.Error: {1}".format(
1841 module.params["name"], get_error_message(res)
1842 )
1843 )
1844 module.exit_json(changed=changed)
1847def delete_nfs_policy(module, blade):
1848 """Delete NFS Export Policy, or Rule
1850 If client is provided then delete the client rule if it exists.
1851 """
1853 changed = False
1854 versions = get_rest_api_version(blade)
1855 policy_delete = True
1856 if module.params["client"]:
1857 policy_delete = False
1858 if (
1859 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1860 and module.params["context"]
1861 ):
1862 res = blade.get_nfs_export_policies_rules(
1863 policy_names=[module.params["name"]],
1864 filter="client='" + module.params["client"] + "'",
1865 context_names=[module.params["context"]],
1866 )
1867 else:
1868 res = blade.get_nfs_export_policies_rules(
1869 policy_names=[module.params["name"]],
1870 filter="client='" + module.params["client"] + "'",
1871 )
1872 if res.status_code == 200:
1873 if res.total_item_count == 0:
1874 pass
1875 elif res.total_item_count == 1:
1876 rule = list(res.items)[0]
1877 if module.params["client"] == rule.client:
1878 changed = True
1879 if not module.check_mode:
1880 if (
1881 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1882 and module.params["context"]
1883 ):
1884 res = blade.delete_nfs_export_policies_rules(
1885 names=[rule.name],
1886 context_names=[module.params["context"]],
1887 )
1888 else:
1889 res = blade.delete_nfs_export_policies_rules(
1890 names=[rule.name]
1891 )
1892 if res.status_code != 200:
1893 module.fail_json(
1894 msg="Failed to delete rule for client {0} in policy {1}. "
1895 "Error: {2}".format(
1896 module.params["client"],
1897 module.params["name"],
1898 get_error_message(res),
1899 )
1900 )
1901 else:
1902 rules = list(res.items)
1903 for cli in rules:
1904 if cli.client == "*":
1905 changed = True
1906 if not module.check_mode:
1907 if (
1908 LooseVersion(CONTEXT_API_VERSION)
1909 <= LooseVersion(versions)
1910 and module.params["context"]
1911 ):
1912 res = blade.delete_nfs_export_policies_rules(
1913 names=[cli.name],
1914 context_names=[module.params["context"]],
1915 )
1916 else:
1917 res = blade.delete_nfs_export_policies_rules(
1918 names=[cli.name]
1919 )
1920 if res.status_code != 200:
1921 module.fail_json(
1922 msg="Failed to delete rule for client {0} in policy {1}. "
1923 "Error: {2}".format(
1924 module.params["client"],
1925 module.params["name"],
1926 get_error_message(res),
1927 )
1928 )
1929 if policy_delete:
1930 changed = True
1931 if not module.check_mode:
1932 if (
1933 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
1934 and module.params["context"]
1935 ):
1936 res = blade.delete_nfs_export_policies(
1937 names=[module.params["name"]],
1938 context_names=[module.params["context"]],
1939 )
1940 else:
1941 res = blade.delete_nfs_export_policies(names=[module.params["name"]])
1942 if res.status_code != 200:
1943 module.fail_json(
1944 msg="Failed to delete export policy {0}. Error: {1}".format(
1945 module.params["name"], get_error_message(res)
1946 )
1947 )
1948 module.exit_json(changed=changed)
1951def _network_rules_for_client(blade, module):
1952 """Return the rules of the network access policy that match I(client).
1954 The filter is applied client-side as well: a client of ``*`` is a
1955 wildcard to the REST filter and would otherwise match every rule.
1956 """
1957 res = blade.get_network_access_policies_rules(
1958 policy_names=[module.params["name"]],
1959 filter="client='" + module.params["client"] + "'",
1960 )
1961 if res.status_code != 200:
1962 return []
1963 return [rule for rule in list(res.items) if rule.client == module.params["client"]]
1966def update_network_access_policy(module, blade):
1967 """Update Network Access Policy Rule
1969 The network-access-policies endpoints take no fleet context, so no
1970 context_names is passed here.
1971 """
1973 changed = False
1974 if module.params["client"]:
1975 existing_rules = _network_rules_for_client(blade, module)
1976 if not existing_rules:
1977 rule = NetworkAccessPolicyRule(
1978 client=module.params["client"],
1979 effect=_default(module.params["effect"], "allow"),
1980 interfaces=module.params["interfaces"],
1981 )
1982 changed = True
1983 if not module.check_mode:
1984 post_kwargs = {}
1985 if module.params["before_rule"]:
1986 post_kwargs["before_rule_name"] = (
1987 module.params["name"] + "." + str(module.params["before_rule"])
1988 )
1989 res = blade.post_network_access_policies_rules(
1990 policy_names=[module.params["name"]], rule=rule, **post_kwargs
1991 )
1992 if res.status_code != 200:
1993 module.fail_json(
1994 msg="Failed to create rule for client {0} "
1995 "in policy {1}. Error: {2}".format(
1996 module.params["client"],
1997 module.params["name"],
1998 get_error_message(res),
1999 )
2000 )
2001 else:
2002 old_policy_rule = existing_rules[0]
2003 rule_name = module.params["name"] + "." + str(old_policy_rule.index)
2004 # Fields the task did not set keep their current value. Interface
2005 # lists are compared order-insensitively.
2006 current_rule = {
2007 "effect": old_policy_rule.effect,
2008 "interfaces": sorted(old_policy_rule.interfaces or []),
2009 }
2010 new_rule = {
2011 "effect": _default(module.params["effect"], current_rule["effect"]),
2012 "interfaces": (
2013 sorted(module.params["interfaces"])
2014 if module.params["interfaces"]
2015 else current_rule["interfaces"]
2016 ),
2017 }
2018 if current_rule != new_rule:
2019 changed = True
2020 if not module.check_mode:
2021 rule = NetworkAccessPolicyRule(
2022 client=module.params["client"],
2023 effect=new_rule["effect"],
2024 interfaces=new_rule["interfaces"],
2025 )
2026 res = blade.patch_network_access_policies_rules(
2027 names=[rule_name], rule=rule
2028 )
2029 if res.status_code != 200:
2030 module.fail_json(
2031 msg="Failed to update network access client rule {0}. "
2032 "Error: {1}".format(rule_name, get_error_message(res))
2033 )
2034 if (
2035 module.params["before_rule"]
2036 and module.params["before_rule"] != old_policy_rule.index
2037 ):
2038 changed = True
2039 if not module.check_mode:
2040 before_name = (
2041 module.params["name"] + "." + str(module.params["before_rule"])
2042 )
2043 res = blade.patch_network_access_policies_rules(
2044 names=[rule_name],
2045 rule=NetworkAccessPolicyRule(),
2046 before_rule_name=before_name,
2047 )
2048 if res.status_code != 200:
2049 module.fail_json(
2050 msg="Failed to move network access client rule {0}. "
2051 "Error: {1}".format(rule_name, get_error_message(res))
2052 )
2053 current_policy = list(
2054 blade.get_network_access_policies(names=[module.params["name"]]).items
2055 )[0]
2056 if current_policy.enabled != module.params["enabled"]:
2057 changed = True
2058 if not module.check_mode:
2059 res = blade.patch_network_access_policies(
2060 policy=NetworkAccessPolicy(enabled=module.params["enabled"]),
2061 names=[module.params["name"]],
2062 )
2063 if res.status_code != 200:
2064 module.fail_json(
2065 msg="Failed to change state of network access policy {0}."
2066 "Error: {1}".format(module.params["name"], get_error_message(res))
2067 )
2068 module.exit_json(changed=changed)
2071def delete_network_access_policy(module, blade):
2072 """Delete Network Access Policy Rule
2074 If client is provided then delete the client rule if it exists. The REST
2075 API has no DELETE for network-access-policies themselves, so a whole
2076 policy delete is refused.
2077 """
2079 changed = False
2080 if not module.params["client"]:
2081 module.fail_json(
2082 msg="Network access policies cannot be deleted. Specify `client` "
2083 "to delete a rule from policy {0}.".format(module.params["name"])
2084 )
2085 for rule in _network_rules_for_client(blade, module):
2086 changed = True
2087 if not module.check_mode:
2088 res = blade.delete_network_access_policies_rules(names=[rule.name])
2089 if res.status_code != 200:
2090 module.fail_json(
2091 msg="Failed to delete rule for client {0} in policy {1}. "
2092 "Error: {2}".format(
2093 module.params["client"],
2094 module.params["name"],
2095 get_error_message(res),
2096 )
2097 )
2098 module.exit_json(changed=changed)
2101def delete_worm_data_policy(module, blade):
2102 """Delete WORM data Policy"""
2104 changed = True
2105 versions = get_rest_api_version(blade)
2106 if not module.check_mode:
2107 if (
2108 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2109 and module.params["context"]
2110 ):
2111 res = blade.delete_worm_data_policies(
2112 names=[module.params["name"]], context_names=[module.params["context"]]
2113 )
2114 else:
2115 res = blade.delete_worm_data_policies(names=[module.params["name"]])
2116 if res.status_code != 200:
2117 module.fail_json(
2118 msg="Failed to delete WORM data policy {0}. Error: {1}".format(
2119 module.params["name"], get_error_message(res)
2120 )
2121 )
2122 module.exit_json(changed=changed)
2125def rename_worm_data_policy(module):
2126 """Rename WORM Data Policy"""
2128 changed = False
2129 module.warn("Renaming of WORM Data policies is not supported")
2130 module.exit_json(changed=changed)
2133def rename_network_access_policy(module, blade):
2134 """Rename Network Access Policy"""
2136 changed = True
2137 if not module.check_mode:
2138 res = blade.patch_network_access_policies(
2139 names=[module.params["name"]],
2140 policy=NetworkAccessPolicy(name=module.params["rename"]),
2141 )
2142 if res.status_code != 200:
2143 module.fail_json(
2144 msg="Failed to rename network access policy {0} to {1}. Error: {2}".format(
2145 module.params["name"],
2146 module.params["rename"],
2147 get_error_message(res),
2148 )
2149 )
2150 module.exit_json(changed=changed)
2153def rename_nfs_policy(module, blade):
2154 """Rename NFS Export Policy"""
2156 changed = True
2157 versions = get_rest_api_version(blade)
2158 if not module.check_mode:
2159 if (
2160 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2161 and module.params["context"]
2162 ):
2163 res = blade.patch_nfs_export_policies(
2164 names=[module.params["name"]],
2165 policy=NfsExportPolicy(name=module.params["rename"]),
2166 context_names=[module.params["context"]],
2167 )
2168 else:
2169 res = blade.patch_nfs_export_policies(
2170 names=[module.params["name"]],
2171 policy=NfsExportPolicy(name=module.params["rename"]),
2172 )
2173 if res.status_code != 200:
2174 module.fail_json(
2175 msg="Failed to rename NFS export policy {0} to {1}. Error: {2}".format(
2176 module.params["name"],
2177 module.params["rename"],
2178 get_error_message(res),
2179 )
2180 )
2181 module.exit_json(changed=changed)
2184def update_worm_data_policy(module, blade):
2185 """Update WORM data policy"""
2187 changed = False
2188 versions = get_rest_api_version(blade)
2189 if (
2190 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2191 and module.params["context"]
2192 ):
2193 current_policy_config = list(
2194 blade.get_worm_data_policies(
2195 names=[module.params["name"]], context_names=[module.params["context"]]
2196 ).items
2197 )[0]
2198 else:
2199 current_policy_config = list(
2200 blade.get_worm_data_policies(names=[module.params["name"]]).items
2201 )[0]
2202 current_policy = {
2203 "default_retention": current_policy_config.default_retention,
2204 "enabled": current_policy_config.enabled,
2205 "max_retention": current_policy_config.max_retention,
2206 "min_retention": current_policy_config.min_retention,
2207 "retention_lock": current_policy_config.retention_lock,
2208 }
2209 new_policy = {
2210 "default_retention": current_policy_config.default_retention,
2211 "enabled": current_policy_config.enabled,
2212 "max_retention": current_policy_config.max_retention,
2213 "min_retention": current_policy_config.min_retention,
2214 "retention_lock": current_policy_config.retention_lock,
2215 }
2216 if module.params["enabled"] != current_policy["enabled"]:
2217 new_policy["enabled"] = module.params["enabled"]
2218 if (
2219 module.params["retention_lock"]
2220 and module.params["retention_lock"] != current_policy["retention_lock"]
2221 ):
2222 new_policy["retention_lock"] = module.params["retention_lock"]
2223 if (
2224 module.params["default_retention"]
2225 and time_to_milliseconds(module.params["default_retention"])
2226 != current_policy["default_retention"]
2227 ):
2228 new_policy["default_retention"] = time_to_milliseconds(
2229 module.params["default_retention"]
2230 )
2231 if (
2232 module.params["max_retention"]
2233 and time_to_milliseconds(module.params["max_retention"])
2234 != current_policy["max_retention"]
2235 ):
2236 new_policy["max_retention"] = time_to_milliseconds(
2237 module.params["max_retention"]
2238 )
2239 if (
2240 module.params["min_retention"]
2241 and time_to_milliseconds(module.params["min_retention"])
2242 != current_policy["min_retention"]
2243 ):
2244 new_policy["min_retention"] = time_to_milliseconds(
2245 module.params["min_retention"]
2246 )
2247 if new_policy != current_policy:
2248 changed = True
2249 if not module.check_mode:
2250 worm_policy = WormDataPolicy(
2251 enabled=new_policy["enabled"],
2252 retention_lock=new_policy["retention_lock"],
2253 min_retention=new_policy["min_retention"],
2254 max_retention=new_policy["max_retention"],
2255 default_retention=new_policy["default_retention"],
2256 )
2257 if (
2258 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2259 and module.params["context"]
2260 ):
2261 res = blade.patch_worm_data_policies(
2262 names=[module.params["name"]],
2263 policy=worm_policy,
2264 context_names=[module.params["context"]],
2265 )
2266 else:
2267 res = blade.patch_worm_data_policies(
2268 names=[module.params["name"]], policy=worm_policy
2269 )
2270 if res.status_code != 200:
2271 module.fail_json(
2272 msg="Failed to update WORM data policy {0}. Error: {1}".format(
2273 module.params["name"],
2274 get_error_message(res),
2275 )
2276 )
2277 module.exit_json(changed=changed)
2280def update_nfs_policy(module, blade):
2281 """Update NFS Export Policy Rule"""
2283 changed = False
2284 versions = get_rest_api_version(blade)
2285 if module.params["client"]:
2286 if (
2287 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2288 and module.params["context"]
2289 ):
2290 current_policy_rule = blade.get_nfs_export_policies_rules(
2291 policy_names=[module.params["name"]],
2292 filter="client='" + module.params["client"] + "'",
2293 context_names=[module.params["context"]],
2294 )
2295 else:
2296 current_policy_rule = blade.get_nfs_export_policies_rules(
2297 policy_names=[module.params["name"]],
2298 filter="client='" + module.params["client"] + "'",
2299 )
2300 # If the rule lookup fails or returns nothing then no rule exists for
2301 # this client yet - some Purity//FB versions return an error rather
2302 # than an empty result set - so create it. Otherwise reconcile the
2303 # existing rule.
2304 if (
2305 current_policy_rule.status_code != 200
2306 or current_policy_rule.total_item_count == 0
2307 ):
2308 rule = _nfs_rule_from_params(module, versions)
2309 changed = True
2310 if not module.check_mode:
2311 if module.params["before_rule"]:
2312 before_name = (
2313 module.params["name"] + "." + str(module.params["before_rule"])
2314 )
2315 if (
2316 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2317 and module.params["context"]
2318 ):
2319 res = blade.post_nfs_export_policies_rules(
2320 policy_names=[module.params["name"]],
2321 rule=rule,
2322 before_rule_name=before_name,
2323 context_names=[module.params["context"]],
2324 )
2325 else:
2326 res = blade.post_nfs_export_policies_rules(
2327 policy_names=[module.params["name"]],
2328 rule=rule,
2329 before_rule_name=before_name,
2330 )
2331 else:
2332 if (
2333 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2334 and module.params["context"]
2335 ):
2336 res = blade.post_nfs_export_policies_rules(
2337 policy_names=[module.params["name"]],
2338 rule=rule,
2339 context_names=[module.params["context"]],
2340 )
2341 else:
2342 res = blade.post_nfs_export_policies_rules(
2343 policy_names=[module.params["name"]],
2344 rule=rule,
2345 )
2346 if res.status_code != 200:
2347 module.fail_json(
2348 msg="Failed to create rule for client {0} "
2349 "in export policy {1}. Error: {2}".format(
2350 module.params["client"],
2351 module.params["name"],
2352 get_error_message(res),
2353 )
2354 )
2355 else:
2356 rules = list(current_policy_rule.items)
2357 cli_count = None
2358 done = False
2359 if module.params["client"] == "*":
2360 for cli in rules:
2361 if cli.client == "*":
2362 cli_count = cli
2363 if not cli_count:
2364 rule = _nfs_rule_from_params(module, versions)
2365 done = True
2366 changed = True
2367 if not module.check_mode:
2368 if module.params["before_rule"]:
2369 if (
2370 LooseVersion(CONTEXT_API_VERSION)
2371 <= LooseVersion(versions)
2372 and module.params["context"]
2373 ):
2374 res = blade.post_nfs_export_policies_rules(
2375 policy_names=[module.params["name"]],
2376 rule=rule,
2377 before_rule_name=(
2378 module.params["name"]
2379 + "."
2380 + str(module.params["before_rule"]),
2381 ),
2382 context_names=[module.params["context"]],
2383 )
2384 else:
2385 res = blade.post_nfs_export_policies_rules(
2386 policy_names=[module.params["name"]],
2387 rule=rule,
2388 before_rule_name=(
2389 module.params["name"]
2390 + "."
2391 + str(module.params["before_rule"]),
2392 ),
2393 )
2394 else:
2395 if (
2396 LooseVersion(CONTEXT_API_VERSION)
2397 <= LooseVersion(versions)
2398 and module.params["context"]
2399 ):
2400 res = blade.post_nfs_export_policies_rules(
2401 policy_names=[module.params["name"]],
2402 rule=rule,
2403 context_names=[module.params["context"]],
2404 )
2405 else:
2406 res = blade.post_nfs_export_policies_rules(
2407 policy_names=[module.params["name"]],
2408 rule=rule,
2409 )
2410 if res.status_code != 200:
2411 module.fail_json(
2412 msg="Failed to create rule for "
2413 "client {0} in export policy {1}. Error: {2}".format(
2414 module.params["client"],
2415 module.params["name"],
2416 get_error_message(res),
2417 )
2418 )
2419 if not done:
2420 old_policy_rule = rules[0]
2421 # Fields the task did not set keep their current value. The
2422 # anon ids are compared as int (the PATCH body renders them
2423 # as strings, see _nfs_rule) and security is compared
2424 # order-insensitively but sent in the order it is stored,
2425 # since NFS clients can treat sec= order as a preference.
2426 current_rule = {
2427 "access": getattr(old_policy_rule, "access", None),
2428 "anongid": _anon_id(getattr(old_policy_rule, "anongid", None)),
2429 "anonuid": _anon_id(getattr(old_policy_rule, "anonuid", None)),
2430 "atime": old_policy_rule.atime,
2431 "client": old_policy_rule.client,
2432 "fileid_32bit": old_policy_rule.fileid_32bit,
2433 "permission": old_policy_rule.permission,
2434 "secure": old_policy_rule.secure,
2435 "security": sorted(old_policy_rule.security or []),
2436 }
2437 new_rule = {
2438 "access": _default(module.params["access"], current_rule["access"]),
2439 "anongid": _default(
2440 _anon_id(module.params["anongid"]), current_rule["anongid"]
2441 ),
2442 "anonuid": _default(
2443 _anon_id(module.params["anonuid"]), current_rule["anonuid"]
2444 ),
2445 "atime": _default(module.params["atime"], current_rule["atime"]),
2446 "client": module.params["client"],
2447 "fileid_32bit": _default(
2448 module.params["fileid_32bit"], current_rule["fileid_32bit"]
2449 ),
2450 "permission": _default(
2451 module.params["permission"], current_rule["permission"]
2452 ),
2453 "secure": _default(module.params["secure"], current_rule["secure"]),
2454 "security": (
2455 sorted(module.params["security"])
2456 if module.params["security"]
2457 else current_rule["security"]
2458 ),
2459 }
2460 if current_rule != new_rule:
2461 changed = True
2462 if not module.check_mode:
2463 if module.params["security"]:
2464 new_rule["security"] = module.params["security"]
2465 else:
2466 new_rule["security"] = old_policy_rule.security
2467 rule = _nfs_rule(versions, patch=True, **new_rule)
2468 if (
2469 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2470 and module.params["context"]
2471 ):
2472 res = blade.patch_nfs_export_policies_rules(
2473 names=[
2474 module.params["name"]
2475 + "."
2476 + str(old_policy_rule.index)
2477 ],
2478 rule=rule,
2479 context_names=[module.params["context"]],
2480 )
2481 else:
2482 res = blade.patch_nfs_export_policies_rules(
2483 names=[
2484 module.params["name"]
2485 + "."
2486 + str(old_policy_rule.index)
2487 ],
2488 rule=rule,
2489 )
2490 if res.status_code != 200:
2491 module.fail_json(
2492 msg="Failed to update NFS export rule {0}. Error: {1}".format(
2493 module.params["name"]
2494 + "."
2495 + str(old_policy_rule.index),
2496 get_error_message(res),
2497 )
2498 )
2499 if (
2500 module.params["before_rule"]
2501 and module.params["before_rule"] != old_policy_rule.index
2502 ):
2503 changed = True
2504 if not module.check_mode:
2505 before_name = (
2506 module.params["name"]
2507 + "."
2508 + str(module.params["before_rule"])
2509 )
2510 if (
2511 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2512 and module.params["context"]
2513 ):
2514 res = blade.patch_nfs_export_policies_rules(
2515 names=[
2516 module.params["name"]
2517 + "."
2518 + str(old_policy_rule.index)
2519 ],
2520 rule=_nfs_rule(versions),
2521 before_rule_name=before_name,
2522 context_names=[module.params["context"]],
2523 )
2524 else:
2525 res = blade.patch_nfs_export_policies_rules(
2526 names=[
2527 module.params["name"]
2528 + "."
2529 + str(old_policy_rule.index)
2530 ],
2531 rule=_nfs_rule(versions),
2532 before_rule_name=before_name,
2533 )
2534 if res.status_code != 200:
2535 module.fail_json(
2536 msg="Failed to move NFS export rule {0}. Error: {1}".format(
2537 module.params["name"]
2538 + "."
2539 + str(old_policy_rule.index),
2540 get_error_message(res),
2541 )
2542 )
2543 if (
2544 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2545 and module.params["context"]
2546 ):
2547 current_policy = list(
2548 blade.get_nfs_export_policies(
2549 names=[module.params["name"]], context_names=[module.params["context"]]
2550 ).items
2551 )[0]
2552 else:
2553 current_policy = list(
2554 blade.get_nfs_export_policies(names=[module.params["name"]]).items
2555 )[0]
2556 if current_policy.enabled != module.params["enabled"]:
2557 changed = True
2558 if not module.check_mode:
2559 if (
2560 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2561 and module.params["context"]
2562 ):
2563 res = blade.patch_nfs_export_policies(
2564 policy=NfsExportPolicy(enabled=module.params["enabled"]),
2565 names=[module.params["name"]],
2566 context_names=[module.params["context"]],
2567 )
2568 else:
2569 res = blade.patch_nfs_export_policies(
2570 policy=NfsExportPolicy(enabled=module.params["enabled"]),
2571 names=[module.params["name"]],
2572 )
2573 if res.status_code != 200:
2574 module.fail_json(
2575 msg="Failed to change state of nfs export policy {0}.Error: {1}".format(
2576 module.params["name"], get_error_message(res)
2577 )
2578 )
2579 module.exit_json(changed=changed)
2582def create_nfs_policy(module, blade):
2583 """Create NFS Export Policy"""
2584 changed = True
2585 versions = get_rest_api_version(blade)
2586 if not module.check_mode:
2587 if (
2588 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2589 and module.params["context"]
2590 ):
2591 res = blade.post_nfs_export_policies(
2592 names=[module.params["name"]], context_names=[module.params["context"]]
2593 )
2594 else:
2595 res = blade.post_nfs_export_policies(names=[module.params["name"]])
2596 if res.status_code != 200:
2597 module.fail_json(
2598 msg="Failed to create nfs export policy {0}.Error: {1}".format(
2599 module.params["name"], get_error_message(res)
2600 )
2601 )
2602 if not module.params["enabled"]:
2603 if (
2604 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2605 and module.params["context"]
2606 ):
2607 res = blade.patch_nfs_export_policies(
2608 policy=NfsExportPolicy(enabled=False),
2609 names=[module.params["name"]],
2610 context_names=[module.params["context"]],
2611 )
2612 else:
2613 res = blade.patch_nfs_export_policies(
2614 policy=NfsExportPolicy(enabled=False), names=[module.params["name"]]
2615 )
2616 if res.status_code != 200:
2617 if (
2618 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2619 and module.params["context"]
2620 ):
2621 blade.delete_nfs_export_policies(
2622 names=[module.params["name"]],
2623 context_names=[module.params["context"]],
2624 )
2625 else:
2626 blade.delete_nfs_export_policies(names=[module.params["name"]])
2627 module.fail_json(
2628 msg="Failed to create nfs export policy {0}.Error: {1}".format(
2629 module.params["name"], get_error_message(res)
2630 )
2631 )
2632 if module.params["client"]:
2633 rule = _nfs_rule_from_params(module, versions)
2634 if (
2635 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2636 and module.params["context"]
2637 ):
2638 res = blade.post_nfs_export_policies_rules(
2639 policy_names=[module.params["name"]],
2640 rule=rule,
2641 context_names=[module.params["context"]],
2642 )
2643 else:
2644 res = blade.post_nfs_export_policies_rules(
2645 policy_names=[module.params["name"]],
2646 rule=rule,
2647 )
2648 if res.status_code != 200:
2649 module.fail_json(
2650 msg="Failed to rule for policy {0}. Error: {1}".format(
2651 module.params["name"],
2652 get_error_message(res),
2653 )
2654 )
2655 module.exit_json(changed=changed)
2658def delete_os_policy(module, blade):
2659 """Delete Object Store Access Policy, Rule, or User
2661 If rule is provided then delete the rule if it exists.
2662 If user is provided then remove grant from user if granted.
2663 If no user or rule provided delete the whole policy.
2664 Cannot delete a policy with attached users, so delete all users
2665 if the force_delete option is selected.
2666 """
2668 changed = False
2669 versions = get_rest_api_version(blade)
2670 policy_name = module.params["account"] + "/" + module.params["name"]
2671 policy_delete = True
2672 if module.params["rule"]:
2673 policy_delete = False
2674 if (
2675 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2676 and module.params["context"]
2677 ):
2678 res = blade.get_object_store_access_policies_rules(
2679 policy_names=[policy_name],
2680 names=[module.params["rule"]],
2681 context_names=[module.params["context"]],
2682 )
2683 else:
2684 res = blade.get_object_store_access_policies_rules(
2685 policy_names=[policy_name], names=[module.params["rule"]]
2686 )
2687 if res.status_code == 200 and res.total_item_count != 0:
2688 changed = True
2689 if not module.check_mode:
2690 if (
2691 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2692 and module.params["context"]
2693 ):
2694 res = blade.delete_object_store_access_policies_rules(
2695 policy_names=[policy_name],
2696 names=[module.params["rule"]],
2697 context_names=[module.params["context"]],
2698 )
2699 else:
2700 res = blade.delete_object_store_access_policies_rules(
2701 policy_names=[policy_name], names=[module.params["rule"]]
2702 )
2703 if res.status_code != 200:
2704 module.fail_json(
2705 msg="Failed to delete users from policy {0}. Error: {1} - {2}".format(
2706 policy_name, res.errors[0].context, get_error_message(res)
2707 )
2708 )
2710 if module.params["user"]:
2711 member_name = module.params["account"] + "/" + module.params["user"]
2712 policy_delete = False
2713 if (
2714 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2715 and module.params["context"]
2716 ):
2717 res = blade.get_object_store_access_policies_object_store_users(
2718 policy_names=[policy_name],
2719 member_names=[member_name],
2720 context_names=[module.params["context"]],
2721 )
2722 else:
2723 res = blade.get_object_store_access_policies_object_store_users(
2724 policy_names=[policy_name], member_names=[member_name]
2725 )
2726 if res.status_code == 200 and res.total_item_count != 0:
2727 changed = True
2728 if not module.check_mode:
2729 member_name = module.params["account"] + "/" + module.params["user"]
2730 if (
2731 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2732 and module.params["context"]
2733 ):
2734 res = blade.delete_object_store_access_policies_object_store_users(
2735 policy_names=[policy_name],
2736 member_names=[member_name],
2737 context_names=[module.params["context"]],
2738 )
2739 else:
2740 res = blade.delete_object_store_access_policies_object_store_users(
2741 policy_names=[policy_name], member_names=[member_name]
2742 )
2743 if res.status_code != 200:
2744 module.fail_json(
2745 msg="Failed to delete users from policy {0}. Error: {1} - {2}".format(
2746 policy_name, res.errors[0].context, get_error_message(res)
2747 )
2748 )
2750 if policy_delete:
2751 if module.params["account"].lower() == "pure:policy":
2752 module.fail_json(msg="System-Wide policies cannot be deleted.")
2753 if (
2754 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2755 and module.params["context"]
2756 ):
2757 policy_users = list(
2758 blade.get_object_store_access_policies_object_store_users(
2759 policy_names=[policy_name], context_names=[module.params["context"]]
2760 ).items
2761 )
2762 else:
2763 policy_users = list(
2764 blade.get_object_store_access_policies_object_store_users(
2765 policy_names=[policy_name]
2766 ).items
2767 )
2768 if len(policy_users) == 0:
2769 changed = True
2770 if not module.check_mode:
2771 if (
2772 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2773 and module.params["context"]
2774 ):
2775 res = blade.delete_object_store_access_policies(
2776 names=[policy_name], context_names=[module.params["context"]]
2777 )
2778 else:
2779 res = blade.delete_object_store_access_policies(names=[policy_name])
2780 if res.status_code != 200:
2781 module.fail_json(
2782 msg="Failed to delete policy {0}. Error: {1}".format(
2783 policy_name, get_error_message(res)
2784 )
2785 )
2786 else:
2787 if module.params["force_delete"]:
2788 changed = True
2789 if not module.check_mode:
2790 for user in policy_users:
2791 if (
2792 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2793 and module.params["context"]
2794 ):
2795 res = blade.delete_object_store_access_policies_object_store_users(
2796 member_names=[user.member.name],
2797 policy_names=[policy_name],
2798 context_names=[module.params["context"]],
2799 )
2800 else:
2801 res = blade.delete_object_store_access_policies_object_store_users(
2802 member_names=[user.member.name],
2803 policy_names=[policy_name],
2804 )
2805 if res.status_code != 200:
2806 module.fail_json(
2807 msg="Failed to delete user {0} from policy {1}, "
2808 "Error: {2}".format(
2809 user.member,
2810 policy_name,
2811 get_error_message(res),
2812 )
2813 )
2814 if (
2815 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2816 and module.params["context"]
2817 ):
2818 res = blade.delete_object_store_access_policies(
2819 names=[policy_name],
2820 context_names=[module.params["context"]],
2821 )
2822 else:
2823 res = blade.delete_object_store_access_policies(
2824 names=[policy_name]
2825 )
2826 if res.status_code != 200:
2827 module.fail_json(
2828 msg="Failed to delete policy {0}. Error: {1}".format(
2829 policy_name, get_error_message(res)
2830 )
2831 )
2832 else:
2833 module.fail_json(
2834 msg="Policy {0} cannot be deleted with connected users".format(
2835 policy_name
2836 )
2837 )
2838 module.exit_json(changed=changed)
2841def create_os_policy(module, blade):
2842 """Create Object Store Access Policy"""
2843 changed = True
2844 policy_name = module.params["account"] + "/" + module.params["name"]
2845 versions = get_rest_api_version(blade)
2846 if not module.check_mode:
2847 if (
2848 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2849 and module.params["context"]
2850 ):
2851 res = blade.post_object_store_access_policies(
2852 names=[policy_name],
2853 policy=ObjectStoreAccessPolicyPost(description=module.params["desc"]),
2854 context_names=[module.params["context"]],
2855 )
2856 else:
2857 res = blade.post_object_store_access_policies(
2858 names=[policy_name],
2859 policy=ObjectStoreAccessPolicyPost(description=module.params["desc"]),
2860 )
2861 if res.status_code != 200:
2862 module.fail_json(
2863 msg="Failed to create access policy {0}.".format(policy_name)
2864 )
2865 if module.params["rule"]:
2866 if not module.params["actions"] or not module.params["object_resources"]:
2867 module.fail_json(
2868 msg="Parameters `actions` and `object_resources` "
2869 "are required to create a new rule"
2870 )
2871 conditions = PolicyRuleObjectAccessCondition(
2872 source_ips=module.params["source_ips"],
2873 s3_delimiters=module.params["s3_delimiters"],
2874 s3_prefixes=module.params["s3_prefixes"],
2875 )
2876 if LooseVersion(SMB_ENCRYPT_API_VERSION) <= LooseVersion(versions):
2877 rule = PolicyRuleObjectAccessPost(
2878 actions=module.params["actions"],
2879 resources=module.params["object_resources"],
2880 conditions=conditions,
2881 effect=_default(module.params["effect"], "allow"),
2882 )
2883 else:
2884 rule = PolicyRuleObjectAccessPost(
2885 actions=module.params["actions"],
2886 resources=module.params["object_resources"],
2887 conditions=conditions,
2888 )
2889 if (
2890 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2891 and module.params["context"]
2892 ):
2893 res = blade.post_object_store_access_policies_rules(
2894 policy_names=policy_name,
2895 names=[module.params["rule"]],
2896 enforce_action_restrictions=module.params["ignore_enforcement"],
2897 rule=rule,
2898 context_names=[module.params["context"]],
2899 )
2900 else:
2901 res = blade.post_object_store_access_policies_rules(
2902 policy_names=policy_name,
2903 names=[module.params["rule"]],
2904 enforce_action_restrictions=module.params["ignore_enforcement"],
2905 rule=rule,
2906 )
2907 if res.status_code != 200:
2908 module.fail_json(
2909 msg="Failed to create rule {0} to policy {1}. Error: {2}".format(
2910 module.params["rule"], policy_name, get_error_message(res)
2911 )
2912 )
2913 if module.params["user"]:
2914 member_name = module.params["account"] + "/" + module.params["user"]
2915 if (
2916 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2917 and module.params["context"]
2918 ):
2919 res = blade.post_object_store_access_policies_object_store_users(
2920 member_names=[member_name],
2921 policy_names=[policy_name],
2922 context_names=[module.params["context"]],
2923 )
2924 else:
2925 res = blade.post_object_store_access_policies_object_store_users(
2926 member_names=[member_name], policy_names=[policy_name]
2927 )
2928 if res.status_code != 200:
2929 module.fail_json(
2930 msg="Failed to add users to policy {0}. Error: {1} - {2}".format(
2931 policy_name, res.errors[0].context, get_error_message(res)
2932 )
2933 )
2934 module.exit_json(changed=changed)
2937def update_os_policy(module, blade):
2938 """Update Object Store Access Policy"""
2939 changed = False
2940 versions = get_rest_api_version(blade)
2941 policy_name = module.params["account"] + "/" + module.params["name"]
2942 if module.params["rule"]:
2943 if (
2944 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2945 and module.params["context"]
2946 ):
2947 current_policy_rule = blade.get_object_store_access_policies_rules(
2948 policy_names=[policy_name],
2949 names=[module.params["rule"]],
2950 context_names=[module.params["context"]],
2951 )
2952 else:
2953 current_policy_rule = blade.get_object_store_access_policies_rules(
2954 policy_names=[policy_name], names=[module.params["rule"]]
2955 )
2956 if current_policy_rule.status_code != 200:
2957 changed = True
2958 if not module.check_mode:
2959 conditions = PolicyRuleObjectAccessCondition(
2960 source_ips=module.params["source_ips"],
2961 s3_delimiters=module.params["s3_delimiters"],
2962 s3_prefixes=module.params["s3_prefixes"],
2963 )
2964 rule_kwargs = {
2965 "actions": module.params["actions"],
2966 "resources": module.params["object_resources"],
2967 "conditions": conditions,
2968 }
2969 if LooseVersion(SMB_ENCRYPT_API_VERSION) <= LooseVersion(versions):
2970 rule_kwargs["effect"] = _default(module.params["effect"], "allow")
2971 rule = PolicyRuleObjectAccessPost(**rule_kwargs)
2972 if (
2973 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
2974 and module.params["context"]
2975 ):
2976 res = blade.post_object_store_access_policies_rules(
2977 policy_names=policy_name,
2978 names=[module.params["rule"]],
2979 enforce_action_restrictions=module.params["ignore_enforcement"],
2980 rule=rule,
2981 context_names=[module.params["context"]],
2982 )
2983 else:
2984 res = blade.post_object_store_access_policies_rules(
2985 policy_names=policy_name,
2986 names=[module.params["rule"]],
2987 enforce_action_restrictions=module.params["ignore_enforcement"],
2988 rule=rule,
2989 )
2990 if res.status_code != 200:
2991 module.fail_json(
2992 msg="Failed to create rule {0} in policy {1}. Error: {2}".format(
2993 module.params["rule"], policy_name, get_error_message(res)
2994 )
2995 )
2996 else:
2997 old_policy_rule = list(current_policy_rule.items)[0]
2998 # Lists are compared order-insensitively (the array may return
2999 # them in any order) and fields the task did not set keep their
3000 # current value, including the rule effect.
3001 current_rule = {
3002 "actions": _sorted_or_none(old_policy_rule.actions),
3003 "resources": _sorted_or_none(old_policy_rule.resources),
3004 "ips": _sorted_or_none(
3005 getattr(old_policy_rule.conditions, "source_ips", None)
3006 ),
3007 "prefixes": _sorted_or_none(
3008 getattr(old_policy_rule.conditions, "s3_prefixes", None)
3009 ),
3010 "delimiters": _sorted_or_none(
3011 getattr(old_policy_rule.conditions, "s3_delimiters", None)
3012 ),
3013 "effect": getattr(old_policy_rule, "effect", None),
3014 }
3015 new_rule = {
3016 "actions": _sorted_or_none(module.params["actions"])
3017 or current_rule["actions"],
3018 "resources": _sorted_or_none(module.params["object_resources"])
3019 or current_rule["resources"],
3020 "ips": _sorted_or_none(module.params["source_ips"])
3021 or current_rule["ips"],
3022 "prefixes": _sorted_or_none(module.params["s3_prefixes"])
3023 or current_rule["prefixes"],
3024 "delimiters": _sorted_or_none(module.params["s3_delimiters"])
3025 or current_rule["delimiters"],
3026 "effect": _default(module.params["effect"], current_rule["effect"]),
3027 }
3028 if current_rule != new_rule:
3029 changed = True
3030 if not module.check_mode:
3031 conditions = PolicyRuleObjectAccessCondition(
3032 source_ips=new_rule["ips"],
3033 s3_prefixes=new_rule["prefixes"],
3034 s3_delimiters=new_rule["delimiters"],
3035 )
3036 rule_kwargs = {
3037 "actions": new_rule["actions"],
3038 "resources": new_rule["resources"],
3039 "conditions": conditions,
3040 }
3041 if new_rule["effect"] is not None:
3042 rule_kwargs["effect"] = new_rule["effect"]
3043 # PolicyRuleObjectAccess is the response model and drops
3044 # `effect` from the request body; the PATCH body model
3045 # (as the SDK itself types it) is PolicyRuleObjectAccessPost.
3046 rule = PolicyRuleObjectAccessPost(**rule_kwargs)
3047 if (
3048 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3049 and module.params["context"]
3050 ):
3051 res = blade.patch_object_store_access_policies_rules(
3052 policy_names=[policy_name],
3053 names=[module.params["rule"]],
3054 rule=rule,
3055 enforce_action_restrictions=module.params[
3056 "ignore_enforcement"
3057 ],
3058 context_names=[module.params["context"]],
3059 )
3060 else:
3061 res = blade.patch_object_store_access_policies_rules(
3062 policy_names=[policy_name],
3063 names=[module.params["rule"]],
3064 rule=rule,
3065 enforce_action_restrictions=module.params[
3066 "ignore_enforcement"
3067 ],
3068 )
3069 if res.status_code != 200:
3070 module.fail_json(
3071 msg="Failed to update rule {0} in policy {1}. Error: {2}".format(
3072 module.params["rule"],
3073 policy_name,
3074 get_error_message(res),
3075 )
3076 )
3077 if module.params["user"]:
3078 member_name = module.params["account"] + "/" + module.params["user"]
3079 if (
3080 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3081 and module.params["context"]
3082 ):
3083 res = blade.get_object_store_access_policies_object_store_users(
3084 policy_names=[policy_name],
3085 member_names=[member_name],
3086 context_names=[module.params["context"]],
3087 )
3088 else:
3089 res = blade.get_object_store_access_policies_object_store_users(
3090 policy_names=[policy_name], member_names=[member_name]
3091 )
3092 if res.status_code != 200 or (
3093 res.status_code == 200 and res.total_item_count == 0
3094 ):
3095 changed = True
3096 if not module.check_mode:
3097 if (
3098 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3099 and module.params["context"]
3100 ):
3101 res = blade.post_object_store_access_policies_object_store_users(
3102 member_names=[member_name],
3103 policy_names=[policy_name],
3104 context_names=[module.params["context"]],
3105 )
3106 else:
3107 res = blade.post_object_store_access_policies_object_store_users(
3108 member_names=[member_name], policy_names=[policy_name]
3109 )
3110 if res.status_code != 200:
3111 module.fail_json(
3112 msg="Failed to add user {0} to policy {1}. Error: {2}".format(
3113 member_name, policy_name, get_error_message(res)
3114 )
3115 )
3116 module.exit_json(changed=changed)
3119def copy_os_policy_rule(module, blade):
3120 """Copy an existing policy rule to a new policy"""
3121 changed = True
3122 versions = get_rest_api_version(blade)
3123 policy_name = module.params["account"] + "/" + module.params["name"]
3124 if not module.params["target_rule"]:
3125 module.params["target_rule"] = module.params["rule"]
3126 if (
3127 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3128 and module.params["context"]
3129 ):
3130 res = blade.get_object_store_access_policies_rules(
3131 policy_names=[module.params["target"]],
3132 names=[module.params["target_rule"]],
3133 context_names=[module.params["context"]],
3134 )
3135 else:
3136 res = blade.get_object_store_access_policies_rules(
3137 policy_names=[module.params["target"]], names=[module.params["target_rule"]]
3138 )
3139 if res.status_code == 200:
3140 module.fail_json(
3141 msg="Target rule {0} already exists in policy {1}".format(
3142 module.params["target_rule"], policy_name
3143 )
3144 )
3145 if (
3146 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3147 and module.params["context"]
3148 ):
3149 current_rule = list(
3150 blade.get_object_store_access_policies_rules(
3151 policy_names=[policy_name],
3152 names=[module.params["rule"]],
3153 context_names=[module.params["context"]],
3154 ).items
3155 )[0]
3156 else:
3157 current_rule = list(
3158 blade.get_object_store_access_policies_rules(
3159 policy_names=[policy_name], names=[module.params["rule"]]
3160 ).items
3161 )[0]
3162 if not module.check_mode:
3163 conditions = PolicyRuleObjectAccessCondition(
3164 source_ips=current_rule.conditions.source_ips,
3165 s3_delimiters=current_rule.conditions.s3_delimiters,
3166 s3_prefixes=current_rule.conditions.s3_prefixes,
3167 )
3168 rule_kwargs = {
3169 "actions": current_rule.actions,
3170 "resources": current_rule.resources,
3171 "conditions": conditions,
3172 }
3173 if getattr(current_rule, "effect", None) is not None:
3174 # Keep a deny rule a deny rule when it is copied.
3175 rule_kwargs["effect"] = current_rule.effect
3176 rule = PolicyRuleObjectAccessPost(**rule_kwargs)
3177 if (
3178 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3179 and module.params["context"]
3180 ):
3181 res = blade.post_object_store_access_policies_rules(
3182 policy_names=module.params["target"],
3183 names=[module.params["target_rule"]],
3184 enforce_action_restrictions=module.params["ignore_enforcement"],
3185 rule=rule,
3186 context_names=[module.params["context"]],
3187 )
3188 else:
3189 res = blade.post_object_store_access_policies_rules(
3190 policy_names=module.params["target"],
3191 names=[module.params["target_rule"]],
3192 enforce_action_restrictions=module.params["ignore_enforcement"],
3193 rule=rule,
3194 )
3195 if res.status_code != 200:
3196 module.fail_json(
3197 msg="Failed to copy rule {0} from policy {1} to policy {2}. "
3198 "Error: {3}".format(
3199 module.params["rule"],
3200 policy_name,
3201 module.params["target"],
3202 get_error_message(res),
3203 )
3204 )
3205 module.exit_json(changed=changed)
3208def delete_snap_policy(module, blade):
3209 """Delete snapshot policy
3211 If any rule parameters are provided then delete any rules that match
3212 all of the parameters provided.
3213 If no rule parameters are provided delete the entire policy
3214 """
3216 changed = False
3217 versions = get_rest_api_version(blade)
3218 rule_delete = False
3219 if (
3220 module.params["at"]
3221 or module.params["every"]
3222 or module.params["timezone"]
3223 or module.params["keep_for"]
3224 ):
3225 rule_delete = True
3226 if rule_delete:
3227 if (
3228 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3229 and module.params["context"]
3230 ):
3231 current_rules = list(
3232 blade.get_policies(
3233 names=[module.params["name"]],
3234 context_names=[module.params["context"]],
3235 ).items
3236 )[0].rules
3237 else:
3238 current_rules = list(
3239 blade.get_policies(names=[module.params["name"]]).items
3240 )[0].rules
3241 for rule in current_rules:
3242 current_rule = {
3243 "at": rule.at,
3244 "every": rule.every,
3245 "keep_for": rule.keep_for,
3246 "time_zone": rule.time_zone,
3247 }
3248 if not module.params["at"]:
3249 delete_at = rule.at
3250 else:
3251 delete_at = time_to_milliseconds(module.params["at"])
3252 if module.params["keep_for"]:
3253 delete_keep_for = module.params["keep_for"]
3254 else:
3255 delete_keep_for = int(rule.keep_for / 1000)
3256 if module.params["every"]:
3257 delete_every = module.params["every"]
3258 else:
3259 delete_every = int(rule.every / 1000)
3260 if not module.params["timezone"]:
3261 delete_tz = rule.time_zone
3262 else:
3263 delete_tz = module.params["timezone"]
3264 delete_rule = {
3265 "at": delete_at,
3266 "every": delete_every * 1000,
3267 "keep_for": delete_keep_for * 1000,
3268 "time_zone": delete_tz,
3269 }
3270 if current_rule == delete_rule:
3271 changed = True
3272 attr = SnapshotPolicyPatch(remove_rules=[delete_rule])
3273 if not module.check_mode:
3274 if (
3275 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3276 and module.params["context"]
3277 ):
3278 res = blade.patch_policies(
3279 destroy_snapshots=module.params["destroy_snapshots"],
3280 names=[module.params["name"]],
3281 policy=attr,
3282 context_names=[module.params["context"]],
3283 )
3284 else:
3285 res = blade.patch_policies(
3286 destroy_snapshots=module.params["destroy_snapshots"],
3287 names=[module.params["name"]],
3288 policy=attr,
3289 )
3290 if res.status_code != 200:
3291 module.fail_json(
3292 msg="Failed to delete policy rule {0}. Error: {1}".format(
3293 module.params["name"], get_error_message(res)
3294 )
3295 )
3296 else:
3297 changed = True
3298 if not module.check_mode:
3299 if (
3300 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3301 and module.params["context"]
3302 ):
3303 res = blade.delete_policies(
3304 names=[module.params["name"]],
3305 context_names=[module.params["context"]],
3306 )
3307 else:
3308 res = blade.delete_policies(names=[module.params["name"]])
3309 if res.status_code != 200:
3310 module.fail_json(
3311 msg="Failed to delete policy {0}. Error: {1}".format(
3312 module.params["name"], get_error_message(res)
3313 )
3314 )
3315 module.exit_json(changed=changed)
3318def create_snap_policy(module, blade):
3319 """Create snapshot policy"""
3320 changed = True
3321 versions = get_rest_api_version(blade)
3322 if (
3323 module.params["keep_for"]
3324 and not module.params["every"]
3325 or module.params["every"]
3326 and not module.params["keep_for"]
3327 ):
3328 module.fail_json(msg="`keep_for` and `every` are required.")
3329 if module.params["timezone"] and not module.params["at"]:
3330 module.fail_json(msg="`timezone` requires `at` to be provided.")
3331 if module.params["at"] and not module.params["every"]:
3332 module.fail_json(msg="`at` requires `every` to be provided.")
3334 if not module.check_mode:
3335 if module.params["at"] and module.params["every"]:
3336 if not module.params["every"] % 86400 == 0:
3337 module.fail_json(
3338 msg="At time can only be set if every value is a multiple of 86400"
3339 )
3340 if not module.params["timezone"]:
3341 module.params["timezone"] = _get_local_tz(module)
3342 if module.params["timezone"] not in pytz.all_timezones_set:
3343 module.fail_json(
3344 msg="Timezone {0} is not valid".format(
3345 module.params["timezone"]
3346 )
3347 )
3348 if not module.params["keep_for"]:
3349 module.params["keep_for"] = 0
3350 if not module.params["every"]:
3351 module.params["every"] = 0
3352 if module.params["keep_for"] < module.params["every"]:
3353 module.fail_json(
3354 msg="Retention period cannot be less than snapshot interval."
3355 )
3356 if module.params["at"] and not module.params["timezone"]:
3357 module.params["timezone"] = _get_local_tz(module)
3358 if module.params["timezone"] not in set(pytz.all_timezones_set):
3359 module.fail_json(
3360 msg="Timezone {0} is not valid".format(module.params["timezone"])
3361 )
3363 if module.params["keep_for"]:
3364 if not 300 <= module.params["keep_for"] <= 34560000:
3365 module.fail_json(
3366 msg="keep_for parameter is out of range (300 to 34560000)"
3367 )
3368 if not 300 <= module.params["every"] <= 34560000:
3369 module.fail_json(
3370 msg="every parameter is out of range (300 to 34560000)"
3371 )
3372 if module.params["at"]:
3373 attr = Policy(
3374 enabled=module.params["enabled"],
3375 rules=[
3376 PolicyRule(
3377 keep_for=module.params["keep_for"] * 1000,
3378 every=module.params["every"] * 1000,
3379 at=time_to_milliseconds(module.params["at"]),
3380 time_zone=module.params["timezone"],
3381 )
3382 ],
3383 )
3384 else:
3385 attr = Policy(
3386 enabled=module.params["enabled"],
3387 rules=[
3388 PolicyRule(
3389 keep_for=module.params["keep_for"] * 1000,
3390 every=module.params["every"] * 1000,
3391 )
3392 ],
3393 )
3394 else:
3395 attr = Policy(enabled=module.params["enabled"])
3396 if (
3397 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3398 and module.params["context"]
3399 ):
3400 res = blade.post_policies(
3401 names=[module.params["name"]],
3402 policy=attr,
3403 context_names=[module.params["context"]],
3404 )
3405 else:
3406 res = blade.post_policies(names=[module.params["name"]], policy=attr)
3407 if res.status_code != 200:
3408 module.fail_json(
3409 msg="Failed to create snapshot policy {0}. Error: {1}".format(
3410 module.params["name"], get_error_message(res)
3411 )
3412 )
3413 if module.params["filesystem"]:
3414 for filesystem in module.params["filesystem"]:
3415 if (
3416 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3417 and module.params["context"]
3418 ):
3419 res = blade.get_file_systems(
3420 names=[filesystem],
3421 destroyed=False,
3422 context_names=[module.params["context"]],
3423 )
3424 else:
3425 res = blade.get_file_systems(names=[filesystem], destroyed=False)
3426 if res.status_code != 200:
3427 module.fail_json(
3428 msg="Filesystems to assign to {0} does not "
3429 "exist, or is deleted.".format(module.params["name"])
3430 )
3431 if (
3432 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3433 and module.params["context"]
3434 ):
3435 res = blade.post_policies_file_systems(
3436 policy_names=[module.params["name"]],
3437 member_names=[filesystem],
3438 context_names=[module.params["context"]],
3439 )
3440 else:
3441 res = blade.post_policies_file_systems(
3442 policy_names=[module.params["name"]],
3443 member_names=[filesystem],
3444 )
3445 if res.status_code != 200:
3446 module.fail_json(
3447 msg="Failed to add filesystem {0} to "
3448 "policy {1}. Error: {2}.".format(
3449 filesystem, module.params["name"], get_error_message(res)
3450 )
3451 )
3452 if module.params["replica_link"]:
3453 repl_link = []
3454 for link in module.params["replica_link"]:
3455 if (
3456 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3457 and module.params["context"]
3458 ):
3459 res = blade.get_file_system_replica_links(
3460 local_file_system_names=[link],
3461 context_names=[module.params["context"]],
3462 )
3463 else:
3464 res = blade.get_file_system_replica_links(
3465 local_file_system_names=[link]
3466 )
3467 if res.status_code != 200:
3468 module.fail_json(
3469 msg="Replica Link {0} does not exist.".format(link)
3470 )
3471 else:
3472 repl_link = list(res.items)[0]
3473 if (
3474 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3475 and module.params["context"]
3476 ):
3477 res = blade.post_policies_file_system_replica_links(
3478 policy_names=[module.params["name"]],
3479 local_file_system_names=[link],
3480 remote_names=[repl_link.remote.name],
3481 context_names=[module.params["context"]],
3482 )
3483 else:
3484 res = blade.post_policies_file_system_replica_links(
3485 policy_names=[module.params["name"]],
3486 local_file_system_names=[link],
3487 remote_names=[repl_link.remote.name],
3488 )
3489 if res.status_code != 200:
3490 module.fail_json(
3491 msg="Failed to connect filesystem replicsa link {0} to policy {1}. "
3492 "Error: {2}".format(
3493 link, module.params["name"], get_error_message(res)
3494 )
3495 )
3496 module.exit_json(changed=changed)
3499def update_snap_policy(module, blade):
3500 """Update snapshot policy
3502 Add new rules to the policy using this function.
3503 Should it be necessary to modify an existing rule these are the rules:
3505 Due to the 'best fit' nature of Purity we only add new rulkes in this function.
3506 If you trying to update an existing rule, then this should be done by deleting
3507 the current rule and then adding the new rule.
3509 Purity may recover some snapshots as long as the add happens before the eradication delay
3510 (typically 24h) causes the snapshots to be eradicated.
3511 """
3513 changed = False
3514 versions = get_rest_api_version(blade)
3515 if (
3516 module.params["keep_for"]
3517 and not module.params["every"]
3518 or module.params["every"]
3519 and not module.params["keep_for"]
3520 ):
3521 module.fail_json(msg="`keep_for` and `every` are required.")
3522 if module.params["timezone"] and not module.params["at"]:
3523 module.fail_json(msg="`timezone` requires `at` to be provided.")
3524 if module.params["at"] and not module.params["every"]:
3525 module.fail_json(msg="`at` requires `every` to be provided.")
3526 ctx = _context_kwargs(module, versions)
3527 current_policy = list(
3528 blade.get_policies(names=[module.params["name"]], **ctx).items
3529 )[0]
3530 current_rules = current_policy.rules or []
3531 # Rules and the enabled state are only reconciled for state=present; a
3532 # state=absent call reaches here to detach members and must not re-enable
3533 # the policy through the default of `enabled`.
3534 if module.params["state"] == "present":
3535 # Snapshot policies carry a single schedule rule. Fall back to the
3536 # existing rule's value for any field the task did not specify, so a
3537 # partial update (e.g. changing only keep_for) does not reset the
3538 # other fields to zero.
3539 existing_rule = current_rules[0] if current_rules else None
3540 if module.params["at"]:
3541 new_at = time_to_milliseconds(module.params["at"])
3542 elif existing_rule is not None:
3543 new_at = existing_rule.at
3544 else:
3545 new_at = None
3546 if module.params["keep_for"]:
3547 new_keep_for = module.params["keep_for"]
3548 elif existing_rule is not None:
3549 new_keep_for = int(existing_rule.keep_for / 1000)
3550 else:
3551 new_keep_for = 0
3552 if module.params["every"]:
3553 new_every = module.params["every"]
3554 elif existing_rule is not None:
3555 new_every = int(existing_rule.every / 1000)
3556 else:
3557 new_every = 0
3558 if module.params["timezone"]:
3559 new_tz = module.params["timezone"]
3560 elif existing_rule is not None:
3561 new_tz = existing_rule.time_zone
3562 else:
3563 new_tz = None
3564 new_rule = {
3565 "at": new_at,
3566 "every": new_every * 1000,
3567 "keep_for": new_keep_for * 1000,
3568 "time_zone": new_tz,
3569 }
3570 # Only add the rule when no existing rule already matches it, so a
3571 # second rule is not re-added on every run.
3572 rule_exists = any(
3573 {
3574 "at": rule.at,
3575 "every": rule.every,
3576 "keep_for": rule.keep_for,
3577 "time_zone": rule.time_zone,
3578 }
3579 == new_rule
3580 for rule in current_rules
3581 )
3582 add_rule = bool(new_every or new_keep_for) and not rule_exists
3583 # The enabled state is reconciled on its own, not only when a rule is
3584 # added.
3585 enable_change = current_policy.enabled != module.params["enabled"]
3586 if add_rule or enable_change:
3587 changed = True
3588 if not module.check_mode:
3589 if add_rule:
3590 if new_at is not None and new_every % 86400 != 0:
3591 module.fail_json(
3592 msg="At time can only be set if every value is a "
3593 "multiple of 86400"
3594 )
3595 if new_at is not None and not new_tz:
3596 new_tz = _get_local_tz(module)
3597 if new_tz not in pytz.all_timezones_set:
3598 module.fail_json(
3599 msg="Timezone {0} is not valid".format(new_tz)
3600 )
3601 if new_keep_for < new_every:
3602 module.fail_json(
3603 msg="Retention period cannot be less than snapshot interval."
3604 )
3605 if not 300 <= new_keep_for <= 34560000:
3606 module.fail_json(
3607 msg="keep_for parameter is out of range (300 to 34560000)"
3608 )
3609 if not 300 <= new_every <= 34560000:
3610 module.fail_json(
3611 msg="every parameter is out of range (300 to 34560000)"
3612 )
3613 rule_kwargs = {
3614 "keep_for": new_keep_for * 1000,
3615 "every": new_every * 1000,
3616 }
3617 if new_at is not None:
3618 rule_kwargs["at"] = new_at
3619 rule_kwargs["time_zone"] = new_tz
3620 attr = SnapshotPolicyPatch(
3621 enabled=module.params["enabled"],
3622 add_rules=[PolicyRule(**rule_kwargs)],
3623 )
3624 else:
3625 attr = SnapshotPolicyPatch(enabled=module.params["enabled"])
3626 res = blade.patch_policies(
3627 names=[module.params["name"]], policy=attr, **ctx
3628 )
3629 if res.status_code != 200:
3630 module.fail_json(
3631 msg="Failed to update snapshot policy {0}. Error: {1}".format(
3632 module.params["name"], get_error_message(res)
3633 )
3634 )
3636 if module.params["filesystem"]:
3637 current_filesystems = []
3638 if (
3639 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3640 and module.params["context"]
3641 ):
3642 policy_fs_details = list(
3643 blade.get_policies_file_systems(
3644 policy_names=[module.params["name"]],
3645 context_names=[module.params["context"]],
3646 ).items
3647 )
3648 else:
3649 policy_fs_details = list(
3650 blade.get_policies_file_systems(
3651 policy_names=[module.params["name"]]
3652 ).items
3653 )
3654 for member in policy_fs_details:
3655 current_filesystems.append(member.member.name)
3656 if module.params["state"] == "present":
3657 difference_set = [
3658 item
3659 for item in module.params["filesystem"]
3660 if item not in current_filesystems
3661 ]
3662 for new_fs in difference_set:
3663 changed = True
3664 if (
3665 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3666 and module.params["context"]
3667 ):
3668 res = blade.get_file_systems(
3669 names=[new_fs],
3670 destroyed=False,
3671 context_names=[module.params["context"]],
3672 )
3673 else:
3674 res = blade.get_file_systems(names=[new_fs], destroyed=False)
3675 if res.status_code != 200:
3676 module.fail_json(
3677 msg="Filesystem {0} to assign to {1} does not "
3678 "exist, or is deleted.".format(new_fs, module.params["name"])
3679 )
3680 if (
3681 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3682 and module.params["context"]
3683 ):
3684 res = blade.post_policies_file_systems(
3685 policy_names=[module.params["name"]],
3686 member_names=[new_fs],
3687 context_names=[module.params["context"]],
3688 )
3689 else:
3690 res = blade.post_policies_file_systems(
3691 policy_names=[module.params["name"]],
3692 member_names=[new_fs],
3693 )
3694 if res.status_code != 200:
3695 module.fail_json(
3696 msg="Failed to add filesystem {0} to "
3697 "policy {1}. Error: {2}.".format(
3698 new_fs, module.params["name"], get_error_message(res)
3699 )
3700 )
3701 else:
3702 for old_fs in module.params["filesystem"]:
3703 if old_fs in current_filesystems:
3704 changed = True
3705 if (
3706 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3707 and module.params["context"]
3708 ):
3709 res = blade.delete_policies_file_systems(
3710 policy_names=[module.params["name"]],
3711 member_names=[old_fs],
3712 context_names=[module.params["context"]],
3713 )
3714 else:
3715 res = blade.delete_policies_file_systems(
3716 policy_names=[module.params["name"]],
3717 member_names=[old_fs],
3718 )
3719 if res.status_code != 200:
3720 module.fail_json(
3721 msg="Failed to remove filesystem {0} from "
3722 "policy {1}. Error: {2}.".format(
3723 old_fs, module.params["name"], get_error_message(res)
3724 )
3725 )
3726 if module.params["replica_link"]:
3727 current_rls = []
3728 if (
3729 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3730 and module.params["context"]
3731 ):
3732 policy_rl_details = list(
3733 blade.get_policies_file_system_replica_links(
3734 policy_names=[module.params["name"]],
3735 context_names=[module.params["context"]],
3736 ).items
3737 )
3738 else:
3739 policy_rl_details = list(
3740 blade.get_policies_file_system_replica_links(
3741 policy_names=[module.params["name"]]
3742 ).items
3743 )
3744 for member in policy_rl_details:
3745 current_rls.append(member.member.name)
3746 if module.params["state"] == "present":
3747 difference_set = [
3748 item
3749 for item in module.params["replica_link"]
3750 if item not in current_rls
3751 ]
3752 for new_rl in difference_set:
3753 changed = True
3754 if (
3755 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3756 and module.params["context"]
3757 ):
3758 res = blade.get_file_systems_replica_links(
3759 names=[new_rl], context_names=[module.params["context"]]
3760 )
3761 else:
3762 res = blade.get_file_systems_replica_links(names=[new_rl])
3763 if res.status_code != 200:
3764 module.fail_json(
3765 msg="Replica link {0} to assign to {1} does not "
3766 "exist, or is deleted.".format(new_rl, module.params["name"])
3767 )
3768 if (
3769 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3770 and module.params["context"]
3771 ):
3772 res = blade.post_policies_file_system_replica_links(
3773 policy_names=[module.params["name"]],
3774 member_names=[new_rl],
3775 context_names=[module.params["context"]],
3776 )
3777 else:
3778 res = blade.post_policies_file_system_replica_links(
3779 policy_names=[module.params["name"]],
3780 member_names=[new_rl],
3781 )
3782 if res.status_code != 200:
3783 module.fail_json(
3784 msg="Failed to add replica link {0} to "
3785 "policy {1}. Error: {2}.".format(
3786 new_rl, module.params["name"], get_error_message(res)
3787 )
3788 )
3789 else:
3790 for old_rl in module.params["replica_link"]:
3791 if old_rl in current_rls:
3792 changed = True
3793 if (
3794 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
3795 and module.params["context"]
3796 ):
3797 res = blade.delete_policies_file_system_replica_links(
3798 policy_names=[module.params["name"]],
3799 member_names=[old_rl],
3800 context_names=[module.params["context"]],
3801 )
3802 else:
3803 res = blade.delete_policies_file_system_replica_links(
3804 policy_names=[module.params["name"]],
3805 member_names=[old_rl],
3806 )
3807 if res.status_code != 200:
3808 module.fail_json(
3809 msg="Failed to remove replica link {0} from "
3810 "policy {1}. Error: {2}.".format(
3811 old_rl, module.params["name"], get_error_message(res)
3812 )
3813 )
3815 module.exit_json(changed=changed)
3818def rename_snap_policy(module, blade):
3819 """Rename snapshot policy"""
3821 changed = True
3822 versions = get_rest_api_version(blade)
3823 if not module.check_mode:
3824 # Policy keeps `name` in the request body (SnapshotPolicyPatch strips
3825 # it as read-only), so this is the only way to ask for a rename.
3826 res = blade.patch_policies(
3827 names=[module.params["name"]],
3828 policy=Policy(name=module.params["rename"]),
3829 **_context_kwargs(module, versions),
3830 )
3831 if res.status_code != 200:
3832 module.fail_json(
3833 msg="Failed to rename snapshot policy {0} to {1}. Error: {2}".format(
3834 module.params["name"],
3835 module.params["rename"],
3836 get_error_message(res),
3837 )
3838 )
3839 # Purity//FB (verified on 4.6.4, REST 2.21) answers 200 to a body of
3840 # {"name": ...} on PATCH /policies but silently keeps the old name,
3841 # whether addressed by names or ids: snapshot policies have no rename
3842 # on the array. Check the result rather than report a rename that
3843 # never happened.
3844 res = blade.get_policies(
3845 names=[module.params["rename"]], **_context_kwargs(module, versions)
3846 )
3847 if res.status_code != 200:
3848 module.fail_json(
3849 msg="Failed to rename snapshot policy {0} to {1}: the array "
3850 "accepted the request but kept the old name. Purity//FB REST "
3851 "{2} does not support renaming snapshot policies.".format(
3852 module.params["name"], module.params["rename"], versions
3853 )
3854 )
3855 module.exit_json(changed=changed)
3858def _copy_rule_policy(module, blade, source, versions, ctx):
3859 """Create the target of a copy for NFS and SMB policies.
3861 The target is created empty, disabled if the source is disabled, and then
3862 every source rule is re-created on it in evaluation order. Returns the
3863 last SDK response so the caller can report a failure.
3864 """
3865 policy_type = module.params["policy_type"]
3866 target = module.params["target"]
3867 if policy_type == "nfs":
3868 poster = blade.post_nfs_export_policies
3869 patcher = blade.patch_nfs_export_policies
3870 rule_getter = blade.get_nfs_export_policies_rules
3871 rule_poster = blade.post_nfs_export_policies_rules
3872 policy_model = NfsExportPolicy
3873 elif policy_type == "smb_client":
3874 poster = blade.post_smb_client_policies
3875 patcher = blade.patch_smb_client_policies
3876 rule_getter = blade.get_smb_client_policies_rules
3877 rule_poster = blade.post_smb_client_policies_rules
3878 policy_model = SmbClientPolicy
3879 else:
3880 poster = blade.post_smb_share_policies
3881 patcher = blade.patch_smb_share_policies
3882 rule_getter = blade.get_smb_share_policies_rules
3883 rule_poster = blade.post_smb_share_policies_rules
3884 policy_model = SmbSharePolicy
3885 res = poster(names=[target], **ctx)
3886 if res.status_code != 200:
3887 return res
3888 if not source.enabled:
3889 res = patcher(names=[target], policy=policy_model(enabled=False), **ctx)
3890 if res.status_code != 200:
3891 return res
3892 rules_res = rule_getter(policy_names=[source.name], **ctx)
3893 rules = list(rules_res.items) if rules_res.status_code == 200 else []
3894 if policy_type != "smb_share":
3895 # Client rules are evaluated in index order; share rules have no index.
3896 rules.sort(key=lambda rule: rule.index)
3897 for rule in rules:
3898 if policy_type == "nfs":
3899 new_rule = _nfs_rule(
3900 versions,
3901 client=rule.client,
3902 permission=rule.permission,
3903 access=getattr(rule, "access", None),
3904 anonuid=_anon_id(getattr(rule, "anonuid", None)),
3905 anongid=_anon_id(getattr(rule, "anongid", None)),
3906 fileid_32bit=rule.fileid_32bit,
3907 atime=rule.atime,
3908 secure=rule.secure,
3909 security=rule.security,
3910 )
3911 elif policy_type == "smb_client":
3912 if LooseVersion(SMB_ENCRYPT_API_VERSION) <= LooseVersion(versions):
3913 new_rule = SmbClientPolicyRule(
3914 client=rule.client,
3915 permission=rule.permission,
3916 encryption=rule.encryption,
3917 )
3918 else:
3919 new_rule = SmbClientPolicyRule(
3920 client=rule.client, permission=rule.permission
3921 )
3922 else:
3923 new_rule = SmbSharePolicyRule(
3924 principal=rule.principal,
3925 read=rule.read,
3926 change=rule.change,
3927 full_control=rule.full_control,
3928 )
3929 res = rule_poster(policy_names=[target], rule=new_rule, **ctx)
3930 if res.status_code != 200:
3931 return res
3932 return res
3935def copy_policy(module, blade, source):
3936 """Copy a snapshot, NFS, SMB or WORM policy to a new policy I(target).
3938 The REST API has no copy operation for these policy types, so the target
3939 is created with the source settings and the source rules are re-created
3940 on it. Network access policies cannot be created, so they cannot be
3941 copied either.
3942 """
3943 changed = True
3944 versions = get_rest_api_version(blade)
3945 ctx = _context_kwargs(module, versions)
3946 policy_type = module.params["policy_type"]
3947 target = module.params["target"]
3948 if not target:
3949 module.fail_json(msg="`target` is required when state is copy")
3950 if not source:
3951 module.fail_json(msg="Policy {0} does not exist".format(module.params["name"]))
3952 getters = {
3953 "snapshot": blade.get_policies,
3954 "nfs": blade.get_nfs_export_policies,
3955 "smb_client": blade.get_smb_client_policies,
3956 "smb_share": blade.get_smb_share_policies,
3957 "worm": blade.get_worm_data_policies,
3958 }
3959 if policy_type not in getters:
3960 module.fail_json(
3961 msg="Copy is not supported for {0} policies".format(policy_type)
3962 )
3963 if getters[policy_type](names=[target], **ctx).status_code == 200:
3964 module.fail_json(msg="Target policy {0} already exists".format(target))
3965 if not module.check_mode:
3966 if policy_type == "snapshot":
3967 policy_kwargs = {"enabled": source.enabled}
3968 rules = []
3969 for rule in source.rules or []:
3970 rule_kwargs = {"every": rule.every, "keep_for": rule.keep_for}
3971 if rule.at is not None:
3972 rule_kwargs["at"] = rule.at
3973 rule_kwargs["time_zone"] = rule.time_zone
3974 rules.append(PolicyRule(**rule_kwargs))
3975 if rules:
3976 policy_kwargs["rules"] = rules
3977 res = blade.post_policies(
3978 names=[target], policy=Policy(**policy_kwargs), **ctx
3979 )
3980 elif policy_type == "worm":
3981 res = blade.post_worm_data_policies(
3982 names=[target],
3983 policy=WormDataPolicy(
3984 enabled=source.enabled,
3985 mode=source.mode,
3986 min_retention=source.min_retention,
3987 max_retention=source.max_retention,
3988 default_retention=source.default_retention,
3989 retention_lock=source.retention_lock,
3990 ),
3991 **ctx,
3992 )
3993 else:
3994 res = _copy_rule_policy(module, blade, source, versions, ctx)
3995 if res.status_code != 200:
3996 module.fail_json(
3997 msg="Failed to copy policy {0} to {1}. Error: {2}".format(
3998 module.params["name"], target, get_error_message(res)
3999 )
4000 )
4001 module.exit_json(changed=changed)
4004def main():
4005 argument_spec = purefb_argument_spec()
4006 argument_spec.update(
4007 dict(
4008 state=dict(
4009 type="str", default="present", choices=["absent", "present", "copy"]
4010 ),
4011 policy_type=dict(
4012 type="str",
4013 default="snapshot",
4014 choices=[
4015 "snapshot",
4016 "access",
4017 "nfs",
4018 "smb_share",
4019 "smb_client",
4020 "network",
4021 "worm",
4022 ],
4023 ),
4024 enabled=dict(type="bool", default=True),
4025 timezone=dict(type="str"),
4026 name=dict(type="str"),
4027 at=dict(type="str"),
4028 every=dict(type="int"),
4029 keep_for=dict(type="int"),
4030 filesystem=dict(type="list", elements="str"),
4031 replica_link=dict(type="list", elements="str"),
4032 account=dict(type="str"),
4033 target=dict(type="str"),
4034 target_rule=dict(type="str"),
4035 rename=dict(type="str"),
4036 rule=dict(type="str"),
4037 user=dict(type="str"),
4038 effect=dict(type="str", choices=["allow", "deny"]),
4039 actions=dict(
4040 type="list",
4041 elements="str",
4042 choices=[
4043 "s3:*",
4044 "s3:AbortMultipartUpload",
4045 "s3:BypassGovernanceRetention",
4046 "s3:CreateBucket",
4047 "s3:DeleteBucket",
4048 "s3:DeleteObject",
4049 "s3:DeleteObjectVersion",
4050 "s3:ExtendSafemodeRetentionPeriod",
4051 "s3:GetBucketAcl",
4052 "s3:GetBucketLocation",
4053 "s3:GetBucketVersioning",
4054 "s3:GetLifecycleConfiguration",
4055 "s3:GetObject",
4056 "s3:GetObjectAcl",
4057 "s3:GetObjectLegalHold",
4058 "s3:GetObjectLockConfiguration",
4059 "s3:GetObjectRetention",
4060 "s3:GetObjectTagging",
4061 "s3:GetObjectVersion",
4062 "s3:GetObjectVersionTagging",
4063 "s3:ListAllMyBuckets",
4064 "s3:ListBucket",
4065 "s3:ListBucketMultipartUploads",
4066 "s3:ListBucketVersions",
4067 "s3:ListMultipartUploadParts",
4068 "s3:PutBucketVersioning",
4069 "s3:PutLifecycleConfiguration",
4070 "s3:PutObject",
4071 "s3:PutObjectLegalHold",
4072 "s3:PutObjectLockConfiguration",
4073 "s3:PutObjectRetention",
4074 "s3:ResolveSafemodeConflicts",
4075 ],
4076 ),
4077 object_resources=dict(type="list", elements="str"),
4078 source_ips=dict(type="list", elements="str"),
4079 s3_prefixes=dict(type="list", elements="str"),
4080 s3_delimiters=dict(type="list", elements="str"),
4081 ignore_enforcement=dict(type="bool", default=True),
4082 force_delete=dict(type="bool", default=False),
4083 # Rule fields carry no argspec default so that an update only
4084 # touches what the task set; the defaults are applied on create.
4085 access=dict(
4086 type="str",
4087 choices=["root-squash", "all-squash", "no-squash"],
4088 ),
4089 anonuid=dict(type="int"),
4090 anongid=dict(type="int"),
4091 atime=dict(type="bool"),
4092 client=dict(type="str"),
4093 fileid_32bit=dict(type="bool"),
4094 permission=dict(type="str", choices=["rw", "ro"]),
4095 secure=dict(type="bool"),
4096 destroy_snapshots=dict(type="bool", default=False),
4097 security=dict(
4098 type="list",
4099 elements="str",
4100 choices=["sys", "krb5", "krb5i", "krb5p"],
4101 ),
4102 before_rule=dict(type="int"),
4103 principal=dict(type="str"),
4104 change=dict(type="str", choices=["deny", "allow", ""]),
4105 read=dict(type="str", choices=["deny", "allow", ""]),
4106 full_control=dict(type="str", choices=["deny", "allow", ""]),
4107 smb_encryption=dict(
4108 type="str",
4109 choices=["disabled", "optional", "required"],
4110 ),
4111 desc=dict(type="str", default=""),
4112 interfaces=dict(
4113 type="list",
4114 elements="str",
4115 choices=[
4116 "management-ssh",
4117 "management-rest-api",
4118 "management-web-ui",
4119 "snmp",
4120 "local-network-superuser-password-access",
4121 ],
4122 ),
4123 default_retention=dict(type="str"),
4124 min_retention=dict(type="str"),
4125 max_retention=dict(type="str"),
4126 retention_lock=dict(type="str", choices=["locked", "unlocked"]),
4127 context=dict(type="str", default=""),
4128 )
4129 )
4131 required_together = [["keep_for", "every"]]
4132 required_if = [
4133 ["policy_type", "access", ["account", "name"]],
4134 ["policy_type", "nfs", ["name"]],
4135 ["policy_type", "smb_client", ["name"]],
4136 ["policy_type", "smb_share", ["name"]],
4137 ["policy_type", "network", ["interfaces"]],
4138 ]
4140 module = AnsibleModule(
4141 argument_spec,
4142 required_together=required_together,
4143 required_if=required_if,
4144 supports_check_mode=True,
4145 )
4147 if not HAS_PYTZ:
4148 module.fail_json(msg="pytz is required for this module")
4150 state = module.params["state"]
4151 blade = get_system(module)
4152 versions = get_rest_api_version(blade)
4153 if (
4154 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4155 and not module.params["context"]
4156 ):
4157 # Only default the context to the local array name when this array is a
4158 # member of a fleet. A standalone array is "not in a fleet" and rejects
4159 # fleet context, so leave context unset - no context_names is then sent.
4160 fleet_res = blade.get_fleets()
4161 if fleet_res.status_code == 200 and list(fleet_res.items):
4162 module.params["context"] = list(blade.get_arrays().items)[0].name
4163 if module.params["policy_type"] == "access":
4164 if not HAS_PYPURECLIENT:
4165 module.fail_json(msg="py-pure-client sdk is required for this module")
4166 try:
4167 if (
4168 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4169 and module.params["context"]
4170 ):
4171 policy = list(
4172 blade.get_object_store_access_policies(
4173 names=[module.params["account"] + "/" + module.params["name"]],
4174 context_names=[module.params["context"]],
4175 ).items
4176 )[0]
4177 else:
4178 policy = list(
4179 blade.get_object_store_access_policies(
4180 names=[module.params["account"] + "/" + module.params["name"]]
4181 ).items
4182 )[0]
4183 except AttributeError:
4184 policy = None
4185 if module.params["user"]:
4186 member_name = module.params["account"] + "/" + module.params["user"]
4187 if (
4188 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4189 and module.params["context"]
4190 ):
4191 res = blade.get_object_store_users(
4192 names=[member_name], context_names=[module.params["context"]]
4193 )
4194 else:
4195 res = blade.get_object_store_users(names=[member_name])
4196 if res.status_code != 200:
4197 module.fail_json(
4198 msg="User {0} does not exist in account {1}. Error: {2}".format(
4199 module.params["user"],
4200 module.params["account"],
4201 get_error_message(res),
4202 )
4203 )
4204 if policy and state == "present":
4205 update_os_policy(module, blade)
4206 elif state == "present" and not policy:
4207 create_os_policy(module, blade)
4208 elif state == "absent" and policy:
4209 delete_os_policy(module, blade)
4210 elif state == "copy":
4211 if not module.params["target"] or not module.params["rule"]:
4212 module.fail_json(
4213 msg="`target` and `rule` are required to copy an access policy rule"
4214 )
4215 if not policy:
4216 module.fail_json(
4217 msg="Policy {0}/{1} does not exist".format(
4218 module.params["account"], module.params["name"]
4219 )
4220 )
4221 if "/" not in module.params["target"]:
4222 module.fail_json(
4223 msg='Incorrect format for target policy. Must be "<account>/<name>"'
4224 )
4225 if (
4226 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4227 and module.params["context"]
4228 ):
4229 res = blade.get_object_store_access_policies(
4230 names=[module.params["target"]],
4231 context_names=[module.params["context"]],
4232 )
4233 else:
4234 res = blade.get_object_store_access_policies(
4235 names=[module.params["target"]]
4236 )
4237 if res.status_code != 200:
4238 module.fail_json(
4239 msg="Target policy {0} does not exist".format(
4240 module.params["target"]
4241 )
4242 )
4243 copy_os_policy_rule(module, blade)
4244 elif module.params["policy_type"] == "nfs":
4245 new_policy = None
4246 if LooseVersion(NFS_POLICY_API_VERSION) > LooseVersion(versions):
4247 module.fail_json(
4248 msg=(
4249 "Minimum FlashBlade REST version required: {0}".format(
4250 NFS_POLICY_API_VERSION
4251 )
4252 )
4253 )
4254 if not HAS_PYPURECLIENT:
4255 module.fail_json(msg="py-pure-client sdk is required for this module")
4256 try:
4257 if (
4258 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4259 and module.params["context"]
4260 ):
4261 policy = list(
4262 blade.get_nfs_export_policies(
4263 names=[module.params["name"]],
4264 context_names=[module.params["context"]],
4265 ).items
4266 )[0]
4267 else:
4268 policy = list(
4269 blade.get_nfs_export_policies(names=[module.params["name"]]).items
4270 )[0]
4271 except AttributeError:
4272 policy = None
4273 if module.params["rename"]:
4274 try:
4275 if (
4276 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4277 and module.params["context"]
4278 ):
4279 new_policy = list(
4280 blade.get_nfs_export_policies(
4281 names=[module.params["rename"]],
4282 context_names=[module.params["context"]],
4283 ).items
4284 )[0]
4285 else:
4286 new_policy = list(
4287 blade.get_nfs_export_policies(
4288 names=[module.params["rename"]]
4289 ).items
4290 )[0]
4291 except AttributeError:
4292 new_policy = None
4293 if policy and state == "present" and not module.params["rename"]:
4294 if module.params["before_rule"]:
4295 if (
4296 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4297 and module.params["context"]
4298 ):
4299 res = blade.get_nfs_export_policies_rules(
4300 policy_names=[module.params["name"]],
4301 names=[
4302 module.params["name"]
4303 + "."
4304 + str(module.params["before_rule"])
4305 ],
4306 context_names=[module.params["context"]],
4307 )
4308 else:
4309 res = blade.get_nfs_export_policies_rules(
4310 policy_names=[module.params["name"]],
4311 names=[
4312 module.params["name"]
4313 + "."
4314 + str(module.params["before_rule"])
4315 ],
4316 )
4317 if res.status_code != 200:
4318 module.fail_json(
4319 msg="Rule index {0} does not exist.".format(
4320 module.params["before_rule"]
4321 )
4322 )
4323 update_nfs_policy(module, blade)
4324 elif (
4325 state == "present" and module.params["rename"] and policy and not new_policy
4326 ):
4327 rename_nfs_policy(module, blade)
4328 elif state == "present" and not policy and not module.params["rename"]:
4329 create_nfs_policy(module, blade)
4330 elif state == "absent" and policy:
4331 delete_nfs_policy(module, blade)
4332 elif state == "copy":
4333 copy_policy(module, blade, policy)
4334 elif module.params["policy_type"] == "smb_client":
4335 if LooseVersion(SMB_POLICY_API_VERSION) > LooseVersion(versions):
4336 module.fail_json(
4337 msg=(
4338 "Minimum FlashBlade REST version required: {0}".format(
4339 SMB_POLICY_API_VERSION
4340 )
4341 )
4342 )
4343 if not HAS_PYPURECLIENT:
4344 module.fail_json(msg="py-pure-client sdk is required for this module")
4345 try:
4346 if (
4347 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4348 and module.params["context"]
4349 ):
4350 policy = list(
4351 blade.get_smb_client_policies(
4352 names=[module.params["name"]],
4353 context_names=[module.params["context"]],
4354 ).items
4355 )[0]
4356 else:
4357 policy = list(
4358 blade.get_smb_client_policies(names=[module.params["name"]]).items
4359 )[0]
4360 except AttributeError:
4361 policy = None
4362 if module.params["rename"]:
4363 try:
4364 if (
4365 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4366 and module.params["context"]
4367 ):
4368 new_policy = list(
4369 blade.get_smb_client_policies(
4370 names=[module.params["rename"]],
4371 context_names=[module.params["context"]],
4372 ).items
4373 )[0]
4374 else:
4375 new_policy = list(
4376 blade.get_smb_client_policies(
4377 names=[module.params["rename"]]
4378 ).items
4379 )[0]
4380 except AttributeError:
4381 new_policy = None
4382 if policy and state == "present" and not module.params["rename"]:
4383 if module.params["before_rule"]:
4384 if (
4385 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4386 and module.params["context"]
4387 ):
4388 res = blade.get_smb_client_policies_rules(
4389 policy_names=[module.params["name"]],
4390 names=[
4391 module.params["name"]
4392 + "."
4393 + str(module.params["before_rule"])
4394 ],
4395 context_names=[module.params["context"]],
4396 )
4397 else:
4398 res = blade.get_smb_client_policies_rules(
4399 policy_names=[module.params["name"]],
4400 names=[
4401 module.params["name"]
4402 + "."
4403 + str(module.params["before_rule"])
4404 ],
4405 )
4406 if res.status_code != 200:
4407 module.fail_json(
4408 msg="Rule index {0} does not exist.".format(
4409 module.params["before_rule"]
4410 )
4411 )
4412 update_smb_client_policy(module, blade)
4413 elif (
4414 state == "present" and module.params["rename"] and policy and not new_policy
4415 ):
4416 rename_smb_client_policy(module, blade)
4417 elif state == "present" and not policy and not module.params["rename"]:
4418 create_smb_client_policy(module, blade)
4419 elif state == "absent" and policy:
4420 delete_smb_client_policy(module, blade)
4421 elif state == "copy":
4422 copy_policy(module, blade, policy)
4423 elif module.params["policy_type"] == "smb_share":
4424 if LooseVersion(SMB_POLICY_API_VERSION) > LooseVersion(versions):
4425 module.fail_json(
4426 msg=(
4427 "Minimum FlashBlade REST version required: {0}".format(
4428 SMB_POLICY_API_VERSION
4429 )
4430 )
4431 )
4432 if not HAS_PYPURECLIENT:
4433 module.fail_json(msg="py-pure-client sdk is required for this module")
4434 try:
4435 if (
4436 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4437 and module.params["context"]
4438 ):
4439 policy = list(
4440 blade.get_smb_share_policies(
4441 names=[module.params["name"]],
4442 context_names=[module.params["context"]],
4443 ).items
4444 )[0]
4445 else:
4446 policy = list(
4447 blade.get_smb_share_policies(names=[module.params["name"]]).items
4448 )[0]
4449 except AttributeError:
4450 policy = None
4451 if module.params["rename"]:
4452 try:
4453 if (
4454 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4455 and module.params["context"]
4456 ):
4457 new_policy = list(
4458 blade.get_smb_share_policies(
4459 names=[module.params["rename"]],
4460 context_names=[module.params["context"]],
4461 ).items
4462 )[0]
4463 else:
4464 new_policy = list(
4465 blade.get_smb_share_policies(
4466 names=[module.params["rename"]]
4467 ).items
4468 )[0]
4469 except AttributeError:
4470 new_policy = None
4471 if policy and state == "present" and not module.params["rename"]:
4472 if module.params["before_rule"]:
4473 # Share rules are keyed by principal and have no index or
4474 # evaluation order, so there is nothing to move them before.
4475 module.fail_json(
4476 msg="`before_rule` is not supported for SMB share policies"
4477 )
4478 update_smb_share_policy(module, blade)
4479 elif (
4480 state == "present" and module.params["rename"] and policy and not new_policy
4481 ):
4482 rename_smb_share_policy(module, blade)
4483 elif state == "present" and not policy and not module.params["rename"]:
4484 create_smb_share_policy(module, blade)
4485 elif state == "absent" and policy:
4486 delete_smb_share_policy(module, blade)
4487 elif state == "copy":
4488 copy_policy(module, blade, policy)
4489 elif module.params["policy_type"] == "network":
4490 if LooseVersion(NET_POLICY_API_VERSION) > LooseVersion(versions):
4491 module.fail_json(
4492 msg=(
4493 "Minimum FlashBlade REST version required: {0}".format(
4494 NET_POLICY_API_VERSION
4495 )
4496 )
4497 )
4498 if not HAS_PYPURECLIENT:
4499 module.fail_json(msg="py-pure-client sdk is required for this module")
4500 # The network-access-policies endpoints take no fleet context in any
4501 # REST version (the SDK methods have no context_names argument), so
4502 # the context defaulted for fleet members above must not be sent.
4503 try:
4504 policy = list(
4505 blade.get_network_access_policies(names=[module.params["name"]]).items
4506 )[0]
4507 except AttributeError:
4508 policy = None
4509 if module.params["rename"]:
4510 try:
4511 new_policy = list(
4512 blade.get_network_access_policies(
4513 names=[module.params["rename"]]
4514 ).items
4515 )[0]
4516 except AttributeError:
4517 new_policy = None
4518 if policy and state == "present" and not module.params["rename"]:
4519 if module.params["before_rule"]:
4520 res = blade.get_network_access_policies_rules(
4521 policy_names=[module.params["name"]],
4522 names=[
4523 module.params["name"] + "." + str(module.params["before_rule"])
4524 ],
4525 )
4526 if res.status_code != 200:
4527 module.fail_json(
4528 msg="Rule index {0} does not exist.".format(
4529 module.params["before_rule"]
4530 )
4531 )
4532 update_network_access_policy(module, blade)
4533 elif (
4534 state == "present" and module.params["rename"] and policy and not new_policy
4535 ):
4536 rename_network_access_policy(module, blade)
4537 elif state == "present" and not policy and not module.params["rename"]:
4538 create_network_access_policy(module, blade)
4539 elif state == "absent" and policy:
4540 delete_network_access_policy(module, blade)
4541 elif state == "copy":
4542 module.fail_json(msg="Network access policies cannot be copied")
4543 elif module.params["policy_type"] == "worm":
4544 if LooseVersion(WORM_POLICY_API_VERSION) > LooseVersion(versions):
4545 module.fail_json(
4546 msg=(
4547 "Minimum FlashBlade REST version required: {0}".format(
4548 WORM_POLICY_API_VERSION
4549 )
4550 )
4551 )
4552 if not HAS_PYPURECLIENT:
4553 module.fail_json(msg="py-pure-client sdk is required for this module")
4554 try:
4555 if (
4556 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4557 and module.params["context"]
4558 ):
4559 policy = list(
4560 blade.get_worm_data_policies(
4561 names=[module.params["name"]],
4562 context_names=[module.params["context"]],
4563 ).items
4564 )[0]
4565 else:
4566 policy = list(
4567 blade.get_worm_data_policies(names=[module.params["name"]]).items
4568 )[0]
4569 except AttributeError:
4570 policy = None
4571 if module.params["rename"]:
4572 try:
4573 if (
4574 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4575 and module.params["context"]
4576 ):
4577 new_policy = list(
4578 blade.get_worm_data_policies(
4579 names=[module.params["rename"]],
4580 context_names=[module.params["context"]],
4581 ).items
4582 )[0]
4583 else:
4584 new_policy = list(
4585 blade.get_worm_data_policies(
4586 names=[module.params["rename"]]
4587 ).items
4588 )[0]
4589 except AttributeError:
4590 new_policy = None
4591 if policy and state == "present" and not module.params["rename"]:
4592 if module.params["before_rule"]:
4593 if (
4594 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4595 and module.params["context"]
4596 ):
4597 res = blade.get_worm_data_policies_rules(
4598 policy_names=[module.params["name"]],
4599 names=[
4600 module.params["name"]
4601 + "."
4602 + str(module.params["before_rule"])
4603 ],
4604 context_names=[module.params["context"]],
4605 )
4606 else:
4607 res = blade.get_worm_data_policies_rules(
4608 policy_names=[module.params["name"]],
4609 names=[
4610 module.params["name"]
4611 + "."
4612 + str(module.params["before_rule"])
4613 ],
4614 )
4615 if res.status_code != 200:
4616 module.fail_json(
4617 msg="Rule index {0} does not exist.".format(
4618 module.params["before_rule"]
4619 )
4620 )
4621 update_worm_data_policy(module, blade)
4622 elif (
4623 state == "present" and module.params["rename"] and policy and not new_policy
4624 ):
4625 rename_worm_data_policy(module)
4626 elif state == "present" and not policy and not module.params["rename"]:
4627 create_worm_data_policy(module, blade)
4628 elif state == "absent" and policy:
4629 delete_worm_data_policy(module, blade)
4630 elif state == "copy":
4631 copy_policy(module, blade, policy)
4632 else:
4633 if not HAS_PYPURECLIENT:
4634 module.fail_json(msg="py-pure-client sdk is required for this module")
4635 try:
4636 if (
4637 LooseVersion(CONTEXT_API_VERSION) <= LooseVersion(versions)
4638 and module.params["context"]
4639 ):
4640 policy = list(
4641 blade.get_policies(
4642 names=[module.params["name"]],
4643 context_names=[module.params["context"]],
4644 ).items
4645 )[0]
4646 else:
4647 policy = list(blade.get_policies(names=[module.params["name"]]).items)[
4648 0
4649 ]
4650 except AttributeError:
4651 policy = None
4652 if module.params["rename"]:
4653 try:
4654 new_policy = list(
4655 blade.get_policies(
4656 names=[module.params["rename"]],
4657 **_context_kwargs(module, versions),
4658 ).items
4659 )[0]
4660 except AttributeError:
4661 new_policy = None
4662 if state == "present" and module.params["rename"] and policy and not new_policy:
4663 rename_snap_policy(module, blade)
4664 elif not policy and state == "present" and not module.params["rename"]:
4665 create_snap_policy(module, blade)
4666 elif policy and state == "present" and not module.params["rename"]:
4667 update_snap_policy(module, blade)
4668 elif policy and state == "absent":
4669 if module.params["filesystem"] or module.params["replica_link"]:
4670 update_snap_policy(module, blade)
4671 else:
4672 delete_snap_policy(module, blade)
4673 elif state == "copy":
4674 copy_policy(module, blade, policy)
4676 module.exit_json(changed=False)
4679if __name__ == "__main__":
4680 main()