'''
HubbleStack Nova plugin for auditing installed packages.

The module gets the list of installed packages of the system and queries
the Vulners.com Linux Vulnerability Audit API.

The API is described at the link below:
    https://blog.vulners.com/linux-vulnerability-audit-in-vulners/

:maintainer: HubbleStack / avb76
:maturity: 3/26/2017 (TODO: change the format when the release date is established)
:platform: Linux
:requires: SaltStack

This audit module requires a YAML file inside the hubblestack_nova_profiles directory.
The file should have the following format:

vulners_scanner: <random data>

It does not matter what `<random data>` is, as long as the top key of the file is named `vulners_scanner`.
This allows the module to run under a certain profile, as all of the other Nova modules do.
'''

from __future__ import absolute_import

import sys
import requests

# TODO: add logging

def __virtual__():
    return not sys.platform.startswith('win')


def audit(data_list, tags, debug=False):
    os_name = __grains__.get('os').lower()
    os_version = __grains__.get('osmajorrelease')

    ret = {'Success': [], 'Failure': [], 'Controlled': []}

    for profile, data in data_list:
        if 'vulners_scanner' in data:

            local_packages = _get_local_packages()
            vulners_data = _process_vulners(_vulners_query(local_packages, os = os_name, version = os_version))

            total_packages = len(local_packages)
            secure_packages = total_packages - len(vulners_data)

            ret['Success'] = [{'tag': 'Secure packages',
                           'description': '{0} out of {1}'.format(secure_packages, total_packages)}]
            ret['Failure'] = vulners_data

    return ret


def _get_local_packages():
    '''
    Get the packages installed on the system.

    :return: A nice list of packages.
    '''

    local_packages = __salt__['pkg.list_pkgs']()
    return ['{0}-{1}'.format(pkg, local_packages[pkg]) for pkg in local_packages]


def _vulners_query(packages=None, os=None, version=None, url='https://vulners.com/api/v3/audit/audit/'):
    '''
    Query the Vulners.com Linux Vulnerability Audit API for the provided packages.

    :param packages: The list on packages to check
    :param os: The name of the operating system
    :param version: The version of the operating system
    :param url: The URL of the auditing API; the default value is the Vulners.com audit API
                Check the following link for more details:
                    https://blog.vulners.com/linux-vulnerability-audit-in-vulners/
    :return: A dictionary containing the JSON data returned by the HTTP request.
    '''

    # error dict matching the error dict returned by the requests library
    error = {
        'result': 'ERROR',
        'data': {'error': None}
    }

    if not packages:
        error['data']['error'] = 'Missing the list of packages.'
        return error
    if not os and not version:
        error['data']['error'] = 'Missing the operating system name and version.'
        return error
    if not os:
        error['data']['error'] = 'Missing the operating system name.'
        return error
    if not version:
        error['data']['error'] = 'Missing the operating system version.'
        return error


    headers = {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    }

    data = {
        "os": os,
        "package": packages,
        "version": version
    }

    try:
        response = requests.post(url=url, headers=headers, json=data)
        return response.json()
    except requests.Timeout:
        error['data']['error'] = 'Request to {0} timed out'.format(url)
        return error


def _process_vulners(vulners):
    '''
    Process the data returned by the API into the format accepted by `hubble.py`.

    :param vulners: The JSON data returned by the API
    :return: A list of dictionaries as hubble.py swallows
    '''

    packages = vulners.get('data', {}).get('packages')
    if not packages:
        return []

    return [{'tag': pkg,
             'vulnerabilities': packages[pkg],
             'description': ', '.join(packages[pkg].keys())}
            for pkg in packages]


@@ -845,16 +845,31 @@
 e_import
+%0Aimport logging
 %0A%0Aimport
@@ -894,27 +894,43 @@
 ts%0A%0A
-# TODO: add logging
+%0Alog = logging.getLogger(__name__)%0A
 %0A%0Ade
@@ -1129,16 +1129,108 @@
 ease')%0A%0A
+    if debug:%0A        log.debug(%22os_version: %7B0%7D, os_name%7B1%7D%22.format(os_version, os_name))%0A%0A
     ret 
@@ -1356,16 +1356,16 @@
  data:%0A%0A
-
         
@@ -1407,16 +1407,213 @@
 kages()%0A
+            vulners_data = _vulners_query(local_packages, os=os_name, version=os_version)%0A            if vulners_data%5B'result'%5D == 'ERROR':%0A                log.error(vulners_data%5B'data'%5D%5B'error'%5D)%0A
         

Add logging and error handling