diff --git a/lib/ansible/modules/storage/purestorage/purefa_pg.py b/lib/ansible/modules/storage/purestorage/purefa_pg.py index d287297f13c..9a7445adae8 100644 --- a/lib/ansible/modules/storage/purestorage/purefa_pg.py +++ b/lib/ansible/modules/storage/purestorage/purefa_pg.py @@ -18,6 +18,8 @@ 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. author: - Simon Dodsley (@sdodsley) options: @@ -49,17 +51,34 @@ options: - Define whether to enabled snapshots for the protection group. type : bool default: 'yes' + target: + description: + - List of remote arrays for replication protection group to connect to. + - Note that all replicated protection groups are asynchronous. + - Target arrays must already be connected to the source array. + - Maximum number of targets per Portection Group is 4, assuming your + configuration suppors this. + version_added: '2.8' extends_documentation_fragment: - purestorage.fa ''' EXAMPLES = r''' -- name: Create new protection group +- 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 protection group with snapshots disabled purefa_pg: pgroup: foo @@ -93,12 +112,13 @@ EXAMPLES = r''' fa_url: 10.10.10.2 api_token: e31060a7-21fc-e277-6240-25983c6c4592 -- name: Create protection group for volumes +- 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 ''' @@ -110,53 +130,166 @@ 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_arrays(array): + """ Get Connected Arrays""" + arrays = [] + array_details = array.list_array_connections() + for arraycnt in range(0, len(array_details)): + arrays.append(array_details[arraycnt]['array_name']) + + return arrays def get_pgroup(module, array): - + """ Get Protection Group""" pgroup = None - for h in array.list_pgroups(): - if h["name"] == module.params['pgroup']: - pgroup = h + 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 make_pgroup(module, array): - - changed = True - - if not module.check_mode: - host = array.create_pgroup(module.params['pgroup']) - array.set_pgroup(module.params['pgroup'], snap_enabled=module.params['enabled']) - if module.params['volume']: + """ Create Protection Group""" + changed = False + if module.params['target']: + connected_arrays = get_arrays(array) + if connected_arrays == []: + module.fail_json(msg='No target arrays not connected to 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: + module.fail_json(msg='Creation of replicated pgroup {0} failed.'.format(module.params['pgroup'])) + else: + module.fail_json(msg='Target arrays {0} not connected to source array.'.format(module.params['target'])) + else: + try: + array.create_pgroup(module.params['pgroup']) + except: + 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: + 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']) - if module.params['host']: + except: + 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']) - if module.params['hostgroup']: + except: + 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: + 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 - pgroup = module.params['pgroup'] + if module.params['target']: + connected_arrays = get_arrays(array) + if connected_arrays == []: + module.fail_json(msg='No target arrays not connected to source array.') + if not all(x in connected_arrays for x in module.params['target'][0:4]): + try: + array.set_pgroup(module.params['pgroup'], targetlist=[module.params['target'][0:4]]) + changed = True + except: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + module.fail_json(msg='Changing hostgroups in pgroup {0} failed.'.format(module.params['pgroup'])) + module.exit_json(changed=changed) def delete_pgroup(module, array): + """ Delete Protection Group""" changed = True - if not module.check_mode: - array.destroy_pgroup(module.params['pgroup']) - if module.params['eradicate']: - array.eradicate_pgroup(module.params['pgroup']) + array.destroy_pgroup(module.params['pgroup']) + if module.params['eradicate']: + array.eradicate_pgroup(module.params['pgroup']) module.exit_json(changed=changed) @@ -168,14 +301,15 @@ def main(): 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), )) - module = AnsibleModule(argument_spec, supports_check_mode=True) - - if not HAS_PURESTORAGE: - module.fail_json(msg='purestorage sdk is required for this module in host') + 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) @@ -183,17 +317,17 @@ def main(): if module.params['host']: try: - for h in module.params['host']: - array.get_host(h) + for hst in module.params['host']: + array.get_host(hst) except: - module.fail_json(msg='Host {} not found'.format(h)) + module.fail_json(msg='Host {} not found'.format(hst)) if module.params['hostgroup']: try: - for hg in module.params['hostgroup']: - array.get_hgroup(hg) + for hstg in module.params['hostgroup']: + array.get_hgroup(hstg) except: - module.fail_json(msg='Hostgroup {} not found'.format(hg)) + module.fail_json(msg='Hostgroup {} not found'.format(hstg)) if pgroup and state == 'present': update_pgroup(module, array)