1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com> 5# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10ANSIBLE_METADATA = {'metadata_version': '1.1', 11 'status': ['preview'], 12 'supported_by': 'community'} 13 14DOCUMENTATION = r''' 15--- 16module: mso_schema_site_anp_epg 17short_description: Manage site-local Endpoint Groups (EPGs) in schema template 18description: 19- Manage site-local EPGs in schema template on Cisco ACI Multi-Site. 20author: 21- Dag Wieers (@dagwieers) 22options: 23 schema: 24 description: 25 - The name of the schema. 26 type: str 27 required: yes 28 site: 29 description: 30 - The name of the site. 31 type: str 32 required: yes 33 template: 34 description: 35 - The name of the template. 36 type: str 37 required: yes 38 anp: 39 description: 40 - The name of the ANP. 41 type: str 42 required: yes 43 epg: 44 description: 45 - The name of the EPG to manage. 46 type: str 47 aliases: [ name ] 48 state: 49 description: 50 - Use C(present) or C(absent) for adding or removing. 51 - Use C(query) for listing an object or multiple objects. 52 type: str 53 choices: [ absent, present, query ] 54 default: present 55seealso: 56- module: cisco.mso.mso_schema_site_anp 57- module: cisco.mso.mso_schema_site_anp_epg_subnet 58- module: cisco.mso.mso_schema_template_anp_epg 59extends_documentation_fragment: cisco.mso.modules 60''' 61 62EXAMPLES = r''' 63- name: Add a new site EPG 64 cisco.mso.mso_schema_site_anp_epg: 65 host: mso_host 66 username: admin 67 password: SomeSecretPassword 68 schema: Schema1 69 site: Site1 70 template: Template1 71 anp: ANP1 72 epg: EPG1 73 state: present 74 delegate_to: localhost 75 76- name: Remove a site EPG 77 cisco.mso.mso_schema_site_anp_epg: 78 host: mso_host 79 username: admin 80 password: SomeSecretPassword 81 schema: Schema1 82 site: Site1 83 template: Template1 84 anp: ANP1 85 epg: EPG1 86 state: absent 87 delegate_to: localhost 88 89- name: Query a specific site EPGs 90 cisco.mso.mso_schema_site_anp_epg: 91 host: mso_host 92 username: admin 93 password: SomeSecretPassword 94 schema: Schema1 95 site: Site1 96 template: Template1 97 anp: ANP1 98 epg: EPG1 99 state: query 100 delegate_to: localhost 101 register: query_result 102 103- name: Query all site EPGs 104 cisco.mso.mso_schema_site_anp_epg: 105 host: mso_host 106 username: admin 107 password: SomeSecretPassword 108 schema: Schema1 109 site: Site1 110 template: Template1 111 anp: ANP1 112 state: query 113 delegate_to: localhost 114 register: query_result 115''' 116 117RETURN = r''' 118''' 119 120from ansible.module_utils.basic import AnsibleModule 121from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec 122 123 124def main(): 125 argument_spec = mso_argument_spec() 126 argument_spec.update( 127 schema=dict(type='str', required=True), 128 site=dict(type='str', required=True), 129 template=dict(type='str', required=True), 130 anp=dict(type='str', required=True), 131 epg=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects 132 state=dict(type='str', default='present', choices=['absent', 'present', 'query']), 133 ) 134 135 module = AnsibleModule( 136 argument_spec=argument_spec, 137 supports_check_mode=True, 138 required_if=[ 139 ['state', 'absent', ['epg']], 140 ['state', 'present', ['epg']], 141 ], 142 ) 143 144 schema = module.params.get('schema') 145 site = module.params.get('site') 146 template = module.params.get('template').replace(' ', '') 147 anp = module.params.get('anp') 148 epg = module.params.get('epg') 149 state = module.params.get('state') 150 151 mso = MSOModule(module) 152 153 # Get schema objects 154 schema_id, schema_path, schema_obj = mso.query_schema(schema) 155 156 # Get site 157 site_id = mso.lookup_site(site) 158 if 'sites' not in schema_obj: 159 mso.fail_json(msg="No site associated with template '{0}'. Associate the site with the template using mso_schema_site.".format(template)) 160 sites = [(s.get('siteId'), s.get('templateName')) for s in schema_obj.get('sites')] 161 if (site_id, template) not in sites: 162 mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites))) 163 164 # Schema-access uses indexes 165 site_idx = sites.index((site_id, template)) 166 # Path-based access uses site_id-template 167 site_template = '{0}-{1}'.format(site_id, template) 168 169 # Get ANP 170 anp_ref = mso.anp_ref(schema_id=schema_id, template=template, anp=anp) 171 anps = [a.get('anpRef') for a in schema_obj.get('sites')[site_idx]['anps']] 172 if anp_ref not in anps: 173 mso.fail_json(msg="Provided anp '{0}' does not exist. Existing anps: {1}".format(anp, ', '.join(anps))) 174 anp_idx = anps.index(anp_ref) 175 176 # Get EPG 177 epg_ref = mso.epg_ref(schema_id=schema_id, template=template, anp=anp, epg=epg) 178 epgs = [e.get('epgRef') for e in schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs']] 179 if epg is not None and epg_ref in epgs: 180 epg_idx = epgs.index(epg_ref) 181 epg_path = '/sites/{0}/anps/{1}/epgs/{2}'.format(site_template, anp, epg) 182 mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'][epg_idx] 183 184 if state == 'query': 185 if epg is None: 186 mso.existing = schema_obj.get('sites')[site_idx]['anps'][anp_idx]['epgs'] 187 elif not mso.existing: 188 mso.fail_json(msg="EPG '{epg}' not found".format(epg=epg)) 189 mso.exit_json() 190 191 epgs_path = '/sites/{0}/anps/{1}/epgs'.format(site_template, anp) 192 ops = [] 193 194 mso.previous = mso.existing 195 if state == 'absent': 196 if mso.existing: 197 mso.sent = mso.existing = {} 198 ops.append(dict(op='remove', path=epg_path)) 199 200 elif state == 'present': 201 202 payload = dict( 203 epgRef=dict( 204 schemaId=schema_id, 205 templateName=template, 206 anpName=anp, 207 epgName=epg, 208 ), 209 ) 210 211 mso.sanitize(payload, collate=True) 212 213 if not mso.existing: 214 ops.append(dict(op='add', path=epgs_path + '/-', value=mso.sent)) 215 216 mso.existing = mso.proposed 217 218 if not module.check_mode: 219 mso.request(schema_path, method='PATCH', data=ops) 220 221 mso.exit_json() 222 223 224if __name__ == "__main__": 225 main() 226