ACI: Update ACIModule to dynamically build URLs (#28618)

pull/27817/head
Jacob McGill 7 years ago committed by Dag Wieers
parent 2634ef955a
commit ddd3e6fb91

@ -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…
Cancel
Save