ACI: Update ACIModule to dynamically build URLs (#28618)
This commit is contained in:
parent
2634ef955a
commit
ddd3e6fb91
1 changed files with 261 additions and 5 deletions
|
@ -61,6 +61,39 @@ aci_argument_spec = dict(
|
|||
validate_certs=dict(type='bool', default=True),
|
||||
)
|
||||
|
||||
URL_MAPPING = dict(
|
||||
action_rule=dict(aci_class='rtctrlAttrP', mo='attr-', key='name'),
|
||||
aep=dict(aci_class='infraAttEntityP', mo='infra/attentp-', key='name'),
|
||||
ap=dict(aci_class='fvAp', mo='ap-', key='name'),
|
||||
bd=dict(aci_class='fvBD', mo='BD-', key='name'),
|
||||
bd_l3out=dict(aci_class='fvRsBDToOut', mo='rsBDToOut-', key='tnL3extOutName'),
|
||||
contract=dict(aci_class='vzBrCP', mo='brc-', key='name'),
|
||||
entry=dict(aci_class='vzEntry', mo='e-', key='name'),
|
||||
epg=dict(aci_class='fvAEPg', mo='epg-', key='name'),
|
||||
epg_consumer=dict(aci_class='fvRsCons', mo='rscons-', key='tnVzBrCPName'),
|
||||
epg_domain=dict(aci_class='fvRsDomAtt', mo='rsdomAtt-', key='tDn'),
|
||||
epg_provider=dict(aci_class='fvRsProv', mo='rsprov-', key='tnVzBrCPName'),
|
||||
epr_policy=dict(aci_class='fvEpRetPol', mo='epRPol-', key='name'),
|
||||
fc_policy=dict(aci_class='fcIfPol', mo='infra/fcIfPol-', key='name'),
|
||||
filter=dict(aci_class='vzFilter', mo='flt-', key='name'),
|
||||
gateway_addr=dict(aci_class='fvSubnet', mo='subnet-', key='ip'),
|
||||
l2_policy=dict(aci_class='l2IfPol', mo='infra/l2IfP-', key='name'),
|
||||
lldp_policy=dict(aci_class='lldpIfPol', mo='infra/lldpIfP-', key='name'),
|
||||
mcp=dict(aci_class='mcpIfPol', mo='infra/mcpIfP-', key='name'),
|
||||
monitoring_policy=dict(aci_class='monEPGPol', mo='monepg-', key='name'),
|
||||
port_channel=dict(aci_class='lacpLagPol', mo='infra/lacplagp-', key='name'),
|
||||
port_security=dict(aci_class='l2PortSecurityPol', mo='infra/portsecurityP-', key='name'),
|
||||
rtp=dict(aci_class='l3extRouteTagPol', mo='rttag-', key='name'),
|
||||
subject=dict(aci_class='vzSubj', mo='subj-', key='name'),
|
||||
subject_filter=dict(aci_class='vzRsSubjFiltAtt', mo='rssubjFiltAtt-', key='tnVzFilterName'),
|
||||
taboo_contract=dict(aci_class='vzTaboo', mo='taboo-', key='name'),
|
||||
tenant=dict(aci_class='fvTenant', mo='tn-', key='name'),
|
||||
tenant_span_dst_grp=dict(aci_class='spanDestGrp', mo='destgrp-', key='name'),
|
||||
tenant_span_src_grp=dict(aci_class='spanSrcGrp', mo='srcgrp-', key='name'),
|
||||
tenant_span_src_grp_dst_grp=dict(aci_class='spanSpanLbl', mo='spanlbl-', key='name'),
|
||||
vrf=dict(aci_class='fvCtx', mo='ctx-', key='name'),
|
||||
)
|
||||
|
||||
|
||||
def aci_response_error(result):
|
||||
''' Set error information when found '''
|
||||
|
@ -243,6 +276,231 @@ class ACIModule(object):
|
|||
if self.result['diff']['before'] != self.result['diff']['after']:
|
||||
self.result['changed'] = True
|
||||
|
||||
def construct_url(self, root_class, subclass_1=None, subclass_2=None, subclass_3=None, child_classes=None):
|
||||
"""
|
||||
This method is used to retrieve the appropriate URL path and filter_string to make the request to the APIC.
|
||||
|
||||
:param root_class: Type str.
|
||||
The top-level class naming parameter per the modules (EX: tenant).
|
||||
:param sublass_1: Type str.
|
||||
The second-level class naming parameter per the modules (EX: bd).
|
||||
:param sublass_2: Type str.
|
||||
The third-level class naming parameter per the modules (EX: gateway).
|
||||
:param sublass_3: Type str.
|
||||
The fourth-level class naming parameter per the modules.
|
||||
:param child_classes: Type tuple.
|
||||
The list of child classes that the module supports along with the object.
|
||||
:return: The path and filter_string needed to build the full URL.
|
||||
"""
|
||||
if child_classes is None:
|
||||
child_includes = ''
|
||||
else:
|
||||
child_includes = ','.join(child_classes)
|
||||
child_includes = '&rsp-subtree=full&rsp-subtree-class=' + child_includes
|
||||
|
||||
if subclass_3 is not None:
|
||||
path, filter_string = self._construct_url_4(root_class, subclass_1, subclass_2, subclass_3, child_includes)
|
||||
elif subclass_2 is not None:
|
||||
path, filter_string = self._construct_url_3(root_class, subclass_1, subclass_2, child_includes)
|
||||
elif subclass_1 is not None:
|
||||
path, filter_string = self._construct_url_2(root_class, subclass_1, child_includes)
|
||||
else:
|
||||
path, filter_string = self._construct_url_1(root_class, child_includes)
|
||||
|
||||
self.result['url'] = '{}://{}/{}'.format(self.module.params['protocol'], self.module.params['hostname'], path)
|
||||
self.result['filter_string'] = filter_string
|
||||
|
||||
def _construct_url_1(self, obj_class, child_includes):
|
||||
"""
|
||||
This method is used by get_url when the object is the top-level class.
|
||||
"""
|
||||
obj = self.module.params.get(obj_class)
|
||||
obj_dict = URL_MAPPING[obj_class]
|
||||
obj_class = obj_dict['aci_class']
|
||||
obj_mo = obj_dict['mo']
|
||||
|
||||
# State is present or absent
|
||||
if self.module.params['state'] != 'query':
|
||||
path = 'api/mo/uni/{}[{}].json'.format(obj_mo, obj)
|
||||
filter_string = '?rsp-prop-include=config-only' + child_includes
|
||||
# Query for all objects of the module's class
|
||||
elif obj is None:
|
||||
path = 'api/class/{}.json'.format(obj_class)
|
||||
filter_string = ''
|
||||
# Query for a specific object in the module's class
|
||||
else:
|
||||
path = 'api/mo/uni/{}[{}].json'.format(obj_mo, obj)
|
||||
filter_string = ''
|
||||
|
||||
# Append child_includes to filter_string if filter string is empty
|
||||
if child_includes is not None and filter_string == '':
|
||||
filter_string = child_includes.replace('&', '?', 1)
|
||||
|
||||
return path, filter_string
|
||||
|
||||
def _construct_url_2(self, parent_class, obj_class, child_includes):
|
||||
"""
|
||||
This method is used by get_url when the object is the second-level class.
|
||||
"""
|
||||
parent = self.module.params.get(parent_class)
|
||||
parent_dict = URL_MAPPING[parent_class]
|
||||
parent_class = parent_dict['aci_class']
|
||||
parent_mo = parent_dict['mo']
|
||||
obj = self.module.params.get(obj_class)
|
||||
obj_dict = URL_MAPPING[obj_class]
|
||||
obj_class = obj_dict['aci_class']
|
||||
obj_mo = obj_dict['mo']
|
||||
obj_key = obj_dict['key']
|
||||
|
||||
if not child_includes:
|
||||
self_child_includes = '?rsp-subtree=full&rsp-subtree-class=' + obj_class
|
||||
else:
|
||||
self_child_includes = child_includes.replace('&', '?', 1) + ',' + obj_class
|
||||
|
||||
# State is present or absent
|
||||
if self.module.params['state'] != 'query':
|
||||
path = 'api/mo/uni/{}[{}]/{}[{}].json'.format(parent_mo, parent, obj_mo, obj)
|
||||
filter_string = '?rsp-prop-include=config-only' + child_includes
|
||||
# Query for all objects of the module's class
|
||||
elif obj is None and parent is None:
|
||||
path = 'api/class/{}.json'.format(obj_class)
|
||||
filter_string = ''
|
||||
# Queries when parent object is provided
|
||||
elif parent is not None:
|
||||
# Query for specific object in the module's class
|
||||
if obj is not None:
|
||||
path = 'api/mo/uni/{}[{}]/{}[{}].json'.format(parent_mo, parent, obj_mo, obj)
|
||||
filter_string = ''
|
||||
# Query for all object's of the module's class that belong to a specific parent object
|
||||
else:
|
||||
path = 'api/mo/uni/{}[{}].json'.format(parent_mo, parent)
|
||||
filter_string = self_child_includes
|
||||
# Query for all objects of the module's class that match the provided ID value
|
||||
else:
|
||||
path = 'api/class/{}.json'.format(obj_class)
|
||||
filter_string = '?query-target-filter=eq({}.{}, \"{}\")'.format(obj_class, obj_key, obj) + child_includes
|
||||
|
||||
# Append child_includes to filter_string if filter string is empty
|
||||
if child_includes is not None and filter_string == '':
|
||||
filter_string = child_includes.replace('&', '?', 1)
|
||||
|
||||
return path, filter_string
|
||||
|
||||
def _construct_url_3(self, root_class, parent_class, obj_class, child_includes):
|
||||
"""
|
||||
This method is used by get_url when the object is the third-level class.
|
||||
"""
|
||||
root = self.module.params.get(root_class)
|
||||
root_dict = URL_MAPPING[root_class]
|
||||
root_class = root_dict['aci_class']
|
||||
root_mo = root_dict['mo']
|
||||
parent = self.module.params.get(parent_class)
|
||||
parent_dict = URL_MAPPING[parent_class]
|
||||
parent_class = parent_dict['aci_class']
|
||||
parent_mo = parent_dict['mo']
|
||||
parent_key = parent_dict['key']
|
||||
obj = self.module.params.get(obj_class)
|
||||
obj_dict = URL_MAPPING[obj_class]
|
||||
obj_class = obj_dict['aci_class']
|
||||
obj_mo = obj_dict['mo']
|
||||
obj_key = obj_dict['key']
|
||||
|
||||
if not child_includes:
|
||||
self_child_includes = '&rsp-subtree=full&rsp-subtree-class=' + obj_class
|
||||
else:
|
||||
self_child_includes = '{},{}'.format(child_includes, obj_class)
|
||||
|
||||
if not child_includes:
|
||||
parent_self_child_includes = '&rsp-subtree=full&rsp-subtree-class={},{}'.format(parent_class, obj_class)
|
||||
else:
|
||||
parent_self_child_includes = '{},{},{}'.format(child_includes, parent_class, obj_class)
|
||||
|
||||
# State is ablsent or present
|
||||
if self.module.params['state'] != 'query':
|
||||
path = 'api/mo/uni/{}[{}]/{}[{}]/{}[{}].json'.format(root_mo, root, parent_mo, parent, obj_mo, obj)
|
||||
filter_string = '?rsp-prop-include=config-only' + child_includes
|
||||
# Query for all objects of the module's class
|
||||
elif obj is None and parent is None and root is None:
|
||||
path = 'api/class/{}.json'.format(obj_class)
|
||||
filter_string = ''
|
||||
# Queries when root object is provided
|
||||
elif root is not None:
|
||||
# Queries when parent object is provided
|
||||
if parent is not None:
|
||||
# Query for a specific object of the module's class
|
||||
if obj is not None:
|
||||
path = 'api/mo/uni/{}[{}]/{}[{}]/{}[{}].json'.format(root_mo, root, parent_mo, parent, obj_mo, obj)
|
||||
filter_string = ''
|
||||
# Query for all objects of the module's class that belong to a specific parent object
|
||||
else:
|
||||
path = 'api/mo/uni/{}[{}]/{}[{}].json'.format(root_mo, root, parent_mo, parent)
|
||||
filter_string = self_child_includes.replace('&', '?', 1)
|
||||
# Query for all objects of the module's class that match the provided ID value and belong to a specefic root object
|
||||
elif obj is not None:
|
||||
path = 'api/mo/uni/{}[{}].json'.format(root_mo, root)
|
||||
filter_string = '?rsp-subtree-filter=eq({}.{}, \"{}\"){}'.format(obj_class, obj_key, obj, self_child_includes)
|
||||
# Query for all objects of the module's class that belong to a specific root object
|
||||
else:
|
||||
path = 'api/mo/uni/{}[{}].json'.format(root_mo, root)
|
||||
filter_string = '?' + parent_self_child_includes
|
||||
# Queries when parent object is provided but root object is not provided
|
||||
elif parent is not None:
|
||||
# Query for all objects of the module's class that belong to any parent class
|
||||
# matching the provided ID values for both object and parent object
|
||||
if obj is not None:
|
||||
path = 'api/class/{}.json'.format(parent_class)
|
||||
filter_string = '?query-target-filter=eq({}.{}, \"{}\"){}&rsp-subtree-filter=eq({}.{}, \"{}\")'.format(
|
||||
parent_class, parent_key, parent, self_child_includes, obj_class, obj_key, obj)
|
||||
# Query for all objects of the module's class that belong to any parent class
|
||||
# matching the provided ID value for the parent object
|
||||
else:
|
||||
path = 'api/class/{}.json'.format(parent_class)
|
||||
filter_string = '?query-target-filter=eq({}.{}, \"{}\"){}'.format(parent_class, parent_key, parent, self_child_includes)
|
||||
# Query for all objects of the module's class matching the provided ID value of the object
|
||||
else:
|
||||
path = 'api/class/{}.json'.format(obj_class)
|
||||
filter_string = '?query-target-filter=eq({}.{}, \"{}\")'.format(obj_class, obj_key, obj) + child_includes
|
||||
|
||||
# append child_includes to filter_string if filter string is empty
|
||||
if child_includes is not None and filter_string == '':
|
||||
filter_string = child_includes.replace('&', '?', 1)
|
||||
|
||||
return path, filter_string
|
||||
|
||||
def _construct_url_4(self, root_class, sec_class, parent_class, obj_class, child_includes):
|
||||
"""
|
||||
This method is used by get_url when the object is the third-level class.
|
||||
"""
|
||||
root = self.module.params.get(root_class)
|
||||
root_dict = URL_MAPPING[root_class]
|
||||
root_class = root_dict['aci_class']
|
||||
root_mo = root_dict['mo']
|
||||
sec = self.module.params.get(sec_class)
|
||||
sec_dict = URL_MAPPING[sec_class]
|
||||
sec_class = sec_dict['aci_class']
|
||||
sec_mo = sec_dict['mo']
|
||||
# sec_key = sec_dict['key']
|
||||
parent = self.module.params.get(parent_class)
|
||||
parent_dict = URL_MAPPING[parent_class]
|
||||
parent_class = parent_dict['aci_class']
|
||||
parent_mo = parent_dict['mo']
|
||||
# parent_key = parent_dict['key']
|
||||
obj = self.module.params.get(obj_class)
|
||||
obj_dict = URL_MAPPING[obj_class]
|
||||
obj_class = obj_dict['aci_class']
|
||||
obj_mo = obj_dict['mo']
|
||||
# obj_key = obj_dict['key']
|
||||
|
||||
# State is ablsent or present
|
||||
if self.module.params['state'] != 'query':
|
||||
path = 'api/mo/uni/{}[{}]/{}[{}]/{}[{}]/{}[{}].json'.format(root_mo, root, sec_mo, sec, parent_mo, parent, obj_mo, obj)
|
||||
filter_string = '?rsp-prop-include=config-only' + child_includes
|
||||
else:
|
||||
path = 'api/class/{}.json'.format(obj_class)
|
||||
filter_string = child_includes
|
||||
|
||||
return path, filter_string
|
||||
|
||||
def delete_config(self):
|
||||
"""
|
||||
This method is used to handle the logic when the modules state is equal to absent. The method only pushes a change if
|
||||
|
@ -375,18 +633,16 @@ class ACIModule(object):
|
|||
|
||||
return child_updates
|
||||
|
||||
def get_existing(self, filter_string=""):
|
||||
def get_existing(self):
|
||||
"""
|
||||
This method is used to get the existing object(s) based on the path specified in the module. Each module should
|
||||
build the URL so that if the object's name is supplied, then it will retrieve the configuration for that particular
|
||||
object, but if no name is supplied, then it will retrieve all MOs for the class. Following this method will ensure
|
||||
that this method can be used to supply the existing configuration when using the get_diff method. The response, status,
|
||||
and existing configuration will be added to the self.result dictionary.
|
||||
|
||||
:param filter_string: Type str.
|
||||
The filter to use in order to retrieve the filtered configuration.
|
||||
"""
|
||||
uri = self.result['url'] + filter_string
|
||||
uri = self.result['url'] + self.result['filter_string']
|
||||
|
||||
resp, info = fetch_url(self.module, uri,
|
||||
headers=self.headers,
|
||||
method='GET',
|
||||
|
|
Loading…
Add table
Reference in a new issue