#============================================================================
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#============================================================================
# Copyright (C) 2006 Anthony Liguori <aliguori@xxxxxxxxxx>
# Copyright (C) 2006 XenSource Ltd.
#============================================================================

import xmlrpclib

from xen.xend import XendDomain, XendDomainInfo, XendNode, \
                     XendLogging, XendDmesg
from xen.util.xmlrpclib2 import UnixXMLRPCServer, TCPXMLRPCServer

from xen.xend.XendClient import XML_RPC_SOCKET, ERROR_INVALID_DOMAIN
from xen.xend.XendError import *
from xen.xend.XendLogging import log
from types import ListType

def lookup(domid):
    info = XendDomain.instance().domain_lookup_by_name_or_id(domid)
    if not info:
        raise XendInvalidDomain(str(domid))
    return info

def dispatch(domid, fn, args):
    info = lookup(domid)
    return getattr(info, fn)(*args)

# vcpu_avail is a long and is not needed by the clients.  It's far easier
# to just remove it then to try and marshal the long.
def fixup_sxpr(sexpr):
    ret = []
    for k in sexpr:
        if type(k) is ListType:
            if len(k) != 2 or k[0] != 'vcpu_avail':
                ret.append(fixup_sxpr(k))
        else:
            ret.append(k)
    return ret

def domain(domid):
    info = lookup(domid)
    return fixup_sxpr(info.sxpr())

def domains(detail=1):
    if detail < 1:
        return XendDomain.instance().list_names()
    else:
        domains = XendDomain.instance().list_sorted()
        return map(lambda dom: fixup_sxpr(dom.sxpr()), domains)

def domain_create(config):
    info = XendDomain.instance().domain_create(config)
    return fixup_sxpr(info.sxpr())

def domain_restore(src):
    info = XendDomain.instance().domain_restore(src)
    return fixup_sxpr(info.sxpr())

def get_log():
    f = open(XendLogging.getLogFilename(), 'r')
    try:
        return f.read()
    finally:
        f.close()

methods = ['device_create', 'destroyDevice', 'getDeviceSxprs',
           'setMemoryTarget', 'setName', 'setVCpuCount', 'shutdown',
           'send_sysrq', 'getVCPUInfo', 'waitForDevices']

exclude = ['domain_create', 'domain_restore']

class XMLRPCServer:
    def __init__(self, use_tcp=False):
        self.ready = False
        self.use_tcp = use_tcp
        
    def run(self):
        if self.use_tcp:
            self.server = TCPXMLRPCServer(("", 8005), logRequests=False)
        else:
            self.server = UnixXMLRPCServer(XML_RPC_SOCKET, False)

        def login(username, password):
            log.info("User %s logged in.", username);
            return { "Status" : "Success", "Value" : "SESSION" }

        self.server.register_function(login, 'Session.login_with_password')

        def logout(session):
            log.info("Session %s logged out.", session);
            return { "Status" : "Success", "Value" : "" }

        self.server.register_function(logout, 'Session.logout')

        def get_this_host(session):
            log.info("Get this host %s.", session);
            return { "Status" : "Success", "Value" : "THIS_HOST" }

        self.server.register_function(get_this_host, 'Session.get_this_host')

        def get_software_version(session, host):
            log.info("Get software version %s %s.", session, host);
            return { "Status" : "Success", "Value" : { "Xen": "3.0.2", "Xend" : "3.0.2.2" }}

        self.server.register_function(get_software_version, 'host.get_software_version')

        # Functions in XendDomainInfo
        for name in methods:
            fn = eval("lambda domid, *args: dispatch(domid, '%s', args)"%name)
            self.server.register_function(fn, "xend.domain.%s" % name)

        # Functions in XendDomain
        def vm_get_by_uuid(session, uuid):
            res = XendDomain.instance().domain_lookup_by_name_or_id("Domain-0")
            if res:
                return { 'Status' : 'Success', 'Value' : uuid }
            else:
                return { 'Status' : 'Failure',
                         'ErrorDescription' : [ 'NOSUCHUUID', uuid ] }
        self.server.register_function(vm_get_by_uuid, 'VM.get_by_uuid')

        def vm_get_record(session, uuid):
            res = XendDomain.instance().domain_lookup_by_name_or_id("Domain-0")
            host_uuid = "THIS_HOST"
            if res:
                return { 'Status' : 'Success', 'Value' :
                         { 'uuid' : uuid, 
                           "power_state" : "Running",
                           "name_label" : res.getName(),
                           "name_description" : res.getName(),
                           "user_version" : str(1),
                           "is_a_template" : False,
                           "resident_on" : host_uuid,
                           "memory_static_max" : str(0),
                           "memory_dynamic_max" : str(0),
                           "memory_actual" : str(0),
                           "memory_dynamic_min" : str(0),
                           "memory_static_min" : str(0),
                           "vcpus_policy" : "policy",
                           "vcpus_params" : "params",
                           "vcpus_number" : str(1),
                           "vcpus_utilisation" : { "1" : 1.0, "2" : 1.3 },
                           "vcpus_features_required" : [],
                           "vcpus_features_can_use" : [ "3DNOW" ],
                           "vcpus_features_force_on" : [ "CMOV" ],
                           "vcpus_features_force_off" : [ "MMX" ],
                           "actions_after_shutdown" : "destroy",
                           "actions_after_reboot" : "restart",
                           "actions_after_suspend" : "destroy",
                           "actions_after_crash" : "preserve",
                           "vifs" : [ "VIF1_UUID", "VIF2_UUID" ],
                           "vbds" : [ "VBD1_UUID", "VBD2_UUID" ],
                           "tpm_instance" : "64101201201",
                           "tpm_backend" : "-1",
                           "bios_boot" : "VBD1_UUID",
                           "platform_std_vga" : True,
                           "platform_serial" : "shreddies",
                           "platform_localtime" : False,
                           "platform_clock_offset" : False,
                           "platform_enable_audio" : False,
                           "builder" : "Linux",
                           "boot_method" : "kernel_external",
                           "kernel_kernel" : "vmlinuz",
                           "kernel_initrd" : "initrd.img",
                           "kernel_args" : "nousb",
                           "grub_cmdline" : "",
                           "pci_bus" : "",
                           "tools_version" : { "PV DISK" : "1.3.0" },
                           "otherconfig" : { "PV DISK SPEED" : "FAST" }
                           }
                         }
            else:
                return { 'Status' : 'Failure',
                         'ErrorDescription' : [ 'NOSUCHUUID', uuid ] }
        self.server.register_function(vm_get_record, 'VM.get_record')

        def vm_create(session, struct):
            log.info("VM.create got %s", struct)
            return { 'Status' : 'Success', 'Value' : "NEWVMUUID" }
        self.server.register_function(vm_create, 'VM.create')

        def vdi_create(session, struct):
            log.info("VDI.create got %s", struct)
            return { 'Status' : 'Success', 'Value' : "NEWVDIUUID" }
        self.server.register_function(vdi_create, 'VDI.create')

        def vbd_create(session, struct):
            log.info("VBD.create got %s", struct)
            return { 'Status' : 'Success', 'Value' : "NEWVBDUUID" }
        self.server.register_function(vbd_create, 'VBD.create')

        def sr_get_by_name_label(session, label):
            log.info("SR.get_by_name_label(%s)", label)
            return { 'Status' : 'Success', 'Value' : "SRUUID" }
        self.server.register_function(sr_get_by_name_label, 'SR.get_by_name_label')

        inst = XendDomain.instance()

        for name in dir(inst):
            fn = getattr(inst, name)
            if name.startswith("domain_") and callable(fn):
                if name not in exclude:
                    self.server.register_function(fn, "xend.domain.%s" % name[7:])

        # Functions in XendNode and XendDmesg
        for type, lst, n in [(XendNode, ['info', 'cpu_bvt_slice_set'], 'node'),
                             (XendDmesg, ['info', 'clear'], 'node.dmesg')]:
            inst = type.instance()
            for name in lst:
                self.server.register_function(getattr(inst, name),
                                              "xend.%s.%s" % (n, name))

        # A few special cases
        self.server.register_function(domain, 'xend.domain')
        self.server.register_function(domains, 'xend.domains')
        self.server.register_function(get_log, 'xend.node.log')
        self.server.register_function(domain_create, 'xend.domain.create')
        self.server.register_function(domain_restore, 'xend.domain.restore')

        self.server.register_introspection_functions()
        self.ready = True
        self.server.serve_forever()
