diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
commit | a453ac31f3428614cceb99027f8efbdb9258a40b (patch) | |
tree | f61f87408f32a8511cbd91799f9cececb53e0374 /collections-debian-merged/ansible_collections/purestorage/flashblade | |
parent | Initial commit. (diff) | |
download | ansible-upstream.tar.xz ansible-upstream.zip |
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collections-debian-merged/ansible_collections/purestorage/flashblade')
69 files changed, 9099 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/CHANGELOG.rst b/collections-debian-merged/ansible_collections/purestorage/flashblade/CHANGELOG.rst new file mode 100644 index 00000000..28ad2bba --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/CHANGELOG.rst @@ -0,0 +1,90 @@ +==================================== +Purestorage.Flashblade Release Notes +==================================== + +.. contents:: Topics + + +v1.4.0 +====== + +Minor Changes +------------- + +- purefb_banner - Module to manage the GUI and SSH login message +- purefb_certgrp - Module to manage FlashBlade Certificate Groups +- purefb_certs - Module to create and delete SSL certificates +- purefb_connect - Support idempotency when exisitng connection is incoming +- purefb_fs - Add new options for filesystem control (https://github.com/Pure-Storage-Ansible/FlashBlade-Collection/pull/81) +- purefb_fs - Default filesystem size on creation changes from 32G to ``unlimited`` +- purefb_fs - Fix error in deletion and eradication of filesystem +- purefb_fs_replica - Remove condition to attach/detach policies on unhealthy replica-link +- purefb_info - Add support to list filesystem policies +- purefb_lifecycle - Module to manage FlashBlade Bucket Lifecycle Rules +- purefb_s3user - Add support for imported user access keys +- purefb_syslog - Module to manage syslog server configuration + +Bugfixes +-------- + +- purefa_policy - Resolve multiple issues related to incorrect use of timezones +- purefb_connect - Ensure changing encryption status on array connection is performed correctly +- purefb_connect - Fix breaking change created in purity_fb SDK 1.9.2 for deletion of array connections +- purefb_connect - Hide target array API token +- purefb_ds - Ensure updating directory service configurations completes correctly +- purefb_info - Fix issue getting array info when encrypted connection exists + +New Modules +----------- + +- purestorage.flashblade.purefb_banner - Configure Pure Storage FlashBlade GUI and SSH MOTD message +- purestorage.flashblade.purefb_certgrp - Manage FlashBlade Certifcate Groups +- purestorage.flashblade.purefb_certs - Manage FlashBlade SSL Certifcates +- purestorage.flashblade.purefb_lifecycle - Manage FlashBlade object lifecycles +- purestorage.flashblade.purefb_syslog - Configure Pure Storage FlashBlade syslog settings + +v1.3.0 +====== + +Release Summary +--------------- + +| Release Date: 2020-08-08 +| This changlelog describes all changes made to the modules and plugins included in this collection since Ansible 2.9.0 + + +Major Changes +------------- + +- purefb_alert - manage alert email settings on a FlashBlade +- purefb_bladename - manage FlashBlade name +- purefb_bucket_replica - manage bucket replica links on a FlashBlade +- purefb_connect - manage connections between FlashBlades +- purefb_dns - manage DNS settings on a FlashBlade +- purefb_fs_replica - manage filesystem replica links on a FlashBlade +- purefb_inventory - get information about the hardware inventory of a FlashBlade +- purefb_ntp - manage the NTP settings for a FlashBlade +- purefb_phonehome - manage the phone home settings for a FlashBlade +- purefb_policy - manage the filesystem snapshot policies for a FlashBlade +- purefb_proxy - manage the phone home HTTP proxy settings for a FlashBlade +- purefb_remote_cred - manage the Object Store Remote Credentials on a FlashBlade +- purefb_snmp_agent - modify the FlashBlade SNMP Agent +- purefb_snmp_mgr - manage SNMP Managers on a FlashBlade +- purefb_target - manage remote S3-capable targets for a FlashBlade +- purefb_user - manage local ``pureuser`` account password on a FlashBlade + +Minor Changes +------------- + +- purefb_bucket - Versioning support added +- purefb_info - new options added for information collection +- purefb_network - Add replication service type +- purefb_s3user - Limit ``access_key`` recreation to 3 times +- purefb_s3user - return dict changed from ``ansible_facts`` to ``s3user_info`` + +Bugfixes +-------- + +- purefb_bucket - Add warning message if ``state`` is ``absent`` without ``eradicate:`` +- purefb_fs - Add graceful exist when ``state`` is ``absent`` and filesystem not eradicated +- purefb_fs - Add warning message if ``state`` is ``absent`` without ``eradicate`` diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/FILES.json b/collections-debian-merged/ansible_collections/purestorage/flashblade/FILES.json new file mode 100644 index 00000000..c28c37a1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/FILES.json @@ -0,0 +1,586 @@ +{ + "files": [ + { + "format": 1, + "ftype": "dir", + "chksum_sha256": null, + "name": ".", + "chksum_type": null + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "roles", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "name": "roles/.keep", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "tests", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "tests/sanity", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "name": "tests/sanity/ignore-2.9.txt", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "name": "tests/sanity/ignore-2.10.txt", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "098d26a10ec061d4cabd9d8504a138aaf16175ceca1b8c81bbe63effff052513", + "name": "README.md", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "playbooks", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "playbooks/roles", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "name": "playbooks/roles/.keep", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "name": "playbooks/.keep", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "playbooks/templates", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "name": "playbooks/templates/.keep", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "playbooks/files", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "name": "playbooks/files/.keep", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "playbooks/vars", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "name": "playbooks/vars/.keep", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "playbooks/tasks", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "name": "playbooks/tasks/.keep", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "440310120f2f25ceb0bbb0ea80d67426cf5c862f21d6bd3f039e8a665e33d850", + "name": "CHANGELOG.rst", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "changelogs", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "72e2d29ddbf66c657d6998587962e1710c9686ba4b977cc1390ba4add6e09009", + "name": "changelogs/config.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "71fca98f6027e6f9bf34a63f042bc4ec41e09cc25a93ba8adb3c8a2f4d634519", + "name": "changelogs/.plugin-cache.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "9b63a7241dbce0ea01bdbc8466d6d45e6273836d2011df1a910c08f841e92ed8", + "name": "changelogs/changelog.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "changelogs/fragments", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "abb817b52fdfa70b538ca9efce8d642282383b6961c47bde20ce0a023d2b941d", + "name": "changelogs/fragments/81_purefb_fs_new_options.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "2e9c5c95b8333fee22646f4e83e9034172182b1e99c084725f08df48e45d3d47", + "name": "changelogs/fragments/101_fix_policy_and_timezone_error.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "6d8689e8f46ab7d3286b7d3ee46dfa13a8bf0585cc9b197a5ca8271c9dd9590e", + "name": "changelogs/fragments/76_default_fs_size.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "ad1078e90875745edce8071846183eed39c3878156d14f96b5db78ab1c5be973", + "name": "changelogs/fragments/90_imported_keys.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "828cc0c94acf44d1d373402a0cc657527d9fce8ac744319fbe0d8035684932b4", + "name": "changelogs/fragments/96_fix_update_connection.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "ee600c3bcae632d7450ff3447192f8ca2d1622eecd67bc87c59fdd3dd8326bc6", + "name": "changelogs/fragments/85_add_banner.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "f3504f5e1acadaf52bd9d420373b7edce2015435232e5fa53282455361bcd440", + "name": "changelogs/fragments/80_support_reverse_replica_link.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "1d286bf0fe3301a898bcdcad0bf70955732608eb51468097ca6d70ae269654d8", + "name": "changelogs/fragments/84_add_cert.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "b7513178564ee1707090e4b3df65af56f28a71119e0ebf73b074dc9d2c0e1d65", + "name": "changelogs/fragments/83_add_certgrp.yml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e140fbfc3ac4eaab3dd9c482e3beb37efd98ad4c3892b36f93ffb00d89c9283f", + "name": "changelogs/fragments/97_fix_encrpyted_array_connection_info.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "8befcbbddf6fc2db62ff48b4f3a1030fe115fb7ababfc9b03c8e693628087337", + "name": "changelogs/fragments/92_fix_ds_update.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e42ee9ea2a2bffa465347a52a3fcf4bfaa51f377e7f33bf4a405eb46ae507442", + "name": "changelogs/fragments/86_add_syslog.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "64bd3d32085373ce61a414518c2ed87bfd003d163d3002d087f41f4a54b0b1a0", + "name": "changelogs/fragments/v1.3.0_summary.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "fdc6c425f03ffc0b4a008230f290f6ef37874a270909cb2ee311843dc08909f6", + "name": "changelogs/fragments/88_add_lifecycle.yml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "57a7b5ed892c4ea2f5149023b2bdde9481eb8c0a7593e4e76a4603e706971100", + "name": "changelogs/fragments/78_update_filesystem_replica_link.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "b4cd3cbdb65de6b71cfbe179d56a42be2afbf6486e1ce0df9fdd3a7042bd57b0", + "name": "changelogs/fragments/79_hide_connect_api.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "8c7090d551cb59c49622a89c0ed25f12ad89104a9e2ab6708a01fc01fce9e049", + "name": "changelogs/fragments/77_filesystem_policies_info.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "787138033d123fa59a9d3cdb424dc093183a020eebf1e76b46cbf059006e18e5", + "name": "changelogs/fragments/90_delete_conn_fix.yaml", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "plugins", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "plugins/modules", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e2085de6051fd21ca0c15054a30fd30aa70a001a5116c2f0e21ce8af0cb737c6", + "name": "plugins/modules/purefb_bladename.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "3a83537f84375cb5bf3dfdd8b9c1cbc342299bbfffab074d8988052976dd1076", + "name": "plugins/modules/purefb_proxy.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "cc3b2332039bdbcdd16e7cac8b192589d6f76175d9bc3a75ebd14864a933d58b", + "name": "plugins/modules/purefb_user.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "219dbbcc1889d99e67d50b4b0d53334c74e285477f816a250b38b4002ab4e044", + "name": "plugins/modules/purefb_dns.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "11c74692eea80fd141478baf5addff6d1f374b3b76bb75287f30269f74d5b116", + "name": "plugins/modules/purefb_alert.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "0b56bc85c621d21e05c0dc94019edbb1ea43e1bfa1d767807476331b39e5c9d0", + "name": "plugins/modules/purefb_remote_cred.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "d3c18977c8bb214b5f339ee4f1f8d68ad4c3b3215b0274ec18e9290de9cdb60a", + "name": "plugins/modules/purefb_connect.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "23f52e9de6abe2afa60cf8c2d2e0fc9034cd2caa955d63ef4131b5933bf607d8", + "name": "plugins/modules/purefb_fs.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "85944c3b8ca407b8e4e9a07e279268ac2c97d96904cb78c52305bbdd50d848c4", + "name": "plugins/modules/purefb_network.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "12912b6a31a8d0df3fbcd2bdd554076d22b6559c51a4fbe7a28f71d3bfe4b44a", + "name": "plugins/modules/purefb_target.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "96e2af842acc8018bab4517b00708936a922f83e57558816aa11e6018c1ba238", + "name": "plugins/modules/purefb_certgrp.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "f1ad5424beaf199be26a6a3f58c5ceb94eba7534d8e5b33ddfed2d508836d0c3", + "name": "plugins/modules/purefb_ds.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "f5d40480816595790d19f655a6a3ce173695acccc3a06d16ed8fb3ed6f021a71", + "name": "plugins/modules/purefb_certs.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "3db5be15bdd4a0732d6fcaf39ab21c9f56c8436b7d6841b1668c7c1c45214da9", + "name": "plugins/modules/purefb_snmp_agent.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "ec782d0a07640f7aa7cbad32edb509a003f4d5c59c8b94414edb3b5131da8fdb", + "name": "plugins/modules/purefb_subnet.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "0ac0045d8abba2bbcdbfe528beb55bc2d83ac610f2c387ea4c5b707dad4c6a79", + "name": "plugins/modules/purefb_inventory.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "6e5da3cd489d95a6543065d0138df4d2df16c60d2b6dc26edb958fcd964bdf70", + "name": "plugins/modules/purefb_syslog.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "a2cc3787bb04e5f234988bb69c21a5af2b863e3b45d6afc2926fa49416c7cc6c", + "name": "plugins/modules/purefb_banner.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "797af2cd252e220310e50c611ff8aff0dd4007742503de46645f2d5812daaf05", + "name": "plugins/modules/purefb_snmp_mgr.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "298f484adc1916af94d3b0ff09fea240ff8173e407bb4660152231a75510a7c1", + "name": "plugins/modules/purefb_ra.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "bcb8eacb2407bedae3d5fa1b4997b2a45b7ef16847b1224c64a72355b0723b67", + "name": "plugins/modules/purefb_s3acc.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "260f7dbf877a474a64992ae575fd2f3595e890ab0e365ebc17ff301eff3a0066", + "name": "plugins/modules/purefb_info.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "269843d389a0b6a5e184f785ee7eb379e99523a12e71af1837510fbcb2ed0d8b", + "name": "plugins/modules/purefb_s3user.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "42ebcbe13e0b892315497bcbddd44e580f2052ef5ac1f3eddd10ef52a2def635", + "name": "plugins/modules/purefb_snap.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "197ffb1eacbfc83cf2274cb978c9c2770ad46d30ca29e3169041d28f7a2c3212", + "name": "plugins/modules/purefb_smtp.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "8d130c8196f3b7ed94b6336dccad26f7f3fc9f18b9622ab1a3dcc037df74d75f", + "name": "plugins/modules/purefb_dsrole.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "94c0616e56fd88f432aa606b6a943959a0db9e113a6f5952977681ce276b5c2d", + "name": "plugins/modules/purefb_bucket.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "5c2286ffc1af45be2c5193d7c11f9369229b00ffd59c0f6448594236622d9425", + "name": "plugins/modules/purefb_ntp.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "86815a118728e6d5ae012f0a55a2c7e745213364957fbc75ab9e8f9182c303ba", + "name": "plugins/modules/purefb_policy.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "627bf1595d92e3caa55d3cef7f2abca719302771653b4fef1c6d33eab63d1ca4", + "name": "plugins/modules/purefb_bucket_replica.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "e4a59857a7058333217d8c543032738dd0265f2e0889bc8a7aad45328cc0d5df", + "name": "plugins/modules/purefb_fs_replica.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "ccff87f81fec33e66e0057e073b84ae797fd8b78213deca8859a252eab1657f6", + "name": "plugins/modules/purefb_phonehome.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "33f186f9b8159035d760d6a45d2cca4037abc14397c3026963118057ca083a70", + "name": "plugins/modules/purefb_lifecycle.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "plugins/doc_fragments", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "3048ea891e7ed4ea764faa477efbbfbd3ca3a6740adb1c0d1718d19149ee6e29", + "name": "plugins/doc_fragments/purestorage.py", + "chksum_type": "sha256", + "format": 1 + }, + { + "ftype": "dir", + "chksum_sha256": null, + "name": "plugins/module_utils", + "chksum_type": null, + "format": 1 + }, + { + "ftype": "file", + "chksum_sha256": "a9d526f8cff8e82d7b23eecd1b3817a2ffc694bb11323dc39633370bf68add2a", + "name": "plugins/module_utils/purefb.py", + "chksum_type": "sha256", + "format": 1 + } + ], + "format": 1 +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/MANIFEST.json b/collections-debian-merged/ansible_collections/purestorage/flashblade/MANIFEST.json new file mode 100644 index 00000000..d6ece309 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/MANIFEST.json @@ -0,0 +1,37 @@ +{ + "collection_info": { + "description": "Collection of modules to manage Pure Storage FlashBlades", + "repository": "https://github.com/Pure-Storage-Ansible/FlashBlade-Collection", + "tags": [ + "purestorage", + "flashblade", + "storage", + "object", + "nfs" + ], + "dependencies": {}, + "authors": [ + "Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>" + ], + "issues": "https://github.com/Pure-Storage-Ansible/FlashBlade-Collection/issues", + "name": "flashblade", + "license": [ + "GPL-3.0-or-later", + "BSD-2-Clause" + ], + "documentation": "https://docs.ansible.com/ansible/2.10/collections/purestorage/flashblade", + "namespace": "purestorage", + "version": "1.4.0", + "readme": "README.md", + "license_file": null, + "homepage": null + }, + "file_manifest_file": { + "format": 1, + "ftype": "file", + "chksum_sha256": "3c5f556ad88cdf9bb97523a4f3f25285ebb6e0dbc38850578f049a4bc2bf8f8e", + "name": "FILES.json", + "chksum_type": "sha256" + }, + "format": 1 +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/README.md b/collections-debian-merged/ansible_collections/purestorage/flashblade/README.md new file mode 100644 index 00000000..f8533587 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/README.md @@ -0,0 +1,77 @@ +# Pure Storage FlashBlade Collection + +The Pure Storage FlashBlade collection consists of the latest versions of the FlashBlade modules. + +## Supported Platforms + +- Pure Storage FlashBlade with Purity 2.1.2 or later +- Certain modules and functionality require higher versions of Purity. Modules will inform you if your Purity version is not high enough to use a module. + +## Prerequisites + +- Ansible 2.9 or later +- Pure Storage FlashBlade system running Purity//FB 2.1.2 or later + - some modules require higher versions of Purity//FB +- purity_fb >=v1.10 +- python >=v2.7, <3.7 +- netaddr + +## Idempotency + +All modules are idempotent with the exception of modules that change or set passwords. Due to security requirements exisitng passwords can be validated against and therefore will always be modified, even if there is no change. + +## Available Modules + +- purefb_alert - manage alert email settings on a FlashBlade +- purefb_banner - manage FlashBlade login banner +- purefb_bladename - manage FlashBlade name +- purefb_bucket - manage S3 buckets on a FlashBlade +- purefb_bucket_replica - manage bucket replica links on a FlashBlade +- purefb_certs - manage FlashBlade SSL certificates +- purefb_certgrp - manage FlashBlade certificate groups +- purefb_connect - manage connections between FlashBlades +- purefb_dns - manage DNS settings on a FlashBlade +- purefb_ds - manage Directory Services settings on a FlashBlade +- purefb_dsrole - manage Directory Service Roles on a FlashBlade +- purefb_fs - manage filesystems on a FlashBlade +- purefb_fs_replica - manage filesystem replica links on a FlashBlade +- purefb_inventory - get information about the hardware inventory of a FlashBlade +- purefb_info - get information about the configuration of a FlashBlade +- purefb_lifecycle - manage FlashBlade Bucket Lifecycle Rules +- purefb_network - manage the network settings for a FlashBlade +- purefb_ntp - manage the NTP settings for a FlashBlade +- purefb_phonehome - manage the phone home settings for a FlashBlade +- purefb_policy - manage the filesystem snapshot policies for a FlashBlade +- purefb_proxy - manage the phone home HTTP proxy settings for a FlashBlade +- purefb_ra - manage the Remote Assist connections on a FlashBlade +- purefb_remote_cred - manage the Object Store Remote Credentials on a FlashBlade +- purefb_s3acc - manage the object store accounts on a FlashBlade +- purefb_s3user - manage the object atore users on a FlashBlade +- purefb_smtp - manage SMTP settings on a FlashBlade +- purefb_snap - manage filesystem snapshots on a FlashBlade +- purefb_snmp_mgr - manage SNMP Managers on a FlashBlade +- purefb_snmp_agent - modify the FlashBlade SNMP Agent +- purefb_subnet - manage network subnets on a FlashBlade +- purefb_syslog - manage FlashBlade syslog server configuration +- purefb_target - manage remote S3-capable targets for a FlashBlade +- purefb_user - manage local *pureuser* account password on a FlashBlade + +## Instructions + +Install the Pure Storage FlashBlade collection on your Ansible management host. + +- Using ansible-galaxy (Ansible 2.9 or later): +``` +ansible-galaxy collection install purestorage.flashblade -p ~/.ansible/collections +``` + +All servers that execute the modules must have the appropriate Pure Storage Python SDK installed on the host. + +## License + +[BSD-2-Clause](https://directory.fsf.org/wiki?title=License:FreeBSD) +[GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.en.html) + +## Author + +This collection was created in 2019 by [Simon Dodsley](@sdodsley) for, and on behalf of, the [Pure Storage Ansible Team](pure-ansible-team@purestorage.com) diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/.plugin-cache.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/.plugin-cache.yaml new file mode 100644 index 00000000..853fad74 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/.plugin-cache.yaml @@ -0,0 +1,180 @@ +plugins: + become: {} + cache: {} + callback: {} + cliconf: {} + connection: {} + httpapi: {} + inventory: {} + lookup: {} + module: + purefb_alert: + description: Configure Pure Storage FlashBlade alert email settings + name: purefb_alert + namespace: '' + version_added: 1.0.0 + purefb_banner: + description: Configure Pure Storage FlashBlade GUI and SSH MOTD message + name: purefb_banner + namespace: '' + version_added: 1.4.0 + purefb_bladename: + description: Configure Pure Storage FlashBlade name + name: purefb_bladename + namespace: '' + version_added: 1.0.0 + purefb_bucket: + description: Manage Object Store Buckets on a Pure Storage FlashBlade. + name: purefb_bucket + namespace: '' + version_added: 1.0.0 + purefb_bucket_replica: + description: Manage bucket replica links between Pure Storage FlashBlades + name: purefb_bucket_replica + namespace: '' + version_added: 1.0.0 + purefb_certgrp: + description: Manage FlashBlade Certifcate Groups + name: purefb_certgrp + namespace: '' + version_added: 1.4.0 + purefb_certs: + description: Manage FlashBlade SSL Certifcates + name: purefb_certs + namespace: '' + version_added: 1.4.0 + purefb_connect: + description: Manage replication connections between two FlashBlades + name: purefb_connect + namespace: '' + version_added: 1.0.0 + purefb_dns: + description: Configure Pure Storage FlashBlade DNS settings + name: purefb_dns + namespace: '' + version_added: 1.0.0 + purefb_ds: + description: Configure FlashBlade Directory Service + name: purefb_ds + namespace: '' + version_added: 1.0.0 + purefb_dsrole: + description: Configure FlashBlade Management Directory Service Roles + name: purefb_dsrole + namespace: '' + version_added: 1.0.0 + purefb_fs: + description: Manage filesystemon Pure Storage FlashBlade` + name: purefb_fs + namespace: '' + version_added: 1.0.0 + purefb_fs_replica: + description: Manage filesystem replica links between Pure Storage FlashBlades + name: purefb_fs_replica + namespace: '' + version_added: 1.0.0 + purefb_info: + description: Collect information from Pure Storage FlashBlade + name: purefb_info + namespace: '' + version_added: 1.0.0 + purefb_inventory: + description: Collect information from Pure Storage FlashBlade + name: purefb_inventory + namespace: '' + version_added: 1.0.0 + purefb_lifecycle: + description: Manage FlashBlade object lifecycles + name: purefb_lifecycle + namespace: '' + version_added: 1.4.0 + purefb_network: + description: Manage network interfaces in a Pure Storage FlashBlade + name: purefb_network + namespace: '' + version_added: 1.0.0 + purefb_ntp: + description: Configure Pure Storage FlashBlade NTP settings + name: purefb_ntp + namespace: '' + version_added: 1.0.0 + purefb_phonehome: + description: Enable or Disable Pure Storage FlashBlade Phone Home + name: purefb_phonehome + namespace: '' + version_added: 1.0.0 + purefb_policy: + description: Manage FlashBlade policies + name: purefb_policy + namespace: '' + version_added: 1.0.0 + purefb_proxy: + description: Configure FlashBlade phonehome HTTPs proxy settings + name: purefb_proxy + namespace: '' + version_added: 1.0.0 + purefb_ra: + description: Enable or Disable Pure Storage FlashBlade Remote Assist + name: purefb_ra + namespace: '' + version_added: 1.0.0 + purefb_remote_cred: + description: Create, modify and delete FlashBlade object store remote credentials + name: purefb_remote_cred + namespace: '' + version_added: 1.0.0 + purefb_s3acc: + description: Create or delete FlashBlade Object Store accounts + name: purefb_s3acc + namespace: '' + version_added: 1.0.0 + purefb_s3user: + description: Create or delete FlashBlade Object Store account users + name: purefb_s3user + namespace: '' + version_added: 1.0.0 + purefb_smtp: + description: Configure SMTP for Pure Storage FlashBlade + name: purefb_smtp + namespace: '' + version_added: 1.0.0 + purefb_snap: + description: Manage filesystem snapshots on Pure Storage FlashBlades + name: purefb_snap + namespace: '' + version_added: 1.0.0 + purefb_snmp_agent: + description: Configure the FlashBlade SNMP Agent + name: purefb_snmp_agent + namespace: '' + version_added: 1.0.0 + purefb_snmp_mgr: + description: Configure FlashBlade SNMP Managers + name: purefb_snmp_mgr + namespace: '' + version_added: 1.0.0 + purefb_subnet: + description: Manage network subnets in a Pure Storage FlashBlade + name: purefb_subnet + namespace: '' + version_added: 1.0.0 + purefb_syslog: + description: Configure Pure Storage FlashBlade syslog settings + name: purefb_syslog + namespace: '' + version_added: 1.4.0 + purefb_target: + description: Manage remote S3-capable targets for a FlashBlade + name: purefb_target + namespace: '' + version_added: 1.0.0 + purefb_user: + description: Modify FlashBlade local user account password + name: purefb_user + namespace: '' + version_added: 1.0.0 + netconf: {} + shell: {} + strategy: {} + vars: {} +version: 1.4.0 diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/changelog.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/changelog.yaml new file mode 100644 index 00000000..d584eb48 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/changelog.yaml @@ -0,0 +1,101 @@ +ancestor: null +releases: + 1.3.0: + changes: + bugfixes: + - purefb_bucket - Add warning message if ``state`` is ``absent`` without ``eradicate:`` + - purefb_fs - Add graceful exist when ``state`` is ``absent`` and filesystem + not eradicated + - purefb_fs - Add warning message if ``state`` is ``absent`` without ``eradicate`` + major_changes: + - purefb_alert - manage alert email settings on a FlashBlade + - purefb_bladename - manage FlashBlade name + - purefb_bucket_replica - manage bucket replica links on a FlashBlade + - purefb_connect - manage connections between FlashBlades + - purefb_dns - manage DNS settings on a FlashBlade + - purefb_fs_replica - manage filesystem replica links on a FlashBlade + - purefb_inventory - get information about the hardware inventory of a FlashBlade + - purefb_ntp - manage the NTP settings for a FlashBlade + - purefb_phonehome - manage the phone home settings for a FlashBlade + - purefb_policy - manage the filesystem snapshot policies for a FlashBlade + - purefb_proxy - manage the phone home HTTP proxy settings for a FlashBlade + - purefb_remote_cred - manage the Object Store Remote Credentials on a FlashBlade + - purefb_snmp_agent - modify the FlashBlade SNMP Agent + - purefb_snmp_mgr - manage SNMP Managers on a FlashBlade + - purefb_target - manage remote S3-capable targets for a FlashBlade + - purefb_user - manage local ``pureuser`` account password on a FlashBlade + minor_changes: + - purefb_bucket - Versioning support added + - purefb_info - new options added for information collection + - purefb_network - Add replication service type + - purefb_s3user - Limit ``access_key`` recreation to 3 times + - purefb_s3user - return dict changed from ``ansible_facts`` to ``s3user_info`` + release_summary: '| Release Date: 2020-08-08 + + | This changlelog describes all changes made to the modules and plugins included + in this collection since Ansible 2.9.0 + +' + fragments: + - v1.3.0_summary.yaml + release_date: '2020-08-06' + 1.4.0: + changes: + bugfixes: + - purefa_policy - Resolve multiple issues related to incorrect use of timezones + - purefb_connect - Ensure changing encryption status on array connection is + performed correctly + - purefb_connect - Fix breaking change created in purity_fb SDK 1.9.2 for deletion + of array connections + - purefb_connect - Hide target array API token + - purefb_ds - Ensure updating directory service configurations completes correctly + - purefb_info - Fix issue getting array info when encrypted connection exists + minor_changes: + - purefb_banner - Module to manage the GUI and SSH login message + - purefb_certgrp - Module to manage FlashBlade Certificate Groups + - purefb_certs - Module to create and delete SSL certificates + - purefb_connect - Support idempotency when exisitng connection is incoming + - purefb_fs - Add new options for filesystem control (https://github.com/Pure-Storage-Ansible/FlashBlade-Collection/pull/81) + - purefb_fs - Default filesystem size on creation changes from 32G to ``unlimited`` + - purefb_fs - Fix error in deletion and eradication of filesystem + - purefb_fs_replica - Remove condition to attach/detach policies on unhealthy + replica-link + - purefb_info - Add support to list filesystem policies + - purefb_lifecycle - Module to manage FlashBlade Bucket Lifecycle Rules + - purefb_s3user - Add support for imported user access keys + - purefb_syslog - Module to manage syslog server configuration + fragments: + - 101_fix_policy_and_timezone_error.yaml + - 76_default_fs_size.yaml + - 77_filesystem_policies_info.yaml + - 78_update_filesystem_replica_link.yaml + - 79_hide_connect_api.yaml + - 80_support_reverse_replica_link.yaml + - 81_purefb_fs_new_options.yaml + - 83_add_certgrp.yml + - 84_add_cert.yaml + - 85_add_banner.yaml + - 86_add_syslog.yaml + - 88_add_lifecycle.yml + - 90_delete_conn_fix.yaml + - 90_imported_keys.yaml + - 92_fix_ds_update.yaml + - 96_fix_update_connection.yaml + - 97_fix_encrpyted_array_connection_info.yaml + modules: + - description: Configure Pure Storage FlashBlade GUI and SSH MOTD message + name: purefb_banner + namespace: '' + - description: Manage FlashBlade Certifcate Groups + name: purefb_certgrp + namespace: '' + - description: Manage FlashBlade SSL Certifcates + name: purefb_certs + namespace: '' + - description: Manage FlashBlade object lifecycles + name: purefb_lifecycle + namespace: '' + - description: Configure Pure Storage FlashBlade syslog settings + name: purefb_syslog + namespace: '' + release_date: '2020-10-14' diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/config.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/config.yaml new file mode 100644 index 00000000..c4e01b38 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/config.yaml @@ -0,0 +1,31 @@ +changelog_filename_template: ../CHANGELOG.rst +changelog_filename_version_depth: 0 +changes_file: changelog.yaml +changes_format: combined +ignore_other_fragment_extensions: true +keep_fragments: true +mention_ancestor: true +new_plugins_after_name: removed_features +notesdir: fragments +prelude_section_name: release_summary +prelude_section_title: Release Summary +sections: +- - major_changes + - Major Changes +- - minor_changes + - Minor Changes +- - breaking_changes + - Breaking Changes / Porting Guide +- - deprecated_features + - Deprecated Features +- - removed_features + - Removed Features (previously deprecated) +- - security_fixes + - Security Fixes +- - bugfixes + - Bugfixes +- - known_issues + - Known Issues +title: Purestorage.Flashblade +trivial_section_name: trivial +use_fqcn: true diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/101_fix_policy_and_timezone_error.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/101_fix_policy_and_timezone_error.yaml new file mode 100644 index 00000000..e6c1ea64 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/101_fix_policy_and_timezone_error.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefa_policy - Resolve multiple issues related to incorrect use of timezones diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/76_default_fs_size.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/76_default_fs_size.yaml new file mode 100644 index 00000000..b899c31f --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/76_default_fs_size.yaml @@ -0,0 +1,3 @@ +minor_changes: + - purefb_fs - Default filesystem size on creation changes from 32G to ``unlimited`` + - purefb_fs - Fix error in deletion and eradication of filesystem diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/77_filesystem_policies_info.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/77_filesystem_policies_info.yaml new file mode 100644 index 00000000..c4d84070 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/77_filesystem_policies_info.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_info - Add support to list filesystem policies diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/78_update_filesystem_replica_link.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/78_update_filesystem_replica_link.yaml new file mode 100644 index 00000000..09bc6c3a --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/78_update_filesystem_replica_link.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_fs_replica - Remove condition to attach/detach policies on unhealthy replica-link diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/79_hide_connect_api.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/79_hide_connect_api.yaml new file mode 100644 index 00000000..d6dcb9fe --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/79_hide_connect_api.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_connect - Hide target array API token diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/80_support_reverse_replica_link.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/80_support_reverse_replica_link.yaml new file mode 100644 index 00000000..42d8f1fe --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/80_support_reverse_replica_link.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_connect - Support idempotency when exisitng connection is incoming diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/81_purefb_fs_new_options.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/81_purefb_fs_new_options.yaml new file mode 100644 index 00000000..a6eb75c0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/81_purefb_fs_new_options.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_fs - Add new options for filesystem control (https://github.com/Pure-Storage-Ansible/FlashBlade-Collection/pull/81) diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/83_add_certgrp.yml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/83_add_certgrp.yml new file mode 100644 index 00000000..4f87b305 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/83_add_certgrp.yml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_certgrp - Module to manage FlashBlade Certificate Groups diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/84_add_cert.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/84_add_cert.yaml new file mode 100644 index 00000000..1470d302 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/84_add_cert.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_certs - Module to create and delete SSL certificates diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/85_add_banner.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/85_add_banner.yaml new file mode 100644 index 00000000..279173cc --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/85_add_banner.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_banner - Module to manage the GUI and SSH login message diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/86_add_syslog.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/86_add_syslog.yaml new file mode 100644 index 00000000..0cde34ca --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/86_add_syslog.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_syslog - Module to manage syslog server configuration diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/88_add_lifecycle.yml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/88_add_lifecycle.yml new file mode 100644 index 00000000..3caa436a --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/88_add_lifecycle.yml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_lifecycle - Module to manage FlashBlade Bucket Lifecycle Rules diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/90_delete_conn_fix.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/90_delete_conn_fix.yaml new file mode 100644 index 00000000..93876fed --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/90_delete_conn_fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_connect - Fix breaking change created in purity_fb SDK 1.9.2 for deletion of array connections diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/90_imported_keys.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/90_imported_keys.yaml new file mode 100644 index 00000000..af012f74 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/90_imported_keys.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_s3user - Add support for imported user access keys diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/92_fix_ds_update.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/92_fix_ds_update.yaml new file mode 100644 index 00000000..c4d52cab --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/92_fix_ds_update.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_ds - Ensure updating directory service configurations completes correctly diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/96_fix_update_connection.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/96_fix_update_connection.yaml new file mode 100644 index 00000000..87bfbeee --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/96_fix_update_connection.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_connect - Ensure changing encryption status on array connection is performed correctly diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/97_fix_encrpyted_array_connection_info.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/97_fix_encrpyted_array_connection_info.yaml new file mode 100644 index 00000000..5019c18e --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/97_fix_encrpyted_array_connection_info.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_info - Fix issue getting array info when encrypted connection exists diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/v1.3.0_summary.yaml b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/v1.3.0_summary.yaml new file mode 100644 index 00000000..35cff95f --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/changelogs/fragments/v1.3.0_summary.yaml @@ -0,0 +1,33 @@ +release_summary: | + | Release Date: 2020-08-08 + | This changlelog describes all changes made to the modules and plugins included in this collection since Ansible 2.9.0 + +major_changes: + - purefb_alert - manage alert email settings on a FlashBlade + - purefb_bladename - manage FlashBlade name + - purefb_bucket_replica - manage bucket replica links on a FlashBlade + - purefb_connect - manage connections between FlashBlades + - purefb_dns - manage DNS settings on a FlashBlade + - purefb_fs_replica - manage filesystem replica links on a FlashBlade + - purefb_inventory - get information about the hardware inventory of a FlashBlade + - purefb_ntp - manage the NTP settings for a FlashBlade + - purefb_phonehome - manage the phone home settings for a FlashBlade + - purefb_policy - manage the filesystem snapshot policies for a FlashBlade + - purefb_proxy - manage the phone home HTTP proxy settings for a FlashBlade + - purefb_remote_cred - manage the Object Store Remote Credentials on a FlashBlade + - purefb_snmp_agent - modify the FlashBlade SNMP Agent + - purefb_snmp_mgr - manage SNMP Managers on a FlashBlade + - purefb_target - manage remote S3-capable targets for a FlashBlade + - purefb_user - manage local ``pureuser`` account password on a FlashBlade + +minor_changes: + - purefb_s3user - return dict changed from ``ansible_facts`` to ``s3user_info`` + - purefb_s3user - Limit ``access_key`` recreation to 3 times + - purefb_info - new options added for information collection + - purefb_bucket - Versioning support added + - purefb_network - Add replication service type + +bugfixes: + - purefb_fs - Add graceful exist when ``state`` is ``absent`` and filesystem not eradicated + - purefb_fs - Add warning message if ``state`` is ``absent`` without ``eradicate`` + - purefb_bucket - Add warning message if ``state`` is ``absent`` without ``eradicate:`` diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/.keep b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/.keep diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/files/.keep b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/files/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/files/.keep diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/roles/.keep b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/roles/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/roles/.keep diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/tasks/.keep b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/tasks/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/tasks/.keep diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/templates/.keep b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/templates/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/templates/.keep diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/vars/.keep b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/vars/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/playbooks/vars/.keep diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/doc_fragments/purestorage.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/doc_fragments/purestorage.py new file mode 100644 index 00000000..53ae5f81 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/doc_fragments/purestorage.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Simon Dodsley <simon@purestorage.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class ModuleDocFragment(object): + + # Standard Pure Storage documentation fragment + DOCUMENTATION = r''' +options: + - See separate platform section for more details +requirements: + - See separate platform section for more details +notes: + - Ansible modules are available for the following Pure Storage products: FlashArray, FlashBlade +''' + + # Documentation fragment for FlashBlade + FB = r''' +options: + fb_url: + description: + - FlashBlade management IP address or Hostname. + type: str + api_token: + description: + - FlashBlade API token for admin privileged user. + type: str +notes: + - This module requires the C(purity_fb) Python library + - You must set C(PUREFB_URL) and C(PUREFB_API) environment variables + if I(fb_url) and I(api_token) arguments are not passed to the module directly +requirements: + - python >= 2.7 + - purity_fb >= 1.9 + - netaddr + - pytz +''' diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/module_utils/purefb.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/module_utils/purefb.py new file mode 100644 index 00000000..296146b0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/module_utils/purefb.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Copyright (c), Simon Dodsley <simon@purestorage.com>,2017 +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +HAS_PURITY_FB = True +try: + from purity_fb import PurityFb +except ImportError: + HAS_PURITY_FB = False + +from os import environ +import platform + +VERSION = '1.3' +USER_AGENT_BASE = 'Ansible' +API_AGENT_VERSION = '1.5' + + +def get_blade(module): + """Return System Object or Fail""" + user_agent = '%(base)s %(class)s/%(version)s (%(platform)s)' % { + 'base': USER_AGENT_BASE, + 'class': __name__, + 'version': VERSION, + 'platform': platform.platform() + } + blade_name = module.params['fb_url'] + api = module.params['api_token'] + + if HAS_PURITY_FB: + if blade_name and api: + blade = PurityFb(blade_name) + blade.disable_verify_ssl() + try: + blade.login(api) + versions = blade.api_version.list_versions().versions + if API_AGENT_VERSION in versions: + blade._api_client.user_agent = user_agent + except Exception: + module.fail_json(msg="Pure Storage FlashBlade authentication failed. Check your credentials") + elif environ.get('PUREFB_URL') and environ.get('PUREFB_API'): + blade = PurityFb(environ.get('PUREFB_URL')) + blade.disable_verify_ssl() + try: + blade.login(environ.get('PUREFB_API')) + versions = blade.api_version.list_versions().versions + if API_AGENT_VERSION in versions: + blade._api_client.user_agent = user_agent + except Exception: + module.fail_json(msg="Pure Storage FlashBlade authentication failed. Check your credentials") + else: + module.fail_json(msg="You must set PUREFB_URL and PUREFB_API environment variables " + "or the fb_url and api_token module arguments") + else: + module.fail_json(msg="purity_fb SDK not installed.") + return blade + + +def purefb_argument_spec(): + """Return standard base dictionary used for the argument_spec argument in AnsibleModule""" + + return dict( + fb_url=dict(), + api_token=dict(no_log=True), + ) diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_alert.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_alert.py new file mode 100644 index 00000000..ea70cd63 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_alert.py @@ -0,0 +1,201 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_alert +version_added: '1.0.0' +short_description: Configure Pure Storage FlashBlade alert email settings +description: +- Configure alert email configuration for Pure Storage FlashArrays. +- Add or delete an individual syslog server to the existing + list of serves. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + type: str + description: + - Create or delete alert email + default: present + choices: [ absent, present ] + address: + type: str + description: + - Email address (valid format required) + required: true + enabled: + type: bool + default: true + description: + - Set specified email address to be enabled or disabled + severity: + type: str + description: + - The minimum severity that an alert must have in order for + emails to be sent to the array's alert watchers + default: info + choices: [ info, warning, critical ] +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Add new email recipient and enable, or enable existing email + purefb_alert: + address: "user@domain.com" + enabled: true + state: present + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +- name: Delete existing email recipient + purefb_alert: + state: absent + address: "user@domain.com" + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + + +HAS_PURITY_FB = True +try: + from purity_fb import AlertWatcher +except ImportError: + HAS_PURITY_FB = False + + +import re +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.9' + + +def create_alert(module, blade): + """Create Alert Email""" + changed = True + if not module.check_mode: + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION in api_version: + watcher_settings = AlertWatcher(minimum_notification_severity=module.params['severity']) + try: + blade.alert_watchers.create_alert_watchers(names=[module.params['address']], + watcher_settings=watcher_settings) + except Exception: + module.fail_json(msg='Failed to create alert email: {0}'.format(module.params['address'])) + else: + try: + blade.alert_watchers.create_alert_watchers(names=[module.params['address']]) + except Exception: + module.fail_json(msg='Failed to create alert email: {0}'.format(module.params['address'])) + if not module.params['enabled']: + watcher_settings = AlertWatcher(enabled=module.params['enabled']) + try: + blade.alert_watchers.update_alert_watchers(names=[module.params['address']], watcher_settings=watcher_settings) + except Exception: + module.fail_json(msg='Failed to disable during create alert email: {0}'.format(module.params['address'])) + module.exit_json(changed=changed) + + +def update_alert(module, blade): + """Update alert Watcher""" + changed = True + if not module.check_mode: + api_version = blade.api_version.list_versions().versions + mod_alert = False + try: + alert = blade.alert_watchers.list_alert_watchers(names=[module.params['address']]) + except Exception: + module.fail_json(msg='Failed to get information for alert email: {0}'.format(module.params['address'])) + current_state = {'enabled': alert.items[0].enabled, + 'severity': alert.items[0].minimum_notification_severity + } + if current_state['enabled'] != module.params['enabled']: + mod_alert = True + if MIN_REQUIRED_API_VERSION in api_version: + if current_state['severity'] != module.params['severity']: + mod_alert = True + if mod_alert: + if MIN_REQUIRED_API_VERSION in api_version: + watcher_settings = AlertWatcher(enabled=module.params['enabled'], + minimum_notification_severity=module.params['severity']) + else: + watcher_settings = AlertWatcher(enabled=module.params['enabled']) + try: + blade.alert_watchers.update_alert_watchers(names=[module.params['address']], watcher_settings=watcher_settings) + except Exception: + module.fail_json(msg='Failed to update alert email: {0}'.format(module.params['address'])) + else: + changed = False + module.exit_json(changed=changed) + + +def delete_alert(module, blade): + """Delete Alert Email""" + changed = False + if not module.check_mode: + try: + blade.alert_watchers.delete_alert_watchers(names=[module.params['address']]) + except Exception: + module.fail_json(msg='Failed to delete alert email: {0}'.format(module.params['address'])) + changed = True + + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + address=dict(type='str', required=True), + enabled=dict(type='bool', default=True), + severity=dict(type='str', default='info', choices=['info', 'warning', 'critical']), + state=dict(type='str', default='present', choices=['absent', 'present']), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb SDK is required for this module') + + pattern = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$") + if not pattern.match(module.params['address']): + module.fail_json(msg='Valid email address not provided.') + + blade = get_blade(module) + + exists = False + try: + emails = blade.alert_watchers.list_alert_watchers() + except Exception: + module.fail_json(msg='Failed to get exisitng email list') + for email in range(0, len(emails.items)): + if emails.items[email].name == module.params['address']: + exists = True + break + if module.params['state'] == 'present' and not exists: + create_alert(module, blade) + elif module.params['state'] == 'present' and exists: + update_alert(module, blade) + elif module.params['state'] == 'absent' and exists: + delete_alert(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_banner.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_banner.py new file mode 100644 index 00000000..54b64e7c --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_banner.py @@ -0,0 +1,133 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_banner +version_added: '1.4.0' +short_description: Configure Pure Storage FlashBlade GUI and SSH MOTD message +description: +- Configure MOTD for Pure Storage FlashBlades. +- This will be shown during an SSH or GUI login to the system. +- Multiple line messages can be achieved using \\n. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Set ot delete the MOTD + default: present + type: str + choices: [ present, absent ] + banner: + description: + - Banner text, or MOTD, to use + type: str + default: "Welcome to the machine..." +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Set new banner text + purefb_banner: + banner: "Banner over\ntwo lines" + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete banner text + purefb_banner: + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import PureArray +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.10' + + +def set_banner(module, blade): + """Set MOTD banner text""" + changed = True + if not module.check_mode: + try: + if not module.params['banner']: + module.fail_json(msg='Invalid MOTD banner given') + blade_settings = PureArray(banner=module.params['banner']) + blade.arrays.update_arrays(array_settings=blade_settings) + except Exception: + module.fail_json(msg='Failed to set MOTD banner text') + + module.exit_json(changed=changed) + + +def delete_banner(module, blade): + """Delete MOTD banner text""" + changed = True + if not module.check_mode: + try: + blade_settings = PureArray(banner='') + blade.arrays.update_arrays(array_settings=blade_settings) + except Exception: + module.fail_json(msg='Failed to delete current MOTD banner text') + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + banner=dict(type='str', default="Welcome to the machine..."), + state=dict(type='str', default='present', choices=['present', 'absent']), + )) + + required_if = [('state', 'present', ['banner'])] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + current_banner = blade.login_banner.list_login_banner().login_banner + +# set banner if empty value or value differs + if state == 'present' and (not current_banner or current_banner != module.params['banner']): + set_banner(module, blade) + # clear banner if it has a value + elif state == 'absent' and current_banner: + delete_banner(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bladename.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bladename.py new file mode 100644 index 00000000..19d3fdce --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bladename.py @@ -0,0 +1,102 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_bladename +version_added: '1.0.0' +short_description: Configure Pure Storage FlashBlade name +description: +- Configure name of Pure Storage FlashBlades. +- Ideal for Day 0 initial configuration. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Set the FlashBlade name + type: str + default: present + choices: [ present ] + name: + description: + - Name of the FlashBlade. Must conform to correct naming schema. + type: str + required: true +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Set new FlashBlade name + purefb_bladename: + name: new-flashblade-name + state: present + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + + +HAS_PURITY_FB = True +try: + from purity_fb import PureArray +except ImportError: + HAS_PURITY_FB = False + + +import re +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +def update_name(module, blade): + """Change aray name""" + changed = True + if not module.check_mode: + try: + blade_settings = PureArray(name=module.params['name']) + blade.arrays.update_arrays(array_settings=blade_settings) + except Exception: + module.fail_json(msg='Failed to change array name to {0}'.format(module.params['name'])) + + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + name=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['present']), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + blade = get_blade(module) + pattern = re.compile("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,54}[a-zA-Z0-9])?$") + if not pattern.match(module.params['name']): + module.fail_json(msg='FlashBlade name {0} does not conform to array name rules'.format(module.params['name'])) + if module.params['name'] != blade.arrays.list_arrays().items[0].name: + update_name(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py new file mode 100644 index 00000000..cb1a37c0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py @@ -0,0 +1,290 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: purefb_bucket +version_added: "1.0.0" +short_description: Manage Object Store Buckets on a Pure Storage FlashBlade. +description: + - This module managess object store (s3) buckets on Pure Storage FlashBlade. +author: Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Bucket Name. + required: true + type: str + account: + description: + - Object Store Account for Bucket. + required: true + type: str + versioning: + description: + - State of S3 bucket versioning + required: false + default: absent + type: str + choices: [ "enabled", "suspended", "absent" ] + state: + description: + - Create, delete or modifies a bucket. + required: false + default: present + type: str + choices: [ "present", "absent" ] + eradicate: + description: + - Define whether to eradicate the bucket on delete or leave in trash. + required: false + type: bool + default: false +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = ''' +- name: Create new bucket named foo in account bar + purefb_bucket: + name: foo + account: bar + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete bucket named foo in account bar + purefb_bucket: + name: foo + account: bar + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Change bucket versioning state + purefb_bucket: + name: foo + account: bar + versioning: enabled + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Recover deleted bucket named foo in account bar + purefb_bucket: + name: foo + account: bar + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Eradicate bucket named foo in account bar + purefb_bucket: + name: foo + account: bar + state: absent + eradicate: true + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 +''' + +RETURN = ''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Bucket, Reference, BucketPatch, BucketPost +except ImportError: + HAS_PURITY_FB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.5' +VERSIONING_VERSION = '1.9' + + +def get_s3acc(module, blade): + """Return Object Store Account or None""" + s3acc = None + accts = blade.object_store_accounts.list_object_store_accounts() + for acct in range(0, len(accts.items)): + if accts.items[acct].name == module.params['account']: + s3acc = accts.items[acct] + return s3acc + + +def get_bucket(module, blade): + """Return Bucket or None""" + s3bucket = None + buckets = blade.buckets.list_buckets() + for bucket in range(0, len(buckets.items)): + if buckets.items[bucket].name == module.params['name']: + s3bucket = buckets.items[bucket] + return s3bucket + + +def create_bucket(module, blade): + """Create bucket""" + changed = True + if not module.check_mode: + try: + api_version = blade.api_version.list_versions().versions + if VERSIONING_VERSION in api_version: + attr = BucketPost() + attr.account = Reference(name=module.params['account']) + blade.buckets.create_buckets(names=[module.params['name']], bucket=attr) + else: + attr = Bucket() + attr.account = Reference(name=module.params['account']) + blade.buckets.create_buckets(names=[module.params['name']], account=attr) + if module.params['versioning'] != 'absent' and VERSIONING_VERSION in api_version: + try: + blade.buckets.update_buckets(names=[module.params['name']], + bucket=BucketPatch(versioning=module.params['versioning'])) + except Exception: + module.fail_json(msg='Object Store Bucket {0} Created but versioning state failed'.format(module.params['name'])) + except Exception: + module.fail_json(msg='Object Store Bucket {0}: Creation failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def delete_bucket(module, blade): + """ Delete Bucket""" + changed = True + if not module.check_mode: + try: + api_version = blade.api_version.list_versions().versions + if VERSIONING_VERSION in api_version: + blade.buckets.update_buckets(names=[module.params['name']], + bucket=BucketPatch(destroyed=True)) + else: + blade.buckets.update_buckets(names=[module.params['name']], + destroyed=Bucket(destroyed=True)) + if module.params['eradicate']: + try: + blade.buckets.delete_buckets(names=[module.params['name']]) + except Exception: + module.fail_json(msg='Object Store Bucket {0}: Eradication failed'.format(module.params['name'])) + except Exception: + module.fail_json(msg='Object Store Bucket {0}: Deletion failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def recover_bucket(module, blade): + """ Recover Bucket""" + changed = True + if not module.check_mode: + try: + api_version = blade.api_version.list_versions().versions + if VERSIONING_VERSION in api_version: + blade.buckets.update_buckets(names=[module.params['name']], + bucket=BucketPatch(destroyed=False)) + else: + blade.buckets.update_buckets(names=[module.params['name']], + destroyed=Bucket(destroyed=False)) + except Exception: + module.fail_json(msg='Object Store Bucket {0}: Recovery failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def update_bucket(module, blade, bucket): + """ Update Bucket """ + changed = True + if not module.check_mode: + changed = False + api_version = blade.api_version.list_versions().versions + if VERSIONING_VERSION in api_version: + module.warn('{0}'.format(bucket.versioning)) + if bucket.versioning != 'none': + if module.params['versioning'] == 'absent': + versioning = 'suspended' + else: + versioning = module.params['versioning'] + if bucket.versioning != versioning: + try: + blade.buckets.update_buckets(names=[module.params['name']], + bucket=BucketPatch(versioning=versioning)) + changed = True + except Exception: + module.fail_json(msg='Object Store Bucket {0}: Versioning change failed'.format(module.params['name'])) + elif module.params['versioning'] != 'absent': + try: + blade.buckets.update_buckets(names=[module.params['name']], + bucket=BucketPatch(versioning=module.params['versioning'])) + changed = True + except Exception: + module.fail_json(msg='Object Store Bucket {0}: Versioning change failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def eradicate_bucket(module, blade): + """ Eradicate Bucket""" + changed = True + if not module.check_mode: + try: + blade.buckets.delete_buckets(names=[module.params['name']]) + except Exception: + module.fail_json(msg='Object Store Bucket {0}: Eradication failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + name=dict(required=True), + account=dict(required=True), + eradicate=dict(default='false', type='bool'), + versioning=dict(default='absent', choices=['enabled', 'suspended', 'absent']), + state=dict(default='present', choices=['present', 'absent']), + ) + ) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + bucket = get_bucket(module, blade) + if not get_s3acc(module, blade): + module.fail_json(msg="Object Store Account {0} does not exist.".format(module.params['account'])) + + if module.params['eradicate'] and state == 'present': + module.warn('Eradicate flag ignored without state=absent') + + if state == 'present' and not bucket: + create_bucket(module, blade) + elif state == 'present' and bucket and bucket.destroyed: + recover_bucket(module, blade) + elif state == 'absent' and bucket and not bucket.destroyed: + delete_bucket(module, blade) + elif state == 'present' and bucket: + update_bucket(module, blade, bucket) + elif state == 'absent' and bucket and bucket.destroyed and module.params['eradicate']: + eradicate_bucket(module, blade) + elif state == 'absent' and not bucket: + module.exit_json(changed=False) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket_replica.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket_replica.py new file mode 100644 index 00000000..d094e039 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket_replica.py @@ -0,0 +1,257 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: purefb_bucket_replica +version_added: '1.0.0' +short_description: Manage bucket replica links between Pure Storage FlashBlades +description: + - This module manages bucket replica links between Pure Storage FlashBlades. +author: Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Local Bucket Name. + required: true + type: str + state: + description: + - Creates or modifies a bucket replica link + required: false + default: present + type: str + choices: [ "present", "absent" ] + target: + description: + - Remote array or target name to create replica on. + required: false + type: str + target_bucket: + description: + - Name of target bucket name + - If not supplied, will default to I(name). + type: str + required: false + paused: + description: + - State of the bucket replica link + type: bool + default: false + credential: + description: + - Name of remote credential name to use. + required: false + type: str +extends_documentation_fragment: + - purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = ''' +- name: Create new bucket replica from foo to bar on arrayB + purefb_bucket_replica: + name: foo + target: arrayB + target_bucket: bar + credentials: cred_1 + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Pause exisitng bucket replica link + purefb_bucket_replica: + name: foo + paused: true + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete bucket replica link foo + purefb_fs_replica: + name: foo + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641''' + +RETURN = ''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import BucketReplicaLink, ObjectStoreRemoteCredentials +except ImportError: + HAS_PURITY_FB = False + +MIN_REQUIRED_API_VERSION = '1.9' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +def get_local_bucket(module, blade): + """Return Bucket or None""" + try: + res = blade.buckets.list_buckets(names=[module.params['name']]) + return res.items[0] + except Exception: + return None + + +def get_remote_cred(module, blade, target): + """Return Remote Credential or None""" + try: + res = blade.object_store_remote_credentials.list_object_store_remote_credentials(names=[target + '/' + module.params['credential']]) + return res.items[0] + except Exception: + return None + + +def get_local_rl(module, blade): + """Return Bucket Replica Link or None""" + try: + res = blade.bucket_replica_links.list_bucket_replica_links(local_bucket_names=[module.params['name']]) + return res.items[0] + except Exception: + return None + + +def get_connected(module, blade): + connected_blades = blade.array_connections.list_array_connections() + for target in range(0, len(connected_blades.items)): + if (connected_blades.items[target].remote.name == module.params['target'] or + connected_blades.items[target].management_address == module.params['target']) and \ + connected_blades.items[target].status in ["connected", "connecting", "partially_connected"]: + return connected_blades.items[target].remote.name + connected_targets = blade.targets.list_targets() + for target in range(0, len(connected_targets.items)): + if connected_targets.items[target].name == module.params['target'] and \ + connected_targets.items[target].status in ["connected", "connecting", "partially_connected"]: + return connected_targets.items[target].name + return None + + +def create_rl(module, blade, remote_cred): + """Create Bucket Replica Link""" + changed = True + if not module.check_mode: + try: + if not module.params['target_bucket']: + module.params['target_bucket'] = module.params['name'] + else: + module.params['target_bucket'] = module.params['target_bucket'].lower() + blade.bucket_replica_links.create_bucket_replica_links( + local_bucket_names=[module.params['name']], + remote_bucket_names=[module.params['target_bucket']], + remote_credentials_names=[remote_cred.name], + bucket_replica_link=BucketReplicaLink(paused=module.params['paused'])) + except Exception: + module.fail_json(msg="Failed to create bucket replica link {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def update_rl_policy(module, blade, local_replica_link): + """Update Bucket Replica Link""" + changed = True + if not module.check_mode: + changed = False + new_cred = local_replica_link.remote.name + '/' + module.params['credential'] + if local_replica_link.paused != module.params['paused'] or local_replica_link.remote_credentials.name != new_cred: + try: + module.warn('{0}'.format(local_replica_link)) + blade.bucket_replica_links.update_bucket_replica_links( + local_bucket_names=[module.params['name']], + remote_bucket_names=[local_replica_link.remote_bucket.name], + remote_names=[local_replica_link.remote.name], + bucket_replica_link=BucketReplicaLink(paused=module.params['paused'], + remote_credentials=ObjectStoreRemoteCredentials(name=new_cred))) + changed = True + except Exception: + module.fail_json(msg="Failed to update bucket replica link {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def delete_rl_policy(module, blade, local_replica_link): + """ Delete Bucket Replica Link""" + changed = True + if not module.check_mode: + try: + blade.bucket_replica_links.delete_bucket_replica_links( + remote_names=[local_replica_link.remote.name], + local_bucket_names=[module.params['name']], + remote_bucket_names=[local_replica_link.remote_bucket.name]) + except Exception: + module.fail_json(msg="Failed to delete bucket replica link {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + name=dict(type='str', required=True), + target=dict(type='str'), + target_bucket=dict(type='str'), + paused=dict(type='bool', default=False), + credential=dict(type='str'), + state=dict(default='present', choices=['present', 'absent']) + ) + ) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + module.params['name'] = module.params['name'].lower() + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + local_bucket = get_local_bucket(module, blade) + local_replica_link = get_local_rl(module, blade) + target = get_connected(module, blade) + + if not target: + module.fail_json(msg='Selected target {0} is not connected.'.format(module.params['target'])) + + if local_replica_link and not module.params['credential']: + module.params['credential'] = local_replica_link.remote_credentials.name.split('/')[1] + remote_cred = get_remote_cred(module, blade, target) + if not remote_cred: + module.fail_json(msg='Selected remote credential {0} does not exist for target {1}.'.format(module.params['credential'], + module.params['target'])) + + if not local_bucket: + module.fail_json(msg='Selected local bucket {0} does not exist.'.format(module.params['name'])) + + if local_replica_link: + if local_replica_link.status == 'unhealthy': + module.fail_json(msg='Replica Link unhealthy - please check target') + + if state == 'present' and not local_replica_link: + create_rl(module, blade, remote_cred) + elif state == 'present' and local_replica_link: + update_rl_policy(module, blade, local_replica_link) + elif state == 'absent' and local_replica_link: + delete_rl_policy(module, blade, local_replica_link) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_certgrp.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_certgrp.py new file mode 100644 index 00000000..54a638f7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_certgrp.py @@ -0,0 +1,193 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_certgrp +version_added: '1.4.0' +short_description: Manage FlashBlade Certifcate Groups +description: +- Manage certifcate groups for FlashBlades +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete certifcate group + default: present + type: str + choices: [ absent, present ] + name: + description: + - Name of the certificate group + type: str + certificates: + description: + - List of certifcates to add to a policy on creation + type: list + elements: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create a certifcate group + purefb_certgrp: + name: test_grp + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Create a cerifcate group and add existing certificates + purefb_certgrp: + name: test_grp + certifcates: + - cert1 + - cert2 + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Delete a certifcate from a group + purefb_certgrp: + name: test_grp + certificates: + - cert2 + state: absent + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Delete a certifcate group + purefb_certgrp: + name: test_grp + state: absent + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.9' + + +def delete_certgrp(module, blade): + """Delete certifcate group""" + changed = True + if not module.check_mode: + try: + blade.certificate_groups.delete_certificate_groups(names=[module.params['name']]) + except Exception: + module.fail_json(msg="Failed to delete certifcate group {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def create_certgrp(module, blade): + """Create certifcate group""" + changed = True + if not module.check_mode: + try: + blade.certificate_groups.create_certificate_groups(names=[module.params['name']]) + except Exception: + module.fail_json(msg="Failed to create certificate group {0}.".format(module.params['name'])) + if module.params['certificates']: + try: + blade.certificate_groups.add_certificate_group_certificates(certificate_names=module.params['certificates'], + certificate_group_names=[module.params['name']]) + except Exception: + blade.certificate_groups.delete_certificate_groups(names=[module.params['name']]) + module.fail_json(msg="Failed to add certifcates {0}. " + "Please check they all exist".format(module.params['certificates'])) + module.exit_json(changed=changed) + + +def update_certgrp(module, blade): + """Update certificate group""" + changed = True + if not module.check_mode: + changed = False + try: + certs = blade.certificate_groups.list_certificate_group_certificates(certificate_group_names=[module.params['name']]) + except Exception: + module.fail_json(msg="Failed to get certifates list for group {0}.".format(module.params['name'])) + if not certs: + if module.params['state'] == 'present': + try: + blade.certificate_groups.add_certificate_group_certificates(certificate_names=module.params['certificates'], + certificate_group_names=[module.params['name']]) + changed = True + except Exception: + module.fail_json(msg="Failed to add certifcates {0}. " + "Please check they all exist".format(module.params['certificates'])) + else: + current = [] + for cert in range(0, len(certs.items)): + current.append(certs.items[cert].member.name) + for new_cert in range(0, len(module.params['certificates'])): + certificate = module.params['certificates'][new_cert] + if certificate in current: + if module.params['state'] == 'absent': + try: + blade.certificate_groups.remove_certificate_group_certificates(certificate_names=[certificate], + certificate_group_names=[module.params['name']]) + changed = True + except Exception: + module.fail_json(msg="Failed to delete certifcate {0} from group {1}.".format(certificate, module.params['name'])) + else: + if module.params['state'] == 'present': + try: + blade.certificate_groups.add_certificate_group_certificates(certificate_names=[certificate], + certificate_group_names=[module.params['name']]) + changed = True + except Exception: + module.fail_json(msg="Failed to add certifcate {0} to group {1}".format(certificate, module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['absent', 'present']), + name=dict(type='str'), + certificates=dict(type='list', elements='str'), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + state = module.params['state'] + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + try: + certgrp = blade.certificate_groups.list_certificate_groups(names=[module.params['name']]).items[0] + except Exception: + certgrp = None + + if certgrp and state == 'present' and module.params['certificates']: + update_certgrp(module, blade) + elif state == 'present' and not certgrp: + create_certgrp(module, blade) + elif state == 'absent' and certgrp: + if module.params['certificates']: + update_certgrp(module, blade) + else: + delete_certgrp(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_certs.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_certs.py new file mode 100644 index 00000000..df270ee4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_certs.py @@ -0,0 +1,128 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_certs +version_added: '1.4.0' +short_description: Manage FlashBlade SSL Certifcates +description: +- Manage SSL certifcates for FlashBlades +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete certifcate + default: present + type: str + choices: [ absent, present ] + name: + description: + - Name of the certificate + type: str + contents: + description: + - SSL certifcate text + type: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create a SSL certifcate + purefb_certs: + name: test_cert + contents: "{{lookup('file', 'certicate_file_name') }}" + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Delete a SSL certifcate + purefb_certs: + name: test_cert + state: absent + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +HAS_PURITYFB = True +try: + from purity_fb import CertificatePost +except ImportError: + HAS_PURITYFB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.9' + + +def delete_cert(module, blade): + """Delete certifcate""" + changed = True + if not module.check_mode: + try: + blade.certificates.delete_certificates(names=[module.params['name']]) + except Exception: + module.fail_json(msg="Failed to delete certifcate {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def create_cert(module, blade): + """Create certifcate""" + changed = True + if not module.check_mode: + try: + body = CertificatePost(certificate=module.params['contents'], certificate_type='external') + blade.certificates.create_certificates(names=[module.params['name']], certificate=body) + except Exception: + module.fail_json(msg="Failed to create certificate {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['absent', 'present']), + name=dict(type='str'), + contents=dict(type='str', no_log=True), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + state = module.params['state'] + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + try: + cert = blade.certificates.list_certificates(names=[module.params['name']]) + except Exception: + cert = None + + if not cert and state == 'present': + create_cert(module, blade) + elif state == 'absent' and cert: + delete_cert(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py new file mode 100644 index 00000000..dcac30c3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py @@ -0,0 +1,195 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_connect +version_added: '1.0.0' +short_description: Manage replication connections between two FlashBlades +description: +- Manage replication connections to specified remote FlashBlade system +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete replication connection + default: present + type: str + choices: [ absent, present ] + encrypted: + description: + - Define if replication connection is encrypted + type: bool + default: False + target_url: + description: + - Management IP address of target FlashBlade system + type: str + required: true + target_api: + description: + - API token for target FlashBlade system + type: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create a connection to remote FlashBlade system + purefb_connect: + target_url: 10.10.10.20 + target_api: 9c0b56bc-f941-f7a6-9f85-dcc3e9a8f7d6 + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +- name: Delete connection to target FlashBlade system + purefb_connect: + state: absent + target_url: 10.10.10.20 + target_api: 9c0b56bc-f941-f7a6-9f85-dcc3e9a8f7d6 + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + +HAS_PURITYFB = True +try: + from purity_fb import PurityFb, ArrayConnection, ArrayConnectionPost +except ImportError: + HAS_PURITYFB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.9' + + +def _check_connected(module, blade): + connected_blades = blade.array_connections.list_array_connections() + for target in range(0, len(connected_blades.items)): + if connected_blades.items[target].management_address is None: + try: + remote_system = PurityFb(module.params['target_url']) + remote_system.login(module.params['target_api']) + remote_array = remote_system.arrays.list_arrays().items[0].name + if connected_blades.items[target].remote.name == remote_array: + return connected_blades.items[target] + except Exception: + module.fail_json(msg="Failed to connect to remote array {0}.".format(module.params['target_url'])) + if connected_blades.items[target].management_address == module.params['target_url'] and \ + connected_blades.items[target].status in ["connected", "connecting", "partially_connected"]: + return connected_blades.items[target] + return None + + +def break_connection(module, blade, target_blade): + """Break connection between arrays""" + changed = True + if not module.check_mode: + source_blade = blade.arrays.list_arrays().items[0].name + try: + if target_blade.management_address is None: + module.fail_json(msg="Disconnect can only happen from the array that formed the connection") + blade.array_connections.delete_array_connections(remote_names=[target_blade.remote.name]) + except Exception: + module.fail_json(msg="Failed to disconnect {0} from {1}.".format(target_blade.remote.name, source_blade)) + module.exit_json(changed=changed) + + +def create_connection(module, blade): + """Create connection between arrays""" + changed = True + if not module.check_mode: + remote_array = module.params['target_url'] + try: + remote_system = PurityFb(module.params['target_url']) + remote_system.login(module.params['target_api']) + remote_array = remote_system.arrays.list_arrays().items[0].name + remote_conn_cnt = remote_system.array_connections.list_array_connections().pagination_info.total_item_count + # TODO: SD - Update with new max when fan-in/fan-out is enabled for FB + if remote_conn_cnt == 1: + module.fail_json(msg="Remote array {0} already connected to another array. Fan-In not supported".format(remote_array)) + connection_key = remote_system.array_connections.create_array_connections_connection_keys().items[0].connection_key + remote_array = remote_system.arrays.list_arrays().items[0].name + connection_info = ArrayConnectionPost(management_address=module.params['target_url'], + encrypted=module.params['encrypted'], + connection_key=connection_key) + blade.array_connections.create_array_connections(array_connection=connection_info) + except Exception: + module.fail_json(msg="Failed to connect to remote array {0}.".format(remote_array)) + module.exit_json(changed=changed) + + +def update_connection(module, blade, target_blade): + """Update array connection - only encryption currently""" + changed = True + if not module.check_mode: + if target_blade.management_address is None: + module.fail_json(msg="Update can only happen from the array that formed the connection") + if module.params['encrypted'] != target_blade.encrypted: + if module.params['encrypted'] and blade.file_system_replica_links.list_file_system_replica_links().pagination_info.total_item_count != 0: + module.fail_json(msg='Cannot turn array connection encryption on if file system replica links exist') + new_attr = ArrayConnection(encrypted=module.params['encrypted']) + try: + blade.array_connections.update_array_connections(remote_names=[target_blade.remote.name], array_connection=new_attr) + except Exception: + module.fail_json(msg='Failed to change encryption setting for array connection.') + else: + changed = False + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['absent', 'present']), + encrypted=dict(type='bool', default=False), + target_url=dict(type='str', required=True), + target_api=dict(type='str', no_log=True), + )) + + required_if = [('state', 'present', ['target_api'])] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURITYFB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + target_blade = _check_connected(module, blade) + if state == 'present' and not target_blade: + # TODO: SD - Update with new max when fan-out is supported + if blade.array_connections.list_array_connections().pagination_info.total_item_count == 1: + module.fail_json(msg='Source FlashBlade already connected to another array. Fan-Out not supported') + create_connection(module, blade) + elif state == 'present' and target_blade: + update_connection(module, blade, target_blade) + elif state == 'absent' and target_blade: + break_connection(module, blade, target_blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_dns.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_dns.py new file mode 100644 index 00000000..304d7ff9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_dns.py @@ -0,0 +1,167 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_dns +version_added: '1.0.0' +short_description: Configure Pure Storage FlashBlade DNS settings +description: +- Set or erase DNS configuration for Pure Storage FlashBlades. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete DNS servers configuration + type: str + default: present + choices: [ absent, present ] + domain: + description: + - Domain suffix to be appended when perofrming DNS lookups. + type: str + nameservers: + description: + - List of up to 3 unique DNS server IP addresses. These can be + IPv4 or IPv6 - No validation is done of the addresses is performed. + type: list + elements: str + search: + description: + - Ordered list of domain names to search + type: list + elements: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Delete exisitng DNS settings + purefb_dns: + state: absent + fa_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 + +- name: Set DNS settings + purefb_dns: + domain: purestorage.com + nameservers: + - 8.8.8.8 + - 8.8.4.4 + search: + - purestorage.com + - acme.com + fa_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Dns +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +def remove(duplicate): + final_list = [] + for num in duplicate: + if num not in final_list: + final_list.append(num) + return final_list + + +def delete_dns(module, blade): + """Delete DNS Settings""" + changed = True + if not module.check_mode: + changed = False + current_dns = blade.dns.list_dns() + if current_dns.items[0].domain or current_dns.items[0].search != [] or current_dns.items[0].nameservers != []: + try: + blade.dns.update_dns(dns_settings=Dns(domain='', search=[], nameservers=[])) + changed = True + except Exception: + module.fail_json(msg='Deletion of DNS settings failed') + module.exit_json(changed=changed) + + +def update_dns(module, blade): + """Set DNS Settings""" + changed = True + if not module.check_mode: + changed = False + current_dns = blade.dns.list_dns() + if module.params['domain']: + if current_dns.items[0].domain != module.params['domain']: + try: + blade.dns.update_dns(dns_settings=Dns(domain=module.params['domain'])) + changed = True + except Exception: + module.fail_json(msg='Update of DNS domain failed') + if module.params['nameservers']: + if sorted(module.params['nameservers']) != sorted(current_dns.items[0].nameservers): + try: + blade.dns.update_dns(dns_settings=Dns(nameservers=module.params['nameservers'])) + changed = True + except Exception: + module.fail_json(msg='Update of DNS nameservers failed') + if module.params['search']: + if sorted(module.params['search']) != sorted(current_dns.items[0].search): + try: + blade.dns.update_dns(dns_settings=Dns(search=module.params['search'])) + changed = True + except Exception: + module.fail_json(msg='Update of DNS search failed') + module.exit_json(changed=changed) + + +def main(): + + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + nameservers=dict(type='list', elements='str'), + search=dict(type='list', elements='str'), + domain=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present']), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + blade = get_blade(module) + + if module.params['state'] == 'absent': + delete_dns(module, blade) + elif module.params['state'] == 'present': + if module.params['nameservers']: + module.params['nameservers'] = remove(module.params['nameservers']) + if module.params['search']: + module.params['search'] = remove(module.params['search']) + update_dns(module, blade) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ds.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ds.py new file mode 100644 index 00000000..07300f88 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ds.py @@ -0,0 +1,390 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_ds +version_added: '1.0.0' +short_description: Configure FlashBlade Directory Service +description: +- Create, modify or erase directory services configurations. There is no + facility to SSL certificates at this time. Use the FlashBlade GUI for this + additional configuration work. +- If updating a directory service and i(bind_password) is provided this + will always cause a change, even if the password given isn't different from + the current. This makes this part of the module non-idempotent.. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete directory service configuration + default: present + type: str + choices: [ absent, present ] + dstype: + description: + - The type of directory service to work on + choices: [ management, nfs, smb ] + type: str + required: true + enable: + description: + - Whether to enable or disable directory service support. + default: false + type: bool + uri: + description: + - A list of up to 30 URIs of the directory servers. Each URI must include + the scheme ldap:// or ldaps:// (for LDAP over SSL), a hostname, and a + domain name or IP address. For example, ldap://ad.company.com configures + the directory service with the hostname "ad" in the domain "company.com" + while specifying the unencrypted LDAP protocol. + type: list + elements: str + base_dn: + description: + - Sets the base of the Distinguished Name (DN) of the directory service + groups. The base should consist of only Domain Components (DCs). The + base_dn will populate with a default value when a URI is entered by + parsing domain components from the URI. The base DN should specify DC= + for each domain component and multiple DCs should be separated by commas. + type: str + bind_password: + description: + - Sets the password of the bind_user user name account. + type: str + bind_user: + description: + - Sets the user name that can be used to bind to and query the directory. + - For Active Directory, enter the username - often referred to as + sAMAccountName or User Logon Name - of the account that is used to + perform directory lookups. + - For OpenLDAP, enter the full DN of the user. + type: str + nis_servers: + description: + - A list of up to 30 IP addresses or FQDNs for NIS servers. + - This cannot be used in conjunction with LDAP configurations. + type: list + elements: str + nis_domain: + description: + - The NIS domain to search + - This cannot be used in conjunction with LDAP configurations. + type: str + join_ou: + description: + - The optional organizational unit (OU) where the machine account + for the directory service will be created. + type: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Delete existing management directory service + purefb_ds: + dstype: management + state: absent + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Create NFS directory service (disabled) + purefb_ds: + dstype: nfs + uri: "ldaps://lab.purestorage.com" + base_dn: "DC=lab,DC=purestorage,DC=com" + bind_user: Administrator + bind_password: password + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Enable existing SMB directory service + purefb_ds: + dstypr: smb + enable: true + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Disable existing management directory service + purefb_ds: + dstype: management + enable: false + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Create NFS directory service (enabled) + purefb_ds: + dstype: nfs + enable: true + uri: "ldaps://lab.purestorage.com" + base_dn: "DC=lab,DC=purestorage,DC=com" + bind_user: Administrator + bind_password: password + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + + +NIS_API_VERSION = '1.7' +HAS_PURITY_FB = True +try: + from purity_fb import DirectoryService +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +def enable_ds(module, blade): + """Enable Directory Service""" + changed = True + if not module.check_mode: + try: + blade.directory_services.update_directory_services(names=[module.params['dstype']], + directory_service=DirectoryService(enabled=True)) + changed = True + except Exception: + module.fail_json(msg='Enable {0} Directory Service failed'.format(module.params['dstype'])) + module.exit_json(changed=changed) + + +def disable_ds(module, blade): + """Disable Directory Service""" + changed = True + if not module.check_mode: + try: + blade.directory_services.update_directory_services(names=[module.params['dstype']], + directory_service=DirectoryService(enabled=False)) + except Exception: + module.fail_json(msg='Disable {0} Directory Service failed'.format(module.params['dstype'])) + module.exit_json(changed=changed) + + +def delete_ds(module, blade): + """Delete Directory Service""" + changed = True + if not module.check_mode: + dirserv = blade.directory_services.list_directory_services(names=[module.params['dstype']]) + try: + if module.params['dstype'] == 'management': + if dirserv.items[0].uris: + dir_service = DirectoryService(uris=[''], + base_dn="", + bind_user="", + bind_password="", + enabled=False) + else: + changed = False + elif module.params['dstype'] == 'smb': + if dirserv.items[0].uris: + smb_attrs = {'join_ou': ''} + dir_service = DirectoryService(uris=[''], + base_dn='', + bind_user='', + bind_password='', + smb=smb_attrs, + enabled=False) + else: + changed = False + elif module.params['dstype'] == 'nfs': + if dirserv.items[0].uris: + dir_service = DirectoryService(uris=[''], + base_dn='', + bind_user='', + bind_password='', + enabled=False) + elif dirserv.items[0].nfs.nis_domains: + nfs_attrs = {'nis_domains': [], + 'nis_servers': []} + dir_service = DirectoryService(nfs=nfs_attrs, + enabled=False) + else: + changed = False + if changed: + blade.directory_services.update_directory_services(names=[module.params['dstype']], + directory_service=dir_service) + except Exception: + module.fail_json(msg='Delete {0} Directory Service failed'.format(module.params['dstype'])) + module.exit_json(changed=changed) + + +def update_ds(module, blade): + """Update Directory Service""" + changed = True + if not module.check_mode: + changed = False + mod_ds = False + attr = {} + try: + ds_now = blade.directory_services.list_directory_services(names=[module.params['dstype']]).items[0] + if module.params['dstype'] == 'nfs' and module.params['nis_servers']: + if sorted(module.params['nis_servers']) != sorted(ds_now.nfs.nis_servers) or \ + module.params['nis_domain'] != ''.join(map(str, ds_now.nfs.nis_domains)): + attr['nfs'] = {'nis_domains': [module.params['nis_domain']], + 'nis_servers': module.params['nis_servers'][0:30]} + mod_ds = True + else: + if module.params['uri']: + if sorted(module.params['uri'][0:30]) != sorted(ds_now.uris): + attr['uris'] = module.params['uri'][0:30] + mod_ds = True + if module.params['base_dn']: + if module.params['base_dn'] != ds_now.base_dn: + attr['base_dn'] = module.params['base_dn'] + mod_ds = True + if module.params['bind_user']: + if module.params['bind_user'] != ds_now.bind_user: + attr['bind_user'] = module.params['bind_user'] + mod_ds = True + if module.params['enable']: + if module.params['enable'] != ds_now.enabled: + attr['enabled'] = module.params['enable'] + mod_ds = True + if module.params['bind_password']: + attr['bind_password'] = module.params['bind_password'] + mod_ds = True + if module.params['dstype'] == 'smb': + if module.params['join_ou'] != ds_now.smb.join_ou: + attr['smb'] = {'join_ou': module.params['join_ou']} + mod_ds = True + if mod_ds: + n_attr = DirectoryService(**attr) + try: + blade.directory_services.update_directory_services(names=[module.params['dstype']], + directory_service=n_attr) + except Exception: + module.fail_json(msg='Failed to change {0} directory service.'.format(module.params['dstype'])) + except Exception: + module.fail_json(msg='Failed to get current {0} directory service.'.format(module.params['dstype'])) + changed = True + module.exit_json(changed=changed) + + +def create_ds(module, blade): + """Create Directory Service""" + changed = True + if not module.check_mode: + try: + if module.params['dstype'] == 'management': + if module.params['uri']: + dir_service = DirectoryService(uris=module.params['uri'][0:30], + base_dn=module.params['base_dn'], + bind_user=module.params['bind_user'], + bind_password=module.params['bind_password'], + enabled=module.params['enable']) + else: + module.fail_json(msg="Incorrect parameters provided for dstype {0}".format(module.params['dstype'])) + elif module.params['dstype'] == 'smb': + if module.params['uri']: + smb_attrs = {'join_ou': module.params['join_ou']} + dir_service = DirectoryService(uris=module.params['uri'][0:30], + base_dn=module.params['base_dn'], + bind_user=module.params['bind_user'], + bind_password=module.params['bind_password'], + smb=smb_attrs, + enabled=module.params['enable']) + else: + module.fail_json(msg="Incorrect parameters provided for dstype {0}".format(module.params['dstype'])) + elif module.params['dstype'] == 'nfs': + if module.params['nis_domain']: + nfs_attrs = {'nis_domains': [module.params['nis_domain']], + 'nis_servers': module.params['nis_servers'][0:30]} + dir_service = DirectoryService(nfs=nfs_attrs, + enabled=module.params['enable']) + else: + dir_service = DirectoryService(uris=module.params['uri'][0:30], + base_dn=module.params['base_dn'], + bind_user=module.params['bind_user'], + bind_password=module.params['bind_password'], + enabled=module.params['enable']) + blade.directory_services.update_directory_services(names=[module.params['dstype']], + directory_service=dir_service) + except Exception: + module.fail_json(msg='Create {0} Directory Service failed'.format(module.params['dstype'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + uri=dict(type='list', elements='str'), + dstype=dict(required=True, type='str', choices=['management', 'nfs', 'smb']), + state=dict(type='str', default='present', choices=['absent', 'present']), + enable=dict(type='bool', default=False), + bind_password=dict(type='str', no_log=True), + bind_user=dict(type='str'), + base_dn=dict(type='str'), + join_ou=dict(type='str'), + nis_domain=dict(type='str'), + nis_servers=dict(type='list', elements='str'), + )) + + required_together = [['uri', 'bind_password', 'bind_user', 'base_dn'], + ['nis_servers', 'nis_domain']] + mutually_exclusive = [['uri', 'nis_domain']] + + module = AnsibleModule(argument_spec, + required_together=required_together, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True) + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + ds_configured = False + dirserv = blade.directory_services.list_directory_services(names=[module.params['dstype']]) + ds_enabled = dirserv.items[0].enabled + if dirserv.items[0].base_dn is not None: + ds_configured = True + if (module.params['nis_domain'] or module.params['join_ou']) and (NIS_API_VERSION not in api_version): + module.fail_json(msg="NFS or SMB directory service attributes not supported by FlashBlade Purity version") + ldap_uri = False + set_ldap = False + for uri in range(0, len(dirserv.items[0].uris)): + if "ldap" in dirserv.items[0].uris[uri].lower(): + ldap_uri = True + if module.params['uri']: + for uri in range(0, len(module.params['uri'])): + if "ldap" in module.params['uri'][uri].lower(): + set_ldap = True + if not module.params['uri'] and ldap_uri or \ + module.params['uri'] and set_ldap: + if module.params['nis_servers'] or module.params['nis_domain']: + module.fail_json(msg="NIS configuration not supported in an LDAP environment") + if state == 'absent': + delete_ds(module, blade) + elif ds_configured and module.params['enable'] and ds_enabled: + update_ds(module, blade) + elif ds_configured and not module.params['enable'] and ds_enabled: + disable_ds(module, blade) + elif ds_configured and module.params['enable'] and not ds_enabled: + enable_ds(module, blade) +# Now we have enabled the DS lets make sure there aren't any new updates... + update_ds(module, blade) + elif not ds_configured and state == 'present': + create_ds(module, blade) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_dsrole.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_dsrole.py new file mode 100644 index 00000000..9b6680ae --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_dsrole.py @@ -0,0 +1,177 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_dsrole +version_added: '1.0.0' +short_description: Configure FlashBlade Management Directory Service Roles +description: +- Set or erase directory services role configurations. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete directory service role + default: present + type: str + choices: [ absent, present ] + role: + description: + - The directory service role to work on + choices: [ array_admin, ops_admin, readonly, storage_admin ] + type: str + required: true + group_base: + description: + - Specifies where the configured group is located in the directory + tree. This field consists of Organizational Units (OUs) that combine + with the base DN attribute and the configured group CNs to complete + the full Distinguished Name of the groups. The group base should + specify OU= for each OU and multiple OUs should be separated by commas. + The order of OUs is important and should get larger in scope from left + to right. + - Each OU should not exceed 64 characters in length. + type: str + group: + description: + - Sets the common Name (CN) of the configured directory service group + containing users for the FlashBlade. This name should be just the + Common Name of the group without the CN= specifier. + - Common Names should not exceed 64 characters in length. + type: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Delete existing array_admin directory service role + purefb_dsrole: + role: array_admin + state: absent + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Create array_admin directory service role + purefb_dsrole: + role: array_admin + group_base: "OU=PureGroups,OU=SANManagers" + group: pureadmins + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Update ops_admin directory service role + purefb_dsrole: + role: ops_admin + group_base: "OU=PureGroups" + group: opsgroup + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + + +HAS_PURITY_FB = True +try: + from purity_fb import DirectoryServiceRole +except ImportError: + HAS_PURITY_FB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +def update_role(module, blade): + """Update Directory Service Role""" + changed = True + if not module.check_mode: + role = blade.directory_services.list_directory_services_roles(names=[module.params['role']]) + if role.items[0].group_base != module.params['group_base'] or role.items[0].group != module.params['group']: + try: + role = DirectoryServiceRole(group_base=module.params['group_base'], + group=module.params['group']) + blade.directory_services.update_directory_services_roles(names=[module.params['role']], + directory_service_role=role) + except Exception: + module.fail_json(msg='Update Directory Service Role {0} failed'.format(module.params['role'])) + module.exit_json(changed=changed) + + +def delete_role(module, blade): + """Delete Directory Service Role""" + changed = True + if not module.check_mode: + try: + role = DirectoryServiceRole(group_base='', + group='') + blade.directory_services.update_directory_services_roles(names=[module.params['role']], + directory_service_role=role) + except Exception: + module.fail_json(msg='Delete Directory Service Role {0} failed'.format(module.params['role'])) + module.exit_json(changed=changed) + + +def create_role(module, blade): + """Create Directory Service Role""" + changed = True + if not module.check_mode: + try: + role = DirectoryServiceRole(group_base=module.params['group_base'], + group=module.params['group']) + blade.directory_services.update_directory_services_roles(names=[module.params['role']], + directory_service_role=role) + except Exception: + module.fail_json(msg='Create Directory Service Role {0} failed'.format(module.params['role'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + role=dict(required=True, type='str', choices=['array_admin', 'ops_admin', 'readonly', 'storage_admin']), + state=dict(type='str', default='present', choices=['absent', 'present']), + group_base=dict(type='str'), + group=dict(type='str'), + )) + + required_together = [['group', 'group_base']] + + module = AnsibleModule(argument_spec, + required_together=required_together, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + role_configured = False + role = blade.directory_services.list_directory_services_roles(names=[module.params['role']]) + if role.items[0].group is not None: + role_configured = True + + if state == 'absent' and role_configured: + delete_role(module, blade) + elif role_configured and state == 'present': + update_role(module, blade) + elif not role_configured and state == 'present': + create_role(module, blade) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py new file mode 100644 index 00000000..0b1a8efe --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py @@ -0,0 +1,645 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: purefb_fs +version_added: "1.0.0" +short_description: Manage filesystemon Pure Storage FlashBlade` +description: + - This module manages filesystems on Pure Storage FlashBlade. +author: Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Filesystem Name. + required: true + type: str + state: + description: + - Create, delete or modifies a filesystem. + required: false + default: present + type: str + choices: [ "present", "absent" ] + eradicate: + description: + - Define whether to eradicate the filesystem on delete or leave in trash. + required: false + type: bool + default: false + size: + description: + - Volume size in M, G, T or P units. See examples. + - If size is not set at filesystem creation time the filesystem size becomes unlimited. + type: str + required: false + nfsv3: + description: + - Define whether to NFSv3 protocol is enabled for the filesystem. + required: false + type: bool + default: true + nfsv4: + description: + - Define whether to NFSv4.1 protocol is enabled for the filesystem. + required: false + type: bool + default: true + nfs_rules: + description: + - Define the NFS rules in operation. + - If not set at filesystem creation time it defaults to I(*(rw,no_root_squash)) + - Supported binary options are ro/rw, secure/insecure, fileid_32bit/no_fileid_32bit, + root_squash/no_root_squash, all_squash/no_all_squash and atime/noatime + - Supported non-binary options are anonuid=#, anongid=#, sec=(sys|krb5) + required: false + type: str + smb: + description: + - Define whether to SMB protocol is enabled for the filesystem. + required: false + type: bool + default: false + smb_aclmode: + description: + - Specify the ACL mode for the SMB protocol. + required: false + type: str + default: shared + choices: [ "shared", "native" ] + http: + description: + - Define whether to HTTP/HTTPS protocol is enabled for the filesystem. + required: false + type: bool + default: false + snapshot: + description: + - Define whether a snapshot directory is enabled for the filesystem. + required: false + type: bool + default: false + writable: + description: + - Define if a filesystem is writeable. + required: false + type: bool + promote: + description: + - Promote/demote a filesystem. + - Can only demote the file-system if it is in a replica-link relationship. + required: false + type: bool + fastremove: + description: + - Define whether the fast remove directory is enabled for the filesystem. + required: false + type: bool + default: false + hard_limit: + description: + - Define whether the capacity for a filesystem is a hard limit. + - CAUTION This will cause the filesystem to go Read-Only if the + capacity has already exceeded the logical size of the filesystem. + required: false + type: bool + default: false + user_quota: + description: + - Default quota in M, G, T or P units for a user under this file system. + required: false + type: str + group_quota: + description: + - Default quota in M, G, T or P units for a group under this file system. + required: false + type: str + policy: + description: + - Filesystem policy to assign to or remove from a filesystem. + required: false + type: str + policy_state: + description: + - Add or delete a policy from a filesystem + required: false + default: present + type: str + choices: [ "absent", "present" ] + delete_link: + description: + - Define if the filesystem can be deleted even if it has a replica link + required: false + default: false + type: bool + discard_snaps: + description: + - Allow a filesystem to be demoted. + required: false + default: false + type: bool +extends_documentation_fragment: + - purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = ''' +- name: Create new filesystem named foo + purefb_fs: + name: foo + size: 1T + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete filesystem named foo + purefb_fs: + name: foo + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Recover filesystem named foo + purefb_fs: + name: foo + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Eradicate filesystem named foo + purefb_fs: + name: foo + state: absent + eradicate: true + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Promote filesystem named foo ready for failover + purefb_fs: + name: foo + promote: true + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Demote filesystem named foo after failover + purefb_fs: + name: foo + promote: false + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Modify attributes of an existing filesystem named foo + purefb_fs: + name: foo + size: 2T + nfsv3 : false + nfsv4 : true + user_quota: 10K + group_quota: 25M + nfs_rules: '10.21.200.0/24(ro)' + snapshot: true + fastremove: true + hard_limit: true + smb: true + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641''' + +RETURN = ''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import FileSystem, ProtocolRule, NfsRule, SmbRule +except ImportError: + HAS_PURITY_FB = False + +from ansible.module_utils.basic import AnsibleModule, human_to_bytes +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +HARD_LIMIT_API_VERSION = '1.4' +NFSV4_API_VERSION = '1.6' +REPLICATION_API_VERSION = '1.9' + + +def get_fs(module, blade): + """Return Filesystem or None""" + fsys = [] + fsys.append(module.params['name']) + try: + res = blade.file_systems.list_file_systems(names=fsys) + return res.items[0] + except Exception: + return None + + +def create_fs(module, blade): + """Create Filesystem""" + changed = True + if not module.check_mode: + try: + if not module.params['nfs_rules']: + module.params['nfs_rules'] = '*(rw,no_root_squash)' + if module.params['size']: + size = human_to_bytes(module.params['size']) + else: + size = 0 + + if module.params['user_quota']: + user_quota = human_to_bytes(module.params['user_quota']) + else: + user_quota = None + if module.params['group_quota']: + group_quota = human_to_bytes(module.params['group_quota']) + else: + group_quota = None + + api_version = blade.api_version.list_versions().versions + if HARD_LIMIT_API_VERSION in api_version: + if NFSV4_API_VERSION in api_version: + if REPLICATION_API_VERSION in api_version: + fs_obj = FileSystem(name=module.params['name'], + provisioned=size, + fast_remove_directory_enabled=module.params['fastremove'], + hard_limit_enabled=module.params['hard_limit'], + snapshot_directory_enabled=module.params['snapshot'], + nfs=NfsRule(v3_enabled=module.params['nfsv3'], + v4_1_enabled=module.params['nfsv4'], + rules=module.params['nfs_rules']), + smb=SmbRule(enabled=module.params['smb'], + acl_mode=module.params['smb_aclmode']), + http=ProtocolRule(enabled=module.params['http']), + default_user_quota=user_quota, + default_group_quota=group_quota + ) + else: + fs_obj = FileSystem(name=module.params['name'], + provisioned=size, + fast_remove_directory_enabled=module.params['fastremove'], + hard_limit_enabled=module.params['hard_limit'], + snapshot_directory_enabled=module.params['snapshot'], + nfs=NfsRule(v3_enabled=module.params['nfsv3'], + v4_1_enabled=module.params['nfsv4'], + rules=module.params['nfs_rules']), + smb=ProtocolRule(enabled=module.params['smb']), + http=ProtocolRule(enabled=module.params['http']), + default_user_quota=user_quota, + default_group_quota=group_quota + ) + else: + fs_obj = FileSystem(name=module.params['name'], + provisioned=size, + fast_remove_directory_enabled=module.params['fastremove'], + hard_limit_enabled=module.params['hard_limit'], + snapshot_directory_enabled=module.params['snapshot'], + nfs=NfsRule(enabled=module.params['nfsv3'], rules=module.params['nfs_rules']), + smb=ProtocolRule(enabled=module.params['smb']), + http=ProtocolRule(enabled=module.params['http']) + ) + else: + fs_obj = FileSystem(name=module.params['name'], + provisioned=size, + fast_remove_directory_enabled=module.params['fastremove'], + snapshot_directory_enabled=module.params['snapshot'], + nfs=NfsRule(enabled=module.params['nfs'], rules=module.params['nfs_rules']), + smb=ProtocolRule(enabled=module.params['smb']), + http=ProtocolRule(enabled=module.params['http']) + ) + blade.file_systems.create_file_systems(fs_obj) + except Exception: + module.fail_json(msg="Failed to create filesystem {0}.".format(module.params['name'])) + if REPLICATION_API_VERSION in api_version: + if module.params['policy']: + try: + blade.policies.list_policies(names=[module.params['policy']]) + except Exception: + _delete_fs(module, blade) + module.fail_json(msg="Policy {0} doesn't exist.".format(module.params['policy'])) + try: + blade.policies.create_policy_filesystems(policy_names=[module.params['policy']], + member_names=[module.params['name']]) + except Exception: + _delete_fs(module, blade) + module.fail_json(msg="Failed to apply policy {0} when creating filesystem {1}.".format(module.params['policy'], + module.params['name'])) + module.exit_json(changed=changed) + + +def modify_fs(module, blade): + """Modify Filesystem""" + changed = True + if not module.check_mode: + changed = False + mod_fs = False + attr = {} + if module.params['policy'] and module.params['policy_state'] == 'present': + try: + policy = blade.policies.list_policy_filesystems(policy_names=[module.params['policy']], + member_names=[module.params['name']]) + except Exception: + module.fail_json(msg='Policy {0} does not exist.'.format(module.params['policy'])) + if not policy.items: + try: + blade.policies.create_policy_filesystems(policy_names=[module.params['policy']], + member_names=[module.params['name']]) + mod_fs = True + except Exception: + module.fail_json(msg='Failed to add filesystem {0} to policy {1}.'.format(module.params['name'], + module.params['polict'])) + if module.params['policy'] and module.params['policy_state'] == 'absent': + try: + policy = blade.policies.list_policy_filesystems(policy_names=[module.params['policy']], + member_names=[module.params['name']]) + except Exception: + module.fail_json(msg='Policy {0} does not exist.'.format(module.params['policy'])) + if len(policy.items) == 1: + try: + blade.policies.delete_policy_filesystems(policy_names=[module.params['policy']], + member_names=[module.params['name']]) + mod_fs = True + except Exception: + module.fail_json(msg='Failed to remove filesystem {0} to policy {1}.'.format(module.params['name'], + module.params['polict'])) + if module.params['user_quota']: + user_quota = human_to_bytes(module.params['user_quota']) + if module.params['group_quota']: + group_quota = human_to_bytes(module.params['group_quota']) + fsys = get_fs(module, blade) + if fsys.destroyed: + attr['destroyed'] = False + mod_fs = True + if module.params['size']: + if human_to_bytes(module.params['size']) != fsys.provisioned: + attr['provisioned'] = human_to_bytes(module.params['size']) + mod_fs = True + api_version = blade.api_version.list_versions().versions + if NFSV4_API_VERSION in api_version: + if module.params['nfsv3'] and not fsys.nfs.v3_enabled: + attr['nfs'] = NfsRule(v3_enabled=module.params['nfsv3']) + mod_fs = True + if not module.params['nfsv3'] and fsys.nfs.v3_enabled: + attr['nfs'] = NfsRule(v3_enabled=module.params['nfsv3']) + mod_fs = True + if module.params['nfsv4'] and not fsys.nfs.v4_1_enabled: + attr['nfs'] = NfsRule(v4_1_enabled=module.params['nfsv4']) + mod_fs = True + if not module.params['nfsv4'] and fsys.nfs.v4_1_enabled: + attr['nfs'] = NfsRule(v4_1_enabled=module.params['nfsv4']) + mod_fs = True + if module.params['nfsv3'] or module.params['nfsv4'] and fsys.nfs.v3_enabled or fsys.nfs.v4_1_enabled: + if module.params['nfs_rules'] is not None: + if fsys.nfs.rules != module.params['nfs_rules']: + attr['nfs'] = NfsRule(rules=module.params['nfs_rules']) + mod_fs = True + if module.params['user_quota'] and user_quota != fsys.default_user_quota: + attr['default_user_quota'] = user_quota + mod_fs = True + if module.params['group_quota'] and group_quota != fsys.default_group_quota: + attr['default_group_quota'] = group_quota + mod_fs = True + else: + if module.params['nfsv3'] and not fsys.nfs.enabled: + attr['nfs'] = NfsRule(enabled=module.params['nfsv3']) + mod_fs = True + if not module.params['nfsv3'] and fsys.nfs.enabled: + attr['nfs'] = NfsRule(enabled=module.params['nfsv3']) + mod_fs = True + if module.params['nfsv3'] and fsys.nfs.enabled: + if fsys.nfs.rules != module.params['nfs_rules']: + attr['nfs'] = NfsRule(rules=module.params['nfs_rules']) + mod_fs = True + if REPLICATION_API_VERSION in api_version: + if module.params['smb'] and not fsys.smb.enabled: + attr['smb'] = SmbRule(enabled=module.params['smb'], + acl_mode=module.params['smb_aclmode']) + mod_fs = True + if not module.params['smb'] and fsys.smb.enabled: + attr['smb'] = ProtocolRule(enabled=module.params['smb']) + mod_fs = True + if module.params['smb'] and fsys.smb.enabled: + if fsys.smb.acl_mode != module.params['smb_aclmode']: + attr['smb'] = SmbRule(enabled=module.params['smb'], + acl_mode=module.params['smb_aclmode']) + mod_fs = True + else: + if module.params['smb'] and not fsys.smb.enabled: + attr['smb'] = ProtocolRule(enabled=module.params['smb']) + mod_fs = True + if not module.params['smb'] and fsys.smb.enabled: + attr['smb'] = ProtocolRule(enabled=module.params['smb']) + mod_fs = True + if module.params['http'] and not fsys.http.enabled: + attr['http'] = ProtocolRule(enabled=module.params['http']) + mod_fs = True + if not module.params['http'] and fsys.http.enabled: + attr['http'] = ProtocolRule(enabled=module.params['http']) + mod_fs = True + if module.params['snapshot'] and not fsys.snapshot_directory_enabled: + attr['snapshot_directory_enabled'] = module.params['snapshot'] + mod_fs = True + if not module.params['snapshot'] and fsys.snapshot_directory_enabled: + attr['snapshot_directory_enabled'] = module.params['snapshot'] + mod_fs = True + if module.params['fastremove'] and not fsys.fast_remove_directory_enabled: + attr['fast_remove_directory_enabled'] = module.params['fastremove'] + mod_fs = True + if not module.params['fastremove'] and fsys.fast_remove_directory_enabled: + attr['fast_remove_directory_enabled'] = module.params['fastremove'] + mod_fs = True + if HARD_LIMIT_API_VERSION in api_version: + if not module.params['hard_limit'] and fsys.hard_limit_enabled: + attr['hard_limit_enabled'] = module.params['hard_limit'] + mod_fs = True + if module.params['hard_limit'] and not fsys.hard_limit_enabled: + attr['hard_limit_enabled'] = module.params['hard_limit'] + mod_fs = True + if REPLICATION_API_VERSION in api_version: + if module.params['writable'] is not None: + if not module.params['writable'] and fsys.writable: + attr['writable'] = module.params['writable'] + mod_fs = True + if module.params['writable'] and not fsys.writable and fsys.promotion_status == 'promoted': + attr['writable'] = module.params['writable'] + mod_fs = True + if module.params['promote'] is not None: + if module.params['promote'] and fsys.promotion_status != 'promoted': + attr['requested_promotion_state'] = 'promoted' + mod_fs = True + if not module.params['promote'] and fsys.promotion_status == 'promoted': + # Demotion only allowed on filesystems in a replica-link + try: + blade.file_system_replica_links.list_file_system_replica_links(local_file_system_names=[module.params['name']]).items[0] + except Exception: + module.fail_json(msg='Filesystem {0} not demoted. Not in a replica-link'.format(module.params['name'])) + attr['requested_promotion_state'] = module.params['promote'] + mod_fs = True + if mod_fs: + n_attr = FileSystem(**attr) + if REPLICATION_API_VERSION in api_version: + try: + blade.file_systems.update_file_systems(name=module.params['name'], attributes=n_attr, + discard_non_snapshotted_data=module.params['discard_snaps']) + changed = True + except Exception: + module.fail_json(msg="Failed to update filesystem {0}.".format(module.params['name'])) + else: + try: + blade.file_systems.update_file_systems(name=module.params['name'], attributes=n_attr) + changed = True + except Exception: + module.fail_json(msg="Failed to update filesystem {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def _delete_fs(module, blade): + """ In module Delete Filesystem""" + api_version = blade.api_version.list_versions().versions + if NFSV4_API_VERSION in api_version: + blade.file_systems.update_file_systems(name=module.params['name'], + attributes=FileSystem(nfs=NfsRule(v3_enabled=False, + v4_1_enabled=False), + smb=ProtocolRule(enabled=False), + http=ProtocolRule(enabled=False), + destroyed=True) + ) + else: + blade.file_systems.update_file_systems(name=module.params['name'], + attributes=FileSystem(nfs=NfsRule(enabled=False), + smb=ProtocolRule(enabled=False), + http=ProtocolRule(enabled=False), + destroyed=True) + ) + + blade.file_systems.delete_file_systems(module.params['name']) + + +def delete_fs(module, blade): + """ Delete Filesystem""" + changed = True + if not module.check_mode: + try: + api_version = blade.api_version.list_versions().versions + if REPLICATION_API_VERSION in api_version: + if NFSV4_API_VERSION in api_version: + blade.file_systems.update_file_systems(name=module.params['name'], + attributes=FileSystem(nfs=NfsRule(v3_enabled=False, + v4_1_enabled=False), + smb=ProtocolRule(enabled=False), + http=ProtocolRule(enabled=False), + destroyed=True), + delete_link_on_eradication=module.params['delete_link'] + ) + else: + blade.file_systems.update_file_systems(name=module.params['name'], + attributes=FileSystem(nfs=NfsRule(enabled=False), + smb=ProtocolRule(enabled=False), + http=ProtocolRule(enabled=False), + destroyed=True), + delete_link_on_eradication=module.params['delete_link'] + ) + else: + if NFSV4_API_VERSION in api_version: + blade.file_systems.update_file_systems(name=module.params['name'], + attributes=FileSystem(nfs=NfsRule(v3_enabled=False, + v4_1_enabled=False), + smb=ProtocolRule(enabled=False), + http=ProtocolRule(enabled=False), + destroyed=True) + ) + else: + blade.file_systems.update_file_systems(name=module.params['name'], + attributes=FileSystem(nfs=NfsRule(enabled=False), + smb=ProtocolRule(enabled=False), + http=ProtocolRule(enabled=False), + destroyed=True) + ) + if module.params['eradicate']: + try: + blade.file_systems.delete_file_systems(name=module.params['name']) + except Exception: + module.fail_json(msg="Failed to delete filesystem {0}.".format(module.params['name'])) + except Exception: + module.fail_json(msg="Failed to update filesystem {0} prior to deletion.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def eradicate_fs(module, blade): + """ Eradicate Filesystem""" + changed = True + if not module.check_mode: + try: + blade.file_systems.delete_file_systems(name=module.params['name']) + except Exception: + module.fail_json(msg="Failed to eradicate filesystem {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + name=dict(type='str', required=True), + eradicate=dict(default='false', type='bool'), + nfsv3=dict(default='true', type='bool'), + nfsv4=dict(default='true', type='bool'), + nfs_rules=dict(type='str'), + smb=dict(default='false', type='bool'), + http=dict(default='false', type='bool'), + snapshot=dict(default='false', type='bool'), + writable=dict(type='bool'), + promote=dict(type='bool'), + fastremove=dict(default='false', type='bool'), + hard_limit=dict(default='false', type='bool'), + user_quota=dict(type='str'), + policy=dict(type='str'), + group_quota=dict(type='str'), + smb_aclmode=dict(type='str', default='shared', choices=['shared', 'native']), + policy_state=dict(default='present', choices=['present', 'absent']), + state=dict(default='present', choices=['present', 'absent']), + delete_link=dict(default=False, type='bool'), + discard_snaps=dict(default=False, type='bool'), + size=dict(type='str') + ) + ) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + fsys = get_fs(module, blade) + + if module.params['eradicate'] and state == 'present': + module.warn('Eradicate flag ignored without state=absent') + + if state == 'present' and not fsys: + create_fs(module, blade) + elif state == 'present' and fsys: + modify_fs(module, blade) + elif state == 'absent' and fsys and not fsys.destroyed: + delete_fs(module, blade) + elif state == 'absent' and fsys and fsys.destroyed and module.params['eradicate']: + eradicate_fs(module, blade) + elif state == 'absent' and not fsys: + module.exit_json(changed=False) + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs_replica.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs_replica.py new file mode 100644 index 00000000..57d876e0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs_replica.py @@ -0,0 +1,250 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: purefb_fs_replica +version_added: '1.0.0' +short_description: Manage filesystem replica links between Pure Storage FlashBlades +description: + - This module manages filesystem replica links between Pure Storage FlashBlades. +author: Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Local Filesystem Name. + required: true + type: str + state: + description: + - Creates or modifies a filesystem replica link + required: false + default: present + type: str + choices: [ "present", "absent" ] + target_array: + description: + - Remote array name to create replica on. + required: false + type: str + target_fs: + description: + - Name of target filesystem name + - If not supplied, will default to I(name). + type: str + required: false + policy: + description: + - Name of filesystem snapshot policy to apply to the replica link. + required: false + type: str +extends_documentation_fragment: + - purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = ''' +- name: Create new filesystem replica from foo to bar on arrayB + purefb_fs_replica: + name: foo + target_array: arrayB + target_fs: bar + policy: daily + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Add new snapshot policy to exisitng filesystem replica link + purefb_fs_replica: + name: foo + policy: weekly + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete snapshot policy from filesystem replica foo + purefb_fs_replica: + name: foo + policy: weekly + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641''' + +RETURN = ''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import FileSystemReplicaLink, LocationReference +except ImportError: + HAS_PURITY_FB = False + +MIN_REQUIRED_API_VERSION = '1.9' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +def get_local_fs(module, blade): + """Return Filesystem or None""" + try: + res = blade.file_systems.list_file_systems(names=[module.params['name']]) + return res.items[0] + except Exception: + return None + + +def get_local_rl(module, blade): + """Return Filesystem Replica Link or None""" + try: + res = blade.file_system_replica_links.list_file_system_replica_links(local_file_system_names=[module.params['name']]) + return res.items[0] + except Exception: + return None + + +def _check_connected(module, blade): + connected_blades = blade.array_connections.list_array_connections() + for target in range(0, len(connected_blades.items)): + if (connected_blades.items[target].remote.name == module.params['target_array'] or + connected_blades.items[target].management_address == module.params['target_array']) and \ + connected_blades.items[target].status in ["connected", "connecting", "partially_connected"]: + return connected_blades.items[target] + return None + + +def create_rl(module, blade): + """Create Filesystem Replica Link""" + changed = True + if not module.check_mode: + try: + remote_array = _check_connected(module, blade) + if remote_array: + if not module.params['target_fs']: + module.params['target_fs'] = module.params['name'] + if not module.params['policy']: + blade.file_system_replica_links.create_file_system_replica_links( + local_file_system_names=[module.params['name']], + remote_file_system_names=[module.params['target_fs']], + remote_names=[remote_array.remote.name]) + else: + blade.file_system_replica_links.create_file_system_replica_links( + local_file_system_names=[module.params['name']], + remote_file_system_names=[module.params['target_fs']], + remote_names=[remote_array.remote.name], + file_system_replica_link=FileSystemReplicaLink(policies=[LocationReference(name=module.params['policy'])])) + else: + module.fail_json(msg='Target array {0} is not connected'.format(module.params['target_array'])) + except Exception: + module.fail_json(msg="Failed to create filesystem replica link for {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def add_rl_policy(module, blade): + """Add Policy to Filesystem Replica Link""" + changed = True + if not module.check_mode: + if not module.params['target_array']: + module.params['target_array'] = blade.file_system_replica_links.list_file_system_replica_links( + local_file_system_names=[module.params['name']]).items[0].remote.name + remote_array = _check_connected(module, blade) + try: + already_a_policy = blade.file_system_replica_links.list_file_system_replica_link_policies( + local_file_system_names=[module.params['name']], + policy_names=[module.params['policy']], + remote_names=[remote_array.remote.name]) + if already_a_policy.items: + changed = False + else: + blade.file_system_replica_links.create_file_system_replica_link_policies( + policy_names=[module.params['policy']], + local_file_system_names=[module.params['name']], + remote_names=[remote_array.remote.name]) + except Exception: + module.fail_json(msg="Failed to add policy {0} to replica link {1}.".format(module.params['policy'], module.params['name'])) + module.exit_json(changed=changed) + + +def delete_rl_policy(module, blade): + """ Delete Policy from Filesystem Replica Link""" + changed = True + if not module.check_mode: + current_policy = blade.file_system_replica_links.list_file_system_replica_link_policies( + local_file_system_names=[module.params['name']], + policy_names=[module.params['policy']]) + if current_policy.items: + try: + blade.file_system_replica_links.delete_file_system_replica_link_policies( + policy_names=[module.params['policy']], + local_file_system_names=[module.params['name']], + remote_names=[current_policy.items[0].link.remote.name]) + except Exception: + module.fail_json(msg="Failed to remove policy {0} from replica link {1}.".format(module.params['policy'], module.params['name'])) + else: + changed = False + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + name=dict(type='str', required=True), + target_fs=dict(type='str'), + target_array=dict(type='str'), + policy=dict(type='str'), + state=dict(default='present', choices=['present', 'absent']) + ) + ) + + required_if = [['state', 'absent', ['policy']]] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + local_fs = get_local_fs(module, blade) + local_replica_link = get_local_rl(module, blade) + + if not local_fs: + module.fail_json(msg='Selected local filesystem {0} does not exist.'.format(module.params['name'])) + + if module.params['policy']: + try: + policy = blade.policies.list_policies(names=[module.params['policy']]) + except Exception: + module.fail_json(msg='Selected policy {0} does not exist.'.format(module.params['policy'])) + else: + policy = None + if state == 'present' and not local_replica_link: + create_rl(module, blade) + elif state == 'present' and local_replica_link and policy: + add_rl_policy(module, blade) + elif state == 'absent' and policy: + delete_rl_policy(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py new file mode 100644 index 00000000..eeb95f7b --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py @@ -0,0 +1,899 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2019, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or +# https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_info +version_added: '1.0.0' +short_description: Collect information from Pure Storage FlashBlade +description: + - Collect information from a Pure Storage FlashBlade running the + Purity//FB operating system. By default, the module will collect basic + information including hosts, host groups, protection + groups and volume counts. Additional information can be collected + based on the configured set of arguements. +author: + - Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + gather_subset: + description: + - When supplied, this argument will define the information to be collected. + Possible values for this include all, minimum, config, performance, + capacity, network, subnets, lags, filesystems, snapshots, buckets, + replication, policies and arrays. + required: false + type: list + elements: str + default: minimum +extends_documentation_fragment: + - purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: collect default set of info + purefb_info: + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + register: blade_info +- name: show default information + debug: + msg: "{{ blade_info['purefb_info']['default'] }}" + +- name: collect configuration and capacity info + purefb_info: + gather_subset: + - config + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + register: blade_info +- name: show config information + debug: + msg: "{{ blade_info['purefb_info']['config'] }}" + +- name: collect all info + purefb_info: + gather_subset: + - all + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + register: blade_info +- name: show all information + debug: + msg: "{{ blade_info['purefb_info'] }}" +''' + +RETURN = r''' +purefb_info: + description: Returns the information collected from the FlashBlade + returned: always + type: complex + sample: { + "capacity": { + "aggregate": { + "data_reduction": 1.1179228, + "snapshots": 0, + "total_physical": 17519748439, + "unique": 17519748439, + "virtual": 19585726464 + }, + "file-system": { + "data_reduction": 1.3642412, + "snapshots": 0, + "total_physical": 4748219708, + "unique": 4748219708, + "virtual": 6477716992 + }, + "object-store": { + "data_reduction": 1.0263462, + "snapshots": 0, + "total_physical": 12771528731, + "unique": 12771528731, + "virtual": 6477716992 + }, + "total": 83359896948925 + }, + "config": { + "alert_watchers": { + "enabled": true, + "name": "notify@acmestorage.com" + }, + "array_management": { + "base_dn": null, + "bind_password": null, + "bind_user": null, + "enabled": false, + "name": "management", + "services": [ + "management" + ], + "uris": [] + }, + "directory_service_roles": { + "array_admin": { + "group": null, + "group_base": null + }, + "ops_admin": { + "group": null, + "group_base": null + }, + "readonly": { + "group": null, + "group_base": null + }, + "storage_admin": { + "group": null, + "group_base": null + } + }, + "dns": { + "domain": "demo.acmestorage.com", + "name": "demo-fb-1", + "nameservers": [ + "8.8.8.8" + ], + "search": [ + "demo.acmestorage.com" + ] + }, + "nfs_directory_service": { + "base_dn": null, + "bind_password": null, + "bind_user": null, + "enabled": false, + "name": "nfs", + "services": [ + "nfs" + ], + "uris": [] + }, + "ntp": [ + "0.ntp.pool.org" + ], + "smb_directory_service": { + "base_dn": null, + "bind_password": null, + "bind_user": null, + "enabled": false, + "name": "smb", + "services": [ + "smb" + ], + "uris": [] + }, + "smtp": { + "name": "demo-fb-1", + "relay_host": null, + "sender_domain": "acmestorage.com" + }, + "ssl_certs": { + "certificate": "-----BEGIN CERTIFICATE-----\n\n-----END CERTIFICATE-----", + "common_name": "Acme Storage", + "country": "US", + "email": null, + "intermediate_certificate": null, + "issued_by": "Acme Storage", + "issued_to": "Acme Storage", + "key_size": 4096, + "locality": null, + "name": "global", + "organization": "Acme Storage", + "organizational_unit": "Acme Storage", + "passphrase": null, + "private_key": null, + "state": null, + "status": "self-signed", + "valid_from": "1508433967000", + "valid_to": "2458833967000" + } + }, + "default": { + "blades": 15, + "buckets": 7, + "filesystems": 2, + "flashblade_name": "demo-fb-1", + "object_store_accounts": 1, + "object_store_users": 1, + "purity_version": "2.2.0", + "snapshots": 1, + "total_capacity": 83359896948925 + }, + "filesystems": { + "k8s-pvc-d24b1357-579e-11e8-811f-ecf4bbc88f54": { + "destroyed": false, + "fast_remove": false, + "hard_limit": true, + "nfs_rules": "10.21.255.0/24(rw,no_root_squash)", + "provisioned": 21474836480, + "snapshot_enabled": false + }, + "z": { + "destroyed": false, + "fast_remove": false, + "hard_limit": false, + "provisioned": 1073741824, + "snapshot_enabled": false + } + }, + "lag": { + "uplink": { + "lag_speed": 0, + "port_speed": 40000000000, + "ports": [ + { + "name": "CH1.FM1.ETH1.1" + }, + { + "name": "CH1.FM1.ETH1.2" + }, + ], + "status": "healthy" + } + }, + "network": { + "fm1.admin0": { + "address": "10.10.100.6", + "gateway": "10.10.100.1", + "mtu": 1500, + "netmask": "255.255.255.0", + "services": [ + "support" + ], + "type": "vip", + "vlan": 2200 + }, + "fm2.admin0": { + "address": "10.10.100.7", + "gateway": "10.10.100.1", + "mtu": 1500, + "netmask": "255.255.255.0", + "services": [ + "support" + ], + "type": "vip", + "vlan": 2200 + }, + "nfs1": { + "address": "10.10.100.4", + "gateway": "10.10.100.1", + "mtu": 1500, + "netmask": "255.255.255.0", + "services": [ + "data" + ], + "type": "vip", + "vlan": 2200 + }, + "vir0": { + "address": "10.10.100.5", + "gateway": "10.10.100.1", + "mtu": 1500, + "netmask": "255.255.255.0", + "services": [ + "management" + ], + "type": "vip", + "vlan": 2200 + } + }, + "performance": { + "aggregate": { + "bytes_per_op": 0, + "bytes_per_read": 0, + "bytes_per_write": 0, + "read_bytes_per_sec": 0, + "reads_per_sec": 0, + "usec_per_other_op": 0, + "usec_per_read_op": 0, + "usec_per_write_op": 0, + "write_bytes_per_sec": 0, + "writes_per_sec": 0 + }, + "http": { + "bytes_per_op": 0, + "bytes_per_read": 0, + "bytes_per_write": 0, + "read_bytes_per_sec": 0, + "reads_per_sec": 0, + "usec_per_other_op": 0, + "usec_per_read_op": 0, + "usec_per_write_op": 0, + "write_bytes_per_sec": 0, + "writes_per_sec": 0 + }, + "nfs": { + "bytes_per_op": 0, + "bytes_per_read": 0, + "bytes_per_write": 0, + "read_bytes_per_sec": 0, + "reads_per_sec": 0, + "usec_per_other_op": 0, + "usec_per_read_op": 0, + "usec_per_write_op": 0, + "write_bytes_per_sec": 0, + "writes_per_sec": 0 + }, + "s3": { + "bytes_per_op": 0, + "bytes_per_read": 0, + "bytes_per_write": 0, + "read_bytes_per_sec": 0, + "reads_per_sec": 0, + "usec_per_other_op": 0, + "usec_per_read_op": 0, + "usec_per_write_op": 0, + "write_bytes_per_sec": 0, + "writes_per_sec": 0 + } + }, + "snapshots": { + "z.188": { + "destroyed": false, + "source": "z", + "source_destroyed": false, + "suffix": "188" + } + }, + "subnet": { + "new-mgmt": { + "gateway": "10.10.100.1", + "interfaces": [ + { + "name": "fm1.admin0" + }, + { + "name": "fm2.admin0" + }, + { + "name": "nfs1" + }, + { + "name": "vir0" + } + ], + "lag": "uplink", + "mtu": 1500, + "prefix": "10.10.100.0/24", + "services": [ + "data", + "management", + "support" + ], + "vlan": 2200 + } + } + } +''' + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.3' +HARD_LIMIT_API_VERSION = '1.4' +POLICIES_API_VERSION = '1.5' +CERT_GROUPS_API_VERSION = '1.8' +REPLICATION_API_VERSION = '1.9' + + +def generate_default_dict(blade): + default_info = {} + defaults = blade.arrays.list_arrays().items[0] + default_info['flashblade_name'] = defaults.name + default_info['purity_version'] = defaults.version + default_info['filesystems'] = \ + len(blade.file_systems.list_file_systems().items) + default_info['snapshots'] = \ + len(blade.file_system_snapshots.list_file_system_snapshots().items) + default_info['buckets'] = len(blade.buckets.list_buckets().items) + default_info['object_store_users'] = \ + len(blade.object_store_users.list_object_store_users().items) + default_info['object_store_accounts'] = \ + len(blade.object_store_accounts.list_object_store_accounts().items) + default_info['blades'] = len(blade.blade.list_blades().items) + default_info['certificates'] = \ + len(blade.certificates.list_certificates().items) + default_info['total_capacity'] = \ + blade.arrays.list_arrays_space().items[0].capacity + api_version = blade.api_version.list_versions().versions + if POLICIES_API_VERSION in api_version: + default_info['policies'] = \ + len(blade.policies.list_policies().items) + if CERT_GROUPS_API_VERSION in api_version: + default_info['certificate_groups'] = \ + len(blade.certificate_groups.list_certificate_groups().items) + if REPLICATION_API_VERSION in api_version: + default_info['fs_replicas'] = \ + len(blade.file_system_replica_links.list_file_system_replica_links().items) + default_info['remote_credentials'] = \ + len(blade.object_store_remote_credentials.list_object_store_remote_credentials().items) + default_info['bucket_replicas'] = \ + len(blade.bucket_replica_links.list_bucket_replica_links().items) + default_info['connected_arrays'] = \ + len(blade.array_connections.list_array_connections().items) + default_info['targets'] = \ + len(blade.targets.list_targets().items) + default_info['kerberos_keytabs'] = \ + len(blade.keytabs.list_keytabs().items) + return default_info + + +def generate_perf_dict(blade): + perf_info = {} + total_perf = blade.arrays.list_arrays_performance() + http_perf = blade.arrays.list_arrays_performance(protocol='http') + s3_perf = blade.arrays.list_arrays_performance(protocol='s3') + nfs_perf = blade.arrays.list_arrays_performance(protocol='nfs') + perf_info['aggregate'] = { + 'bytes_per_op': total_perf.items[0].bytes_per_op, + 'bytes_per_read': total_perf.items[0].bytes_per_read, + 'bytes_per_write': total_perf.items[0].bytes_per_write, + 'read_bytes_per_sec': total_perf.items[0].read_bytes_per_sec, + 'reads_per_sec': total_perf.items[0].reads_per_sec, + 'usec_per_other_op': total_perf.items[0].usec_per_other_op, + 'usec_per_read_op': total_perf.items[0].usec_per_read_op, + 'usec_per_write_op': total_perf.items[0].usec_per_write_op, + 'write_bytes_per_sec': total_perf.items[0].write_bytes_per_sec, + 'writes_per_sec': total_perf.items[0].writes_per_sec, + } + perf_info['http'] = { + 'bytes_per_op': http_perf.items[0].bytes_per_op, + 'bytes_per_read': http_perf.items[0].bytes_per_read, + 'bytes_per_write': http_perf.items[0].bytes_per_write, + 'read_bytes_per_sec': http_perf.items[0].read_bytes_per_sec, + 'reads_per_sec': http_perf.items[0].reads_per_sec, + 'usec_per_other_op': http_perf.items[0].usec_per_other_op, + 'usec_per_read_op': http_perf.items[0].usec_per_read_op, + 'usec_per_write_op': http_perf.items[0].usec_per_write_op, + 'write_bytes_per_sec': http_perf.items[0].write_bytes_per_sec, + 'writes_per_sec': http_perf.items[0].writes_per_sec, + } + perf_info['s3'] = { + 'bytes_per_op': s3_perf.items[0].bytes_per_op, + 'bytes_per_read': s3_perf.items[0].bytes_per_read, + 'bytes_per_write': s3_perf.items[0].bytes_per_write, + 'read_bytes_per_sec': s3_perf.items[0].read_bytes_per_sec, + 'reads_per_sec': s3_perf.items[0].reads_per_sec, + 'usec_per_other_op': s3_perf.items[0].usec_per_other_op, + 'usec_per_read_op': s3_perf.items[0].usec_per_read_op, + 'usec_per_write_op': s3_perf.items[0].usec_per_write_op, + 'write_bytes_per_sec': s3_perf.items[0].write_bytes_per_sec, + 'writes_per_sec': s3_perf.items[0].writes_per_sec, + } + perf_info['nfs'] = { + 'bytes_per_op': nfs_perf.items[0].bytes_per_op, + 'bytes_per_read': nfs_perf.items[0].bytes_per_read, + 'bytes_per_write': nfs_perf.items[0].bytes_per_write, + 'read_bytes_per_sec': nfs_perf.items[0].read_bytes_per_sec, + 'reads_per_sec': nfs_perf.items[0].reads_per_sec, + 'usec_per_other_op': nfs_perf.items[0].usec_per_other_op, + 'usec_per_read_op': nfs_perf.items[0].usec_per_read_op, + 'usec_per_write_op': nfs_perf.items[0].usec_per_write_op, + 'write_bytes_per_sec': nfs_perf.items[0].write_bytes_per_sec, + 'writes_per_sec': nfs_perf.items[0].writes_per_sec, + } + # TODO (SD): Only add in when new python SDK exists that support Python 3.7 + # api_version = blade.api_version.list_versions().versions + # if REPLICATION_API_VERSION in api_version: + # file_repl_perf = blade.array_connections.list_array_connections_performance_replication(type='file-system') + # obj_repl_perf = blade.array_connections.list_array_connections_performance_replication(type='object-store') + # if len(file_repl_perf.total): + # perf_info['file_replication'] = { + # 'received_bytes_per_sec': file_repl_perf.total[0].async.received_bytes_per_sec, + # 'transmitted_bytes_per_sec': file_repl_perf.total[0].async.transmitted_bytes_per_sec, + # } + # if len(obj_repl_perf.total): + # perf_info['object_replication'] = { + # 'received_bytes_per_sec': obj_repl_perf.total[0].async.received_bytes_per_sec, + # 'transmitted_bytes_per_sec': obj_repl_perf.total[0].async.transmitted_bytes_per_sec, + # } + return perf_info + + +def generate_config_dict(blade): + config_info = {} + config_info['dns'] = blade.dns.list_dns().items[0].to_dict() + config_info['smtp'] = blade.smtp.list_smtp().items[0].to_dict() + try: + config_info['alert_watchers'] = \ + blade.alert_watchers.list_alert_watchers().items[0].to_dict() + except Exception: + config_info['alert_watchers'] = '' + api_version = blade.api_version.list_versions().versions + if HARD_LIMIT_API_VERSION in api_version: + config_info['array_management'] = \ + blade.directory_services.list_directory_services(names=['management']).items[0].to_dict() + config_info['directory_service_roles'] = {} + roles = blade.directory_services.list_directory_services_roles() + for role in range(0, len(roles.items)): + role_name = roles.items[role].name + config_info['directory_service_roles'][role_name] = { + 'group': roles.items[role].group, + 'group_base': roles.items[role].group_base + } + config_info['nfs_directory_service'] = \ + blade.directory_services.list_directory_services(names=['nfs']).items[0].to_dict() + config_info['smb_directory_service'] = \ + blade.directory_services.list_directory_services(names=['smb']).items[0].to_dict() + config_info['ntp'] = blade.arrays.list_arrays().items[0].ntp_servers + config_info['ssl_certs'] = \ + blade.certificates.list_certificates().items[0].to_dict() + api_version = blade.api_version.list_versions().versions + if CERT_GROUPS_API_VERSION in api_version: + try: + config_info['certificate_groups'] = \ + blade.certificate_groups.list_certificate_groups().items[0].to_dict() + except Exception: + config_info['certificate_groups'] = '' + if REPLICATION_API_VERSION in api_version: + config_info['snmp_agents'] = {} + snmp_agents = blade.snmp_agents.list_snmp_agents() + for agent in range(0, len(snmp_agents.items)): + agent_name = snmp_agents.items[agent].name + config_info['snmp_agents'][agent_name] = { + 'version': snmp_agents.items[agent].version, + 'engine_id': snmp_agents.items[agent].engine_id + } + if config_info['snmp_agents'][agent_name]['version'] == 'v3': + config_info['snmp_agents'][agent_name]['auth_protocol'] = snmp_agents.items[agent].v3.auth_protocol + config_info['snmp_agents'][agent_name]['privacy_protocol'] = snmp_agents.items[agent].v3.privacy_protocol + config_info['snmp_agents'][agent_name]['user'] = snmp_agents.items[agent].v3.user + config_info['snmp_managers'] = {} + snmp_managers = blade.snmp_managers.list_snmp_managers() + for manager in range(0, len(snmp_managers.items)): + mgr_name = snmp_managers.items[manager].name + config_info['snmp_managers'][mgr_name] = { + 'version': snmp_managers.items[manager].version, + 'host': snmp_managers.items[manager].host, + 'notification': snmp_managers.items[manager].notification + } + if config_info['snmp_managers'][mgr_name]['version'] == 'v3': + config_info['snmp_managers'][mgr_name]['auth_protocol'] = snmp_managers.items[manager].v3.auth_protocol + config_info['snmp_managers'][mgr_name]['privacy_protocol'] = snmp_managers.items[manager].v3.privacy_protocol + config_info['snmp_managers'][mgr_name]['user'] = snmp_managers.items[manager].v3.user + return config_info + + +def generate_subnet_dict(blade): + sub_info = {} + subnets = blade.subnets.list_subnets() + for sub in range(0, len(subnets.items)): + sub_name = subnets.items[sub].name + if subnets.items[sub].enabled: + sub_info[sub_name] = { + 'gateway': subnets.items[sub].gateway, + 'mtu': subnets.items[sub].mtu, + 'vlan': subnets.items[sub].vlan, + 'prefix': subnets.items[sub].prefix, + 'services': subnets.items[sub].services, + } + sub_info[sub_name]['lag'] = subnets.items[sub].link_aggregation_group.name + sub_info[sub_name]['interfaces'] = [] + for iface in range(0, len(subnets.items[sub].interfaces)): + sub_info[sub_name]['interfaces'].append({'name': subnets.items[sub].interfaces[iface].name}) + return sub_info + + +def generate_lag_dict(blade): + lag_info = {} + groups = blade.link_aggregation_groups.list_link_aggregation_groups() + for groupcnt in range(0, len(groups.items)): + lag_name = groups.items[groupcnt].name + lag_info[lag_name] = { + 'lag_speed': groups.items[groupcnt].lag_speed, + 'port_speed': groups.items[groupcnt].port_speed, + 'status': groups.items[groupcnt].status, + } + lag_info[lag_name]['ports'] = [] + for port in range(0, len(groups.items[groupcnt].ports)): + lag_info[lag_name]['ports'].append({'name': groups.items[groupcnt].ports[port].name}) + return lag_info + + +def generate_targets_dict(blade): + targets_info = {} + targets = blade.targets.list_targets() + for target in range(0, len(targets.items)): + target_name = targets.items[target].name + targets_info[target_name] = { + 'address': targets.items[target].address, + 'status': targets.items[target].status, + 'status_details': targets.items[target].status_details, + } + return targets_info + + +def generate_remote_creds_dict(blade): + remote_creds_info = {} + remote_creds = blade.object_store_remote_credentials.list_object_store_remote_credentials() + for cred_cnt in range(0, len(remote_creds.items)): + cred_name = remote_creds.items[cred_cnt].name + remote_creds_info[cred_name] = { + 'access_key': remote_creds.items[cred_cnt].access_key_id, + 'remote_array': remote_creds.items[cred_cnt].remote.name, + } + return remote_creds_info + + +def generate_file_repl_dict(blade): + file_repl_info = {} + file_links = blade.file_system_replica_links.list_file_system_replica_links() + for linkcnt in range(0, len(file_links.items)): + fs_name = file_links.items[linkcnt].local_file_system.name + file_repl_info[fs_name] = { + 'direction': file_links.items[linkcnt].direction, + 'lag': file_links.items[linkcnt].lag, + 'status': file_links.items[linkcnt].status, + 'remote_fs': file_links.items[linkcnt].remote.name + ":" + file_links.items[linkcnt].remote_file_system.name, + 'recovery_point': file_links.items[linkcnt].recovery_point, + } + file_repl_info[fs_name]['policies'] = [] + for policy_cnt in range(0, len(file_links.items[linkcnt].policies)): + file_repl_info[fs_name]['policies'].append(file_links.items[linkcnt].policies[policy_cnt].display_name) + return file_repl_info + + +def generate_bucket_repl_dict(blade): + bucket_repl_info = {} + bucket_links = blade.bucket_replica_links.list_bucket_replica_links() + for linkcnt in range(0, len(bucket_links.items)): + bucket_name = bucket_links.items[linkcnt].local_bucket.name + bucket_repl_info[bucket_name] = { + 'direction': bucket_links.items[linkcnt].direction, + 'lag': bucket_links.items[linkcnt].lag, + 'paused': bucket_links.items[linkcnt].paused, + 'status': bucket_links.items[linkcnt].status, + 'remote_bucket': bucket_links.items[linkcnt].remote_bucket.name, + 'remote_credentials': bucket_links.items[linkcnt].remote_credentials.name, + 'recovery_point': bucket_links.items[linkcnt].recovery_point, + } + return bucket_repl_info + + +def generate_network_dict(blade): + net_info = {} + ports = blade.network_interfaces.list_network_interfaces() + for portcnt in range(0, len(ports.items)): + int_name = ports.items[portcnt].name + if ports.items[portcnt].enabled: + net_info[int_name] = { + 'type': ports.items[portcnt].type, + 'mtu': ports.items[portcnt].mtu, + 'vlan': ports.items[portcnt].vlan, + 'address': ports.items[portcnt].address, + 'services': ports.items[portcnt].services, + 'gateway': ports.items[portcnt].gateway, + 'netmask': ports.items[portcnt].netmask, + } + return net_info + + +def generate_capacity_dict(blade): + capacity_info = {} + total_cap = blade.arrays.list_arrays_space() + file_cap = blade.arrays.list_arrays_space(type='file-system') + object_cap = blade.arrays.list_arrays_space(type='object-store') + capacity_info['total'] = total_cap.items[0].capacity + capacity_info['aggregate'] = { + 'data_reduction': total_cap.items[0].space.data_reduction, + 'snapshots': total_cap.items[0].space.snapshots, + 'total_physical': total_cap.items[0].space.total_physical, + 'unique': total_cap.items[0].space.unique, + 'virtual': total_cap.items[0].space.virtual, + } + capacity_info['file-system'] = { + 'data_reduction': file_cap.items[0].space.data_reduction, + 'snapshots': file_cap.items[0].space.snapshots, + 'total_physical': file_cap.items[0].space.total_physical, + 'unique': file_cap.items[0].space.unique, + 'virtual': file_cap.items[0].space.virtual, + } + capacity_info['object-store'] = { + 'data_reduction': object_cap.items[0].space.data_reduction, + 'snapshots': object_cap.items[0].space.snapshots, + 'total_physical': object_cap.items[0].space.total_physical, + 'unique': object_cap.items[0].space.unique, + 'virtual': file_cap.items[0].space.virtual, + } + + return capacity_info + + +def generate_snap_dict(blade): + snap_info = {} + snaps = blade.file_system_snapshots.list_file_system_snapshots() + api_version = blade.api_version.list_versions().versions + for snap in range(0, len(snaps.items)): + snapshot = snaps.items[snap].name + snap_info[snapshot] = { + 'destroyed': snaps.items[snap].destroyed, + 'source': snaps.items[snap].source, + 'suffix': snaps.items[snap].suffix, + 'source_destroyed': snaps.items[snap].source_destroyed, + } + if REPLICATION_API_VERSION in api_version: + snap_info[snapshot]['owner'] = snaps.items[snap].owner.name + snap_info[snapshot]['owner_destroyed'] = snaps.items[snap].owner_destroyed + snap_info[snapshot]['source_display_name'] = snaps.items[snap].source_display_name + snap_info[snapshot]['source_is_local'] = snaps.items[snap].source_is_local + snap_info[snapshot]['source_location'] = snaps.items[snap].source_location.name + return snap_info + + +def generate_snap_transfer_dict(blade): + snap_transfer_info = {} + snap_transfers = blade.file_system_snapshots.list_file_system_snapshots_transfer() + for snap_transfer in range(0, len(snap_transfers.items)): + transfer = snap_transfers.items[snap_transfer].name + snap_transfer_info[transfer] = { + 'completed': snap_transfers.items[snap_transfer].completed, + 'data_transferred': snap_transfers.items[snap_transfer].data_transferred, + 'progress': snap_transfers.items[snap_transfer].progress, + 'direction': snap_transfers.items[snap_transfer].direction, + 'remote': snap_transfers.items[snap_transfer].remote.name, + 'remote_snapshot': snap_transfers.items[snap_transfer].remote_snapshot.name, + 'started': snap_transfers.items[snap_transfer].started, + 'status': snap_transfers.items[snap_transfer].status + } + return snap_transfer_info + + +def generate_array_conn_dict(blade): + array_conn_info = {} + arrays = blade.array_connections.list_array_connections() + for arraycnt in range(0, len(arrays.items)): + array = arrays.items[arraycnt].remote.name + array_conn_info[array] = { + 'encrypted': arrays.items[arraycnt].encrypted, + 'replication_addresses': arrays.items[arraycnt].replication_addresses, + 'management_address': arrays.items[arraycnt].management_address, + 'id': arrays.items[arraycnt].remote.id, + 'status': arrays.items[arraycnt].status, + 'version': arrays.items[arraycnt].version, + } + if arrays.items[arraycnt].encrypted: + array_conn_info[array]['ca_certificate_group'] = arrays.items[arraycnt].ca_certificate_group.name + return array_conn_info + + +def generate_policies_dict(blade): + policies_info = {} + policies = blade.policies.list_policies() + for policycnt in range(0, len(policies.items)): + policy = policies.items[policycnt].name + policies_info[policy] = {} + policies_info[policy]['enabled'] = policies.items[policycnt].enabled + if policies.items[policycnt].rules: + policies_info[policy]['rules'] = policies.items[policycnt].rules[0].to_dict() + return policies_info + + +def generate_bucket_dict(blade): + bucket_info = {} + buckets = blade.buckets.list_buckets() + for bckt in range(0, len(buckets.items)): + bucket = buckets.items[bckt].name + bucket_info[bucket] = { + 'versioning': buckets.items[bckt].versioning, + 'object_count': buckets.items[bckt].object_count, + 'id': buckets.items[bckt].id, + 'account_name': buckets.items[bckt].account.name, + 'data_reduction': buckets.items[bckt].space.data_reduction, + 'snapshot_space': buckets.items[bckt].space.snapshots, + 'total_physical_space': buckets.items[bckt].space.total_physical, + 'unique_space': buckets.items[bckt].space.unique, + 'virtual_space': buckets.items[bckt].space.virtual, + 'created': buckets.items[bckt].created, + 'destroyed': buckets.items[bckt].destroyed, + 'time_remaining': buckets.items[bckt].time_remaining, + } + return bucket_info + + +def generate_fs_dict(blade): + fs_info = {} + fsys = blade.file_systems.list_file_systems() + for fsystem in range(0, len(fsys.items)): + share = fsys.items[fsystem].name + fs_info[share] = { + 'fast_remove': fsys.items[fsystem].fast_remove_directory_enabled, + 'snapshot_enabled': fsys.items[fsystem].snapshot_directory_enabled, + 'provisioned': fsys.items[fsystem].provisioned, + 'destroyed': fsys.items[fsystem].destroyed, + } + if fsys.items[fsystem].http.enabled: + fs_info[share]['http'] = fsys.items[fsystem].http.enabled + if fsys.items[fsystem].smb.enabled: + fs_info[share]['smb_mode'] = fsys.items[fsystem].smb.acl_mode + if fsys.items[fsystem].nfs.enabled: + fs_info[share]['nfs_rules'] = fsys.items[fsystem].nfs.rules + api_version = blade.api_version.list_versions().versions + if HARD_LIMIT_API_VERSION in api_version: + fs_info[share]['hard_limit'] = fsys.items[fsystem].hard_limit_enabled + if REPLICATION_API_VERSION in api_version: + fs_info[share]['promotion_status'] = fsys.items[fsystem].promotion_status + fs_info[share]['requested_promotion_state'] = fsys.items[fsystem].requested_promotion_state + fs_info[share]['writable'] = fsys.items[fsystem].writable + fs_info[share]['source'] = { + 'is_local': fsys.items[fsystem].source.is_local, + 'name': fsys.items[fsystem].source.name + } + return fs_info + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + gather_subset=dict(default='minimum', type='list', elements='str') + )) + + module = AnsibleModule(argument_spec, supports_check_mode=False) + + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + subset = [test.lower() for test in module.params['gather_subset']] + valid_subsets = ('all', 'minimum', 'config', 'performance', 'capacity', + 'network', 'subnets', 'lags', 'filesystems', 'snapshots', + 'buckets', 'arrays', 'replication', 'policies') + subset_test = (test in valid_subsets for test in subset) + if not all(subset_test): + module.fail_json(msg="value must gather_subset must be one or more of: %s, got: %s" + % (",".join(valid_subsets), ",".join(subset))) + + info = {} + + if 'minimum' in subset or 'all' in subset: + info['default'] = generate_default_dict(blade) + if 'performance' in subset or 'all' in subset: + info['performance'] = generate_perf_dict(blade) + if 'config' in subset or 'all' in subset: + info['config'] = generate_config_dict(blade) + if 'capacity' in subset or 'all' in subset: + info['capacity'] = generate_capacity_dict(blade) + if 'lags' in subset or 'all' in subset: + info['lag'] = generate_lag_dict(blade) + if 'network' in subset or 'all' in subset: + info['network'] = generate_network_dict(blade) + if 'subnets' in subset or 'all' in subset: + info['subnet'] = generate_subnet_dict(blade) + if 'filesystems' in subset or 'all' in subset: + info['filesystems'] = generate_fs_dict(blade) + if 'snapshots' in subset or 'all' in subset: + info['snapshots'] = generate_snap_dict(blade) + if 'buckets' in subset or 'all' in subset: + info['buckets'] = generate_bucket_dict(blade) + api_version = blade.api_version.list_versions().versions + if POLICIES_API_VERSION in api_version: + if 'policies' in subset or 'all' in subset: + info['policies'] = generate_policies_dict(blade) + if REPLICATION_API_VERSION in api_version: + if 'arrays' in subset or 'all' in subset: + info['arrays'] = generate_array_conn_dict(blade) + if 'replication' in subset or 'all' in subset: + info['file_replication'] = generate_file_repl_dict(blade) + info['bucket_replication'] = generate_bucket_repl_dict(blade) + info['snap_transfers'] = generate_snap_transfer_dict(blade) + info['remote_credentials'] = generate_remote_creds_dict(blade) + info['targets'] = generate_targets_dict(blade) + + module.exit_json(changed=False, purefb_info=info) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_inventory.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_inventory.py new file mode 100644 index 00000000..239de7f7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_inventory.py @@ -0,0 +1,149 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_inventory +version_added: '1.0.0' +short_description: Collect information from Pure Storage FlashBlade +description: + - Collect information from a Pure Storage FlashBlade running the + Purity//FB operating system. By default, the module will collect basic + information including hosts, host groups, protection + groups and volume counts. Additional information can be collected + based on the configured set of arguements. +author: + - Pure Storage ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +extends_documentation_fragment: + - purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: collect FlashBlade invenroty + purefa_inventory: + fa_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +- name: show default information + debug: + msg: "{{ array_info['purefb_info'] }}" + +''' + +RETURN = r''' +purefb_inventory: + description: Returns the inventory information for the FlashArray + returned: always + type: complex + sample: { + "admins": { + "pureuser": { + "role": "array_admin", + "type": "local" + } + }, + "apps": { + "offload": { + "description": "Snapshot offload to NFS or Amazon S3", + "status": "healthy", + "version": "5.2.1" + } + } + } +''' + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +def generate_hardware_dict(blade): + hw_info = {'fans': {}, + 'controllers': {}, + 'blades': {}, + 'chassis': {}, + 'ethernet': {}, + 'modules': {}, + 'power': {}, + 'switch': {}, + } + components = blade.hardware.list_hardware(filter='type=\'fm\'') + for component in range(0, len(components.items)): + component_name = components.items[component].name + hw_info['modules'][component_name] = {'slot': components.items[component].slot, + 'status': components.items[component].status, + 'serial': components.items[component].serial, + 'model': components.items[component].model + } + components = blade.hardware.list_hardware(filter='type=\'eth\'') + for component in range(0, len(components.items)): + component_name = components.items[component].name + hw_info['ethernet'][component_name] = {'slot': components.items[component].slot, + 'status': components.items[component].status, + 'serial': components.items[component].serial, + 'model': components.items[component].model, + 'speed': components.items[component].speed + } + components = blade.hardware.list_hardware(filter='type=\'fan\'') + for component in range(0, len(components.items)): + component_name = components.items[component].name + hw_info['fans'][component_name] = {'slot': components.items[component].slot, + 'status': components.items[component].status + } + components = blade.hardware.list_hardware(filter='type=\'fb\'') + for component in range(0, len(components.items)): + component_name = components.items[component].name + hw_info['blades'][component_name] = {'slot': components.items[component].slot, + 'status': components.items[component].status, + 'serial': components.items[component].serial, + 'model': components.items[component].model + } + components = blade.hardware.list_hardware(filter='type=\'pwr\'') + for component in range(0, len(components.items)): + component_name = components.items[component].name + hw_info['power'][component_name] = {'slot': components.items[component].slot, + 'status': components.items[component].status, + 'serial': components.items[component].serial, + 'model': components.items[component].model + } + components = blade.hardware.list_hardware(filter='type=\'xfm\'') + for component in range(0, len(components.items)): + component_name = components.items[component].name + hw_info['switch'][component_name] = {'slot': components.items[component].slot, + 'status': components.items[component].status, + 'serial': components.items[component].serial, + 'model': components.items[component].model + } + components = blade.hardware.list_hardware(filter='type=\'ch\'') + for component in range(0, len(components.items)): + component_name = components.items[component].name + hw_info['chassis'][component_name] = {'slot': components.items[component].slot, + 'index': components.items[component].index, + 'status': components.items[component].status, + 'serial': components.items[component].serial, + 'model': components.items[component].model + } + + return hw_info + + +def main(): + argument_spec = purefb_argument_spec() + + module = AnsibleModule(argument_spec, supports_check_mode=True) + blade = get_blade(module) + + module.exit_json(changed=False, purefb_info=generate_hardware_dict(blade)) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_lifecycle.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_lifecycle.py new file mode 100644 index 00000000..1ec3a7d8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_lifecycle.py @@ -0,0 +1,233 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_lifecycle +version_added: '1.4.0' +short_description: Manage FlashBlade object lifecycles +description: +- Manage lifecycles for object buckets +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete lifecycle rule + default: present + type: str + choices: [ absent, present ] + bucket: + description: + - Bucket the lifecycle rule applies to + type: str + required: true + name: + description: + - Name of the lifecycle rule + type: str + required: true + enabled: + description: + - State of lifecycle rule + type: bool + default: True + keep_for: + description: + - Time after which previous versions will be marked expired. + - Enter as days (d) or weeks (w). Range is 1 - 2147483647 days. + type: str + prefix: + description: + - Object key prefix identifying one or more objects in the bucket + type: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create a lifecycle rule called bar for bucket foo + purefb_lifecycle: + name: bar + bucket: foo + keep_for: 2d + prefix: test + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Delete lifecycle rule foo from bucket foo + purefb_lifecycle: + name: foo + bucket: bar + state: absent + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +HAS_PURITYFB = True +try: + from purity_fb import LifecycleRulePost, LifecycleRulePatch, Reference +except ImportError: + HAS_PURITYFB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.10' + + +def _get_bucket(module, blade): + s3bucket = None + buckets = blade.buckets.list_buckets() + for bucket in range(0, len(buckets.items)): + if buckets.items[bucket].name == module.params['bucket']: + s3bucket = buckets.items[bucket] + return s3bucket + + +def _convert_to_millisecs(day): + if day[-1:].lower() == "w": + return int(day[:-1]) * 7 * 86400000 + elif day[-1:].lower() == "d": + return int(day[:-1]) * 86400000 + return 0 + + +def _findstr(text, match): + for line in text.splitlines(): + if match in line: + found = line + return found + + +def delete_rule(module, blade): + """Delete lifecycle rule""" + changed = True + if not module.check_mode: + try: + blade.lifecycle_rules.delete_lifecycle_rules(names=[module.params['bucket'] + '/' + module.params['name']]) + except Exception: + module.fail_json(msg="Failed to delete lifecycle rule {0} for bucket {1}.".format(module.params['name'], + module.params['bucket'])) + module.exit_json(changed=changed) + + +def create_rule(module, blade): + """Create lifecycle policy""" + changed = True + if not module.check_mode: + if not module.params['keep_for']: + module.fail_json(msg="\'keep_for\' is required to create a new lifecycle rule") + if not module.params['keep_for'][-1:].lower() in ['w', 'd']: + module.fail_json(msg="\'keep_for\' format incorrect - specify as \'d\' or \'w\'") + try: + attr = LifecycleRulePost(rule_id=module.params['name'], + keep_previous_version_for=_convert_to_millisecs(module.params['keep_for']), + prefix=module.params['prefix']) + attr.bucket = Reference(name=module.params['bucket']) + blade.lifecycle_rules.create_lifecycle_rules(rule=attr) + if not module.params['enabled']: + attr = LifecycleRulePatch() + attr.enabled = False + blade.lifecycle_rules.update_lifecycle_rules(name=[module.params['bucket'] + '/' + module.params['name']], + rule=attr) + except Exception: + module.fail_json(msg="Failed to create lifecycle rule {0} for bucket {1}.".format(module.params['name'], + module.params['bucket'])) + module.exit_json(changed=changed) + + +def update_rule(module, blade, rule): + """Update snapshot policy""" + changed = True + if not module.check_mode: + changed = False + current_rule = {'prefix': rule.prefix, + 'keep_previous_version_for': rule.keep_previous_version_for, + 'enabled': rule.enabled} + if not module.params['prefix']: + prefix = current_rule['prefix'] + else: + prefix = module.params['prefix'] + if not module.params['keep_for']: + keep_for = current_rule['keep_previous_version_for'] + else: + keep_for = _convert_to_millisecs(module.params['keep_for']) + new_rule = {'prefix': prefix, + 'keep_previous_version_for': keep_for, + 'enabled': module.params['enabled']} + + if current_rule != new_rule: + try: + attr = LifecycleRulePatch(keep_previous_version_for=new_rule['keep_previous_version_for'], + prefix=new_rule['prefix']) + attr.enabled = module.params['enabled'] + blade.lifecycle_rules.update_lifecycle_rules(names=[module.params['bucket'] + '/' + module.params['name']], + rule=attr) + changed = True + except Exception: + module.fail_json(msg='Failed to update lifecycle rule {0} for bucket {1}.'.format(module.params['name'], + module.params['bucket'])) + else: + changed = False + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['absent', 'present']), + enabled=dict(type='bool', default=True), + bucket=dict(type='str', required=True), + name=dict(type='str', required=True), + prefix=dict(type='str',), + keep_for=dict(type='str'), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITYFB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + if not _get_bucket(module, blade): + module.fail_json(msg='Specified bucket {0} does not exist'.format(module.params['bucket'])) + + try: + rule = blade.lifecycle_rules.list_lifecycle_rules(names=[module.params['bucket'] + '/' + module.params['name']]) + except Exception: + rule = None + + if rule and state == 'present': + update_rule(module, blade, rule.items[0]) + elif state == 'present' and not rule: + create_rule(module, blade) + elif state == 'absent' and rule: + delete_rule(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_network.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_network.py new file mode 100644 index 00000000..c60dd4a7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_network.py @@ -0,0 +1,204 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: purefb_network +version_added: "1.0.0" +short_description: Manage network interfaces in a Pure Storage FlashBlade +description: + - This module manages network interfaces on Pure Storage FlashBlade. + - When creating a network interface a subnet must already exist with + a network prefix that covers the IP address of the interface being + created. +author: Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Interface Name. + required: true + type: str + state: + description: + - Create, delete or modifies a network interface. + required: false + default: present + choices: [ "present", "absent" ] + type: str + address: + description: + - IP address of interface. + required: false + type: str + services: + description: + - Define which services are configured for the interfaces. + required: false + choices: [ "data", "replication" ] + default: data + type: str + itype: + description: + - Type of interface. + required: false + choices: [ "vip" ] + default: vip + type: str +extends_documentation_fragment: + - purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = ''' +- name: Create new network interface named foo + purefb_network: + name: foo + address: 10.21.200.23 + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Change IP address of network interface named foo + purefb_network: + name: foo + state: present + address: 10.21.200.123 + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete network interface named foo + purefb_network: + name: foo + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641''' + +RETURN = ''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import NetworkInterface +except ImportError: + HAS_PURITY_FB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MINIMUM_API_VERSION = '1.3' + + +def get_iface(module, blade): + """Return Filesystem or None""" + iface = [] + iface.append(module.params['name']) + try: + res = blade.network_interfaces.list_network_interfaces(names=iface) + return res.items[0] + except Exception: + return None + + +def create_iface(module, blade): + """Create Network Interface""" + changed = True + if not module.check_mode: + iface = [] + services = [] + iface.append(module.params['name']) + services.append(module.params['services']) + try: + blade.network_interfaces.create_network_interfaces(names=iface, + network_interface=NetworkInterface(address=module.params['address'], + services=services, + type=module.params['itype'] + ) + ) + except Exception: + module.fail_json(msg='Interface creation failed. Check subnet exists for {0}'.format(module.params['address'])) + module.exit_json(changed=changed) + + +def modify_iface(module, blade): + """Modify Network Interface IP address""" + changed = True + if not module.check_mode: + changed = False + iface = get_iface(module, blade) + iface_new = [] + iface_new.append(module.params['name']) + if module.params['address'] != iface.address: + try: + blade.network_interfaces.update_network_interfaces(names=iface_new, + network_interface=NetworkInterface(address=module.params['address'])) + changed = True + except Exception: + module.fail_json(msg='Failed to modify Interface {0}'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def delete_iface(module, blade): + """ Delete Network Interface""" + changed = True + if not module.check_mode: + iface = [] + iface.append(module.params['name']) + try: + blade.network_interfaces.delete_network_interfaces(names=iface) + except Exception: + module.fail_json(msg='Failed to delete network {0}'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + name=dict(required=True), + state=dict(default='present', choices=['present', 'absent']), + address=dict(), + services=dict(default='data', choices=['data', 'replication']), + itype=dict(default='vip', choices=['vip']), + ) + ) + + required_if = [["state", "present", ["address"]]] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MINIMUM_API_VERSION not in api_version: + module.fail_json(msg='Upgrade Purity//FB to enable this module') + iface = get_iface(module, blade) + + if state == 'present' and not iface: + create_iface(module, blade) + elif state == 'present' and iface: + modify_iface(module, blade) + elif state == 'absent' and iface: + delete_iface(module, blade) + elif state == 'absent' and not iface: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ntp.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ntp.py new file mode 100644 index 00000000..8f2eb670 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ntp.py @@ -0,0 +1,149 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_ntp +version_added: '1.0.0' +short_description: Configure Pure Storage FlashBlade NTP settings +description: +- Set or erase NTP configuration for Pure Storage FlashBlades. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete NTP servers configuration + type: str + default: present + choices: [ absent, present ] + ntp_servers: + type: list + elements: str + description: + - A list of up to 4 alternate NTP servers. These may include IPv4, + IPv6 or FQDNs. Invalid IP addresses will cause the module to fail. + No validation is performed for FQDNs. + - If more than 4 servers are provided, only the first 4 unique + nameservers will be used. + - if no servers are given a default of I(0.pool.ntp.org) will be used. +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Delete exisitng NTP server entries + purefb_ntp: + state: absent + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Set array NTP servers + purefb_ntp: + state: present + ntp_servers: + - "0.pool.ntp.org" + - "1.pool.ntp.org" + - "2.pool.ntp.org" + - "3.pool.ntp.org" + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import PureArray +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.3' + + +def remove(duplicate): + final_list = [] + for num in duplicate: + if num not in final_list: + final_list.append(num) + return final_list + + +def delete_ntp(module, blade): + """Delete NTP Servers""" + changed = True + if not module.check_mode: + if blade.arrays.list_arrays().items[0].ntp_servers != []: + try: + blade_settings = PureArray(ntp_servers=[]) + blade.arrays.update_arrays(array_settings=blade_settings) + except Exception: + module.fail_json(msg='Deletion of NTP servers failed') + module.exit_json(changed=changed) + + +def create_ntp(module, blade): + """Set NTP Servers""" + changed = True + if not module.check_mode: + if not module.params['ntp_servers']: + module.params['ntp_servers'] = ['0.pool.ntp.org'] + try: + blade_settings = PureArray(ntp_servers=module.params['ntp_servers'][0:4]) + blade.arrays.update_arrays(array_settings=blade_settings) + except Exception: + module.fail_json(msg='Update of NTP servers failed') + module.exit_json(changed=changed) + + +def main(): + + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + ntp_servers=dict(type='list', elements='str'), + state=dict(type='str', default='present', choices=['absent', 'present']), + )) + + required_if = [['state', 'present', ['ntp_servers']]] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + blade = get_blade(module) + + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + + if module.params['state'] == 'absent': + delete_ntp(module, blade) + else: + module.params['ntp_servers'] = remove(module.params['ntp_servers']) + if sorted(blade.arrays.list_arrays().items[0].ntp_servers) != sorted(module.params['ntp_servers'][0:4]): + create_ntp(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_phonehome.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_phonehome.py new file mode 100644 index 00000000..69c8686f --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_phonehome.py @@ -0,0 +1,111 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_phonehome +version_added: '1.0.0' +short_description: Enable or Disable Pure Storage FlashBlade Phone Home +description: +- Enablke or Disable Remote Phone Home for a Pure Storage FlashBlade. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Define state of phone home + type: str + default: present + choices: [ present, absent ] +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Enable Remote Phone Home + purefb_phonehome: + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Disable Remote Phone Home + purefb_phonehome: + state: absent + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Support +except ImportError: + HAS_PURITY_FB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = "1.6" + + +def enable_ph(module, blade): + """Enable Phone Hone""" + changed = True + if not module.check_mode: + ph_settings = Support(phonehome_enabled=True) + try: + blade.support.update_support(support=ph_settings) + except Exception: + module.fail_json(msg='Enabling Phone Home failed') + module.exit_json(changed=changed) + + +def disable_ph(module, blade): + """Disable Phone Home""" + changed = True + if not module.check_mode: + ph_settings = Support(phonehome_enabled=False) + try: + blade.support.update_support(support=ph_settings) + except Exception: + module.fail_json(msg='Disabling Phone Home failed') + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['present', 'absent']), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb SDK is required for this module') + + if module.params['state'] == 'present' and not blade.support.list_support().items[0].phonehome_enabled: + enable_ph(module, blade) + elif module.params['state'] == 'absent' and blade.support.list_support().items[0].phonehome_enabled: + disable_ph(module, blade) + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py new file mode 100644 index 00000000..ae0cc579 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py @@ -0,0 +1,441 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_policy +version_added: '1.0.0' +short_description: Manage FlashBlade policies +description: +- Manage policies for filesystem and file replica links +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete policy + default: present + type: str + choices: [ absent, present ] + name: + description: + - Name of the policy + type: str + enabled: + description: + - State of policy + type: bool + default: True + every: + description: + - Interval between snapshots in seconds + - Range available 300 - 31536000 (equates to 5m to 365d) + type: int + keep_for: + description: + - How long to keep snapshots for + - Range available 300 - 31536000 (equates to 5m to 365d) + - Must not be set less than I(every) + type: int + at: + description: + - Provide a time in 12-hour AM/PM format, eg. 11AM + type: str + timezone: + description: + - Time Zone used for the I(at) parameter + - If not provided, the module will attempt to get the current local timezone from the server + type: str + filesystem: + description: + - List of filesystems to add to a policy on creation + - To amend policy members use the I(purefb_fs) module + type: list + elements: str + replica_link: + description: + - List of filesystem replica links to add to a policy on creation + - To amend policy members use the I(purefb_fs_replica) module + type: list + elements: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create a simple policy with no rules + purefb_policy: + name: test_policy + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Create a policy and connect to existing filesystems and filesystem replica links + purefb_policy: + name: test_policy_with_members + filesystem: + - fs1 + - fs2 + replica_link: + - rl1 + - rl2 + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Create a policy with rules + purefb_policy: + name: test_policy2 + at: 11AM + keep_for: 86400 + every: 86400 + timezone: Asia/Shanghai + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Delete a policy + purefb_policy: + name: test_policy + state: absent + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +HAS_PURITYFB = True +try: + from purity_fb import Policy, PolicyRule, PolicyPatch +except ImportError: + HAS_PURITYFB = False + +HAS_PYTZ = True +try: + import pytz +except ImportError: + HAS_PYTX = False + +import os +import re +import platform + +from ansible.module_utils.common.process import get_bin_path +from ansible.module_utils.facts.utils import get_file_content +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.9' + + +def _convert_to_millisecs(hour): + if hour[-2:] == "AM" and hour[:2] == "12": + return 0 + elif hour[-2:] == "AM": + return int(hour[:-2]) * 3600000 + elif hour[-2:] == "PM" and hour[:2] == "12": + return 43200000 + return (int(hour[:-2]) + 12) * 3600000 + + +def _findstr(text, match): + for line in text.splitlines(): + if match in line: + found = line + return found + + +def _get_local_tz(module, timezone='UTC'): + """ + We will attempt to get the local timezone of the server running the module and use that. + If we can't get the timezone then we will set the default to be UTC + + Linnux has been tested and other opersting systems should be OK. + Failures cause assumption of UTC + + Windows is not supported and will assume UTC + """ + if platform.system() == 'Linux': + timedatectl = get_bin_path('timedatectl') + if timedatectl is not None: + rcode, stdout, stderr = module.run_command(timedatectl) + if rcode == 0 and stdout: + line = _findstr(stdout, 'Time zone') + full_tz = line.split(":", 1)[1].rstrip() + timezone = full_tz.split()[0] + return timezone + else: + module.warn('Incorrect timedatectl output. Timezone will be set to UTC') + else: + if os.path.exists('/etc/timezone'): + timezone = get_file_content('/etc/timezone') + else: + module.warn('Could not find /etc/timezone. Assuming UTC') + + elif platform.system() == 'SunOS': + if os.path.exists('/etc/default/init'): + for line in get_file_content('/etc/default/init', '').splitlines(): + if line.startswith('TZ='): + timezone = line.split('=', 1)[1] + return timezone + else: + module.warn('Could not find /etc/default/init. Assuming UTC') + + elif re.match('^Darwin', platform.platform()): + systemsetup = get_bin_path('systemsetup') + if systemsetup is not None: + rcode, stdout, stderr = module.execute(systemsetup, '-gettimezone') + if rcode == 0 and stdout: + timezone = stdout.split(':', 1)[1].lstrip() + else: + module.warn('Could not run systemsetup. Assuming UTC') + else: + module.warn('Could not find systemsetup. Assuming UTC') + + elif re.match('^(Free|Net|Open)BSD', platform.platform()): + if os.path.exists('/etc/timezone'): + timezone = get_file_content('/etc/timezone') + else: + module.warn('Could not find /etc/timezone. Assuming UTC') + + elif platform.system() == 'AIX': + aix_oslevel = int(platform.version() + platform.release()) + if aix_oslevel >= 61: + if os.path.exists('/etc/environment'): + for line in get_file_content('/etc/environment', '').splitlines(): + if line.startswith('TZ='): + timezone = line.split('=', 1)[1] + return timezone + else: + module.warn('Could not find /etc/environment. Assuming UTC') + else: + module.warn('Cannot determine timezone when AIX os level < 61. Assuming UTC') + + else: + module.warn('Could not find /etc/timezone. Assuming UTC') + + return timezone + + +def delete_policy(module, blade): + """Delete policy""" + changed = True + if not module.check_mode: + try: + blade.policies.delete_policies(names=[module.params['name']]) + except Exception: + module.fail_json(msg="Failed to delete policy {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def create_policy(module, blade): + """Create snapshot policy""" + changed = True + if not module.check_mode: + try: + if module.params['at'] and module.params['every']: + if not module.params['every'] % 86400 == 0: + module.fail_json(msg='At time can only be set if every value is a multiple of 86400') + if not module.params['timezone']: + module.params['timezone'] = _get_local_tz(module) + if module.params['timezone'] not in pytz.all_timezones_set: + module.fail_json(msg='Timezone {0} is not valid'.format(module.params['timezone'])) + if module.params['keep_for'] < module.params['every']: + module.fail_json(msg='Retention period cannot be less than snapshot interval.') + if module.params['at'] and not module.params['timezone']: + module.params['timezone'] = _get_local_tz(module) + if module.params['timezone'] not in set(pytz.all_timezones_set): + module.fail_json(msg='Timezone {0} is not valid'.format(module.params['timezone'])) + + if module.params['keep_for']: + if not 300 <= module.params['keep_for'] <= 34560000: + module.fail_json(msg="keep_for parameter is out of range (300 to 34560000)") + if not 300 <= module.params['every'] <= 34560000: + module.fail_json(msg="every parameter is out of range (300 to 34560000)") + if module.params['at']: + attr = Policy(enabled=module.params['enabled'], rules=[PolicyRule(keep_for=module.params['keep_for'] * 1000, + every=module.params['every'] * 1000, + at=_convert_to_millisecs(module.params['at']), + time_zone=module.params['timezone'])]) + else: + attr = Policy(enabled=module.params['enabled'], rules=[PolicyRule(keep_for=module.params['keep_for'] * 1000, + every=module.params['every'] * 1000)]) + else: + attr = Policy(enabled=module.params['enabled']) + blade.policies.create_policies(names=[module.params['name']], policy=attr) + except Exception: + module.fail_json(msg="Failed to create policy {0}.".format(module.params['name'])) + if module.params['filesystem']: + try: + blade.file_systems.list_file_systems(names=module.params['filesystem']) + blade.policies.create_policy_filesystems(policy_names=[module.params['name']], + member_names=module.params['filesystem']) + except Exception: + blade.policies.delete_policies(names=[module.params['name']]) + module.fail_json(msg="Failed to connect filesystems to policy {0}, " + "or one of {1} doesn't exist.".format(module.params['name'], + module.params['filesystem'])) + if module.params['replica_link']: + for link in module.params['replica_link']: + remote_array = blade.file_system_replica_links.list_file_system_replica_links(local_file_system_names=[link]) + try: + blade.policies.create_policy_file_system_replica_links(policy_names=[module.params['name']], + member_names=[link], + remote_names=[remote_array.items[0].remote.name]) + except Exception: + blade.policies.delete_policies(names=[module.params['name']]) + module.fail_json(msg="Failed to connect filesystem replicsa link {0} to policy {1}. " + "Replica Link {0} does not exist.".format(link, module.params['name'])) + module.exit_json(changed=changed) + + +def update_policy(module, blade, policy): + """Update snapshot policy""" + changed = True + if not module.check_mode: + changed = False + if not policy.rules: + current_policy = {'time_zone': None, + 'every': 0, + 'keep_for': 0, + 'at': 0, + 'enabled': policy.enabled} + else: + if policy.rules[0].keep_for != 0: + policy.rules[0].keep_for = int(policy.rules[0].keep_for / 1000) + if policy.rules[0].every != 0: + policy.rules[0].every = int(policy.rules[0].every / 1000) + + current_policy = {'time_zone': policy.rules[0].time_zone, + 'every': policy.rules[0].every, + 'keep_for': policy.rules[0].keep_for, + 'at': policy.rules[0].at, + 'enabled': policy.enabled} + if not module.params['every']: + every = 0 + else: + every = module.params['every'] + if not module.params['keep_for']: + keep_for = 0 + else: + keep_for = module.params['keep_for'] + if module.params['at']: + at_time = _convert_to_millisecs(module.params['at']) + else: + at_time = None + if not module.params['timezone']: + timezone = _get_local_tz(module) + else: + timezone = module.params['timezone'] + if at_time: + new_policy = {'time_zone': timezone, + 'every': every, + 'keep_for': keep_for, + 'at': at_time, + 'enabled': module.params['enabled']} + else: + new_policy = {'time_zone': None, + 'every': every, + 'keep_for': keep_for, + 'at': None, + 'enabled': module.params['enabled']} + if new_policy['time_zone'] and new_policy['time_zone'] not in pytz.all_timezones_set: + module.fail_json(msg='Timezone {0} is not valid'.format(module.params['timezone'])) + + if current_policy != new_policy: + if not module.params['at']: + module.params['at'] = current_policy['at'] + if not module.params['keep_for']: + module.params['keep_for'] = current_policy['keep_for'] + if not module.params['every']: + module.params['every'] = current_policy['every'] + if module.params['at'] and module.params['every']: + if not module.params['every'] % 86400 == 0: + module.fail_json(msg='At time can only be set if every value is a multiple of 86400') + if module.params['keep_for'] < module.params['every']: + module.fail_json(msg='Retention period cannot be less than snapshot interval.') + if module.params['at'] and not module.params['timezone']: + module.params['timezone'] = _get_local_tz(module) + if module.params['timezone'] not in set(pytz.all_timezones_set): + module.fail_json(msg='Timezone {0} is not valid'.format(module.params['timezone'])) + + try: + attr = PolicyPatch() + attr.enabled = module.params['enabled'] + if at_time: + attr.add_rules = [PolicyRule(keep_for=module.params['keep_for'] * 1000, + every=module.params['every'] * 1000, + at=at_time, + time_zone=timezone)] + else: + attr.add_rules = [PolicyRule(keep_for=module.params['keep_for'] * 1000, + every=module.params['every'] * 1000)] + attr.remove_rules = [PolicyRule(keep_for=current_policy['keep_for'] * 1000, + every=current_policy['every'] * 1000, + at=current_policy['at'], + time_zone=current_policy['time_zone'])] + blade.policies.update_policies(names=[module.params['name']], + policy_patch=attr) + changed = True + except Exception: + module.fail_json(msg='Failed to update policy {0}.'.format(module.params['name'])) + else: + changed = False + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['absent', 'present']), + enabled=dict(type='bool', default=True), + timezone=dict(type='str'), + name=dict(type='str'), + at=dict(type='str'), + every=dict(type='int'), + keep_for=dict(type='int'), + filesystem=dict(type='list', elements='str'), + replica_link=dict(type='list', elements='str'), + )) + + required_together = [['keep_for', 'every']] + + module = AnsibleModule(argument_spec, + required_together=required_together, + supports_check_mode=True) + + if not HAS_PURITYFB: + module.fail_json(msg='purity_fb sdk is required for this module') + if not HAS_PYTZ: + module.fail_json(msg='pytz is required for this module') + + state = module.params['state'] + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + try: + policy = blade.policies.list_policies(names=[module.params['name']]) + except Exception: + policy = None + + if policy and state == 'present': + update_policy(module, blade, policy.items[0]) + elif state == 'present' and not policy: + create_policy(module, blade) + elif state == 'absent' and policy: + delete_policy(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_proxy.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_proxy.py new file mode 100644 index 00000000..afd31810 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_proxy.py @@ -0,0 +1,135 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2019, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_proxy +version_added: '1.0.0' +author: + - Pure Storage ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +short_description: Configure FlashBlade phonehome HTTPs proxy settings +description: +- Set or erase configuration for the HTTPS phonehome proxy settings. +options: + state: + description: + - Set or delete proxy configuration + default: present + type: str + choices: [ absent, present ] + host: + description: + - The proxy host name. + type: str + port: + description: + - The proxy TCP/IP port number. + type: int +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Delete exisitng proxy settings + purefb_proxy: + state: absent + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Set proxy settings + purefb_proxy: + host: purestorage.com + port: 8080 + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Support +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +def delete_proxy(module, blade): + """Delete proxy settings""" + changed = True + if not module.check_mode: + current_proxy = blade.support.list_support().items[0].proxy + if current_proxy != '': + try: + proxy_settings = Support(proxy='') + blade.support.update_support(support=proxy_settings) + except Exception: + module.fail_json(msg='Delete proxy settigs failed') + else: + changed = False + module.exit_json(changed=changed) + + +def create_proxy(module, blade): + """Set proxy settings""" + changed = True + if not module.check_mode: + current_proxy = blade.support.list_support().items[0].proxy + if current_proxy is not None: + new_proxy = "https://" + module.params['host'] + ":" + str(module.params['port']) + if new_proxy != current_proxy: + try: + proxy_settings = Support(proxy=new_proxy) + blade.support.update_support(support=proxy_settings) + except Exception: + module.fail_json(msg='Set phone home proxy failed.') + else: + changed = False + + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['absent', 'present']), + host=dict(type='str'), + port=dict(type='int'), + )) + + required_together = [['host', 'port']] + + module = AnsibleModule(argument_spec, + required_together=required_together, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb SDK is required for this module') + + state = module.params['state'] + blade = get_blade(module) + + if state == 'absent': + delete_proxy(module, blade) + elif state == 'present': + create_proxy(module, blade) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ra.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ra.py new file mode 100644 index 00000000..87287be4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ra.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_ra +version_added: '1.0.0' +short_description: Enable or Disable Pure Storage FlashBlade Remote Assist +description: +- Enablke or Disable Remote Assist for a Pure Storage FlashBlade. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Define state of remote assist + - When set to I(enable) the RA port can be exposed using the + I(debug) module. + type: str + default: present + choices: [ present, absent ] +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Enable Remote Assist port + purefb_ra: + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Disable Remote Assist port + purefb_ra: + state: absent + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Support +except ImportError: + HAS_PURITY_FB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = "1.6" + + +def enable_ra(module, blade): + """Enable Remote Assist""" + changed = True + if not module.check_mode: + ra_settings = Support(remote_assist_active=True) + try: + blade.support.update_support(support=ra_settings) + except Exception: + module.fail_json(msg='Enabling Remote Assist failed') + module.exit_json(changed=changed) + + +def disable_ra(module, blade): + """Disable Remote Assist""" + changed = True + if not module.check_mode: + ra_settings = Support(remote_assist_active=False) + try: + blade.support.update_support(support=ra_settings) + except Exception: + module.fail_json(msg='Disabling Remote Assist failed') + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['present', 'absent']), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb SDK is required for this module') + + if module.params['state'] == 'present' and not blade.support.list_support().items[0].remote_assist_active: + enable_ra(module, blade) + elif module.params['state'] == 'absent' and blade.support.list_support().items[0].remote_assist_active: + disable_ra(module, blade) + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_remote_cred.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_remote_cred.py new file mode 100644 index 00000000..bc0543b7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_remote_cred.py @@ -0,0 +1,202 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_remote_cred +version_added: '1.0.0' +short_description: Create, modify and delete FlashBlade object store remote credentials +description: +- Create, modify and delete object store remote credentials +- You must have a correctly configured remote array or target +- This module is B(not) idempotent when updating existing remote credentials +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Define state of remote credential + default: present + choices: [ absent, present ] + type: str + name: + description: + - The name of the credential + required: true + type: str + access_key: + description: + - Access Key ID of the S3 target + type: str + secret: + description: + - Secret Access Key for the S3 or Azure target + type: str + target: + description: + - Define whether to initialize the S3 bucket + required: true + type: str + +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create remote credential + purefb_remote_cred: + name: cred1 + access_key: "3794fb12c6204e19195f" + secret: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + target: target1 + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete remote credential + purefb_remote_cred: + name: cred1 + target: target1 + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import ObjectStoreRemoteCredentials +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + +MIN_REQUIRED_API_VERSION = '1.9' + + +def get_connected(module, blade): + """Return connected device or None""" + connected_blades = blade.array_connections.list_array_connections() + for target in range(0, len(connected_blades.items)): + if (connected_blades.items[target].remote.name == module.params['target'] or + connected_blades.items[target].management_address == module.params['target']) and \ + connected_blades.items[target].status in ["connected", "connecting", "partially_connected"]: + return connected_blades.items[target].remote.name + connected_targets = blade.targets.list_targets() + for target in range(0, len(connected_targets.items)): + if connected_targets.items[target].name == module.params['target'] and \ + connected_targets.items[target].status in ["connected", "connecting", "partially_connected"]: + return connected_targets.items[target].name + return None + + +def get_remote_cred(module, blade): + """Return Remote Credential or None""" + try: + res = blade.object_store_remote_credentials.list_object_store_remote_credentials(names=[module.params['target'] + '/' + module.params['name']]) + return res.items[0] + except Exception: + return None + + +def create_credential(module, blade): + """Create remote credential""" + changed = True + if not module.check_mode: + remote_cred = module.params['target'] + '/' + module.params['name'] + remote_credentials = ObjectStoreRemoteCredentials(access_key_id=module.params['access_key'], + secret_access_key=module.params['secret']) + try: + blade.object_store_remote_credentials.create_object_store_remote_credentials(names=[remote_cred], + remote_credentials=remote_credentials) + except Exception: + module.fail_json(msg='Failed to create remote credential {0}'.format(remote_cred)) + module.exit_json(changed=changed) + + +def update_credential(module, blade): + """Update remote credential""" + changed = True + if not module.check_mode: + remote_cred = module.params['target'] + '/' + module.params['name'] + new_attr = ObjectStoreRemoteCredentials(access_key_id=module.params['access_key'], + secret_access_key=module.params['secret']) + try: + blade.object_store_remote_credentials.update_object_store_remote_credentials(names=[remote_cred], + remote_credentials=new_attr) + except Exception: + module.fail_json(msg='Failed to update remote credential {0}'.format(remote_cred)) + module.exit_json(changed=changed) + + +def delete_credential(module, blade): + """Delete remote credential""" + changed = True + if not module.check_mode: + remote_cred = module.params['target'] + '/' + module.params['name'] + try: + blade.object_store_remote_credentials.delete_object_store_remote_credentials(names=[remote_cred]) + except Exception: + module.fail_json(msg='Failed to delete remote credential {0}.'.format(remote_cred)) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['present', 'absent']), + name=dict(type='str', required=True), + access_key=dict(type='str'), + secret=dict(type='str', no_log=True), + target=dict(type='str', required=True), + )) + + required_if = [['state', 'present', ['access_key', 'secret']]] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg='FlashBlade REST version not supported. ' + 'Minimum version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + target = get_connected(module, blade) + + if not target: + module.fail_json(msg='Selected target {0} is not connected.'.format(module.params['target'])) + + remote_cred = get_remote_cred(module, blade) + + if module.params['state'] == 'present' and not remote_cred: + create_credential(module, blade) + elif module.params['state'] == 'present': + update_credential(module, blade) + elif module.params['state'] == 'absent' and remote_cred: + delete_credential(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3acc.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3acc.py new file mode 100644 index 00000000..f98dfa7f --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3acc.py @@ -0,0 +1,140 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_s3acc +version_added: '1.0.0' +short_description: Create or delete FlashBlade Object Store accounts +description: +- Create or delete object store accounts on a Pure Stoage FlashBlade. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete object store account + default: present + choices: [ absent, present ] + type: str + name: + description: + - The name of object store account + type: str + required: true +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Crrate object store account foo + purefb_s3acc: + name: foo + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Delete object store account foo + purefb_s3acc: + name: foo + state: absent + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.3' + + +def get_s3acc(module, blade): + """Return Object Store Account or None""" + s3acc = None + accts = blade.object_store_accounts.list_object_store_accounts() + for acct in range(0, len(accts.items)): + if accts.items[acct].name == module.params['name']: + s3acc = accts.items[acct] + return s3acc + + +def update_s3acc(module, blade): + """Update Object Store Account""" + changed = True + if not module.check_mode: + changed = False + module.exit_json(changed=changed) + + +def create_s3acc(module, blade): + """Create Object Store Account""" + changed = True + if not module.check_mode: + try: + blade.object_store_accounts.create_object_store_accounts(names=[module.params['name']]) + except Exception: + module.fail_json(msg='Object Store Account {0}: Creation failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def delete_s3acc(module, blade): + """Delete Object Store Account""" + changed = True + if not module.check_mode: + count = len(blade.object_store_users.list_object_store_users(filter='name=\'' + module.params['name'] + '/*\'').items) + if count != 0: + module.fail_json(msg='Remove all Users from Object Store Account {0} \ + before deletion'.format(module.params['name'])) + else: + try: + blade.object_store_accounts.delete_object_store_accounts(names=[module.params['name']]) + except Exception: + module.fail_json(msg='Object Store Account {0}: Deletion failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + name=dict(required=True, type='str'), + state=dict(default='present', choices=['present', 'absent']), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + state = module.params['state'] + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + s3acc = get_s3acc(module, blade) + + if state == 'absent' and s3acc: + delete_s3acc(module, blade) + elif state == 'present' and s3acc: + update_s3acc(module, blade) + elif not s3acc and state == 'present': + create_s3acc(module, blade) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3user.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3user.py new file mode 100644 index 00000000..b5298538 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3user.py @@ -0,0 +1,274 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_s3user +version_added: '1.0.0' +short_description: Create or delete FlashBlade Object Store account users +description: +- Create or delete object store account users on a Pure Stoage FlashBlade. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete object store account user + default: present + choices: [ absent, present ] + type: str + name: + description: + - The name of object store user + type: str + required: true + account: + description: + - The name of object store account associated with user + type: str + required: true + access_key: + description: + - Create secret access key. + - Key can be exposed using the I(debug) module + - If enabled this will override I(imported_key) + type: bool + default: false + imported_key: + description: + - Access key of imported credentials + type: str + version_added: "1.4.0" + imported_secret: + description: + - Access key secret for access key to import + type: str + version_added: "1.4.0" +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create object store user (with access ID and key) foo in account bar + purefb_s3user: + name: foo + account: bar + access_key: true + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + register: result + +- debug: + msg: "S3 User: {{ result['s3user_info'] }}" + +- name: Create object store user foo using imported key/secret in account bar + purefb_s3user: + name: foo + account: bar + imported_key: "PSABSSZRHPMEDKHMAAJPJBONPJGGDDAOFABDGLBJLHO" + imported_secret: "BAG61F63105e0d3669/e066+5C5DFBE2c127d395LBGG" + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Delete object store user foo in account bar + purefb_s3user: + name: foo + account: bar + state: absent + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 +''' + +RETURN = r''' +''' + + +HAS_PURITY_FB = True +try: + from purity_fb import ObjectStoreAccessKey, ObjectStoreAccessKeyPost +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.3' +IMPORT_KEY_API_VERSION = '1.10' + + +def get_s3acc(module, blade): + """Return Object Store Account or None""" + s3acc = None + accts = blade.object_store_accounts.list_object_store_accounts() + for acct in range(0, len(accts.items)): + if accts.items[acct].name == module.params['account']: + s3acc = accts.items[acct] + return s3acc + + +def get_s3user(module, blade): + """Return Object Store Account or None""" + full_user = module.params['account'] + "/" + module.params['name'] + s3user = None + s3users = blade.object_store_users.list_object_store_users() + for user in range(0, len(s3users.items)): + if s3users.items[user].name == full_user: + s3user = s3users.items[user] + return s3user + + +def update_s3user(module, blade): + """Update Object Store User""" + changed = True + if not module.check_mode: + changed = False + exists = False + s3user_facts = {} + user = module.params['account'] + "/" + module.params['name'] + if module.params['access_key'] or module.params['imported_key']: + key_count = 0 + keys = blade.object_store_access_keys.list_object_store_access_keys() + for key in range(0, len(keys.items)): + if module.params['imported_key']: + versions = blade.api_version.list_versions().versions + if IMPORT_KEY_API_VERSION in versions: + if keys.items[key].name == module.params['imported_key']: + module.warn('Imported key provided already belongs to a user') + exists = True + if keys.items[key].user.name == user: + key_count += 1 + if not exists: + if key_count < 2: + try: + if module.params['access_key'] and module.params['imported_key']: + module.warn('\'access_key: true\' overrides imported keys') + if module.params['access_key']: + result = blade.object_store_access_keys.create_object_store_access_keys( + object_store_access_key=ObjectStoreAccessKey(user={'name': user})) + changed = True + s3user_facts['fb_s3user'] = {'user': user, + 'access_key': result.items[0].secret_access_key, + 'access_id': result.items[0].name} + else: + if IMPORT_KEY_API_VERSION in versions: + blade.object_store_access_keys.create_object_store_access_keys( + names=[module.params['imported_key']], + object_store_access_key=ObjectStoreAccessKeyPost( + user={'name': user}, secret_access_key=module.params['imported_secret'])) + changed = True + except Exception: + if module.params['imported_key']: + module.fail_json(msg='Object Store User {0}: Access Key import failed'.format(user)) + else: + module.fail_json(msg='Object Store User {0}: Access Key creation failed'.format(user)) + else: + module.warn('Object Store User {0}: Maximum Access Key count reached'.format(user)) + module.exit_json(changed=changed, s3user_info=s3user_facts) + + +def create_s3user(module, blade): + """Create Object Store Account""" + s3user_facts = {} + changed = True + if not module.check_mode: + user = module.params['account'] + "/" + module.params['name'] + try: + blade.object_store_users.create_object_store_users(names=[user]) + if module.params['access_key'] and module.params['imported_key']: + module.warn('\'access_key: true\' overrides imported keys') + if module.params['access_key']: + try: + result = blade.object_store_access_keys.create_object_store_access_keys( + object_store_access_key=ObjectStoreAccessKey(user={'name': user})) + s3user_facts['fb_s3user'] = {'user': user, + 'access_key': result.items[0].secret_access_key, + 'access_id': result.items[0].name} + except Exception: + delete_s3user(module, blade) + module.fail_json(msg='Object Store User {0}: Creation failed'.format(user)) + else: + if module.params['imported_key']: + versions = blade.api_version.list_versions().versions + if IMPORT_KEY_API_VERSION in versions: + try: + blade.object_store_access_keys.create_object_store_access_keys( + names=[module.params['imported_key']], + object_store_access_key=ObjectStoreAccessKeyPost( + user={'name': user}, secret_access_key=module.params['imported_secret'])) + except Exception: + delete_s3user(module, blade) + module.fail_json(msg='Object Store User {0}: Creation failed with imported access key'.format(user)) + except Exception: + module.fail_json(msg='Object Store User {0}: Creation failed'.format(user)) + module.exit_json(changed=changed, s3user_info=s3user_facts) + + +def delete_s3user(module, blade): + """Delete Object Store Account""" + changed = True + if not module.check_mode: + user = module.params['account'] + "/" + module.params['name'] + try: + blade.object_store_users.delete_object_store_users(names=[user]) + except Exception: + module.fail_json(msg='Object Store Account {0}: Deletion failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + name=dict(required=True, type='str'), + account=dict(required=True, type='str'), + access_key=dict(default='false', type='bool'), + imported_key=dict(type='str'), + imported_secret=dict(type='str', no_log=True), + state=dict(default='present', choices=['present', 'absent']), + )) + + required_together = [['imported_key', 'imported_secret']] + + module = AnsibleModule(argument_spec, + required_together=required_together, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + versions = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in versions: + module.fail_json(msg='Minimum FlashBlade REST version required: {0}'.format(MIN_REQUIRED_API_VERSION)) + + s3acc = get_s3acc(module, blade) + if not s3acc: + module.fail_json(msg='Object Store Account {0} does not exist'.format(module.params['account'])) + + s3user = get_s3user(module, blade) + + if state == 'absent' and s3user: + delete_s3user(module, blade) + elif state == 'present' and s3user: + update_s3user(module, blade) + elif not s3user and state == 'present': + create_s3user(module, blade) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_smtp.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_smtp.py new file mode 100644 index 00000000..f3347f26 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_smtp.py @@ -0,0 +1,114 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_smtp +version_added: '1.0.0' +short_description: Configure SMTP for Pure Storage FlashBlade +description: +- Configure SMTP for a Pure Storage FlashBlade. +- Whilst there can be no relay host, a sender domain must be configured. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + host: + description: + - Relay server name + type: str + domain: + description: + - Domain name for alert messages + required: true + type: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Configure SMTP settings + purefb_smtp: + host: hostname + domain: xyz.com + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Smtp +except ImportError: + HAS_PURITY_FB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = "1.6" + + +def set_smtp(module, blade): + """Configure SMTP settings""" + changed = True + if not module.check_mode: + current_smtp = blade.smtp.list_smtp().items[0] + if module.params['host'] and module.params['host'] != current_smtp.relay_host: + smtp_settings = Smtp(relay_host=module.params['host']) + try: + blade.smtp.update_smtp(smtp_settings=smtp_settings) + except Exception: + module.fail_json(msg='Configuring SMTP relay host failed') + elif current_smtp.relay_host and not module.params['host']: + smtp_settings = Smtp(relay_host='') + try: + blade.smtp.update_smtp(smtp_settings=smtp_settings) + except Exception: + module.fail_json(msg='Configuring SMTP relay host failed') + if module.params['domain'] != current_smtp.sender_domain: + smtp_settings = Smtp(sender_domain=module.params['domain']) + try: + blade.smtp.update_smtp(smtp_settings=smtp_settings) + except Exception: + module.fail_json(msg='Configuring SMTP sender domain failed') + else: + changed = False + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + host=dict(type='str'), + domain=dict(type='str', required=True), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb SDK is required for this module') + + set_smtp(module, blade) + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snap.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snap.py new file mode 100644 index 00000000..6039a387 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snap.py @@ -0,0 +1,274 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_snap +version_added: '1.0.0' +short_description: Manage filesystem snapshots on Pure Storage FlashBlades +description: +- Create or delete volumes and filesystem snapshots on Pure Storage FlashBlades. +- Restoring a filesystem from a snapshot is only supported using + the latest snapshot. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - The name of the source filesystem. + required: true + type: str + suffix: + description: + - Suffix of snapshot name. + type: str + state: + description: + - Define whether the filesystem snapshot should exist or not. + choices: [ absent, present, restore ] + default: present + type: str + eradicate: + description: + - Define whether to eradicate the snapshot on delete or leave in trash. + type: bool + default: 'no' +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create snapshot foo.ansible + purefb_snap: + name: foo + suffix: ansible + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + state: present + +- name: Delete snapshot named foo.snap + purefb_snap: + name: foo + suffix: snap + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + state: absent + +- name: Recover deleted snapshot foo.ansible + purefb_snap: + name: foo + suffix: ansible + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + state: present + +- name: Restore filesystem foo (uses latest snapshot) + purefb_snap: + name: foo + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + state: restore + +- name: Eradicate snapshot named foo.snap + purefb_snap: + name: foo + suffix: snap + eradicate: true + fb_url: 10.10.10.2 + api_token: e31060a7-21fc-e277-6240-25983c6c4592 + state: absent +''' + +RETURN = r''' +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + +from datetime import datetime + +HAS_PURITY_FB = True +try: + from purity_fb import FileSystemSnapshot, SnapshotSuffix, FileSystem, Reference +except ImportError: + HAS_PURITY_FB = False + + +def get_fs(module, blade): + """Return Filesystem or None""" + filesystem = [] + filesystem.append(module.params['name']) + try: + res = blade.file_systems.list_file_systems(names=filesystem) + return res.items[0] + except Exception: + return None + + +def get_latest_fssnapshot(module, blade): + """ Get the name of the latest snpshot or None""" + try: + filt = 'source=\'' + module.params['name'] + '\'' + all_snaps = blade.file_system_snapshots.list_file_system_snapshots(filter=filt) + if not all_snaps.items[0].destroyed: + return all_snaps.items[0].name + else: + module.fail_json(msg='Latest snapshot {0} is destroyed.' + ' Eradicate or recover this first.'.format(all_snaps.items[0].name)) + except Exception: + return None + + +def get_fssnapshot(module, blade): + """Return Snapshot or None""" + try: + filt = 'source=\'' + module.params['name'] + '\' and suffix=\'' + module.params['suffix'] + '\'' + res = blade.file_system_snapshots.list_file_system_snapshots(filter=filt) + return res.items[0] + except Exception: + return None + + +def create_snapshot(module, blade): + """Create Snapshot""" + if not module.check_mode: + source = [] + source.append(module.params['name']) + try: + blade.file_system_snapshots.create_file_system_snapshots(sources=source, + suffix=SnapshotSuffix(module.params['suffix'])) + changed = True + except Exception: + changed = False + module.exit_json(changed=changed) + + +def restore_snapshot(module, blade): + """Restore a filesystem back from the latest snapshot""" + if not module.check_mode: + snapname = get_latest_fssnapshot(module, blade) + if snapname is not None: + fs_attr = FileSystem(name=module.params['name'], + source=Reference(name=snapname)) + try: + blade.file_systems.create_file_systems(overwrite=True, + discard_non_snapshotted_data=True, + file_system=fs_attr) + changed = True + except Exception: + changed = False + else: + module.fail_json(msg='Filesystem {0} has no snapshots to restore from.'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def recover_snapshot(module, blade): + """Recover deleted Snapshot""" + if not module.check_mode: + snapname = module.params['name'] + "." + module.params['suffix'] + new_attr = FileSystemSnapshot(destroyed=False) + try: + blade.file_system_snapshots.update_file_system_snapshots(name=snapname, attributes=new_attr) + changed = True + except Exception: + changed = False + module.exit_json(changed=changed) + + +def update_snapshot(module, blade): + """Update Snapshot""" + changed = False + module.exit_json(changed=changed) + + +def delete_snapshot(module, blade): + """ Delete Snapshot""" + if not module.check_mode: + snapname = module.params['name'] + "." + module.params['suffix'] + new_attr = FileSystemSnapshot(destroyed=True) + try: + blade.file_system_snapshots.update_file_system_snapshots(name=snapname, attributes=new_attr) + changed = True + if module.params['eradicate']: + try: + blade.file_system_snapshots.delete_file_system_snapshots(name=snapname) + changed = True + except Exception: + changed = False + except Exception: + changed = False + module.exit_json(changed=changed) + + +def eradicate_snapshot(module, blade): + """ Eradicate Snapshot""" + if not module.check_mode: + snapname = module.params['name'] + "." + module.params['suffix'] + try: + blade.file_system_snapshots.delete_file_system_snapshots(name=snapname) + changed = True + except Exception: + changed = False + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + name=dict(required=True), + suffix=dict(type='str'), + eradicate=dict(default='false', type='bool'), + state=dict(default='present', choices=['present', 'absent', 'restore']) + ) + ) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + if module.params['suffix'] is None: + suffix = "snap-" + str((datetime.utcnow() - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds()) + module.params['suffix'] = suffix.replace(".", "") + + state = module.params['state'] + blade = get_blade(module) + filesystem = get_fs(module, blade) + snap = get_fssnapshot(module, blade) + + if state == 'present' and filesystem and not filesystem.destroyed and not snap: + create_snapshot(module, blade) + elif state == 'present' and filesystem and not filesystem.destroyed and snap and not snap.destroyed: + update_snapshot(module, blade) + elif state == 'present' and filesystem and not filesystem.destroyed and snap and snap.destroyed: + recover_snapshot(module, blade) + elif state == 'present' and filesystem and filesystem.destroyed: + update_snapshot(module, blade) + elif state == 'present' and not filesystem: + update_snapshot(module, blade) + elif state == 'restore' and filesystem: + restore_snapshot(module, blade) + elif state == 'absent' and snap and not snap.destroyed: + delete_snapshot(module, blade) + elif state == 'absent' and snap and snap.destroyed: + eradicate_snapshot(module, blade) + elif state == 'absent' and not snap: + module.exit_json(changed=False) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snmp_agent.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snmp_agent.py new file mode 100644 index 00000000..71872b32 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snmp_agent.py @@ -0,0 +1,191 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_snmp_agent +version_added: '1.0.0' +short_description: Configure the FlashBlade SNMP Agent +description: +- Configure the management SNMP Agent on a Pure Storage FlashBlade. +- This module is not idempotent and will always modify the + existing management SNMP agent due to hidden parameters that cannot + be compared to the play parameters. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + auth_passphrase: + type: str + description: + - SNMPv3 only. Passphrase of 8 - 32 characters. + auth_protocol: + type: str + description: + - SNMP v3 only. Hash algorithm to use + choices: [ MD5, SHA ] + community: + type: str + description: + - SNMP v2c only. Manager community ID. Between 1 and 32 characters long. + user: + type: str + description: + - SNMP v3 only. User ID recognized by the specified SNMP agent. + Must be between 1 and 32 characters. + version: + type: str + description: + - Version of SNMP protocol to use for the agent. + choices: [ v2c, v3 ] + privacy_passphrase: + type: str + description: + - SNMPv3 only. Passphrase to encrypt SNMP messages. + Must be between 8 and 63 non-space ASCII characters. + privacy_protocol: + type: str + description: + - SNMP v3 only. Encryption protocol to use + choices: [ AES, DES ] +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Update v2c SNMP agent + purefb_snmp_agent: + community: public + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Update v3 SNMP agent + purefb_snmp_agent: + version: v3 + auth_protocol: MD5 + auth_passphrase: password + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + + +HAS_PURITY_FB = True +try: + from purity_fb import SnmpAgent, SnmpV2c, SnmpV3 +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = "1.9" + + +def update_agent(module, blade): + """Update SNMP Agent""" + changed = True + if not module.check_mode: + changed = False + try: + agent = blade.snmp_agents.list_snmp_agents() + except Exception: + module.fail_json(msg="Failed to get configuration for SNMP agent.") + current_attr = {'community': agent.items[0].v2c.community, + 'version': agent.items[0].version, + 'auth_passphrase': agent.items[0].v3.auth_passphrase, + 'auth_protocol': agent.items[0].v3.auth_protocol, + 'privacy_passphrase': agent.items[0].v3.privacy_passphrase, + 'privacy_protocol': agent.items[0].v3.privacy_protocol, + 'user': agent.items[0].v3.user, + } + new_attr = {'community': module.params['community'], + 'version': module.params['version'], + 'auth_passphrase': module.params['auth_passphrase'], + 'auth_protocol': module.params['auth_protocol'], + 'privacy_passphrase': module.params['privacy_passphrase'], + 'privacy_protocol': module.params['privacy_protocol'], + 'user': module.params['user'] + } + if current_attr != new_attr: + if new_attr['version'] == 'v2c': + updated_v2c_attrs = SnmpV2c(community=new_attr['community']) + updated_v2c_agent = SnmpAgent(version='v2c', v2c=updated_v2c_attrs) + try: + blade.snmp_agents.update_snmp_agents(snmp_agent=updated_v2c_agent) + changed = True + except Exception: + module.fail_json(msg="Failed to update v2c SNMP agent.") + else: + updated_v3_attrs = SnmpV3(auth_protocol=new_attr['auth_protocol'], + auth_passphrase=new_attr['auth_passphrase'], + privacy_protocol=new_attr['privacy_protocol'], + privacy_passphrase=new_attr['privacy_passphrase'], + user=new_attr['user'] + ) + updated_v3_agent = SnmpAgent(version='v3', v3=updated_v3_attrs) + try: + blade.snmp_agents.update_snmp_agents(snmp_agent=updated_v3_agent) + changed = True + except Exception: + module.fail_json(msg="Failed to update v3 SNMP agent.") + + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + user=dict(type='str'), + auth_passphrase=dict(type='str', no_log=True), + auth_protocol=dict(type='str', choices=['MD5', 'SHA']), + privacy_passphrase=dict(type='str', no_log=True), + privacy_protocol=dict(type='str', choices=['AES', 'DES']), + version=dict(type='str', choices=['v2c', 'v3']), + community=dict(type='str'), + )) + + required_together = [['auth_passphrase', 'auth_protocol'], + ['privacy_passphrase', 'privacy_protocol']] + required_if = [['version', 'v2c', ['community']], + ['version', 'v3', ['user']]] + + module = AnsibleModule(argument_spec, + required_together=required_together, + required_if=required_if, + supports_check_mode=True) + + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb SDK is required for this module') + + if module.params['version'] == "v3": + if module.params['auth_passphrase'] and (8 > len(module.params['auth_passphrase']) > 32): + module.fail_json(msg="auth_password must be between 8 and 32 characters") + if module.params['privacy_passphrase'] and 8 > len(module.params['privacy_passphrase']) > 63: + module.fail_json(msg="privacy_password must be between 8 and 63 characters") + + update_agent(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snmp_mgr.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snmp_mgr.py new file mode 100644 index 00000000..61e47cc4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_snmp_mgr.py @@ -0,0 +1,302 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_snmp_mgr +version_added: '1.0.0' +short_description: Configure FlashBlade SNMP Managers +description: +- Manage SNMP managers on a Pure Storage FlashBlade. +- This module is not idempotent and will always modify an + existing SNMP manager due to hidden parameters that cannot + be compared to the play parameters. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Name of SNMP Manager + required: True + type: str + state: + description: + - Create or delete SNMP manager + type: str + default: present + choices: [ absent, present ] + auth_passphrase: + type: str + description: + - SNMPv3 only. Passphrase of 8 - 32 characters. + auth_protocol: + type: str + description: + - SNMP v3 only. Hash algorithm to use + choices: [ MD5, SHA ] + community: + type: str + description: + - SNMP v2c only. Manager community ID. Between 1 and 32 characters long. + host: + type: str + description: + - IPv4 or IPv6 address or FQDN to send trap messages to. + user: + type: str + description: + - SNMP v3 only. User ID recognized by the specified SNMP manager. + Must be between 1 and 32 characters. + version: + type: str + description: + - Version of SNMP protocol to use for the manager. + choices: [ v2c, v3 ] + notification: + type: str + description: + - Action to perform on event. + default: trap + choices: [ inform, trap ] + privacy_passphrase: + type: str + description: + - SNMPv3 only. Passphrase to encrypt SNMP messages. + Must be between 8 and 63 non-space ASCII characters. + privacy_protocol: + type: str + description: + - SNMP v3 only. Encryption protocol to use + choices: [ AES, DES ] +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Delete exisitng SNMP manager + purefb_snmp_mgr: + name: manager1 + state: absent + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Create v2c SNMP manager + purefb_snmp_mgr: + name: manager1 + community: public + host: 10.21.22.23 + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Create v3 SNMP manager + purefb_snmp_mgr: + name: manager2 + version: v3 + auth_protocol: MD5 + auth_passphrase: password + host: 10.21.22.23 + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Update existing SNMP manager + purefb_snmp_mgr: + name: manager1 + community: private + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + + +HAS_PURITY_FB = True +try: + from purity_fb import SnmpManager, SnmpV2c, SnmpV3 +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = "1.9" + + +def update_manager(module, blade): + """Update SNMP Manager""" + changed = True + if not module.check_mode: + changed = False + try: + mgr = blade.snmp_managers.list_snmp_managers(names=[module.params['name']]) + except Exception: + module.fail_json(msg="Failed to get configuration for SNMP manager {0}.".format(module.params['name'])) + current_attr = {'community': mgr.items[0].v2c.community, + 'notification': mgr.items[0].notification, + 'host': mgr.items[0].host, + 'version': mgr.items[0].version, + 'auth_passphrase': mgr.items[0].v3.auth_passphrase, + 'auth_protocol': mgr.items[0].v3.auth_protocol, + 'privacy_passphrase': mgr.items[0].v3.privacy_passphrase, + 'privacy_protocol': mgr.items[0].v3.privacy_protocol, + 'user': mgr.items[0].v3.user, + } + new_attr = {'community': module.params['community'], + 'notification': module.params['notification'], + 'host': module.params['host'], + 'version': module.params['version'], + 'auth_passphrase': module.params['auth_passphrase'], + 'auth_protocol': module.params['auth_protocol'], + 'privacy_passphrase': module.params['privacy_passphrase'], + 'privacy_protocol': module.params['privacy_protocol'], + 'user': module.params['user'] + } + if current_attr != new_attr: + if new_attr['version'] == 'v2c': + updated_v2c_attrs = SnmpV2c(community=new_attr['community']) + updated_v2c_manager = SnmpManager(host=new_attr['host'], notification=new_attr['notification'], + version='v2c', v2c=updated_v2c_attrs + ) + try: + blade.snmp_managers.update_snmp_managers(names=[module.params['name']], + snmp_manager=updated_v2c_manager + ) + changed = True + except Exception: + module.fail_json(msg="Failed to update v2c SNMP manager {0}.".format(module.params['name'])) + else: + updated_v3_attrs = SnmpV3(auth_protocol=new_attr['auth_protocol'], + auth_passphrase=new_attr['auth_passphrase'], + privacy_protocol=new_attr['privacy_protocol'], + privacy_passphrase=new_attr['privacy_passphrase'], + user=new_attr['user'] + ) + updated_v3_manager = SnmpManager(host=new_attr['host'], notification=new_attr['notification'], + version='v3', v3=updated_v3_attrs + ) + try: + blade.snmp_managers.update_snmp_managers(names=[module.params['name']], + snmp_manager=updated_v3_manager + ) + changed = True + except Exception: + module.fail_json(msg="Failed to update v3 SNMP manager {0}.".format(module.params['name'])) + + module.exit_json(changed=changed) + + +def delete_manager(module, blade): + """Delete SNMP Manager""" + changed = True + if not module.check_mode: + try: + blade.snmp_managers.delete_snmp_managers(names=[module.params['name']]) + except Exception: + module.fail_json(msg='Delete SNMP manager {0} failed'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def create_manager(module, blade): + """Create SNMP Manager""" + changed = True + if not module.check_mode: + if not module.params['version']: + module.fail_json(msg='SNMP version required to create a new manager') + if module.params['version'] == "v2c": + v2_attrs = SnmpV2c(community=module.params['community']) + new_v2_manager = SnmpManager(host=module.params['host'], notification=module.params['notification'], + version='v2c', v2c=v2_attrs + ) + try: + blade.snmp_managers.create_snmp_managers(names=[module.params['name']], + snmp_manager=new_v2_manager + ) + except Exception: + module.fail_json(msg="Failed to create v2c SNMP manager {0}.".format(module.params['name'])) + else: + v3_attrs = SnmpV3(auth_protocol=module.params['auth_protocol'], + auth_passphrase=module.params['auth_passphrase'], + privacy_protocol=module.params['privacy_protocol'], + privacy_passphrase=module.params['privacy_passphrase'], + user=module.params['user'] + ) + new_v3_manager = SnmpManager(host=module.params['host'], notification=module.params['notification'], + version='v3', v3=v3_attrs + ) + try: + blade.snmp_managers.create_snmp_managers(names=[module.params['name']], + snmp_manager=new_v3_manager + ) + except Exception: + module.fail_json(msg="Failed to create v3 SNMP manager {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + name=dict(type='str', required=True), + host=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present']), + user=dict(type='str'), + notification=dict(type='str', choices=['inform', 'trap'], default='trap'), + auth_passphrase=dict(type='str', no_log=True), + auth_protocol=dict(type='str', choices=['MD5', 'SHA']), + privacy_passphrase=dict(type='str', no_log=True), + privacy_protocol=dict(type='str', choices=['AES', 'DES']), + version=dict(type='str', choices=['v2c', 'v3']), + community=dict(type='str'), + )) + + required_together = [['auth_passphrase', 'auth_protocol'], + ['privacy_passphrase', 'privacy_protocol']] + required_if = [['version', 'v2c', ['community', 'host']], + ['version', 'v3', ['host', 'user']]] + + module = AnsibleModule(argument_spec, + required_together=required_together, + required_if=required_if, + supports_check_mode=True) + + state = module.params['state'] + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb SDK is required for this module') + + mgr_configured = False + mgrs = blade.snmp_managers.list_snmp_managers() + for mgr in range(0, len(mgrs.items)): + if mgrs.items[mgr].name == module.params['name']: + mgr_configured = True + break + if module.params['version'] == "v3": + if module.params['auth_passphrase'] and (8 > len(module.params['auth_passphrase']) > 32): + module.fail_json(msg="auth_password must be between 8 and 32 characters") + if module.params['privacy_passphrase'] and 8 > len(module.params['privacy_passphrase']) > 63: + module.fail_json(msg="privacy_password must be between 8 and 63 characters") + if state == 'absent' and mgr_configured: + delete_manager(module, blade) + elif mgr_configured and state == 'present': + update_manager(module, blade) + elif not mgr_configured and state == 'present': + create_manager(module, blade) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_subnet.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_subnet.py new file mode 100644 index 00000000..5cb27b74 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_subnet.py @@ -0,0 +1,263 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: purefb_subnet +version_added: "1.0.0" +short_description: Manage network subnets in a Pure Storage FlashBlade +description: + - This module manages network subnets on Pure Storage FlashBlade. +author: Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Subnet Name. + required: true + type: str + state: + description: + - Create, delete or modifies a subnet. + required: false + default: present + choices: [ "present", "absent" ] + type: str + gateway: + description: + - IPv4 or IPv6 address of subnet gateway. + required: false + type: str + mtu: + description: + - MTU size of the subnet. Range is 1280 to 9216. + required: false + default: 1500 + type: int + prefix: + description: + - IPv4 or IPv6 address associated with the subnet. + - Supply the prefix length (CIDR) as well as the IP address. + required: false + type: str + vlan: + description: + - VLAN ID of the subnet. + required: false + default: 0 + type: int +extends_documentation_fragment: + - purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = ''' +- name: Create new network subnet named foo + purefb_subnet: + name: foo + prefix: "10.21.200.3/24" + gateway: 10.21.200.1 + mtu: 9000 + vlan: 2200 + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Change configuration of existing subnet foo + purefb_network: + name: foo + state: present + prefix: "10.21.100.3/24" + gateway: 10.21.100.1 + mtu: 1500 + address: 10.21.200.123 + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete network subnet named foo + purefb_subnet: + name: foo + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641''' + +RETURN = ''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Subnet +except ImportError: + HAS_PURITY_FB = False + +try: + import netaddr + HAS_NETADDR = True +except ImportError: + HAS_NETADDR = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MINIMUM_API_VERSION = '1.3' + + +def get_subnet(module, blade): + """Return Subnet or None""" + subnet = [] + subnet.append(module.params['name']) + try: + res = blade.subnets.list_subnets(names=subnet) + return res.items[0] + except Exception: + return None + + +def create_subnet(module, blade): + """Create Subnet""" + changed = True + if not module.check_mode: + subnet = [] + subnet.append(module.params['name']) + try: + blade.subnets.create_subnets(names=subnet, + subnet=Subnet(prefix=module.params['prefix'], + vlan=module.params['vlan'], + mtu=module.params['mtu'], + gateway=module.params['gateway'] + ) + ) + except Exception: + module.fail_json(msg='Failed to create subnet {0}. Confirm supplied parameters'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def modify_subnet(module, blade): + """Modify Subnet settings""" + changed = True + if not module.check_mode: + changed = False + subnet = get_subnet(module, blade) + subnet_new = [] + subnet_new.append(module.params['name']) + if module.params['prefix']: + if module.params['prefix'] != subnet.prefix: + try: + blade.subnets.update_subnets(names=subnet_new, + subnet=Subnet(prefix=module.params['prefix'])) + changed = True + except Exception: + module.fail_json(msg='Failed to change subnet {0} prefix to {1}'.format(module.params['name'], + module.params['prefix'])) + if module.params['vlan']: + if module.params['vlan'] != subnet.vlan: + try: + blade.subnets.update_subnets(names=subnet_new, + subnet=Subnet(vlan=module.params['vlan'])) + changed = True + except Exception: + module.fail_json(msg='Failed to change subnet {0} VLAN to {1}'.format(module.params['name'], + module.params['vlan'])) + if module.params['gateway']: + if module.params['gateway'] != subnet.gateway: + try: + blade.subnets.update_subnets(names=subnet_new, + subnet=Subnet(gateway=module.params['gateway'])) + changed = True + except Exception: + module.fail_json(msg='Failed to change subnet {0} gateway to {1}'.format(module.params['name'], + module.params['gateway'])) + if module.params['mtu']: + if module.params['mtu'] != subnet.mtu: + try: + blade.subnets.update_subnets(names=subnet_new, + subnet=Subnet(mtu=module.params['mtu'])) + changed = True + except Exception: + module.fail_json(msg='Failed to change subnet {0} MTU to {1}'.format(module.params['name'], + module.params['mtu'])) + module.exit_json(changed=changed) + + +def delete_subnet(module, blade): + """ Delete Subnet""" + changed = True + if not module.check_mode: + subnet = [] + subnet.append(module.params['name']) + try: + blade.subnets.delete_subnets(names=subnet) + except Exception: + module.fail_json(msg='Failed to delete subnet {0}'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + name=dict(required=True), + state=dict(default='present', choices=['present', 'absent']), + gateway=dict(), + mtu=dict(type='int', default=1500), + prefix=dict(), + vlan=dict(type='int', default=0), + ) + ) + + required_if = [["state", "present", ["gateway", 'prefix']]] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + if not HAS_NETADDR: + module.fail_json(msg='netaddr module is required') + + state = module.params['state'] + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MINIMUM_API_VERSION not in api_version: + module.fail_json(msg='Upgrade Purity//FB to enable this module') + subnet = get_subnet(module, blade) + if state == 'present': + if not (1280 <= module.params['mtu'] <= 9216): + module.fail_json(msg='MTU {0} is out of range (1280 to 9216)'.format(module.params['mtu'])) + if not (0 <= module.params['vlan'] <= 4094): + module.fail_json(msg='VLAN ID {0} is out of range (0 to 4094)'.format(module.params['vlan'])) + if netaddr.IPAddress(module.params['gateway']) not in netaddr.IPNetwork(module.params['prefix']): + module.fail_json(msg='Gateway and subnet are not compatible.') + subnets = blade.subnets.list_subnets() + nrange = netaddr.IPSet([module.params['prefix']]) + for sub in range(0, len(subnets.items)): + if subnets.items[sub].vlan == module.params['vlan'] and subnets.items[sub].name != module.params['name']: + module.fail_json(msg='VLAN ID {0} is already in use.'.format(module.params['vlan'])) + if nrange & netaddr.IPSet([subnets.items[sub].prefix]) and subnets.items[sub].name != module.params['name']: + module.fail_json(msg='Prefix CIDR overlaps with existing subnet.') + + if state == 'present' and not subnet: + create_subnet(module, blade) + elif state == 'present' and subnet: + modify_subnet(module, blade) + elif state == 'absent' and subnet: + delete_subnet(module, blade) + elif state == 'absent' and not subnet: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_syslog.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_syslog.py new file mode 100644 index 00000000..f0f5a638 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_syslog.py @@ -0,0 +1,180 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_syslog +version_added: '1.4.0' +short_description: Configure Pure Storage FlashBlade syslog settings +description: +- Configure syslog configuration for Pure Storage FlashBlades. +- Add or delete an individual syslog server to the existing + list of serves. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Unique identifier for the syslog server address + type: str + required: true + state: + description: + - Create or delete syslog servers configuration + default: present + type: str + choices: [ absent, present ] + protocol: + description: + - Protocol which server uses + type: str + choices: [ tcp, tls, udp ] + port: + description: + - Port at which the server is listening. If no port is specified + the system will use 514 + type: str + address: + description: + - Syslog server address. + This field supports IPv4 or FQDN. + An invalid IP addresses will cause the module to fail. + No validation is performed for FQDNs. + type: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Delete exisitng syslog server entries + purefb_syslog: + name: syslog1 + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Set array syslog servers + purefb_syslog: + state: present + name: syslog1 + address: syslog1.com + protocol: udp + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 +''' + +RETURN = r''' +''' + + +HAS_PURITY_FB = True +try: + from purity_fb import SyslogServerPostOrPatch +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MIN_REQUIRED_API_VERSION = '1.10' + + +def delete_syslog(module, blade): + """Delete Syslog Server""" + changed = True + if not module.check_mode: + changed = False + try: + server = blade.syslog.list_syslog_servers(names=[module.params['name']]) + except Exception: + server = None + + if server: + try: + blade.syslog.delete_syslog_servers(names=[module.params['name']]) + changed = True + except Exception: + module.fail_json(msg='Failed to remove syslog server: {0}'.format(module.params['name'])) + + module.exit_json(changed=changed) + + +def add_syslog(module, blade): + """Add Syslog Server""" + changed = True + if not module.check_mode: + changed = False + noport_address = module.params['protocol'] + "://" + module.params['address'] + + if module.params['port']: + full_address = noport_address + ":" + module.params['port'] + else: + full_address = noport_address + + address_list = blade.syslog.list_syslog_servers() + if len(address_list.items) == 3: + module.fail_json(msg='Maximum number of syslog servers (3) already configured.') + exists = False + + if address_list: + for address in range(0, len(address_list.items)): + if address_list.items[address].name == module.params['name']: + exists = True + break + if not exists: + try: + attr = SyslogServerPostOrPatch(uri=full_address) + blade.syslog.create_syslog_servers(syslog=attr, names=[module.params['name']]) + changed = True + except Exception: + module.fail_json(msg='Failed to add syslog server {0} - {1}'.format(module.params['name'], full_address)) + + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + address=dict(type='str'), + protocol=dict(type='str', choices=['tcp', 'tls', 'udp']), + port=dict(type='str'), + name=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['absent', 'present']), + )) + + required_if = [['state', 'present', ['address', 'protocol']]] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + + if module.params['state'] == 'absent': + delete_syslog(module, blade) + else: + add_syslog(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_target.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_target.py new file mode 100644 index 00000000..7c087b2d --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_target.py @@ -0,0 +1,174 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2020, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_target +version_added: '1.0.0' +short_description: Manage remote S3-capable targets for a FlashBlade +description: +- Manage remote S3-capable targets for a FlashBlade system +- Use this for non-FlashBlade targets. +- Use I(purefb_connect) for FlashBlade targets. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + state: + description: + - Create or delete remote target + default: present + type: str + choices: [ absent, present ] + name: + description: + - Name of S3-capable target (IP or FQDN) + type: str + required: true + address: + description: + - Address of S3-capable target (IP or FQDN) + type: str +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Create a connection to remote S3-capable target + purefb_target: + name: target_1 + address: 10.10.10.20 + fb_url: 10.10.10.2 + api_token: T-89faa581-c668-483d-b77d-23c5d88ba35c +- name: Delete connection to remote S3-capable system + purefb_target: + state: absent + name: target_1 + target_api: 9c0b56bc-f941-f7a6-9f85-dcc3e9a8f7d6 + fb_url: 10.10.10.2 + api_token: T-89faa581-c668-483d-b77d-23c5d88ba35c +''' + +RETURN = r''' +''' + +HAS_PURITYFB = True +try: + from purity_fb import TargetPost, Target +except ImportError: + HAS_PURITYFB = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + + +MINIMUM_API_VERSION = '1.9' + + +def _check_replication_configured(module, blade): + interfaces = blade.network_interfaces.list_network_interfaces() + repl_ok = False + for link in range(0, len(interfaces.items)): + if 'replication' in interfaces.items[link].services: + repl_ok = True + if not repl_ok: + module.fail_json(msg='Replication network interface required to configure a target') + + +def _check_connected(module, blade): + connected_targets = blade.targets.list_targets() + for target in range(0, len(connected_targets.items)): + if connected_targets.items[target].name == module.params['name']: + return connected_targets.items[target] + return None + + +def break_connection(module, blade): + """Break connection to remote target""" + changed = True + if not module.check_mode: + try: + blade.targets.delete_targets(names=[module.params['name']]) + except Exception: + module.fail_json(msg="Failed to disconnect target {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def create_connection(module, blade): + """Create connection to remote target""" + changed = True + if not module.check_mode: + connected_targets = blade.targets.list_targets() + for target in range(0, len(connected_targets.items)): + if connected_targets.items[target].address == module.params['address']: + module.fail_json(msg='Target already exists with same connection address') + try: + target = TargetPost(address=module.params['address']) + blade.targets.create_targets(names=[module.params['name']], target=target) + except Exception: + module.fail_json(msg="Failed to connect to remote target {0}.".format(module.params['name'])) + module.exit_json(changed=changed) + + +def update_connection(module, blade, connection): + """Update target connection address""" + changed = True + if not module.check_mode: + connected_targets = blade.targets.list_targets() + for target in range(0, len(connected_targets.items)): + if connected_targets.items[target].address == module.params['address'] and \ + connected_targets.items[target].name != module.params['name']: + module.fail_json(msg='Target already exists with same connection address') + if module.params['address'] != connection.address: + new_address = Target(name=module.params['name'], address=module.params['address']) + try: + blade.targets.update_targets(names=[connection.name], target=new_address) + except Exception: + module.fail_json(msg='Failed to change address for target {0}.'.format(module.params['name'])) + else: + changed = False + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + state=dict(type='str', default='present', choices=['absent', 'present']), + name=dict(type='str', required=True), + address=dict(type='str'), + )) + + required_if = [['state', 'present', ['address']]] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURITYFB: + module.fail_json(msg='purity_fb sdk is required for this module') + + state = module.params['state'] + blade = get_blade(module) + _check_replication_configured(module, blade) + target = _check_connected(module, blade) + if state == 'present' and not target: + create_connection(module, blade) + elif state == 'present' and target: + update_connection(module, blade, target) + elif state == 'absent' and target: + break_connection(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_user.py b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_user.py new file mode 100644 index 00000000..66909ef9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/plugins/modules/purefb_user.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2019, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: purefb_user +version_added: '1.0.0' +short_description: Modify FlashBlade local user account password +description: +- Modify local user's password on a Pure Stoage FlashBlade. +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - The name of the local user account + type: str + default: pureuser + password: + description: + - Password for the local user. + type: str + required: true + old_password: + description: + - If changing an existing password, you must provide the old password for security + type: str + required: true +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +''' + +EXAMPLES = r''' +- name: Change password for local user (NOT IDEMPOTENT) + purefb_user: + password: anewpassword + old_password: apassword + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +''' + +RETURN = r''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Admin +except ImportError: + HAS_PURITY_FB = False + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import get_blade, purefb_argument_spec + +MIN_REQUIRED_API_VERSION = '1.3' + + +def update_user(module, blade): + """Create or Update Local User Account""" + changed = True + if not module.check_mode: + if module.params['password']: + if module.params['password'] != module.params['old_password']: + try: + newAdmin = Admin() + newAdmin.password = module.params['password'] + newAdmin.old_password = module.params['old_password'] + blade.admins.update_admins(names=[module.params['name']], admin=newAdmin) + except Exception: + module.fail_json(msg='Local User {0}: Password reset failed. ' + 'Check passwords. One of these is incorrect.'.format(module.params['name'])) + else: + module.fail_json(msg='Local User Account {0}: Password change failed - ' + 'Old and new passwords are the same'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update(dict( + name=dict(type='str', default='pureuser'), + password=dict(required=True, type='str', no_log=True), + old_password=dict(required=True, type='str', no_log=True), + )) + + module = AnsibleModule(argument_spec, + supports_check_mode=True) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json(msg="Purity//FB must be upgraded to support this module.") + + update_user(module, blade) + + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/roles/.keep b/collections-debian-merged/ansible_collections/purestorage/flashblade/roles/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/roles/.keep diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.10.txt b/collections-debian-merged/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.10.txt new file mode 100644 index 00000000..771db46e --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.10.txt @@ -0,0 +1,2 @@ +plugins/modules/purefb_info.py validate-modules:return-syntax-error +plugins/modules/purefb_inventory.py validate-modules:return-syntax-error diff --git a/collections-debian-merged/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.9.txt b/collections-debian-merged/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.9.txt new file mode 100644 index 00000000..771db46e --- /dev/null +++ b/collections-debian-merged/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.9.txt @@ -0,0 +1,2 @@ +plugins/modules/purefb_info.py validate-modules:return-syntax-error +plugins/modules/purefb_inventory.py validate-modules:return-syntax-error |