# -*- 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 __metaclass__ = type from copy import deepcopy from ansible.module_utils.common._collections_compat import Mapping from ansible.module_utils.six import iteritems 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") return sorted( val, key=lambda d: tuple(d[k] for k in sorted_keys[0]) ) 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 ") if not isinstance(other, dict): raise AssertionError("`other` must be of type ") 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 def to_list(val): if isinstance(val, (list, tuple, set)): return list(val) elif val is not None: return [val] else: return list()