[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] Support xm create through the Xen-API.
# HG changeset patch # User Ewan Mellor <ewan@xxxxxxxxxxxxx> # Date 1174175945 0 # Node ID 6b2875302558c69873cd63cf1e64d940788a5733 # Parent cfb265b93b2d508a51d058fb19e279bba09a00f1 Support xm create through the Xen-API. Signed-off-by: Tom Wilkie <tom.wilkie@xxxxxxxxx> --- tools/python/xen/xend/XendConfig.py | 105 +++-- tools/python/xen/xend/XendDomainInfo.py | 52 ++ tools/python/xen/xend/XendNode.py | 3 tools/python/xen/xm/create.dtd | 118 +++++ tools/python/xen/xm/create.py | 82 ++-- tools/python/xen/xm/new.py | 13 tools/python/xen/xm/xenapi_create.py | 634 ++++++++++++++++++++++++++++++++ 7 files changed, 939 insertions(+), 68 deletions(-) diff -r cfb265b93b2d -r 6b2875302558 tools/python/xen/xend/XendConfig.py --- a/tools/python/xen/xend/XendConfig.py Sat Mar 17 23:57:17 2007 +0000 +++ b/tools/python/xen/xend/XendConfig.py Sat Mar 17 23:59:05 2007 +0000 @@ -105,8 +105,6 @@ XENAPI_CFG_TO_LEGACY_CFG = { 'uuid': 'uuid', 'vcpus_number': 'vcpus', 'cpus': 'cpus', - 'memory_static_min': 'memory', - 'memory_static_max': 'maxmem', 'name_label': 'name', 'actions_after_shutdown': 'on_poweroff', 'actions_after_reboot': 'on_reboot', @@ -136,11 +134,10 @@ XENAPI_CFG_TYPES = { 'user_version': str, 'is_a_template': bool0, 'resident_on': str, - 'memory_static_min': int, + 'memory_static_min': int, # note these are stored in bytes, not KB! 'memory_static_max': int, 'memory_dynamic_min': int, 'memory_dynamic_max': int, - 'memory_actual': int, 'cpus': list, 'vcpus_policy': str, 'vcpus_params': dict, @@ -314,7 +311,6 @@ class XendConfig(dict): 'shadow_memory': 0, 'memory_static_max': 0, 'memory_dynamic_max': 0, - 'memory_actual': 0, 'devices': {}, 'security': None, 'on_xend_start': 'ignore', @@ -334,20 +330,39 @@ class XendConfig(dict): return defaults + # + # Here we assume these values exist in the dict. + # If they don't we have a bigger problem, lets not + # try and 'fix it up' but acutually fix the cause ;-) + # def _memory_sanity_check(self): - if self['memory_static_min'] == 0: - self['memory_static_min'] = self['memory_dynamic_min'] - - # If the static max is not set, let's set it to dynamic max. - # If the static max is smaller than static min, then fix it! - self['memory_static_max'] = max(self['memory_static_max'], - self['memory_dynamic_max'], - self['memory_static_min']) - - for mem_type in ('memory_static_min', 'memory_static_max'): - if self[mem_type] <= 0: - raise XendConfigError('Memory value too low for %s: %d' % - (mem_type, self[mem_type])) + log.debug("_memory_sanity_check memory_static_min: %s, " + "memory_static_max: %i, " + "memory_dynamic_min: %i, " + "memory_dynamic_max: %i", + self["memory_static_min"], + self["memory_static_max"], + self["memory_dynamic_min"], + self["memory_dynamic_max"]) + + if not self["memory_static_min"] <= self["memory_static_max"]: + raise XendConfigError("memory_static_min must be less " \ + "than or equal to memory_static_max") + if not self["memory_dynamic_min"] <= self["memory_dynamic_max"]: + raise XendConfigError("memory_dynamic_min must be less " \ + "than or equal to memory_dynamic_max") + if not self["memory_static_min"] <= self["memory_dynamic_min"]: + raise XendConfigError("memory_static_min must be less " \ + "than or equal to memory_dynamic_min") + if not self["memory_dynamic_max"] <= self["memory_static_max"]: + raise XendConfigError("memory_dynamic_max must be less " \ + "than or equal to memory_static_max") + if not self["memory_dynamic_max"] > 0: + raise XendConfigError("memory_dynamic_max must be greater " \ + "than zero") + if not self["memory_static_max"] > 0: + raise XendConfigError("memory_static_max must be greater " \ + "than zero") def _actions_sanity_check(self): for event in ['shutdown', 'reboot', 'crash']: @@ -392,8 +407,12 @@ class XendConfig(dict): self['domid'] = dominfo['domid'] self['online_vcpus'] = dominfo['online_vcpus'] self['vcpus_number'] = dominfo['max_vcpu_id'] + 1 - self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024 - self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024 + + self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024 + self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024 + self['memory_static_min'] = 0 + self['memory_static_max'] = dominfo['maxmem_kb'] * 1024 + self['cpu_time'] = dominfo['cpu_time']/1e9 # TODO: i don't know what the security stuff expects here if dominfo.get('ssidref'): @@ -447,6 +466,13 @@ class XendConfig(dict): log.warn('Ignoring unrecognised value for deprecated option:' 'restart = \'%s\'', restart) + # Handle memory, passed in as MiB + + if sxp.child_value(sxp_cfg, "memory") != None: + cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory")) + if sxp.child_value(sxp_cfg, "maxmem") != None: + cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem")) + # Only extract options we know about. extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values() @@ -616,6 +642,21 @@ class XendConfig(dict): except KeyError: pass + # Lets try and handle memory correctly + + MiB = 1024 * 1024 + + if "memory" in cfg: + self["memory_static_min"] = 0 + self["memory_static_max"] = int(cfg["memory"]) * MiB + self["memory_dynamic_min"] = int(cfg["memory"]) * MiB + self["memory_dynamic_max"] = int(cfg["memory"]) * MiB + + if "maxmem" in cfg: + self["memory_static_max"] = int(cfg["maxmem"]) * MiB + + self._memory_sanity_check() + def update_with(n, o): if not self.get(n): self[n] = cfg.get(o, '') @@ -631,13 +672,6 @@ class XendConfig(dict): for key in XENAPI_PLATFORM_CFG: if key in cfg: self['platform'][key] = cfg[key] - - # make sure a sane maximum is set - if self['memory_static_max'] <= 0: - self['memory_static_max'] = self['memory_static_min'] - - self['memory_dynamic_max'] = self['memory_static_max'] - self['memory_dynamic_min'] = self['memory_static_min'] # set device references in the configuration self['devices'] = cfg.get('devices', {}) @@ -812,6 +846,21 @@ class XendConfig(dict): else: sxpr.append([legacy, self[xenapi]]) + MiB = 1024*1024 + + sxpr.append(["maxmem", int(self["memory_static_max"])/MiB]) + sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB]) + + if not legacy_only: + sxpr.append(['memory_dynamic_min', + int(self.get('memory_dynamic_min'))]) + sxpr.append(['memory_dynamic_max', + int(self.get('memory_dynamic_max'))]) + sxpr.append(['memory_static_max', + int(self.get('memory_static_max'))]) + sxpr.append(['memory_static_min', + int(self.get('memory_static_min'))]) + for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG: if legacy in ('domid', 'uuid'): # skip these continue @@ -820,8 +869,6 @@ class XendConfig(dict): sxpr.append(['image', self.image_sxpr()]) sxpr.append(['status', domain.state]) - sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')]) - sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')]) if domain.getDomid() is not None: sxpr.append(['state', self._get_old_state_string()]) diff -r cfb265b93b2d -r 6b2875302558 tools/python/xen/xend/XendDomainInfo.py --- a/tools/python/xen/xend/XendDomainInfo.py Sat Mar 17 23:57:17 2007 +0000 +++ b/tools/python/xen/xend/XendDomainInfo.py Sat Mar 17 23:59:05 2007 +0000 @@ -576,7 +576,7 @@ class XendDomainInfo: if target <= 0: raise XendError('Invalid memory size') - self.info['memory_static_min'] = target + self.info['memory_static_min'] = target * 1024 * 1024 if self.domid >= 0: self.storeVm("memory", target) self.storeDom("memory/target", target << 10) @@ -664,6 +664,10 @@ class XendDomainInfo: if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG: xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg] self.info[xapiarg] = val + elif arg == "memory": + self.info["static_memory_min"] = val + elif arg == "maxmem": + self.info["static_memory_max"] = val else: self.info[arg] = val @@ -780,7 +784,7 @@ class XendDomainInfo: 'vm': self.vmpath, 'name': self.info['name_label'], 'console/limit': str(xoptions.get_console_limit() * 1024), - 'memory/target': str(self.info['memory_static_min'] * 1024), + 'memory/target': str(self.info['memory_dynamic_max'] / 1024), } def f(n, v): @@ -864,7 +868,15 @@ class XendDomainInfo: xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg] if val != None and val != self.info[xapiarg]: self.info[xapiarg] = val - changed= True + changed = True + elif arg == "memory": + if val != None and val != self.info["static_memory_min"]: + self.info["static_memory_min"] = val + changed = True + elif arg == "maxmem": + if val != None and val != self.info["static_memory_max"]: + self.info["static_memory_max"] = val + changed = True # Check whether image definition has been updated image_sxp = self._readVm('image') @@ -969,11 +981,12 @@ class XendDomainInfo: def getMemoryTarget(self): """Get this domain's target memory size, in KB.""" - return self.info['memory_static_min'] * 1024 + return self.info['memory_static_min'] / 1024 def getMemoryMaximum(self): """Get this domain's maximum memory size, in KB.""" - return self.info['memory_static_max'] * 1024 + # remember, info now stores memory in bytes + return self.info['memory_static_max'] / 1024 def getResume(self): return str(self._resume) @@ -1455,13 +1468,14 @@ class XendDomainInfo: # Use architecture- and image-specific calculations to determine # the various headrooms necessary, given the raw configured # values. maxmem, memory, and shadow are all in KiB. + # but memory_static_max etc are all stored in bytes now. memory = self.image.getRequiredAvailableMemory( - self.info['memory_static_min'] * 1024) + self.info['memory_static_min'] / 1024) maxmem = self.image.getRequiredAvailableMemory( - self.info['memory_static_max'] * 1024) + self.info['memory_static_max'] / 1024) shadow = self.image.getRequiredShadowMemory( - self.info['shadow_memory'] * 1024, - self.info['memory_static_max'] * 1024) + self.info['shadow_memory'] / 1024, + self.info['memory_static_max'] / 1024) log.debug("_initDomain:shadow_memory=0x%x, memory_static_max=0x%x, memory_static_min=0x%x.", self.info['shadow_memory'], self.info['memory_static_max'], self.info['memory_static_min'],) # Round shadow up to a multiple of a MiB, as shadow_mem_control @@ -1650,7 +1664,18 @@ class XendDomainInfo: log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.") from xen.xend import XendDomain - XendDomain.instance().remove_domain(self) + + if "transient" in self.info["other_config"]\ + and bool(self.info["other_config"]["transient"]): + xendDomainInstance = XendDomain.instance() + + xendDomainInstance.domains_lock.acquire() + xendDomainInstance._refresh(refresh_shutdown = False) + xendDomainInstance.domains_lock.release() + + xendDomainInstance.domain_delete(self.info["name_label"]) + else: + XendDomain.instance().remove_domain(self) self.cleanupDomain() self._cleanup_phantom_devs(paths) @@ -1833,7 +1858,7 @@ class XendDomainInfo: # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than # the minimum that Xen would allocate if no value were given. overhead_kb = self.info['vcpus_number'] * 1024 + \ - self.info['memory_static_max'] * 4 + (self.info['memory_static_max'] / 1024 / 1024) * 4 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024 # The domain might already have some shadow memory overhead_kb -= xc.shadow_mem_control(self.domid) * 1024 @@ -1898,6 +1923,11 @@ class XendDomainInfo: info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key) if self._infoIsSet(info_key): to_store[key] = str(self.info[info_key]) + + if self._infoIsSet("static_memory_min"): + to_store["memory"] = str(self.info["static_memory_min"]) + if self._infoIsSet("static_memory_max"): + to_store["maxmem"] = str(self.info["static_memory_max"]) image_sxpr = self.info.image_sxpr() if image_sxpr: diff -r cfb265b93b2d -r 6b2875302558 tools/python/xen/xend/XendNode.py --- a/tools/python/xen/xend/XendNode.py Sat Mar 17 23:57:17 2007 +0000 +++ b/tools/python/xen/xend/XendNode.py Sat Mar 17 23:59:05 2007 +0000 @@ -530,7 +530,8 @@ class XendNode: info['cores_per_socket'] * info['threads_per_core']) info['cpu_mhz'] = info['cpu_khz'] / 1000 - # physinfo is in KiB + + # physinfo is in KiB, need it in MiB info['total_memory'] = info['total_memory'] / 1024 info['free_memory'] = info['free_memory'] / 1024 diff -r cfb265b93b2d -r 6b2875302558 tools/python/xen/xm/create.dtd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xm/create.dtd Sat Mar 17 23:59:05 2007 +0000 @@ -0,0 +1,118 @@ +<!ENTITY % HTMLlat1 PUBLIC + "-//W3C//ENTITIES Latin 1 for XHTML//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent"> +%HTMLlat1; +<!ENTITY % HTMLsymbol PUBLIC + "-//W3C//ENTITIES Symbols for XHTML//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent"> +%HTMLsymbol; +<!ENTITY % HTMLspecial PUBLIC + "-//W3C//ENTITIES Special for XHTML//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent"> +%HTMLspecial; +<!-- a Uniform Resource Identifier, see [RFC2396] --> +<!ENTITY % URI "CDATA"> +<!ENTITY % NAMEID "name ID #REQUIRED"> +<!ENTITY % CRASH_BEHAVIOUR "( destroy + | coredump_and_destroy + | restart + | coredump_and_restart + | preserve + | rename_restart )"> +<!ENTITY % NORMAL_EXIT "( destroy | restart )"> +<!ENTITY % VDI_TYPE "( system + | user + | ephemeral + | suspend + | crashdump )"> + +<!ELEMENT xm (vm*, + vdi*)> + +<!ELEMENT version (#PCDATA)> + +<!ELEMENT vm (name, + version, + (pv|hvm), + memory, + vbd*, + vif*, + vcpu_param*, + other_config*)> +<!ATTLIST vm is_a_template CDATA #REQUIRED + auto_power_on CDATA #REQUIRED + vcpus_max CDATA #REQUIRED + vcpus_at_startup CDATA #REQUIRED + actions_after_shutdown %NORMAL_EXIT; #REQUIRED + actions_after_reboot %NORMAL_EXIT; #REQUIRED + actions_after_crash %CRASH_BEHAVIOUR; #REQUIRED + platform_std_VGA CDATA #REQUIRED + platform_serial CDATA #REQUIRED + platform_localtime CDATA #REQUIRED + platform_clock_offet CDATA #REQUIRED + platform_enable_audio CDATA #REQUIRED + PCI_bus CDATA #REQUIRED> + +<!ELEMENT memory EMPTY> +<!ATTLIST memory static_min CDATA #REQUIRED + static_max CDATA #REQUIRED + dynamic_min CDATA #REQUIRED + dynamic_max CDATA #REQUIRED> + +<!ELEMENT vbd (qos_algorithm_param*)> +<!ATTLIST vbd %NAMEID; + mode (RO | RW) #REQUIRED + vdi IDREF #REQUIRED + device CDATA #REQUIRED + bootable CDATA #REQUIRED + type (CD | disk) #REQUIRED + qos_algorithm_type CDATA #REQUIRED> + +<!ELEMENT vif (qos_algorithm_param*)> +<!ATTLIST vif %NAMEID; + mac CDATA #REQUIRED + mtu CDATA #REQUIRED + device CDATA #REQUIRED + qos_algorithm_type CDATA #REQUIRED + bridge CDATA #IMPLIED + network CDATA #IMPLIED> + +<!ELEMENT pv EMPTY> +<!ATTLIST pv kernel CDATA #REQUIRED + bootloader CDATA #REQUIRED + ramdisk CDATA #REQUIRED + args CDATA #REQUIRED + bootloader_args CDATA #REQUIRED> + +<!ELEMENT hvm (boot_param*)> +<!ATTLIST hvm boot_policy CDATA #REQUIRED> + +<!ELEMENT boot_param EMPTY> +<!ATTLIST boot_param key CDATA #REQUIRED + value CDATA #REQUIRED> + +<!ELEMENT vdi (name)> +<!ATTLIST vdi %NAMEID; + src %URI; #REQUIRED + type %VDI_TYPE; #REQUIRED + size CDATA #REQUIRED + shareable CDATA #REQUIRED + read_only CDATA #REQUIRED> + +<!ELEMENT name (label, + description)> + +<!ELEMENT label (#PCDATA)> +<!ELEMENT description (#PCDATA)> + +<!ELEMENT vcpu_param EMPTY> +<!ATTLIST vcpu_param key CDATA #REQUIRED + value CDATA #REQUIRED> + +<!ELEMENT other_config EMPTY> +<!ATTLIST other_config key CDATA #REQUIRED + value CDATA #REQUIRED> + +<!ELEMENT qos_algorithm_param EMPTY> +<!ATTLIST qos_algorithm_param key CDATA #REQUIRED + value CDATA #REQUIRED> diff -r cfb265b93b2d -r 6b2875302558 tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Sat Mar 17 23:57:17 2007 +0000 +++ b/tools/python/xen/xm/create.py Sat Mar 17 23:59:05 2007 +0000 @@ -28,12 +28,14 @@ import xmlrpclib import xmlrpclib from xen.xend import sxp -from xen.xend import PrettyPrint +from xen.xend import PrettyPrint as SXPPrettyPrint from xen.xend import osdep import xen.xend.XendClient from xen.xend.XendBootloader import bootloader from xen.util import blkif from xen.util import security +from xen.xm.main import serverType, SERVER_XEN_API, get_single_vm +from xen.xm.xenapi_create import sxp2xml, xenapi_create from xen.xm.opts import * @@ -96,6 +98,11 @@ gopts.opt('dryrun', short='n', gopts.opt('dryrun', short='n', fn=set_true, default=0, use="Dry run - prints the resulting configuration in SXP but " + "does not create the domain.") + +gopts.opt('xmldryrun', short='x', + fn=set_true, default=0, + use="XML dry run - prints the resulting configuration in XML but " "does not create the domain.") gopts.opt('paused', short='p', @@ -1241,34 +1248,57 @@ def main(argv): except IOError, exn: raise OptionError("Cannot read file %s: %s" % (config, exn[1])) + if serverType == SERVER_XEN_API: + sxp2xml_inst = sxp2xml() + doc = sxp2xml_inst.convert_sxp_to_xml(config, transient=True) + if opts.vals.dryrun: - PrettyPrint.prettyprint(config) + SXPPrettyPrint.prettyprint(config) + + if opts.vals.xmldryrun and serverType == SERVER_XEN_API: + from xml.dom.ext import PrettyPrint as XMLPrettyPrint + XMLPrettyPrint(doc) + + if opts.vals.dryrun or opts.vals.xmldryrun: + return + + if opts.vals.console_autoconnect: + do_console(sxp.child_value(config, 'name', -1)) + + if serverType == SERVER_XEN_API: + xenapi_create_inst = xenapi_create() + vm_refs = xenapi_create_inst.create(document = doc) + + map(lambda vm_ref: server.xenapi.VM.start(vm_ref, 0), vm_refs) else: if not create_security_check(config): - raise security.ACMError('Security Configuration prevents domain from starting') - else: - if opts.vals.console_autoconnect: - cpid = os.fork() - if cpid != 0: - for i in range(10): - # Catch failure of the create process - time.sleep(1) - (p, rv) = os.waitpid(cpid, os.WNOHANG) - if os.WIFEXITED(rv): - if os.WEXITSTATUS(rv) != 0: - sys.exit(os.WEXITSTATUS(rv)) - try: - # Acquire the console of the created dom - name = sxp.child_value(config, 'name', -1) - dom = server.xend.domain(name) - domid = int(sxp.child_value(dom, 'domid', '-1')) - console.execConsole(domid) - except: - pass - print("Could not start console\n"); - sys.exit(0) - dom = make_domain(opts, config) - + raise security.ACMError( + 'Security Configuration prevents domain from starting') + dom = make_domain(opts, config) + +def do_console(domain_name): + cpid = os.fork() + if cpid != 0: + for i in range(10): + # Catch failure of the create process + time.sleep(1) + (p, rv) = os.waitpid(cpid, os.WNOHANG) + if os.WIFEXITED(rv): + if os.WEXITSTATUS(rv) != 0: + sys.exit(os.WEXITSTATUS(rv)) + try: + # Acquire the console of the created dom + if serverType == SERVER_XEN_API: + domid = server.xenapi.VM.get_domid( + get_single_vm(domain_name)) + else: + dom = server.xend.domain(name) + domid = int(sxp.child_value(dom, 'domid', '-1')) + console.execConsole(domid) + except: + pass + print("Could not start console\n"); + sys.exit(0) if __name__ == '__main__': main(sys.argv) diff -r cfb265b93b2d -r 6b2875302558 tools/python/xen/xm/new.py --- a/tools/python/xen/xm/new.py Sat Mar 17 23:57:17 2007 +0000 +++ b/tools/python/xen/xm/new.py Sat Mar 17 23:59:05 2007 +0000 @@ -21,6 +21,9 @@ from xen.xend import PrettyPrint from xen.xend import PrettyPrint from xen.xend import sxp from xen.xend import XendClient + +from xen.xm.main import serverType, SERVER_XEN_API +from xen.xm.xenapi_create import * from opts import * from create import * @@ -65,7 +68,15 @@ def main(argv): if opts.vals.dryrun: PrettyPrint.prettyprint(config) - else: + return + + if serverType == SERVER_XEN_API: + sxp2xml_inst = sxp2xml() + doc = sxp2xml_inst.convert_sxp_to_xml(config) + + xenapi_create_inst = xenapi_create() + vm_refs = xenapi_create_inst.create(document = doc) + else: make_unstarted_domain(opts, config) if __name__ == '__main__': diff -r cfb265b93b2d -r 6b2875302558 tools/python/xen/xm/xenapi_create.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xm/xenapi_create.py Sat Mar 17 23:59:05 2007 +0000 @@ -0,0 +1,634 @@ +#!/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@xxxxxxxxx> +#============================================================================ +"""Domain creation using new XenAPI +""" + +from xen.xm.main import server +from xml.dom.minidom import parse, getDOMImplementation +from xml.dom.ext import PrettyPrint +from xml.parsers.xmlproc import xmlproc, xmlval, xmldtd +from xen.xend import sxp +from xen.xend.XendAPIConstants import XEN_API_ON_NORMAL_EXIT, \ + XEN_API_ON_CRASH_BEHAVIOUR + + +import sys +import os +import traceback + +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 tag_node.nodeValue + +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 = [sr_ref + for sr_ref in server.xenapi.SR.get_all() + if server.xenapi.SR.get_type(sr_ref) == "local"][0] + + self.dtd = "/usr/lib/python/xen/xm/create.dtd" + + def create(self, filename=None, document=None): + """ + Create a domain from an XML file or DOM tree + """ + if filename is not None: + self.check_dtd(file) + document = parse(file) + elif document is not None: + self.check_dom_against_dtd(document) + + self.check_doc(document) + + vdis = document.getElementsByTagName("vdi") + vdi_refs_dict = self.create_vdis(vdis) + + try: + vms = document.getElementsByTagName("vm") + return self.create_vms(vms, vdi_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 + """ + dtd = xmldtd.load_dtd(self.dtd) + parser = xmlproc.XMLProcessor() + parser.set_application(xmlval.ValidatingApp(dtd, parser)) + parser.dtd = dtd + parser.ent = dtd + parser.parse_resource(file) + + def check_dom_against_dtd(self, dom): + """ + Check DOM again DTD. + Doesn't give as nice error messages. + (no location info) + """ + dtd = xmldtd.load_dtd(self.dtd) + app = xmlval.ValidatingApp(dtd, self) + app.set_locator(self) + self.dom2sax(dom, app) + + # Get errors back from ValidatingApp + def report_error(self, number, args=None): + self.errors = xmlproc.errors.english + try: + msg = self.errors[number] + if args != None: + msg = msg % args + except KeyError: + msg = self.errors[4002] % number # Unknown err msg :-) + print msg + sys.exit(-1) + + # Here for compatibility with ValidatingApp + def get_line(self): + return -1 + + def get_column(self): + return -1 + + def dom2sax(self, dom, app): + """ + Take a dom tree and tarverse it, + issuing SAX calls to app. + """ + for child in dom.childNodes: + if child.nodeType == child.TEXT_NODE: + data = child.nodeValue + app.handle_data(data, 0, len(data)) + else: + app.handle_start_tag( + child.nodeName, + self.attrs_to_dict(child.attributes)) + self.dom2sax(child, app) + app.handle_end_tag(child.nodeName) + + def attrs_to_dict(self, attrs): + return dict(attrs.items()) + + # + # 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): + """ + Check that the vif has + either a bridge or network + name but not both + """ + if "bridge" in vif.attributes.keys() \ + and "network" in vif.attributes.keys(): + raise "You cannot specify both a bridge and\ + a network name." + + # 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") + + 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, + "shareable": vdi.attributes["shareable"].value, + "read_only": vdi.attributes["read_only"].value, + "other_config": {"location": + vdi.attributes["src"].value} + } + + key = vdi.attributes["name"].value + value = server.xenapi.VDI.create(vdi_record) + + return (key, value) + + def create_vms(self, vms, vdis): + log(DEBUG, "create_vms") + return map(lambda vm: self.create_vm(vm, vdis), vms) + + def create_vm(self, vm, vdis): + 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, + "auto_power_on": + vm.attributes["auto_power_on"].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_std_VGA": + vm.attributes["platform_std_VGA"].value, + "platform_serial": + vm.attributes["platform_serial"].value, + "platform_localtime": + vm.attributes["platform_localtime"].value, + "platform_clock_offet": + vm.attributes["platform_clock_offet"].value, + "platform_enable_audio": + vm.attributes["platform_enable_audio"].value, + "PCI_bus": + vm.attributes["platform_enable_audio"].value, + "other_config": + get_child_nodes_as_dict(vm, "other_config", "key", "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_params", "key", "value") + }) + try: + vm_ref = server.xenapi.VM.create(vm_record) + except: + traceback.print_exc() + sys.exit(-1) + + # 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) + + return vm_ref + + 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, + "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): + log(DEBUG, "create_vifs") + return map(lambda vif: self.create_vif(vm_ref, vif), vifs) + + def create_vif(self, vm_ref, vif): + log(DEBUG, "create_vif") + + if "bridge" in vif.attributes.keys(): + raise "Not allowed to add by bridge just yet" + elif "network" in vif.attributes.keys(): + network = [network_ref + for network_ref in server.xenapi.network.get_all() + if server.xenapi.network.get_name_label(network_ref) + == vif.attributes["network"].value][0] + else: + network = self._get_network_ref() + + vif_record = { + "device": + vif.attributes["device"].value, + "network": + network, + "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") + } + + 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 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] == "vbd"]) + + vifs_sxp = map(lambda x: x[1], [device for device in devices + if device[1][0] == "vif"]) + # 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): + val.replace("-", "_") + if val not in vals: + raise "Invalid value: " + val + else: + return val + + 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["platform_std_VGA"] = "false" + vm.attributes["platform_serial"] = "" + vm.attributes["platform_localtime"] = "" + vm.attributes["platform_clock_offet"] = "" + vm.attributes["platform_enable_audio"] = "" + 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)) + + # 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("1.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"] = "" + 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"] = "" + + vm.appendChild(pv) + elif image[0] == "hvm": + hvm = document.createElement("hvm") + hvm.attributes["boot_policy"] = "" + + 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"] = memory_str + memory.attributes["static_max"] = memory_str + memory.attributes["dynamic_min"] = memory_str + memory.attributes["dynamic_max"] = memory_str + + 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) + + # 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") + name = str(src.__hash__()) + + vbd = document.createElement("vbd") + + vbd.attributes["name"] = "vdb" + name + vbd.attributes["vdi"] = "vdi" + name + vbd.attributes["mode"] \ + = get_child_by_name(vbd_sxp, "mode") != "w" \ + and "RO" or "RW" + vbd.attributes["device"] \ + = get_child_by_name(vbd_sxp, "dev") + vbd.attributes["bootable"] = "1" + vbd.attributes["type"] = "disk" + vbd.attributes["qos_algorithm_type"] = "" + + return vbd + + def extract_vdi(self, vbd_sxp, document): + src = get_child_by_name(vbd_sxp, "uname") + name = "vdi" + str(src.__hash__()) + path = src[src.find(":")+1:] + + vdi = document.createElement("vdi") + + vdi.attributes["src"] = src + vdi.attributes["read_only"] \ + = (get_child_by_name(vbd_sxp, "mode") != "w") \ + and "true" or "false" + vdi.attributes["size"] \ + = str(os.path.getsize(path)) + vdi.attributes["type"] = "system" + vdi.attributes["shareable"] = "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", "eth0") + + 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"] = "" + + if get_child_by_name(vif_sxp, "bridge") is not None: + vif.attributes["bridge"] \ + = get_child_by_name(vif_sxp, "bridge") + + return vif + + + + + _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |