#!{{ python_executable }}

import datetime
import json
import os
import subprocess
import sys
import sqlite3
import time
import threading
from optparse import OptionParser, make_option


FAILURE = "FAILURE"
SUCCESS = "SUCCESS"

db_path = "{{ monitor_parameter['db-path'] }}"
instance_path = "{{ directory['home'] }}"
monitor_dir = "{{ directory['monitor-custom-scripts'] }}"
pid_dir = "{{ directory['run'] }}"
promise_dir = "{{ directory['promise'] }}"

monitoring_file_json = "{{ monitoring_file_json }}"

option_list = [
  make_option("-a", "--all", action="store_true", dest="all",
              help="test everything : promises, services, customs"),
  make_option("-n", "--no-write", action="store_true", dest="only_stdout",
              help="just show the json output on stdout"),
  make_option("-m", "--monitors", action="store_true", dest="monitor",
              help="add the custom monitoring file to the files to monitor"),
  make_option("-p", "--promises", action="store_true", dest="promise",
              help="add the promises\'file to the files to monitor"),
  make_option("-s", "--services", action="store_true", dest="service",
              help="add the file containing services\'pid to the files to monitor")
]

class Popen(subprocess.Popen):
  __timeout = None

  def timeout(self, delay, delay_before_kill=5):
    if self.__timeout is not None: self.__timeout.cancel()
    self.__timeout = threading.Timer(delay, self.stop, [delay_before_kill])
    self.__timeout.start()
    def waiter():
        self.wait()
        self.__timeout.cancel()
    threading.Thread(target=waiter).start()

  def stop(self, delay_before_kill=5):
    if self.__timeout is not None: self.__timeout.cancel()
    self.terminate()
    t = threading.Timer(delay_before_kill, self.kill)
    t.start()
    r = self.wait()
    t.cancel()
    return r

def init_db():
  db = sqlite3.connect(db_path)
  c = db.cursor()
  c.executescript("""
CREATE TABLE IF NOT EXISTS status (
  timestamp INTEGER UNIQUE,
  status VARCHAR(255));
CREATE TABLE IF NOT EXISTS individual_status (
  timestamp INTEGER,
  status VARCHAR(255),
  element VARCHAR(255),
  output TEXT);
""")
  db.commit()
  db.close()

def getListOfScripts(directory):
  """
  Get the list of script inside of a directory (not recursive)
  """
  scripts = []
  if os.path.exists(directory) and os.path.isdir(directory):
    for file_name in os.listdir(directory):
      file = os.path.join(directory, file_name)
      if os.access(file, os.X_OK) and not os.path.isdir(file):
        scripts.append(file)
  else:
    exit("There is a problem in your directories" \
          "of monitoring. Please check them")
  return scripts


def runServices(directory):
  services = getListOfScripts(directory)
  result = {}
  for service in services:
    service_path = os.path.join(pid_dir, service)
    service_name = os.path.basename(service_path)
    try:
      pid = int(open(service_path).read())
    ### because apache (or others) can write sockets
    ### We also ignore not readable pid files
    except (IOError, ValueError):
      continue
    try:
      os.kill(pid, 0)
      result[service_name] = ''
    except OSError:
      result[service_name] = "This service is not running anymore"
  return result


def runScripts(directory):
  scripts = getListOfScripts(directory)
  # XXX script_timeout could be passed as parameters
  script_timeout = 60 # in seconds
  result = {}
  for script in scripts:
    command = [os.path.join(promise_dir, script)]
    script = os.path.basename(command[0])
    result[script] = ''

    process_handler = Popen(command,
                            cwd=instance_path,
                            env=None if sys.platform == 'cygwin' else {},
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE,
                            stdin=subprocess.PIPE)
    process_handler.stdin.flush()
    process_handler.stdin.close()
    process_handler.stdin = None

    process_handler.timeout(script_timeout)
    process_handler.wait()

    if process_handler.poll() is None:
      process_handler.terminate()
      result[script] = "Time Out"
    elif process_handler.poll() != 0:
      stderr = process_handler.communicate()[1]
      if stderr is not None:
        result[script] = stderr.strip()
  return result


def writeFiles(monitors):
  timestamp = int(time.time())
  date = datetime.datetime.now().ctime()
  init_db()
  db = sqlite3.connect(db_path)
  fail = False
  for key, value in monitors.iteritems():
    element_status = SUCCESS
    if value != "" :
      fail = True
      element_status = FAILURE
    db.execute("insert into individual_status(timestamp, element, output, status) values (?, ?, ?, ?)", (timestamp, key, value, element_status))
  db.commit()
  status = SUCCESS
  if fail:
    status = FAILURE
  db.execute("insert into status(timestamp, status) values (?, ?)", (timestamp, status))
  db.commit()
  db.close()
  monitors['datetime'] = date
  open(monitoring_file_json, "w+").write(json.dumps(monitors))

def main():
  parser = OptionParser(option_list=option_list)
  monitors = {}
  (options, args) = parser.parse_args()

  if not (options.monitor or options.promise
         or options.service or options.all):
    exit("Please provide at list one arg in : -a, -m, -p, -s")

  if options.monitor or options.all:
    monitors.update(runScripts(monitor_dir))
  if options.promise or options.all:
    monitors.update(runScripts(promise_dir))
  if options.service or options.all:
    monitors.update(runServices(pid_dir))

  if options.only_stdout:
    print json.dumps(monitors)
  else:
    writeFiles(monitors)
  if len(monitors) == 0:
    exit(0)
  else:
    exit(1)


if __name__ == "__main__":
  main()
