From 7a06a632a5cf5ba1ad4aacd279572815fe9cbb7b Mon Sep 17 00:00:00 2001 From: Ansible Core Team Date: Mon, 9 Mar 2020 09:40:37 +0000 Subject: Migrated to purestorage.flasharray --- .../modules/storage/purestorage/purefa_alert.py | 172 ---- .../storage/purestorage/purefa_arrayname.py | 90 -- .../modules/storage/purestorage/purefa_banner.py | 116 --- .../modules/storage/purestorage/purefa_connect.py | 155 --- .../modules/storage/purestorage/purefa_dns.py | 133 --- .../modules/storage/purestorage/purefa_ds.py | 326 ------- .../modules/storage/purestorage/purefa_dsrole.py | 164 ---- .../modules/storage/purestorage/purefa_hg.py | 263 ----- .../modules/storage/purestorage/purefa_host.py | 476 --------- .../modules/storage/purestorage/purefa_info.py | 1019 -------------------- .../modules/storage/purestorage/purefa_ntp.py | 128 --- .../modules/storage/purestorage/purefa_offload.py | 270 ------ .../modules/storage/purestorage/purefa_pg.py | 489 ---------- .../modules/storage/purestorage/purefa_pgsnap.py | 294 ------ .../storage/purestorage/purefa_phonehome.py | 99 -- .../modules/storage/purestorage/purefa_ra.py | 113 --- .../modules/storage/purestorage/purefa_smtp.py | 150 --- .../modules/storage/purestorage/purefa_snap.py | 238 ----- .../modules/storage/purestorage/purefa_snmp.py | 333 ------- .../modules/storage/purestorage/purefa_syslog.py | 158 --- .../modules/storage/purestorage/purefa_user.py | 222 ----- .../modules/storage/purestorage/purefa_vg.py | 189 ---- .../modules/storage/purestorage/purefa_volume.py | 498 ---------- test/sanity/ignore.txt | 34 - 24 files changed, 6129 deletions(-) delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_alert.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_arrayname.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_banner.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_connect.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_dns.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_ds.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_dsrole.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_hg.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_host.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_info.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_ntp.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_offload.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_pg.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_pgsnap.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_phonehome.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_ra.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_smtp.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_snap.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_snmp.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_syslog.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_user.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_vg.py delete mode 100644 lib/ansible/modules/storage/purestorage/purefa_volume.py diff --git a/lib/ansible/modules/storage/purestorage/purefa_alert.py b/lib/ansible/modules/storage/purestorage/purefa_alert.py deleted file mode 100644 index 23c683a4b6..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_alert.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/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: purefa_alert -version_added: '2.9' -short_description: Configure Pure Storage FlashArray 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: -- Simon Dodsley (@sdodsley) -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 -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Add new email recipient and enable, or enable existing email - purefa_alert: - address: "user@domain.com" - enabled: true - state: present - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Delete existing email recipient - purefa_alert: - state: absent - address: "user@domain.com" - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def create_alert(module, array): - """Create Alert Email""" - changed = False - if not module.check_mode: - try: - array.create_alert_recipient(module.params['address']) - changed = True - except Exception: - module.fail_json(msg='Failed to create alert email: {0}'.format(module.params['address'])) - - if not module.params['enabled']: - try: - array.disable_alert_recipient(module.params['address']) - except Exception: - module.fail_json(msg='Failed to create alert email: {0}'.format(module.params['address'])) - changed = True - - module.exit_json(changed=changed) - - -def enable_alert(module, array): - """Enable Alert Email""" - changed = False - if not module.check_mode: - try: - array.enable_alert_recipient(module.params['address']) - except Exception: - module.fail_json(msg='Failed to enable alert email: {0}'.format(module.params['address'])) - changed = True - - module.exit_json(changed=changed) - - -def disable_alert(module, array): - """Disable Alert Email""" - changed = False - if not module.check_mode: - try: - array.disable_alert_recipient(module.params['address']) - except Exception: - module.fail_json(msg='Failed to disable alert email: {0}'.format(module.params['address'])) - changed = True - - module.exit_json(changed=changed) - - -def delete_alert(module, array): - """Delete Alert Email""" - changed = False - if module.params['address'] == "flasharray-alerts@purestorage.com": - module.fail_json(msg='Built-in address {0} cannot be deleted.'.format(module.params['address'])) - if not module.check_mode: - try: - array.delete_alert_recipient(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 = purefa_argument_spec() - argument_spec.update(dict( - address=dict(type='str', required=True), - enabled=dict(type='bool', default=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - )) - - module = AnsibleModule(argument_spec, - supports_check_mode=True) - - 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.') - - array = get_system(module) - - exists = False - try: - emails = array.list_alert_recipients() - except Exception: - module.fail_json(msg='Failed to get existing email list') - for email in range(0, len(emails)): - if emails[email]['name'] == module.params['address']: - exists = True - enabled = emails[email]['enabled'] - break - if module.params['state'] == 'present' and not exists: - create_alert(module, array) - elif module.params['state'] == 'present' and exists and not enabled and module.params['enabled']: - enable_alert(module, array) - elif module.params['state'] == 'present' and exists and enabled and not module.params['enabled']: - disable_alert(module, array) - elif module.params['state'] == 'absent' and exists: - delete_alert(module, array) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_arrayname.py b/lib/ansible/modules/storage/purestorage/purefa_arrayname.py deleted file mode 100644 index fe765d4f4f..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_arrayname.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/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: purefa_arrayname -version_added: '2.9' -short_description: Configure Pure Storage FlashArray array name -description: -- Configure name of array for Pure Storage FlashArrays. -- Ideal for Day 0 initial configuration. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: Set the array name - type: str - default: present - choices: [ present ] - name: - description: - - Name of the array. Must conform to correct naming schema. - type: str - required: true -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Set new array name - purefa_arrayname: - name: new-array-name - state: present - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def update_name(module, array): - """Change array name""" - changed = False - - try: - array.set(name=module.params['name']) - changed = True - 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 = purefa_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=False) - - array = get_system(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='Array name {0} does not conform to array name rules. See documentation.'.format(module.params['name'])) - if module.params['name'] != array.get()['array_name']: - update_name(module, array) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_banner.py b/lib/ansible/modules/storage/purestorage/purefa_banner.py deleted file mode 100644 index ca89d1fb5a..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_banner.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/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: purefa_banner -version_added: '2.9' -short_description: Configure Pure Storage FlashArray GUI and SSH MOTD message -description: -- Configure MOTD for Pure Storage FlashArrays. -- This will be shown during an SSH or GUI login to the array. -- Multiple line messages can be achieved using \\n. -author: -- Pure Storage Ansible Team (@sdodsley) -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.fa -''' - -EXAMPLES = r''' -- name: Set new banner text - purefa_banner: - banner: "Banner over\ntwo lines" - state: present - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Delete banner text - purefa_banner: - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def set_banner(module, array): - """Set MOTD banner text""" - changed = False - - try: - if not module.params['banner']: - module.fail_json(msg='Invalid MOTD banner given') - - if not module.check_mode: - array.set(banner=module.params['banner']) - changed = True - except Exception: - module.fail_json(msg='Failed to set MOTD banner text') - - module.exit_json(changed=changed) - - -def delete_banner(module, array): - """Delete MOTD banner text""" - changed = False - try: - array.set(banner="") - changed = True - except Exception: - module.fail_json(msg='Failed to delete current MOTD banner text') - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_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=False) - - state = module.params['state'] - array = get_system(module) - current_banner = array.get(banner=True)['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, array) - # clear banner if it has a value - elif state == 'absent' and current_banner: - delete_banner(module, array) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_connect.py b/lib/ansible/modules/storage/purestorage/purefa_connect.py deleted file mode 100644 index 80976a4650..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_connect.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/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: purefa_connect -version_added: '2.9' -short_description: Manage replication connections between two FlashArrays -description: -- Manage array connections to specified target array -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Create or delete array connection - default: present - type: str - choices: [ absent, present ] - target_url: - description: - - Management IP address of remote array. - type: str - required: true - target_api: - description: - - API token for target array - type: str - connection: - description: Type of connection between arrays. - type: str - choices: [ sync, async ] - default: async -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create an async connection to remote array - purefa_connect: - target_url: 10.10.10.20 - target_api: - connection: async - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Delete connection to remote array - purefa_connect: - state: absent - target_url: 10.10.10.20 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -''' - -RETURN = r''' -''' - -HAS_PURESTORAGE = True -try: - from purestorage import FlashArray -except ImportError: - HAS_PURESTORAGE = False - -import platform -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def _check_connected(module, array): - connected_arrays = array.list_array_connections() - for target in range(0, len(connected_arrays)): - if connected_arrays[target]['management_address'] == module.params['target_url'] and \ - connected_arrays[target]['connected']: - return connected_arrays[target] - return None - - -def break_connection(module, array, target_array): - """Break connection between arrays""" - changed = True - if not module.check_mode: - source_array = array.get()['array_name'] - try: - array.disconnect_array(target_array['array_name']) - except Exception: - module.fail_json(msg="Failed to disconnect {0} from {1}.".format(target_array['array_name'], source_array)) - module.exit_json(changed=changed) - - -def create_connection(module, array): - """Create connection between arrays""" - changed = True - if not module.check_mode: - remote_array = module.params['target_url'] - user_agent = '%(base)s %(class)s/%(version)s (%(platform)s)' % { - 'base': 'Ansible', - 'class': __name__, - 'version': 1.2, - 'platform': platform.platform() - } - try: - remote_system = FlashArray(module.params['target_url'], - api_token=module.params['target_api'], - user_agent=user_agent) - connection_key = remote_system.get(connection_key=True)['connection_key'] - remote_array = remote_system.get()['array_name'] - array.connect_array(module.params['target_url'], connection_key, [module.params['connection']]) - except Exception: - module.fail_json(msg="Failed to connect to remote array {0}.".format(remote_array)) - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - state=dict(type='str', default='present', choices=['absent', 'present']), - connection=dict(type='str', default='async', choices=['async', 'sync']), - target_url=dict(type='str', required=True), - target_api=dict(type='str'), - )) - - required_if = [('state', 'present', ['target_api'])] - - module = AnsibleModule(argument_spec, - required_if=required_if, - supports_check_mode=True) - - if not HAS_PURESTORAGE: - module.fail_json(msg='purestorage sdk is required for this module') - - state = module.params['state'] - array = get_system(module) - target_array = _check_connected(module, array) - - if state == 'present' and target_array is None: - create_connection(module, array) - elif state == 'absent'and target_array is not None: - break_connection(module, array, target_array) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_dns.py b/lib/ansible/modules/storage/purestorage/purefa_dns.py deleted file mode 100644 index c8cf215100..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_dns.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/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: purefa_dns -version_added: '2.8' -short_description: Configure FlashArray DNS settings -description: -- Set or erase configuration for the DNS settings. -- Nameservers provided will overwrite any existing nameservers. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Set or delete directory service configuration - default: present - type: str - choices: [ absent, present ] - domain: - description: - - Domain suffix to be appended when performing 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 -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Delete existing DNS settings - purefa_dns: - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Set DNS settings - purefa_dns: - domain: purestorage.com - nameservers: - - 8.8.8.8 - - 8.8.4.4 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_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, array): - """Delete DNS settings""" - changed = False - current_dns = array.get_dns() - if current_dns['domain'] == '' and current_dns['nameservers'] == ['']: - module.exit_json(changed=changed) - else: - try: - array.set_dns(domain='', nameservers=[]) - changed = True - except Exception: - module.fail_json(msg='Delete DNS settings failed') - module.exit_json(changed=changed) - - -def create_dns(module, array): - """Set DNS settings""" - changed = False - current_dns = array.get_dns() - if current_dns['domain'] != module.params['domain'] or sorted(module.params['nameservers']) != sorted(current_dns['nameservers']): - try: - array.set_dns(domain=module.params['domain'], - nameservers=module.params['nameservers'][0:3]) - changed = True - except Exception: - module.fail_json(msg='Set DNS settings failed: Check configuration') - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - state=dict(type='str', default='present', choices=['absent', 'present']), - domain=dict(type='str'), - nameservers=dict(type='list'), - )) - - required_if = [('state', 'present', ['domain', 'nameservers'])] - - module = AnsibleModule(argument_spec, - required_if=required_if, - supports_check_mode=False) - - state = module.params['state'] - array = get_system(module) - - if state == 'absent': - delete_dns(module, array) - elif state == 'present': - module.params['nameservers'] = remove(module.params['nameservers']) - create_dns(module, array) - else: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_ds.py b/lib/ansible/modules/storage/purestorage/purefa_ds.py deleted file mode 100644 index 361dc200b5..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_ds.py +++ /dev/null @@ -1,326 +0,0 @@ -#!/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: purefa_ds -version_added: '2.6' -short_description: Configure FlashArray Directory Service -description: -- Set or erase configuration for the directory service. There is no facility - to SSL certificates at this time. Use the FlashArray GUI for this - additional configuration work. -- To modify an existing directory service configuration you must first delete - an existing configuration and then recreate with new settings. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - type: str - description: - - Create or delete directory service configuration - default: present - choices: [ absent, present ] - enable: - description: - - Whether to enable or disable directory service support. - default: false - type: bool - uri: - type: list - 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. - base_dn: - type: str - 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. - required: true - bind_password: - type: str - description: - - Sets the password of the bind_user user name account. - bind_user: - type: str - 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. - group_base: - type: str - description: - - Specifies where the configured groups are 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. - - Not Supported from Purity 5.2.0 or higher. Use I(purefa_dsrole) module. - ro_group: - type: str - description: - - Sets the common Name (CN) of the configured directory service group - containing users with read-only privileges on the FlashArray. This - name should be just the Common Name of the group without the CN= - specifier. Common Names should not exceed 64 characters in length. - - Not Supported from Purity 5.2.0 or higher. Use I(purefa_dsrole) module. - sa_group: - type: str - description: - - Sets the common Name (CN) of the configured directory service group - containing administrators with storage-related privileges on the - FlashArray. This name should be just the Common Name of the group - without the CN= specifier. Common Names should not exceed 64 - characters in length. - - Not Supported from Purity 5.2.0 or higher. Use I(purefa_dsrole) module. - aa_group: - type: str - description: - - Sets the common Name (CN) of the directory service group containing - administrators with full privileges when managing the FlashArray. - The name should be just the Common Name of the group without the - CN= specifier. Common Names should not exceed 64 characters in length. - - Not Supported from Purity 5.2.0 or higher. Use I(purefa_dsrole) module. -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Delete existing directory service - purefa_ds: - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create directory service (disabled) - Pre-5.2.0 - purefa_ds: - uri: "ldap://lab.purestorage.com" - base_dn: "DC=lab,DC=purestorage,DC=com" - bind_user: Administrator - bind_password: password - group_base: "OU=Pure-Admin" - ro_group: PureReadOnly - sa_group: PureStorage - aa_group: PureAdmin - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create directory service (disabled) - 5.2.0 or higher - purefa_ds: - uri: "ldap://lab.purestorage.com" - base_dn: "DC=lab,DC=purestorage,DC=com" - bind_user: Administrator - bind_password: password - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Enable existing directory service - purefa_ds: - enable: true - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Disable existing directory service - purefa_ds: - enable: false - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create directory service (enabled) - Pre-5.2.0 - purefa_ds: - enable: true - uri: "ldap://lab.purestorage.com" - base_dn: "DC=lab,DC=purestorage,DC=com" - bind_user: Administrator - bind_password: password - group_base: "OU=Pure-Admin" - ro_group: PureReadOnly - sa_group: PureStorage - aa_group: PureAdmin - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create directory service (enabled) - 5.2.0 or higher - purefa_ds: - enable: true - uri: "ldap://lab.purestorage.com" - base_dn: "DC=lab,DC=purestorage,DC=com" - bind_user: Administrator - bind_password: password - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -DS_ROLE_REQUIRED_API_VERSION = '1.16' - - -def update_ds(module, array): - """Update Directory Service""" - changed = False - module.exit_json(changed=changed) - - -def enable_ds(module, array): - """Enable Directory Service""" - changed = False - try: - array.enable_directory_service() - changed = True - except Exception: - module.fail_json(msg='Enable Directory Service failed: Check Configuration') - module.exit_json(changed=changed) - - -def disable_ds(module, array): - """Disable Directory Service""" - """Disable Directory Service""" - changed = False - try: - array.disable_directory_service() - changed = True - except Exception: - module.fail_json(msg='Disable Directory Service failed') - module.exit_json(changed=changed) - - -def delete_ds(module, array): - """Delete Directory Service""" - changed = False - try: - api_version = array._list_available_rest_versions() - array.set_directory_service(enabled=False) - if DS_ROLE_REQUIRED_API_VERSION in api_version: - array.set_directory_service(uri=[''], - base_dn="", - bind_user="", - bind_password="", - certificate="") - changed = True - else: - array.set_directory_service(uri=[''], - base_dn="", - group_base="", - bind_user="", - bind_password="", - readonly_group="", - storage_admin_group="", - array_admin_group="", - certificate="") - changed = True - except Exception: - module.fail_json(msg='Delete Directory Service failed') - module.exit_json(changed=changed) - - -def create_ds(module, array): - """Create Directory Service""" - changed = False - api_version = array._list_available_rest_versions() - if DS_ROLE_REQUIRED_API_VERSION in api_version: - if not module.params['role']: - module.fail_json(msg='At least one role must be configured') - try: - array.set_directory_service(uri=module.params['uri'], - base_dn=module.params['base_dn'], - bind_user=module.params['bind_user'], - bind_password=module.params['bind_password']) - array.set_directory_service(enabled=module.params['enable']) - changed = True - except Exception: - module.fail_json(msg='Create Directory Service failed: Check configuration') - else: - groups_rule = [not module.params['ro_group'], - not module.params['sa_group'], - not module.params['aa_group']] - - if all(groups_rule): - module.fail_json(msg='At least one group must be configured') - try: - array.set_directory_service(uri=module.params['uri'], - base_dn=module.params['base_dn'], - group_base=module.params['group_base'], - bind_user=module.params['bind_user'], - bind_password=module.params['bind_password'], - readonly_group=module.params['ro_group'], - storage_admin_group=module.params['sa_group'], - array_admin_group=module.params['aa_group']) - array.set_directory_service(enabled=module.params['enable']) - changed = True - except Exception: - module.fail_json(msg='Create Directory Service failed: Check configuration') - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - uri=dict(type='list'), - 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'), - group_base=dict(type='str'), - ro_group=dict(type='str'), - sa_group=dict(type='str'), - aa_group=dict(type='str'), - )) - - required_together = [['uri', 'bind_password', 'bind_user', - 'base_dn', 'group_base']] - - module = AnsibleModule(argument_spec, - required_together=required_together, - supports_check_mode=False) - - state = module.params['state'] - array = get_system(module) - ds_exists = False - dirserv = array.get_directory_service() - ds_enabled = dirserv['enabled'] - if dirserv['base_dn']: - ds_exists = True - - if state == 'absent' and ds_exists: - delete_ds(module, array) - elif ds_exists and module.params['enable'] and ds_enabled: - update_ds(module, array) - elif ds_exists and not module.params['enable'] and ds_enabled: - disable_ds(module, array) - elif ds_exists and module.params['enable'] and not ds_enabled: - enable_ds(module, array) - elif not ds_exists and state == 'present': - create_ds(module, array) - else: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_dsrole.py b/lib/ansible/modules/storage/purestorage/purefa_dsrole.py deleted file mode 100644 index cbc35223c6..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_dsrole.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/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: purefa_dsrole -version_added: '2.8' -short_description: Configure FlashArray Directory Service Roles -description: -- Set or erase directory services role configurations. -- Only available for FlashArray running Purity 5.2.0 or higher -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Create or delete directory service role - type: str - default: present - choices: [ absent, present ] - role: - description: - - The directory service role to work on - choices: [ array_admin, ops_admin, readonly, storage_admin ] - group_base: - type: str - 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. - group: - type: str - 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. -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Delete existing array_admin directory service role - purefa_dsrole: - role: array_admin - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create array_admin directory service role - purefa_dsrole: - role: array_admin - group_base: "OU=PureGroups,OU=SANManagers" - group: pureadmins - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Update ops_admin directory service role - purefa_dsrole: - role: ops_admin - group_base: "OU=PureGroups" - group: opsgroup - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def update_role(module, array): - """Update Directory Service Role""" - changed = False - role = array.list_directory_services_roles(names=[module.params['role']]) - if role['group_base'] != module.params['group_base'] or role['group'] != module.params['group']: - try: - array.set_directory_services_roles(names=[module.params['role']], - group_base=module.params['group_base'], - group=module.params['group']) - changed = True - 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, array): - """Delete Directory Service Role""" - changed = False - try: - array.set_directory_services_roles(names=[module.params['role']], - group_base='', - group='') - changed = True - 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, array): - """Create Directory Service Role""" - changed = False - try: - array.set_directory_services_roles(names=[module.params['role']], - group_base=module.params['group_base'], - group=module.params['group']) - changed = True - except Exception: - module.fail_json(msg='Create Directory Service Role {0} failed: Check configuration'.format(module.params['role'])) - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_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=False) - - state = module.params['state'] - array = get_system(module) - role_configured = False - role = array.list_directory_services_roles(names=[module.params['role']]) - if role['group'] is not None: - role_configured = True - - if state == 'absent' and role_configured: - delete_role(module, array) - elif role_configured and state == 'present': - update_role(module, array) - elif not role_configured and state == 'present': - create_role(module, array) - else: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_hg.py b/lib/ansible/modules/storage/purestorage/purefa_hg.py deleted file mode 100644 index 0614d66872..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_hg.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/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: purefa_hg -version_added: '2.4' -short_description: Manage hostgroups on Pure Storage FlashArrays -description: -- Create, delete or modify hostgroups on Pure Storage FlashArrays. -author: -- Pure Storage ansible Team (@sdodsley) -options: - hostgroup: - description: - - The name of the hostgroup. - type: str - required: true - state: - description: - - Define whether the hostgroup should exist or not. - type: str - default: present - choices: [ absent, present ] - host: - type: list - description: - - List of existing hosts to add to hostgroup. - volume: - type: list - description: - - List of existing volumes to add to hostgroup. -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create empty hostgroup - purefa_hg: - hostgroup: foo - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Add hosts and volumes to existing or new hostgroup - purefa_hg: - hostgroup: foo - host: - - host1 - - host2 - volume: - - vol1 - - vol2 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Delete hosts and volumes from hostgroup - purefa_hg: - hostgroup: foo - host: - - host1 - - host2 - volume: - - vol1 - - vol2 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: absent - -# This will disconnect all hosts and volumes in the hostgroup -- name: Delete hostgroup - purefa_hg: - hostgroup: foo - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: absent - -- name: Create host group with hosts and volumes - purefa_hg: - hostgroup: bar - host: - - host1 - - host2 - volume: - - vol1 - - vol2 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -try: - from purestorage import purestorage - HAS_PURESTORAGE = True -except ImportError: - HAS_PURESTORAGE = False - - -def get_hostgroup(module, array): - - hostgroup = None - - for host in array.list_hgroups(): - if host["name"] == module.params['hostgroup']: - hostgroup = host - break - - return hostgroup - - -def make_hostgroup(module, array): - - changed = True - - try: - array.create_hgroup(module.params['hostgroup']) - except Exception: - changed = False - if module.params['host']: - array.set_hgroup(module.params['hostgroup'], hostlist=module.params['host']) - if module.params['volume']: - for vol in module.params['volume']: - array.connect_hgroup(module.params['hostgroup'], vol) - module.exit_json(changed=changed) - - -def update_hostgroup(module, array): - changed = False - hgroup = get_hostgroup(module, array) - volumes = array.list_hgroup_connections(module.params['hostgroup']) - if module.params['state'] == "present": - if module.params['host']: - new_hosts = list(set(module.params['host']).difference(hgroup['hosts'])) - if new_hosts: - try: - array.set_hgroup(module.params['hostgroup'], addhostlist=new_hosts) - changed = True - except Exception: - module.fail_josn(msg='Failed to add host(s) to hostgroup') - if module.params['volume']: - if volumes: - current_vols = [vol['vol'] for vol in volumes] - new_volumes = list(set(module.params['volume']).difference(set(current_vols))) - for cvol in new_volumes: - try: - array.connect_hgroup(module.params['hostgroup'], cvol) - changed = True - except Exception: - changed = False - else: - for cvol in module.params['volume']: - try: - array.connect_hgroup(module.params['hostgroup'], cvol) - changed = True - except Exception: - changed = False - else: - if module.params['host']: - old_hosts = list(set(module.params['host']).intersection(hgroup['hosts'])) - if old_hosts: - try: - array.set_hgroup(module.params['hostgroup'], remhostlist=old_hosts) - changed = True - except Exception: - changed = False - if module.params['volume']: - old_volumes = list(set(module.params['volume']).difference(set([vol['name'] for vol in volumes]))) - for cvol in old_volumes: - try: - array.disconnect_hgroup(module.params['hostgroup'], cvol) - changed = True - except Exception: - changed = False - - module.exit_json(changed=changed) - - -def delete_hostgroup(module, array): - changed = True - try: - vols = array.list_hgroup_connections(module.params['hostgroup']) - for vol in vols: - try: - array.disconnect_hgroup(module.params['hostgroup'], vol["vol"]) - except Exception: - changed = False - host = array.get_hgroup(module.params['hostgroup']) - try: - array.set_hgroup(module.params['hostgroup'], remhostlist=host['hosts']) - try: - array.delete_hgroup(module.params['hostgroup']) - except Exception: - changed = False - except Exception: - changed = False - except Exception: - changed = False - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - hostgroup=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - host=dict(type='list'), - volume=dict(type='list'), - )) - - module = AnsibleModule(argument_spec, supports_check_mode=False) - - if not HAS_PURESTORAGE: - module.fail_json(msg='purestorage sdk is required for this module in host') - - state = module.params['state'] - array = get_system(module) - hostgroup = get_hostgroup(module, array) - - if module.params['host']: - try: - for hst in module.params['host']: - array.get_host(hst) - except Exception: - module.fail_json(msg='Host {0} not found'.format(hst)) - - if module.params['volume']: - try: - for vol in module.params['volume']: - array.get_volume(vol) - except Exception: - module.fail_json(msg='Volume {0} not found'.format(vol)) - - if hostgroup and state == 'present': - update_hostgroup(module, array) - elif hostgroup and module.params['volume'] and state == 'absent': - update_hostgroup(module, array) - elif hostgroup and module.params['host'] and state == 'absent': - update_hostgroup(module, array) - elif hostgroup and state == 'absent': - delete_hostgroup(module, array) - elif hostgroup is None and state == 'absent': - module.exit_json(changed=False) - else: - make_hostgroup(module, array) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_host.py b/lib/ansible/modules/storage/purestorage/purefa_host.py deleted file mode 100644 index aeeb82b634..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_host.py +++ /dev/null @@ -1,476 +0,0 @@ -#!/usr/bin/python -# -*- 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 - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: purefa_host -version_added: '2.4' -short_description: Manage hosts on Pure Storage FlashArrays -description: -- Create, delete or modify hosts on Pure Storage FlashArrays. -author: -- Pure Storage Ansible Team (@sdodsley) -notes: -- If specifying C(lun) option ensure host support requested value -options: - host: - description: - - The name of the host. - type: str - required: true - state: - description: - - Define whether the host should exist or not. - - When removing host all connected volumes will be disconnected. - type: str - default: present - choices: [ absent, present ] - protocol: - description: - - Defines the host connection protocol for volumes. - type: str - default: iscsi - choices: [ fc, iscsi, nvme, mixed ] - wwns: - type: list - description: - - List of wwns of the host if protocol is fc or mixed. - iqn: - type: list - description: - - List of IQNs of the host if protocol is iscsi or mixed. - nqn: - type: list - description: - - List of NQNs of the host if protocol is nvme or mixed. - version_added: '2.8' - volume: - type: str - description: - - Volume name to map to the host. - lun: - description: - - LUN ID to assign to volume for host. Must be unique. - - If not provided the ID will be automatically assigned. - - Range for LUN ID is 1 to 4095. - type: int - version_added: '2.8' - personality: - type: str - description: - - Define which operating system the host is. Recommended for - ActiveCluster integration. - default: '' - choices: ['hpux', 'vms', 'aix', 'esxi', 'solaris', 'hitachi-vsp', 'oracle-vm-server', 'delete', ''] - version_added: '2.7' - preferred_array: - type: list - description: - - List of preferred arrays in an ActiveCluster environment. - - To remove existing preferred arrays from the host, specify I(delete). - version_added: '2.9' -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create new AIX host - purefa_host: - host: foo - personality: aix - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Delete host - purefa_host: - host: foo - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: absent - -- name: Make host bar with wwn ports - purefa_host: - host: bar - protocol: fc - wwns: - - 00:00:00:00:00:00:00 - - 11:11:11:11:11:11:11 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Make host bar with iSCSI ports - purefa_host: - host: bar - protocol: iscsi - iqn: - - iqn.1994-05.com.redhat:7d366003913 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Make host bar with NVMe ports - purefa_host: - host: bar - protocol: nvme - nqn: - - nqn.2014-08.com.vendor:nvme:nvm-subsystem-sn-d78432 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Make mixed protocol host - purefa_host: - host: bar - protocol: mixed - nqn: - - nqn.2014-08.com.vendor:nvme:nvm-subsystem-sn-d78432 - iqn: - - iqn.1994-05.com.redhat:7d366003914 - wwns: - - 00:00:00:00:00:00:01 - - 11:11:11:11:11:11:12 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Map host foo to volume bar as LUN ID 12 - purefa_host: - host: foo - volume: bar - lun: 12 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Add preferred arrays to host foo - purefa_host: - host: foo - preferred_array: - - array1 - - array2 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Delete preferred arrays from host foo - purefa_host: - host: foo - preferred_array: delete - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -AC_REQUIRED_API_VERSION = '1.14' -PREFERRED_ARRAY_API_VERSION = '1.15' -NVME_API_VERSION = '1.16' - - -def _is_cbs(module, array, is_cbs=False): - """Is the selected array a Cloud Block Store""" - model = '' - ct0_model = array.get_hardware('CT0')['model'] - if ct0_model: - model = ct0_model - else: - ct1_model = array.get_hardware('CT1')['model'] - model = ct1_model - if 'CBS' in model: - is_cbs = True - return is_cbs - - -def _set_host_initiators(module, array): - """Set host initiators.""" - if module.params['protocol'] in ['nvme', 'mixed']: - if module.params['nqn']: - try: - array.set_host(module.params['host'], - nqnlist=module.params['nqn']) - except Exception: - module.fail_json(msg='Setting of NVMe NQN failed.') - if module.params['protocol'] in ['iscsi', 'mixed']: - if module.params['iqn']: - try: - array.set_host(module.params['host'], - iqnlist=module.params['iqn']) - except Exception: - module.fail_json(msg='Setting of iSCSI IQN failed.') - if module.params['protocol'] in ['fc', 'mixed']: - if module.params['wwns']: - try: - array.set_host(module.params['host'], - wwnlist=module.params['wwns']) - except Exception: - module.fail_json(msg='Setting of FC WWNs failed.') - - -def _update_host_initiators(module, array, answer=False): - """Change host initiator if iscsi or nvme or add new FC WWNs""" - if module.params['protocol'] in ['nvme', 'mixed']: - if module.params['nqn']: - current_nqn = array.get_host(module.params['host'])['nqn'] - if current_nqn != module.params['nqn']: - try: - array.set_host(module.params['host'], - nqnlist=module.params['nqn']) - answer = True - except Exception: - module.fail_json(msg='Change of NVMe NQN failed.') - if module.params['protocol'] in ['iscsi', 'mixed']: - if module.params['iqn']: - current_iqn = array.get_host(module.params['host'])['iqn'] - if current_iqn != module.params['iqn']: - try: - array.set_host(module.params['host'], - iqnlist=module.params['iqn']) - answer = True - except Exception: - module.fail_json(msg='Change of iSCSI IQN failed.') - if module.params['protocol'] in ['fc', 'mixed']: - if module.params['wwns']: - module.params['wwns'] = [wwn.replace(':', '') for wwn in module.params['wwns']] - module.params['wwns'] = [wwn.upper() for wwn in module.params['wwns']] - current_wwn = array.get_host(module.params['host'])['wwn'] - if current_wwn != module.params['wwns']: - try: - array.set_host(module.params['host'], - wwnlist=module.params['wwns']) - answer = True - except Exception: - module.fail_json(msg='FC WWN change failed.') - return answer - - -def _connect_new_volume(module, array, answer=False): - """Connect volume to host""" - api_version = array._list_available_rest_versions() - if AC_REQUIRED_API_VERSION in api_version and module.params['lun']: - try: - array.connect_host(module.params['host'], - module.params['volume'], - lun=module.params['lun']) - answer = True - except Exception: - module.fail_json(msg='LUN ID {0} invalid. Check for duplicate LUN IDs.'.format(module.params['lun'])) - else: - array.connect_host(module.params['host'], module.params['volume']) - answer = True - return answer - - -def _set_host_personality(module, array): - """Set host personality. Only called when supported""" - if module.params['personality'] != 'delete': - array.set_host(module.params['host'], - personality=module.params['personality']) - else: - array.set_host(module.params['host'], personality='') - - -def _set_preferred_array(module, array): - """Set preferred array list. Only called when supported""" - if module.params['preferred_array'] != ['delete']: - array.set_host(module.params['host'], - preferred_array=module.params['preferred_array']) - else: - array.set_host(module.params['host'], personality='') - - -def _update_host_personality(module, array, answer=False): - """Change host personality. Only called when supported""" - personality = array.get_host(module.params['host'], personality=True)['personality'] - if personality is None and module.params['personality'] != 'delete': - try: - array.set_host(module.params['host'], - personality=module.params['personality']) - answer = True - except Exception: - module.fail_json(msg='Personality setting failed.') - if personality is not None: - if module.params['personality'] == 'delete': - try: - array.set_host(module.params['host'], personality='') - answer = True - except Exception: - module.fail_json(msg='Personality deletion failed.') - elif personality != module.params['personality']: - try: - array.set_host(module.params['host'], - personality=module.params['personality']) - answer = True - except Exception: - module.fail_json(msg='Personality change failed.') - return answer - - -def _update_preferred_array(module, array, answer=False): - """Update existing preferred array list. Only called when supported""" - preferred_array = array.get_host(module.params['host'], preferred_array=True)['preferred_array'] - if preferred_array == [] and module.params['preferred_array'] != ['delete']: - try: - array.set_host(module.params['host'], - preferred_array=module.params['preferred_array']) - answer = True - except Exception: - module.fail_json(msg='Preferred array list creation failed for {0}.'.format(module.params['host'])) - elif preferred_array != []: - if module.params['preferred_array'] == ['delete']: - try: - array.set_host(module.params['host'], preferred_array=[]) - answer = True - except Exception: - module.fail_json(msg='Preferred array list deletion failed for {0}.'.format(module.params['host'])) - elif preferred_array != module.params['preferred_array']: - try: - array.set_host(module.params['host'], - preferred_array=module.params['preferred_array']) - answer = True - except Exception: - module.fail_json(msg='Preferred array list change failed for {0}.'.format(module.params['host'])) - return answer - - -def get_host(module, array): - host = None - for hst in array.list_hosts(): - if hst["name"] == module.params['host']: - host = hst - break - return host - - -def make_host(module, array): - changed = True - if not module.check_mode: - try: - array.create_host(module.params['host']) - except Exception: - module.fail_json(msg='Host {0} creation failed.'.format(module.params['host'])) - try: - _set_host_initiators(module, array) - api_version = array._list_available_rest_versions() - if AC_REQUIRED_API_VERSION in api_version and module.params['personality']: - _set_host_personality(module, array) - if PREFERRED_ARRAY_API_VERSION in api_version and module.params['preferred_array']: - _set_preferred_array(module, array) - if module.params['volume']: - if module.params['lun']: - array.connect_host(module.params['host'], - module.params['volume'], - lun=module.params['lun']) - else: - array.connect_host(module.params['host'], module.params['volume']) - except Exception: - module.fail_json(msg='Host {0} configuration failed.'.format(module.params['host'])) - module.exit_json(changed=changed) - - -def update_host(module, array): - changed = True - if not module.check_mode: - init_changed = vol_changed = pers_changed = pref_changed = False - volumes = array.list_host_connections(module.params['host']) - if module.params['iqn'] or module.params['wwns'] or module.params['nqn']: - init_changed = _update_host_initiators(module, array) - if module.params['volume']: - current_vols = [vol['vol'] for vol in volumes] - if not module.params['volume'] in current_vols: - vol_changed = _connect_new_volume(module, array) - api_version = array._list_available_rest_versions() - if AC_REQUIRED_API_VERSION in api_version: - if module.params['personality']: - pers_changed = _update_host_personality(module, array) - if PREFERRED_ARRAY_API_VERSION in api_version: - if module.params['preferred_array']: - pref_changed = _update_preferred_array(module, array) - changed = init_changed or vol_changed or pers_changed or pref_changed - module.exit_json(changed=changed) - - -def delete_host(module, array): - changed = True - if not module.check_mode: - try: - for vol in array.list_host_connections(module.params['host']): - array.disconnect_host(module.params['host'], vol["vol"]) - array.delete_host(module.params['host']) - except Exception: - module.fail_json(msg='Host {0} deletion failed'.format(module.params['host'])) - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - host=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - protocol=dict(type='str', default='iscsi', choices=['fc', 'iscsi', 'nvme', 'mixed']), - nqn=dict(type='list'), - iqn=dict(type='list'), - wwns=dict(type='list'), - volume=dict(type='str'), - lun=dict(type='int'), - personality=dict(type='str', default='', - choices=['hpux', 'vms', 'aix', 'esxi', 'solaris', - 'hitachi-vsp', 'oracle-vm-server', 'delete', '']), - preferred_array=dict(type='list'), - )) - - module = AnsibleModule(argument_spec, supports_check_mode=True) - - array = get_system(module) - if _is_cbs(module, array) and module.params['wwns'] or module.params['nqn']: - module.fail_json(msg='Cloud block Store only support iSCSI as a protocol') - api_version = array._list_available_rest_versions() - if module.params['nqn'] is not None and NVME_API_VERSION not in api_version: - module.fail_json(msg='NVMe protocol not supported. Please upgrade your array.') - state = module.params['state'] - host = get_host(module, array) - if module.params['lun'] and not 1 <= module.params['lun'] <= 4095: - module.fail_json(msg='LUN ID of {0} is out of range (1 to 4095)'.format(module.params['lun'])) - if module.params['volume']: - try: - array.get_volume(module.params['volume']) - except Exception: - module.fail_json(msg='Volume {0} not found'.format(module.params['volume'])) - if module.params['preferred_array']: - try: - if module.params['preferred_array'] != ['delete']: - all_connected_arrays = array.list_array_connections() - if not all_connected_arrays: - module.fail_json(msg='No target arrays connected to source array. Setting preferred arrays not possible.') - else: - current_arrays = [array.get()['array_name']] - for current_array in range(0, len(all_connected_arrays)): - if all_connected_arrays[current_array]['type'] == "sync-replication": - current_arrays.append(all_connected_arrays[current_array]['array_name']) - for array_to_connect in range(0, len(module.params['preferred_array'])): - if module.params['preferred_array'][array_to_connect] not in current_arrays: - module.fail_json(msg='Array {0} is not a synchronously connected array.'.format(module.params['preferred_array'][array_to_connect])) - except Exception: - module.fail_json(msg='Failed to get existing array connections.') - - if host is None and state == 'present': - make_host(module, array) - elif host and state == 'present': - update_host(module, array) - elif host and state == 'absent': - delete_host(module, array) - elif host is None and state == 'absent': - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_info.py b/lib/ansible/modules/storage/purestorage/purefa_info.py deleted file mode 100644 index 78080506f6..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_info.py +++ /dev/null @@ -1,1019 +0,0 @@ -#!/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: purefa_info -version_added: '2.9' -short_description: Collect information from Pure Storage FlashArray -description: - - Collect information from a Pure Storage Flasharray running the - Purity//FA 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 arguments. -author: - - Pure Storage ansible Team (@sdodsley) -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, subnet, interfaces, hgroups, pgroups, hosts, - admins, volumes, snapshots, pods, vgroups, offload, apps, arrays, - certs and kmip. - type: list - required: false - default: minimum -extends_documentation_fragment: - - purestorage.fa -''' - -EXAMPLES = r''' -- name: collect default set of information - purefa_info: - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - register: array_info -- name: show default information - debug: - msg: "{{ array_info['purefa_info']['default'] }}" - -- name: collect configuration and capacity information - purefa_info: - gather_subset: - - config - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - register: array_info -- name: show configuration information - debug: - msg: "{{ array_info['purefa_info']['config'] }}" - -- name: collect all information - purefa_info: - gather_subset: - - all - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -- name: show all information - debug: - msg: "{{ array_info['purefa_info'] }}" -''' - -RETURN = r''' -purefa_info: - description: Returns the information collected from 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" - } - }, - "arrays": {}, - "capacity": { - "data_reduction": 11.664774599686346, - "free_space": 6995782867042, - "provisioned_space": 442391871488, - "shared_space": 3070918120, - "snapshot_space": 284597118, - "system_space": 0, - "thin_provisioning": 0.8201773449669771, - "total_capacity": 7002920315199, - "total_reduction": 64.86821472825108, - "volume_space": 3781932919 - }, - "config": { - "directory_service": { - "base_dn": null, - "bind_password": null, - "bind_user": null, - "check_peer": false, - "enabled": false, - "uri": [], - "user_login_attribute": null, - "user_object_class": null - }, - "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": "acme.com", - "nameservers": [ - "8.8.4.4" - ] - }, - "global_admin": { - "lockout_duration": null, - "max_login_attempts": null, - "min_password_length": 1, - "single_sign_on_enabled": false - }, - "idle_timeout": 0, - "ntp": [ - "prod-ntp1.puretec.purestorage.com" - ], - "phonehome": "enabled", - "proxy": "", - "relayhost": "smtp.puretec.purestorage.com", - "scsi_timeout": 60, - "senderdomain": "purestorage.com", - "smtp": [ - { - "enabled": true, - "name": "flasharray-alerts@purestorage.com" - } - ], - "snmp": [ - { - "auth_passphrase": null, - "auth_protocol": null, - "community": "****", - "host": "10.21.23.34", - "name": "manager1", - "notification": "trap", - "privacy_passphrase": null, - "privacy_protocol": null, - "user": null, - "version": "v2c" - } - ], - "syslog": [ - "udp://prod-ntp2.puretec.purestorage.com:333" - ] - }, - "default": { - "admins": 1, - "array_model": "FA-405", - "array_name": "array", - "connected_arrays": 0, - "connection_key": "c6033033-fe69-2515-a9e8-966bb7fe4b40", - "hostgroups": 0, - "hosts": 15, - "pods": 1, - "protection_groups": 1, - "purity_version": "5.2.1", - "snapshots": 2, - "volume_groups": 1 - }, - "hgroups": {}, - "hosts": { - "@offload": { - "hgroup": null, - "iqn": [], - "nqn": [], - "personality": null, - "preferred_array": [], - "target_port": [], - "wwn": [] - }, - "docker-host": { - "hgroup": null, - "iqn": [ - "iqn.1994-05.com.redhat:d97adf78472" - ], - "nqn": [], - "personality": null, - "preferred_array": [], - "target_port": [ - "CT0.ETH4", - "CT1.ETH4" - ], - "wwn": [] - } - }, - "interfaces": { - "CT0.ETH4": "iqn.2010-06.com.purestorage:flasharray.2111b767484e4682", - "CT1.ETH4": "iqn.2010-06.com.purestorage:flasharray.2111b767484e4682", - }, - "network": { - "@offload.data0": { - "address": "10.21.200.222", - "gateway": "10.21.200.1", - "hwaddr": "52:54:30:02:b9:4e", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "app" - ], - "speed": 10000000000 - }, - "ct0.eth0": { - "address": "10.21.200.211", - "gateway": "10.21.200.1", - "hwaddr": "ec:f4:bb:c8:8a:04", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "management" - ], - "speed": 1000000000 - }, - "ct0.eth2": { - "address": "10.21.200.218", - "gateway": null, - "hwaddr": "ec:f4:bb:c8:8a:00", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "replication" - ], - "speed": 10000000000 - }, - "ct0.eth4": { - "address": "10.21.200.214", - "gateway": null, - "hwaddr": "90:e2:ba:83:79:0c", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "iscsi" - ], - "speed": 10000000000 - }, - "ct1.eth0": { - "address": "10.21.200.212", - "gateway": "10.21.200.1", - "hwaddr": "ec:f4:bb:e4:c6:3c", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "management" - ], - "speed": 1000000000 - }, - "ct1.eth2": { - "address": "10.21.200.220", - "gateway": null, - "hwaddr": "ec:f4:bb:e4:c6:38", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "replication" - ], - "speed": 10000000000 - }, - "ct1.eth4": { - "address": "10.21.200.216", - "gateway": null, - "hwaddr": "90:e2:ba:8b:b1:8c", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "iscsi" - ], - "speed": 10000000000 - }, - "vir0": { - "address": "10.21.200.210", - "gateway": "10.21.200.1", - "hwaddr": "fe:ba:e9:e7:6b:0f", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "management" - ], - "speed": 1000000000 - } - }, - "nfs_offload": {}, - "performance": { - "input_per_sec": 0, - "local_queue_usec_per_op": 0, - "output_per_sec": 0, - "qos_rate_limit_usec_per_read_op": 0, - "qos_rate_limit_usec_per_write_op": 0, - "queue_depth": 0, - "queue_usec_per_read_op": 0, - "queue_usec_per_write_op": 0, - "reads_per_sec": 0, - "san_usec_per_read_op": 0, - "san_usec_per_write_op": 0, - "time": "2019-08-14T21:33:51Z", - "usec_per_read_op": 0, - "usec_per_write_op": 0, - "writes_per_sec": 0 - }, - "pgroups": { - "test_pg": { - "hgroups": null, - "hosts": null, - "source": "docker-host", - "targets": null, - "volumes": null - } - }, - "pods": { - "test": { - "arrays": [ - { - "array_id": "043be47c-1233-4399-b9d6-8fe38727dd9d", - "mediator_status": "online", - "name": "array2", - "status": "online" - } - ], - "source": null - } - }, - "s3_offload": { - "s3-offload": { - "access_key_id": "AKIAILNVEPWZTV4FGWZQ", - "bucket": "offload-bucket", - "protocol": "s3", - "status": "connected" - } - }, - "snapshots": { - "@offload_boot.1": { - "created": "2019-03-14T15:29:20Z", - "size": 68719476736, - "source": "@offload_boot" - } - }, - "subnet": {}, - "vgroups": { - "test": { - "volumes": [ - "test/test", - "test/test1" - ] - } - }, - "volumes": { - "@offload_boot": { - "bandwidth": null, - "hosts": [ - [ - "@offload", - 1 - ] - ], - "serial": "43BE47C12334399B00013959", - "size": 68719476736, - "source": null - }, - "docker-store": { - "bandwidth": null, - "hosts": [ - [ - "docker-host", - 1 - ] - ], - "serial": "43BE47C12334399B00011418", - "size": 21474836480, - "source": null - } - } - } -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec -import time - -ADMIN_API_VERSION = '1.14' -S3_REQUIRED_API_VERSION = '1.16' -LATENCY_REQUIRED_API_VERSION = '1.16' -AC_REQUIRED_API_VERSION = '1.14' -CAP_REQUIRED_API_VERSION = '1.6' -SAN_REQUIRED_API_VERSION = '1.10' -NVME_API_VERSION = '1.16' -PREFERRED_API_VERSION = '1.15' -P53_API_VERSION = '1.17' - - -def generate_default_dict(array): - default_info = {} - defaults = array.get() - api_version = array._list_available_rest_versions() - if AC_REQUIRED_API_VERSION in api_version: - default_info['volume_groups'] = len(array.list_vgroups()) - default_info['connected_arrays'] = len(array.list_array_connections()) - default_info['pods'] = len(array.list_pods()) - default_info['connection_key'] = array.get(connection_key=True)['connection_key'] - hosts = array.list_hosts() - admins = array.list_admins() - snaps = array.list_volumes(snap=True, pending=True) - pgroups = array.list_pgroups(pending=True) - hgroups = array.list_hgroups() - # Old FA arrays only report model from the primary controller - ct0_model = array.get_hardware('CT0')['model'] - if ct0_model: - model = ct0_model - else: - ct1_model = array.get_hardware('CT1')['model'] - model = ct1_model - default_info['array_model'] = model - default_info['array_name'] = defaults['array_name'] - default_info['purity_version'] = defaults['version'] - default_info['hosts'] = len(hosts) - default_info['snapshots'] = len(snaps) - default_info['protection_groups'] = len(pgroups) - default_info['hostgroups'] = len(hgroups) - default_info['admins'] = len(admins) - if P53_API_VERSION in api_version: - default_info['maintenance_window'] = array.list_maintenance_windows() - return default_info - - -def generate_perf_dict(array): - perf_info = {} - api_version = array._list_available_rest_versions() - if LATENCY_REQUIRED_API_VERSION in api_version: - latency_info = array.get(action='monitor', latency=True)[0] - perf_info = array.get(action='monitor')[0] - # IOPS - perf_info['writes_per_sec'] = perf_info['writes_per_sec'] - perf_info['reads_per_sec'] = perf_info['reads_per_sec'] - - # Bandwidth - perf_info['input_per_sec'] = perf_info['input_per_sec'] - perf_info['output_per_sec'] = perf_info['output_per_sec'] - - # Latency - if LATENCY_REQUIRED_API_VERSION in api_version: - perf_info['san_usec_per_read_op'] = latency_info['san_usec_per_read_op'] - perf_info['san_usec_per_write_op'] = latency_info['san_usec_per_write_op'] - perf_info['queue_usec_per_read_op'] = latency_info['queue_usec_per_read_op'] - perf_info['queue_usec_per_write_op'] = latency_info['queue_usec_per_write_op'] - perf_info['qos_rate_limit_usec_per_read_op'] = latency_info['qos_rate_limit_usec_per_read_op'] - perf_info['qos_rate_limit_usec_per_write_op'] = latency_info['qos_rate_limit_usec_per_write_op'] - perf_info['local_queue_usec_per_op'] = perf_info['local_queue_usec_per_op'] - perf_info['usec_per_read_op'] = perf_info['usec_per_read_op'] - perf_info['usec_per_write_op'] = perf_info['usec_per_write_op'] - perf_info['queue_depth'] = perf_info['queue_depth'] - return perf_info - - -def generate_config_dict(array): - config_info = {} - api_version = array._list_available_rest_versions() - # DNS - config_info['dns'] = array.get_dns() - # SMTP - config_info['smtp'] = array.list_alert_recipients() - # SNMP - config_info['snmp'] = array.list_snmp_managers() - config_info['snmp_v3_engine_id'] = array.get_snmp_engine_id()['engine_id'] - # DS - config_info['directory_service'] = array.get_directory_service() - if S3_REQUIRED_API_VERSION in api_version: - config_info['directory_service_roles'] = {} - roles = array.list_directory_service_roles() - for role in range(0, len(roles)): - role_name = roles[role]['name'] - config_info['directory_service_roles'][role_name] = { - 'group': roles[role]['group'], - 'group_base': roles[role]['group_base'], - } - else: - config_info['directory_service'].update(array.get_directory_service(groups=True)) - # NTP - config_info['ntp'] = array.get(ntpserver=True)['ntpserver'] - # SYSLOG - config_info['syslog'] = array.get(syslogserver=True)['syslogserver'] - # Phonehome - config_info['phonehome'] = array.get(phonehome=True)['phonehome'] - # Proxy - config_info['proxy'] = array.get(proxy=True)['proxy'] - # Relay Host - config_info['relayhost'] = array.get(relayhost=True)['relayhost'] - # Sender Domain - config_info['senderdomain'] = array.get(senderdomain=True)['senderdomain'] - # SYSLOG - config_info['syslog'] = array.get(syslogserver=True)['syslogserver'] - # Idle Timeout - config_info['idle_timeout'] = array.get(idle_timeout=True)['idle_timeout'] - # SCSI Timeout - config_info['scsi_timeout'] = array.get(scsi_timeout=True)['scsi_timeout'] - # Global Admin settings - if S3_REQUIRED_API_VERSION in api_version: - config_info['global_admin'] = array.get_global_admin_attributes() - return config_info - - -def generate_admin_dict(array): - admin_info = {} - api_version = array._list_available_rest_versions() - if ADMIN_API_VERSION in api_version: - admins = array.list_admins() - for admin in range(0, len(admins)): - admin_name = admins[admin]['name'] - admin_info[admin_name] = { - 'type': admins[admin]['type'], - 'role': admins[admin]['role'], - } - return admin_info - - -def generate_subnet_dict(array): - sub_info = {} - subnets = array.list_subnets() - for sub in range(0, len(subnets)): - sub_name = subnets[sub]['name'] - if subnets[sub]['enabled']: - sub_info[sub_name] = { - 'gateway': subnets[sub]['gateway'], - 'mtu': subnets[sub]['mtu'], - 'vlan': subnets[sub]['vlan'], - 'prefix': subnets[sub]['prefix'], - 'interfaces': subnets[sub]['interfaces'], - 'services': subnets[sub]['services'], - } - return sub_info - - -def generate_network_dict(array): - net_info = {} - ports = array.list_network_interfaces() - for port in range(0, len(ports)): - int_name = ports[port]['name'] - net_info[int_name] = { - 'hwaddr': ports[port]['hwaddr'], - 'mtu': ports[port]['mtu'], - 'enabled': ports[port]['enabled'], - 'speed': ports[port]['speed'], - 'address': ports[port]['address'], - 'slaves': ports[port]['slaves'], - 'services': ports[port]['services'], - 'gateway': ports[port]['gateway'], - 'netmask': ports[port]['netmask'], - } - if ports[port]['subnet']: - subnets = array.get_subnet(ports[port]['subnet']) - if subnets['enabled']: - net_info[int_name]['subnet'] = { - 'name': subnets['name'], - 'prefix': subnets['prefix'], - 'vlan': subnets['vlan'], - } - return net_info - - -def generate_capacity_dict(array): - capacity_info = {} - api_version = array._list_available_rest_versions() - if CAP_REQUIRED_API_VERSION in api_version: - volumes = array.list_volumes(pending=True) - capacity_info['provisioned_space'] = sum(item['size'] for item in volumes) - capacity = array.get(space=True) - total_capacity = capacity[0]['capacity'] - used_space = capacity[0]["total"] - capacity_info['free_space'] = total_capacity - used_space - capacity_info['total_capacity'] = total_capacity - capacity_info['data_reduction'] = capacity[0]['data_reduction'] - capacity_info['system_space'] = capacity[0]['system'] - capacity_info['volume_space'] = capacity[0]['volumes'] - capacity_info['shared_space'] = capacity[0]['shared_space'] - capacity_info['snapshot_space'] = capacity[0]['snapshots'] - capacity_info['thin_provisioning'] = capacity[0]['thin_provisioning'] - capacity_info['total_reduction'] = capacity[0]['total_reduction'] - - return capacity_info - - -def generate_snap_dict(array): - snap_info = {} - snaps = array.list_volumes(snap=True) - for snap in range(0, len(snaps)): - snapshot = snaps[snap]['name'] - snap_info[snapshot] = { - 'size': snaps[snap]['size'], - 'source': snaps[snap]['source'], - 'created': snaps[snap]['created'], - } - return snap_info - - -def generate_vol_dict(array): - volume_info = {} - vols = array.list_volumes() - for vol in range(0, len(vols)): - volume = vols[vol]['name'] - volume_info[volume] = { - 'source': vols[vol]['source'], - 'size': vols[vol]['size'], - 'serial': vols[vol]['serial'], - 'hosts': [], - 'bandwidth': "" - } - api_version = array._list_available_rest_versions() - if AC_REQUIRED_API_VERSION in api_version: - qvols = array.list_volumes(qos=True) - for qvol in range(0, len(qvols)): - volume = qvols[qvol]['name'] - qos = qvols[qvol]['bandwidth_limit'] - volume_info[volume]['bandwidth'] = qos - if P53_API_VERSION in api_version: - iops = qvols[qvol]['iops_limit'] - volume_info[volume]['iops_limit'] = iops - vvols = array.list_volumes(protocol_endpoint=True) - for vvol in range(0, len(vvols)): - volume = vvols[vvol]['name'] - volume_info[volume] = { - 'source': vvols[vvol]['source'], - 'serial': vvols[vvol]['serial'], - 'hosts': [] - } - if P53_API_VERSION in array._list_available_rest_versions(): - pe_e2ees = array.list_volumes(protocol_endpoint=True, host_encryption_key=True) - for pe_e2ee in range(0, len(pe_e2ees)): - volume = pe_e2ees[pe_e2ee]['name'] - volume_info[volume]['host_encryption_key_status'] = pe_e2ees[pe_e2ee]['host_encryption_key_status'] - if P53_API_VERSION in array._list_available_rest_versions(): - e2ees = array.list_volumes(host_encryption_key=True) - for e2ee in range(0, len(e2ees)): - volume = e2ees[e2ee]['name'] - volume_info[volume]['host_encryption_key_status'] = e2ees[e2ee]['host_encryption_key_status'] - cvols = array.list_volumes(connect=True) - for cvol in range(0, len(cvols)): - volume = cvols[cvol]['name'] - voldict = {'host': cvols[cvol]['host'], 'lun': cvols[cvol]['lun']} - volume_info[volume]['hosts'].append(voldict) - return volume_info - - -def generate_host_dict(array): - api_version = array._list_available_rest_versions() - host_info = {} - hosts = array.list_hosts() - for host in range(0, len(hosts)): - hostname = hosts[host]['name'] - tports = [] - host_all_info = array.get_host(hostname, all=True) - if host_all_info: - tports = host_all_info[0]['target_port'] - host_info[hostname] = { - 'hgroup': hosts[host]['hgroup'], - 'iqn': hosts[host]['iqn'], - 'wwn': hosts[host]['wwn'], - 'personality': array.get_host(hostname, - personality=True)['personality'], - 'target_port': tports - } - if NVME_API_VERSION in api_version: - host_info[hostname]['nqn'] = hosts[host]['nqn'] - if PREFERRED_API_VERSION in api_version: - hosts = array.list_hosts(preferred_array=True) - for host in range(0, len(hosts)): - hostname = hosts[host]['name'] - host_info[hostname]['preferred_array'] = hosts[host]['preferred_array'] - return host_info - - -def generate_pgroups_dict(array): - pgroups_info = {} - pgroups = array.list_pgroups() - for pgroup in range(0, len(pgroups)): - protgroup = pgroups[pgroup]['name'] - pgroups_info[protgroup] = { - 'hgroups': pgroups[pgroup]['hgroups'], - 'hosts': pgroups[pgroup]['hosts'], - 'source': pgroups[pgroup]['source'], - 'targets': pgroups[pgroup]['targets'], - 'volumes': pgroups[pgroup]['volumes'], - } - prot_sched = array.get_pgroup(protgroup, schedule=True) - prot_reten = array.get_pgroup(protgroup, retention=True) - if prot_sched['snap_enabled'] or prot_sched['replicate_enabled']: - pgroups_info[protgroup]['snap_freqyency'] = prot_sched['snap_frequency'] - pgroups_info[protgroup]['replicate_freqyency'] = prot_sched['replicate_frequency'] - pgroups_info[protgroup]['snap_enabled'] = prot_sched['snap_enabled'] - pgroups_info[protgroup]['replicate_enabled'] = prot_sched['replicate_enabled'] - pgroups_info[protgroup]['snap_at'] = prot_sched['snap_at'] - pgroups_info[protgroup]['replicate_at'] = prot_sched['replicate_at'] - pgroups_info[protgroup]['replicate_blackout'] = prot_sched['replicate_blackout'] - pgroups_info[protgroup]['per_day'] = prot_reten['per_day'] - pgroups_info[protgroup]['target_per_day'] = prot_reten['target_per_day'] - pgroups_info[protgroup]['target_days'] = prot_reten['target_days'] - pgroups_info[protgroup]['days'] = prot_reten['days'] - pgroups_info[protgroup]['all_for'] = prot_reten['all_for'] - pgroups_info[protgroup]['target_all_for'] = prot_reten['target_all_for'] - if ":" in protgroup: - snap_transfers = array.get_pgroup(protgroup, snap=True, transfer=True) - pgroups_info[protgroup]['snaps'] = {} - for snap_transfer in range(0, len(snap_transfers)): - snap = snap_transfers[snap_transfer]['name'] - pgroups_info[protgroup]['snaps'][snap] = { - 'created': snap_transfers[snap_transfer]['created'], - 'started': snap_transfers[snap_transfer]['started'], - 'completed': snap_transfers[snap_transfer]['completed'], - 'physical_bytes_written': snap_transfers[snap_transfer]['physical_bytes_written'], - 'data_transferred': snap_transfers[snap_transfer]['data_transferred'], - 'progress': snap_transfers[snap_transfer]['progress'], - } - return pgroups_info - - -def generate_pods_dict(array): - pods_info = {} - api_version = array._list_available_rest_versions() - if AC_REQUIRED_API_VERSION in api_version: - pods = array.list_pods() - for pod in range(0, len(pods)): - acpod = pods[pod]['name'] - pods_info[acpod] = { - 'source': pods[pod]['source'], - 'arrays': pods[pod]['arrays'], - } - return pods_info - - -def generate_conn_array_dict(array): - conn_array_info = {} - api_version = array._list_available_rest_versions() - carrays = array.list_array_connections() - for carray in range(0, len(carrays)): - arrayname = carrays[carray]['array_name'] - conn_array_info[arrayname] = { - 'array_id': carrays[carray]['id'], - 'throttled': carrays[carray]['throttled'], - 'version': carrays[carray]['version'], - 'type': carrays[carray]['type'], - 'mgmt_ip': carrays[carray]['management_address'], - 'repl_ip': carrays[carray]['replication_address'], - } - if P53_API_VERSION in api_version: - conn_array_info[arrayname]['status'] = carrays[carray]['status'] - throttles = array.list_array_connections(throttle=True) - for throttle in range(0, len(throttles)): - arrayname = throttles[throttle]['array_name'] - if conn_array_info[arrayname]['throttled']: - conn_array_info[arrayname]['throttling'] = { - 'default_limit': throttles[throttle]['default_limit'], - 'window_limit': throttles[throttle]['window_limit'], - 'window': throttles[throttle]['window'], - } - return conn_array_info - - -def generate_apps_dict(array): - apps_info = {} - api_version = array._list_available_rest_versions() - if SAN_REQUIRED_API_VERSION in api_version: - apps = array.list_apps() - for app in range(0, len(apps)): - appname = apps[app]['name'] - apps_info[appname] = { - 'version': apps[app]['version'], - 'status': apps[app]['status'], - 'description': apps[app]['description'], - } - return apps_info - - -def generate_vgroups_dict(array): - vgroups_info = {} - api_version = array._list_available_rest_versions() - if AC_REQUIRED_API_VERSION in api_version: - vgroups = array.list_vgroups() - for vgroup in range(0, len(vgroups)): - virtgroup = vgroups[vgroup]['name'] - vgroups_info[virtgroup] = { - 'volumes': vgroups[vgroup]['volumes'], - } - return vgroups_info - - -def generate_certs_dict(array): - certs_info = {} - api_version = array._list_available_rest_versions() - if P53_API_VERSION in api_version: - certs = array.list_certificates() - for cert in range(0, len(certs)): - certificate = certs[cert]['name'] - valid_from = time.strftime("%a, %d %b %Y %H:%M:%S %Z", time.localtime(certs[cert]['valid_from'] / 1000)) - valid_to = time.strftime("%a, %d %b %Y %H:%M:%S %Z", time.localtime(certs[cert]['valid_to'] / 1000)) - certs_info[certificate] = { - 'status': certs[cert]['status'], - 'issued_to': certs[cert]['issued_to'], - 'valid_from': valid_from, - 'locality': certs[cert]['locality'], - 'country': certs[cert]['country'], - 'issued_by': certs[cert]['issued_by'], - 'valid_to': valid_to, - 'state': certs[cert]['state'], - 'key_size': certs[cert]['key_size'], - 'org_unit': certs[cert]['organizational_unit'], - 'common_name': certs[cert]['common_name'], - 'organization': certs[cert]['organization'], - 'email': certs[cert]['email'], - } - return certs_info - - -def generate_kmip_dict(array): - kmip_info = {} - api_version = array._list_available_rest_versions() - if P53_API_VERSION in api_version: - kmips = array.list_kmip() - for kmip in range(0, len(kmips)): - key = kmips[kmip]['name'] - kmip_info[key] = { - 'certificate': kmips[kmip]['certificate'], - 'ca_cert_configured': kmips[kmip]['ca_certificate_configured'], - 'uri': kmips[kmip]['uri'], - } - return kmip_info - - -def generate_nfs_offload_dict(array): - offload_info = {} - api_version = array._list_available_rest_versions() - if AC_REQUIRED_API_VERSION in api_version: - offload = array.list_nfs_offload() - for target in range(0, len(offload)): - offloadt = offload[target]['name'] - offload_info[offloadt] = { - 'status': offload[target]['status'], - 'mount_point': offload[target]['mount_point'], - 'protocol': offload[target]['protocol'], - 'mount_options': offload[target]['mount_options'], - 'address': offload[target]['address'], - } - return offload_info - - -def generate_s3_offload_dict(array): - offload_info = {} - api_version = array._list_available_rest_versions() - if S3_REQUIRED_API_VERSION in api_version: - offload = array.list_s3_offload() - for target in range(0, len(offload)): - offloadt = offload[target]['name'] - offload_info[offloadt] = { - 'status': offload[target]['status'], - 'bucket': offload[target]['bucket'], - 'protocol': offload[target]['protocol'], - 'access_key_id': offload[target]['access_key_id'], - } - if P53_API_VERSION in api_version: - offload_info[offloadt]['placement_strategy'] = offload[target]['placement_strategy'] - return offload_info - - -def generate_azure_offload_dict(array): - offload_info = {} - api_version = array._list_available_rest_versions() - if P53_API_VERSION in api_version: - offload = array.list_azure_offload() - for target in range(0, len(offload)): - offloadt = offload[target]['name'] - offload_info[offloadt] = { - 'status': offload[target]['status'], - 'account_name': offload[target]['account_name'], - 'protocol': offload[target]['protocol'], - 'secret_access_key': offload[target]['secret_access_key'], - 'container_name': offload[target]['container_name'], - } - return offload_info - - -def generate_hgroups_dict(array): - hgroups_info = {} - hgroups = array.list_hgroups() - for hgroup in range(0, len(hgroups)): - hostgroup = hgroups[hgroup]['name'] - hgroups_info[hostgroup] = { - 'hosts': hgroups[hgroup]['hosts'], - 'pgs': [], - 'vols': [], - } - pghgroups = array.list_hgroups(protect=True) - for pghg in range(0, len(pghgroups)): - pgname = pghgroups[pghg]['name'] - hgroups_info[pgname]['pgs'].append(pghgroups[pghg]['protection_group']) - volhgroups = array.list_hgroups(connect=True) - for pgvol in range(0, len(volhgroups)): - pgname = volhgroups[pgvol]['name'] - volpgdict = [volhgroups[pgvol]['vol'], volhgroups[pgvol]['lun']] - hgroups_info[pgname]['vols'].append(volpgdict) - return hgroups_info - - -def generate_interfaces_dict(array): - api_version = array._list_available_rest_versions() - int_info = {} - ports = array.list_ports() - for port in range(0, len(ports)): - int_name = ports[port]['name'] - if ports[port]['wwn']: - int_info[int_name] = ports[port]['wwn'] - if ports[port]['iqn']: - int_info[int_name] = ports[port]['iqn'] - if NVME_API_VERSION in api_version: - if ports[port]['nqn']: - int_info[int_name] = ports[port]['nqn'] - return int_info - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - gather_subset=dict(default='minimum', type='list',) - )) - - module = AnsibleModule(argument_spec, supports_check_mode=False) - array = get_system(module) - - subset = [test.lower() for test in module.params['gather_subset']] - valid_subsets = ('all', 'minimum', 'config', 'performance', 'capacity', - 'network', 'subnet', 'interfaces', 'hgroups', 'pgroups', - 'hosts', 'admins', 'volumes', 'snapshots', 'pods', - 'vgroups', 'offload', 'apps', 'arrays', 'certs', 'kmip') - 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 or 'apps' in subset: - info['default'] = generate_default_dict(array) - if 'performance' in subset or 'all' in subset: - info['performance'] = generate_perf_dict(array) - if 'config' in subset or 'all' in subset: - info['config'] = generate_config_dict(array) - if 'capacity' in subset or 'all' in subset: - info['capacity'] = generate_capacity_dict(array) - if 'network' in subset or 'all' in subset: - info['network'] = generate_network_dict(array) - if 'subnet' in subset or 'all' in subset: - info['subnet'] = generate_subnet_dict(array) - if 'interfaces' in subset or 'all' in subset: - info['interfaces'] = generate_interfaces_dict(array) - if 'hosts' in subset or 'all' in subset: - info['hosts'] = generate_host_dict(array) - if 'volumes' in subset or 'all' in subset: - info['volumes'] = generate_vol_dict(array) - if 'snapshots' in subset or 'all' in subset: - info['snapshots'] = generate_snap_dict(array) - if 'hgroups' in subset or 'all' in subset: - info['hgroups'] = generate_hgroups_dict(array) - if 'pgroups' in subset or 'all' in subset: - info['pgroups'] = generate_pgroups_dict(array) - if 'pods' in subset or 'all' in subset: - info['pods'] = generate_pods_dict(array) - if 'admins' in subset or 'all' in subset: - info['admins'] = generate_admin_dict(array) - if 'vgroups' in subset or 'all' in subset: - info['vgroups'] = generate_vgroups_dict(array) - if 'offload' in subset or 'all' in subset: - info['azure_offload'] = generate_azure_offload_dict(array) - info['nfs_offload'] = generate_nfs_offload_dict(array) - info['s3_offload'] = generate_s3_offload_dict(array) - if 'apps' in subset or 'all' in subset: - if 'CBS' not in info['default']['array_model']: - info['apps'] = generate_apps_dict(array) - else: - info['apps'] = {} - if 'arrays' in subset or 'all' in subset: - info['arrays'] = generate_conn_array_dict(array) - if 'certs' in subset or 'all' in subset: - info['certs'] = generate_certs_dict(array) - if 'kmip' in subset or 'all' in subset: - info['kmip'] = generate_kmip_dict(array) - - module.exit_json(changed=False, purefa_info=info) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_ntp.py b/lib/ansible/modules/storage/purestorage/purefa_ntp.py deleted file mode 100644 index a47c5016e8..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_ntp.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/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: purefa_ntp -version_added: '2.8' -short_description: Configure Pure Storage FlashArray NTP settings -description: -- Set or erase NTP configuration for Pure Storage FlashArrays. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Create or delete NTP servers configuration - type: str - default: present - choices: [ absent, present ] - ntp_servers: - type: list - 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.fa -''' - -EXAMPLES = r''' -- name: Delete existing NTP server entries - purefa_ntp: - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Set array NTP servers - purefa_ntp: - state: present - ntp_servers: - - "0.pool.ntp.org" - - "1.pool.ntp.org" - - "2.pool.ntp.org" - - "3.pool.ntp.org" - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_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_ntp(module, array): - """Delete NTP Servers""" - changed = False - if array.get(ntpserver=True)['ntpserver'] != []: - try: - array.set(ntpserver=[]) - changed = True - except Exception: - module.fail_json(msg='Deletion of NTP servers failed') - module.exit_json(changed=changed) - - -def create_ntp(module, array): - """Set NTP Servers""" - changed = False - if not module.params['ntp_servers']: - module.params['ntp_servers'] = ['0.pool.ntp.org'] - try: - array.set(ntpserver=module.params['ntp_servers'][0:4]) - changed = True - except Exception: - module.fail_json(msg='Update of NTP servers failed') - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - ntp_servers=dict(type='list'), - 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=False) - - array = get_system(module) - - if module.params['state'] == 'absent': - delete_ntp(module, array) - else: - module.params['ntp_servers'] = remove(module.params['ntp_servers']) - if sorted(array.get(ntpserver=True)['ntpserver']) != sorted(module.params['ntp_servers'][0:4]): - create_ntp(module, array) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_offload.py b/lib/ansible/modules/storage/purestorage/purefa_offload.py deleted file mode 100644 index 25a061b491..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_offload.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/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: purefa_offload -version_added: '2.8' -short_description: Create, modify and delete NFS or S3 offload targets -description: -- Create, modify and delete NFS or S3 offload targets. -- Only supported on Purity v5.2.0 or higher. -- You must have a correctly configured offload network for offload to work. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Define state of offload - default: present - choices: [ absent, present ] - type: str - name: - description: - - The name of the offload target - required: true - type: str - protocol: - description: - - Define which protocol the offload engine uses - default: nfs - choices: [ nfs, s3 ] - type: str - address: - description: - - The IP or FQDN address of the NFS server - type: str - share: - description: - - NFS export on the NFS server - type: str - options: - description: - - Additional mount options for the NFS share - - Supported mount options include I(port), I(rsize), - I(wsize), I(nfsvers), and I(tcp) or I(udp) - required: false - default: "" - type: str - access_key: - description: - - Access Key ID of the S3 target - type: str - bucket: - description: - - Name of the bucket for the S3 target - type: str - secret: - description: - - Secret Access Key for the S3 target - type: str - initialize: - description: - - Define whether to initialize the S3 bucket - type: bool - default: true - -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create NFS offload target - purefa_offload: - name: nfs-offload - protocol: nfs - address: 10.21.200.4 - share: "/offload_target" - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create S3 offload target - purefa_offload: - name: s3-offload - protocol: s3 - access_key: "3794fb12c6204e19195f" - bucket: offload-bucket - secret: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Delete offload target - purefa_offload: - name: nfs-offload - protocol: nfs - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - - -''' - -RETURN = r''' -''' - -import re -from distutils.version import LooseVersion - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - -MIN_REQUIRED_API_VERSION = '1.16' -REGEX_TARGET_NAME = re.compile(r"^[a-zA-Z0-9\-]*$") - - -def get_target(module, array): - """Return target or None""" - try: - return array.get_offload(module.params['name']) - except Exception: - return None - - -def create_offload(module, array): - """Create offload target""" - changed = False - # First check if the offload network interface is there and enabled - try: - if not array.get_network_interface('@offload.data')['enabled']: - module.fail_json(msg='Offload Network interface not enabled. Please resolve.') - except Exception: - module.fail_json(msg='Offload Network interface not correctly configured. Please resolve.') - if module.params['protocol'] == 'nfs': - try: - array.connect_nfs_offload(module.params['name'], - mount_point=module.params['share'], - address=module.params['address'], - mount_options=module.params['options']) - changed = True - except Exception: - module.fail_json(msg='Failed to create NFS offload {0}. ' - 'Please perform diagnostic checks.'.format(module.params['name'])) - if module.params['protocol'] == 's3': - try: - array.connect_s3_offload(module.params['name'], - access_key_id=module.params['access_key'], - secret_access_key=module.params['secret'], - bucket=module.params['bucket'], - initialize=module.params['initialize']) - changed = True - except Exception: - module.fail_json(msg='Failed to create S3 offload {0}. ' - 'Please perform diagnostic checks.'.format(module.params['name'])) - module.exit_json(changed=changed) - - -def update_offload(module, array): - """Update offload target""" - changed = False - module.exit_json(changed=changed) - - -def delete_offload(module, array): - """Delete offload target""" - changed = False - if module.params['protocol'] == 'nfs': - try: - array.disconnect_nfs_offload(module.params['name']) - changed = True - except Exception: - module.fail_json(msg='Failed to delete NFS offload {0}.'.format(module.params['name'])) - if module.params['protocol'] == 's3': - try: - array.disconnect_nfs_offload(module.params['name']) - changed = True - except Exception: - module.fail_json(msg='Failed to delete S3 offload {0}.'.format(module.params['name'])) - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - state=dict(type='str', default='present', choices=['present', 'absent']), - protocol=dict(type='str', default='nfs', choices=['nfs', 's3']), - name=dict(type='str', required=True), - initialize=dict(default=True, type='bool'), - access_key=dict(type='str'), - secret=dict(type='str', no_log=True), - bucket=dict(type='str'), - share=dict(type='str'), - address=dict(type='str'), - options=dict(type='str', default=''), - )) - - required_if = [] - - if argument_spec['state'] == "present": - required_if = [ - ('protocol', 'nfs', ['address', 'share']), - ('protocol', 's3', ['access_key', 'secret', 'bucket']) - ] - - module = AnsibleModule(argument_spec, - required_if=required_if, - supports_check_mode=False) - - array = get_system(module) - api_version = array._list_available_rest_versions() - - if MIN_REQUIRED_API_VERSION not in api_version: - module.fail_json(msg='FlashArray REST version not supported. ' - 'Minimum version required: {0}'.format(MIN_REQUIRED_API_VERSION)) - - if not re.match(r"^[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9]$", module.params['name']) or len(module.params['name']) > 56: - module.fail_json(msg='Target name invalid. ' - 'Target name must be between 1 and 56 characters (alphanumeric and -) in length ' - 'and begin and end with a letter or number. The name must include at least one letter.') - if module.params['protocol'] == "s3": - if not re.match(r"^[a-z0-9][a-z0-9.\-]*[a-z0-9]$", module.params['bucket']) or len(module.params['bucket']) > 63: - module.fail_json(msg='Bucket name invalid. ' - 'Bucket name must be between 3 and 63 characters ' - '(ilowercase, alphanumeric, dash or period) in length ' - 'and begin and end with a letter or number.') - - apps = array.list_apps() - app_version = 0 - all_good = False - for app in range(0, len(apps)): - if apps[app]['name'] == 'offload': - if (apps[app]['enabled'] and - apps[app]['status'] == 'healthy' and - LooseVersion(apps[app]['version']) >= LooseVersion('5.2.0')): - all_good = True - app_version = apps[app]['version'] - break - - if not all_good: - module.fail_json(msg='Correct Offload app not installed or incorrectly configured') - else: - if LooseVersion(array.get()['version']) != LooseVersion(app_version): - module.fail_json(msg='Offload app version must match Purity version. Please upgrade.') - - target = get_target(module, array) - if module.params['state'] == 'present' and not target: - target_count = len(array.list_offload()) - # Currently only 1 offload target is supported - # TODO: (SD) when more targets supported add in REST version check as well - if target_count != 0: - module.fail_json(msg='Currently only 1 Offload Target is supported.') - create_offload(module, array) - elif module.params['state'] == 'present' and target: - update_offload(module, array) - elif module.params['state'] == 'absent' and target: - delete_offload(module, array) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_pg.py b/lib/ansible/modules/storage/purestorage/purefa_pg.py deleted file mode 100644 index ad45cb33f4..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_pg.py +++ /dev/null @@ -1,489 +0,0 @@ -#!/usr/bin/python -# -*- 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 - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: purefa_pg -version_added: '2.4' -short_description: Manage protection groups on Pure Storage FlashArrays -description: -- Create, delete or modify protection groups on Pure Storage FlashArrays. -- If a protection group exists and you try to add non-valid types, eg. a host - to a volume protection group the module will ignore the invalid types. -- Protection Groups on Offload targets are supported. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - pgroup: - description: - - The name of the protection group. - type: str - required: true - state: - description: - - Define whether the protection group should exist or not. - type: str - default: present - choices: [ absent, present ] - volume: - description: - - List of existing volumes to add to protection group. - type: list - host: - description: - - List of existing hosts to add to protection group. - type: list - hostgroup: - description: - - List of existing hostgroups to add to protection group. - type: list - eradicate: - description: - - Define whether to eradicate the protection group on delete and leave in trash. - type : bool - default: 'no' - enabled: - description: - - Define whether to enabled snapshots for the protection group. - type : bool - default: 'yes' - target: - description: - - List of remote arrays or offload target for replication protection group - to connect to. - - Note that all replicated protection groups are asynchronous. - - Target arrays or offload targets must already be connected to the source array. - - Maximum number of targets per Protection Group is 4, assuming your - configuration supports this. - type: list - version_added: '2.8' -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create new local protection group - purefa_pg: - pgroup: foo - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create new replicated protection group - purefa_pg: - pgroup: foo - target: - - arrayb - - arrayc - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create new replicated protection group to offload target and remote array - purefa_pg: - pgroup: foo - target: - - offload - - arrayc - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create new protection group with snapshots disabled - purefa_pg: - pgroup: foo - enabled: false - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Delete protection group - purefa_pg: - pgroup: foo - eradicate: true - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: absent - -- name: Eradicate protection group foo on offload target where source array is arrayA - purefa_pg: - pgroup: "arrayA:foo" - target: offload - eradicate: true - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: absent - -- name: Create protection group for hostgroups - purefa_pg: - pgroup: bar - hostgroup: - - hg1 - - hg2 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create protection group for hosts - purefa_pg: - pgroup: bar - host: - - host1 - - host2 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create replicated protection group for volumes - purefa_pg: - pgroup: bar - volume: - - vol1 - - vol2 - target: arrayb - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -OFFLOAD_API_VERSION = '1.16' - - -def get_targets(array): - """Get Offload Targets""" - targets = [] - try: - target_details = array.list_offload() - except Exception: - return None - - for targetcnt in range(0, len(target_details)): - if target_details[targetcnt]['status'] == "connected": - targets.append(target_details[targetcnt]['name']) - return targets - - -def get_arrays(array): - """ Get Connected Arrays""" - arrays = [] - array_details = array.list_array_connections() - for arraycnt in range(0, len(array_details)): - if array_details[arraycnt]['connected']: - arrays.append(array_details[arraycnt]['array_name']) - - return arrays - - -def get_pending_pgroup(module, array): - """ Get Protection Group""" - pgroup = None - if ":" in module.params['pgroup']: - for pgrp in array.list_pgroups(pending=True, on="*"): - if pgrp["name"] == module.params['pgroup'] and pgrp['time_remaining']: - pgroup = pgrp - break - else: - for pgrp in array.list_pgroups(pending=True): - if pgrp["name"] == module.params['pgroup'] and pgrp['time_remaining']: - pgroup = pgrp - break - - return pgroup - - -def get_pgroup(module, array): - """ Get Protection Group""" - pgroup = None - if ":" in module.params['pgroup']: - for pgrp in array.list_pgroups(on="*"): - if pgrp["name"] == module.params['pgroup']: - pgroup = pgrp - break - else: - for pgrp in array.list_pgroups(): - if pgrp["name"] == module.params['pgroup']: - pgroup = pgrp - break - - return pgroup - - -def get_pgroup_sched(module, array): - """ Get Protection Group Schedule""" - pgroup = None - - for pgrp in array.list_pgroups(schedule=True): - if pgrp["name"] == module.params['pgroup']: - pgroup = pgrp - break - - return pgroup - - -def check_pg_on_offload(module, array): - """ Check if PG already exists on offload target """ - array_name = array.get()['array_name'] - remote_pg = array_name + ":" + module.params['pgroup'] - targets = get_targets(array) - for target in targets: - remote_pgs = array.list_pgroups(pending=True, on=target) - for rpg in range(0, len(remote_pgs)): - if remote_pg == remote_pgs[rpg]['name']: - return target - return None - - -def make_pgroup(module, array): - """ Create Protection Group""" - changed = False - if module.params['target']: - api_version = array._list_available_rest_versions() - connected_targets = [] - connected_arrays = get_arrays(array) - if OFFLOAD_API_VERSION in api_version: - connected_targets = get_targets(array) - offload_name = check_pg_on_offload(module, array) - if offload_name and offload_name in module.params['target'][0:4]: - module.fail_json(msg='Protection Group {0} already exists on offload target {1}.'.format(module.params['pgroup'], offload_name)) - - connected_arrays = connected_arrays + connected_targets - if connected_arrays == []: - module.fail_json(msg='No connected targets on source array.') - if set(module.params['target'][0:4]).issubset(connected_arrays): - try: - array.create_pgroup(module.params['pgroup'], targetlist=module.params['target'][0:4]) - except Exception: - module.fail_json(msg='Creation of replicated pgroup {0} failed. {1}'.format(module.params['pgroup'], module.params['target'][0:4])) - else: - module.fail_json(msg='Check all selected targets are connected to the source array.') - else: - try: - array.create_pgroup(module.params['pgroup']) - except Exception: - module.fail_json(msg='Creation of pgroup {0} failed.'.format(module.params['pgroup'])) - try: - if module.params['target']: - array.set_pgroup(module.params['pgroup'], replicate_enabled=module.params['enabled']) - else: - array.set_pgroup(module.params['pgroup'], snap_enabled=module.params['enabled']) - except Exception: - module.fail_json(msg='Enabling pgroup {0} failed.'.format(module.params['pgroup'])) - if module.params['volume']: - try: - array.set_pgroup(module.params['pgroup'], vollist=module.params['volume']) - except Exception: - module.fail_json(msg='Adding volumes to pgroup {0} failed.'.format(module.params['pgroup'])) - if module.params['host']: - try: - array.set_pgroup(module.params['pgroup'], hostlist=module.params['host']) - except Exception: - module.fail_json(msg='Adding hosts to pgroup {0} failed.'.format(module.params['pgroup'])) - if module.params['hostgroup']: - try: - array.set_pgroup(module.params['pgroup'], hgrouplist=module.params['hostgroup']) - except Exception: - module.fail_json(msg='Adding hostgroups to pgroup {0} failed.'.format(module.params['pgroup'])) - changed = True - module.exit_json(changed=changed) - - -def update_pgroup(module, array): - """ Update Protection Group""" - changed = False - if module.params['target']: - api_version = array._list_available_rest_versions() - connected_targets = [] - connected_arrays = get_arrays(array) - - if OFFLOAD_API_VERSION in api_version: - connected_targets = get_targets(array) - offload_name = check_pg_on_offload(module, array) - if offload_name and offload_name in module.params['target'][0:4]: - module.fail_json(msg='Protection Group {0} already exists on offload target {1}.'.format(module.params['pgroup'], offload_name)) - - connected_arrays = connected_arrays + connected_targets - if connected_arrays == []: - module.fail_json(msg='No targets connected to source array.') - current_connects = array.get_pgroup(module.params['pgroup'])['targets'] - current_targets = [] - - if current_connects: - for targetcnt in range(0, len(current_connects)): - current_targets.append(current_connects[targetcnt]['name']) - - if set(module.params['target'][0:4]) != set(current_targets): - if not set(module.params['target'][0:4]).issubset(connected_arrays): - module.fail_json(msg='Check all selected targets are connected to the source array.') - try: - array.set_pgroup(module.params['pgroup'], targetlist=module.params['target'][0:4]) - changed = True - except Exception: - module.fail_json(msg='Changing targets for pgroup {0} failed.'.format(module.params['pgroup'])) - - if module.params['target'] and module.params['enabled'] != get_pgroup_sched(module, array)['replicate_enabled']: - try: - array.set_pgroup(module.params['pgroup'], replicate_enabled=module.params['enabled']) - changed = True - except Exception: - module.fail_json(msg='Changing enabled status of pgroup {0} failed.'.format(module.params['pgroup'])) - elif not module.params['target'] and module.params['enabled'] != get_pgroup_sched(module, array)['snap_enabled']: - try: - array.set_pgroup(module.params['pgroup'], snap_enabled=module.params['enabled']) - changed = True - except Exception: - module.fail_json(msg='Changing enabled status of pgroup {0} failed.'.format(module.params['pgroup'])) - - if module.params['volume'] and get_pgroup(module, array)['hosts'] is None and get_pgroup(module, array)['hgroups'] is None: - if get_pgroup(module, array)['volumes'] is None: - try: - array.set_pgroup(module.params['pgroup'], vollist=module.params['volume']) - changed = True - except Exception: - module.fail_json(msg='Adding volumes to pgroup {0} failed.'.format(module.params['pgroup'])) - else: - if not all(x in get_pgroup(module, array)['volumes'] for x in module.params['volume']): - try: - array.set_pgroup(module.params['pgroup'], vollist=module.params['volume']) - changed = True - except Exception: - module.fail_json(msg='Changing volumes in pgroup {0} failed.'.format(module.params['pgroup'])) - - if module.params['host'] and get_pgroup(module, array)['volumes'] is None and get_pgroup(module, array)['hgroups'] is None: - if not get_pgroup(module, array)['hosts'] is None: - try: - array.set_pgroup(module.params['pgroup'], hostlist=module.params['host']) - changed = True - except Exception: - module.fail_json(msg='Adding hosts to pgroup {0} failed.'.format(module.params['pgroup'])) - else: - if not all(x in get_pgroup(module, array)['hosts'] for x in module.params['host']): - try: - array.set_pgroup(module.params['pgroup'], hostlist=module.params['host']) - changed = True - except Exception: - module.fail_json(msg='Changing hosts in pgroup {0} failed.'.format(module.params['pgroup'])) - - if module.params['hostgroup'] and get_pgroup(module, array)['hosts'] is None and get_pgroup(module, array)['volumes'] is None: - if not get_pgroup(module, array)['hgroups'] is None: - try: - array.set_pgroup(module.params['pgroup'], hgrouplist=module.params['hostgroup']) - changed = True - except Exception: - module.fail_json(msg='Adding hostgroups to pgroup {0} failed.'.format(module.params['pgroup'])) - else: - if not all(x in get_pgroup(module, array)['hgroups'] for x in module.params['hostgroup']): - try: - array.set_pgroup(module.params['pgroup'], hgrouplist=module.params['hostgroup']) - changed = True - except Exception: - module.fail_json(msg='Changing hostgroups in pgroup {0} failed.'.format(module.params['pgroup'])) - - module.exit_json(changed=changed) - - -def eradicate_pgroup(module, array): - """ Eradicate Protection Group""" - changed = False - if ":" in module.params['pgroup']: - try: - target = ''.join(module.params['target']) - array.destroy_pgroup(module.params['pgroup'], on=target, eradicate=True) - changed = True - except Exception: - module.fail_json(msg='Eradicating pgroup {0} failed.'.format(module.params['pgroup'])) - else: - try: - array.destroy_pgroup(module.params['pgroup'], eradicate=True) - changed = True - except Exception: - module.fail_json(msg='Eradicating pgroup {0} failed.'.format(module.params['pgroup'])) - module.exit_json(changed=changed) - - -def delete_pgroup(module, array): - """ Delete Protection Group""" - changed = False - if ":" in module.params['pgroup']: - try: - target = ''.join(module.params['target']) - array.destroy_pgroup(module.params['pgroup'], on=target) - changed = True - except Exception: - module.fail_json(msg='Deleting pgroup {0} failed.'.format(module.params['pgroup'])) - else: - try: - array.destroy_pgroup(module.params['pgroup']) - changed = True - except Exception: - module.fail_json(msg='Deleting pgroup {0} failed.'.format(module.params['pgroup'])) - if module.params['eradicate']: - eradicate_pgroup(module, array) - - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - pgroup=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - volume=dict(type='list'), - host=dict(type='list'), - hostgroup=dict(type='list'), - target=dict(type='list'), - eradicate=dict(type='bool', default=False), - enabled=dict(type='bool', default=True), - )) - - mutually_exclusive = [['volume', 'host', 'hostgroup']] - module = AnsibleModule(argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=False) - - state = module.params['state'] - array = get_system(module) - api_version = array._list_available_rest_versions() - if ":" in module.params['pgroup'] and OFFLOAD_API_VERSION not in api_version: - module.fail_json(msg='API version does not support offload protection groups.') - - pgroup = get_pgroup(module, array) - xpgroup = get_pending_pgroup(module, array) - - if module.params['host']: - try: - for hst in module.params['host']: - array.get_host(hst) - except Exception: - module.fail_json(msg='Host {0} not found'.format(hst)) - - if module.params['hostgroup']: - try: - for hstg in module.params['hostgroup']: - array.get_hgroup(hstg) - except Exception: - module.fail_json(msg='Hostgroup {0} not found'.format(hstg)) - - if pgroup and state == 'present': - update_pgroup(module, array) - elif pgroup and state == 'absent': - delete_pgroup(module, array) - elif xpgroup and state == 'absent' and module.params['eradicate']: - eradicate_pgroup(module, array) - elif not pgroup and not xpgroup and state == 'present': - make_pgroup(module, array) - elif pgroup is None and state == 'absent': - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_pgsnap.py b/lib/ansible/modules/storage/purestorage/purefa_pgsnap.py deleted file mode 100644 index d672692eb6..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_pgsnap.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/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: purefa_pgsnap -version_added: '2.6' -short_description: Manage protection group snapshots on Pure Storage FlashArrays -description: -- Create or delete protection group snapshots on Pure Storage FlashArray. -- Recovery of replicated snapshots on the replica target array is enabled. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - name: - description: - - The name of the source protection group. - type: str - required: true - suffix: - description: - - Suffix of snapshot name. - state: - description: - - Define whether the protection group snapshot should exist or not. - Copy (added in 2.7) will create a full read/write clone of the - snapshot. - type: str - choices: [ absent, present, copy ] - default: present - eradicate: - description: - - Define whether to eradicate the snapshot on delete or leave in trash. - type: bool - default: 'no' - restore: - description: - - Restore a specific volume from a protection group snapshot. - type: str - version_added: 2.7 - overwrite: - description: - - Define whether to overwrite the target volume if it already exists. - type: bool - default: 'no' - version_added: 2.8 - target: - description: - - Volume to restore a specified volume to. - - If not supplied this will default to the volume defined in I(restore) - type: str - version_added: 2.8 - now: - description: Whether to initiate a snapshot of the protection group immediately - type: bool - default: False - version_added: 2.9 - apply_retention: - description: Apply retention schedule settings to the snapshot - type: bool - default: False - version_added: 2.9 - remote: - description: Force immeadiate snapshot to remote targets - type: bool - default: False - version_added: 2.9 -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create protection group snapshot foo.ansible - purefa_pgsnap: - name: foo - suffix: ansible - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: present - -- name: Delete and eradicate protection group snapshot named foo.snap - purefa_pgsnap: - name: foo - suffix: snap - eradicate: true - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: absent - -- name: Restore volume data from local protection group snapshot named foo.snap to volume data2 - purefa_pgsnap: - name: foo - suffix: snap - restore: data - target: data2 - overwrite: true - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: copy - -- name: Restore remote protection group snapshot arrayA:pgname.snap.data to local copy - purefa_pgsnap: - name: arrayA:pgname - suffix: snap - restore: data - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: copy - -- name: Create snapshot of existing pgroup foo with suffix and force immeadiate copy to remote targets - purefa_pgsnap: - name: pgname - suffix: force - now: True - apply_retention: True - remote: True - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: copy -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - -from datetime import datetime - - -def get_pgroup(module, array): - """Return Protection Group or None""" - try: - return array.get_pgroup(module.params['name']) - except Exception: - return None - - -def get_pgroupvolume(module, array): - """Return Protection Group Volume or None""" - try: - pgroup = array.get_pgroup(module.params['name']) - for volume in pgroup['volumes']: - if volume == module.params['restore']: - return volume - except Exception: - return None - - -def get_rpgsnapshot(module, array): - """Return iReplicated Snapshot or None""" - try: - snapname = module.params['name'] + "." + module.params['suffix'] + "." + module.params['restore'] - for snap in array.list_volumes(snap=True): - if snap['name'] == snapname: - return snapname - except Exception: - return None - - -def get_pgsnapshot(module, array): - """Return Snapshot (active or deleted) or None""" - try: - snapname = module.params['name'] + "." + module.params['suffix'] - for snap in array.get_pgroup(module.params['name'], snap=True, pending=True): - if snap['name'] == snapname: - return snapname - except Exception: - return None - - -def create_pgsnapshot(module, array): - """Create Protection Group Snapshot""" - changed = True - if not module.check_mode: - try: - if module.params['now'] and array.get_pgroup(module.params['name'])['targets'] is not None: - array.create_pgroup_snapshot(source=module.params['name'], - suffix=module.params['suffix'], - snap=True, - apply_retention=module.params['apply_retention'], - replicate_now=module.params['remote']) - else: - array.create_pgroup_snapshot(source=module.params['name'], - suffix=module.params['suffix'], - snap=True, - apply_retention=module.params['apply_retention']) - except Exception: - module.fail_json(msg="Snapshot of pgroup {0} failed.".format(module.params['name'])) - module.exit_json(changed=changed) - - -def restore_pgsnapvolume(module, array): - """Restore a Protection Group Snapshot Volume""" - changed = True - if not module.check_mode: - if ":" in module.params['name']: - if get_rpgsnapshot(module, array)is None: - module.fail_json(msg="Selected restore snapshot {0} does not exist in the Protection Group".format(module.params['restore'])) - else: - if get_pgroupvolume(module, array) is None: - module.fail_json(msg="Selected restore volume {0} does not exist in the Protection Group".format(module.params['restore'])) - volume = module.params['name'] + "." + module.params['suffix'] + "." + module.params['restore'] - try: - array.copy_volume(volume, module.params['target'], overwrite=module.params['overwrite']) - except Exception: - module.fail_json(msg="Failed to restore {0} from pgroup {1}".format(volume, module.params['name'])) - module.exit_json(changed=changed) - - -def update_pgsnapshot(module, array): - """Update Protection Group Snapshot""" - changed = True - module.exit_json(changed=changed) - - -def delete_pgsnapshot(module, array): - """ Delete Protection Group Snapshot""" - changed = True - if not module.check_mode: - snapname = module.params['name'] + "." + module.params['suffix'] - try: - array.destroy_pgroup(snapname) - if module.params['eradicate']: - try: - array.eradicate_pgroup(snapname) - except Exception: - module.fail_json(msg="Failed to eradicate pgroup {0}".format(snapname)) - except Exception: - module.fail_json(msg="Failed to delete pgroup {0}".format(snapname)) - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - name=dict(type='str', required=True), - suffix=dict(type='str'), - restore=dict(type='str'), - overwrite=dict(type='bool', default=False), - target=dict(type='str'), - eradicate=dict(type='bool', default=False), - now=dict(type='bool', default=False), - apply_retention=dict(type='bool', default=False), - remote=dict(type='bool', default=False), - state=dict(type='str', default='present', choices=['absent', 'present', 'copy']), - )) - - required_if = [('state', 'copy', ['suffix', 'restore'])] - - module = AnsibleModule(argument_spec, - required_if=required_if, - supports_check_mode=True) - - 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(".", "") - - if not module.params['target'] and module.params['restore']: - module.params['target'] = module.params['restore'] - - state = module.params['state'] - array = get_system(module) - pgroup = get_pgroup(module, array) - if pgroup is None: - module.fail_json(msg="Protection Group {0} does not exist.".format(module.params['name'])) - pgsnap = get_pgsnapshot(module, array) - - if state == 'copy': - restore_pgsnapvolume(module, array) - elif state == 'present' and not pgsnap: - create_pgsnapshot(module, array) - elif state == 'present' and pgsnap: - update_pgsnapshot(module, array) - elif state == 'absent' and pgsnap: - delete_pgsnapshot(module, array) - elif state == 'absent' and not pgsnap: - module.exit_json(changed=False) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_phonehome.py b/lib/ansible/modules/storage/purestorage/purefa_phonehome.py deleted file mode 100644 index 07def6084a..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_phonehome.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/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: purefa_phonehome -version_added: '2.9' -short_description: Enable or Disable Pure Storage FlashArray Phonehome -description: -- Enable or Disable Phonehome for a Pure Storage FlashArray. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Define state of phonehome - type: str - default: present - choices: [ present, absent ] -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Enable Phonehome - purefa_phonehome: - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Disable Phonehome - purefa_phonehome: - state: disable - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def enable_ph(module, array): - """Enable Remote Assist""" - changed = False - if array.get_phonehome()['phonehome'] != 'enabled': - try: - if not module.check_mode: - array.enable_phonehome() - changed = True - except Exception: - module.fail_json(msg='Enabling Phonehome failed') - module.exit_json(changed=changed) - - -def disable_ph(module, array): - """Disable Remote Assist""" - changed = False - if array.get_phonehome()['phonehome'] == 'enabled': - try: - if not module.check_mode: - array.disable_phonehome() - changed = True - except Exception: - module.fail_json(msg='Disabling Remote Assist failed') - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - state=dict(type='str', default='present', choices=['present', 'absent']), - )) - - module = AnsibleModule(argument_spec, - supports_check_mode=True) - - array = get_system(module) - - if module.params['state'] == 'present': - enable_ph(module, array) - else: - disable_ph(module, array) - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_ra.py b/lib/ansible/modules/storage/purestorage/purefa_ra.py deleted file mode 100644 index b3d5ec0229..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_ra.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/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: purefa_ra -version_added: '2.8' -short_description: Enable or Disable Pure Storage FlashArray Remote Assist -description: -- Enable or Disable Remote Assist for a Pure Storage FlashArray. -author: -- Pure Storage Ansible Team (@sdodsley) -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: enable - choices: [ enable, disable ] -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Enable Remote Assist port - purefa_ra: - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - register: result - -- debug: - msg: "Remote Assist: {{ result['ra_info'] }}" - -- name: Disable Remote Assist port - purefa_ra: - state: disable - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def enable_ra(module, array): - """Enable Remote Assist""" - changed = False - ra_facts = {} - if array.get_remote_assist_status()['status'] != 'enabled': - try: - ra_data = array.enable_remote_assist() - ra_facts['fa_ra'] = {'name': ra_data['name'], - 'port': ra_data['port']} - changed = True - except Exception: - module.fail_json(msg='Enabling Remote Assist failed') - else: - try: - ra_data = array.get_remote_assist_status() - ra_facts['fa_ra'] = {'name': ra_data['name'], - 'port': ra_data['port']} - except Exception: - module.fail_json(msg='Getting Remote Assist failed') - module.exit_json(changed=changed, ra_info=ra_facts) - - -def disable_ra(module, array): - """Disable Remote Assist""" - changed = False - if array.get_remote_assist_status()['status'] == 'enabled': - try: - array.disable_remote_assist() - changed = True - except Exception: - module.fail_json(msg='Disabling Remote Assist failed') - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - state=dict(type='str', default='enable', choices=['enable', 'disable']), - )) - - module = AnsibleModule(argument_spec, - supports_check_mode=False) - - array = get_system(module) - - if module.params['state'] == 'enable': - enable_ra(module, array) - else: - disable_ra(module, array) - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_smtp.py b/lib/ansible/modules/storage/purestorage/purefa_smtp.py deleted file mode 100644 index 70c07c6c82..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_smtp.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/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: purefa_smtp -version_added: '2.9' -author: - - Pure Storage ansible Team (@sdodsley) -short_description: Configure FlashArray SMTP settings -description: -- Set or erase configuration for the SMTP settings. -- If username/password are set this will always force a change as there is - no way to see if the password is different from the current SMTP configuration. -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Set or delete SMTP configuration - default: present - type: str - choices: [ absent, present ] - password: - description: - - The SMTP password. - type: str - user: - description: - - The SMTP username. - type: str - relay_host: - description: - - IPv4 or IPv6 address or FQDN. A port number may be appended. - type: str - sender_domain: - description: - - Domain name. - type: str -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Delete existing SMTP settings - purefa_smtp: - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Set SMTP settings - purefa_smtp: - sender_domain: purestorage.com - password: account_password - user: smtp_account - relay_host: 10.2.56.78:2345 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def delete_smtp(module, array): - """Delete SMTP settings""" - changed = True - if not module.check_mode: - try: - array.set_smtp(sender_domain='', username='', password='', relay_host='') - except Exception: - module.fail_json(msg='Delete SMTP settings failed') - module.exit_json(changed=changed) - - -def create_smtp(module, array): - """Set SMTP settings""" - changed = True - current_smtp = array.get_smtp() - if not module.check_mode: - if module.params['sender_domain'] and current_smtp['sender_domain'] != module.params['sender_domain']: - try: - array.set_smtp(sender_domain=module.params['sender_domain']) - changed_sender = True - except Exception: - module.fail_json(msg='Set SMTP sender domain failed.') - else: - changed_sender = False - if module.params['relay_host'] and current_smtp['relay_host'] != module.params['relay_host']: - try: - array.set_smtp(relay_host=module.params['relay_host']) - changed_relay = True - except Exception: - module.fail_json(msg='Set SMTP relay host failed.') - else: - changed_relay = False - if module.params['user']: - try: - array.set_smtp(user_name=module.params['user'], password=module.params['password']) - changed_creds = True - except Exception: - module.fail_json(msg='Set SMTP username/password failed.') - else: - changed_creds = False - changed = bool(changed_sender or changed_relay or changed_creds) - - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - state=dict(type='str', default='present', choices=['absent', 'present']), - sender_domain=dict(type='str'), - password=dict(type='str', no_log=True), - user=dict(type='str'), - relay_host=dict(type='str'), - )) - - required_together = [['user', 'password']] - - module = AnsibleModule(argument_spec, - required_together=required_together, - supports_check_mode=True) - - state = module.params['state'] - array = get_system(module) - - if state == 'absent': - delete_smtp(module, array) - elif state == 'present': - create_smtp(module, array) - else: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_snap.py b/lib/ansible/modules/storage/purestorage/purefa_snap.py deleted file mode 100644 index 6f7a776502..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_snap.py +++ /dev/null @@ -1,238 +0,0 @@ -#!/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: purefa_snap -version_added: '2.4' -short_description: Manage volume snapshots on Pure Storage FlashArrays -description: -- Create or delete volumes and volume snapshots on Pure Storage FlashArray. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - name: - description: - - The name of the source volume. - type: str - required: true - suffix: - description: - - Suffix of snapshot name. - type: str - target: - description: - - Name of target volume if creating from snapshot. - type: str - overwrite: - description: - - Define whether to overwrite existing volume when creating from snapshot. - type: bool - default: 'no' - state: - description: - - Define whether the volume snapshot should exist or not. - choices: [ absent, copy, present ] - type: str - default: present - eradicate: - description: - - Define whether to eradicate the snapshot on delete or leave in trash. - type: bool - default: 'no' -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create snapshot foo.ansible - purefa_snap: - name: foo - suffix: ansible - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: present - -- name: Create R/W clone foo_clone from snapshot foo.snap - purefa_snap: - name: foo - suffix: snap - target: foo_clone - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: copy - -- name: Overwrite existing volume foo_clone with snapshot foo.snap - purefa_snap: - name: foo - suffix: snap - target: foo_clone - overwrite: true - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: copy - -- name: Delete and eradicate snapshot named foo.snap - purefa_snap: - name: foo - suffix: snap - eradicate: true - fa_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.module_utils.pure import get_system, purefa_argument_spec - -from datetime import datetime - -try: - from purestorage import purestorage - HAS_PURESTORAGE = True -except ImportError: - HAS_PURESTORAGE = False - - -def get_volume(module, array): - """Return Volume or None""" - try: - return array.get_volume(module.params['name']) - except Exception: - return None - - -def get_target(module, array): - """Return Volume or None""" - try: - return array.get_volume(module.params['target']) - except Exception: - return None - - -def get_snapshot(module, array): - """Return Snapshot or None""" - try: - snapname = module.params['name'] + "." + module.params['suffix'] - for s in array.get_volume(module.params['name'], snap='true'): - if s['name'] == snapname: - return snapname - except Exception: - return None - - -def create_snapshot(module, array): - """Create Snapshot""" - changed = True - if not module.check_mode: - try: - array.create_snapshot(module.params['name'], suffix=module.params['suffix']) - except Exception: - changed = False - module.exit_json(changed=changed) - - -def create_from_snapshot(module, array): - """Create Volume from Snapshot""" - source = module.params['name'] + "." + module.params['suffix'] - tgt = get_target(module, array) - if tgt is None: - changed = True - if not module.check_mode: - array.copy_volume(source, - module.params['target']) - elif tgt is not None and module.params['overwrite']: - changed = True - if not module.check_mode: - array.copy_volume(source, - module.params['target'], - overwrite=module.params['overwrite']) - elif tgt is not None and not module.params['overwrite']: - changed = False - module.exit_json(changed=changed) - - -def update_snapshot(module, array): - """Update Snapshot""" - changed = False - module.exit_json(changed=changed) - - -def delete_snapshot(module, array): - """ Delete Snapshot""" - changed = True - if not module.check_mode: - snapname = module.params['name'] + "." + module.params['suffix'] - try: - array.destroy_volume(snapname) - if module.params['eradicate']: - try: - array.eradicate_volume(snapname) - except Exception: - changed = False - except Exception: - changed = False - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - name=dict(type='str', required=True), - suffix=dict(type='str'), - target=dict(type='str'), - overwrite=dict(type='bool', default=False), - eradicate=dict(type='bool', default=False), - state=dict(type='str', default='present', choices=['absent', 'copy', 'present']), - )) - - required_if = [('state', 'copy', ['target', 'suffix'])] - - module = AnsibleModule(argument_spec, - required_if=required_if, - supports_check_mode=True) - - if not HAS_PURESTORAGE: - module.fail_json(msg='purestorage sdk is required for this module in volume') - - 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'] - array = get_system(module) - volume = get_volume(module, array) - target = get_target(module, array) - snap = get_snapshot(module, array) - - if state == 'present' and volume and not snap: - create_snapshot(module, array) - elif state == 'present' and volume and snap: - update_snapshot(module, array) - elif state == 'present' and not volume: - update_snapshot(module, array) - elif state == 'copy' and snap: - create_from_snapshot(module, array) - elif state == 'copy' and not snap: - update_snapshot(module, array) - elif state == 'absent' and snap: - delete_snapshot(module, array) - elif state == 'absent' and not snap: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_snmp.py b/lib/ansible/modules/storage/purestorage/purefa_snmp.py deleted file mode 100644 index c2479de861..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_snmp.py +++ /dev/null @@ -1,333 +0,0 @@ -#!/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: purefa_snmp -version_added: '2.9' -short_description: Configure FlashArray SNMP Managers -description: -- Manage SNMP managers on a Pure Storage FlashArray. -- Changing of a named SNMP managers version is not supported. -- 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) -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. - required: True - 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 ] - default: v2c - 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.fa -''' - -EXAMPLES = r''' -- name: Delete existing SNMP manager - purefa_snmp: - name: manager1 - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create v2c SNMP manager - purefa_snmp: - name: manager1 - community: public - host: 10.21.22.23 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Create v3 SNMP manager - purefa_snmp: - name: manager2 - version: v3 - auth_protocol: MD5 - auth_passphrase: password - host: 10.21.22.23 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Update existing SNMP manager - purefa_snmp: - name: manager1 - community: private - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def update_manager(module, array): - """Update SNMP Manager""" - changed = True - if not module.check_mode: - try: - mgr = array.get_snmp_manager(module.params['name']) - except Exception: - module.fail_json(msg="Failed to get current configuration for SNMP manager {0}.".format(module.params['name'])) - if mgr['version'] != module.params['version']: - module.fail_json(msg="Changing an SNMP managers version is not supported.") - elif module.params['version'] == "v2c": - try: - array.set_snmp_manager(module.params['name'], - community=module.params['community'], - notification=module.params['notification'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to update SNMP manager {0}.".format(module.params['name'])) - else: - if module.params['auth_protocol'] and module.params['privacy_protocol']: - try: - array.set_snmp_manager(module.params['name'], - auth_passphrase=module.params['auth_passphrase'], - auth_protocol=module.params['auth_protocol'], - privacy_passphrase=module.params['privacy_passphrase'], - privacy_protocol=module.params['privacy_protocol'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to update SNMP manager {0}.".format(module.params['name'])) - elif module.params['auth_protocol'] and not module.params['privacy_protocol']: - try: - array.set_snmp_manager(module.params['name'], - version=module.params['version'], - auth_passphrase=module.params['auth_passphrase'], - auth_protocol=module.params['auth_protocol'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to update SNMP manager {0}.".format(module.params['name'])) - elif not module.params['auth_protocol'] and module.params['privacy_protocol']: - try: - array.set_snmp_manager(module.params['name'], - version=module.params['version'], - privacy_passphrase=module.params['privacy_passphrase'], - privacy_protocol=module.params['privacy_protocol'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to update SNMP manager {0}.".format(module.params['name'])) - elif not module.params['auth_protocol'] and not module.params['privacy_protocol']: - try: - array.set_snmp_manager(module.params['name'], - version=module.params['version'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to update SNMP manager {0}.".format(module.params['name'])) - else: - module.fail_json(msg="Invalid parameters selected in update. Please raise issue in Ansible GitHub") - - module.exit_json(changed=changed) - - -def delete_manager(module, array): - """Delete SNMP Manager""" - changed = True - if not module.check_mode: - try: - array.delete_snmp_manager(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, array): - """Create SNMP Manager""" - changed = True - if not module.check_mode: - if module.params['version'] == "v2c": - try: - array.create_snmp_manager(module.params['name'], - version=module.params['version'], - community=module.params['community'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to create SNMP manager {0}.".format(module.params['name'])) - else: - if module.params['auth_protocol'] and module.params['privacy_protocol']: - try: - array.create_snmp_manager(module.params['name'], - 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'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to create SNMP manager {0}.".format(module.params['name'])) - elif module.params['auth_protocol'] and not module.params['privacy_protocol']: - try: - array.create_snmp_manager(module.params['name'], - version=module.params['version'], - auth_passphrase=module.params['auth_passphrase'], - auth_protocol=module.params['auth_protocol'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to create SNMP manager {0}.".format(module.params['name'])) - elif not module.params['auth_protocol'] and module.params['privacy_protocol']: - try: - array.create_snmp_manager(module.params['name'], - version=module.params['version'], - privacy_passphrase=module.params['privacy_passphrase'], - privacy_protocol=module.params['privacy_protocol'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to create SNMP manager {0}.".format(module.params['name'])) - elif not module.params['auth_protocol'] and not module.params['privacy_protocol']: - try: - array.create_snmp_manager(module.params['name'], - version=module.params['version'], - notification=module.params['notification'], - user=module.params['user'], - host=module.params['host'] - ) - except Exception: - module.fail_json(msg="Failed to create SNMP manager {0}.".format(module.params['name'])) - else: - module.fail_json(msg="Invalid parameters selected in create. Please raise issue in Ansible GitHub") - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - name=dict(type='str', required=True), - host=dict(type='str', required=True), - 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', default='v2c', 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'] - array = get_system(module) - mgr_configured = False - mgrs = array.list_snmp_managers() - for mgr in range(0, len(mgrs)): - if mgrs[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, array) - elif mgr_configured and state == 'present': - update_manager(module, array) - elif not mgr_configured and state == 'present': - create_manager(module, array) - else: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_syslog.py b/lib/ansible/modules/storage/purestorage/purefa_syslog.py deleted file mode 100644 index b94c398de0..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_syslog.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/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: purefa_syslog -version_added: '2.9' -short_description: Configure Pure Storage FlashArray syslog settings -description: -- Configure syslog configuration for Pure Storage FlashArrays. -- Add or delete an individual syslog server to the existing - list of serves. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Create or delete syslog servers configuration - default: present - type: str - choices: [ absent, present ] - protocol: - description: - - Protocol which server uses - required: true - 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, IPv6 or FQDN. - An invalid IP addresses will cause the module to fail. - No validation is performed for FQDNs. - type: str - required: true -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Delete existing syslog server entries - purefa_syslog: - address: syslog1.com - protocol: tcp - state: absent - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Set array syslog servers - purefa_syslog: - state: present - address: syslog1.com - protocol: udp - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -def delete_syslog(module, array): - """Delete Syslog Server""" - 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 = array.get(syslogserver=True)['syslogserver'] - - if address_list: - for address in range(0, len(address_list)): - if address_list[address] == full_address: - del address_list[address] - try: - array.set(syslogserver=address_list) - changed = True - break - except Exception: - module.fail_json(msg='Failed to remove syslog server: {0}'.format(full_address)) - - module.exit_json(changed=changed) - - -def add_syslog(module, array): - """Add Syslog Server""" - 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 = array.get(syslogserver=True)['syslogserver'] - exists = False - - if address_list: - for address in range(0, len(address_list)): - if address_list[address] == full_address: - exists = True - break - if not exists: - try: - address_list.append(full_address) - array.set(syslogserver=address_list) - changed = True - except Exception: - module.fail_json(msg='Failed to add syslog server: {0}'.format(full_address)) - - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - address=dict(type='str', required=True), - protocol=dict(type='str', choices=['tcp', 'tls', 'udp'], required=True), - port=dict(type='str'), - state=dict(type='str', default='present', choices=['absent', 'present']), - )) - - module = AnsibleModule(argument_spec, - supports_check_mode=False) - - array = get_system(module) - - if module.params['state'] == 'absent': - delete_syslog(module, array) - else: - add_syslog(module, array) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_user.py b/lib/ansible/modules/storage/purestorage/purefa_user.py deleted file mode 100644 index 5e687c3851..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_user.py +++ /dev/null @@ -1,222 +0,0 @@ -#!/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: purefa_user -version_added: '2.8' -short_description: Create, modify or delete FlashArray local user account -description: -- Create, modify or delete local users on a Pure Storage FlashArray. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - state: - description: - - Create, delete or update local user account - default: present - type: str - choices: [ absent, present ] - name: - description: - - The name of the local user account - type: str - role: - description: - - Sets the local user's access level to the array - type: str - choices: [ readonly, storage_admin, array_admin ] - password: - description: - - Password for the local user. - type: str - old_password: - description: - - If changing an existing password, you must provide the old password for security - type: str - api: - description: - - Define whether to create an API token for this user - - Token can be exposed using the I(debug) module - type: bool - default: false -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create new user ansible with API token - purefa_user: - name: ansible - password: apassword - role: storage_admin - api: true - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - register: result - - debug: - msg: "API Token: {{ result['user_info']['user_api'] }}" - -- name: Change role type for existing user - purefa_user: - name: ansible - role: array_admin - state: update - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Change password type for existing user (NOT IDEMPOTENT) - purefa_user: - name: ansible - password: anewpassword - old_password: apassword - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Change API token for existing user - purefa_user: - name: ansible - api: true - state: update - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - register: result - - debug: - msg: "API Token: {{ result['user_info']['user_api'] }}" -''' - -RETURN = r''' -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - -MIN_REQUIRED_API_VERSION = '1.14' - - -def get_user(module, array): - """Return Local User Account or None""" - user = None - users = array.list_admins() - for acct in range(0, len(users)): - if users[acct]['name'] == module.params['name']: - user = users[acct] - return user - - -def create_user(module, array): - """Create or Update Local User Account""" - changed = False - user = get_user(module, array) - role = module.params['role'] - api_changed = False - role_changed = False - passwd_changed = False - user_token = {} - if not user: - try: - if not role: - role = 'readonly' - array.create_admin(module.params['name'], role=role, - password=module.params['password']) - if module.params['api']: - try: - user_token['user_api'] = array.create_api_token(module.params['name'])['api_token'] - except Exception: - array.delete_user(module.params['name']) - module.fail_json(msg='Local User {0}: Creation failed'.format(module.params['name'])) - changed = True - except Exception: - module.fail_json(msg='Local User {0}: Creation failed'.format(module.params['name'])) - else: - if module.params['password'] and not module.params['old_password']: - changed = False - module.exit_json(changed=changed) - if module.params['password'] and module.params['old_password']: - if module.params['old_password'] and (module.params['password'] != module.params['old_password']): - try: - array.set_admin(module.params['name'], password=module.params['password'], - old_password=module.params['old_password']) - passwd_changed = True - except Exception: - module.fail_json(msg='Local User {0}: Password reset failed. ' - 'Check old password.'.format(module.params['name'])) - else: - module.fail_json(msg='Local User Account {0}: Password change failed - ' - 'Check both old and new passwords'.format(module.params['name'])) - if module.params['api']: - try: - if not array.get_api_token(module.params['name'])['api_token'] is None: - array.delete_api_token(module.params['name']) - user_token['user_api'] = array.create_api_token(module.params['name'])['api_token'] - api_changed = True - except Exception: - module.fail_json(msg='Local User {0}: API token change failed'.format(module.params['name'])) - if module.params['role'] != user['role']: - try: - array.set_admin(module.params['name'], role=module.params['role']) - role_changed = True - except Exception: - module.fail_json(msg='Local User {0}: Role changed failed'.format(module.params['name'])) - if passwd_changed or role_changed or api_changed: - changed = True - module.exit_json(changed=changed, user_info=user_token) - - -def delete_user(module, array): - """Delete Local User Account""" - changed = False - if get_user(module, array): - try: - array.delete_admin(module.params['name']) - changed = True - 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 = purefa_argument_spec() - argument_spec.update(dict( - name=dict(required=True, type='str'), - role=dict(type='str', choices=['readonly', 'storage_admin', 'array_admin']), - state=dict(type='str', default='present', choices=['absent', 'present']), - password=dict(type='str', no_log=True), - old_password=dict(type='str', no_log=True), - api=dict(type='bool', default=False), - )) - - module = AnsibleModule(argument_spec, - supports_check_mode=False) - - state = module.params['state'] - array = get_system(module) - api_version = array._list_available_rest_versions() - - if MIN_REQUIRED_API_VERSION not in api_version: - module.fail_json(msg='FlashArray REST version not supported. ' - 'Minimum version required: {0}'.format(MIN_REQUIRED_API_VERSION)) - - if state == 'absent': - delete_user(module, array) - elif state == 'present': - create_user(module, array) - else: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_vg.py b/lib/ansible/modules/storage/purestorage/purefa_vg.py deleted file mode 100644 index 2bb3591b40..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_vg.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/python -# -*- 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 - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: purefa_vg -version_added: '2.9' -short_description: Manage volume groups on Pure Storage FlashArrays -description: -- Create, delete or modify volume groups on Pure Storage FlashArrays. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - vgroup: - description: - - The name of the volume group. - type: str - required: true - state: - description: - - Define whether the volume group should exist or not. - type: str - default: present - choices: [ absent, present ] - eradicate: - description: - - Define whether to eradicate the volume group on delete and leave in trash. - type : bool - default: 'no' -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create new volume group - purefa_vg: - vgroup: foo - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Destroy volume group - purefa_vg: - vgroup: foo - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: absent - -- name: Recover deleted volume group - purefa_vg: - vgroup: foo - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - -- name: Destroy and Eradicate volume group - purefa_vg: - vgroup: foo - eradicate: true - fa_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.module_utils.pure import get_system, purefa_argument_spec - - -VGROUP_API_VERSION = '1.13' - - -def get_pending_vgroup(module, array): - """ Get Deleted Volume Group""" - vgroup = None - for vgrp in array.list_vgroups(pending=True): - if vgrp["name"] == module.params['vgroup'] and vgrp['time_remaining']: - vgroup = vgrp - break - - return vgroup - - -def get_vgroup(module, array): - """ Get Volume Group""" - vgroup = None - for vgrp in array.list_vgroups(): - if vgrp["name"] == module.params['vgroup']: - vgroup = vgrp - break - - return vgroup - - -def make_vgroup(module, array): - """ Create Volume Group""" - changed = True - if not module.check_mode: - try: - array.create_vgroup(module.params['vgroup']) - except Exception: - module.fail_json(msg='creation of volume group {0} failed.'.format(module.params['vgroup'])) - - module.exit_json(changed=changed) - - -def recover_vgroup(module, array): - """ Recover Volume Group""" - changed = True - if not module.check_mode: - try: - array.recover_vgroup(module.params['vgroup']) - except Exception: - module.fail_json(msg='Recovery of volume group {0} failed.'.format(module.params['vgroup'])) - - module.exit_json(changed=changed) - - -def eradicate_vgroup(module, array): - """ Eradicate Volume Group""" - changed = True - if not module.check_mode: - try: - array.eradicate_vgroup(module.params['vgroup']) - except Exception: - module.fail_json(msg='Eradicating vgroup {0} failed.'.format(module.params['vgroup'])) - module.exit_json(changed=changed) - - -def delete_vgroup(module, array): - """ Delete Volume Group""" - changed = True - if not module.check_mode: - try: - array.destroy_vgroup(module.params['vgroup']) - except Exception: - module.fail_json(msg='Deleting vgroup {0} failed.'.format(module.params['vgroup'])) - if module.params['eradicate']: - eradicate_vgroup(module, array) - - module.exit_json(changed=changed) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - vgroup=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - eradicate=dict(type='bool', default=False), - )) - - module = AnsibleModule(argument_spec, - supports_check_mode=True) - - state = module.params['state'] - array = get_system(module) - api_version = array._list_available_rest_versions() - if VGROUP_API_VERSION not in api_version: - module.fail_json(msg='API version does not support volume groups.') - - vgroup = get_vgroup(module, array) - xvgroup = get_pending_vgroup(module, array) - - if xvgroup and state == 'present': - recover_vgroup(module, array) - elif vgroup and state == 'absent': - delete_vgroup(module, array) - elif xvgroup and state == 'absent' and module.params['eradicate']: - eradicate_vgroup(module, array) - elif not vgroup and not xvgroup and state == 'present': - make_vgroup(module, array) - elif vgroup is None and state == 'absent': - module.exit_json(changed=False) - - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/storage/purestorage/purefa_volume.py b/lib/ansible/modules/storage/purestorage/purefa_volume.py deleted file mode 100644 index 9749125928..0000000000 --- a/lib/ansible/modules/storage/purestorage/purefa_volume.py +++ /dev/null @@ -1,498 +0,0 @@ -#!/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: purefa_volume -version_added: '2.4' -short_description: Manage volumes on Pure Storage FlashArrays -description: -- Create, delete or extend the capacity of a volume on Pure Storage FlashArray. -author: -- Pure Storage Ansible Team (@sdodsley) -options: - name: - description: - - The name of the volume. - type: str - required: true - target: - description: - - The name of the target volume, if copying. - type: str - state: - description: - - Define whether the volume should exist or not. - default: present - choices: [ absent, present ] - type: str - eradicate: - description: - - Define whether to eradicate the volume on delete or leave in trash. - type: bool - default: 'no' - overwrite: - description: - - Define whether to overwrite a target volume if it already exists. - type: bool - default: 'no' - size: - description: - - Volume size in M, G, T or P units. - type: str - bw_qos: - description: - - Bandwidth limit for volume in M or G units. - M will set MB/s - G will set GB/s - To clear an existing QoS setting use 0 (zero) - version_added: '2.8' - type: str - aliases: [ qos ] - iops_qos: - description: - - IOPs limit for volume - use value or K or M - K will mean 1000 - M will mean 1000000 - To clear an existing IOPs setting use 0 (zero) - version_added: '2.10' - type: str -extends_documentation_fragment: -- purestorage.fa -''' - -EXAMPLES = r''' -- name: Create new volume named foo with a QoS limit - purefa_volume: - name: foo - size: 1T - bw_qos: 58M - iops_qos: 23K - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: present - -- name: Extend the size of an existing volume named foo - purefa_volume: - name: foo - size: 2T - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: present - -- name: Delete and eradicate volume named foo - purefa_volume: - name: foo - eradicate: yes - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: absent - -- name: Create clone of volume bar named foo - purefa_volume: - name: foo - target: bar - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: present - -- name: Overwrite volume bar with volume foo - purefa_volume: - name: foo - target: bar - overwrite: yes - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: present - -- name: Clear volume QoS from volume foo - purefa_volume: - name: foo - bw_qos: 0 - iops_qos: 0 - fa_url: 10.10.10.2 - api_token: e31060a7-21fc-e277-6240-25983c6c4592 - state: present -''' - -RETURN = r''' -volume: - description: A dictionary describing the changed volume. Only some - attributes below will be returned with various actions. - type: dict - returned: success - contains: - source: - description: Volume name of source volume used for volume copy - type: str - serial: - description: Volume serial number - type: str - sample: '361019ECACE43D83000120A4' - created: - description: Volume creation time - type: str - sample: '2019-03-13T22:49:24Z' - name: - description: Volume name - type: str - size: - description: Volume size in bytes - type: int - bandwidth_limit: - description: Volume bandwidth limit in bytes/sec - type: int - iops_limit: - description: Volume IOPs limit - type: int -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.pure import get_system, purefa_argument_spec - - -QOS_API_VERSION = "1.14" -VGROUPS_API_VERSION = "1.13" -POD_API_VERSION = "1.13" -IOPS_API_VERSION = "1.17" - - -def human_to_bytes(size): - """Given a human-readable byte string (e.g. 2G, 30M), - return the number of bytes. Will return 0 if the argument has - unexpected form. - """ - bytes = size[:-1] - unit = size[-1] - if bytes.isdigit(): - bytes = int(bytes) - if unit == 'P': - bytes *= 1125899906842624 - elif unit == 'T': - bytes *= 1099511627776 - elif unit == 'G': - bytes *= 1073741824 - elif unit == 'M': - bytes *= 1048576 - elif unit == 'K': - bytes *= 1024 - else: - bytes = 0 - else: - bytes = 0 - return bytes - - -def human_to_real(iops): - """Given a human-readable IOPs string (e.g. 2K, 30M), - return the real number. Will return 0 if the argument has - unexpected form. - """ - digit = iops[:-1] - unit = iops[-1] - if digit.isdigit(): - digit = int(digit) - if unit == 'M': - digit *= 1000000 - elif unit == 'K': - digit *= 1000 - else: - digit = 0 - else: - digit = 0 - return digit - - -def get_volume(module, array): - """Return Volume or None""" - try: - return array.get_volume(module.params['name']) - except Exception: - return None - - -def get_destroyed_volume(module, array): - """Return Destroyed Volume or None""" - try: - return bool(array.get_volume(module.params['name'], pending=True)['time_remaining'] != '') - except Exception: - return False - - -def get_target(module, array): - """Return Volume or None""" - try: - return array.get_volume(module.params['target']) - except Exception: - return None - - -def check_vgroup(module, array): - """Check is the requested VG to create volume in exists""" - vg_exists = False - api_version = array._list_available_rest_versions() - if VGROUPS_API_VERSION in api_version: - vg_name = module.params["name"].split("/")[0] - try: - vgs = array.list_vgroups() - except Exception: - module.fail_json(msg="Failed to get volume groups list. Check array.") - for vgroup in range(0, len(vgs)): - if vg_name == vgs[vgroup]['name']: - vg_exists = True - break - else: - module.fail_json(msg="VG volumes are not supported. Please upgrade your FlashArray.") - return vg_exists - - -def check_pod(module, array): - """Check is the requested pod to create volume in exists""" - pod_exists = False - api_version = array._list_available_rest_versions() - if POD_API_VERSION in api_version: - pod_name = module.params["name"].split("::")[0] - try: - pods = array.list_pods() - except Exception: - module.fail_json(msg="Failed to get pod list. Check array.") - for pod in range(0, len(pods)): - if pod_name == pods[pod]['name']: - pod_exists = True - break - else: - module.fail_json(msg="Pod volumes are not supported. Please upgrade your FlashArray.") - return pod_exists - - -def create_volume(module, array): - """Create Volume""" - changed = True - volfact = [] - if not module.check_mode: - if "/" in module.params['name'] and not check_vgroup(module, array): - module.fail_json(msg="Failed to create volume {0}. Volume Group does not exist.".format(module.params["name"])) - if "::" in module.params['name'] and not check_pod(module, array): - module.fail_json(msg="Failed to create volume {0}. Poid does not exist".format(module.params["name"])) - api_version = array._list_available_rest_versions() - if module.params['bw_qos'] or module.params['iops_qos']: - if module.params['bw_qos'] and QOS_API_VERSION in api_version or module.params['iops_qos'] and IOPS_API_VERSION in api_version: - if module.params['bw_qos'] and not module.params['iops_qos']: - if 549755813888 >= int(human_to_bytes(module.params['bw_qos'])) >= 1048576: - try: - volfact = array.create_volume(module.params['name'], - module.params['size'], - bandwidth_limit=module.params['bw_qos']) - except Exception: - module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name'])) - else: - module.fail_json(msg='Bandwidth QoS value {0} out of range.'.format(module.params['bw_qos'])) - elif module.params['iops_qos'] and not module.params['bw_qos']: - if 100000000 >= int(human_to_real(module.params['iops_qos'])) >= 100: - try: - volfact = array.create_volume(module.params['name'], - module.params['size'], - iops_limit=module.params['iops_qos']) - except Exception: - module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name'])) - else: - module.fail_json(msg='IOPs QoS value {0} out of range.'.format(module.params['iops_qos'])) - else: - bw_qos_size = int(human_to_bytes(module.params['bw_qos'])) - if 100000000 >= int(human_to_real(module.params['iops_qos'])) >= 100 and 549755813888 >= bw_qos_size >= 1048576: - try: - volfact = array.create_volume(module.params['name'], - module.params['size'], - iops_limit=module.params['iops_qos'], - bandwidth_limit=module.params['bw_qos']) - except Exception: - module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name'])) - else: - module.fail_json(msg='IOPs or Bandwidth QoS value out of range.') - - else: - try: - volfact = array.create_volume(module.params['name'], module.params['size']) - except Exception: - module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name'])) - - module.exit_json(changed=changed, volume=volfact) - - -def copy_from_volume(module, array): - """Create Volume Clone""" - changed = True - volfact = [] - if not module.check_mode: - tgt = get_target(module, array) - - if tgt is None: - try: - volfact = array.copy_volume(module.params['name'], - module.params['target']) - except Exception: - module.fail_json(msg='Copy volume {0} to volume {1} failed.'.format(module.params['name'], - module.params['target'])) - elif tgt is not None and module.params['overwrite']: - try: - volfact = array.copy_volume(module.params['name'], - module.params['target'], - overwrite=module.params['overwrite']) - except Exception: - module.fail_json(msg='Copy volume {0} to volume {1} failed.'.format(module.params['name'], - module.params['target'])) - - module.exit_json(changed=changed, volume=volfact) - - -def update_volume(module, array): - """Update Volume size and/or QoS""" - changed = True - volfact = [] - if not module.check_mode: - change = False - api_version = array._list_available_rest_versions() - vol = array.get_volume(module.params['name']) - vol_qos = array.get_volume(module.params['name'], qos=True) - if QOS_API_VERSION in api_version: - if vol_qos['bandwidth_limit'] is None: - vol_qos['bandwidth_limit'] = 0 - if IOPS_API_VERSION in api_version: - if vol_qos['iops_limit'] is None: - vol_qos['iops_limit'] = 0 - if module.params['size']: - if human_to_bytes(module.params['size']) != vol['size']: - if human_to_bytes(module.params['size']) > vol['size']: - try: - volfact = array.extend_volume(module.params['name'], module.params['size']) - change = True - except Exception: - module.fail_json(msg='Volume {0} resize failed.'.format(module.params['name'])) - if module.params['bw_qos'] and QOS_API_VERSION in api_version: - if human_to_bytes(module.params['bw_qos']) != vol_qos['bandwidth_limit']: - if module.params['bw_qos'] == '0': - try: - volfact = array.set_volume(module.params['name'], bandwidth_limit='') - change = True - except Exception: - module.fail_json(msg='Volume {0} Bandwidth QoS removal failed.'.format(module.params['name'])) - elif 549755813888 >= int(human_to_bytes(module.params['bw_qos'])) >= 1048576: - try: - volfact = array.set_volume(module.params['name'], - bandwidth_limit=module.params['bw_qos']) - change = True - except Exception: - module.fail_json(msg='Volume {0} Bandwidth QoS change failed.'.format(module.params['name'])) - else: - module.fail_json(msg='Bandwidth QoS value {0} out of range.'.format(module.params['bw_qos'])) - if module.params['iops_qos'] and IOPS_API_VERSION in api_version: - if human_to_real(module.params['iops_qos']) != vol_qos['iops_limit']: - if module.params['iops_qos'] == '0': - try: - volfact = array.set_volume(module.params['name'], iops_limit='') - change = True - except Exception: - module.fail_json(msg='Volume {0} IOPs QoS removal failed.'.format(module.params['name'])) - elif 100000000 >= int(human_to_real(module.params['iops_qos'])) >= 100: - try: - volfact = array.set_volume(module.params['name'], - iops_limit=module.params['iops_qos']) - except Exception: - module.fail_json(msg='Volume {0} IOPs QoS change failed.'.format(module.params['name'])) - else: - module.fail_json(msg='Bandwidth QoS value {0} out of range.'.format(module.params['bw_qos'])) - - module.exit_json(changed=change, volume=volfact) - - module.exit_json(changed=changed) - - -def delete_volume(module, array): - """ Delete Volume""" - changed = True - volfact = [] - if not module.check_mode: - try: - array.destroy_volume(module.params['name']) - if module.params['eradicate']: - try: - volfact = array.eradicate_volume(module.params['name']) - except Exception: - module.fail_json(msg='Eradicate volume {0} failed.'.format(module.params['name'])) - except Exception: - module.fail_json(msg='Delete volume {0} failed.'.format(module.params['name'])) - module.exit_json(changed=changed, volume=volfact) - - -def eradicate_volume(module, array): - """ Eradicate Deleted Volume""" - changed = True - volfact = [] - if not module.check_mode: - if module.params['eradicate']: - try: - array.eradicate_volume(module.params['name']) - except Exception: - module.fail_json(msg='Eradication of volume {0} failed'.format(module.params['name'])) - module.exit_json(changed=changed, volume=volfact) - - -def main(): - argument_spec = purefa_argument_spec() - argument_spec.update(dict( - name=dict(type='str', required=True), - target=dict(type='str'), - overwrite=dict(type='bool', default=False), - eradicate=dict(type='bool', default=False), - state=dict(type='str', default='present', choices=['absent', 'present']), - bw_qos=dict(type='str', aliases=['qos']), - iops_qos=dict(type='str'), - size=dict(type='str'), - )) - - mutually_exclusive = [['size', 'target'], ['qos', 'target']] - - module = AnsibleModule(argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - size = module.params['size'] - bw_qos = module.params['bw_qos'] - iops_qos = module.params['iops_qos'] - state = module.params['state'] - array = get_system(module) - volume = get_volume(module, array) - if not volume: - destroyed = get_destroyed_volume(module, array) - target = get_target(module, array) - - if state == 'present' and not volume and size: - create_volume(module, array) - elif state == 'present' and volume and (size or bw_qos or iops_qos): - update_volume(module, array) - elif state == 'present' and volume and target: - copy_from_volume(module, array) - elif state == 'present' and volume and not target: - copy_from_volume(module, array) - elif state == 'absent' and volume: - delete_volume(module, array) - elif state == 'absent' and destroyed: - eradicate_volume(module, array) - elif state == 'present' and not volume or not size: - module.exit_json(changed=False) - elif state == 'absent' and not volume: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index ef3fc3cf0a..78f741bf38 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -993,40 +993,6 @@ lib/ansible/modules/source_control/git.py validate-modules:parameter-type-not-in lib/ansible/modules/source_control/subversion.py validate-modules:doc-required-mismatch lib/ansible/modules/source_control/subversion.py validate-modules:parameter-type-not-in-doc lib/ansible/modules/source_control/subversion.py validate-modules:undocumented-parameter -lib/ansible/modules/storage/purestorage/purefa_alert.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_arrayname.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_banner.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_connect.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_dns.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_dns.py validate-modules:parameter-list-no-elements -lib/ansible/modules/storage/purestorage/purefa_ds.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_ds.py validate-modules:parameter-list-no-elements -lib/ansible/modules/storage/purestorage/purefa_dsrole.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_dsrole.py validate-modules:parameter-type-not-in-doc -lib/ansible/modules/storage/purestorage/purefa_hg.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_hg.py validate-modules:parameter-list-no-elements -lib/ansible/modules/storage/purestorage/purefa_host.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_host.py validate-modules:parameter-list-no-elements -lib/ansible/modules/storage/purestorage/purefa_info.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_info.py validate-modules:parameter-list-no-elements -lib/ansible/modules/storage/purestorage/purefa_info.py validate-modules:return-syntax-error -lib/ansible/modules/storage/purestorage/purefa_ntp.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_ntp.py validate-modules:parameter-list-no-elements -lib/ansible/modules/storage/purestorage/purefa_offload.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_pg.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_pg.py validate-modules:parameter-list-no-elements -lib/ansible/modules/storage/purestorage/purefa_pgsnap.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_pgsnap.py validate-modules:parameter-type-not-in-doc -lib/ansible/modules/storage/purestorage/purefa_phonehome.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_ra.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_smtp.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_snap.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_snmp.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_syslog.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_user.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_vg.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_volume.py validate-modules:doc-required-mismatch -lib/ansible/modules/storage/purestorage/purefa_volume.py validate-modules:mutually_exclusive-unknown lib/ansible/modules/storage/purestorage/purefb_ds.py validate-modules:doc-required-mismatch lib/ansible/modules/storage/purestorage/purefb_ds.py validate-modules:parameter-list-no-elements lib/ansible/modules/storage/purestorage/purefb_dsrole.py validate-modules:doc-required-mismatch -- cgit v1.2.1