aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorEwan Mellor <ewan@xensource.com>2007-03-17 23:59:05 +0000
committerEwan Mellor <ewan@xensource.com>2007-03-17 23:59:05 +0000
commit61fd72f74805924868e7f6e99c699fa43fd397a9 (patch)
tree3a07ac8f9f0854e0f5347ac9ad156bed95cc2f5b /tools
parentc0767e1cc9a2e1af82ec6d88487c01060ff7d863 (diff)
downloadxen-61fd72f74805924868e7f6e99c699fa43fd397a9.tar.gz
xen-61fd72f74805924868e7f6e99c699fa43fd397a9.tar.bz2
xen-61fd72f74805924868e7f6e99c699fa43fd397a9.zip
Support xm create through the Xen-API.
Signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/python/xen/xend/XendConfig.py105
-rw-r--r--tools/python/xen/xend/XendDomainInfo.py52
-rw-r--r--tools/python/xen/xend/XendNode.py3
-rw-r--r--tools/python/xen/xm/create.dtd118
-rw-r--r--tools/python/xen/xm/create.py82
-rw-r--r--tools/python/xen/xm/new.py13
-rw-r--r--tools/python/xen/xm/xenapi_create.py634
7 files changed, 939 insertions, 68 deletions
diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py
index bdf9370380..36ababeb40 100644
--- a/tools/python/xen/xend/XendConfig.py
+++ b/tools/python/xen/xend/XendConfig.py
@@ -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, '')
@@ -632,13 +673,6 @@ class XendConfig(dict):
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', {})
self['console_refs'] = cfg.get('console_refs', [])
@@ -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 --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py
index 58af5f28ad..78a97b3c45 100644
--- a/tools/python/xen/xend/XendDomainInfo.py
+++ b/tools/python/xen/xend/XendDomainInfo.py
@@ -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
@@ -1899,6 +1924,11 @@ class XendDomainInfo:
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:
to_store['image'] = sxp.to_string(image_sxpr)
diff --git a/tools/python/xen/xend/XendNode.py b/tools/python/xen/xend/XendNode.py
index 954b57a2de..3917c8080c 100644
--- a/tools/python/xen/xend/XendNode.py
+++ b/tools/python/xen/xend/XendNode.py
@@ -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 --git a/tools/python/xen/xm/create.dtd b/tools/python/xen/xm/create.dtd
new file mode 100644
index 0000000000..b544edc5bd
--- /dev/null
+++ b/tools/python/xen/xm/create.dtd
@@ -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 --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py
index 450e3283d1..6a4795d2ed 100644
--- a/tools/python/xen/xm/create.py
+++ b/tools/python/xen/xm/create.py
@@ -28,12 +28,14 @@ import time
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 *
@@ -98,6 +100,11 @@ gopts.opt('dryrun', short='n',
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',
fn=set_true, default=0,
use='Leave the domain paused after it is created.')
@@ -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 --git a/tools/python/xen/xm/new.py b/tools/python/xen/xm/new.py
index 2c6cae84b6..742c7ff8fd 100644
--- a/tools/python/xen/xm/new.py
+++ b/tools/python/xen/xm/new.py
@@ -22,6 +22,9 @@ 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 --git a/tools/python/xen/xm/xenapi_create.py b/tools/python/xen/xm/xenapi_create.py
new file mode 100644
index 0000000000..498dbba4ae
--- /dev/null
+++ b/tools/python/xen/xm/xenapi_create.py
@@ -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@gmail.com>
+#============================================================================
+"""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
+
+
+
+
+