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