Returns a dictionary with the raw output of all commands passed as arguments.

    commands
        List of commands to be executed on the device.

    textfsm_parse: ``False``
        Try parsing the outputs using the TextFSM templates.

        .. versionadded:: 2018.3.0

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``napalm_cli_textfsm_parse``.

    textfsm_path
        The path where the TextFSM templates can be found. This option implies
        the usage of the TextFSM index file.
        ``textfsm_path`` can be either absolute path on the server,
        either specified using the following URL mschemes: ``file://``,
        ``salt://``, ``http://``, ``https://``, ``ftp://``,
        ``s3://``, ``swift://``.

        .. versionadded:: 2018.3.0

        .. note::
            This needs to be a directory with a flat structure, having an
            index file (whose name can be specified using the ``index_file`` option)
            and a number of TextFSM templates.

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``textfsm_path``.

    textfsm_template
        The path to a certain the TextFSM template.
        This can be specified using the absolute path
        to the file, or using one of the following URL schemes:

        - ``salt://``, to fetch the template from the Salt fileserver.
        - ``http://`` or ``https://``
        - ``ftp://``
        - ``s3://``
        - ``swift://``

        .. versionadded:: 2018.3.0

    textfsm_template_dict
        A dictionary with the mapping between a command
        and the corresponding TextFSM path to use to extract the data.
        The TextFSM paths can be specified as in ``textfsm_template``.

        .. versionadded:: 2018.3.0

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``napalm_cli_textfsm_template_dict``.

    platform_grain_name: ``os``
        The name of the grain used to identify the platform name
        in the TextFSM index file. Default: ``os``.

        .. versionadded:: 2018.3.0

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``textfsm_platform_grain``.

    platform_column_name: ``Platform``
        The column name used to identify the platform,
        exactly as specified in the TextFSM index file.
        Default: ``Platform``.

        .. versionadded:: 2018.3.0

        .. note::
            This is field is case sensitive, make sure
            to assign the correct value to this option,
            exactly as defined in the index file.

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``textfsm_platform_column_name``.

    index_file: ``index``
        The name of the TextFSM index file, under the ``textfsm_path``. Default: ``index``.

        .. versionadded:: 2018.3.0

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``textfsm_index_file``.

    saltenv: ``base``
        Salt fileserver envrionment from which to retrieve the file.
        Ignored if ``textfsm_path`` is not a ``salt://`` URL.

        .. versionadded:: 2018.3.0

    include_empty: ``False``
        Include empty files under the ``textfsm_path``.

        .. versionadded:: 2018.3.0

    include_pat
        Glob or regex to narrow down the files cached from the given path.
        If matching with a regex, the regex must be prefixed with ``E@``,
        otherwise the expression will be interpreted as a glob.

        .. versionadded:: 2018.3.0

    exclude_pat
        Glob or regex to exclude certain files from being cached from the given path.
        If matching with a regex, the regex must be prefixed with ``E@``,
        otherwise the expression will be interpreted as a glob.

        .. versionadded:: 2018.3.0

        .. note::
            If used with ``include_pat``, files matching this pattern will be
            excluded from the subset of files defined by ``include_pat``.

    CLI Example:

    .. code-block:: bash

        salt '*' net.cli "show version" "show chassis fan"

    CLI Example with TextFSM template:

    .. code-block:: bash

        salt '*' net.cli textfsm_parse=True textfsm_path=salt://textfsm/

    Example output:

    .. code-block:: python

        {
            'show version and haiku':  'Hostname: re0.edge01.arn01
                                          Model: mx480
                                          Junos: 13.3R6.5
                                            Help me, Obi-Wan
                                            I just saw Episode Two
                                            You're my only hope
                                         ',
            'show chassis fan' :   'Item                      Status   RPM     Measurement
                                      Top Rear Fan              OK       3840    Spinning at intermediate-speed
                                      Bottom Rear Fan           OK       3840    Spinning at intermediate-speed
                                      Top Middle Fan            OK       3900    Spinning at intermediate-speed
                                      Bottom Middle Fan         OK       3840    Spinning at intermediate-speed
                                      Top Front Fan             OK       3810    Spinning at intermediate-speed
                                      Bottom Front Fan          OK       3840    Spinning at intermediate-speed
                                     '
        }

    Example output with TextFSM parsing:

    .. code-block:: json

        {
          "comment": "",
          "result": true,
          "out": {
            "sh ver": [
              {
                "kernel": "9.1S3.5",
                "documentation": "9.1S3.5",
                "boot": "9.1S3.5",
                "crypto": "9.1S3.5",
                "chassis": "",
                "routing": "9.1S3.5",
                "base": "9.1S3.5",
                "model": "mx960"
              }
            ]
          }
        }

def cli(*commands, **kwargs):  # pylint: disable=unused-argument
    '''
    Returns a dictionary with the raw output of all commands passed as arguments.

    commands
        List of commands to be executed on the device.

    textfsm_parse: ``False``
        Try parsing the outputs using the TextFSM templates.

        .. versionadded:: 2018.3.0

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``napalm_cli_textfsm_parse``.

    textfsm_path
        The path where the TextFSM templates can be found. This option implies
        the usage of the TextFSM index file.
        ``textfsm_path`` can be either absolute path on the server,
        either specified using the following URL mschemes: ``file://``,
        ``salt://``, ``http://``, ``https://``, ``ftp://``,
        ``s3://``, ``swift://``.

        .. versionadded:: 2018.3.0

        .. note::
            This needs to be a directory with a flat structure, having an
            index file (whose name can be specified using the ``index_file`` option)
            and a number of TextFSM templates.

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``textfsm_path``.

    textfsm_template
        The path to a certain the TextFSM template.
        This can be specified using the absolute path
        to the file, or using one of the following URL schemes:

        - ``salt://``, to fetch the template from the Salt fileserver.
        - ``http://`` or ``https://``
        - ``ftp://``
        - ``s3://``
        - ``swift://``

        .. versionadded:: 2018.3.0

    textfsm_template_dict
        A dictionary with the mapping between a command
        and the corresponding TextFSM path to use to extract the data.
        The TextFSM paths can be specified as in ``textfsm_template``.

        .. versionadded:: 2018.3.0

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``napalm_cli_textfsm_template_dict``.

    platform_grain_name: ``os``
        The name of the grain used to identify the platform name
        in the TextFSM index file. Default: ``os``.

        .. versionadded:: 2018.3.0

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``textfsm_platform_grain``.

    platform_column_name: ``Platform``
        The column name used to identify the platform,
        exactly as specified in the TextFSM index file.
        Default: ``Platform``.

        .. versionadded:: 2018.3.0

        .. note::
            This is field is case sensitive, make sure
            to assign the correct value to this option,
            exactly as defined in the index file.

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``textfsm_platform_column_name``.

    index_file: ``index``
        The name of the TextFSM index file, under the ``textfsm_path``. Default: ``index``.

        .. versionadded:: 2018.3.0

        .. note::
            This option can be also specified in the minion configuration
            file or pillar as ``textfsm_index_file``.

    saltenv: ``base``
        Salt fileserver envrionment from which to retrieve the file.
        Ignored if ``textfsm_path`` is not a ``salt://`` URL.

        .. versionadded:: 2018.3.0

    include_empty: ``False``
        Include empty files under the ``textfsm_path``.

        .. versionadded:: 2018.3.0

    include_pat
        Glob or regex to narrow down the files cached from the given path.
        If matching with a regex, the regex must be prefixed with ``E@``,
        otherwise the expression will be interpreted as a glob.

        .. versionadded:: 2018.3.0

    exclude_pat
        Glob or regex to exclude certain files from being cached from the given path.
        If matching with a regex, the regex must be prefixed with ``E@``,
        otherwise the expression will be interpreted as a glob.

        .. versionadded:: 2018.3.0

        .. note::
            If used with ``include_pat``, files matching this pattern will be
            excluded from the subset of files defined by ``include_pat``.

    CLI Example:

    .. code-block:: bash

        salt '*' net.cli "show version" "show chassis fan"

    CLI Example with TextFSM template:

    .. code-block:: bash

        salt '*' net.cli textfsm_parse=True textfsm_path=salt://textfsm/

    Example output:

    .. code-block:: python

        {
            'show version and haiku':  'Hostname: re0.edge01.arn01
                                          Model: mx480
                                          Junos: 13.3R6.5
                                            Help me, Obi-Wan
                                            I just saw Episode Two
                                            You're my only hope
                                         ',
            'show chassis fan' :   'Item                      Status   RPM     Measurement
                                      Top Rear Fan              OK       3840    Spinning at intermediate-speed
                                      Bottom Rear Fan           OK       3840    Spinning at intermediate-speed
                                      Top Middle Fan            OK       3900    Spinning at intermediate-speed
                                      Bottom Middle Fan         OK       3840    Spinning at intermediate-speed
                                      Top Front Fan             OK       3810    Spinning at intermediate-speed
                                      Bottom Front Fan          OK       3840    Spinning at intermediate-speed
                                     '
        }

    Example output with TextFSM parsing:

    .. code-block:: json

        {
          "comment": "",
          "result": true,
          "out": {
            "sh ver": [
              {
                "kernel": "9.1S3.5",
                "documentation": "9.1S3.5",
                "boot": "9.1S3.5",
                "crypto": "9.1S3.5",
                "chassis": "",
                "routing": "9.1S3.5",
                "base": "9.1S3.5",
                "model": "mx960"
              }
            ]
          }
        }
    '''
    raw_cli_outputs = salt.utils.napalm.call(
        napalm_device,  # pylint: disable=undefined-variable
        'cli',
        **{
            'commands': list(commands)
        }
    )
    # thus we can display the output as is
    # in case of errors, they'll be catched in the proxy
    if not raw_cli_outputs['result']:
        # Error -> dispaly the output as-is.
        return raw_cli_outputs
    textfsm_parse = kwargs.get('textfsm_parse') or __opts__.get('napalm_cli_textfsm_parse') or\
                    __pillar__.get('napalm_cli_textfsm_parse', False)
    if not textfsm_parse:
        # No TextFSM parsing required, return raw commands.
        log.debug('No TextFSM parsing requested.')
        return raw_cli_outputs
    if 'textfsm.extract' not in __salt__ or 'textfsm.index' not in __salt__:
        raw_cli_outputs['comment'] += 'Unable to process: is TextFSM installed?'
        log.error(raw_cli_outputs['comment'])
        return raw_cli_outputs
    textfsm_template = kwargs.get('textfsm_template')
    log.debug('textfsm_template: %s', textfsm_template)
    textfsm_path = kwargs.get('textfsm_path') or __opts__.get('textfsm_path') or\
                   __pillar__.get('textfsm_path')
    log.debug('textfsm_path: %s', textfsm_path)
    textfsm_template_dict = kwargs.get('textfsm_template_dict') or __opts__.get('napalm_cli_textfsm_template_dict') or\
                            __pillar__.get('napalm_cli_textfsm_template_dict', {})
    log.debug('TextFSM command-template mapping: %s', textfsm_template_dict)
    index_file = kwargs.get('index_file') or __opts__.get('textfsm_index_file') or\
                 __pillar__.get('textfsm_index_file')
    log.debug('index_file: %s', index_file)
    platform_grain_name = kwargs.get('platform_grain_name') or __opts__.get('textfsm_platform_grain') or\
                          __pillar__.get('textfsm_platform_grain', 'os')
    log.debug('platform_grain_name: %s', platform_grain_name)
    platform_column_name = kwargs.get('platform_column_name') or __opts__.get('textfsm_platform_column_name') or\
                           __pillar__.get('textfsm_platform_column_name', 'Platform')
    log.debug('platform_column_name: %s', platform_column_name)
    saltenv = kwargs.get('saltenv', 'base')
    include_empty = kwargs.get('include_empty', False)
    include_pat = kwargs.get('include_pat')
    exclude_pat = kwargs.get('exclude_pat')
    processed_cli_outputs = {
        'comment': raw_cli_outputs.get('comment', ''),
        'result': raw_cli_outputs['result'],
        'out': {}
    }
    log.debug('Starting to analyse the raw outputs')
    for command in list(commands):
        command_output = raw_cli_outputs['out'][command]
        log.debug('Output from command: %s', command)
        log.debug(command_output)
        processed_command_output = None
        if textfsm_path:
            log.debug('Using the templates under %s', textfsm_path)
            processed_cli_output = __salt__['textfsm.index'](command,
                                                             platform_grain_name=platform_grain_name,
                                                             platform_column_name=platform_column_name,
                                                             output=command_output.strip(),
                                                             textfsm_path=textfsm_path,
                                                             saltenv=saltenv,
                                                             include_empty=include_empty,
                                                             include_pat=include_pat,
                                                             exclude_pat=exclude_pat)
            log.debug('Processed CLI output:')
            log.debug(processed_cli_output)
            if not processed_cli_output['result']:
                log.debug('Apparently this didnt work, returnin the raw output')
                processed_command_output = command_output
                processed_cli_outputs['comment'] += '\nUnable to process the output from {0}: {1}.'.format(command,
                    processed_cli_output['comment'])
                log.error(processed_cli_outputs['comment'])
            elif processed_cli_output['out']:
                log.debug('All good, %s has a nice output!', command)
                processed_command_output = processed_cli_output['out']
            else:
                comment = '''\nProcessing "{}" didn't fail, but didn't return anything either. Dumping raw.'''.format(
                    command)
                processed_cli_outputs['comment'] += comment
                log.error(comment)
                processed_command_output = command_output
        elif textfsm_template or command in textfsm_template_dict:
            if command in textfsm_template_dict:
                textfsm_template = textfsm_template_dict[command]
            log.debug('Using %s to process the command: %s', textfsm_template, command)
            processed_cli_output = __salt__['textfsm.extract'](textfsm_template,
                                                               raw_text=command_output,
                                                               saltenv=saltenv)
            log.debug('Processed CLI output:')
            log.debug(processed_cli_output)
            if not processed_cli_output['result']:
                log.debug('Apparently this didnt work, returning '
                          'the raw output')
                processed_command_output = command_output
                processed_cli_outputs['comment'] += '\nUnable to process the output from {0}: {1}'.format(command,
                    processed_cli_output['comment'])
                log.error(processed_cli_outputs['comment'])
            elif processed_cli_output['out']:
                log.debug('All good, %s has a nice output!', command)
                processed_command_output = processed_cli_output['out']
            else:
                log.debug('Processing %s didnt fail, but didnt return'
                          ' anything either. Dumping raw.', command)
                processed_command_output = command_output
        else:
            log.error('No TextFSM template specified, or no TextFSM path defined')
            processed_command_output = command_output
            processed_cli_outputs['comment'] += '\nUnable to process the output from {}.'.format(command)
        processed_cli_outputs['out'][command] = processed_command_output
    processed_cli_outputs['comment'] = processed_cli_outputs['comment'].strip()
    return processed_cli_outputs