diff --git a/netjsonconfig/backends/base/backend.py b/netjsonconfig/backends/base/backend.py index c6da39279..e874d7ed8 100644 --- a/netjsonconfig/backends/base/backend.py +++ b/netjsonconfig/backends/base/backend.py @@ -26,7 +26,14 @@ class BaseBackend(object): FILE_SECTION_DELIMITER = '# ---------- files ---------- #' list_identifiers = [] - def __init__(self, config=None, native=None, templates=None, context=None): + def __init__( + self, + config=None, + native=None, + templates=None, + templates_list_handling=None, + context=None, + ): """ :param config: ``dict`` containing a valid **NetJSON** configuration dictionary :param native: ``str`` or file object representing a native configuration that will @@ -44,7 +51,7 @@ def __init__(self, config=None, native=None, templates=None, context=None): if config is not None: # perform deepcopy to avoid modifying the original config argument config = deepcopy(self._load(config)) - self.config = self._merge_config(config, templates) + self.config = self._merge_config(config, templates, templates_list_handling) self.config = self._evaluate_vars(self.config, context) # backward conversion (native configuration > NetJSON) elif native is not None: @@ -70,7 +77,7 @@ def _load(self, config): ) return config - def _merge_config(self, config, templates): + def _merge_config(self, config, templates, templates_list_handling): """ Merges config with templates """ @@ -82,8 +89,18 @@ def _merge_config(self, config, templates): # merge templates with main configuration result = {} config_list = templates + [config] - for merging in config_list: - result = merge_config(result, self._load(merging), self.list_identifiers) + for i, merging in enumerate(config_list): + if i < len(templates): + result = merge_config( + result, + self._load(merging), + self.list_identifiers, + templates_list_handling[i], + ) + else: + result = merge_config( + result, self._load(merging), self.list_identifiers + ) return result def _evaluate_vars(self, config, context): diff --git a/netjsonconfig/backends/openwrt/openwrt.py b/netjsonconfig/backends/openwrt/openwrt.py index 36a788ba9..b05bc571e 100644 --- a/netjsonconfig/backends/openwrt/openwrt.py +++ b/netjsonconfig/backends/openwrt/openwrt.py @@ -36,7 +36,13 @@ class OpenWrt(BaseBackend): list_identifiers = ['name', 'config_value', 'id'] def __init__( - self, config=None, native=None, templates=None, context=None, dsa=True + self, + config=None, + native=None, + templates=None, + templates_list_handling=None, + context=None, + dsa=True, ): """ :param config: ``dict`` containing a valid **NetJSON** configuration dictionary @@ -52,7 +58,7 @@ def __init__( ``templates`` is not of type ``list`` """ self.dsa = dsa - super().__init__(config, native, templates, context) + super().__init__(config, native, templates, templates_list_handling, context) def _generate_contents(self, tar): """ diff --git a/netjsonconfig/utils.py b/netjsonconfig/utils.py index 844f3c309..123ff8509 100644 --- a/netjsonconfig/utils.py +++ b/netjsonconfig/utils.py @@ -3,7 +3,9 @@ from copy import deepcopy -def merge_config(template, config, list_identifiers=None): +def merge_config( + template, config, list_identifiers=None, list_handling='append_at_end' +): """ Merges ``config`` on top of ``template``. @@ -24,15 +26,17 @@ def merge_config(template, config, list_identifiers=None): for key, value in config.items(): if isinstance(value, dict): node = result.get(key, OrderedDict()) - result[key] = merge_config(node, value) + result[key] = merge_config(node, value, list_handling=list_handling) elif isinstance(value, list) and isinstance(result.get(key), list): - result[key] = merge_list(result[key], value, list_identifiers) + result[key] = merge_list( + result[key], value, list_identifiers, list_handling + ) else: result[key] = value return result -def merge_list(list1, list2, identifiers=None): +def merge_list(list1, list2, identifiers=None, list_handling='append_at_end'): """ Merges ``list2`` on top of ``list1``. @@ -47,6 +51,8 @@ def merge_list(list1, list2, identifiers=None): :param identifiers: ``list`` or ``None`` :returns: merged ``list`` """ + if list_handling == 'override': + return list2 identifiers = identifiers or [] dict_map = {'list1': OrderedDict(), 'list2': OrderedDict()} counter = 1 @@ -74,7 +80,14 @@ def merge_list(list1, list2, identifiers=None): key = tuple(key) container[key] = deepcopy(el) counter += 1 - merged = merge_config(dict_map['list1'], dict_map['list2']) + if list_handling == 'insert_at_beginning': + merged = merge_config( + dict_map['list2'], dict_map['list1'], list_handling=list_handling + ) + else: + merged = merge_config( + dict_map['list1'], dict_map['list2'], list_handling=list_handling + ) return list(merged.values())