Skip to content

Commit 2873077

Browse files
committed
[minor change] New module for node_mgmt_epg to contract binding
1 parent 2fdee82 commit 2873077

File tree

3 files changed

+834
-0
lines changed

3 files changed

+834
-0
lines changed

Diff for: plugins/modules/aci_node_mgmt_epg_to_contract.py

+366
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
5+
# Copyright: (c) 2023, Akini Ross (@akinross) <[email protected]>
6+
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
7+
8+
from __future__ import absolute_import, division, print_function
9+
10+
__metaclass__ = type
11+
12+
ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"}
13+
14+
DOCUMENTATION = r"""
15+
---
16+
module: aci_epg_to_contract
17+
short_description: Bind EPGs to Contracts (fv:RsCons, fv:RsProv, fv:RsProtBy, fv:RsConsIf, and fv:RsIntraEpg)
18+
description:
19+
- Bind EPGs to Contracts on Cisco ACI fabrics.
20+
notes:
21+
- The C(tenant), C(app_profile), C(EPG), and C(Contract) used must exist before using this module in your playbook.
22+
The M(cisco.aci.aci_tenant), M(cisco.aci.aci_ap), M(cisco.aci.aci_epg), and M(cisco.aci.aci_contract) modules can be used for this.
23+
options:
24+
ap:
25+
description:
26+
- Name of an existing application network profile, that will contain the EPGs.
27+
type: str
28+
aliases: [ app_profile, app_profile_name ]
29+
contract:
30+
description:
31+
- The name of the contract or contract interface.
32+
type: str
33+
aliases: [ contract_name, contract_interface ]
34+
contract_type:
35+
description:
36+
- Determines the type of the Contract.
37+
type: str
38+
required: true
39+
choices: [ consumer, provider, taboo, interface, intra_epg ]
40+
epg:
41+
description:
42+
- The name of the end point group.
43+
type: str
44+
aliases: [ epg_name ]
45+
priority:
46+
description:
47+
- QoS class.
48+
- The APIC defaults to C(unspecified) when unset during creation.
49+
type: str
50+
choices: [ level1, level2, level3, level4, level5, level6, unspecified ]
51+
provider_match:
52+
description:
53+
- The matching algorithm for Provided Contracts.
54+
- The APIC defaults to C(at_least_one) when unset during creation.
55+
type: str
56+
choices: [ all, at_least_one, at_most_one, none ]
57+
contract_label:
58+
description:
59+
- Contract label to match
60+
type: str
61+
subject_label:
62+
description:
63+
- Subject label to match
64+
type: str
65+
state:
66+
description:
67+
- Use C(present) or C(absent) for adding or removing.
68+
- Use C(query) for listing an object or multiple objects.
69+
type: str
70+
choices: [ absent, present, query ]
71+
default: present
72+
tenant:
73+
description:
74+
- Name of an existing tenant.
75+
type: str
76+
aliases: [ tenant_name ]
77+
extends_documentation_fragment:
78+
- cisco.aci.aci
79+
- cisco.aci.annotation
80+
81+
seealso:
82+
- module: cisco.aci.aci_ap
83+
- module: cisco.aci.aci_epg
84+
- module: cisco.aci.aci_contract
85+
- name: APIC Management Information Model reference
86+
description: More information about the internal APIC classes B(fv:RsCons), B(fv:RsProv), B(fv:RsProtBy), B(fv:RsConsIf), and B(fv:RsIntraEpg).
87+
link: https://developer.cisco.com/docs/apic-mim-ref/
88+
author:
89+
- Jacob McGill (@jmcgill298)
90+
- Akini Ross (@akinross)
91+
"""
92+
93+
EXAMPLES = r"""
94+
- name: Add a new contract to EPG binding
95+
cisco.aci.aci_inbepg_to_contract:
96+
host: apic
97+
username: admin
98+
password: SomeSecretPassword
99+
tenant: mgmt
100+
epg: anstest
101+
contract: anstest_http
102+
contract_type: provider
103+
state: present
104+
delegate_to: localhost
105+
106+
- name: Remove an existing contract to EPG binding
107+
cisco.aci.aci_inbepg_to_contract:
108+
host: apic
109+
username: admin
110+
password: SomeSecretPassword
111+
tenant: mgmt
112+
epg: anstest
113+
contract: anstest_http
114+
contract_type: provider
115+
state: absent
116+
delegate_to: localhost
117+
118+
- name: Query a specific contract to EPG binding
119+
cisco.aci.aci_inbepg_to_contract:
120+
host: apic
121+
username: admin
122+
password: SomeSecretPassword
123+
tenant: anstest
124+
epg: anstest
125+
contract: anstest_http
126+
contract_type: provider
127+
state: query
128+
delegate_to: localhost
129+
register: query_result
130+
131+
- name: Query all provider contract to EPG bindings
132+
cisco.aci.aci_inbepg_to_contract:
133+
host: apic
134+
username: admin
135+
password: SomeSecretPassword
136+
contract_type: provider
137+
state: query
138+
delegate_to: localhost
139+
register: query_result
140+
"""
141+
142+
RETURN = r"""
143+
current:
144+
description: The existing configuration from the APIC after the module has finished
145+
returned: success
146+
type: list
147+
sample:
148+
[
149+
{
150+
"fvTenant": {
151+
"attributes": {
152+
"descr": "Production environment",
153+
"dn": "uni/tn-production",
154+
"name": "production",
155+
"nameAlias": "",
156+
"ownerKey": "",
157+
"ownerTag": ""
158+
}
159+
}
160+
}
161+
]
162+
error:
163+
description: The error information as returned from the APIC
164+
returned: failure
165+
type: dict
166+
sample:
167+
{
168+
"code": "122",
169+
"text": "unknown managed object class foo"
170+
}
171+
raw:
172+
description: The raw output returned by the APIC REST API (xml or json)
173+
returned: parse error
174+
type: str
175+
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
176+
sent:
177+
description: The actual/minimal configuration pushed to the APIC
178+
returned: info
179+
type: list
180+
sample:
181+
{
182+
"fvTenant": {
183+
"attributes": {
184+
"descr": "Production environment"
185+
}
186+
}
187+
}
188+
previous:
189+
description: The original configuration from the APIC before the module has started
190+
returned: info
191+
type: list
192+
sample:
193+
[
194+
{
195+
"fvTenant": {
196+
"attributes": {
197+
"descr": "Production",
198+
"dn": "uni/tn-production",
199+
"name": "production",
200+
"nameAlias": "",
201+
"ownerKey": "",
202+
"ownerTag": ""
203+
}
204+
}
205+
}
206+
]
207+
proposed:
208+
description: The assembled configuration from the user-provided parameters
209+
returned: info
210+
type: dict
211+
sample:
212+
{
213+
"fvTenant": {
214+
"attributes": {
215+
"descr": "Production environment",
216+
"name": "production"
217+
}
218+
}
219+
}
220+
filter_string:
221+
description: The filter string used for the request
222+
returned: failure or debug
223+
type: str
224+
sample: ?rsp-prop-include=config-only
225+
method:
226+
description: The HTTP method used for the request to the APIC
227+
returned: failure or debug
228+
type: str
229+
sample: POST
230+
response:
231+
description: The HTTP response from the APIC
232+
returned: failure or debug
233+
type: str
234+
sample: OK (30 bytes)
235+
status:
236+
description: The HTTP status from the APIC
237+
returned: failure or debug
238+
type: int
239+
sample: 200
240+
url:
241+
description: The HTTP url used for the request to the APIC
242+
returned: failure or debug
243+
type: str
244+
sample: https://10.11.12.13/api/mo/uni/tn-production.json
245+
"""
246+
247+
from ansible.module_utils.basic import AnsibleModule
248+
from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
249+
from ansible_collections.cisco.aci.plugins.module_utils.constants import ACI_CLASS_MAPPING, CONTRACT_LABEL_MAPPING, PROVIDER_MATCH_MAPPING, SUBJ_LABEL_MAPPING
250+
251+
252+
def main():
253+
argument_spec = aci_argument_spec()
254+
argument_spec.update(aci_annotation_spec())
255+
argument_spec.update(
256+
contract_type=dict(type="str", choices=["consumer", "provider", "taboo", "interface"], required=True),
257+
epg_type=dict(type="str", aliases=["type"], choices=["in_band","out_of_band"], required=True)
258+
epg=dict(type="str", aliases=["epg_name"]), # Not required for querying all objects
259+
contract=dict(type="str", aliases=["contract_name", "contract_interface"]), # Not required for querying all objects
260+
priority=dict(type="str", default="unspecified", choices=["level1", "level2", "level3", "level4", "level5", "level6", "unspecified"]),
261+
provider_match=dict(type="str", choices=["all", "at_least_one", "at_most_one", "none"]),
262+
state=dict(type="str", default="present", choices=["absent", "present", "query"]),
263+
)
264+
265+
module = AnsibleModule(
266+
argument_spec=argument_spec,
267+
supports_check_mode=True,
268+
required_if=[
269+
["state", "absent", ["epg", "contract"]],
270+
["state", "present", ["epg","contract"]],
271+
]
272+
)
273+
274+
epg_type = module.params.get("type")
275+
contract = module.params.get("contract")
276+
contract_type = module.params.get("contract_type")
277+
epg = module.params.get("epg")
278+
priority = module.params.get("priority")
279+
provider_match = module.params.get("provider_match")
280+
state = module.params.get("state")
281+
282+
if epg_type=="in_band":
283+
aci_class = ACI_CLASS_MAPPING[contract_type]["class"]
284+
aci_rn = ACI_CLASS_MAPPING[contract_type]["rn"]
285+
aci_name = ACI_CLASS_MAPPING[contract_type]["name"]
286+
class_config={"matchT": provider_match, "prio": priority, aci_name: contract}
287+
288+
if provider_match is not None:
289+
provider_match = PROVIDER_MATCH_MAPPING[provider_match]
290+
291+
if contract_type != "provider" and provider_match is not None:
292+
module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts")
293+
294+
elif epg_type=="out_of_band":
295+
aci_class = "mgmtRsOoBProv"
296+
aci_rn = "rsooBProv"
297+
aci_name = "tnVzOOBBrCPName"
298+
class_config={"prio": priority, aci_name: contract}
299+
300+
if contract_type != "provider":
301+
module.fail_json(msg="out_of_band EPG only supports Provider contract attachment.")
302+
303+
304+
else:
305+
module.fail_json(msg="epg_type can either be \"in_band\" or \"out_of_band\" only.")
306+
307+
308+
class_Map = {
309+
"in_band": [dict(epg_class="mgmtInB",
310+
epg_rn="inb-{0}")],
311+
312+
"out_of_band": [dict(epg_class="mgmtOoB",
313+
epg_rn="oob-{0}"
314+
)]
315+
}
316+
317+
aci = ACIModule(module)
318+
aci.construct_url(
319+
root_class=dict(
320+
aci_class="fvTenant",
321+
aci_rn="tn-mgmt",
322+
module_object="mgmt",
323+
target_filter={"name": "mgmt"},
324+
),
325+
subclass_1=dict(
326+
aci_class="mgmtMgmtP",
327+
aci_rn="mgmtp-default",
328+
module_object="default",
329+
target_filter={"name": "default"},
330+
),
331+
subclass_2=dict(
332+
aci_class=class_Map[epg_type][0]["epg_class"],
333+
aci_rn=class_Map[epg_type][0]["epg_rn"].format(epg),
334+
module_object=epg,
335+
target_filter={"name": epg},
336+
),
337+
subclass_3=dict(
338+
aci_class=aci_class,
339+
aci_rn="{0}{1}".format(aci_rn, contract),
340+
module_object=contract,
341+
target_filter={aci_name: contract},
342+
)
343+
)
344+
345+
aci.get_existing()
346+
347+
if state == "present":
348+
child_configs = []
349+
aci.payload(
350+
aci_class=aci_class,
351+
class_config=class_config,
352+
child_configs=child_configs,
353+
)
354+
355+
aci.get_diff(aci_class=aci_class)
356+
357+
aci.post_config()
358+
359+
elif state == "absent":
360+
aci.delete_config()
361+
362+
aci.exit_json()
363+
364+
365+
if __name__ == "__main__":
366+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# No ACI simulator yet, so not enabled
2+
# unsupported

0 commit comments

Comments
 (0)