Renders a configuration template (default: Jinja) and loads the result on the device.

    By default this function will commit the changes. If there are no changes,
    it does not commit, discards he config and the flag ``already_configured``
    will be set as ``True`` to point this out.

    To avoid committing the configuration, set the argument ``test`` to ``True``
    and will discard (dry run).

    To preserve the changes, set ``commit`` to ``False``.
    However, this is recommended to be used only in exceptional cases
    when there are applied few consecutive states
    and/or configuration changes.
    Otherwise the user might forget that the config DB is locked
    and the candidate config buffer is not cleared/merged in the running config.

    To replace the config, set ``replace`` to ``True``.

    template_name
        Identifies path to the template source.
        The template can be either stored on the local machine, either remotely.
        The recommended location is under the ``file_roots``
        as specified in the master config file.
        For example, let's suppose the ``file_roots`` is configured as:

        .. code-block:: yaml

            file_roots:
              base:
                - /etc/salt/states

        Placing the template under ``/etc/salt/states/templates/example.jinja``,
        it can be used as ``salt://templates/example.jinja``.
        Alternatively, for local files, the user can specify the absolute path.
        If remotely, the source can be retrieved via ``http``, ``https`` or ``ftp``.

        Examples:

        - ``salt://my_template.jinja``
        - ``/absolute/path/to/my_template.jinja``
        - ``http://example.com/template.cheetah``
        - ``https:/example.com/template.mako``
        - ``ftp://example.com/template.py``

        .. versionchanged:: 2019.2.0
            This argument can now support a list of templates to be rendered.
            The resulting configuration text is loaded at once, as a single
            configuration chunk.

    template_source: None
        Inline config template to be rendered and loaded on the device.

    template_hash: None
        Hash of the template file. Format: ``{hash_type: 'md5', 'hsum': <md5sum>}``

        .. versionadded:: 2016.11.2

    context: None
        Overrides default context variables passed to the template.

        .. versionadded:: 2019.2.0

    template_hash_name: None
        When ``template_hash`` refers to a remote file,
        this specifies the filename to look for in that file.

        .. versionadded:: 2016.11.2

    saltenv: ``base``
        Specifies the template environment.
        This will influence the relative imports inside the templates.

        .. versionadded:: 2016.11.2

    template_engine: jinja
        The following templates engines are supported:

        - :mod:`cheetah<salt.renderers.cheetah>`
        - :mod:`genshi<salt.renderers.genshi>`
        - :mod:`jinja<salt.renderers.jinja>`
        - :mod:`mako<salt.renderers.mako>`
        - :mod:`py<salt.renderers.py>`
        - :mod:`wempy<salt.renderers.wempy>`

        .. versionadded:: 2016.11.2

    skip_verify: True
        If ``True``, hash verification of remote file sources
        (``http://``, ``https://``, ``ftp://``) will be skipped,
        and the ``source_hash`` argument will be ignored.

        .. versionadded:: 2016.11.2

    test: False
        Dry run? If set to ``True``, will apply the config,
        discard and return the changes.
        Default: ``False`` and will commit the changes on the device.

    commit: True
        Commit? (default: ``True``)

    debug: False
        Debug mode. Will insert a new key under the output dictionary,
        as ``loaded_config`` containing the raw result after the template was rendered.

        .. versionadded:: 2016.11.2

    replace: False
        Load and replace the configuration.

        .. versionadded:: 2016.11.2

    commit_in: ``None``
        Commit the changes in a specific number of minutes / hours. Example of
        accepted formats: ``5`` (commit in 5 minutes), ``2m`` (commit in 2
        minutes), ``1h`` (commit the changes in 1 hour)`, ``5h30m`` (commit
        the changes in 5 hours and 30 minutes).

        .. note::
            This feature works on any platforms, as it does not rely on the
            native features of the network operating system.

        .. note::
            After the command is executed and the ``diff`` is not satisfactory,
            or for any other reasons you have to discard the commit, you are
            able to do so using the
            :py:func:`net.cancel_commit <salt.modules.napalm_network.cancel_commit>`
            execution function, using the commit ID returned by this function.

        .. warning::
            Using this feature, Salt will load the exact configuration you
            expect, however the diff may change in time (i.e., if an user
            applies a manual configuration change, or a different process or
            command changes the configuration in the meanwhile).

        .. versionadded:: 2019.2.0

    commit_at: ``None``
        Commit the changes at a specific time. Example of accepted formats:
        ``1am`` (will commit the changes at the next 1AM), ``13:20`` (will
        commit at 13:20), ``1:20am``, etc.

        .. note::
            This feature works on any platforms, as it does not rely on the
            native features of the network operating system.

        .. note::
            After the command is executed and the ``diff`` is not satisfactory,
            or for any other reasons you have to discard the commit, you are
            able to do so using the
            :py:func:`net.cancel_commit <salt.modules.napalm_network.cancel_commit>`
            execution function, using the commit ID returned by this function.

        .. warning::
            Using this feature, Salt will load the exact configuration you
            expect, however the diff may change in time (i.e., if an user
            applies a manual configuration change, or a different process or
            command changes the configuration in the meanwhile).

        .. versionadded:: 2019.2.0

    revert_in: ``None``
        Commit and revert the changes in a specific number of minutes / hours.
        Example of accepted formats: ``5`` (revert in 5 minutes), ``2m`` (revert
        in 2 minutes), ``1h`` (revert the changes in 1 hour)`, ``5h30m`` (revert
        the changes in 5 hours and 30 minutes).

        .. note::
            To confirm the commit, and prevent reverting the changes, you will
            have to execute the
            :mod:`net.confirm_commit <salt.modules.napalm_network.confirm_commit>`
            function, using the commit ID returned by this function.

        .. warning::
            This works on any platform, regardless if they have or don't have
            native capabilities to confirming a commit. However, please be
            *very* cautious when using this feature: on Junos (as it is the only
            NAPALM core platform supporting this natively) it executes a commit
            confirmed as you would do from the command line.
            All the other platforms don't have this capability natively,
            therefore the revert is done via Salt. That means, your device needs
            to be reachable at the moment when Salt will attempt to revert your
            changes. Be cautious when pushing configuration changes that would
            prevent you reach the device.

            Similarly, if an user or a different process apply other
            configuration changes in the meanwhile (between the moment you
            commit and till the changes are reverted), these changes would be
            equally reverted, as Salt cannot be aware of them.

        .. versionadded:: 2019.2.0

    revert_at: ``None``
        Commit and revert the changes at a specific time. Example of accepted
        formats: ``1am`` (will commit and revert the changes at the next 1AM),
        ``13:20`` (will commit and revert at 13:20), ``1:20am``, etc.

        .. note::
            To confirm the commit, and prevent reverting the changes, you will
            have to execute the
            :mod:`net.confirm_commit <salt.modules.napalm_network.confirm_commit>`
            function, using the commit ID returned by this function.

        .. warning::
            This works on any platform, regardless if they have or don't have
            native capabilities to confirming a commit. However, please be
            *very* cautious when using this feature: on Junos (as it is the only
            NAPALM core platform supporting this natively) it executes a commit
            confirmed as you would do from the command line.
            All the other platforms don't have this capability natively,
            therefore the revert is done via Salt. That means, your device needs
            to be reachable at the moment when Salt will attempt to revert your
            changes. Be cautious when pushing configuration changes that would
            prevent you reach the device.

            Similarly, if an user or a different process apply other
            configuration changes in the meanwhile (between the moment you
            commit and till the changes are reverted), these changes would be
            equally reverted, as Salt cannot be aware of them.

        .. versionadded:: 2019.2.0

    defaults: None
        Default variables/context passed to the template.

        .. versionadded:: 2016.11.2

    template_vars
        Dictionary with the arguments/context to be used when the template is rendered.

        .. note::
            Do not explicitly specify this argument. This represents any other
            variable that will be sent to the template rendering system.
            Please see the examples below!

        .. note::
            It is more recommended to use the ``context`` argument to avoid
            conflicts between CLI arguments and template variables.

    :return: a dictionary having the following keys:

    - result (bool): if the config was applied successfully. It is ``False``
      only in case of failure. In case there are no changes to be applied and
      successfully performs all operations it is still ``True`` and so will be
      the ``already_configured`` flag (example below)
    - comment (str): a message for the user
    - already_configured (bool): flag to check if there were no changes applied
    - loaded_config (str): the configuration loaded on the device, after
      rendering the template. Requires ``debug`` to be set as ``True``
    - diff (str): returns the config changes applied

    The template can use variables from the ``grains``, ``pillar`` or ``opts``, for example:

    .. code-block:: jinja

        {% set router_model = grains.get('model') -%}
        {% set router_vendor = grains.get('vendor') -%}
        {% set os_version = grains.get('version') -%}
        {% set hostname = pillar.get('proxy', {}).get('host') -%}
        {% if router_vendor|lower == 'juniper' %}
        system {
            host-name {{hostname}};
        }
        {% elif router_vendor|lower == 'cisco' %}
        hostname {{hostname}}
        {% endif %}

    CLI Examples:

    .. code-block:: bash

        salt '*' net.load_template set_ntp_peers peers=[192.168.0.1]  # uses NAPALM default templates

        # inline template:
        salt -G 'os:junos' net.load_template template_source='system { host-name {{host_name}}; }' \
        host_name='MX480.lab'

        # inline template using grains info:
        salt -G 'os:junos' net.load_template \
        template_source='system { host-name {{grains.model}}.lab; }'
        # if the device is a MX480, the command above will set the hostname as: MX480.lab

        # inline template using pillar data:
        salt -G 'os:junos' net.load_template template_source='system { host-name {{pillar.proxy.host}}; }'

        salt '*' net.load_template https://bit.ly/2OhSgqP hostname=example  # will commit
        salt '*' net.load_template https://bit.ly/2OhSgqP hostname=example test=True  # dry run

        salt '*' net.load_template salt://templates/example.jinja debug=True  # Using the salt:// URI

        # render a mako template:
        salt '*' net.load_template salt://templates/example.mako template_engine=mako debug=True

        # render remote template
        salt -G 'os:junos' net.load_template http://bit.ly/2fReJg7 test=True debug=True peers=['192.168.0.1']
        salt -G 'os:ios' net.load_template http://bit.ly/2gKOj20 test=True debug=True peers=['192.168.0.1']

        # render multiple templates at once
        salt '*' net.load_template "['https://bit.ly/2OhSgqP', 'salt://templates/example.jinja']" context="{'hostname': 'example'}"

    Example output:

    .. code-block:: python

        {
            'comment': '',
            'already_configured': False,
            'result': True,
            'diff': '[edit system]+  host-name edge01.bjm01',
            'loaded_config': 'system { host-name edge01.bjm01; }''
        }

def load_template(template_name=None,
                  template_source=None,
                  context=None,
                  defaults=None,
                  template_engine='jinja',
                  saltenv='base',
                  template_hash=None,
                  template_hash_name=None,
                  skip_verify=False,
                  test=False,
                  commit=True,
                  debug=False,
                  replace=False,
                  commit_in=None,
                  commit_at=None,
                  revert_in=None,
                  revert_at=None,
                  inherit_napalm_device=None,  # pylint: disable=unused-argument
                  **template_vars):
    '''
    Renders a configuration template (default: Jinja) and loads the result on the device.

    By default this function will commit the changes. If there are no changes,
    it does not commit, discards he config and the flag ``already_configured``
    will be set as ``True`` to point this out.

    To avoid committing the configuration, set the argument ``test`` to ``True``
    and will discard (dry run).

    To preserve the changes, set ``commit`` to ``False``.
    However, this is recommended to be used only in exceptional cases
    when there are applied few consecutive states
    and/or configuration changes.
    Otherwise the user might forget that the config DB is locked
    and the candidate config buffer is not cleared/merged in the running config.

    To replace the config, set ``replace`` to ``True``.

    template_name
        Identifies path to the template source.
        The template can be either stored on the local machine, either remotely.
        The recommended location is under the ``file_roots``
        as specified in the master config file.
        For example, let's suppose the ``file_roots`` is configured as:

        .. code-block:: yaml

            file_roots:
              base:
                - /etc/salt/states

        Placing the template under ``/etc/salt/states/templates/example.jinja``,
        it can be used as ``salt://templates/example.jinja``.
        Alternatively, for local files, the user can specify the absolute path.
        If remotely, the source can be retrieved via ``http``, ``https`` or ``ftp``.

        Examples:

        - ``salt://my_template.jinja``
        - ``/absolute/path/to/my_template.jinja``
        - ``http://example.com/template.cheetah``
        - ``https:/example.com/template.mako``
        - ``ftp://example.com/template.py``

        .. versionchanged:: 2019.2.0
            This argument can now support a list of templates to be rendered.
            The resulting configuration text is loaded at once, as a single
            configuration chunk.

    template_source: None
        Inline config template to be rendered and loaded on the device.

    template_hash: None
        Hash of the template file. Format: ``{hash_type: 'md5', 'hsum': <md5sum>}``

        .. versionadded:: 2016.11.2

    context: None
        Overrides default context variables passed to the template.

        .. versionadded:: 2019.2.0

    template_hash_name: None
        When ``template_hash`` refers to a remote file,
        this specifies the filename to look for in that file.

        .. versionadded:: 2016.11.2

    saltenv: ``base``
        Specifies the template environment.
        This will influence the relative imports inside the templates.

        .. versionadded:: 2016.11.2

    template_engine: jinja
        The following templates engines are supported:

        - :mod:`cheetah<salt.renderers.cheetah>`
        - :mod:`genshi<salt.renderers.genshi>`
        - :mod:`jinja<salt.renderers.jinja>`
        - :mod:`mako<salt.renderers.mako>`
        - :mod:`py<salt.renderers.py>`
        - :mod:`wempy<salt.renderers.wempy>`

        .. versionadded:: 2016.11.2

    skip_verify: True
        If ``True``, hash verification of remote file sources
        (``http://``, ``https://``, ``ftp://``) will be skipped,
        and the ``source_hash`` argument will be ignored.

        .. versionadded:: 2016.11.2

    test: False
        Dry run? If set to ``True``, will apply the config,
        discard and return the changes.
        Default: ``False`` and will commit the changes on the device.

    commit: True
        Commit? (default: ``True``)

    debug: False
        Debug mode. Will insert a new key under the output dictionary,
        as ``loaded_config`` containing the raw result after the template was rendered.

        .. versionadded:: 2016.11.2

    replace: False
        Load and replace the configuration.

        .. versionadded:: 2016.11.2

    commit_in: ``None``
        Commit the changes in a specific number of minutes / hours. Example of
        accepted formats: ``5`` (commit in 5 minutes), ``2m`` (commit in 2
        minutes), ``1h`` (commit the changes in 1 hour)`, ``5h30m`` (commit
        the changes in 5 hours and 30 minutes).

        .. note::
            This feature works on any platforms, as it does not rely on the
            native features of the network operating system.

        .. note::
            After the command is executed and the ``diff`` is not satisfactory,
            or for any other reasons you have to discard the commit, you are
            able to do so using the
            :py:func:`net.cancel_commit <salt.modules.napalm_network.cancel_commit>`
            execution function, using the commit ID returned by this function.

        .. warning::
            Using this feature, Salt will load the exact configuration you
            expect, however the diff may change in time (i.e., if an user
            applies a manual configuration change, or a different process or
            command changes the configuration in the meanwhile).

        .. versionadded:: 2019.2.0

    commit_at: ``None``
        Commit the changes at a specific time. Example of accepted formats:
        ``1am`` (will commit the changes at the next 1AM), ``13:20`` (will
        commit at 13:20), ``1:20am``, etc.

        .. note::
            This feature works on any platforms, as it does not rely on the
            native features of the network operating system.

        .. note::
            After the command is executed and the ``diff`` is not satisfactory,
            or for any other reasons you have to discard the commit, you are
            able to do so using the
            :py:func:`net.cancel_commit <salt.modules.napalm_network.cancel_commit>`
            execution function, using the commit ID returned by this function.

        .. warning::
            Using this feature, Salt will load the exact configuration you
            expect, however the diff may change in time (i.e., if an user
            applies a manual configuration change, or a different process or
            command changes the configuration in the meanwhile).

        .. versionadded:: 2019.2.0

    revert_in: ``None``
        Commit and revert the changes in a specific number of minutes / hours.
        Example of accepted formats: ``5`` (revert in 5 minutes), ``2m`` (revert
        in 2 minutes), ``1h`` (revert the changes in 1 hour)`, ``5h30m`` (revert
        the changes in 5 hours and 30 minutes).

        .. note::
            To confirm the commit, and prevent reverting the changes, you will
            have to execute the
            :mod:`net.confirm_commit <salt.modules.napalm_network.confirm_commit>`
            function, using the commit ID returned by this function.

        .. warning::
            This works on any platform, regardless if they have or don't have
            native capabilities to confirming a commit. However, please be
            *very* cautious when using this feature: on Junos (as it is the only
            NAPALM core platform supporting this natively) it executes a commit
            confirmed as you would do from the command line.
            All the other platforms don't have this capability natively,
            therefore the revert is done via Salt. That means, your device needs
            to be reachable at the moment when Salt will attempt to revert your
            changes. Be cautious when pushing configuration changes that would
            prevent you reach the device.

            Similarly, if an user or a different process apply other
            configuration changes in the meanwhile (between the moment you
            commit and till the changes are reverted), these changes would be
            equally reverted, as Salt cannot be aware of them.

        .. versionadded:: 2019.2.0

    revert_at: ``None``
        Commit and revert the changes at a specific time. Example of accepted
        formats: ``1am`` (will commit and revert the changes at the next 1AM),
        ``13:20`` (will commit and revert at 13:20), ``1:20am``, etc.

        .. note::
            To confirm the commit, and prevent reverting the changes, you will
            have to execute the
            :mod:`net.confirm_commit <salt.modules.napalm_network.confirm_commit>`
            function, using the commit ID returned by this function.

        .. warning::
            This works on any platform, regardless if they have or don't have
            native capabilities to confirming a commit. However, please be
            *very* cautious when using this feature: on Junos (as it is the only
            NAPALM core platform supporting this natively) it executes a commit
            confirmed as you would do from the command line.
            All the other platforms don't have this capability natively,
            therefore the revert is done via Salt. That means, your device needs
            to be reachable at the moment when Salt will attempt to revert your
            changes. Be cautious when pushing configuration changes that would
            prevent you reach the device.

            Similarly, if an user or a different process apply other
            configuration changes in the meanwhile (between the moment you
            commit and till the changes are reverted), these changes would be
            equally reverted, as Salt cannot be aware of them.

        .. versionadded:: 2019.2.0

    defaults: None
        Default variables/context passed to the template.

        .. versionadded:: 2016.11.2

    template_vars
        Dictionary with the arguments/context to be used when the template is rendered.

        .. note::
            Do not explicitly specify this argument. This represents any other
            variable that will be sent to the template rendering system.
            Please see the examples below!

        .. note::
            It is more recommended to use the ``context`` argument to avoid
            conflicts between CLI arguments and template variables.

    :return: a dictionary having the following keys:

    - result (bool): if the config was applied successfully. It is ``False``
      only in case of failure. In case there are no changes to be applied and
      successfully performs all operations it is still ``True`` and so will be
      the ``already_configured`` flag (example below)
    - comment (str): a message for the user
    - already_configured (bool): flag to check if there were no changes applied
    - loaded_config (str): the configuration loaded on the device, after
      rendering the template. Requires ``debug`` to be set as ``True``
    - diff (str): returns the config changes applied

    The template can use variables from the ``grains``, ``pillar`` or ``opts``, for example:

    .. code-block:: jinja

        {% set router_model = grains.get('model') -%}
        {% set router_vendor = grains.get('vendor') -%}
        {% set os_version = grains.get('version') -%}
        {% set hostname = pillar.get('proxy', {}).get('host') -%}
        {% if router_vendor|lower == 'juniper' %}
        system {
            host-name {{hostname}};
        }
        {% elif router_vendor|lower == 'cisco' %}
        hostname {{hostname}}
        {% endif %}

    CLI Examples:

    .. code-block:: bash

        salt '*' net.load_template set_ntp_peers peers=[192.168.0.1]  # uses NAPALM default templates

        # inline template:
        salt -G 'os:junos' net.load_template template_source='system { host-name {{host_name}}; }' \
        host_name='MX480.lab'

        # inline template using grains info:
        salt -G 'os:junos' net.load_template \
        template_source='system { host-name {{grains.model}}.lab; }'
        # if the device is a MX480, the command above will set the hostname as: MX480.lab

        # inline template using pillar data:
        salt -G 'os:junos' net.load_template template_source='system { host-name {{pillar.proxy.host}}; }'

        salt '*' net.load_template https://bit.ly/2OhSgqP hostname=example  # will commit
        salt '*' net.load_template https://bit.ly/2OhSgqP hostname=example test=True  # dry run

        salt '*' net.load_template salt://templates/example.jinja debug=True  # Using the salt:// URI

        # render a mako template:
        salt '*' net.load_template salt://templates/example.mako template_engine=mako debug=True

        # render remote template
        salt -G 'os:junos' net.load_template http://bit.ly/2fReJg7 test=True debug=True peers=['192.168.0.1']
        salt -G 'os:ios' net.load_template http://bit.ly/2gKOj20 test=True debug=True peers=['192.168.0.1']

        # render multiple templates at once
        salt '*' net.load_template "['https://bit.ly/2OhSgqP', 'salt://templates/example.jinja']" context="{'hostname': 'example'}"

    Example output:

    .. code-block:: python

        {
            'comment': '',
            'already_configured': False,
            'result': True,
            'diff': '[edit system]+  host-name edge01.bjm01',
            'loaded_config': 'system { host-name edge01.bjm01; }''
        }
    '''
    _rendered = ''
    _loaded = {
        'result': True,
        'comment': '',
        'out': None
    }
    loaded_config = None
    # prechecks
    deprecated_args = ('template_user', 'template_attrs', 'template_group', 'template_mode')
    for deprecated_arg in deprecated_args:
        if template_vars.get(deprecated_arg):
            del template_vars[deprecated_arg]
            salt.utils.versions.warn_until(
                'Sodium',
                ('The \'{arg}\' argument to \'net.load_template\' is deprecated '
                 'and has been ignored').format(arg=deprecated_arg)
            )
    if template_engine not in salt.utils.templates.TEMPLATE_REGISTRY:
        _loaded.update({
            'result': False,
            'comment': 'Invalid templating engine! Choose between: {tpl_eng_opts}'.format(
                tpl_eng_opts=', '.join(list(salt.utils.templates.TEMPLATE_REGISTRY.keys()))
            )
        })
        return _loaded  # exit

    # to check if will be rendered by salt or NAPALM
    salt_render_prefixes = ('salt://', 'http://', 'https://', 'ftp://')
    salt_render = False
    file_exists = False
    if not isinstance(template_name, (tuple, list)):
        for salt_render_prefix in salt_render_prefixes:
            if not salt_render:
                salt_render = salt_render or template_name.startswith(salt_render_prefix)
        file_exists = __salt__['file.file_exists'](template_name)

    if template_source or file_exists or salt_render or isinstance(template_name, (tuple, list)):
        # either inline template
        # either template in a custom path
        # either abs path send
        # either starts with salt:// and
        # then use Salt render system

        if context is None:
            context = {}
        context.update(template_vars)
        # if needed to render the template send as inline arg
        if template_source:
            # render the content
            _rendered = __salt__['file.apply_template_on_contents'](
                contents=template_source,
                template=template_engine,
                context=context,
                defaults=defaults,
                saltenv=saltenv
            )
            if not isinstance(_rendered, six.string_types):
                if 'result' in _rendered:
                    _loaded['result'] = _rendered['result']
                else:
                    _loaded['result'] = False
                if 'comment' in _rendered:
                    _loaded['comment'] = _rendered['comment']
                else:
                    _loaded['comment'] = 'Error while rendering the template.'
                return _loaded
        else:
            # render the file - either local, either remote
            if not isinstance(template_name, (list, tuple)):
                template_name = [template_name]
            if template_hash_name and not isinstance(template_hash_name, (list, tuple)):
                template_hash_name = [template_hash_name]
            elif not template_hash_name:
                template_hash_name = [None] * len(template_name)
            if template_hash and isinstance(template_hash, six.string_types) and not\
                    (template_hash.startswith('salt://') or template_hash.startswith('file://')):
                # If the template hash is passed as string, and it's not a file
                # (starts with the salt:// or file:// URI), then make it a list
                # of 1 element (for the iteration below)
                template_hash = [template_hash]
            elif template_hash and isinstance(template_hash, six.string_types) and\
                    (template_hash.startswith('salt://') or template_hash.startswith('file://')):
                # If the template hash is a file URI, then provide the same value
                # for each of the templates in the list, as probably they all
                # share the same hash file, otherwise the user should provide
                # this as a list
                template_hash = [template_hash] * len(template_name)
            elif not template_hash:
                template_hash = [None] * len(template_name)
            for tpl_index, tpl_name in enumerate(template_name):
                tpl_hash = template_hash[tpl_index]
                tpl_hash_name = template_hash_name[tpl_index]
                _rand_filename = __salt__['random.hash'](tpl_name, 'md5')
                _temp_file = __salt__['file.join']('/tmp', _rand_filename)
                _managed = __salt__['file.get_managed'](name=_temp_file,
                                                        source=tpl_name,
                                                        source_hash=tpl_hash,
                                                        source_hash_name=tpl_hash_name,
                                                        user=None,
                                                        group=None,
                                                        mode=None,
                                                        attrs=None,
                                                        template=template_engine,
                                                        context=context,
                                                        defaults=defaults,
                                                        saltenv=saltenv,
                                                        skip_verify=skip_verify)
                if not isinstance(_managed, (list, tuple)) and isinstance(_managed, six.string_types):
                    _loaded['comment'] += _managed
                    _loaded['result'] = False
                elif isinstance(_managed, (list, tuple)) and not _managed:
                    _loaded['result'] = False
                    _loaded['comment'] += 'Error while rendering the template.'
                elif isinstance(_managed, (list, tuple)) and not _managed[0]:
                    _loaded['result'] = False
                    _loaded['comment'] += _managed[-1]  # contains the error message
                if _loaded['result']:  # all good
                    _temp_tpl_file = _managed[0]
                    _temp_tpl_file_exists = __salt__['file.file_exists'](_temp_tpl_file)
                    if not _temp_tpl_file_exists:
                        _loaded['result'] = False
                        _loaded['comment'] += 'Error while rendering the template.'
                        return _loaded
                    _rendered += __salt__['file.read'](_temp_tpl_file)
                    __salt__['file.remove'](_temp_tpl_file)
                else:
                    return _loaded  # exit

        loaded_config = _rendered
        if _loaded['result']:  # all good
            fun = 'load_merge_candidate'
            if replace:  # replace requested
                fun = 'load_replace_candidate'
            if salt.utils.napalm.not_always_alive(__opts__):
                # if a not-always-alive proxy
                # or regular minion
                # do not close the connection after loading the config
                # this will be handled in _config_logic
                # after running the other features:
                # compare_config, discard / commit
                # which have to be over the same session
                napalm_device['CLOSE'] = False  # pylint: disable=undefined-variable
            _loaded = salt.utils.napalm.call(
                napalm_device,  # pylint: disable=undefined-variable
                fun,
                **{
                    'config': _rendered
                }
            )
    else:
        salt.utils.versions.warn_until(
            'Sodium',
            'Native NAPALM templates support will be removed in the Sodium '
            'release. Please consider using the Salt rendering pipeline instead.'
            'If you are using the \'netntp\', \'netsnmp\', or \'netusers\' Salt '
            'State modules, you can ignore this message'
        )
        # otherwise, use NAPALM render system, injecting pillar/grains/opts vars
        load_templates_params = defaults if defaults else {}
        load_templates_params.update(template_vars)
        load_templates_params.update(
            {
                'template_name': template_name,
                'template_source': template_source,  # inline template
                'pillar': __pillar__,  # inject pillar content
                'grains': __grains__,  # inject grains content
                'opts': __opts__  # inject opts content
            }
        )
        if salt.utils.napalm.not_always_alive(__opts__):
            # if a not-always-alive proxy
            # or regular minion
            # do not close the connection after loading the config
            # this will be handled in _config_logic
            # after running the other features:
            # compare_config, discard / commit
            # which have to be over the same session
            # so we'll set the CLOSE global explicitly as False
            napalm_device['CLOSE'] = False  # pylint: disable=undefined-variable
        _loaded = salt.utils.napalm.call(
            napalm_device,  # pylint: disable=undefined-variable
            'load_template',
            **load_templates_params
        )
    return _config_logic(napalm_device,  # pylint: disable=undefined-variable
                         _loaded,
                         test=test,
                         debug=debug,
                         replace=replace,
                         commit_config=commit,
                         loaded_config=loaded_config,
                         commit_at=commit_at,
                         commit_in=commit_in,
                         revert_in=revert_in,
                         revert_at=revert_at,
                         **template_vars)