#!/usr/bin/python
#============================================================================
# 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) 2007 Tom Wilkie <tom.wilkie@gmail.com>
# Copyright (C) 2010 ANEXIA Internetdienstleistungs GmbH
#   Author: Stephan Peijnik <spe@anexia.at>
#============================================================================
"""Domain creation using new XenAPI
"""

from xen.xm.main import server, get_default_SR
from xml.dom.minidom import parse, getDOMImplementation
from lxml import etree
from xen.xend import sxp
from xen.xend.XendAPIConstants import XEN_API_ON_NORMAL_EXIT, \
     XEN_API_ON_CRASH_BEHAVIOUR
from xen.xm.opts import OptionError
from xen.util import xsconstants
from xen.util.pci import pci_opts_list_from_sxp
from xen.util.path import SHAREDIR
import xen.util.xsm.xsm as security

import sys
import os
from os.path import join
import traceback
import re
import warnings # Used by lxml-based validator

def log(_, msg):
    #print "> " + msg
    pass

DEBUG = 0

def get_name_label(node):
    name_node = node.getElementsByTagName("name")[0]
    label_node = name_node.getElementsByTagName("label")[0]
    return " ".join([child.nodeValue for child in label_node.childNodes])

def get_name_description(node):
    name_node = node.getElementsByTagName("name")[0]
    description_node = name_node.getElementsByTagName("description")[0]
    return " ".join([child.nodeValue for child in description_node.childNodes])

def get_text_in_child_node(node, child):
    tag_node = node.getElementsByTagName(child)[0]
    return " ".join([child.nodeValue for child in tag_node.childNodes])

def get_child_node_attribute(node, child, attribute):
    tag_node = node.getElementsByTagName(child)[0]
    return tag_node.attributes[attribute].value

def get_child_nodes_as_dict(node, child_name,
                            key_attribute_name,
                            value_attribute_name):
    return dict([(child.attributes[key_attribute_name].value,
                  child.attributes[value_attribute_name].value)
                 for child in node.getElementsByTagName(child_name)])

def try_quietly(fn, *args):
    try:
        return fn(*args)
    except:
        return None

class xenapi_create:

    def __init__(self):
        self.DEFAULT_STORAGE_REPOSITORY = get_default_SR()

        self.dtd = join(SHAREDIR, "xen", "create.dtd")

    def create(self, filename=None, document=None, skipdtd=False):
        """
        Create a domain from an XML file or DOM tree
        """
        if skipdtd:
            print "Skipping DTD checks.  Dangerous!"
        
        if filename is not None:
            if not skipdtd:
                self.check_dtd(filename)
            document = parse(filename)
        elif document is not None:
            if not skipdtd:
                self.check_dom_against_dtd(document)

        self.check_doc(document)

        vdis = document.getElementsByTagName("vdi")
        vdi_refs_dict = self.create_vdis(vdis)

        networks = document.getElementsByTagName("network")
        network_refs_dict = self.create_networks(networks)
        
        try:    
            vms = document.getElementsByTagName("vm")
            return self.create_vms(vms, vdi_refs_dict, network_refs_dict)
        except Exception, exn:
            try_quietly(self.cleanup_vdis(vdi_refs_dict))
            raise exn

    # Methods to check xml file
    # try to use dtd to check where possible
    def check_dtd(self, file):
        """
        Check file against DTD.
        Use this if possible as it gives nice
        error messages
        """
        try:
            dtd = etree.DTD(open(self.dtd, 'r'))
        except IOError:
            # The old code did neither raise an exception here, nor
            # did it report an error. For now we issue a warning.
            # TODO: How to handle a missing dtd file?
            # --sp
            warnings.warn('DTD file %s not found.' % (self.dtd),
                          UserWarning)
            return
        
        tree = etree.parse(file)
        root = tree.getroot()
        if not dtd.validate(root):
            self.handle_dtd_errors(dtd)
            
    def check_dom_against_dtd(self, dom):
        """
        Check DOM again DTD.
        Doesn't give as nice error messages.
        (no location info)
        """
        try:
            dtd = etree.DTD(open(self.dtd, 'r'))
        except IOError:
            # The old code did neither raise an exception here, nor
            # did it report an error. For now we issue a warning.
            # TODO: How to handle a missing dtd file?
            # --sp
            warnings.warn('DTD file %s not found.' % (self.dtd),
                          UserWarning)
            return

        # XXX: This may be a bit slow. Maybe we should use another way
        # of getting an etree root element from the minidom DOM tree...
        # -- sp
        root = etree.XML(dom.toxml())
        if not dtd.validate(root):
            self.handle_dtd_errors(dtd)

    # Do the same that was done in report_error before. This is directly
    # called by check_dtd and check_dom_against_dtd.
    # We are using sys.stderr instead of print though (python3k clean).
    def handle_dtd_errors(self, dtd):
        # XXX: Do we really want to bail out here?
        # -- sp
        for err in dtd.error_log:
            err_str = 'ERROR: %s\n' % (str(err),)
            sys.stderr.write(err_str)
        sys.stderr.flush()
        sys.exit(-1)

    #
    # Checks which cannot be done with dtd
    #
    def check_doc(self, doc):
        vms = doc.getElementsByTagName("vm")
        self.check_vms(vms)

    def check_vms(self, vms):
        map(self.check_vm, vms)

    def check_vm(self, vm):
        vifs = vm.getElementsByTagName("vif")
        self.check_vifs(vifs)

    def check_vifs(self, vifs):
        map(self.check_vif, vifs)

    def check_vif(self, vif):
        pass

    # Cleanup methods here
    def cleanup_vdis(self, vdi_refs_dict):
        map(self.cleanup_vdi, vdi_refs_dict.values())

    def cleanup_vdi(self, vdi_ref):
        server.xenapi.VDI.destroy(vdi_ref)

    def cleanup_vms(self, vm_refs):
        map(self.cleanup_vm, vm_refs)

    def cleanup_vm(self, vm_ref):
        server.xenapi.VM.destroy(vm_ref)

    # Create methods here
    def create_vdis(self, vdis):
        log(DEBUG, "create_vdis")
        return dict(map(self.create_vdi, vdis))

    def create_vdi(self, vdi):
        log(DEBUG, "create_vdi")

        for ref, record in server.xenapi.VDI.get_all_records().items():
            location = record["other_config"]["location"]
            if vdi.attributes["src"].value != location:
                continue

            # Reuse the VDI because the location is same.
            key = vdi.attributes["name"].value
            return (key, ref)

        # Create a new VDI.
        vdi_record = {
            "name_label":       get_name_label(vdi),
            "name_description": get_name_description(vdi),
            "SR":               self.DEFAULT_STORAGE_REPOSITORY,  
            "virtual_size":     vdi.attributes["size"].value,
            "type":             vdi.attributes["type"].value,
            "sharable":         vdi.attributes["sharable"].value == "True",
            "read_only":        vdi.attributes["read_only"].value == "True",
            "other_config":     {"location":
                vdi.attributes["src"].value}
            }

        key = vdi.attributes["name"].value
        value = server.xenapi.VDI.create(vdi_record)
        
        return (key, value)

    def create_networks(self, networks):
        log(DEBUG, "create_networks")
        return dict(map(self.create_network, networks))

    def create_network(self, network):
        log(DEBUG, "create_network")

        network_record = {
            "name_label":       get_name_label(network),
            "name_description": get_name_description(network),
            "other_config":
                get_child_nodes_as_dict(network, "other_config",
                                        "key", "value"),
            "default_netmask":  network.attributes["default_netmask"].value,
            "default_gateway":  network.attributes["default_gateway"].value
            }

        key = network.attributes["name"].value
        value = server.xenapi.network.create(network_record)

        return (key, value)
        
    def create_vms(self, vms, vdis, networks):
        log(DEBUG, "create_vms")
        return map(lambda vm: self.create_vm(vm, vdis, networks), vms)

    def create_vm(self, vm, vdis, networks):
        log(DEBUG, "create_vm")

        vm_record = {
            "name_label":
                get_name_label(vm),
            "name_description":
                get_name_description(vm),
            "user_version":
                get_text_in_child_node(vm, "version"),
            "is_a_template":
                vm.attributes["is_a_template"].value == 'true',
            "auto_power_on":
                vm.attributes["auto_power_on"].value == 'true',
            "s3_integrity":
                vm.attributes["s3_integrity"].value,
            "superpages":
                vm.attributes["superpages"].value,
            "memory_static_max":
                get_child_node_attribute(vm, "memory", "static_max"),
            "memory_static_min":
                get_child_node_attribute(vm, "memory", "static_min"),
            "memory_dynamic_max":
                get_child_node_attribute(vm, "memory", "dynamic_max"),
            "memory_dynamic_min":
                get_child_node_attribute(vm, "memory", "dynamic_min"),
            "VCPUs_params":
                get_child_nodes_as_dict(vm, "vcpu_param", "key", "value"),
            "VCPUs_max":
                vm.attributes["vcpus_max"].value,
            "VCPUs_at_startup":
                vm.attributes["vcpus_at_startup"].value,
            "actions_after_shutdown":
                vm.attributes["actions_after_shutdown"].value,
            "actions_after_reboot":
                vm.attributes["actions_after_reboot"].value,
            "actions_after_crash":
                vm.attributes["actions_after_crash"].value,
            "platform":
                get_child_nodes_as_dict(vm, "platform", "key", "value"),
            "other_config":
                get_child_nodes_as_dict(vm, "other_config", "key", "value"),
            "pool_name":
                vm.attributes["pool_name"].value,
            "PV_bootloader":
                "",
            "PV_kernel":
                "",
            "PV_ramdisk":
                "",
            "PV_args":
                "",
            "PV_bootloader_args":
                "",
            "HVM_boot_policy":
                "",
            "HVM_boot_params":
                {},
            "PCI_bus":
               ""
            }

        if vm.attributes.has_key("security_label"):
            vm_record.update({
                "security_label":
                    vm.attributes["security_label"].value
                })

        if len(vm.getElementsByTagName("pv")) > 0:
            vm_record.update({
                "PV_bootloader":
                    get_child_node_attribute(vm, "pv", "bootloader"),
                "PV_kernel":
                    get_child_node_attribute(vm, "pv", "kernel"),
                "PV_ramdisk":
                    get_child_node_attribute(vm, "pv", "ramdisk"),
                "PV_args":
                    get_child_node_attribute(vm, "pv", "args"),
                "PV_bootloader_args":
                    get_child_node_attribute(vm, "pv", "bootloader_args")
                })
        else:
            hvm = vm.getElementsByTagName("hvm")[0]
            vm_record.update({
                "HVM_boot_policy":
                    get_child_node_attribute(vm, "hvm", "boot_policy"),
                "HVM_boot_params":
                    get_child_nodes_as_dict(hvm, "boot_param", "key", "value")
                })
        try:
            vm_ref = server.xenapi.VM.create(vm_record)
        except:
            traceback.print_exc()
            sys.exit(-1)

        try:
            # Now create vbds

            vbds = vm.getElementsByTagName("vbd")

            self.create_vbds(vm_ref, vbds, vdis)

            # Now create vifs

            vifs = vm.getElementsByTagName("vif")

            self.create_vifs(vm_ref, vifs, networks)

            # Now create consoles

            consoles = vm.getElementsByTagName("console")

            self.create_consoles(vm_ref, consoles)

            # Now create pcis

            pcis = vm.getElementsByTagName("pci")

            self.create_pcis(vm_ref, pcis)

            # Now create scsis

            scsis = vm.getElementsByTagName("vscsi")

            self.create_scsis(vm_ref, scsis)

            return vm_ref
        except:
            server.xenapi.VM.destroy(vm_ref)
            raise
        
    def create_vbds(self, vm_ref, vbds, vdis):
        log(DEBUG, "create_vbds")
        return map(lambda vbd: self.create_vbd(vm_ref, vbd, vdis), vbds)

    def create_vbd(self, vm_ref, vbd, vdis):
        log(DEBUG, "create_vbd")

        vbd_record = {
            "VM":
                vm_ref,
            "VDI":
                vdis[vbd.attributes["vdi"].value],
            "device":
                vbd.attributes["device"].value,
            "bootable":
                vbd.attributes["bootable"].value == "1",
            "mode":
                vbd.attributes["mode"].value,
            "type":
                vbd.attributes["type"].value,
            "qos_algorithm_type":
                vbd.attributes["qos_algorithm_type"].value,
            "qos_algorithm_params":
                get_child_nodes_as_dict(vbd,
                  "qos_algorithm_param", "key", "value")
            }

        return server.xenapi.VBD.create(vbd_record)

    def create_vifs(self, vm_ref, vifs, networks):
        log(DEBUG, "create_vifs")
        return map(lambda vif: self.create_vif(vm_ref, vif, networks), vifs)

    def create_vif(self, vm_ref, vif, networks):
        log(DEBUG, "create_vif")

        if 'network' in vif.attributes.keys():
            network_name = vif.attributes['network'].value

            if network_name in networks.keys():
                network_uuid = networks[network_name]
            else:
                networks = dict([(record['name_label'], ref)
                                 for ref, record in
                                 server.xenapi.network.get_all_records().items()])
                if network_name in networks.keys():
                    network_uuid = networks[network_name]
                else:
                    raise OptionError("Network %s doesn't exist"
                                  % vif.attributes["network"].value)
        else:
            network_uuid = self._get_network_ref()

        vif_record = {
            "device":
                vif.attributes["device"].value,
            "network":
                network_uuid,
            "VM":
                vm_ref,
            "MAC":
                vif.attributes["mac"].value,
            "MTU":
                vif.attributes["mtu"].value,
            "qos_algorithm_type":
                vif.attributes["qos_algorithm_type"].value,
            "qos_algorithm_params":
                get_child_nodes_as_dict(vif,
                    "qos_algorithm_param", "key", "value"),
            "security_label":
                vif.attributes["security_label"].value
        }

        return server.xenapi.VIF.create(vif_record)

    _network_refs = []

    def _get_network_ref(self):
        try:
            return self._network_refs.pop(0)
        except IndexError:
            self._network_refs = server.xenapi.network.get_all()
            return self._network_refs.pop(0)

    def create_consoles(self, vm_ref, consoles):
        log(DEBUG, "create_consoles")
        return map(lambda console: self.create_console(vm_ref, console),
                   consoles)

    def create_console(self, vm_ref, console):
        log(DEBUG, "create_consoles")

        console_record = {
            "VM":
                vm_ref,
            "protocol":
                console.attributes["protocol"].value,
            "other_config":
                get_child_nodes_as_dict(console,
                  "other_config", "key", "value")
            }

        return server.xenapi.console.create(console_record)

    def create_pcis(self, vm_ref, pcis):
        log(DEBUG, "create_pcis")
        return map(lambda pci: self.create_pci(vm_ref, pci), pcis)

    def create_pci(self, vm_ref, pci):
        log(DEBUG, "create_pci")

        domain = int(pci.attributes["domain"].value, 16)
        bus = int(pci.attributes["bus"].value, 16)
        slot = int(pci.attributes["slot"].value, 16)
        func = int(pci.attributes["func"].value, 16)
        name = "%04x:%02x:%02x.%01x" % (domain, bus, slot, func)

        target_ref = None
        for ppci_ref in server.xenapi.PPCI.get_all():
            if name == server.xenapi.PPCI.get_name(ppci_ref):
                target_ref = ppci_ref
                break
        if target_ref is None:
            log(DEBUG, "create_pci: pci device not found")
            return None

        dpci_record = {
            "VM":
                vm_ref,
            "PPCI":
                target_ref,
            "hotplug_slot":
                int(pci.attributes["vdevfn"].value, 16),
            "options":
                get_child_nodes_as_dict(pci,
                  "pci_opt", "key", "value"),
            "key":
                pci.attributes["key"].value
        }

        return server.xenapi.DPCI.create(dpci_record)

    def create_scsis(self, vm_ref, scsis):
        log(DEBUG, "create_scsis")
        return map(lambda scsi: self.create_scsi(vm_ref, scsi), scsis)

    def create_scsi(self, vm_ref, scsi):
        log(DEBUG, "create_scsi")

        if scsi.attributes["feature-host"].value == "True":
            target_HBA_ref = None
            for pscsi_HBA_ref in server.xenapi.PSCSI_HBA.get_all():
                if int(scsi.attributes["devid"].value) == \
                   int(server.xenapi.PSCSI_HBA.get_physical_host(pscsi_HBA_ref)):
                    target_HBA_ref = pscsi_HBA_ref
                    break
            if target_HBA_ref is None:
                log(DEBUG, "create_scsi: scsi device not found")
                return None

            dscsi_record = {
                "VM":
                    vm_ref,
                "PSCSI_HBA":
                    target_HBA_ref,
                "assignment_mode":
                    "HOST"
            }

            return server.xenapi.DSCSI_HBA.create(dscsi_record)
        else:
            target_ref = None
            for pscsi_ref in server.xenapi.PSCSI.get_all():
                if scsi.attributes["p-dev"].value == \
                   server.xenapi.PSCSI.get_physical_HCTL(pscsi_ref):
                    target_ref = pscsi_ref
                    break
            if target_ref is None:
                log(DEBUG, "create_scsi: scsi device not found")
                return None

            dscsi_record = {
                "VM":
                    vm_ref,
                "PSCSI":
                    target_ref,
                "virtual_HCTL":
                    scsi.attributes["v-dev"].value
            }

            return server.xenapi.DSCSI.create(dscsi_record)

def get_child_by_name(exp, childname, default = None):
    try:
        return [child for child in sxp.children(exp)
                if child[0] == childname][0][1]
    except:
        return default

# Convert old sxp into new xml

class sxp2xml:

    def convert_sxp_to_xml(self, config, transient=False):
       
        devices = [child for child in sxp.children(config)
                   if len(child) > 0 and child[0] == "device"]
                   
        vbds_sxp = map(lambda x: x[1], [device for device in devices
                                        if device[1][0] in ("vbd", "tap", "tap2")])

        vifs_sxp = map(lambda x: x[1], [device for device in devices
                                        if device[1][0] == "vif"])

        vfbs_sxp = map(lambda x: x[1], [device for device in devices
                                        if device[1][0] == "vfb"])

        pcis_sxp = map(lambda x: x[1], [device for device in devices
                                        if device[1][0] == "pci"])

        scsis_sxp = map(lambda x: x[1], [device for device in devices
                                         if device[1][0] == "vscsi"])

        # Create XML Document
        
        impl = getDOMImplementation()

        document = impl.createDocument(None, "xm", None)

        # Lets make the VM tag..

        vm = document.createElement("vm")

        # Some string compatibility

        actions_after_shutdown \
            = get_child_by_name(config, "on_poweroff", "destroy")
        actions_after_reboot \
            = get_child_by_name(config, "on_reboot", "restart")
        actions_after_crash \
            = get_child_by_name(config, "on_crash", "restart")

        def conv_chk(val, vals):
            lval = val.replace("-", "_")
            if lval not in vals:
                raise ValueError("Invalid value: %s" % val)
            else:
                return lval

        actions_after_shutdown = conv_chk(actions_after_shutdown,\
                                          XEN_API_ON_NORMAL_EXIT)
        actions_after_reboot   = conv_chk(actions_after_reboot, \
                                          XEN_API_ON_NORMAL_EXIT)
        actions_after_crash    = conv_chk(actions_after_crash, \
                                          XEN_API_ON_CRASH_BEHAVIOUR)
        # Flesh out tag attributes            

        vm.attributes["is_a_template"] = "false"
        vm.attributes["auto_power_on"] = "false"
        vm.attributes["actions_after_shutdown"] \
            = actions_after_shutdown              
        vm.attributes["actions_after_reboot"] \
            = actions_after_reboot
        vm.attributes["actions_after_crash"] \
            = actions_after_crash
        vm.attributes["PCI_bus"] = ""

        vm.attributes["vcpus_max"] \
            = str(get_child_by_name(config, "vcpus", 1))
        vm.attributes["vcpus_at_startup"] \
            = str(get_child_by_name(config, "vcpus", 1))
        vm.attributes["s3_integrity"] \
            = str(get_child_by_name(config, "s3_integrity", 0))
        vm.attributes["superpages"] \
            = str(get_child_by_name(config, "superpages", 0))
        vm.attributes["pool_name"] \
            = str(get_child_by_name(config, "pool_name", "Pool-0"))

        sec_data = get_child_by_name(config, "security")
        if sec_data:
            try :
                vm.attributes['security_label'] = \
                                    security.set_security_label(sec_data[0][1][1],sec_data[0][2][1])
            except Exception, e:
                raise ValueError("Invalid security data format: %s" % str(sec_data))

        # Make the name tag

        vm.appendChild(self.make_name_tag(
            get_child_by_name(config, "name"), document))

        # Make version tag

        version = document.createElement("version")
        version.appendChild(document.createTextNode("0"))
        vm.appendChild(version)
        
        # Make pv or hvm tag

        image = get_child_by_name(config, "image")

        if image[0] == "linux":
            pv = document.createElement("pv")
            pv.attributes["kernel"] \
                = get_child_by_name(image, "kernel", "")
            pv.attributes["bootloader"] \
                = get_child_by_name(config, "bootloader", "")
            pv.attributes["ramdisk"] \
                = get_child_by_name(image, "ramdisk", "")
            pv.attributes["args"] \
                = "root=" + get_child_by_name(image, "root", "") \
                + " " + get_child_by_name(image, "args", "")
            pv.attributes["bootloader_args"] \
                = get_child_by_name(config, "bootloader_args","")

            vm.appendChild(pv)
        elif image[0] == "hvm":
            hvm = document.createElement("hvm")
            hvm.attributes["boot_policy"] = "BIOS order"

            boot_order = document.createElement("boot_param")
            boot_order.attributes["key"] = "order"
            boot_order.attributes["value"] \
                = get_child_by_name(image, "boot", "abcd")
            hvm.appendChild

            vm.appendChild(hvm)

        # Make memory tag

        memory = document.createElement("memory")

        memory_str = str(int(
            get_child_by_name(config, "memory"))*1024*1024)

        memory.attributes["static_min"] = str(0)
        memory.attributes["static_max"] = memory_str
        memory.attributes["dynamic_min"] = memory_str
        memory.attributes["dynamic_max"] = memory_str

        if get_child_by_name(config, "maxmem"):
            memory.attributes["static_max"] = \
               str(int(get_child_by_name(config, "maxmem")*1024*1024))

        vm.appendChild(memory)

        # And now the vbds

        vbds = map(lambda vbd: self.extract_vbd(vbd, document), vbds_sxp)

        map(vm.appendChild, vbds)

        # And now the vifs

        vifs = map(lambda vif: self.extract_vif(vif, document), vifs_sxp)

        map(vm.appendChild, vifs)

        # And now the pcis

        pcis = self.extract_pcis(pcis_sxp, document)

        map(vm.appendChild, pcis)

        # And now the scsis

        scsis = self.extract_scsis(scsis_sxp, document)

        map(vm.appendChild, scsis)

        # Last but not least the consoles...

        consoles = self.extract_consoles(image, document)

        map(vm.appendChild, consoles)

        vfbs = map(lambda vfb: self.extract_vfb(vfb, document), vfbs_sxp)

        map(vm.appendChild, vfbs)

        # Platform variables...

        platform = self.extract_platform(image, document)

        map(vm.appendChild, platform)

        # And now the vcpu_params

        vcpu_params = self.extract_vcpu_params(config, document)

        map(vm.appendChild, vcpu_params)

        # transient?

        if transient:
            other_config = document.createElement("other_config")
            other_config.attributes["key"] = "transient"
            other_config.attributes["value"] = "True"
            vm.appendChild(other_config)
        
        # Add it to doc_root

        document.documentElement.appendChild(vm)
        
        # We want to pull out vdis

        vdis = map(lambda vdb: self.extract_vdi(vdb, document), vbds_sxp)

        map(document.documentElement.appendChild, vdis)

        return document

    def make_name_tag(self, label_text, document):
        name = document.createElement("name")

        label = document.createElement("label")
        label.appendChild(document.createTextNode(str(label_text)))
        name.appendChild(label)

        description = document.createElement("description")
        description.appendChild(document.createTextNode(" "))
        name.appendChild(description)

        return name

    def extract_vbd(self, vbd_sxp, document):
        src = get_child_by_name(vbd_sxp, "uname")
        mode = get_child_by_name(vbd_sxp, "mode")
        name = str(src.__hash__())

        vbd = document.createElement("vbd")

        vbd.attributes["name"] = "vdb" + name
        vbd.attributes["vdi"] = "vdi" + name
        vbd.attributes["mode"] \
            = re.search("^w!{0,1}$", mode) and "RW" or "RO"
        vbd.attributes["device"] \
            = re.sub(":cdrom$", "", get_child_by_name(vbd_sxp, "dev"))
        vbd.attributes["bootable"] = "1"
        vbd.attributes["type"] \
            = re.search(":cdrom$", get_child_by_name(vbd_sxp, "dev")) \
              and "CD" or "disk"
        vbd.attributes["qos_algorithm_type"] = ""

        return vbd

    def extract_vdi(self, vbd_sxp, document):
        src = get_child_by_name(vbd_sxp, "uname")
        mode = get_child_by_name(vbd_sxp, "mode")
        name = "vdi" + str(src.__hash__())

        vdi = document.createElement("vdi")

        vdi.attributes["src"] = src
        vdi.attributes["read_only"] \
            = re.search("^w!{0,1}$", mode) and "False" or "True"
        vdi.attributes["size"] = '-1'
        vdi.attributes["type"] = "system"
        vdi.attributes["sharable"] \
            = re.search("^w!$", mode) and "True" or "False"
        vdi.attributes["name"] = name

        vdi.appendChild(self.make_name_tag(name, document))

        return vdi

    def extract_vif(self, vif_sxp, document):

        vif = document.createElement("vif")

        dev = get_child_by_name(vif_sxp, "vifname", None)

        if dev is None:
            dev = self.getFreshEthDevice()

        vif.attributes["name"] \
            = "vif" + str(dev.__hash__())
        vif.attributes["mac"] \
            = get_child_by_name(vif_sxp, "mac", "")               
        vif.attributes["mtu"] \
            = get_child_by_name(vif_sxp, "mtu", "")  
        vif.attributes["device"] = dev
        vif.attributes["qos_algorithm_type"] = ""

        policy = get_child_by_name(vif_sxp, "policy")
        label = get_child_by_name(vif_sxp, "label")

        vif.attributes["security_label"] = security.set_security_label(policy, label)

        if get_child_by_name(vif_sxp, "bridge") is not None:
            vif.attributes["network"] \
                = get_child_by_name(vif_sxp, "bridge")
        
        return vif

    def extract_vfb(self, vfb_sxp, document):

        vfb = document.createElement("console")
        vfb.attributes["protocol"] = "rfb"

        if get_child_by_name(vfb_sxp, "type", "") == "vnc":
            vfb.appendChild(self.mk_other_config(
                "type", "vnc",
                document))
            vfb.appendChild(self.mk_other_config(
                "vncunused", get_child_by_name(vfb_sxp, "vncunused", "1"),
                document))
            vfb.appendChild(self.mk_other_config(
                "vnclisten",
                get_child_by_name(vfb_sxp, "vnclisten", "127.0.0.1"),
                document))
            vfb.appendChild(self.mk_other_config(
                "vncdisplay", get_child_by_name(vfb_sxp, "vncdisplay", "0"),
                document))
            vfb.appendChild(self.mk_other_config(
                "vncpasswd", get_child_by_name(vfb_sxp, "vncpasswd", ""),
                document))

        if get_child_by_name(vfb_sxp, "type", "") == "sdl":
            vfb.appendChild(self.mk_other_config(
                "type", "sdl",
                document))
            vfb.appendChild(self.mk_other_config(
                "display", get_child_by_name(vfb_sxp, "display", ""),
                document))
            vfb.appendChild(self.mk_other_config(
                "xauthority",
                get_child_by_name(vfb_sxp, "xauthority", ""),
                document))
            vfb.appendChild(self.mk_other_config(
                "opengl", get_child_by_name(vfb_sxp, "opengl", "1"),
                document))

        return vfb

    def extract_pcis(self, pcis_sxp, document):

        pcis = []

        for pci_sxp in pcis_sxp:
            for dev_sxp in sxp.children(pci_sxp, "dev"):
                pci = document.createElement("pci")

                pci.attributes["domain"] \
                    = get_child_by_name(dev_sxp, "domain", "0")
                pci.attributes["bus"] \
                    = get_child_by_name(dev_sxp, "bus", "0")
                pci.attributes["slot"] \
                    = get_child_by_name(dev_sxp, "slot", "0")
                pci.attributes["func"] \
                    = get_child_by_name(dev_sxp, "func", "0")
                pci.attributes["vdevfn"] \
                    = get_child_by_name(dev_sxp, "vdevfn", "0")
                pci.attributes["key"] \
                    = get_child_by_name(dev_sxp, "key", "0")
                for opt in pci_opts_list_from_sxp(dev_sxp):
                    pci_opt = document.createElement("pci_opt")
                    pci_opt.attributes["key"] = opt[0]
                    pci_opt.attributes["value"] = opt[1]
                    pci.appendChild(pci_opt)

                pcis.append(pci)

        return pcis

    def extract_scsis(self, scsis_sxp, document):

        scsis = []

        for scsi_sxp in scsis_sxp:
            feature_host = sxp.child_value(scsi_sxp, "feature-host")
            for dev_sxp in sxp.children(scsi_sxp, "dev"):
                scsi = document.createElement("vscsi")

                scsi.attributes["feature-host"] \
                    = feature_host and "True" or "False"
                if feature_host:
                    scsi.attributes["devid"] \
                        = str(get_child_by_name(dev_sxp, "devid"))
                    scsis.append(scsi)
                    break
                else:
                    scsi.attributes["p-dev"] \
                        = get_child_by_name(dev_sxp, "p-dev")
                    scsi.attributes["v-dev"] \
                        = get_child_by_name(dev_sxp, "v-dev")
                    scsis.append(scsi)

        return scsis

    def mk_other_config(self, key, value, document):
        other_config = document.createElement("other_config")
        other_config.attributes["key"] = key
        other_config.attributes["value"] = value
        return other_config

    def extract_consoles(self, image, document):
        consoles = []

        if int(get_child_by_name(image, "nographic", "1")) == 1:
            return consoles
        
        if int(get_child_by_name(image, "vnc", "0")) == 1:
            console = document.createElement("console")
            console.attributes["protocol"] = "rfb"
            console.appendChild(self.mk_other_config(
                "type", "vnc",
                document))
            console.appendChild(self.mk_other_config(
                "vncunused", str(get_child_by_name(image, "vncunused", "1")),
                document))
            console.appendChild(self.mk_other_config(
                "vnclisten",
                get_child_by_name(image, "vnclisten", "127.0.0.1"),
                document))
            console.appendChild(self.mk_other_config(
                "vncdisplay", str(get_child_by_name(image, "vncdisplay", "0")),
                document))
            console.appendChild(self.mk_other_config(
                "vncpasswd", get_child_by_name(image, "vncpasswd", ""),
                document))
            consoles.append(console)          
        if int(get_child_by_name(image, "sdl", "0")) == 1:
            console = document.createElement("console")
            console.attributes["protocol"] = "rfb"
            console.appendChild(self.mk_other_config(
                "type", "sdl",
                document))
            console.appendChild(self.mk_other_config(
                "display", get_child_by_name(image, "display", ""),
                document))
            console.appendChild(self.mk_other_config(
                "xauthority", get_child_by_name(image, "xauthority", ""),
                document))
            console.appendChild(self.mk_other_config(
                "opengl", str(get_child_by_name(image, "opengl", "1")),
                document))
            consoles.append(console)
            
        return consoles


    def extract_platform(self, image, document):

        platform_keys = [
            'acpi',
            'apic',
            'boot',
            'device_model',
            'loader',
            'fda',
            'fdb',
            'keymap',
            'isa',
            'localtime',
            'monitor',
            'pae',
            'rtc_timeoffset',
            'serial',
            'soundhw',
            'stdvga',
            'usb',
            'usbdevice',
            'hpet',
            'timer_mode',
            'vpt_align',
            'viridian',
            'vhpt',
            'guest_os_type',
            'hap',
            'oos',
            'pci_msitranslate',
            'pci_power_mgmt',
            'xen_platform_pci',
            'tsc_mode'
            'description',
            'nomigrate'
        ]

        platform_configs = []
        for key in platform_keys:
            value = get_child_by_name(image, key, None)
            if value is not None:
                platform = document.createElement("platform")
                platform.attributes["key"] = key
                platform.attributes["value"] = str(value)
                platform_configs.append(platform)
 
        return platform_configs

    def extract_vcpu_params(self, config, document):
        vcpu_params = []

        vcpu_param = document.createElement("vcpu_param")
        vcpu_param.attributes["key"] = "weight"
        vcpu_param.attributes["value"] \
            = str(get_child_by_name(config, "cpu_weight", 256))
        vcpu_params.append(vcpu_param)

        vcpu_param = document.createElement("vcpu_param")
        vcpu_param.attributes["key"] = "cap"
        vcpu_param.attributes["value"] \
            = str(get_child_by_name(config, "cpu_cap", 0))
        vcpu_params.append(vcpu_param)

        cpus = get_child_by_name(config, "cpus", [])
        if type(cpus) == list:
            vcpu = 0
            for cpu in cpus:
                if cpu:
                    vcpu_param = document.createElement("vcpu_param")
                    vcpu_param.attributes["key"] = "cpumap%i" % vcpu
                    vcpu_param.attributes["value"] = str(cpu)
                    vcpu_params.append(vcpu_param)
                vcpu = vcpu + 1
        else:
            for vcpu in range(0, int(get_child_by_name(config, "vcpus", 1))):
                vcpu_param = document.createElement("vcpu_param")
                vcpu_param.attributes["key"] = "cpumap%i" % vcpu
                vcpu_param.attributes["value"] = str(cpus)
                vcpu_params.append(vcpu_param)

        return vcpu_params
    
    _eths = -1

    def getFreshEthDevice(self):
        self._eths += 1
        return "eth%i" % self._eths
