<file_sep>import os
import sys
import logging
import time
import shutil
import socket
import getpass
import ConfigParser
import simplejson as json
from datetime import datetime
from tabulate import tabulate

temp_dir = "/tmp/"


# Return color based on status
def get_color(status):
    if status is False:
        return "{}{}{}".format('\033[91m', str(status), '\033[0m')
    elif status is True:
        return "{}{}{}".format('\033[33m', str(status), '\033[0m')
    else:
        return "{}{}{}".format('\033[92m', str(status), '\033[0m')


def get_time_rfc_3339():
    time_run = datetime.utcnow()
    return time_run.isoformat("T") + "Z"


def make_dir(dir_name):
    if not os.path.isdir(dir_name):
        try:
            os.makedirs(dir_name)
        except Exception as e:
            logging.error("Unable to create directory '%s' error:'%s'" % (dir_name, e))
            return False
    return True


def log_setup(debug_level, log_file=False, log_stdout=False):
    root = logging.getLogger()
    if root.handlers:
        for handler in root.handlers:
            root.removeHandler(handler)
    kwargs = {}
    if log_file:
        make_dir(os.path.dirname(log_file))
        kwargs.update({"filename": log_file})
    elif log_stdout:
        kwargs.update({"filename": logging.StreamHandler(sys.stdout)})
    log_level = getattr(logging, debug_level)
    kwargs.update({"level": log_level})
    kwargs.update({"format": "%(asctime)s - %(name)s - %(levelname)s - %(lineno)s - %(module)s : %(message)s"})
    logging.basicConfig(**kwargs)


class TaskFile():
    dir_failure = temp_dir + "/aeco_fail"
    file_failure = dir_failure + "/" + "fail_log_%s_callback.json" % os.getpid()
    dir_changed = temp_dir + "/aeco_changed"
    file_changed = dir_changed + "/" + "changed_log_%s_callback.json" % os.getpid()

    @classmethod
    def clean_folders(cls):
        # Clean failure dir
        if os.path.isdir(cls.dir_failure):
            shutil.rmtree(cls.dir_failure + "/")
        # Clean changed dir
        if os.path.isdir(cls.dir_changed):
            shutil.rmtree(cls.dir_changed + "/")

    @staticmethod
    def _write_to_file(file_path, data):
        try:
            with open(file_path, 'at') as the_file:
                the_file.write(json.dumps(data) + "\n")
        except Exception, e:
            logging.error("Write to file failed '%s' error:'%s'" % (file_path, e))

    @classmethod
    def _read_from_file(cls, file_path):
        if os.path.isfile(file_path):
            try:
                with open(file_path, 'rt') as f:
                    lines = f.readlines()
                return lines
            except IOError, e:
                logging.error("Reading file failed '%s' I/O error: %s'" % (file_path, e))
            except Exception as e:
                logging.error("Reading file failed '%s' error: %s'" % (file_path, e))
        return False

    @classmethod
    def write_failures_to_file(cls, host, res, ignore_errors, name=None):
        failure_data = dict()
        invocation = res.get("invocation", {})
        failure_data["fail_module_args"] = invocation.get("module_args", "")
        failure_data["fail_module_name"] = invocation.get("module_name", "")
        failure_data["ignore_error"] = ignore_errors
        if name:
            failure_data["task_name"] = name
        failure_data["host"] = host
        failure_data["fail_msg"] = res.get("msg", "")
        cls._write_to_file(cls.file_failure, failure_data)

    @classmethod
    def write_changed_to_file(cls, host, res, name=None):
        changed_data = dict()
        invocation = res.get("invocation", {})
        changed_data["changed_module_args"] = invocation.get("module_args", "")
        changed_data["changed_module_name"] = invocation.get("module_name", "")
        changed_data["host"] = host
        if name:
            changed_data["task_name"] = name
        changed_data["changed_msg"] = res.get("msg", "")
        cls._write_to_file(cls.file_changed, changed_data)

    @classmethod
    def read_failures_from_file(cls):
        return cls._read_from_file(cls.file_failure)

    @classmethod
    def read_changed_from_file(cls):
        return cls._read_from_file(cls.file_changed)


class AecoCallBack(object):
    class PlayTimer():
        def __init__(self):
            self.timer_start = time.time()
            self.timer_stop = 0
            self.timer_run = 0

        def stop(self):
            self.timer_stop = time.time()
            self.timer_run = format(self.timer_stop - self.timer_start, '.2f')

    class AnsibleStats():
        def __init__(self):
            self.failures = []
            self.changed = []
            self.summaries = []
            self.common = dict()
            self.clock = None  # Timer for run

        def set_common_parm(self, playbook):
            self.common["ansible_version"] = playbook._ansible_version.get("full")
            self.common["check"] = playbook.check
            self.common["play_name"] = playbook.filename
            self.common["only_tags"] = playbook.only_tags
            self.common["skip_tags"] = playbook.skip_tags
            self.common["transport"] = playbook.transport
            self.common["workstation"] = socket.gethostname()
            self.common["user"] = getpass.getuser()
            self.common["extra_vars"] = playbook.extra_vars
            self.common["timeout"] = playbook.timeout

        def set_changed(self):
            timestamp = get_time_rfc_3339()
            lines = TaskFile().read_changed_from_file()

            if not lines:
                return False
            for line in lines:
                json_line = json.loads(line)  # convert json to dic
                changed = dict()
                changed["host"] = json_line.get("host")
                changed["@timestamp"] = timestamp
                changed["changed_msg"] = json_line.get("changed_msg")
                changed["changed_module_name"] = json_line.get("changed_module_name")
                changed["changed_module_args"] = json_line.get("changed_module_args")
                if json_line.get("task_name", False):
                    changed["task_name"] = json_line.get("task_name")
                changed.update(self.common)
                self.changed.append(changed)

        def set_failures(self):
            timestamp = get_time_rfc_3339()
            lines = TaskFile().read_failures_from_file()

            if not lines:
                return False
            for line in lines:
                json_line = json.loads(line)  # convert json to dic
                failure = dict()
                failure["host"] = json_line.get("host")
                failure["@timestamp"] = timestamp
                failure["fail_msg"] = json_line.get("fail_msg")
                failure["fail_module_name"] = json_line.get("fail_module_name")
                failure["ignore_error"] = json_line.get("ignore_error")
                failure["fail_module_args"] = json_line.get("fail_module_args")
                if json_line.get("task_name", False):
                    failure["task_name"] = json_line.get("task_name")

                failure.update(self.common)
                self.failures.append(failure)

        def set_stats(self, stats):
            timestamp = get_time_rfc_3339()
            for host in stats.processed.keys():
                summary = dict()
                summary["host"] = host
                summary["@timestamp"] = timestamp
                summary["time"] = self.clock.timer_run
                summary.update(stats.summarize(host))  # ok,changed,failed, unreachable
                summary.update(self.common)
                self.summaries.append(summary)

    def __init__(self):
        make_dir(TaskFile().dir_failure)
        make_dir(TaskFile().dir_changed)
        self.stats = self.AnsibleStats()
        self.dbs = []
        self.db_settings = []

    def db_insert(self):
        for db_setting in self.db_settings:
            new_db = db_setting["name"](**db_setting["init"])
            new_db.insert_data(self.stats)
            self.dbs.append(new_db)  # register new instances

    def print_summary(self):
        headers = ["", ""]
        display = []
        display.append(["CB--> Execution time", "[ " + self.stats.clock.timer_run + " ]"])
        i = 1
        for db in self.dbs:
            display.append(["{}) {} Connection ".format(i, db.db_name), "[ " + get_color(db.db_status) + " ]"])
            if db.db_status:  # Check db status
                # Status of summary
                display.append(["...../ Summary insert", "[ " + get_color(db.db_summary_status) + " ]"])
                # Status of summary
                display.append(["...../ Changed insert", "[ " + get_color(db.db_changed_status) + " ]"])
                # Status of failures
                display.append(["...../ Failures insert", "[ " + get_color(db.db_failures_status) + " ]"])
            i += 1
        print tabulate(display, headers=headers)


def recurse_parse_section(config, section):
    section_result = {}
    for (each_key, each_value) in config.items(section):
        section_result.update({each_key: each_value})
    return section_result


def parse_config(path_to_me, __CONF__,):
    config = ConfigParser.ConfigParser()
    config_file = path_to_me + __CONF__
    try:
        config.read(config_file)
        result = {}
        for section in config.sections():
            result.update({section: recurse_parse_section(config, section)})
        return result

    except ConfigParser.NoSectionError as E:
        print "Config error %s" % E

<file_sep>import sys
import os
from datetime import datetime

path_to_me = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(path_to_me + "/callback_lib"))
from _base import AecoCallBack, TaskFile, log_setup, parse_config

__CONF__ = "/_aeco_callback.ini"
conf_dic = parse_config(path_to_me, __CONF__)
#TODO Not working
log_config = conf_dic.pop("log", {})
log_setup(log_config.get("level", "INFO"), log_file=log_config.get("filename", False),
          log_stdout=log_config.get("stdout", False))

db_settings = []
for key, value in conf_dic.iteritems():
    try:
        baseImport = __import__(key)
        lib2import = getattr(baseImport, value.get("name"))
    except Exception as E:
        print "Error cant import '%s' raw error '%s'" % (lib2import, E)
    else:
        value.pop("name")
        db_settings.append({"name": lib2import, "init": value})


class CallbackModule(object):
    def __init__(self):
        self.current_task = None
        if len(db_settings) > 1:
            self.my_aeco = AecoCallBack()
            self.my_aeco.db_settings = db_settings
            self.enabled = True
        else:
            self.enabled = False

    def on_any(self, *args, **kwargs):
        pass

    def runner_on_failed(self, host, res, ignore_errors=False):
        if self.enabled:
            TaskFile().write_failures_to_file(host, res, ignore_errors, self.current_task)

    def runner_on_ok(self, host, res):
        if self.enabled and res.get("changed"):
            TaskFile().write_changed_to_file(host, res, self.current_task)

    def runner_on_skipped(self, host, item=None):
        pass

    def runner_on_unreachable(self, host, res):
        pass

    def runner_on_no_hosts(self):
        pass

    def runner_on_async_poll(self, host, res, jid, clock):
        pass

    def runner_on_async_ok(self, host, res, jid):
        pass

    def runner_on_async_failed(self, host, res, jid):
        pass

    def playbook_on_start(self):
        if self.enabled:
            self.my_aeco.stats.clock = self.my_aeco.PlayTimer()

    def playbook_on_notify(self, host, handler):
        pass

    def playbook_on_no_hosts_matched(self):
        pass

    def playbook_on_no_hosts_remaining(self):
        pass

    def playbook_on_task_start(self, name, is_conditional):
        self.current_task = name

    def playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None):
        pass

    def playbook_on_setup(self):
        pass

    def playbook_on_import_for_host(self, host, imported_file):
        pass

    def playbook_on_not_import_for_host(self, host, missing_file):
        pass

    def playbook_on_play_start(self, name):
        pass

    def playbook_on_stats(self, stats):
        if self.enabled:
            # Stop timer
            self.my_aeco.stats.clock.stop()
            # Set global parm
            self.my_aeco.stats.set_common_parm(self.playbook)
            # Update stats dict
            self.my_aeco.stats.set_stats(stats)
            self.my_aeco.stats.set_changed()
            self.my_aeco.stats.set_failures()
            # db
            self.my_aeco.db_insert()
            # Print summary
            self.my_aeco.print_summary()
            # Do clean up of dir
            TaskFile().clean_folders()
<file_sep>import logging
import time

# http://stackoverflow.com/questions/25447869/logging-using-elasticsearch-py


class ElasticCallBack():
    def __init__(self, host, port, timeout=2):
        self.db_name = "Elasticsearch DB"
        try:
            self.elasticsearch = __import__('elasticsearch')
            self.helpers = __import__('elasticsearch.helpers')
            self.db_import = True
        except ImportError:
            self.db_import = False
        self.index_name = "ansible_log-" + time.strftime('%Y.%m.%d')  # ES index name one per day
        # Connection Setting
        self.timeout = int(timeout)
        self.host = host
        self.port = port
        self.db_server = self.host + ":" + str(self.port)
        self.db_status = self._connect()
        self.db_summary_status = False
        self.db_failures_status = False
        self.db_changed_status = False

    def _connect(self):
        if self.db_import:
            try:
                self.es = self.elasticsearch.Elasticsearch(self.db_server, timeout=self.timeout)
            except Exception, e:

                logging.error("Failed to initialized elasticSearch server '%s'. Exception = %s " % (self.db_server, e))
                return False
            try:
                return self.es.ping()
            except Exception, e:
                logging.error("Failed to get ping from elasticSearch server '%s'. Exception = %s " % (self.db_server, e))
                return False

    def _insert(self, data, doc_type=None):
        if self.db_status:
            try:
                result = self.helpers.helpers.bulk(self.es, data, index=self.index_name, doc_type=doc_type)
                logging.debug("Inserting into ES result='%s' doc_type='%s' index='%s data='%s'" %
                              (result, doc_type, self.index_name, data))
                if result:
                    return True
            except Exception, e:
                logging.error("Inserting data into ES 'failed' because %s" % e)
        return False

    def insert_data(self, data):
        if self.db_status:
            # 1- Prepare summaries for insertion
            new_summaries = []
            for summary in data.summaries:
                new_summaries.append({"_source": summary})
            self.db_summary_status = self._insert(new_summaries, doc_type="ansible-summary")

            # Prepare failures for insertion
            if len(data.failures) > 0:
                new_failures = []
                for failure in data.failures:
                    new_failures.append({"_source": failure})
                self.db_failures_status = self._insert(new_failures, doc_type="ansible-failure")
            else:
                # No Failures
                self.db_failures_status = None

            # Prepare changed for insertion
            if len(data.changed) > 0:
                new_changed = []
                for change in data.changed:
                    new_changed.append({"_source": change})
                self.db_changed_status = self._insert(new_changed, doc_type="ansible-changed")
            else:
                # No changed
                self.db_changed_status = None<file_sep>import logging


class RethinkDBCallBack():
    def __init__(self, host, port, timeout=2):
        self.db_name = "RethinkDB"
        try:
            self.r = __import__('rethinkdb')
            self.db_import = True
        except ImportError:
            self.db_import = False

        # Connection Setting
        self.db_database = "ansible"
        self.db_tables = ["summary", "failure", "changed"]
        self.timeout = int(timeout)
        self.host = str(host)
        self.port = str(port)
        self.db_server = self.host + ":" + self.port  # not used but good for printing
        self.db_status = self._connect()
        self.db_summary_status = False
        self.db_failures_status = False
        self.db_changed_status = False

    def _create_db(self):
        try:
            self.r.db_create(self.db_database).run()
        except Exception, e:
            print "Failed to create database '%s' error: '%s'" % (self.db_database, e)
            return False
        return True

    def _create_table(self, table):
        try:
            self.r.db(self.db_database).table_create(table).run()
        except Exception, e:
            print "Failed to create table '%s' in '%s' error: '%s'" % (table, self.db_database, e)
            return False
        return True

    def _connect(self):
        if self.db_import:
            try:
                self.r.connect(self.host, self.port, timeout=self.timeout).repl()
                list_of_db = self.r.db_list().run()
                if not any(self.db_database == db for db in list_of_db):
                    if not self._create_db():
                        return False
                list_of_tables = self.r.db(self.db_database).table_list().run()
                for table in self.db_tables:
                    if not any(table == list_table for list_table in list_of_tables):
                        if not self._create_table(table):
                            return False
                return True
            except Exception, e:

                logging.error("Failed to initialized rethinkdb server '%s'. Error: '%s'" % (self.db_server, e))
        return False

    def _insert(self, table, data):
        if self.db_status:
            try:
                result = self.r.db(self.db_database).table(table).insert(data).run()
                if result.get("inserted") >= 1:
                    return True

                return False
            except Exception, e:
                logging.error("Inserting data into ES 'failed' because %s" % e)
        return False

    def insert_data(self, data):
        if self.db_status:
            self.db_summary_status = self._insert("summary", data.summaries)
            # Prepare failures for insertion
            if len(data.failures) > 0:
                self.db_failures_status = self._insert("failure", data.failures)
            else:
                # No Failures
                self.db_failures_status = None
            # Prepare changed for insertion
            if len(data.changed) > 0:
                self.db_changed_status = self._insert("changed", data.changed)
            else:
                # No Changed
                self.db_changed_status = None<file_sep>__author__ = '<NAME>'
