Skip to content

Commit d44b606

Browse files
committed
enhancement for node_mgmt_epg_to_contract
1 parent aa8b24c commit d44b606

File tree

3 files changed

+855
-0
lines changed

3 files changed

+855
-0
lines changed
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_epg_to_contract:
96+
host: apic
97+
username: admin
98+
password: SomeSecretPassword
99+
tenant: anstest
100+
ap: anstest
101+
epg: anstest
102+
contract: anstest_http
103+
contract_type: provider
104+
contract_label: contractlabel
105+
subject_label: subjlabel
106+
state: present
107+
delegate_to: localhost
108+
109+
- name: Remove an existing contract to EPG binding
110+
cisco.aci.aci_epg_to_contract:
111+
host: apic
112+
username: admin
113+
password: SomeSecretPassword
114+
tenant: anstest
115+
ap: anstest
116+
epg: anstest
117+
contract: anstest_http
118+
contract_type: provider
119+
state: absent
120+
delegate_to: localhost
121+
122+
- name: Query a specific contract to EPG binding
123+
cisco.aci.aci_epg_to_contract:
124+
host: apic
125+
username: admin
126+
password: SomeSecretPassword
127+
tenant: anstest
128+
ap: anstest
129+
epg: anstest
130+
contract: anstest_http
131+
contract_type: provider
132+
state: query
133+
delegate_to: localhost
134+
register: query_result
135+
136+
- name: Query all provider contract to EPG bindings
137+
cisco.aci.aci_epg_to_contract:
138+
host: apic
139+
username: admin
140+
password: SomeSecretPassword
141+
contract_type: provider
142+
state: query
143+
delegate_to: localhost
144+
register: query_result
145+
"""
146+
147+
RETURN = r"""
148+
current:
149+
description: The existing configuration from the APIC after the module has finished
150+
returned: success
151+
type: list
152+
sample:
153+
[
154+
{
155+
"fvTenant": {
156+
"attributes": {
157+
"descr": "Production environment",
158+
"dn": "uni/tn-production",
159+
"name": "production",
160+
"nameAlias": "",
161+
"ownerKey": "",
162+
"ownerTag": ""
163+
}
164+
}
165+
}
166+
]
167+
error:
168+
description: The error information as returned from the APIC
169+
returned: failure
170+
type: dict
171+
sample:
172+
{
173+
"code": "122",
174+
"text": "unknown managed object class foo"
175+
}
176+
raw:
177+
description: The raw output returned by the APIC REST API (xml or json)
178+
returned: parse error
179+
type: str
180+
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
181+
sent:
182+
description: The actual/minimal configuration pushed to the APIC
183+
returned: info
184+
type: list
185+
sample:
186+
{
187+
"fvTenant": {
188+
"attributes": {
189+
"descr": "Production environment"
190+
}
191+
}
192+
}
193+
previous:
194+
description: The original configuration from the APIC before the module has started
195+
returned: info
196+
type: list
197+
sample:
198+
[
199+
{
200+
"fvTenant": {
201+
"attributes": {
202+
"descr": "Production",
203+
"dn": "uni/tn-production",
204+
"name": "production",
205+
"nameAlias": "",
206+
"ownerKey": "",
207+
"ownerTag": ""
208+
}
209+
}
210+
}
211+
]
212+
proposed:
213+
description: The assembled configuration from the user-provided parameters
214+
returned: info
215+
type: dict
216+
sample:
217+
{
218+
"fvTenant": {
219+
"attributes": {
220+
"descr": "Production environment",
221+
"name": "production"
222+
}
223+
}
224+
}
225+
filter_string:
226+
description: The filter string used for the request
227+
returned: failure or debug
228+
type: str
229+
sample: ?rsp-prop-include=config-only
230+
method:
231+
description: The HTTP method used for the request to the APIC
232+
returned: failure or debug
233+
type: str
234+
sample: POST
235+
response:
236+
description: The HTTP response from the APIC
237+
returned: failure or debug
238+
type: str
239+
sample: OK (30 bytes)
240+
status:
241+
description: The HTTP status from the APIC
242+
returned: failure or debug
243+
type: int
244+
sample: 200
245+
url:
246+
description: The HTTP url used for the request to the APIC
247+
returned: failure or debug
248+
type: str
249+
sample: https://10.11.12.13/api/mo/uni/tn-production.json
250+
"""
251+
252+
from ansible.module_utils.basic import AnsibleModule
253+
from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
254+
from ansible_collections.cisco.aci.plugins.module_utils.constants import ACI_CLASS_MAPPING, CONTRACT_LABEL_MAPPING, PROVIDER_MATCH_MAPPING, SUBJ_LABEL_MAPPING
255+
256+
257+
def main():
258+
argument_spec = aci_argument_spec()
259+
argument_spec.update(aci_annotation_spec())
260+
argument_spec.update(
261+
type=dict(type="str", choices=["in_band", "out_of_band"], required=True) #>>>> really required, both are under the same class
262+
contract_type=dict(type="str", required=True, choices=["consumer", "provider", "taboo", "interface"]),
263+
# ap=dict(type="str", aliases=["app_profile", "app_profile_name"]), # Not required for querying all objects
264+
epg=dict(type="str", aliases=["epg_name"]), # Not required for querying all objects
265+
contract=dict(type="str", aliases=["contract_name", "contract_interface"]), # Not required for querying all objects
266+
priority=dict(type="str", choices=["level1", "level2", "level3", "level4", "level5", "level6", "unspecified"]),
267+
provider_match=dict(type="str", choices=["all", "at_least_one", "at_most_one", "none"]),
268+
state=dict(type="str", default="present", choices=["absent", "present", "query"]),
269+
# tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects >>>>>> HARDCODE mgmt TENANT ???
270+
# contract_label=dict(type="str"),
271+
# subject_label=dict(type="str"),
272+
)
273+
274+
module = AnsibleModule(
275+
argument_spec=argument_spec,
276+
supports_check_mode=True,
277+
required_if=[
278+
["state", "absent", ["contract", "epg"]],
279+
["state", "present", ["contract", "epg"]],
280+
],
281+
)
282+
283+
# ap = module.params.get("ap")
284+
contract = module.params.get("contract")
285+
contract_type = module.params.get("contract_type")
286+
epg = module.params.get("epg")
287+
priority = module.params.get("priority")
288+
provider_match = module.params.get("provider_match")
289+
if provider_match is not None:
290+
provider_match = PROVIDER_MATCH_MAPPING[provider_match]
291+
state = module.params.get("state")
292+
# tenant = module.params.get("tenant")
293+
# contract_label = module.params.get("contract_label")
294+
# subject_label = module.params.get("subject_label")
295+
296+
aci_class = ACI_CLASS_MAPPING[contract_type]["class"]
297+
aci_rn = ACI_CLASS_MAPPING[contract_type]["rn"]
298+
aci_name = ACI_CLASS_MAPPING[contract_type]["name"]
299+
child_classes = []
300+
301+
if contract_type != "provider" and provider_match is not None:
302+
module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts")
303+
304+
# if contract_type in ["taboo", "interface"] and (contract_label is not None or subject_label is not None):
305+
# module.fail_json(msg="the 'contract_label' and 'subject_label' are not configurable for {0} contracts".format(contract_type))
306+
307+
if contract_type not in ["taboo", "interface"]:
308+
contract_label_class = CONTRACT_LABEL_MAPPING.get(contract_type)
309+
subject_label_class = SUBJ_LABEL_MAPPING.get(contract_type)
310+
child_classes = [subject_label_class, contract_label_class]
311+
312+
aci = ACIModule(module)
313+
aci.construct_url(
314+
root_class=dict(
315+
aci_class="fvTenant",
316+
aci_rn="tn-mgmt",
317+
module_object="mgmt",
318+
target_filter={"name": "mgmt"},
319+
),
320+
subclass_1=dict(
321+
aci_class="mgmtMgmtP",
322+
aci_rn="mgmtp-default",
323+
module_object="default",
324+
target_filter={"name": "default"},
325+
),
326+
subclass_2=dict(
327+
aci_class="fvAEPg",
328+
aci_rn="epg-{0}".format(epg),
329+
module_object=epg,
330+
target_filter={"name": epg},
331+
),
332+
subclass_3=dict(
333+
aci_class=aci_class,
334+
aci_rn="{0}{1}".format(aci_rn, contract),
335+
module_object=contract,
336+
target_filter={aci_name: contract},
337+
),
338+
child_classes=child_classes, #>>>>> are child classes present under the DN???? Remove if not.
339+
)
340+
341+
aci.get_existing()
342+
343+
if state == "present":
344+
child_configs = []
345+
# if contract_label is not None:
346+
# child_configs.append({contract_label_class: {"attributes": {"name": contract_label}}})
347+
# if subject_label is not None:
348+
# child_configs.append({subject_label_class: {"attributes": {"name": subject_label}}})
349+
aci.payload(
350+
aci_class=aci_class,
351+
class_config={"matchT": provider_match, "prio": priority, aci_name: contract},
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)