#!/usr/bin/env python

#
# XenAPI plugin for starting the conversion VPX
#

import time
import xmlrpclib

import XenAPI
import XenAPIPlugin

from time import gmtime, strftime

CONVERSION_VM = 'conversionvm'

# How long to wait for a VM to start up and report its IP before giving up.
VM_START_TIMEOUT_SECONDS = 120

def poll_until_true_value(func):
    """Decorator for Xenstore read functions.
    Does the read in a loop until a non-None value is received, or raises ConfigurationError on timeout.
    """
    def decorated(session, vm, device=None):
        starttime = time.time()
        while True:
            value = func(session, vm, device)
            if value:
                return value
            if time.time() - starttime > VM_START_TIMEOUT_SECONDS:
                power = session.xenapi.VM.get_power_state(vm)
                vmid = session.xenapi.VM.get_uuid(vm)
                if power == 'Running':
                    raise Exception("Conversion VM %r started, but did not respond in %d seconds." % (vmid, VM_START_TIMEOUT_SECONDS))
                else:
                    raise Exception("Conversion VM %r failed to start in %d seconds, still in power_state %r." % (vmid, VM_START_TIMEOUT_SECONDS, power))
            time.sleep(1)
    return decorated

def vms_with_records(session):
    expr = 'field "is_a_template" = "false"'
    return session.xenapi.VM.get_all_records_where(expr)

def is_conversion_vm(vmrec):
    """Returns true if the VM is a conversion VM cloned from the conversion VPX template"""
    return not vmrec['is_a_template'] and CONVERSION_VM in vmrec['other_config']

def find_conversion_vm(session):
    for vm_ref, vm_rec in vms_with_records(session).iteritems():
        if is_conversion_vm(vm_rec):
            return vm_ref, vm_rec
    return False, None

@poll_until_true_value
def blocking_read_ip_address(session, vm, _):
    # Reads and returns the IP address of the VM's network interface on device 1 from xenstore,
    # or None if it could not be read.
    # device 0 is the internal network for the VM
    # device 1 is the external network for the VM
    try:
        metrics = session.xenapi.VM.get_guest_metrics(vm)
        networks = session.xenapi.VM_guest_metrics.get_networks(metrics)
        return networks.get('1/ip', None)
    except:
        return None

def is_conversion_service_running(ip):
    host = 'https://'
    host += ip
    try:
        server = xmlrpclib.ServerProxy(host)
        server.svc.get_version()
        return True
    except:
        return False

def poll_until_conversion_service_running(ip):
    starttime = time.time()
    while True:
        value = is_conversion_service_running(ip)
        if value:
            return value
        if time.time() - starttime > VM_START_TIMEOUT_SECONDS:
            raise Exception("Conversion VM started, but the conversion service did not respond in %d seconds." % (VM_START_TIMEOUT_SECONDS))
        time.sleep(1)

def main(session, args):
    vm_ref, vm_rec = find_conversion_vm(session)
    if vm_ref:
        try:
            # Start VM if it is not in the started state
            if vm_rec['power_state'] == 'Halted':
                session.xenapi.VM.start(vm_ref, False, True)
        except:
            pass
        ip = blocking_read_ip_address(session, vm_ref)
	poll_until_conversion_service_running(ip)
	return ip
    else:
        raise Exception("Cannot find Conversion VM on this host!")

if __name__ == "__main__":
    XenAPIPlugin.dispatch({"main":main})
