ia64/xen-unstable

view tools/python/xen/xm/xenapi_create.py @ 14999:2a530128f1dc

[XM] Fix some small bugs in XenAPI create code.

signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
author Tom Wilkie <tom.wilkie@gmail.com>
date Tue May 01 11:40:40 2007 +0100 (2007-05-01)
parents e7704d9b4a48
children 73b6733e4bb1
line source
1 #!/usr/bin/python
2 #============================================================================
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of version 2.1 of the GNU Lesser General Public
5 # License as published by the Free Software Foundation.
6 #
7 # This library is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 # Lesser General Public License for more details.
11 #
12 # You should have received a copy of the GNU Lesser General Public
13 # License along with this library; if not, write to the Free Software
14 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 #============================================================================
16 # Copyright (C) 2007 Tom Wilkie <tom.wilkie@gmail.com>
17 #============================================================================
18 """Domain creation using new XenAPI
19 """
21 from xen.xm.main import server, get_default_SR
22 from xml.dom.minidom import parse, getDOMImplementation
23 from xml.parsers.xmlproc import xmlproc, xmlval, xmldtd
24 from xen.xend import sxp
25 from xen.xend.XendAPIConstants import XEN_API_ON_NORMAL_EXIT, \
26 XEN_API_ON_CRASH_BEHAVIOUR
27 from xen.xm.opts import OptionError
29 import sys
30 import os
31 import traceback
33 def log(_, msg):
34 #print "> " + msg
35 pass
37 DEBUG = 0
39 def get_name_label(node):
40 name_node = node.getElementsByTagName("name")[0]
41 label_node = name_node.getElementsByTagName("label")[0]
42 return " ".join([child.nodeValue for child in label_node.childNodes])
44 def get_name_description(node):
45 name_node = node.getElementsByTagName("name")[0]
46 description_node = name_node.getElementsByTagName("description")[0]
47 return " ".join([child.nodeValue for child in description_node.childNodes])
49 def get_text_in_child_node(node, child):
50 tag_node = node.getElementsByTagName(child)[0]
51 return " ".join([child.nodeValue for child in tag_node.childNodes])
53 def get_child_node_attribute(node, child, attribute):
54 tag_node = node.getElementsByTagName(child)[0]
55 return tag_node.attributes[attribute].value
57 def get_child_nodes_as_dict(node, child_name,
58 key_attribute_name,
59 value_attribute_name):
60 return dict([(child.attributes[key_attribute_name].value,
61 child.attributes[value_attribute_name].value)
62 for child in node.getElementsByTagName(child_name)])
64 def try_quietly(fn, *args):
65 try:
66 return fn(*args)
67 except:
68 return None
70 class xenapi_create:
72 def __init__(self):
73 self.DEFAULT_STORAGE_REPOSITORY = get_default_SR()
75 self.dtd = "/usr/share/xen/create.dtd"
77 def create(self, filename=None, document=None, skipdtd=False):
78 """
79 Create a domain from an XML file or DOM tree
80 """
81 if skipdtd:
82 print "Skipping DTD checks. Dangerous!"
84 if filename is not None:
85 if not skipdtd:
86 self.check_dtd(filename)
87 document = parse(filename)
88 elif document is not None:
89 if not skipdtd:
90 self.check_dom_against_dtd(document)
92 self.check_doc(document)
94 vdis = document.getElementsByTagName("vdi")
95 vdi_refs_dict = self.create_vdis(vdis)
97 networks = document.getElementsByTagName("network")
98 network_refs_dict = self.create_networks(networks)
100 try:
101 vms = document.getElementsByTagName("vm")
102 return self.create_vms(vms, vdi_refs_dict, network_refs_dict)
103 except Exception, exn:
104 try_quietly(self.cleanup_vdis(vdi_refs_dict))
105 raise exn
107 # Methods to check xml file
108 # try to use dtd to check where possible
109 def check_dtd(self, file):
110 """
111 Check file against DTD.
112 Use this if possible as it gives nice
113 error messages
114 """
115 dtd = xmldtd.load_dtd(self.dtd)
116 parser = xmlproc.XMLProcessor()
117 parser.set_application(xmlval.ValidatingApp(dtd, parser))
118 parser.dtd = dtd
119 parser.ent = dtd
120 parser.parse_resource(file)
122 def check_dom_against_dtd(self, dom):
123 """
124 Check DOM again DTD.
125 Doesn't give as nice error messages.
126 (no location info)
127 """
128 dtd = xmldtd.load_dtd(self.dtd)
129 app = xmlval.ValidatingApp(dtd, self)
130 app.set_locator(self)
131 self.dom2sax(dom, app)
133 # Get errors back from ValidatingApp
134 def report_error(self, number, args=None):
135 self.errors = xmlproc.errors.english
136 try:
137 msg = self.errors[number]
138 if args != None:
139 msg = msg % args
140 except KeyError:
141 msg = self.errors[4002] % number # Unknown err msg :-)
142 print msg
143 sys.exit(-1)
145 # Here for compatibility with ValidatingApp
146 def get_line(self):
147 return -1
149 def get_column(self):
150 return -1
152 def dom2sax(self, dom, app):
153 """
154 Take a dom tree and tarverse it,
155 issuing SAX calls to app.
156 """
157 for child in dom.childNodes:
158 if child.nodeType == child.TEXT_NODE:
159 data = child.nodeValue
160 app.handle_data(data, 0, len(data))
161 else:
162 app.handle_start_tag(
163 child.nodeName,
164 self.attrs_to_dict(child.attributes))
165 self.dom2sax(child, app)
166 app.handle_end_tag(child.nodeName)
168 def attrs_to_dict(self, attrs):
169 return dict(attrs.items())
171 #
172 # Checks which cannot be done with dtd
173 #
174 def check_doc(self, doc):
175 vms = doc.getElementsByTagName("vm")
176 self.check_vms(vms)
178 def check_vms(self, vms):
179 map(self.check_vm, vms)
181 def check_vm(self, vm):
182 vifs = vm.getElementsByTagName("vif")
183 self.check_vifs(vifs)
185 def check_vifs(self, vifs):
186 map(self.check_vif, vifs)
188 def check_vif(self, vif):
189 pass
191 # Cleanup methods here
192 def cleanup_vdis(self, vdi_refs_dict):
193 map(self.cleanup_vdi, vdi_refs_dict.values())
195 def cleanup_vdi(self, vdi_ref):
196 server.xenapi.VDI.destroy(vdi_ref)
198 def cleanup_vms(self, vm_refs):
199 map(self.cleanup_vm, vm_refs)
201 def cleanup_vm(self, vm_ref):
202 server.xenapi.VM.destroy(vm_ref)
204 # Create methods here
205 def create_vdis(self, vdis):
206 log(DEBUG, "create_vdis")
207 return dict(map(self.create_vdi, vdis))
209 def create_vdi(self, vdi):
210 log(DEBUG, "create_vdi")
212 vdi_record = {
213 "name_label": get_name_label(vdi),
214 "name_description": get_name_description(vdi),
215 "SR": self.DEFAULT_STORAGE_REPOSITORY,
216 "virtual_size": vdi.attributes["size"].value,
217 "type": vdi.attributes["type"].value,
218 "sharable": bool(vdi.attributes["sharable"].value),
219 "read_only": bool(vdi.attributes["read_only"].value),
220 "other_config": {"location":
221 vdi.attributes["src"].value}
222 }
224 key = vdi.attributes["name"].value
225 value = server.xenapi.VDI.create(vdi_record)
227 return (key, value)
229 def create_networks(self, networks):
230 log(DEBUG, "create_networks")
231 return dict(map(self.create_network, networks))
233 def create_network(self, network):
234 log(DEBUG, "create_network")
236 network_record = {
237 "name_label": get_name_label(network),
238 "name_description": get_name_description(network),
239 "other_config":
240 get_child_nodes_as_dict(network, "other_config",
241 "key", "value"),
242 "default_netmask": network.attributes["default_netmask"].value,
243 "default_gateway": network.attributes["default_gateway"].value
244 }
246 key = network.attributes["name"].value
247 value = server.xenapi.network.create(network_record)
249 return (key, value)
251 def create_vms(self, vms, vdis, networks):
252 log(DEBUG, "create_vms")
253 return map(lambda vm: self.create_vm(vm, vdis, networks), vms)
255 def create_vm(self, vm, vdis, networks):
256 log(DEBUG, "create_vm")
258 vm_record = {
259 "name_label":
260 get_name_label(vm),
261 "name_description":
262 get_name_description(vm),
263 "user_version":
264 get_text_in_child_node(vm, "version"),
265 "is_a_template":
266 vm.attributes["is_a_template"].value == 'true',
267 "auto_power_on":
268 vm.attributes["auto_power_on"].value == 'true',
269 "memory_static_max":
270 get_child_node_attribute(vm, "memory", "static_max"),
271 "memory_static_min":
272 get_child_node_attribute(vm, "memory", "static_min"),
273 "memory_dynamic_max":
274 get_child_node_attribute(vm, "memory", "dynamic_max"),
275 "memory_dynamic_min":
276 get_child_node_attribute(vm, "memory", "dynamic_min"),
277 "VCPUs_params":
278 get_child_nodes_as_dict(vm, "vcpu_param", "key", "value"),
279 "VCPUs_max":
280 vm.attributes["vcpus_max"].value,
281 "VCPUs_at_startup":
282 vm.attributes["vcpus_at_startup"].value,
283 "actions_after_shutdown":
284 vm.attributes["actions_after_shutdown"].value,
285 "actions_after_reboot":
286 vm.attributes["actions_after_reboot"].value,
287 "actions_after_crash":
288 vm.attributes["actions_after_crash"].value,
289 "platform":
290 get_child_nodes_as_dict(vm, "platform", "key", "value"),
291 "other_config":
292 get_child_nodes_as_dict(vm, "other_config", "key", "value"),
293 "PV_bootloader":
294 "",
295 "PV_kernel":
296 "",
297 "PV_ramdisk":
298 "",
299 "PV_args":
300 "",
301 "PV_bootloader_args":
302 "",
303 "HVM_boot_policy":
304 "",
305 "HVM_boot_params":
306 {},
307 "PCI_bus":
308 ""
309 }
311 if len(vm.getElementsByTagName("pv")) > 0:
312 vm_record.update({
313 "PV_bootloader":
314 get_child_node_attribute(vm, "pv", "bootloader"),
315 "PV_kernel":
316 get_child_node_attribute(vm, "pv", "kernel"),
317 "PV_ramdisk":
318 get_child_node_attribute(vm, "pv", "ramdisk"),
319 "PV_args":
320 get_child_node_attribute(vm, "pv", "args"),
321 "PV_bootloader_args":
322 get_child_node_attribute(vm, "pv", "bootloader_args")
323 })
324 else:
325 hvm = vm.getElementsByTagName("hvm")[0]
326 vm_record.update({
327 "HVM_boot_policy":
328 get_child_node_attribute(vm, "hvm", "boot_policy"),
329 "HVM_boot_params":
330 get_child_nodes_as_dict(hvm, "boot_param", "key", "value")
331 })
332 try:
333 vm_ref = server.xenapi.VM.create(vm_record)
334 except:
335 traceback.print_exc()
336 sys.exit(-1)
338 try:
339 # Now create vbds
341 vbds = vm.getElementsByTagName("vbd")
343 self.create_vbds(vm_ref, vbds, vdis)
345 # Now create vifs
347 vifs = vm.getElementsByTagName("vif")
349 self.create_vifs(vm_ref, vifs, networks)
351 # Now create consoles
353 consoles = vm.getElementsByTagName("console")
355 self.create_consoles(vm_ref, consoles)
357 return vm_ref
358 except:
359 server.xenapi.VM.destroy(vm_ref)
360 raise
362 def create_vbds(self, vm_ref, vbds, vdis):
363 log(DEBUG, "create_vbds")
364 return map(lambda vbd: self.create_vbd(vm_ref, vbd, vdis), vbds)
366 def create_vbd(self, vm_ref, vbd, vdis):
367 log(DEBUG, "create_vbd")
369 vbd_record = {
370 "VM":
371 vm_ref,
372 "VDI":
373 vdis[vbd.attributes["vdi"].value],
374 "device":
375 vbd.attributes["device"].value,
376 "bootable":
377 vbd.attributes["bootable"].value == "True",
378 "mode":
379 vbd.attributes["mode"].value,
380 "type":
381 vbd.attributes["type"].value,
382 "qos_algorithm_type":
383 vbd.attributes["qos_algorithm_type"].value,
384 "qos_algorithm_params":
385 get_child_nodes_as_dict(vbd,
386 "qos_algorithm_param", "key", "value")
387 }
389 return server.xenapi.VBD.create(vbd_record)
391 def create_vifs(self, vm_ref, vifs, networks):
392 log(DEBUG, "create_vifs")
393 return map(lambda vif: self.create_vif(vm_ref, vif, networks), vifs)
395 def create_vif(self, vm_ref, vif, networks):
396 log(DEBUG, "create_vif")
398 if 'network' in vif.attributes.keys():
399 network_name = vif.attributes['network'].value
401 if network_name in networks.keys():
402 network_uuid = networks[network_name]
403 else:
404 networks = dict([(record['name_label'], ref)
405 for ref, record in
406 server.xenapi.network.get_all_records().items()])
407 if network_name in networks.keys():
408 network_uuid = networks[network_name]
409 else:
410 raise OptionError("Network %s doesn't exist"
411 % vif.attributes["network"].value)
412 else:
413 network_uuid = self._get_network_ref()
415 vif_record = {
416 "device":
417 vif.attributes["device"].value,
418 "network":
419 network_uuid,
420 "VM":
421 vm_ref,
422 "MAC":
423 vif.attributes["mac"].value,
424 "MTU":
425 vif.attributes["mtu"].value,
426 "qos_algorithm_type":
427 vif.attributes["qos_algorithm_type"].value,
428 "qos_algorithm_params":
429 get_child_nodes_as_dict(vif,
430 "qos_algorithm_param", "key", "value")
431 }
433 return server.xenapi.VIF.create(vif_record)
435 _network_refs = []
437 def _get_network_ref(self):
438 try:
439 return self._network_refs.pop(0)
440 except IndexError:
441 self._network_refs = server.xenapi.network.get_all()
442 return self._network_refs.pop(0)
444 def create_consoles(self, vm_ref, consoles):
445 log(DEBUG, "create_consoles")
446 return map(lambda console: self.create_console(vm_ref, console),
447 consoles)
449 def create_console(self, vm_ref, console):
450 log(DEBUG, "create_consoles")
452 console_record = {
453 "VM":
454 vm_ref,
455 "protocol":
456 console.attributes["protocol"].value,
457 "other_params":
458 get_child_nodes_as_dict(console,
459 "other_param", "key", "value")
460 }
462 return server.xenapi.console.create(console_record)
464 def get_child_by_name(exp, childname, default = None):
465 try:
466 return [child for child in sxp.children(exp)
467 if child[0] == childname][0][1]
468 except:
469 return default
471 # Convert old sxp into new xml
473 class sxp2xml:
475 def convert_sxp_to_xml(self, config, transient=False):
477 devices = [child for child in sxp.children(config)
478 if len(child) > 0 and child[0] == "device"]
480 vbds_sxp = map(lambda x: x[1], [device for device in devices
481 if device[1][0] == "vbd"])
483 vifs_sxp = map(lambda x: x[1], [device for device in devices
484 if device[1][0] == "vif"])
485 # Create XML Document
487 impl = getDOMImplementation()
489 document = impl.createDocument(None, "xm", None)
491 # Lets make the VM tag..
493 vm = document.createElement("vm")
495 # Some string compatibility
497 actions_after_shutdown \
498 = get_child_by_name(config, "on_poweroff", "destroy")
499 actions_after_reboot \
500 = get_child_by_name(config, "on_reboot", "restart")
501 actions_after_crash \
502 = get_child_by_name(config, "on_crash", "restart")
504 def conv_chk(val, vals):
505 val.replace("-", "_")
506 if val not in vals:
507 raise "Invalid value: " + val
508 else:
509 return val
511 actions_after_shutdown = conv_chk(actions_after_shutdown,\
512 XEN_API_ON_NORMAL_EXIT)
513 actions_after_reboot = conv_chk(actions_after_reboot, \
514 XEN_API_ON_NORMAL_EXIT)
515 actions_after_crash = conv_chk(actions_after_crash, \
516 XEN_API_ON_CRASH_BEHAVIOUR)
517 # Flesh out tag attributes
519 vm.attributes["is_a_template"] = "false"
520 vm.attributes["auto_power_on"] = "false"
521 vm.attributes["actions_after_shutdown"] \
522 = actions_after_shutdown
523 vm.attributes["actions_after_reboot"] \
524 = actions_after_reboot
525 vm.attributes["actions_after_crash"] \
526 = actions_after_crash
527 vm.attributes["PCI_bus"] = ""
529 vm.attributes["vcpus_max"] \
530 = str(get_child_by_name(config, "vcpus", 1))
531 vm.attributes["vcpus_at_startup"] \
532 = str(get_child_by_name(config, "vcpus", 1))
534 # Make the name tag
536 vm.appendChild(self.make_name_tag(
537 get_child_by_name(config, "name"), document))
539 # Make version tag
541 version = document.createElement("version")
542 version.appendChild(document.createTextNode("0"))
543 vm.appendChild(version)
545 # Make pv or hvm tag
547 image = get_child_by_name(config, "image")
549 if image[0] == "linux":
550 pv = document.createElement("pv")
551 pv.attributes["kernel"] \
552 = get_child_by_name(image, "kernel", "")
553 pv.attributes["bootloader"] = ""
554 pv.attributes["ramdisk"] \
555 = get_child_by_name(image, "ramdisk", "")
556 pv.attributes["args"] \
557 = "root=" + get_child_by_name(image, "root", "") \
558 + " " + get_child_by_name(image, "args", "")
559 pv.attributes["bootloader_args"] = ""
561 vm.appendChild(pv)
562 elif image[0] == "hvm":
563 hvm = document.createElement("hvm")
564 hvm.attributes["boot_policy"] = "BIOS order"
566 boot_order = document.createElement("boot_param")
567 boot_order.attributes["key"] = "order"
568 boot_order.attributes["value"] \
569 = get_child_by_name(image, "boot", "abcd")
570 hvm.appendChild
572 vm.appendChild(hvm)
574 # Make memory tag
576 memory = document.createElement("memory")
578 memory_str = str(int(
579 get_child_by_name(config, "memory"))*1024*1024)
581 memory.attributes["static_min"] = str(0)
582 memory.attributes["static_max"] = memory_str
583 memory.attributes["dynamic_min"] = memory_str
584 memory.attributes["dynamic_max"] = memory_str
586 if get_child_by_name(config, "maxmem"):
587 memory.attributes["static_max"] = \
588 str(int(get_child_by_name(config, "maxmem")*1024*1024))
590 vm.appendChild(memory)
592 # And now the vbds
594 vbds = map(lambda vbd: self.extract_vbd(vbd, document), vbds_sxp)
596 map(vm.appendChild, vbds)
598 # And now the vifs
600 vifs = map(lambda vif: self.extract_vif(vif, document), vifs_sxp)
602 map(vm.appendChild, vifs)
604 # Last but not least the consoles...
606 consoles = self.extract_consoles(image, document)
608 map(vm.appendChild, consoles)
610 # Platform variables...
612 platform = self.extract_platform(image, document)
614 map(vm.appendChild, platform)
616 # transient?
618 if transient:
619 other_config = document.createElement("other_config")
620 other_config.attributes["key"] = "transient"
621 other_config.attributes["value"] = "True"
622 vm.appendChild(other_config)
624 # Add it to doc_root
626 document.documentElement.appendChild(vm)
628 # We want to pull out vdis
630 vdis = map(lambda vdb: self.extract_vdi(vdb, document), vbds_sxp)
632 map(document.documentElement.appendChild, vdis)
634 return document
636 def make_name_tag(self, label_text, document):
637 name = document.createElement("name")
639 label = document.createElement("label")
640 label.appendChild(document.createTextNode(str(label_text)))
641 name.appendChild(label)
643 description = document.createElement("description")
644 description.appendChild(document.createTextNode(" "))
645 name.appendChild(description)
647 return name
649 def extract_vbd(self, vbd_sxp, document):
650 src = get_child_by_name(vbd_sxp, "uname")
651 name = str(src.__hash__())
653 vbd = document.createElement("vbd")
655 vbd.attributes["name"] = "vdb" + name
656 vbd.attributes["vdi"] = "vdi" + name
657 vbd.attributes["mode"] \
658 = get_child_by_name(vbd_sxp, "mode") != "w" \
659 and "RO" or "RW"
660 vbd.attributes["device"] \
661 = get_child_by_name(vbd_sxp, "dev")
662 vbd.attributes["bootable"] = "1"
663 vbd.attributes["type"] = "disk"
664 vbd.attributes["qos_algorithm_type"] = ""
666 return vbd
668 def extract_vdi(self, vbd_sxp, document):
669 src = get_child_by_name(vbd_sxp, "uname")
670 name = "vdi" + str(src.__hash__())
672 vdi = document.createElement("vdi")
674 vdi.attributes["src"] = src
675 vdi.attributes["read_only"] \
676 = (get_child_by_name(vbd_sxp, "mode") != "w") \
677 and "True" or "False"
678 vdi.attributes["size"] = '-1'
679 vdi.attributes["type"] = "system"
680 vdi.attributes["sharable"] = "False"
681 vdi.attributes["name"] = name
683 vdi.appendChild(self.make_name_tag(name, document))
685 return vdi
687 def extract_vif(self, vif_sxp, document):
689 vif = document.createElement("vif")
691 dev = get_child_by_name(vif_sxp, "vifname", None)
693 if dev is None:
694 dev = self.getFreshEthDevice()
696 vif.attributes["name"] \
697 = "vif" + str(dev.__hash__())
698 vif.attributes["mac"] \
699 = get_child_by_name(vif_sxp, "mac", "")
700 vif.attributes["mtu"] \
701 = get_child_by_name(vif_sxp, "mtu", "")
702 vif.attributes["device"] = dev
703 vif.attributes["qos_algorithm_type"] = ""
705 if get_child_by_name(vif_sxp, "bridge") is not None:
706 vif.attributes["network"] \
707 = get_child_by_name(vif_sxp, "bridge")
709 return vif
711 _eths = -1
713 def mk_other_config(self, key, value, document):
714 other_config = document.createElement("other_config")
715 other_config.attributes["key"] = key
716 other_config.attributes["value"] = value
717 return other_config
719 def extract_consoles(self, image, document):
720 consoles = []
722 if int(get_child_by_name(image, "nographic", "1")) == 1:
723 return consoles
725 if int(get_child_by_name(image, "vnc", "0")) == 1:
726 console = document.createElement("console")
727 console.attributes["protocol"] = "rfb"
728 console.appendChild(self.mk_other_config(
729 "vncunused", str(get_child_by_name(image, "vncunused", "0")),
730 document))
731 console.appendChild(self.mk_other_config(
732 "vnclisten",
733 get_child_by_name(image, "vnclisten", "127.0.0.1"),
734 document))
735 console.appendChild(self.mk_other_config(
736 "vncpasswd", get_child_by_name(image, "vncpasswd", ""),
737 document))
738 consoles.append(console)
739 if int(get_child_by_name(image, "sdl", "0")) == 1:
740 console = document.createElement("console")
741 console.attributes["protocol"] = "sdl"
742 console.appendChild(self.mk_other_config(
743 "display", get_child_by_name(image, "display", ""),
744 document))
745 console.appendChild(self.mk_other_config(
746 "xauthority",
747 get_child_by_name(image, "vxauthority", "127.0.0.1"),
748 document))
749 console.appendChild(self.mk_other_config(
750 "vncpasswd", get_child_by_name(image, "vncpasswd", ""),
751 document))
752 consoles.append(console)
754 return consoles
757 def extract_platform(self, image, document):
758 platform_keys = ['acpi', 'apic', 'pae']
760 def extract_platform_key(key):
761 platform = document.createElement("platform")
762 platform.attributes["key"] = key
763 platform.attributes["value"] \
764 = str(get_child_by_name(image, key, "1"))
765 return platform
767 return map(extract_platform_key, platform_keys)
769 def getFreshEthDevice(self):
770 self._eths += 1
771 return "eth%i" % self._eths