Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

construct_deep_url() #53475

Merged
merged 29 commits into from May 22, 2019
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
719bf02
Updates aci.py with the ability to add ACI objects to any depth
RobW3LGA Mar 7, 2019
13b7af7
Two PyTest files to support construct_deep_url
RobW3LGA Mar 7, 2019
2cf2644
Correcting previous upload to incorrect folder
RobW3LGA Mar 7, 2019
e55111e
Deleting for file name change per Matt Clay
RobW3LGA Mar 7, 2019
78a37a7
Deleting for file name change per Matt Clay
RobW3LGA Mar 7, 2019
58220a5
Correcting file names per Matt Clay
RobW3LGA Mar 7, 2019
ba1a485
Wrong location for test file
RobW3LGA Mar 7, 2019
ae87c9d
Wrong location for test file
RobW3LGA Mar 7, 2019
77738dd
First attempt to comply with suggestions
RobW3LGA Mar 7, 2019
7138413
First attempt to comply with suggestions
RobW3LGA Mar 7, 2019
469211f
Second attempt to comply with suggestions
RobW3LGA Mar 7, 2019
64e2f74
Second attempt to comply with suggestions
RobW3LGA Mar 7, 2019
11124ec
Third attempt to comply with suggestions
RobW3LGA Mar 8, 2019
d615d3d
Third attempt to comply with suggestions
RobW3LGA Mar 8, 2019
972709d
Pro Tip: Convert from 'CRLF' to 'LF' in VSCode
RobW3LGA Mar 8, 2019
d53befd
Added setup() support for tests
RobW3LGA Mar 8, 2019
cfc1a67
Continued corrections to support testing
RobW3LGA Mar 8, 2019
fd9e9fe
Added two mocks to support testing
RobW3LGA Mar 8, 2019
c4c8576
Adding tmpdir property to mock_basic.py
RobW3LGA Mar 8, 2019
f8da353
Added last blank line to mock_basic.py
RobW3LGA Mar 8, 2019
83118b4
Attempt to correct setup() issues
RobW3LGA Mar 8, 2019
7abd7e5
Attempt to correct setup() issues
RobW3LGA Mar 8, 2019
06dc681
Attempt to correct setup() issues
RobW3LGA Mar 8, 2019
e936f66
Attempt to correct setup() issues
RobW3LGA Mar 8, 2019
98860cf
Withdrawing pending injectability tweak to aci.py
RobW3LGA Mar 8, 2019
f8c43d5
Withdrawing pending injectability tweak to aci.py
RobW3LGA Mar 8, 2019
1951ef3
Withdrawing pending injectability tweak to aci.py
RobW3LGA Mar 8, 2019
7e98d3b
Withdrawing pending injectability tweak to aci.py
RobW3LGA Mar 8, 2019
b9721be
Merge pull request #1 from ansible/devel
RobW3LGA Mar 8, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
186 changes: 185 additions & 1 deletion lib/ansible/module_utils/network/aci/aci.py
Expand Up @@ -10,6 +10,7 @@
# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# Copyright: (c) 2017, Swetha Chunduri (@schunduri)
# Copyright: (c) 2019, Rob Huelga (@RobW3LGA)
# All rights reserved.

# Redistribution and use in source and binary forms, with or without modification,
Expand Down Expand Up @@ -407,6 +408,189 @@ def build_filter(self, obj_class, params):
elif len(accepted_params) > 1:
return 'and(' + ','.join(['eq({0}.{1}, "{2}")'.format(obj_class, k, v) for (k, v) in accepted_params.items()]) + ')'

def _deep_url_path_builder(self, obj):
target_class = obj['target_class']
target_filter = obj['target_filter']
subtree_class = obj['subtree_class']
subtree_filter = obj['subtree_filter']
object_rn = obj['object_rn']
mo = obj['module_object']
add_subtree_filter = obj['add_subtree_filter']
add_target_filter = obj['add_target_filter']

if self.module.params['state'] in ('absent', 'present') and mo is not None:
self.path = 'api/mo/uni/{0}.json'.format(object_rn)
self.update_qs({'rsp-prop-include': 'config-only'})

else:
# State is 'query'
if object_rn is not None:
# Query for a specific object in the module's class
self.path = 'api/mo/uni/{0}.json'.format(object_rn)
else:
self.path = 'api/class/{0}.json'.format(target_class)

if add_target_filter:
self.update_qs(
{'query-target-filter': self.build_filter(target_class, target_filter)})

if add_subtree_filter:
self.update_qs(
{'rsp-subtree-filter': self.build_filter(subtree_class, subtree_filter)})

if 'port' in self.params and self.params['port'] is not None:
self.url = '{protocol}://{host}:{port}/{path}'.format(
path=self.path, **self.module.params)

else:
self.url = '{protocol}://{host}/{path}'.format(
path=self.path, **self.module.params)

if self.child_classes:
self.update_qs(
{'rsp-subtree': 'full', 'rsp-subtree-class': ','.join(sorted(self.child_classes))})

def _deep_url_parent_object(self, parent_objects, parent_class):

for parent_object in parent_objects:
if parent_object['aci_class'] is parent_class:
return parent_object

return None

def construct_deep_url(self, target_object, parent_objects=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 target_object: The target class dictionary containing parent_class, aci_class, aci_rn, target_filter, and module_object keys.
:param parent_objects: The parent class list of dictionaries containing parent_class, aci_class, aci_rn, target_filter, and module_object keys.
:param child_classes: The list of child classes that the module supports along with the object.
:type target_object: dict
:type parent_objects: list[dict]
:type child_classes: list[string]
:return: The path and filter_string needed to build the full URL.
"""

self.filter_string = ''
rn_builder = None
subtree_classes = None
add_subtree_filter = False
add_target_filter = False
has_target_query = False
has_target_query_compare = False
has_target_query_difference = False
has_target_query_called = False

if child_classes is None:
self.child_classes = set()
else:
self.child_classes = set(child_classes)

target_parent_class = target_object['parent_class']
target_class = target_object['aci_class']
target_rn = target_object['aci_rn']
target_filter = target_object['target_filter']
target_module_object = target_object['module_object']

url_path_object = dict(
target_class=target_class,
target_filter=target_filter,
subtree_class=target_class,
subtree_filter=target_filter,
module_object=target_module_object
)

if target_module_object is not None:
rn_builder = target_rn
else:
has_target_query = True
has_target_query_compare = True

if parent_objects is not None:
current_parent_class = target_parent_class
has_parent_query_compare = False
has_parent_query_difference = False
is_first_parent = True
is_single_parent = None
search_classes = set()

while current_parent_class != 'uni':
parent_object = self._deep_url_parent_object(
parent_objects=parent_objects, parent_class=current_parent_class)

if parent_object is not None:
parent_parent_class = parent_object['parent_class']
parent_class = parent_object['aci_class']
parent_rn = parent_object['aci_rn']
parent_filter = parent_object['target_filter']
parent_module_object = parent_object['module_object']

if is_first_parent:
is_single_parent = True
else:
is_single_parent = False
is_first_parent = False

if parent_parent_class != 'uni':
search_classes.add(parent_class)

if parent_module_object is not None:
if rn_builder is not None:
rn_builder = '{0}/{1}'.format(parent_rn,
rn_builder)
else:
rn_builder = parent_rn

url_path_object['target_class'] = parent_class
url_path_object['target_filter'] = parent_filter

has_target_query = False
else:
rn_builder = None
subtree_classes = search_classes

has_target_query = True
if is_single_parent:
has_parent_query_compare = True

current_parent_class = parent_parent_class
else:
raise ValueError("Reference error for parent_class '{0}'. Each parent_class must reference a valid object".format(current_parent_class))

if not has_target_query_difference and not has_target_query_called:
if has_target_query is not has_target_query_compare:
has_target_query_difference = True
else:
if not has_parent_query_difference and has_target_query is not has_parent_query_compare:
has_parent_query_difference = True
has_target_query_called = True

if not has_parent_query_difference and has_parent_query_compare and target_module_object is not None:
add_target_filter = True

elif has_parent_query_difference and target_module_object is not None:
add_subtree_filter = True
self.child_classes.add(target_class)

if has_target_query:
add_target_filter = True

elif has_parent_query_difference and not has_target_query and target_module_object is None:
self.child_classes.add(target_class)
self.child_classes.update(subtree_classes)

elif not has_parent_query_difference and not has_target_query and target_module_object is None:
self.child_classes.add(target_class)

elif not has_target_query and is_single_parent and target_module_object is None:
self.child_classes.add(target_class)

url_path_object['object_rn'] = rn_builder
url_path_object['add_subtree_filter'] = add_subtree_filter
url_path_object['add_target_filter'] = add_target_filter

self._deep_url_path_builder(url_path_object)

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.
Expand Down Expand Up @@ -446,7 +630,7 @@ def construct_url(self, root_class, subclass_1=None, subclass_2=None, subclass_3

if self.child_classes:
# Append child_classes to filter_string if filter string is empty
self.update_qs({'rsp-subtree': 'full', 'rsp-subtree-class': ','.join(self.child_classes)})
self.update_qs({'rsp-subtree': 'full', 'rsp-subtree-class': ','.join(sorted(self.child_classes))})
dagwieers marked this conversation as resolved.
Show resolved Hide resolved

def _construct_url_1(self, obj):
"""
Expand Down