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

1#!/usr/bin/python 

2# -*- coding: utf-8 -*- 

3 

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) 

6 

7from __future__ import absolute_import, division, print_function 

8 

9__metaclass__ = type 

10 

11ANSIBLE_METADATA = { 

12 "metadata_version": "1.1", 

13 "status": ["preview"], 

14 "supported_by": "community", 

15} 

16 

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""" 

423 

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""" 

629 

630RETURN = r""" 

631""" 

632 

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 

654 

655HAS_PYTZ = True 

656try: 

657 import pytz 

658except ImportError: 

659 HAS_PYTZ = False 

660 

661import os 

662import re 

663import platform 

664 

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) 

683 

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" 

694 

695 

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 

700 

701 Linnux has been tested and other opersting systems should be OK. 

702 Failures cause assumption of UTC 

703 

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") 

722 

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") 

731 

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") 

742 

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") 

748 

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 ) 

763 

764 else: 

765 module.warn("Could not find /etc/timezone. Assuming UTC") 

766 

767 return timezone 

768 

769 

770def _context_kwargs(module, versions): 

771 """Return the ``context_names`` keyword for fleet-aware SDK calls. 

772 

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 {} 

782 

783 

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 

787 

788 

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 

792 

793 

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 

797 

798 

799# SMB share permissions, lowest level first. 

800_SHARE_LEVELS = ("read", "change", "full_control") 

801 

802 

803def _share_rule_perms(current, requested): 

804 """Return the permissions the array stores once I(requested) is applied. 

805 

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 

846 

847 

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 

861 

862 

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 

869 

870 

871def _share_rule_patch(current, desired): 

872 """Return the one-field PATCH body that turns I(current) into I(desired). 

873 

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]: ""} 

884 

885 

886def _anon_id(value): 

887 """Normalise anonuid/anongid for comparison. 

888 

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) 

896 

897 

898def _nfs_rule(versions, patch=False, **fields): 

899 """Build the request body of an NFS export policy rule. 

900 

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. 

906 

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 

919 

920 

921def _nfs_rule_from_params(module, versions): 

922 """Build a new NFS export rule from the task. 

923 

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 ) 

938 

939 

940def _smb_client_rule_from_params(module, versions): 

941 """Build a new SMB client rule from the task. 

942 

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 ) 

955 

956 

957def delete_smb_share_policy(module, blade): 

958 """Delete SMB Share Policy, or Rule 

959 

960 If principal is provided then delete the principal rule if it exists. 

961 """ 

962 

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) 

1024 

1025 

1026def rename_smb_share_policy(module, blade): 

1027 """Rename SMB Share Policy""" 

1028 

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) 

1055 

1056 

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) 

1140 

1141 

1142def update_smb_share_policy(module, blade): 

1143 """Update SMB Share Policy Rule""" 

1144 

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) 

1274 

1275 

1276def delete_smb_client_policy(module, blade): 

1277 """Delete SMB CLient Policy, or Rule 

1278 

1279 If client is provided then delete the client rule if it exists. 

1280 """ 

1281 

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) 

1378 

1379 

1380def rename_smb_client_policy(module, blade): 

1381 """Rename SMB Client Policy""" 

1382 

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) 

1409 

1410 

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) 

1487 

1488 

1489def create_network_access_policy(module, blade): 

1490 """Create Network Access Policy 

1491 

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 ) 

1503 

1504 

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) 

1558 

1559 

1560def update_smb_client_policy(module, blade): 

1561 """Update SMB Client Policy Rule""" 

1562 

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) 

1845 

1846 

1847def delete_nfs_policy(module, blade): 

1848 """Delete NFS Export Policy, or Rule 

1849 

1850 If client is provided then delete the client rule if it exists. 

1851 """ 

1852 

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) 

1949 

1950 

1951def _network_rules_for_client(blade, module): 

1952 """Return the rules of the network access policy that match I(client). 

1953 

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"]] 

1964 

1965 

1966def update_network_access_policy(module, blade): 

1967 """Update Network Access Policy Rule 

1968 

1969 The network-access-policies endpoints take no fleet context, so no 

1970 context_names is passed here. 

1971 """ 

1972 

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) 

2069 

2070 

2071def delete_network_access_policy(module, blade): 

2072 """Delete Network Access Policy Rule 

2073 

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 """ 

2078 

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) 

2099 

2100 

2101def delete_worm_data_policy(module, blade): 

2102 """Delete WORM data Policy""" 

2103 

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) 

2123 

2124 

2125def rename_worm_data_policy(module): 

2126 """Rename WORM Data Policy""" 

2127 

2128 changed = False 

2129 module.warn("Renaming of WORM Data policies is not supported") 

2130 module.exit_json(changed=changed) 

2131 

2132 

2133def rename_network_access_policy(module, blade): 

2134 """Rename Network Access Policy""" 

2135 

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) 

2151 

2152 

2153def rename_nfs_policy(module, blade): 

2154 """Rename NFS Export Policy""" 

2155 

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) 

2182 

2183 

2184def update_worm_data_policy(module, blade): 

2185 """Update WORM data policy""" 

2186 

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) 

2278 

2279 

2280def update_nfs_policy(module, blade): 

2281 """Update NFS Export Policy Rule""" 

2282 

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) 

2580 

2581 

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) 

2656 

2657 

2658def delete_os_policy(module, blade): 

2659 """Delete Object Store Access Policy, Rule, or User 

2660 

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 """ 

2667 

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 ) 

2709 

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 ) 

2749 

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) 

2839 

2840 

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) 

2935 

2936 

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) 

3117 

3118 

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) 

3206 

3207 

3208def delete_snap_policy(module, blade): 

3209 """Delete snapshot policy 

3210 

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 """ 

3215 

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) 

3316 

3317 

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.") 

3333 

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 ) 

3362 

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) 

3497 

3498 

3499def update_snap_policy(module, blade): 

3500 """Update snapshot policy 

3501 

3502 Add new rules to the policy using this function. 

3503 Should it be necessary to modify an existing rule these are the rules: 

3504 

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. 

3508 

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 """ 

3512 

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 ) 

3635 

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 ) 

3814 

3815 module.exit_json(changed=changed) 

3816 

3817 

3818def rename_snap_policy(module, blade): 

3819 """Rename snapshot policy""" 

3820 

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) 

3856 

3857 

3858def _copy_rule_policy(module, blade, source, versions, ctx): 

3859 """Create the target of a copy for NFS and SMB policies. 

3860 

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 

3933 

3934 

3935def copy_policy(module, blade, source): 

3936 """Copy a snapshot, NFS, SMB or WORM policy to a new policy I(target). 

3937 

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) 

4002 

4003 

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 ) 

4130 

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 ] 

4139 

4140 module = AnsibleModule( 

4141 argument_spec, 

4142 required_together=required_together, 

4143 required_if=required_if, 

4144 supports_check_mode=True, 

4145 ) 

4146 

4147 if not HAS_PYTZ: 

4148 module.fail_json(msg="pytz is required for this module") 

4149 

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) 

4675 

4676 module.exit_json(changed=False) 

4677 

4678 

4679if __name__ == "__main__": 

4680 main()