# Path: netjsonconfig/schema.py
# DEFAULT_FILE_MODE = '0644'
# 
# Path: netjsonconfig/utils.py
# def evaluate_vars(data, context=None):
#     """
#     Evaluates variables in ``data``
# 
#     :param data: data structure containing variables, may be
#                  ``str``, ``dict`` or ``list``
#     :param context: ``dict`` containing variables
#     :returns: modified data structure
#     """
#     context = context or {}
#     if isinstance(data, (dict, list)):
#         if isinstance(data, dict):
#             loop_items = data.items()
#         elif isinstance(data, list):
#             loop_items = enumerate(data)
#         for key, value in loop_items:
#             data[key] = evaluate_vars(value, context)
#     elif isinstance(data, str):
#         vars_found = var_pattern.findall(data)
#         for var in vars_found:
#             var = var.strip()
#             # if found multiple variables, create a new regexp pattern for each
#             # variable, otherwise different variables would get the same value
#             # (see https://github.com/openwisp/netjsonconfig/issues/55)
#             if len(vars_found) > 1:
#                 pattern = r'\{\{(\s*%s\s*)\}\}' % var
#             # in case of single variables, use the precompiled
#             # regexp pattern to save computation
#             else:
#                 pattern = var_pattern
#             if var in context:
#                 data = re.sub(pattern, str(context[var]), data)
#     return data
# 
# def merge_config(template, config, list_identifiers=None):
#     """
#     Merges ``config`` on top of ``template``.
# 
#     Conflicting keys are handled in the following way:
# 
#     * simple values (eg: ``str``, ``int``, ``float``, ecc) in ``config`` will
#       overwrite the ones in ``template``
#     * values of type ``list`` in both ``config`` and ``template`` will be
#       merged using to the ``merge_list`` function
#     * values of type ``dict`` will be merged recursively
# 
#     :param template: template ``dict``
#     :param config: config ``dict``
#     :param list_identifiers: ``list`` or ``None``
#     :returns: merged ``dict``
#     """
#     result = deepcopy(template)
#     for key, value in config.items():
#         if isinstance(value, dict):
#             node = result.get(key, OrderedDict())
#             result[key] = merge_config(node, value)
#         elif isinstance(value, list) and isinstance(result.get(key), list):
#             result[key] = merge_list(result[key], value, list_identifiers)
#         else:
#             result[key] = value
#     return result


import gzip
import ipaddress
import json
import tarfile
from collections import OrderedDict
from copy import deepcopy
from io import BytesIO
from jsonschema import Draft4Validator, draft4_format_checker
from jsonschema.exceptions import ValidationError as JsonSchemaError
from ...exceptions import ValidationError
from ...schema import DEFAULT_FILE_MODE
from ...utils import evaluate_vars, merge_config
            if value:
                self.intermediate_data = merge_config(
                    self.intermediate_data, value, list_identifiers=['.name']
                )

    def parse(self, native):
        """
        Parses a native configuration and converts
        it to a NetJSON configuration dictionary
        """
        if not hasattr(self, 'parser') or not self.parser:
            raise NotImplementedError('Parser class not specified')
        parser = self.parser(native)
        self.intermediate_data = parser.intermediate_data
        del parser
        self.to_netjson()

    def to_netjson(self):
        """
        Converts the intermediate data structure (self.intermediate_data)
        to the NetJSON configuration dictionary (self.config)
        """
        self.__backup_intermediate_data()
        self.config = OrderedDict()
        for converter_class in self.converters:
            if not converter_class.should_run_backward(self.intermediate_data):
                continue
            converter = converter_class(self)
            value = converter.to_netjson()
            if value:

# Path: netjsonconfig/schema.py
# DEFAULT_FILE_MODE = '0644'
# 
# Path: netjsonconfig/utils.py
# def evaluate_vars(data, context=None):
#     """
#     Evaluates variables in ``data``
# 
#     :param data: data structure containing variables, may be
#                  ``str``, ``dict`` or ``list``
#     :param context: ``dict`` containing variables
#     :returns: modified data structure
#     """
#     context = context or {}
#     if isinstance(data, (dict, list)):
#         if isinstance(data, dict):
#             loop_items = data.items()
#         elif isinstance(data, list):
#             loop_items = enumerate(data)
#         for key, value in loop_items:
#             data[key] = evaluate_vars(value, context)
#     elif isinstance(data, str):
#         vars_found = var_pattern.findall(data)
#         for var in vars_found:
#             var = var.strip()
#             # if found multiple variables, create a new regexp pattern for each
#             # variable, otherwise different variables would get the same value
#             # (see https://github.com/openwisp/netjsonconfig/issues/55)
#             if len(vars_found) > 1:
#                 pattern = r'\{\{(\s*%s\s*)\}\}' % var
#             # in case of single variables, use the precompiled
#             # regexp pattern to save computation
#             else:
#                 pattern = var_pattern
#             if var in context:
#                 data = re.sub(pattern, str(context[var]), data)
#     return data
# 
# def merge_config(template, config, list_identifiers=None):
#     """
#     Merges ``config`` on top of ``template``.
# 
#     Conflicting keys are handled in the following way:
# 
#     * simple values (eg: ``str``, ``int``, ``float``, ecc) in ``config`` will
#       overwrite the ones in ``template``
#     * values of type ``list`` in both ``config`` and ``template`` will be
#       merged using to the ``merge_list`` function
#     * values of type ``dict`` will be merged recursively
# 
#     :param template: template ``dict``
#     :param config: config ``dict``
#     :param list_identifiers: ``list`` or ``None``
#     :returns: merged ``dict``
#     """
#     result = deepcopy(template)
#     for key, value in config.items():
#         if isinstance(value, dict):
#             node = result.get(key, OrderedDict())
#             result[key] = merge_config(node, value)
#         elif isinstance(value, list) and isinstance(result.get(key), list):
#             result[key] = merge_list(result[key], value, list_identifiers)
#         else:
#             result[key] = value
#     return result


# Path: netjsonconfig/backends/base/backend.py
import gzip
import ipaddress
import json
import tarfile
from collections import OrderedDict
from copy import deepcopy
from io import BytesIO
from jsonschema import Draft4Validator, draft4_format_checker
from jsonschema.exceptions import ValidationError as JsonSchemaError
from ...exceptions import ValidationError
from ...schema import DEFAULT_FILE_MODE
from ...utils import evaluate_vars, merge_config

            if value:
                self.intermediate_data = merge_config(
                    self.intermediate_data, value, list_identifiers=['.name']
                )

    def parse(self, native):
        """
        Parses a native configuration and converts
        it to a NetJSON configuration dictionary
        """
        if not hasattr(self, 'parser') or not self.parser:
            raise NotImplementedError('Parser class not specified')
        parser = self.parser(native)
        self.intermediate_data = parser.intermediate_data
        del parser
        self.to_netjson()

    def to_netjson(self):
        """
        Converts the intermediate data structure (self.intermediate_data)
        to the NetJSON configuration dictionary (self.config)
        """
        self.__backup_intermediate_data()
        self.config = OrderedDict()
        for converter_class in self.converters:
            if not converter_class.should_run_backward(self.intermediate_data):
                continue
            converter = converter_class(self)
            value = converter.to_netjson()
            if value:

                self.config = merge_config(