mirror of https://github.com/ansible/ansible.git
Migrated to purestorage.flasharray
parent
7449ec1546
commit
d9920706d7
@ -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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
options:
|
||||
state:
|
||||
description: Set ot delete the MOTD
|
||||
default: present
|
||||
type: str
|
||||
choices: [ present, absent ]
|
||||
banner:
|
||||
description: Banner text, or MOTD, to use
|
||||
type: str
|
||||
default: "Welcome to the machine..."
|
||||
extends_documentation_fragment:
|
||||
- purestorage.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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
File diff suppressed because it is too large
Load Diff
@ -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) <pure-ansible-team@purestorage.com>
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Create or delete NTP servers configuration
|
||||
type: str
|
||||
default: present
|
||||
choices: [ absent, present ]
|
||||
ntp_servers:
|
||||
type: list
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Define state of remote assist
|
||||
- When set to I(enable) the RA port can be exposed using the
|
||||
I(debug) module.
|
||||
type: str
|
||||
default: 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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of SNMP Manager
|
||||
required: True
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Create or delete SNMP manager
|
||||
type: str
|
||||
default: present
|
||||
choices: [ absent, present ]
|
||||
auth_passphrase:
|
||||
type: str
|
||||
description:
|
||||
- SNMPv3 only. Passphrase of 8 - 32 characters.
|
||||
auth_protocol:
|
||||
type: str
|
||||
description:
|
||||
- SNMP v3 only. Hash algorithm to use
|
||||
choices: [ MD5, SHA ]
|
||||
community:
|
||||
type: str
|
||||
description:
|
||||
- SNMP v2c only. Manager community ID. Between 1 and 32 characters long.
|
||||
host:
|
||||
type: str
|
||||
description:
|
||||
- IPv4 or IPv6 address or FQDN to send trap messages to.
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
@ -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) <pure-ansible-team@purestorage.com>
|
||||
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()
|
Loading…
Reference in New Issue