Installs the given configuration file into the candidate configuration.
    Commits the changes if the commit checks or throws an error.

    path (required)
        Path where the configuration/template file is present. If the file has
        a ``.conf`` extension, the content is treated as text format. If the
        file has a ``.xml`` extension, the content is treated as XML format. If
        the file has a ``.set`` extension, the content is treated as Junos OS
        ``set`` commands.

    mode : exclusive
        The mode in which the configuration is locked. Can be one of
        ``private``, ``dynamic``, ``batch``, ``exclusive``.

    dev_timeout : 30
        Set NETCONF RPC timeout. Can be used for commands which take a while to
        execute.

    overwrite : False
        Set to ``True`` if you want this file is to completely replace the
        configuration file.

    replace : False
        Specify whether the configuration file uses ``replace:`` statements. If
        ``True``, only those statements under the ``replace`` tag will be
        changed.

    format
        Determines the format of the contents

    update : False
        Compare a complete loaded configuration against the candidate
        configuration. For each hierarchy level or configuration object that is
        different in the two configurations, the version in the loaded
        configuration replaces the version in the candidate configuration. When
        the configuration is later committed, only system processes that are
        affected by the changed configuration elements parse the new
        configuration. This action is supported from PyEZ 2.1.

    comment
      Provide a comment for the commit

    confirm
      Provide time in minutes for commit confirmation. If this option is
      specified, the commit will be rolled back in the specified amount of time
      unless the commit is confirmed.

    diffs_file
      Path to the file where the diff (difference in old configuration and the
      committed configuration) will be stored. Note that the file will be
      stored on the proxy minion. To push the files to the master use
      :py:func:`cp.push <salt.modules.cp.push>`.

    template_vars
      Variables to be passed into the template processing engine in addition to
      those present in pillar, the minion configuration, grains, etc.  You may
      reference these variables in your template like so:

      .. code-block:: jinja

          {{ template_vars["var_name"] }}

    CLI Examples:

    .. code-block:: bash

        salt 'device_name' junos.install_config 'salt://production/network/routers/config.set'
        salt 'device_name' junos.install_config 'salt://templates/replace_config.conf' replace=True comment='Committed via SaltStack'
        salt 'device_name' junos.install_config 'salt://my_new_configuration.conf' dev_timeout=300 diffs_file='/salt/confs/old_config.conf' overwrite=True
        salt 'device_name' junos.install_config 'salt://syslog_template.conf' template_vars='{"syslog_host": "10.180.222.7"}'

def install_config(path=None, **kwargs):
    '''
    Installs the given configuration file into the candidate configuration.
    Commits the changes if the commit checks or throws an error.

    path (required)
        Path where the configuration/template file is present. If the file has
        a ``.conf`` extension, the content is treated as text format. If the
        file has a ``.xml`` extension, the content is treated as XML format. If
        the file has a ``.set`` extension, the content is treated as Junos OS
        ``set`` commands.

    mode : exclusive
        The mode in which the configuration is locked. Can be one of
        ``private``, ``dynamic``, ``batch``, ``exclusive``.

    dev_timeout : 30
        Set NETCONF RPC timeout. Can be used for commands which take a while to
        execute.

    overwrite : False
        Set to ``True`` if you want this file is to completely replace the
        configuration file.

    replace : False
        Specify whether the configuration file uses ``replace:`` statements. If
        ``True``, only those statements under the ``replace`` tag will be
        changed.

    format
        Determines the format of the contents

    update : False
        Compare a complete loaded configuration against the candidate
        configuration. For each hierarchy level or configuration object that is
        different in the two configurations, the version in the loaded
        configuration replaces the version in the candidate configuration. When
        the configuration is later committed, only system processes that are
        affected by the changed configuration elements parse the new
        configuration. This action is supported from PyEZ 2.1.

    comment
      Provide a comment for the commit

    confirm
      Provide time in minutes for commit confirmation. If this option is
      specified, the commit will be rolled back in the specified amount of time
      unless the commit is confirmed.

    diffs_file
      Path to the file where the diff (difference in old configuration and the
      committed configuration) will be stored. Note that the file will be
      stored on the proxy minion. To push the files to the master use
      :py:func:`cp.push <salt.modules.cp.push>`.

    template_vars
      Variables to be passed into the template processing engine in addition to
      those present in pillar, the minion configuration, grains, etc.  You may
      reference these variables in your template like so:

      .. code-block:: jinja

          {{ template_vars["var_name"] }}

    CLI Examples:

    .. code-block:: bash

        salt 'device_name' junos.install_config 'salt://production/network/routers/config.set'
        salt 'device_name' junos.install_config 'salt://templates/replace_config.conf' replace=True comment='Committed via SaltStack'
        salt 'device_name' junos.install_config 'salt://my_new_configuration.conf' dev_timeout=300 diffs_file='/salt/confs/old_config.conf' overwrite=True
        salt 'device_name' junos.install_config 'salt://syslog_template.conf' template_vars='{"syslog_host": "10.180.222.7"}'
    '''
    conn = __proxy__['junos.conn']()
    ret = {}
    ret['out'] = True

    if path is None:
        ret['message'] = \
            'Please provide the salt path where the configuration is present'
        ret['out'] = False
        return ret

    op = {}
    if '__pub_arg' in kwargs:
        if kwargs['__pub_arg']:
            if isinstance(kwargs['__pub_arg'][-1], dict):
                op.update(kwargs['__pub_arg'][-1])
    else:
        op.update(kwargs)

    test = op.pop('test', False)
    template_vars = {}
    if "template_vars" in op:
        template_vars = op["template_vars"]

    template_cached_path = salt.utils.files.mkstemp()
    __salt__['cp.get_template'](
        path,
        template_cached_path,
        template_vars=template_vars)

    if not os.path.isfile(template_cached_path):
        ret['message'] = 'Invalid file path.'
        ret['out'] = False
        return ret

    if os.path.getsize(template_cached_path) == 0:
        ret['message'] = 'Template failed to render'
        ret['out'] = False
        return ret

    write_diff = ''
    if 'diffs_file' in op and op['diffs_file'] is not None:
        write_diff = op['diffs_file']
        del op['diffs_file']

    op['path'] = template_cached_path

    if 'format' not in op:
        if path.endswith('set'):
            template_format = 'set'
        elif path.endswith('xml'):
            template_format = 'xml'
        else:
            template_format = 'text'

        op['format'] = template_format

    if 'replace' in op and op['replace']:
        op['merge'] = False
        del op['replace']
    elif 'overwrite' in op and op['overwrite']:
        op['overwrite'] = True
    elif 'overwrite' in op and not op['overwrite']:
        op['merge'] = True
        del op['overwrite']

    db_mode = op.pop('mode', 'exclusive')
    with Config(conn, mode=db_mode) as cu:
        try:
            cu.load(**op)

        except Exception as exception:
            ret['message'] = 'Could not load configuration due to : "{0}"'.format(
                exception)
            ret['format'] = op['format']
            ret['out'] = False
            return ret

        finally:
            salt.utils.files.safe_rm(template_cached_path)

        config_diff = cu.diff()
        if config_diff is None:
            ret['message'] = 'Configuration already applied!'
            ret['out'] = True
            return ret

        commit_params = {}
        if 'confirm' in op:
            commit_params['confirm'] = op['confirm']
        if 'comment' in op:
            commit_params['comment'] = op['comment']

        try:
            check = cu.commit_check()
        except Exception as exception:
            ret['message'] = \
                'Commit check threw the following exception: "{0}"'\
                .format(exception)

            ret['out'] = False
            return ret

        if check and not test:
            try:
                cu.commit(**commit_params)
                ret['message'] = 'Successfully loaded and committed!'
            except Exception as exception:
                ret['message'] = \
                    'Commit check successful but commit failed with "{0}"'\
                    .format(exception)
                ret['out'] = False
                return ret
        elif not check:
            cu.rollback()
            ret['message'] = 'Loaded configuration but commit check failed, hence rolling back configuration.'
            ret['out'] = False
        else:
            cu.rollback()
            ret['message'] = 'Commit check passed, but skipping commit for dry-run and rolling back configuration.'
            ret['out'] = True

        try:
            if write_diff and config_diff is not None:
                with salt.utils.files.fopen(write_diff, 'w') as fp:
                    fp.write(salt.utils.stringutils.to_str(config_diff))
        except Exception as exception:
            ret['message'] = 'Could not write into diffs_file due to: "{0}"'.format(
                exception)
            ret['out'] = False

    return ret