ansible  2.9.27
About: Ansible is an IT Configuration Management, Deployment \
About: Ansible (2.x) is an IT Configuration Management, Deployment & Orchestration tool.
ansible download page.
  Fossies Dox: ansible-2.9.27.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

mso_schema_site_bd_subnet.py
Go to the documentation of this file.
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com>
5# GNU General Public License v3.0+ (see COPYING 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_bd_subnet
17short_description: Manage site-local BD subnets in schema template
18description:
19- Manage site-local BD subnets in schema template on Cisco ACI Multi-Site.
20author:
21- Dag Wieers (@dagwieers)
22version_added: '2.8'
23options:
24 schema:
25 description:
26 - The name of the schema.
27 type: str
28 required: yes
29 site:
30 description:
31 - The name of the site.
32 type: str
33 required: yes
34 template:
35 description:
36 - The name of the template.
37 type: str
38 required: yes
39 bd:
40 description:
41 - The name of the BD.
42 type: str
43 aliases: [ name ]
44 subnet:
45 description:
46 - The IP range in CIDR notation.
47 type: str
48 required: true
49 aliases: [ ip ]
50 description:
51 description:
52 - The description of this subnet.
53 type: str
54 scope:
55 description:
56 - The scope of the subnet.
57 type: str
58 choices: [ private, public ]
59 shared:
60 description:
61 - Whether this subnet is shared between VRFs.
62 type: bool
63 no_default_gateway:
64 description:
65 - Whether this subnet has a default gateway.
66 type: bool
67 state:
68 description:
69 - Use C(present) or C(absent) for adding or removing.
70 - Use C(query) for listing an object or multiple objects.
71 type: str
72 choices: [ absent, present, query ]
73 default: present
74notes:
75- The ACI MultiSite PATCH API has a deficiency requiring some objects to be referenced by index.
76 This can cause silent corruption on concurrent access when changing/removing on object as
77 the wrong object may be referenced. This module is affected by this deficiency.
78seealso:
79- module: mso_schema_site_bd
80- module: mso_schema_template_bd
81extends_documentation_fragment: mso
82'''
83
84EXAMPLES = r'''
85- name: Add a new site BD subnet
86 mso_schema_site_bd_subnet:
87 host: mso_host
88 username: admin
89 password: SomeSecretPassword
90 schema: Schema1
91 site: Site1
92 template: Template1
93 bd: BD1
94 subnet: 11.11.11.0/24
95 state: present
96 delegate_to: localhost
97
98- name: Remove a site BD subnet
99 mso_schema_site_bd_subnet:
100 host: mso_host
101 username: admin
102 password: SomeSecretPassword
103 schema: Schema1
104 site: Site1
105 template: Template1
106 bd: BD1
107 subnet: 11.11.11.0/24
108 state: absent
109 delegate_to: localhost
110
111- name: Query a specific site BD subnet
112 mso_schema_site_bd_subnet:
113 host: mso_host
114 username: admin
115 password: SomeSecretPassword
116 schema: Schema1
117 site: Site1
118 template: Template1
119 bd: BD1
120 subnet: 11.11.11.0/24
121 state: query
122 delegate_to: localhost
123 register: query_result
124
125- name: Query all site BD subnets
126 mso_schema_site_bd_subnet:
127 host: mso_host
128 username: admin
129 password: SomeSecretPassword
130 schema: Schema1
131 site: Site1
132 template: Template1
133 bd: BD1
134 state: query
135 delegate_to: localhost
136 register: query_result
137'''
138
139RETURN = r'''
140'''
141
142from ansible.module_utils.basic import AnsibleModule
143from ansible.module_utils.network.aci.mso import MSOModule, mso_argument_spec, mso_subnet_spec
144
145
146def main():
147 argument_spec = mso_argument_spec()
148 argument_spec.update(
149 schema=dict(type='str', required=True),
150 site=dict(type='str', required=True),
151 template=dict(type='str', required=True),
152 bd=dict(type='str', aliases=['name']), # This parameter is not required for querying all objects
153 state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
154 )
155 argument_spec.update(mso_subnet_spec())
156
157 module = AnsibleModule(
158 argument_spec=argument_spec,
159 supports_check_mode=True,
160 required_if=[
161 ['state', 'absent', ['bd']],
162 ['state', 'present', ['bd']],
163 ],
164 )
165
166 schema = module.params['schema']
167 site = module.params['site']
168 template = module.params['template']
169 bd = module.params['bd']
170 subnet = module.params['subnet']
171 description = module.params['description']
172 scope = module.params['scope']
173 shared = module.params['shared']
174 no_default_gateway = module.params['no_default_gateway']
175 state = module.params['state']
176
177 mso = MSOModule(module)
178
179 # Get schema_id
180 schema_obj = mso.get_obj('schemas', displayName=schema)
181 if not schema_obj:
182 mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema))
183
184 schema_path = 'schemas/{id}'.format(**schema_obj)
185 schema_id = schema_obj['id']
186
187 # Get site
188 site_id = mso.lookup_site(site)
189
190 # Get site_idx
191 sites = [(s['siteId'], s['templateName']) for s in schema_obj['sites']]
192 if (site_id, template) not in sites:
193 mso.fail_json(msg="Provided site/template '{0}-{1}' does not exist. Existing sites/templates: {2}".format(site, template, ', '.join(sites)))
194
195 # Schema-access uses indexes
196 site_idx = sites.index((site_id, template))
197 # Path-based access uses site_id-template
198 site_template = '{0}-{1}'.format(site_id, template)
199
200 # Get BD
201 bd_ref = mso.bd_ref(schema_id=schema_id, template=template, bd=bd)
202 bds = [v['bdRef'] for v in schema_obj['sites'][site_idx]['bds']]
203 if bd_ref not in bds:
204 mso.fail_json(msg="Provided BD '{0}' does not exist. Existing BDs: {1}".format(bd, ', '.join(bds)))
205 bd_idx = bds.index(bd_ref)
206
207 # Get Subnet
208 subnets = [s['ip'] for s in schema_obj['sites'][site_idx]['bds'][bd_idx]['subnets']]
209 if subnet in subnets:
210 subnet_idx = subnets.index(subnet)
211 # FIXME: Changes based on index are DANGEROUS
212 subnet_path = '/sites/{0}/bds/{1}/subnets/{2}'.format(site_template, bd, subnet_idx)
213 mso.existing = schema_obj['sites'][site_idx]['bds'][bd_idx]['subnets'][subnet_idx]
214
215 if state == 'query':
216 if subnet is None:
217 mso.existing = schema_obj['sites'][site_idx]['bds'][bd_idx]['subnets']
218 elif not mso.existing:
219 mso.fail_json(msg="Subnet IP '{subnet}' not found".format(subnet=subnet))
220 mso.exit_json()
221
222 subnets_path = '/sites/{0}/bds/{1}/subnets'.format(site_template, bd)
223 ops = []
224
225 mso.previous = mso.existing
226 if state == 'absent':
227 if mso.existing:
228 mso.sent = mso.existing = {}
229 ops.append(dict(op='remove', path=subnet_path))
230
231 elif state == 'present':
232 if not mso.existing:
233 if description is None:
234 description = subnet
235 if scope is None:
236 scope = 'private'
237 if shared is None:
238 shared = False
239 if no_default_gateway is None:
240 no_default_gateway = False
241
242 payload = dict(
243 ip=subnet,
244 description=description,
245 scope=scope,
246 shared=shared,
247 noDefaultGateway=no_default_gateway,
248 )
249
250 mso.sanitize(payload, collate=True)
251
252 if mso.existing:
253 ops.append(dict(op='replace', path=subnet_path, value=mso.sent))
254 else:
255 ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent))
256
257 mso.existing = mso.proposed
258
259 if not module.check_mode:
260 mso.request(schema_path, method='PATCH', data=ops)
261
262 mso.exit_json()
263
264
265if __name__ == "__main__":
266 main()