direct-io.hg

view tools/python/xen/xm/xenapi_create.py @ 14449:e2f302488983

Add VBD.runtime_properties dictionary, and use that to implement xm block-list
through the Xen-API. Implement xm block-attach and xm block-detach also.

Signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Mar 20 11:45:44 2007 +0000 (2007-03-20)
parents 6b2875302558
children fc918d15c981
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.dom.ext import PrettyPrint
24 from xml.parsers.xmlproc import xmlproc, xmlval, xmldtd
25 from xen.xend import sxp
26 from xen.xend.XendAPIConstants import XEN_API_ON_NORMAL_EXIT, \
27 XEN_API_ON_CRASH_BEHAVIOUR
30 import sys
31 import os
32 import traceback
34 def log(_, msg):
35 #print "> " + msg
36 pass
38 DEBUG = 0
40 def get_name_label(node):
41 name_node = node.getElementsByTagName("name")[0]
42 label_node = name_node.getElementsByTagName("label")[0]
43 return " ".join([child.nodeValue for child in label_node.childNodes])
45 def get_name_description(node):
46 name_node = node.getElementsByTagName("name")[0]
47 description_node = name_node.getElementsByTagName("description")[0]
48 return " ".join([child.nodeValue for child in description_node.childNodes])
50 def get_text_in_child_node(node, child):
51 tag_node = node.getElementsByTagName(child)[0]
52 return tag_node.nodeValue
54 def get_child_node_attribute(node, child, attribute):
55 tag_node = node.getElementsByTagName(child)[0]
56 return tag_node.attributes[attribute].value
58 def get_child_nodes_as_dict(node, child_name,
59 key_attribute_name,
60 value_attribute_name):
61 return dict([(child.attributes[key_attribute_name].value,
62 child.attributes[value_attribute_name].value)
63 for child in node.getElementsByTagName(child_name)])
65 def try_quietly(fn, *args):
66 try:
67 return fn(*args)
68 except:
69 return None
71 class xenapi_create:
73 def __init__(self):
74 self.DEFAULT_STORAGE_REPOSITORY = get_default_SR()
76 self.dtd = "/usr/lib/python/xen/xm/create.dtd"
78 def create(self, filename=None, document=None):
79 """
80 Create a domain from an XML file or DOM tree
81 """
82 if filename is not None:
83 self.check_dtd(file)
84 document = parse(file)
85 elif document is not None:
86 self.check_dom_against_dtd(document)
88 self.check_doc(document)
90 vdis = document.getElementsByTagName("vdi")
91 vdi_refs_dict = self.create_vdis(vdis)
93 try:
94 vms = document.getElementsByTagName("vm")
95 return self.create_vms(vms, vdi_refs_dict)
96 except Exception, exn:
97 try_quietly(self.cleanup_vdis(vdi_refs_dict))
98 raise exn
100 # Methods to check xml file
101 # try to use dtd to check where possible
102 def check_dtd(self, file):
103 """
104 Check file against DTD.
105 Use this if possible as it gives nice
106 error messages
107 """
108 dtd = xmldtd.load_dtd(self.dtd)
109 parser = xmlproc.XMLProcessor()
110 parser.set_application(xmlval.ValidatingApp(dtd, parser))
111 parser.dtd = dtd
112 parser.ent = dtd
113 parser.parse_resource(file)
115 def check_dom_against_dtd(self, dom):
116 """
117 Check DOM again DTD.
118 Doesn't give as nice error messages.
119 (no location info)
120 """
121 dtd = xmldtd.load_dtd(self.dtd)
122 app = xmlval.ValidatingApp(dtd, self)
123 app.set_locator(self)
124 self.dom2sax(dom, app)
126 # Get errors back from ValidatingApp
127 def report_error(self, number, args=None):
128 self.errors = xmlproc.errors.english
129 try:
130 msg = self.errors[number]
131 if args != None:
132 msg = msg % args
133 except KeyError:
134 msg = self.errors[4002] % number # Unknown err msg :-)
135 print msg
136 sys.exit(-1)
138 # Here for compatibility with ValidatingApp
139 def get_line(self):
140 return -1
142 def get_column(self):
143 return -1
145 def dom2sax(self, dom, app):
146 """
147 Take a dom tree and tarverse it,
148 issuing SAX calls to app.
149 """
150 for child in dom.childNodes:
151 if child.nodeType == child.TEXT_NODE:
152 data = child.nodeValue
153 app.handle_data(data, 0, len(data))
154 else:
155 app.handle_start_tag(
156 child.nodeName,
157 self.attrs_to_dict(child.attributes))
158 self.dom2sax(child, app)
159 app.handle_end_tag(child.nodeName)
161 def attrs_to_dict(self, attrs):
162 return dict(attrs.items())
164 #
165 # Checks which cannot be done with dtd
166 #
167 def check_doc(self, doc):
168 vms = doc.getElementsByTagName("vm")
169 self.check_vms(vms)
171 def check_vms(self, vms):
172 map(self.check_vm, vms)
174 def check_vm(self, vm):
175 vifs = vm.getElementsByTagName("vif")
176 self.check_vifs(vifs)
178 def check_vifs(self, vifs):
179 map(self.check_vif, vifs)
181 def check_vif(self, vif):
182 """
183 Check that the vif has
184 either a bridge or network
185 name but not both
186 """
187 if "bridge" in vif.attributes.keys() \
188 and "network" in vif.attributes.keys():
189 raise "You cannot specify both a bridge and\
190 a network name."
192 # Cleanup methods here
193 def cleanup_vdis(self, vdi_refs_dict):
194 map(self.cleanup_vdi, vdi_refs_dict.values())
196 def cleanup_vdi(self, vdi_ref):
197 server.xenapi.VDI.destroy(vdi_ref)
199 def cleanup_vms(self, vm_refs):
200 map(self.cleanup_vm, vm_refs)
202 def cleanup_vm(self, vm_ref):
203 server.xenapi.VM.destroy(vm_ref)
205 # Create methods here
206 def create_vdis(self, vdis):
207 log(DEBUG, "create_vdis")
208 return dict(map(self.create_vdi, vdis))
210 def create_vdi(self, vdi):
211 log(DEBUG, "create_vdi")
213 vdi_record = {
214 "name_label": get_name_label(vdi),
215 "name_description": get_name_description(vdi),
216 "SR": self.DEFAULT_STORAGE_REPOSITORY,
217 "virtual_size": vdi.attributes["size"].value,
218 "type": vdi.attributes["type"].value,
219 "shareable": vdi.attributes["shareable"].value,
220 "read_only": vdi.attributes["read_only"].value,
221 "other_config": {"location":
222 vdi.attributes["src"].value}
223 }
225 key = vdi.attributes["name"].value
226 value = server.xenapi.VDI.create(vdi_record)
228 return (key, value)
230 def create_vms(self, vms, vdis):
231 log(DEBUG, "create_vms")
232 return map(lambda vm: self.create_vm(vm, vdis), vms)
234 def create_vm(self, vm, vdis):
235 log(DEBUG, "create_vm")
237 vm_record = {
238 "name_label":
239 get_name_label(vm),
240 "name_description":
241 get_name_description(vm),
242 "user_version":
243 get_text_in_child_node(vm, "version"),
244 "is_a_template":
245 vm.attributes["is_a_template"].value,
246 "auto_power_on":
247 vm.attributes["auto_power_on"].value,
248 "memory_static_max":
249 get_child_node_attribute(vm, "memory", "static_max"),
250 "memory_static_min":
251 get_child_node_attribute(vm, "memory", "static_min"),
252 "memory_dynamic_max":
253 get_child_node_attribute(vm, "memory", "dynamic_max"),
254 "memory_dynamic_min":
255 get_child_node_attribute(vm, "memory", "dynamic_min"),
256 "vcpus_params":
257 get_child_nodes_as_dict(vm, "vcpu_param", "key", "value"),
258 "vcpus_max":
259 vm.attributes["vcpus_max"].value,
260 "vcpus_at_startup":
261 vm.attributes["vcpus_at_startup"].value,
262 "actions_after_shutdown":
263 vm.attributes["actions_after_shutdown"].value,
264 "actions_after_reboot":
265 vm.attributes["actions_after_reboot"].value,
266 "actions_after_crash":
267 vm.attributes["actions_after_crash"].value,
268 "platform_std_VGA":
269 vm.attributes["platform_std_VGA"].value,
270 "platform_serial":
271 vm.attributes["platform_serial"].value,
272 "platform_localtime":
273 vm.attributes["platform_localtime"].value,
274 "platform_clock_offet":
275 vm.attributes["platform_clock_offet"].value,
276 "platform_enable_audio":
277 vm.attributes["platform_enable_audio"].value,
278 "PCI_bus":
279 vm.attributes["platform_enable_audio"].value,
280 "other_config":
281 get_child_nodes_as_dict(vm, "other_config", "key", "value")
282 }
284 if len(vm.getElementsByTagName("pv")) > 0:
285 vm_record.update({
286 "PV_bootloader":
287 get_child_node_attribute(vm, "pv", "bootloader"),
288 "PV_kernel":
289 get_child_node_attribute(vm, "pv", "kernel"),
290 "PV_ramdisk":
291 get_child_node_attribute(vm, "pv", "ramdisk"),
292 "PV_args":
293 get_child_node_attribute(vm, "pv", "args"),
294 "PV_bootloader_args":
295 get_child_node_attribute(vm, "pv", "bootloader_args")
296 })
297 else:
298 hvm = vm.getElementsByTagName("hvm")[0]
299 vm_record.update({
300 "HVM_boot_policy":
301 get_child_node_attribute(vm, "hvm", "boot_policy"),
302 "HVM_boot_params":
303 get_child_nodes_as_dict(hvm, "boot_params", "key", "value")
304 })
305 try:
306 vm_ref = server.xenapi.VM.create(vm_record)
307 except:
308 traceback.print_exc()
309 sys.exit(-1)
311 # Now create vbds
313 vbds = vm.getElementsByTagName("vbd")
315 self.create_vbds(vm_ref, vbds, vdis)
317 # Now create vifs
319 vifs = vm.getElementsByTagName("vif")
321 self.create_vifs(vm_ref, vifs)
323 return vm_ref
325 def create_vbds(self, vm_ref, vbds, vdis):
326 log(DEBUG, "create_vbds")
327 return map(lambda vbd: self.create_vbd(vm_ref, vbd, vdis), vbds)
329 def create_vbd(self, vm_ref, vbd, vdis):
330 log(DEBUG, "create_vbd")
332 vbd_record = {
333 "VM":
334 vm_ref,
335 "VDI":
336 vdis[vbd.attributes["vdi"].value],
337 "device":
338 vbd.attributes["device"].value,
339 "bootable":
340 vbd.attributes["bootable"].value,
341 "mode":
342 vbd.attributes["mode"].value,
343 "type":
344 vbd.attributes["type"].value,
345 "qos_algorithm_type":
346 vbd.attributes["qos_algorithm_type"].value,
347 "qos_algorithm_params":
348 get_child_nodes_as_dict(vbd,
349 "qos_algorithm_param", "key", "value")
350 }
352 return server.xenapi.VBD.create(vbd_record)
354 def create_vifs(self, vm_ref, vifs):
355 log(DEBUG, "create_vifs")
356 return map(lambda vif: self.create_vif(vm_ref, vif), vifs)
358 def create_vif(self, vm_ref, vif):
359 log(DEBUG, "create_vif")
361 if "bridge" in vif.attributes.keys():
362 raise "Not allowed to add by bridge just yet"
363 elif "network" in vif.attributes.keys():
364 network = [network_ref
365 for network_ref in server.xenapi.network.get_all()
366 if server.xenapi.network.get_name_label(network_ref)
367 == vif.attributes["network"].value][0]
368 else:
369 network = self._get_network_ref()
371 vif_record = {
372 "device":
373 vif.attributes["device"].value,
374 "network":
375 network,
376 "VM":
377 vm_ref,
378 "MAC":
379 vif.attributes["mac"].value,
380 "MTU":
381 vif.attributes["mtu"].value,
382 "qos_algorithm_type":
383 vif.attributes["qos_algorithm_type"].value,
384 "qos_algorithm_params":
385 get_child_nodes_as_dict(vif,
386 "qos_algorithm_param", "key", "value")
387 }
389 return server.xenapi.VIF.create(vif_record)
391 _network_refs = []
393 def _get_network_ref(self):
394 try:
395 return self._network_refs.pop(0)
396 except IndexError:
397 self._network_refs = server.xenapi.network.get_all()
398 return self._network_refs.pop(0)
400 def get_child_by_name(exp, childname, default = None):
401 try:
402 return [child for child in sxp.children(exp)
403 if child[0] == childname][0][1]
404 except:
405 return default
407 # Convert old sxp into new xml
409 class sxp2xml:
411 def convert_sxp_to_xml(self, config, transient=False):
413 devices = [child for child in sxp.children(config)
414 if len(child) > 0 and child[0] == "device"]
416 vbds_sxp = map(lambda x: x[1], [device for device in devices
417 if device[1][0] == "vbd"])
419 vifs_sxp = map(lambda x: x[1], [device for device in devices
420 if device[1][0] == "vif"])
421 # Create XML Document
423 impl = getDOMImplementation()
425 document = impl.createDocument(None, "xm", None)
427 # Lets make the VM tag..
429 vm = document.createElement("vm")
431 # Some string compatibility
433 actions_after_shutdown \
434 = get_child_by_name(config, "on_poweroff", "destroy")
435 actions_after_reboot \
436 = get_child_by_name(config, "on_reboot", "restart")
437 actions_after_crash \
438 = get_child_by_name(config, "on_crash", "restart")
440 def conv_chk(val, vals):
441 val.replace("-", "_")
442 if val not in vals:
443 raise "Invalid value: " + val
444 else:
445 return val
447 actions_after_shutdown = conv_chk(actions_after_shutdown,\
448 XEN_API_ON_NORMAL_EXIT)
449 actions_after_reboot = conv_chk(actions_after_reboot, \
450 XEN_API_ON_NORMAL_EXIT)
451 actions_after_crash = conv_chk(actions_after_crash, \
452 XEN_API_ON_CRASH_BEHAVIOUR)
453 # Flesh out tag attributes
455 vm.attributes["is_a_template"] = "false"
456 vm.attributes["auto_power_on"] = "false"
457 vm.attributes["actions_after_shutdown"] \
458 = actions_after_shutdown
459 vm.attributes["actions_after_reboot"] \
460 = actions_after_reboot
461 vm.attributes["actions_after_crash"] \
462 = actions_after_crash
463 vm.attributes["platform_std_VGA"] = "false"
464 vm.attributes["platform_serial"] = ""
465 vm.attributes["platform_localtime"] = ""
466 vm.attributes["platform_clock_offet"] = ""
467 vm.attributes["platform_enable_audio"] = ""
468 vm.attributes["PCI_bus"] = ""
470 vm.attributes["vcpus_max"] \
471 = str(get_child_by_name(config, "vcpus", 1))
472 vm.attributes["vcpus_at_startup"] \
473 = str(get_child_by_name(config, "vcpus", 1))
475 # Make the name tag
477 vm.appendChild(self.make_name_tag(
478 get_child_by_name(config, "name"), document))
480 # Make version tag
482 version = document.createElement("version")
483 version.appendChild(document.createTextNode("1.0"))
484 vm.appendChild(version)
486 # Make pv or hvm tag
488 image = get_child_by_name(config, "image")
490 if image[0] == "linux":
491 pv = document.createElement("pv")
492 pv.attributes["kernel"] \
493 = get_child_by_name(image, "kernel", "")
494 pv.attributes["bootloader"] = ""
495 pv.attributes["ramdisk"] \
496 = get_child_by_name(image, "ramdisk", "")
497 pv.attributes["args"] \
498 = "root=" + get_child_by_name(image, "root", "") \
499 + " " + get_child_by_name(image, "args", "")
500 pv.attributes["bootloader_args"] = ""
502 vm.appendChild(pv)
503 elif image[0] == "hvm":
504 hvm = document.createElement("hvm")
505 hvm.attributes["boot_policy"] = ""
507 vm.appendChild(hvm)
509 # Make memory tag
511 memory = document.createElement("memory")
513 memory_str = str(int(
514 get_child_by_name(config, "memory"))*1024*1024)
516 memory.attributes["static_min"] = memory_str
517 memory.attributes["static_max"] = memory_str
518 memory.attributes["dynamic_min"] = memory_str
519 memory.attributes["dynamic_max"] = memory_str
521 vm.appendChild(memory)
523 # And now the vbds
525 vbds = map(lambda vbd: self.extract_vbd(vbd, document), vbds_sxp)
527 map(vm.appendChild, vbds)
529 # And now the vifs
531 vifs = map(lambda vif: self.extract_vif(vif, document), vifs_sxp)
533 map(vm.appendChild, vifs)
535 # transient?
537 if transient:
538 other_config = document.createElement("other_config")
539 other_config.attributes["key"] = "transient"
540 other_config.attributes["value"] = "True"
541 vm.appendChild(other_config)
543 # Add it to doc_root
545 document.documentElement.appendChild(vm)
547 # We want to pull out vdis
549 vdis = map(lambda vdb: self.extract_vdi(vdb, document), vbds_sxp)
551 map(document.documentElement.appendChild, vdis)
553 return document
555 def make_name_tag(self, label_text, document):
556 name = document.createElement("name")
558 label = document.createElement("label")
559 label.appendChild(document.createTextNode(str(label_text)))
560 name.appendChild(label)
562 description = document.createElement("description")
563 description.appendChild(document.createTextNode(" "))
564 name.appendChild(description)
566 return name
568 def extract_vbd(self, vbd_sxp, document):
569 src = get_child_by_name(vbd_sxp, "uname")
570 name = str(src.__hash__())
572 vbd = document.createElement("vbd")
574 vbd.attributes["name"] = "vdb" + name
575 vbd.attributes["vdi"] = "vdi" + name
576 vbd.attributes["mode"] \
577 = get_child_by_name(vbd_sxp, "mode") != "w" \
578 and "RO" or "RW"
579 vbd.attributes["device"] \
580 = get_child_by_name(vbd_sxp, "dev")
581 vbd.attributes["bootable"] = "1"
582 vbd.attributes["type"] = "disk"
583 vbd.attributes["qos_algorithm_type"] = ""
585 return vbd
587 def extract_vdi(self, vbd_sxp, document):
588 src = get_child_by_name(vbd_sxp, "uname")
589 name = "vdi" + str(src.__hash__())
590 path = src[src.find(":")+1:]
592 vdi = document.createElement("vdi")
594 vdi.attributes["src"] = src
595 vdi.attributes["read_only"] \
596 = (get_child_by_name(vbd_sxp, "mode") != "w") \
597 and "true" or "false"
598 vdi.attributes["size"] \
599 = str(os.path.getsize(path))
600 vdi.attributes["type"] = "system"
601 vdi.attributes["shareable"] = "false"
602 vdi.attributes["name"] = name
604 vdi.appendChild(self.make_name_tag(name, document))
606 return vdi
608 def extract_vif(self, vif_sxp, document):
610 vif = document.createElement("vif")
612 dev = get_child_by_name(vif_sxp, "vifname", "eth0")
614 vif.attributes["name"] \
615 = "vif" + str(dev.__hash__())
616 vif.attributes["mac"] \
617 = get_child_by_name(vif_sxp, "mac", "")
618 vif.attributes["mtu"] \
619 = get_child_by_name(vif_sxp, "mtu", "")
620 vif.attributes["device"] = dev
621 vif.attributes["qos_algorithm_type"] = ""
623 if get_child_by_name(vif_sxp, "bridge") is not None:
624 vif.attributes["bridge"] \
625 = get_child_by_name(vif_sxp, "bridge")
627 return vif