2020-10-19 15:23:50 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2020 Red Hat
|
|
|
|
# GNU General Public License v3.0+
|
|
|
|
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
|
2022-05-26 17:18:57 +00:00
|
|
|
|
2020-10-19 15:23:50 +00:00
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
from copy import deepcopy
|
|
|
|
|
|
|
|
from ansible.module_utils.common._collections_compat import Mapping
|
2020-10-28 20:39:20 +00:00
|
|
|
from ansible.module_utils.six import iteritems
|
2020-10-19 15:23:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
def sort_list(val):
|
|
|
|
if isinstance(val, list):
|
|
|
|
if isinstance(val[0], dict):
|
|
|
|
sorted_keys = [tuple(sorted(dict_.keys())) for dict_ in val]
|
|
|
|
# All keys should be identical
|
|
|
|
if len(set(sorted_keys)) != 1:
|
|
|
|
raise ValueError("dictionaries do not match")
|
|
|
|
|
2022-05-26 17:18:57 +00:00
|
|
|
return sorted(val, key=lambda d: tuple(d[k] for k in sorted_keys[0]))
|
2020-10-19 15:23:50 +00:00
|
|
|
return sorted(val)
|
|
|
|
return val
|
|
|
|
|
|
|
|
|
|
|
|
def dict_merge(base, other):
|
|
|
|
"""Return a new dict object that combines base and other
|
|
|
|
|
|
|
|
This will create a new dict object that is a combination of the key/value
|
|
|
|
pairs from base and other. When both keys exist, the value will be
|
|
|
|
selected from other.
|
|
|
|
|
|
|
|
If the value in base is a list, and the value in other is a list
|
|
|
|
the base list will be extended with the values from the other list that were
|
|
|
|
not already present in the base list
|
|
|
|
|
|
|
|
If the value in base is a list, and the value in other is a list
|
|
|
|
and the two have the same entries, the value from other will be
|
|
|
|
used, preserving the order from the other list
|
|
|
|
|
|
|
|
If the value in base is a list, and the value in other is not a list
|
|
|
|
the value from other will be used
|
|
|
|
|
|
|
|
:param base: dict object to serve as base
|
|
|
|
:param other: dict object to combine with base
|
|
|
|
|
|
|
|
:returns: new combined dict object
|
|
|
|
"""
|
|
|
|
if not isinstance(base, dict):
|
|
|
|
raise AssertionError("`base` must be of type <dict>")
|
|
|
|
if not isinstance(other, dict):
|
|
|
|
raise AssertionError("`other` must be of type <dict>")
|
|
|
|
|
|
|
|
combined = dict()
|
|
|
|
|
|
|
|
for key, value in iteritems(deepcopy(base)):
|
|
|
|
if isinstance(value, dict):
|
|
|
|
if key in other:
|
|
|
|
item = other.get(key)
|
|
|
|
if item is not None:
|
|
|
|
if isinstance(other[key], Mapping):
|
|
|
|
combined[key] = dict_merge(value, other[key])
|
|
|
|
else:
|
|
|
|
combined[key] = other[key]
|
|
|
|
else:
|
|
|
|
combined[key] = item
|
|
|
|
else:
|
|
|
|
combined[key] = value
|
|
|
|
elif isinstance(value, list):
|
|
|
|
if key in other:
|
|
|
|
item = other.get(key)
|
|
|
|
if isinstance(item, list):
|
|
|
|
if sort_list(value) == sort_list(item):
|
|
|
|
combined[key] = item
|
|
|
|
else:
|
|
|
|
value.extend([i for i in item if i not in value])
|
|
|
|
combined[key] = value
|
|
|
|
else:
|
|
|
|
combined[key] = item
|
|
|
|
else:
|
|
|
|
combined[key] = value
|
|
|
|
else:
|
|
|
|
if key in other:
|
|
|
|
other_value = other.get(key)
|
|
|
|
if other_value is not None:
|
|
|
|
if sort_list(base[key]) != sort_list(other_value):
|
|
|
|
combined[key] = other_value
|
|
|
|
else:
|
|
|
|
combined[key] = value
|
|
|
|
else:
|
|
|
|
combined[key] = other_value
|
|
|
|
else:
|
|
|
|
combined[key] = value
|
|
|
|
|
|
|
|
for key in set(other.keys()).difference(base.keys()):
|
|
|
|
combined[key] = other.get(key)
|
|
|
|
|
|
|
|
return combined
|
2020-10-28 20:39:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
def to_list(val):
|
|
|
|
if isinstance(val, (list, tuple, set)):
|
|
|
|
return list(val)
|
|
|
|
elif val is not None:
|
|
|
|
return [val]
|
|
|
|
else:
|
|
|
|
return list()
|