#!/usr/bin/env python

# Parse Ansible result log file and and generate a report

import os, time
import json
import sqlite3
from datetime import datetime, timedelta

import sys

FIELDS = ['cmd', 'command', 'start', 'end', 'delta', 'msg', 'stdout', 'stderr',
          'response', 'status_code', 'url', 'dest']

class ansibleReport(object):

  def __init__(self, db_path,
                    ansible_log_dir,
                    name):
    self.db_path = db_path
    self.ansible_log_dir = ansible_log_dir
    self.name = name
    self.result_OK = '127.0.0.1_OK'
    self.result_failed = '127.0.0.1_FAILED'
    self.result_failed_ignore = '127.0.0.1_FAILED_INGORED' # tipo in ansible log upload pluging
    self.date_format = '%Y-%m-%d %H:%M:%S'
    self.day_format = '%Y-%m-%d'
    
    self._init_db()

  def _init_db(self):
    db = sqlite3.connect(self.db_path)
    c = db.cursor()
    c.executescript("""
CREATE TABLE IF NOT EXISTS ansible_report (
  name VARCHAR(40),
  reportdate VARCHAR(15),
  createdate VARCHAR(15),
  status VARCHAR(20),
  success_count INTEGER,
  ignored_count INTEGER,
  failed_count INTEGER,
  ignored TEXT,
  failed TEXT,
  success TEXT);
""")
    db.commit()
    db.close()

  def connect_db(self):
    db = sqlite3.connect(self.db_path)
    return db

  def insertEntryDb(self, table_name, data_dict):
    db = self.connect_db()
    columns = data_dict.keys()
    entries = ', '.join(columns)
    values = '?' + ', ?' * (len(columns)-1)
    sql_string = "insert into %s(%s) values (%s)" % (
                    table_name, entries, values)
    tuple_data = ()
    for key in columns:
      tuple_data += (data_dict[key],)
    db.execute(sql_string, tuple_data)
    db.commit()
    db.close()

  def selectEntriesDb(self, fields=[], start_date=None, limit=0, success=None, order='DESC', where=""):
    db = self.connect_db()

    entries = ', '.join(fields) if fields else '*'
    query = "select %s from ansible_report " % entries
    where = " and %s" % where if where else ""
    if not start_date:
      start_date = datetime.utcnow().strftime(self.day_format)
    tuple_values = (start_date,)
    if success is not None:
      status = 'OK' if success else 'FAILLED'
      query += "where createdate>=? and status=? %s order by createdate %s" % (where, order)
      tuple_values += (status,)
    else:
      query += "where createdate>=? %s order by createdate %s" % (where, order)
    if limit:
      query += " limit ?"
      tuple_values += (limit,)

    rows = db.cursor().execute(query, tuple_values)
    #db.close()
    if rows:
      return [list(row) for row in rows]
    return []

  def truncateEntriesDb(self, table_name, on_field, to_value, operator='<'):
    db = self.connect_db()
    query = "delete from %s where %s%s?" % (table_name, on_field,
                                            operator)
    db.execute(query, (to_value,))
    db.commit()
    db.close()

  def getLogString(self, res, head=False):
    log = ""
    if type(res) == type(dict()):
      log = '%s, args [%s]\n' % (res['invocation']['module_name'],
                                        res['invocation']['module_args'])
      if head:
        return log
      for field in FIELDS:
        if field in res.keys():
        	# use default encoding, check out sys.setdefaultencoding
          log += '\n{0}:\n{1}'.format(field, res[field])
  
    return log

  def _read_file(self, filepath):
    content = '[]'
    with open(filepath, 'r') as f:
      content = f.read()
    return content

  def saveResult(self):
    
    date = datetime.utcnow().strftime(self.date_format)

    files_list = os.listdir(self.ansible_log_dir)
    if not len(files_list):
      return

    to_date = (datetime.now() - timedelta(days=2)).strftime(self.date_format)
    cmp_file = os.path.join(self.ansible_log_dir, files_list.pop())
    modification_date = datetime.fromtimestamp(
                            os.path.getmtime(cmp_file)
                          ).strftime(self.date_format)
    # Get the latest insert date
    result = self.selectEntriesDb(['reportdate'], start_date=to_date, limit=1)
    if len(result):
      latest_date = result[0][0]
      if latest_date >= modification_date:
        return

    file_map = dict(
          success_file=os.path.join(self.ansible_log_dir, self.result_OK),
          failed_file=os.path.join(self.ansible_log_dir, self.result_failed),
          ignored_file=os.path.join(self.ansible_log_dir, self.result_failed_ignore))
    
    data = dict(name=self.name, status='FAILED',
                reportdate=modification_date, createdate=date,
                success_count=0, ignored_count=0,
                failed_count=0, success="",
                failed="", ignored="")
    for category in ('failed', 'success', 'ignored'):
      file_category = file_map['%s_file' % category]
      if os.path.exists(file_category):
        text_content = self._read_file(file_category)
        count = len(json.loads(text_content))
        if count > 0:
          data['%s_count' % category] = count
          data[category] = text_content
    
    if data['failed_count'] == 0:
      data['status'] = 'OK'

    self.insertEntryDb('ansible_report', data)

  def getAnsibleReport(self, start_date=None, limit=0, success=None, order='DESC', category=None, head=False, only_state=True):
    """Get one or many entries from the ansible report table.
    """
    where = ""
    get_content = category is not None
    fields = ['name', 'reportdate', 'createdate', 'status', 'success_count',
                'ignored_count', 'failed_count']
    if category:
      where = " %s_count>0" % category
      if not only_state:
        fields.append(category)

    rows = self.selectEntriesDb(fields=fields, start_date=start_date,
                                limit=limit, success=success, order=order,
                                where=where)
    result_dict = {}
    if category and not only_state:
      last_pos = len(fields) -1
      for i in range (0, len(rows)):
        message = ""
        message_list = json.loads(rows[i][last_pos])
        for msg in message_list:
          message += '%s\n\n' %  self.getLogString(msg, head=head)
        rows[i][last_pos] = message
    else:
      return {}
    return rows


if __name__ == "__main__":
  json = """{
  "status": "OK",
  "message": "kvm-1: OK(114) FAILED(0) IGNORED(2)",
  "description": "Ansible playbook report in kvm-1. Execution date is: 2015-08-28 17:42:01."
}"""
  parameter_dict = json.loads(sys.argv[1])
  with open(parameter_dict['status_path'], 'w') as status_file:
    status_file.write(json)