diff --git a/lib/ansible/modules/network/aci/mso_schema.py b/lib/ansible/modules/network/aci/mso_schema.py index 1026ea7f218..ba2afe8adaf 100644 --- a/lib/ansible/modules/network/aci/mso_schema.py +++ b/lib/ansible/modules/network/aci/mso_schema.py @@ -48,7 +48,7 @@ options: choices: [ absent, present, query ] default: present notes: -- This module cannot create empty schemas (i.e. schemas without templates). +- Due to restrictions of the MSO REST API this module cannot create empty schemas (i.e. schemas without templates). Use the M(mso_schema_template) to automatically create schemas with templates. seealso: - module: mso_schema_site diff --git a/lib/ansible/modules/network/aci/mso_schema_site.py b/lib/ansible/modules/network/aci/mso_schema_site.py index 676b51181a7..1c5e2859f6e 100644 --- a/lib/ansible/modules/network/aci/mso_schema_site.py +++ b/lib/ansible/modules/network/aci/mso_schema_site.py @@ -135,7 +135,6 @@ def main(): mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) # Schema exists - schema_id = schema_obj['id'] schema_path = 'schemas/{id}'.format(**schema_obj) # Get site @@ -159,16 +158,16 @@ def main(): mso.existing = [] mso.exit_json() + sites_path = '/sites' + site_path = '/sites/{0}'.format(site) ops = [] mso.previous = mso.existing if state == 'absent': - mso.proposed = mso.sent = {} - if mso.existing: # Remove existing site - mso.existing = {} - ops.append(dict(op='remove', path='/sites/{0}'.format(site_idx))) + mso.sent = mso.existing = {} + ops.append(dict(op='remove', path=site_path)) elif state == 'present': if not mso.existing: @@ -187,8 +186,9 @@ def main(): mso.sanitize(payload, collate=True) - mso.existing = mso.proposed = mso.sent - ops.append(dict(op='add', path='/sites/-', value=mso.sent)) + ops.append(dict(op='add', path=sites_path + '/-', value=mso.sent)) + + mso.existing = mso.proposed if not module.check_mode: mso.request(schema_path, method='PATCH', data=ops) diff --git a/lib/ansible/modules/network/aci/mso_schema_template.py b/lib/ansible/modules/network/aci/mso_schema_template.py index e77ef837def..3f337102ca8 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template.py +++ b/lib/ansible/modules/network/aci/mso_schema_template.py @@ -21,6 +21,11 @@ author: - Dag Wieers (@dagwieers) version_added: '2.8' options: + tenant: + description: + - The tenant used for this template. + type: str + required: yes schema: description: - The name of the schema. @@ -31,10 +36,6 @@ options: - The name of the template. type: str aliases: [ name ] - tenant: - description: - - The tenant used for this template. - type: str display_name: description: - The name as displayed on the MSO web interface. @@ -47,7 +48,7 @@ options: choices: [ absent, present, query ] default: present notes: -- This module creates schemas when needed, and removes them when the last template has been removed. +- Due to restrictions of the MSO REST API this module creates schemas when needed, and removes them when the last template has been removed. seealso: - module: mso_schema - module: mso_schema_site @@ -137,30 +138,34 @@ def main(): # Get schema schema_obj = mso.get_obj('schemas', displayName=schema) + + mso.existing = {} if schema_obj: # Schema exists - path = 'schemas/{id}'.format(**schema_obj) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] - if template is None: - mso.existing = schema_obj['templates'] - elif template in templates: - template_idx = templates.index(template) - mso.existing = schema_obj['templates'][template_idx] + if template: + if template in templates: + template_idx = templates.index(template) + mso.existing = schema_obj['templates'][template_idx] else: - mso.existing = {} + mso.existing = schema_obj['templates'] else: - path = 'schemas' - - if template is None: - mso.existing = [] - else: - mso.existing = {} + schema_path = 'schemas' if state == 'query': + if not mso.existing: + if template: + mso.fail_json(msg="Template '{0}' not found".format(template)) + else: + mso.existing = [] mso.exit_json() + template_path = '/templates/{0}'.format(template) + ops = [] + mso.previous = mso.existing if state == 'absent': mso.proposed = mso.sent = {} @@ -172,15 +177,11 @@ def main(): # There is only one tenant, remove schema mso.existing = {} if not module.check_mode: - mso.request(path, method='DELETE') + mso.request(schema_path, method='DELETE') elif mso.existing: # Remove existing template mso.existing = {} - operation = [ - dict(op='remove', path='/templates/{template}'.format(template=template)), - ] - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + ops.append(dict(op='remove', path=template_path)) else: # There was no template to begin with pass @@ -206,7 +207,7 @@ def main(): mso.existing = payload['templates'][0] if not module.check_mode: - mso.request(path, method='POST', data=payload) + mso.request(schema_path, method='POST', data=payload) elif mso.existing: # Template exists, so we have to update it @@ -218,14 +219,10 @@ def main(): mso.sanitize(payload, collate=True) - mso.existing = payload - operations = [ - dict(op='replace', path='/templates/{template}/displayName'.format(template=template), value=display_name), - dict(op='replace', path='/templates/{template}/tenantId'.format(template=template), value=tenant_id), - ] + ops.append(dict(op='replace', path=template_path + '/displayName', value=display_name)) + ops.append(dict(op='replace', path=template_path + '/tenantId', value=tenant_id)) - if not module.check_mode: - mso.request(path, method='PATCH', data=operations) + mso.existing = mso.proposed else: # Template does not exist, so we have to add it payload = dict( @@ -234,13 +231,14 @@ def main(): tenantId=tenant_id, ) - mso.existing = payload - operations = [ - dict(op='add', path='/templates/-', value=payload), - ] + mso.sanitize(payload, collate=True) - if not module.check_mode: - mso.request(path, method='PATCH', data=operations) + ops.append(dict(op='add', path='/templates/-', value=payload)) + + mso.existing = mso.proposed + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_anp.py b/lib/ansible/modules/network/aci/mso_schema_template_anp.py index 87c53917db7..975c01c1434 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_anp.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_anp.py @@ -30,6 +30,7 @@ options: description: - The name of the template. type: list + required: yes anp: description: - The name of the ANP to manage. @@ -134,12 +135,10 @@ def main(): # Get schema_id schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj['id'] - else: + if not schema_obj: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -197,7 +196,7 @@ def main(): mso.existing = mso.proposed if not module.check_mode: - mso.request(path, method='PATCH', data=ops) + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg.py b/lib/ansible/modules/network/aci/mso_schema_template_anp_epg.py index b9b9c83b541..f6a8fc33514 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_anp_epg.py @@ -30,10 +30,12 @@ options: description: - The name of the template to change. type: list + required: yes anp: description: - The name of the ANP. type: str + required: yes epg: description: - The name of the EPG to manage. @@ -231,7 +233,7 @@ def main(): else: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -258,16 +260,15 @@ def main(): mso.fail_json(msg="EPG '{epg}' not found".format(epg=epg)) mso.exit_json() + epgs_path = '/templates/{0}/anps/{1}/epgs'.format(template, anp) + epg_path = '/templates/{0}/anps/{1}/epgs/{2}'.format(template, anp, epg) + ops = [] + mso.previous = mso.existing if state == 'absent': if mso.existing: mso.sent = mso.existing = {} - operation = [dict( - op='remove', - path='/templates/{template}/anps/{anp}/epgs/{epg}'.format(template=template, anp=anp, epg=epg), - )] - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + ops.append(dict(op='remove', path=epg_path)) elif state == 'present': bd_ref = mso.make_reference(bd, 'bd', schema_id, template) @@ -292,21 +293,14 @@ def main(): mso.sanitize(payload, collate=True) if mso.existing: - operation = [dict( - op='replace', - path='/templates/{template}/anps/{anp}/epgs/{epg}'.format(template=template, anp=anp, epg=epg), - value=mso.sent, - )] + ops.append(dict(op='replace', path=epg_path, value=mso.sent)) else: - operation = [dict( - op='add', - path='/templates/{template}/anps/{anp}/epgs/-'.format(template=template, anp=anp), - value=mso.sent, - )] + ops.append(dict(op='add', path=epgs_path + '/-', value=mso.sent)) mso.existing = mso.proposed - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_subnet.py b/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_subnet.py index 5fbcc300db3..bfa4196ec34 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_subnet.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_anp_epg_subnet.py @@ -30,14 +30,17 @@ options: description: - The name of the template to change. type: list + required: yes anp: description: - The name of the ANP. type: str + required: yes epg: description: - The name of the EPG to manage. type: str + required: yes ip: description: - The IP range in CIDR notation. @@ -67,6 +70,8 @@ options: type: str choices: [ absent, present, query ] default: present +notes: +- Due to restrictions of the MSO REST API concurrent modifications to EPG subnets can be dangerous and corrupt data. extends_documentation_fragment: mso ''' @@ -164,14 +169,12 @@ def main(): mso = MSOModule(module) - # Get schema_id + # Get schema schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj['id'] - else: + if not schema_obj: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -196,7 +199,8 @@ def main(): subnets = [s['ip'] for s in schema_obj['templates'][template_idx]['anps'][anp_idx]['epgs'][epg_idx]['subnets']] if ip in subnets: ip_idx = subnets.index(ip) - # FIXME: Forced to use index here + # FIXME: Changes based on index are DANGEROUS + subnet_path = '/templates/{0}/anps/{1}/epgs/{2}/subnets/{3}'.format(template, anp, epg, ip_idx) mso.existing = schema_obj['templates'][template_idx]['anps'][anp_idx]['epgs'][epg_idx]['subnets'][ip_idx] if state == 'query': @@ -206,17 +210,14 @@ def main(): mso.fail_json(msg="Subnet '{ip}' not found".format(ip=ip)) mso.exit_json() + subnets_path = '/templates/{0}/anps/{1}/epgs/{2}/subnets'.format(template, anp, epg) + ops = [] + mso.previous = mso.existing if state == 'absent': if mso.existing: - mso.sent = mso.existing = {} - operation = [dict( - op='remove', - # FIXME: Forced to use index here - path='/templates/{template}/anps/{anp}/epgs/{epg}/subnets/{ip}'.format(template=template, anp=anp, epg=epg, ip=ip_idx), - )] - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + mso.existing = {} + ops.append(dict(op='remove', path=subnet_path)) elif state == 'present': if description is None and not mso.existing: @@ -239,22 +240,14 @@ def main(): mso.sanitize(payload, collate=True) if mso.existing: - operation = [dict( - op='replace', - # FIXME: Forced to use index here - path='/templates/{template}/anps/{anp}/epgs/{epg}/subnets/{ip}'.format(template=template, anp=anp, epg=epg, ip=ip_idx), - value=mso.sent, - )] + ops.append(dict(op='replace', path=subnet_path, value=mso.sent)) else: - operation = [dict( - op='add', - path='/templates/{template}/anps/{anp}/epgs/{epg}/subnets/-'.format(template=template, anp=anp, epg=epg), - value=mso.sent, - )] + ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent)) mso.existing = mso.proposed - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_bd.py b/lib/ansible/modules/network/aci/mso_schema_template_bd.py index dddc6f5d49d..57b01922e44 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_bd.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_bd.py @@ -30,6 +30,7 @@ options: description: - The name of the template. type: list + required: yes bd: description: - The name of the BD to manage. @@ -41,11 +42,11 @@ options: type: str vrf: description: - - The VRF associated to this ANP. + - The VRF associated to this BD. type: str subnets: description: - - The subnets associated to this ANP. + - The subnets associated to this BD. type: list suboptions: ip: @@ -203,7 +204,7 @@ def main(): else: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -225,16 +226,15 @@ def main(): mso.fail_json(msg="BD '{bd}' not found".format(bd=bd)) mso.exit_json() + bds_path = '/templates/{0}/bds'.format(template) + bd_path = '/templates/{0}/bds/{1}'.format(template, bd) + ops = [] + mso.previous = mso.existing if state == 'absent': if mso.existing: mso.sent = mso.existing = {} - operation = [dict( - op='remove', - path='/templates/{template}/bds/{bd}'.format(template=template, bd=bd), - )] - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + ops.append(dict(op='remove', path=bd_path)) elif state == 'present': vrf_ref = mso.make_reference(vrf, 'vrf', schema_id, template) @@ -242,6 +242,8 @@ def main(): if display_name is None and not mso.existing: display_name = bd + if subnets is None and not mso.existing: + subnets = [] payload = dict( name=bd, @@ -255,25 +257,17 @@ def main(): vrfRef=vrf_ref, ) - mso.sanitize(payload, collate=True, unwanted=['bdRef', 'vrfRef']) + mso.sanitize(payload, collate=True) if mso.existing: - operation = [dict( - op='replace', - path='/templates/{template}/bds/{bd}'.format(template=template, bd=bd), - value=mso.sent, - )] - + ops.append(dict(op='replace', path=bd_path, value=mso.sent)) else: - operation = [dict( - op='add', - path='/templates/{template}/bds/-'.format(template=template), - value=mso.sent, - )] + ops.append(dict(op='add', path=bds_path + '/-', value=mso.sent)) mso.existing = mso.proposed - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_bd_subnet.py b/lib/ansible/modules/network/aci/mso_schema_template_bd_subnet.py index 314fcf5df5b..8751bf87f7b 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_bd_subnet.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_bd_subnet.py @@ -30,10 +30,12 @@ options: description: - The name of the template to change. type: list + required: yes bd: description: - The name of the BD to manage. type: str + required: yes ip: description: - The IP range in CIDR notation. @@ -63,6 +65,8 @@ options: type: str choices: [ absent, present, query ] default: present +notes: +- Due to restrictions of the MSO REST API concurrent modifications to BD subnets can be dangerous and corrupt data. extends_documentation_fragment: mso ''' @@ -155,14 +159,12 @@ def main(): mso = MSOModule(module) - # Get schema_id + # Get schema schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj['id'] - else: + if not schema_obj: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -180,7 +182,8 @@ def main(): subnets = [s['ip'] for s in schema_obj['templates'][template_idx]['bds'][bd_idx]['subnets']] if ip in subnets: ip_idx = subnets.index(ip) - # FIXME: Forced to use index here + # FIXME: Changes based on index are DANGEROUS + subnet_path = '/templates/{0}/bds/{1}/subnets/{2}'.format(template, bd, ip_idx) mso.existing = schema_obj['templates'][template_idx]['bds'][bd_idx]['subnets'][ip_idx] if state == 'query': @@ -190,17 +193,14 @@ def main(): mso.fail_json(msg="Subnet '{ip}' not found".format(ip=ip)) mso.exit_json() + subnets_path = '/templates/{0}/bds/{1}/subnets'.format(template, bd) + ops = [] + mso.previous = mso.existing if state == 'absent': if mso.existing: mso.sent = mso.existing = {} - operation = [dict( - op='remove', - # FIXME: Forced to use index here - path='/templates/{template}/bds/{bd}/subnets/{ip}'.format(template=template, bd=bd, ip=ip_idx), - )] - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + ops.append(dict(op='remove', path=subnet_path)) elif state == 'present': if description is None and not mso.existing: @@ -223,22 +223,14 @@ def main(): mso.sanitize(payload, collate=True) if mso.existing: - operation = [dict( - op='replace', - # FIXME: Forced to use index here - path='/templates/{template}/bds/{bd}/subnets/{ip}'.format(template=template, bd=bd, ip=ip_idx), - value=mso.sent, - )] + ops.append(dict(op='replace', path=subnet_path, value=mso.sent)) else: - operation = [dict( - op='add', - path='/templates/{template}/bds/{bd}/subnets/-'.format(template=template, bd=bd), - value=mso.sent, - )] + ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent)) mso.existing = mso.proposed - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py b/lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py index 53900e1edf0..5584050b581 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_contract_filter.py @@ -30,10 +30,12 @@ options: description: - The name of the template. type: list + required: yes contract: description: - The name of the contract to manage. type: str + required: yes contract_display_name: description: - The name as displayed on the MSO web interface. @@ -78,6 +80,11 @@ options: type: str choices: [ absent, present, query ] default: present +seealso: +- module: mso_schema_template_filter_entry +notes: +- Due to restrictions of the MSO REST API this module creates contracts when needed, and removes them when the last filter has been removed. +- Due to restrictions of the MSO REST API concurrent modifications to contract filters can be dangerous and corrupt data. extends_documentation_fragment: mso ''' @@ -209,7 +216,7 @@ def main(): else: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -230,6 +237,8 @@ def main(): filter_ref = mso.filter_ref(filter_schema_id, filter_template, filter_name) if filter_ref in filters: filter_idx = filters.index(filter_ref) + # FIXME: Changes based on index are DANGEROUS + filter_path = '/templates/{0}/contracts/{1}/{2}/{3}'.format(template, contract, filter_key, filter_idx) mso.existing = schema_obj['templates'][template_idx]['contracts'][contract_idx][filter_key][filter_idx] if state == 'query': @@ -244,8 +253,7 @@ def main(): ops = [] contract_path = '/templates/{0}/contracts/{1}'.format(template, contract) - # FIXME: Removing based on index is DANGEROUS - filter_path = '/templates/{0}/contracts/{1}/{2}/{3}'.format(template, contract, filter_key, filter_idx) + filters_path = '/templates/{0}/contracts/{1}/{2}'.format(template, contract, filter_key) mso.previous = mso.existing if state == 'absent': @@ -266,9 +274,6 @@ def main(): mso.existing = {} ops.append(dict(op='remove', path=filter_path)) - if not module.check_mode: - mso.request(path, method='PATCH', data=ops) - elif state == 'present': payload = dict( @@ -282,11 +287,6 @@ def main(): mso.sanitize(payload, collate=True) mso.existing = mso.sent - ops = [] - contract_path = '/templates/{0}/contracts/{1}'.format(template, contract) - filters_path = '/templates/{0}/contracts/{1}/{2}'.format(template, contract, filter_key) - # FIXME: Updating based on index is DANGEROUS - filter_path = '/templates/{0}/contracts/{1}/{2}/{3}'.format(template, contract, filter_key, filter_idx) if contract_idx is None: # COntract does not exist, so we have to create it @@ -322,8 +322,8 @@ def main(): # Filter exists, we have to update it ops.append(dict(op='replace', path=filter_path, value=mso.sent)) - if not module.check_mode: - mso.request(path, method='PATCH', data=ops) + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_externalepg.py b/lib/ansible/modules/network/aci/mso_schema_template_externalepg.py index 42537705c36..4ab53fe1f55 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_externalepg.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_externalepg.py @@ -30,6 +30,7 @@ options: description: - The name of the template. type: list + required: yes externalepg: description: - The name of the external EPG to manage. @@ -143,7 +144,7 @@ def main(): else: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -165,16 +166,15 @@ def main(): mso.fail_json(msg="External EPG '{externalepg}' not found".format(externalepg=externalepg)) mso.exit_json() + eepgs_path = '/templates/{0}/externalEpgs'.format(template) + eepg_path = '/templates/{0}/externalEpgs/{1}'.format(template, externalepg) + ops = [] + mso.previous = mso.existing if state == 'absent': if mso.existing: mso.sent = mso.existing = {} - operation = [dict( - op='remove', - path='/templates/{template}/externalEpgs/{externalepg}'.format(template=template, externalepg=externalepg), - )] - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + ops.append(dict(op='remove', path=eepg_path)) elif state == 'present': vrf_ref = mso.make_reference(vrf, 'vrf', schema_id, template) @@ -194,22 +194,14 @@ def main(): mso.sanitize(payload, collate=True) if mso.existing: - operation = [dict( - op='replace', - path='/templates/{template}/externalEpgs/{externalepg}'.format(template=template, externalepg=externalepg), - value=mso.sent, - )] - + ops.append(dict(op='replace', path=eepg_path, value=mso.sent)) else: - operation = [dict( - op='add', - path='/templates/{template}/externalEpgs/-'.format(template=template), - value=mso.sent, - )] + ops.append(dict(op='add', path=eepgs_path + '/-', value=mso.sent)) mso.existing = mso.proposed - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py b/lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py index 07afbcb4b7d..a3947aed230 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_filter_entry.py @@ -30,10 +30,12 @@ options: description: - The name of the template. type: list + required: yes filter: description: - The name of the filter to manage. type: str + required: yes filter_display_name: description: - The name as displayed on the MSO web interface. @@ -106,6 +108,10 @@ options: type: str choices: [ absent, present, query ] default: present +seealso: +- module: mso_schema_template_contract_filter +notes: +- Due to restrictions of the MSO REST API this module creates filters when needed, and removes them when the last entry has been removed. extends_documentation_fragment: mso ''' @@ -168,9 +174,9 @@ def main(): argument_spec.update( schema=dict(type='str', required=True), template=dict(type='str', required=True), - filter=dict(type='str', required=True), # This parameter is not required for querying all objects + filter=dict(type='str', required=True), filter_display_name=dict(type='str', aliases=['filter_display_name']), - entry=dict(type='str', required=True, aliases=['name']), + entry=dict(type='str', required=False, aliases=['name']), # This parameter is not required for querying all objects description=dict(type='str', aliases=['entry_description']), display_name=dict(type='str', aliases=['entry_display_name']), ethertype=dict(type='str', choices=['arp', 'fcoe', 'ip', 'ipv4', 'ipv6', 'mac-security', 'mpls-unicast', 'trill', 'unspecified']), @@ -216,14 +222,12 @@ def main(): mso = MSOModule(module) - # Get schema_id + # Get schema schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj['id'] - else: + if not schema_obj: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -247,11 +251,19 @@ def main(): if state == 'query': if entry is None: + if filter_idx is None: + mso.fail_json(msg="Filter '{filter}' not found".format(filter=filter_name)) mso.existing = schema_obj['templates'][template_idx]['filters'][filter_idx]['entries'] elif not mso.existing: mso.fail_json(msg="Entry '{entry}' not found".format(entry=entry)) mso.exit_json() + filters_path = '/templates/{0}/filters'.format(template) + filter_path = '/templates/{0}/filters/{1}'.format(template, filter_name) + entries_path = '/templates/{0}/filters/{1}/entries'.format(template, filter_name) + entry_path = '/templates/{0}/filters/{1}/entries/{2}'.format(template, filter_name, entry) + ops = [] + mso.previous = mso.existing if state == 'absent': mso.proposed = mso.sent = {} @@ -265,18 +277,11 @@ def main(): elif len(entries) == 1: # There is only one entry, remove filter mso.existing = {} - operations = [ - dict(op='remove', path='/templates/{template}/filters/{filter}'.format(template=template, filter=filter_name)), - ] - if not module.check_mode: - mso.request(path, method='PATCH', data=operations) + ops.append(dict(op='remove', path=filter_path)) + else: mso.existing = {} - operations = [ - dict(op='remove', path='/templates/{template}/filters/{filter}/entries/{entry}'.format(template=template, filter=filter_name, entry=entry)), - ] - if not module.check_mode: - mso.request(path, method='PATCH', data=operations) + ops.append(dict(op='remove', path=entry_path)) elif state == 'present': @@ -322,7 +327,6 @@ def main(): ) mso.sanitize(payload, collate=True) - mso.existing = mso.sent if filter_idx is None: # Filter does not exist, so we have to create it @@ -335,37 +339,31 @@ def main(): entries=[mso.sent], ) - operations = [ - dict(op='add', path='/templates/{template}/filters/-'.format(template=template), value=payload), - ] + ops.append(dict(op='add', path=filters_path + '/-', value=payload)) elif entry_idx is None: # Entry does not exist, so we have to add it - operations = [ - dict(op='add', path='/templates/{template}/filters/{filter}/entries/-'.format(template=template, filter=filter_name), value=mso.sent) - ] + ops.append(dict(op='add', path=entries_path + '/-', value=mso.sent)) else: # Entry exists, we have to update it - alias = '/templates/{template}/filters/{filter}/entries/{entry}'.format(template=template, filter=filter_name, entry=entry) - operations = [ - dict(op='replace', path='{alias}/name'.format(alias=alias), value=entry), - dict(op='replace', path='{alias}/displayName'.format(alias=alias), value=display_name), - dict(op='replace', path='{alias}/description'.format(alias=alias), value=description), - dict(op='replace', path='{alias}/etherType'.format(alias=alias), value=ethertype), - dict(op='replace', path='{alias}/ipProtocol'.format(alias=alias), value=ip_protocol), - dict(op='replace', path='{alias}/tcpSessionRules'.format(alias=alias), value=tcp_session_rules), - dict(op='replace', path='{alias}/sourceFrom'.format(alias=alias), value=source_from), - dict(op='replace', path='{alias}/sourceTo'.format(alias=alias), value=source_to), - dict(op='replace', path='{alias}/destinationFrom'.format(alias=alias), value=destination_from), - dict(op='replace', path='{alias}/destinationTo'.format(alias=alias), value=destination_to), - dict(op='replace', path='{alias}/arpFlag'.format(alias=alias), value=arp_flag), - dict(op='replace', path='{alias}/stateful'.format(alias=alias), value=stateful), - dict(op='replace', path='{alias}/matchOnlyFragments'.format(alias=alias), value=fragments_only), - ] - - if not module.check_mode: - mso.request(path, method='PATCH', data=operations) + ops.append(dict(op='replace', path=entry_path + '/displayName', value=display_name)) + ops.append(dict(op='replace', path=entry_path + '/description', value=description)) + ops.append(dict(op='replace', path=entry_path + '/etherType', value=ethertype)) + ops.append(dict(op='replace', path=entry_path + '/ipProtocol', value=ip_protocol)) + ops.append(dict(op='replace', path=entry_path + '/tcpSessionRules', value=tcp_session_rules)) + ops.append(dict(op='replace', path=entry_path + '/sourceFrom', value=source_from)) + ops.append(dict(op='replace', path=entry_path + '/sourceTo', value=source_to)) + ops.append(dict(op='replace', path=entry_path + '/destinationFrom', value=destination_from)) + ops.append(dict(op='replace', path=entry_path + '/destinationTo', value=destination_to)) + ops.append(dict(op='replace', path=entry_path + '/arpFlag', value=arp_flag)) + ops.append(dict(op='replace', path=entry_path + '/stateful', value=stateful)) + ops.append(dict(op='replace', path=entry_path + '/matchOnlyFragments', value=fragments_only)) + + mso.existing = mso.proposed + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_l3out.py b/lib/ansible/modules/network/aci/mso_schema_template_l3out.py index 2a9917204d4..ca6d422ff8e 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_l3out.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_l3out.py @@ -30,6 +30,7 @@ options: description: - The name of the template. type: list + required: yes l3out: description: - The name of the l3out to manage. @@ -41,7 +42,7 @@ options: type: str vrf: description: - - The VRF associated to this ANP. + - The VRF associated to this L3out. type: str state: description: @@ -143,7 +144,7 @@ def main(): else: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -165,16 +166,15 @@ def main(): mso.fail_json(msg="L3out '{l3out}' not found".format(l3out=l3out)) mso.exit_json() + l3outs_path = '/templates/{0}/intersiteL3outs'.format(template) + l3out_path = '/templates/{0}/intersiteL3outs/{1}'.format(template, l3out) + ops = [] + mso.previous = mso.existing if state == 'absent': if mso.existing: mso.sent = mso.existing = {} - operation = [dict( - op='remove', - path='/templates/{template}/intersiteL3outs/{l3out}'.format(template=template, l3out=l3out), - )] - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + ops.append(dict(op='remove', path=l3out_path)) elif state == 'present': vrf_ref = mso.make_reference(vrf, 'vrf', schema_id, template) @@ -191,22 +191,14 @@ def main(): mso.sanitize(payload, collate=True) if mso.existing: - operation = [dict( - op='replace', - path='/templates/{template}/intersiteL3outs/{l3out}'.format(template=template, l3out=l3out), - value=mso.sent, - )] - + ops.append(dict(op='replace', path=l3out_path, value=mso.sent)) else: - operation = [dict( - op='add', - path='/templates/{template}/intersiteL3outs/-'.format(template=template), - value=mso.sent, - )] + ops.append(dict(op='add', path=l3outs_path + '/-', value=mso.sent)) mso.existing = mso.proposed - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json() diff --git a/lib/ansible/modules/network/aci/mso_schema_template_vrf.py b/lib/ansible/modules/network/aci/mso_schema_template_vrf.py index e571b3ad2aa..bd5e6772014 100644 --- a/lib/ansible/modules/network/aci/mso_schema_template_vrf.py +++ b/lib/ansible/modules/network/aci/mso_schema_template_vrf.py @@ -30,6 +30,7 @@ options: description: - The name of the template. type: list + required: yes vrf: description: - The name of the VRF to manage. @@ -138,12 +139,10 @@ def main(): # Get schema_id schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj['id'] - else: + if not schema_obj: mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - path = 'schemas/{id}'.format(id=schema_id) + schema_path = 'schemas/{id}'.format(**schema_obj) # Get template templates = [t['name'] for t in schema_obj['templates']] @@ -165,16 +164,15 @@ def main(): mso.fail_json(msg="VRF '{vrf}' not found".format(vrf=vrf)) mso.exit_json() + vrfs_path = '/templates/{0}/vrfs'.format(template) + vrf_path = '/templates/{0}/vrfs/{1}'.format(template, vrf) + ops = [] + mso.previous = mso.existing if state == 'absent': if mso.existing: mso.sent = mso.existing = {} - operation = [dict( - op='remove', - path='/templates/{template}/vrfs/{vrf}'.format(template=template, vrf=vrf), - )] - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + ops.append(dict(op='remove', path=vrf_path)) elif state == 'present': if display_name is None and not mso.existing: @@ -191,22 +189,14 @@ def main(): mso.sanitize(payload, collate=True) if mso.existing: - operation = [dict( - op='replace', - path='/templates/{template}/vrfs/{vrf}'.format(template=template, vrf=vrf), - value=mso.sent, - )] - + ops.append(dict(op='replace', path=vrf_path, value=mso.sent)) else: - operation = [dict( - op='add', - path='/templates/{template}/vrfs/-'.format(template=template), - value=mso.sent, - )] + ops.append(dict(op='add', path=vrfs_path + '/-', value=mso.sent)) mso.existing = mso.proposed - if not module.check_mode: - mso.request(path, method='PATCH', data=operation) + + if not module.check_mode: + mso.request(schema_path, method='PATCH', data=ops) mso.exit_json()