#!/usr/bin/python
# -*- coding: utf-8 -*-

import re
import time
import datetime
import subprocess
import logging
logging.basicConfig(filename='/var/log/vmPingMonitor.log', format='%(asctime)s: %(message)s', level=logging.DEBUG)
import smtplib
from ConfigParser import SafeConfigParser
parser = SafeConfigParser()
parser.read("/etc/vpscreate/vpscreate.conf")

from ovirtsdk.api import API

URL      = parser.get('vpscreate', 'url')
CACERT   = parser.get('vpscreate', 'cacert')
USERNAME = parser.get('vpscreate', 'username')
PASSWORD = parser.get('vpscreate', 'password')

VMS_LIST = "/root/VMs.list"

api = API(url=URL, ca_file=CACERT, username=USERNAME, password=PASSWORD)

def sendAlertNotification(MESSAGE):
    try:
        logging.debug('%s' % (MESSAGE))
        smtpObj = smtplib.SMTP('smtp.slu.se', 25)
        sender = "storm@slu.se"
        receiver = 'storageadm@slu.se'
        body = """From: oVirt Engine <%s>
To: %s
Subject: vmPingMonitor Error

%s.""" % (sender, receiver, MESSAGE)
        smtpObj.sendmail(sender, receiver, body)
        logging.debug('Successfully sent alert notification.')
    except Exception as e:
        logging.debug('Failed to send alert notification:\n%s' % str(e))

def getVMsFromList(VMsList):
    try:
        logging.debug('Getting list of VMs from file \"%s\".' % (VMsList))
        fo = open(VMsList)
        output = []
        for line in fo:
            strippedline = line.strip('\r\n')
            output.append(strippedline)
        fo.close()
        return output
    except Exception as e:
        sendAlertNotification("Exception in getting list of VMs:\n%s" % str(e))
        exit(1)

def getVMFacts(LIST_VM_NAME):
    try:
        VM = api.vms.get(LIST_VM_NAME)
        try:
            logging.debug('Getting VM \"%s\" Status.' % (LIST_VM_NAME))
            VM_STATUS = VM.get_status().get_state()
            if VM_STATUS == None or VM_STATUS == "":
                sendAlertNotification("Error: Required variable \"VM_STATUS\" is empty!")
                exit(1)
            else:
                if VM_STATUS != 'up':
                    StartVM(LIST_VM_NAME)
                    time.sleep(300)
                    VM = api.vms.get(LIST_VM_NAME)
        except Exception as e:
            sendAlertNotification("Exception in getting VM \"%s\" Status:\n%s" % (LIST_VM_NAME,e))
            exit(1)

        try:
            logging.debug('Getting VM \"%s\" Uptime Hours.' % (LIST_VM_NAME))
            VM_START_TIME_STRIPPED = re.sub('\..*$', '', VM.get_start_time().__str__())
            VM_START_TIME = datetime.datetime.strptime(VM_START_TIME_STRIPPED, '%Y-%m-%d %H:%M:%S')
            VM_UPTIME = abs(VM_START_TIME - datetime.datetime.now())
            VM_UPTIME_SECONDS = VM_UPTIME.days * 86400 + VM_UPTIME.seconds
            VM_UPTIME_HOURS = re.sub('\..*$', '', (VM_UPTIME_SECONDS / 3600.0).__str__())
        except Exception as e:
            sendAlertNotification("Exception in getting VM \"%s\" Uptime Hours:\n%s" % (LIST_VM_NAME,e))
            exit(1)

        try:
            logging.debug('Getting VM \"%s\" IP Address.' % (LIST_VM_NAME))
            IP_ADDRESSES = []
            IPs = VM.get_guest_info().get_ips().get_ip()
            for IP in IPs:
                IP_ADDRESSES.append(IP.get_address())
            IP_ADDRESS = IP_ADDRESSES[0]
        except Exception as e:
            sendAlertNotification("Exception in getting VM \"%s\" IP Address:\n%s" % (LIST_VM_NAME,e))
            exit(1)

        FACTS = [ VM_STATUS, VM_UPTIME_HOURS, IP_ADDRESS ]
        return FACTS
    except Exception as e:
        sendAlertNotification("Exception in getting VM \"%s\" Facts:\n%s" % (LIST_VM_NAME,e))
        exit(1)

def confirmVMResponse(FACTS):
    for FACT in FACTS:
        if FACT == None or FACT == "":
            sendAlertNotification("Error: Required variable is empty!")
            exit(1)
    VM_STATUS = FACTS[0]
    VM_UPTIME_HOURS = FACTS[1]
    IP_ADDRESS = FACTS[2]

    try:
        logging.debug('Confirming VM \"%s\" Response.' % (LIST_VM_NAME))
        if VM_STATUS == "up":
            if VM_UPTIME_HOURS > "0":
                COMMAND = "/bin/ping -W 1 -c 1 %s 2>/dev/null" % (IP_ADDRESS)
                OUTPUT = subprocess.Popen(COMMAND, stdout=subprocess.PIPE, shell=True).communicate()[0].rstrip('\n')
                COUNT = 1
                while not COUNT == 300:
                    if not re.search(", 0% packet loss,", OUTPUT):
                        RESPONSE = "no"
                        COUNT = COUNT + 1
                        OUTPUT = subprocess.Popen(COMMAND, stdout=subprocess.PIPE, shell=True).communicate()[0].rstrip('\n')
                    else:
                        RESPONSE = "yes"
                        break
                return RESPONSE
    except Exception as e:
        sendAlertNotification("Exception in confirming VM \"%s\" Response:\n%s" % (LIST_VM_NAME,e))
        exit(1)

def StopVM(LIST_VM_NAME):
    try:
        if api.vms.get(LIST_VM_NAME).status.state != "down":
            logging.debug("Stopping VM \"%s\"" % (LIST_VM_NAME))
            api.vms.get(LIST_VM_NAME).stop()
            logging.debug("Waiting for VM \"%s\" to reach Down status" % (LIST_VM_NAME))
            status = api.vms.get(LIST_VM_NAME).status.state
            while status != "down":
                time.sleep(1)
                status = api.vms.get(LIST_VM_NAME).status.state
        else:
            logging.debug("VM \"%s\" already Down" % (LIST_VM_NAME))
    except Exception as e:
        sendAlertNotification("Exception in stopping VM \"%s\":\n%s" % (LIST_VM_NAME,e))
        exit(1)

def StartVM(LIST_VM_NAME):
    try:
        if api.vms.get(LIST_VM_NAME).status.state != "up":
            logging.debug("Starting VM \"%s\"" % (LIST_VM_NAME))
            api.vms.get(LIST_VM_NAME).start()
            logging.debug("Waiting for VM \"%s\" to reach Up status" % (LIST_VM_NAME))
            status = api.vms.get(LIST_VM_NAME).status.state
            while status != "up":
                time.sleep(1)
                status = api.vms.get(LIST_VM_NAME).status.state
        else:
            logging.debug("VM \"%s\" already Up" % (LIST_VM_NAME))
    except Exception as e:
        sendAlertNotification("Exception in starting VM \"%s\":\n%s" % (LIST_VM_NAME,e))
        exit(1)

def RestartVM(LIST_VM_NAME):
    try:
        logging.debug("Restarting VM \"%s\"" % (LIST_VM_NAME))
        StopVM(LIST_VM_NAME)
        StartVM(LIST_VM_NAME)
    except Exception as e:
        sendAlertNotification("Exception in restarting VM \"%s\":\n%s" % (LIST_VM_NAME,e))
        exit(1)

def reachVMOverNetwork(LIST_VM_NAME,FACTS):
    if FACTS[2] == None or FACTS[2] == "":
        sendAlertNotification("Error: Required variable \"IP_ADDRESS\" is empty!")
        exit(1)
    IP_ADDRESS = FACTS[2]

    try:
        logging.debug('Pinging VM \"%s\" with IP Address \"%s\".' % (LIST_VM_NAME,IP_ADDRESS))
        COMMAND = "/bin/ping -W 1 -c 1 %s 2>/dev/null" % (IP_ADDRESS)
        OUTPUT = subprocess.Popen(COMMAND, stdout=subprocess.PIPE, shell=True).communicate()[0].rstrip('\n')
        COUNT = 1
        while not COUNT == 300:
            if not re.search(", 0% packet loss,", OUTPUT):
                RESPONSE = "no"
                COUNT = COUNT + 1
                OUTPUT = subprocess.Popen(COMMAND, stdout=subprocess.PIPE, shell=True).communicate()[0].rstrip('\n')
            else:
                RESPONSE = "yes"
                break
        return RESPONSE
    except Exception as e:
        sendAlertNotification("Failed to reach VM \"%s\" over the network:\n%s" % (LIST_VM_NAME,e))
        exit(1)

Facts = []

for LIST_VM_NAME in getVMsFromList(VMS_LIST):
    try:
        FACTS = getVMFacts(LIST_VM_NAME)
        RESPONSE = confirmVMResponse(FACTS)
        if RESPONSE == "no":
            RESPONSE = ""
            logging.debug("VM \"%s\" is not responding. Trying if a restart fixes it." % (LIST_VM_NAME))
            RestartVM(LIST_VM_NAME)
            RESPONSE = reachVMOverNetwork(LIST_VM_NAME,FACTS)
            if RESPONSE == "no":
                sendAlertNotification("The VM \"%s\" doesn´t respond to ping after an attempt was made to restart it, manual intervention required" % (LIST_VM_NAME))
            elif RESPONSE == "yes":
                logging.debug("VM \"%s\" seems OK after a restart." % (LIST_VM_NAME))
            else:
                sendAlertNotification("Cannot get accurate status for VM \"%s\". Breaking..." % (LIST_VM_NAME))
                exit(1)
            RESPONSE = ""
        else:
            logging.debug("VM \"%s\" seems OK." % (LIST_VM_NAME))
        RESPONSE = ""
    except Exception as e:
        sendAlertNotification("Exception in main loop:\n%s" % str(e))
        exit(1)

api.disconnect()

