Enhance module_utils to be cleaner and support more modules (#28197)
This commit is contained in:
parent
4afce6c99c
commit
5e24fb298e
1 changed files with 100 additions and 15 deletions
|
@ -290,7 +290,6 @@ class ACIModule(object):
|
|||
This is the root dictionary key for the MO's configuration body, or the ACI class of the MO.
|
||||
"""
|
||||
proposed_config = self.result['proposed'][aci_class]['attributes']
|
||||
proposed_children = self.result['proposed'][aci_class].get('children')
|
||||
if self.result['existing']:
|
||||
existing_config = self.result['existing'][0][aci_class]['attributes']
|
||||
config = {}
|
||||
|
@ -303,24 +302,79 @@ class ACIModule(object):
|
|||
|
||||
# add name back to config only if the configs do not match
|
||||
if config:
|
||||
config["name"] = proposed_config["name"]
|
||||
# TODO: If URLs are built with the object's name, then we should be able to leave off adding the name back
|
||||
# config["name"] = proposed_config["name"]
|
||||
config = {aci_class: {'attributes': config}}
|
||||
|
||||
# compare existing child dictionaries with what is in existing
|
||||
if proposed_children:
|
||||
existing_children = self.result['existing'][0][aci_class].get('children', [])
|
||||
children = [relation for relation in proposed_children if relation not in existing_children]
|
||||
|
||||
# check for updates to child configs and update new config dictionary
|
||||
children = self.get_diff_children(aci_class)
|
||||
if children and config:
|
||||
config[aci_class].update({'children': children})
|
||||
elif children:
|
||||
config = {aci_class: {'attributes': {'name': proposed_config['name']}, 'children': children}}
|
||||
config = {aci_class: {'attributes': {}, 'children': children}}
|
||||
|
||||
else:
|
||||
config = self.result['proposed']
|
||||
|
||||
self.result['config'] = config
|
||||
|
||||
@staticmethod
|
||||
def get_diff_child(child_class, proposed_child, existing_child):
|
||||
"""
|
||||
This method is used to get the difference between a proposed and existing child configs. The get_nested_config()
|
||||
method should be used to return the proposed and existing config portions of child.
|
||||
|
||||
:param child_class: Type str.
|
||||
The root class (dict key) for the child dictionary.
|
||||
:param proposed_child: Type dict.
|
||||
The config portion of the proposed child dictionary.
|
||||
:param existing_child: Type dict.
|
||||
The config portion of the existing child dictionary.
|
||||
:return: The child config with only values that are updated. If the proposed dictionary has no updates to make
|
||||
to what exists on the APIC, then None is returned.
|
||||
"""
|
||||
update_config = {child_class: {'attributes': {}}}
|
||||
for key, value in proposed_child.items():
|
||||
if value != existing_child[key]:
|
||||
update_config[child_class]['attributes'][key] = value
|
||||
|
||||
if not update_config[child_class]['attributes']:
|
||||
return None
|
||||
|
||||
return update_config
|
||||
|
||||
def get_diff_children(self, aci_class):
|
||||
"""
|
||||
This method is used to retrieve the updated child configs by comparing the proposed children configs
|
||||
agains the objects existing children configs.
|
||||
|
||||
:param aci_class: Type str.
|
||||
This is the root dictionary key for the MO's configuration body, or the ACI class of the MO.
|
||||
:return: The list of updated child config dictionaries. None is returned if there are no changes to the child
|
||||
configurations.
|
||||
"""
|
||||
proposed_children = self.result['proposed'][aci_class].get('children')
|
||||
if proposed_children:
|
||||
child_updates = []
|
||||
existing_children = self.result['existing'][0][aci_class].get('children', [])
|
||||
|
||||
# Loop through proposed child configs and compare against existing child configuration
|
||||
for child in proposed_children:
|
||||
child_class, proposed_child, existing_child = self.get_nested_config(child, existing_children)
|
||||
|
||||
if existing_child is None:
|
||||
child_update = child
|
||||
else:
|
||||
child_update = self.get_diff_child(child_class, proposed_child, existing_child)
|
||||
|
||||
# Update list of updated child configs only if the child config is different than what exists
|
||||
if child_update:
|
||||
child_updates.append(child_update)
|
||||
else:
|
||||
return None
|
||||
|
||||
return child_updates
|
||||
|
||||
def get_existing(self, filter_string=""):
|
||||
"""
|
||||
This method is used to get the existing object(s) based on the path specified in the module. Each module should
|
||||
|
@ -354,6 +408,32 @@ class ACIModule(object):
|
|||
# Connection error
|
||||
self.module.fail_json(msg='Request failed for %(url)s. %(msg)s' % info)
|
||||
|
||||
@staticmethod
|
||||
def get_nested_config(proposed_child, existing_children):
|
||||
"""
|
||||
This method is used for stiping off the outer layers of the child dictionaries so only the configuration
|
||||
key, value pairs are returned.
|
||||
|
||||
:param proposed_child: Type dict.
|
||||
The dictionary that represents the child config.
|
||||
:param existing_children: Type list.
|
||||
The list of existing child config dictionaries.
|
||||
:return: The child's class as str (root config dict key), the child's proposed config dict, and the child's
|
||||
existing configuration dict.
|
||||
"""
|
||||
for key in proposed_child.keys():
|
||||
child_class = key
|
||||
proposed_config = proposed_child[key]['attributes']
|
||||
existing_config = None
|
||||
|
||||
# get existing dictionary from the list of existing to use for comparison
|
||||
for child in existing_children:
|
||||
if child.get(child_class):
|
||||
existing_config = child[key]['attributes']
|
||||
break
|
||||
|
||||
return child_class, proposed_config, existing_config
|
||||
|
||||
def payload(self, aci_class, class_config, child_configs=None):
|
||||
"""
|
||||
This method is used to dynamically build the proposed configuration dictionary from the config related parameters
|
||||
|
@ -369,18 +449,23 @@ class ACIModule(object):
|
|||
include child objects that are used to associate two MOs together. Children that represent
|
||||
MOs should have their own module.
|
||||
"""
|
||||
proposed = dict((k, v) for k, v in class_config.items() if v)
|
||||
proposed = dict((k, str(v)) for k, v in class_config.items() if v is not None)
|
||||
self.result['proposed'] = {aci_class: {'attributes': proposed}}
|
||||
|
||||
# add child objects to proposed
|
||||
if child_configs:
|
||||
children = []
|
||||
for child in child_configs:
|
||||
has_value = False
|
||||
for root_key in child.keys():
|
||||
for final_keys, values in child[root_key]['attributes'].items():
|
||||
if values is not None:
|
||||
if values is None:
|
||||
child[root_key]['attributes'].pop(final_keys)
|
||||
else:
|
||||
child[root_key]['attributes'][final_keys] = str(values)
|
||||
has_value = True
|
||||
if has_value:
|
||||
children.append(child)
|
||||
break
|
||||
|
||||
if children:
|
||||
self.result['proposed'][aci_class].update(dict(children=children))
|
||||
|
|
Loading…
Reference in a new issue