ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 4947:6556b9a0ffa4

bitkeeper revision 1.1421.1.1 (4289b3a0n7JmXiz6fiz0QAfqJ_M1Lg)

XendDomainInfo.py:
Redirect debug output to log.
PrettyPrint.py:
Add prettyprintstring.
XendDomainInfo.py:
Remove debug print.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Tue May 17 09:04:32 2005 +0000 (2005-05-17)
parents d16ae85cb89e
children 5b730cb3857a
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
3 """Representation of a single domain.
4 Includes support for domain construction, using
5 open-ended configurations.
7 Author: Mike Wray <mike.wray@hp.com>
9 """
11 import string
12 import os
13 import time
15 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
16 import xen.util.ip
17 from xen.util.ip import _readline, _readlines
18 from xen.xend.server import channel, controller
19 from xen.util.blkif import blkdev_uname_to_file
21 from server.channel import channelFactory
22 import server.SrvDaemon; xend = server.SrvDaemon.instance()
23 from server import messages
25 from xen.xend.XendBootloader import bootloader
26 import sxp
27 from XendLogging import log
28 from XendError import VmError
29 from XendRoot import get_component
31 from PrettyPrint import prettyprintstring
33 """Flag for a block device backend domain."""
34 SIF_BLK_BE_DOMAIN = (1<<4)
36 """Flag for a net device backend domain."""
37 SIF_NET_BE_DOMAIN = (1<<5)
39 """Shutdown code for poweroff."""
40 DOMAIN_POWEROFF = 0
42 """Shutdown code for reboot."""
43 DOMAIN_REBOOT = 1
45 """Shutdown code for suspend."""
46 DOMAIN_SUSPEND = 2
48 """Map shutdown codes to strings."""
49 shutdown_reasons = {
50 DOMAIN_POWEROFF: "poweroff",
51 DOMAIN_REBOOT : "reboot",
52 DOMAIN_SUSPEND : "suspend" }
54 """Map shutdown reasons to the message type to use.
55 """
56 shutdown_messages = {
57 'poweroff' : 'shutdown_poweroff_t',
58 'reboot' : 'shutdown_reboot_t',
59 'suspend' : 'shutdown_suspend_t',
60 'sysrq' : 'shutdown_sysrq_t',
61 }
63 RESTART_ALWAYS = 'always'
64 RESTART_ONREBOOT = 'onreboot'
65 RESTART_NEVER = 'never'
67 restart_modes = [
68 RESTART_ALWAYS,
69 RESTART_ONREBOOT,
70 RESTART_NEVER,
71 ]
73 STATE_RESTART_PENDING = 'pending'
74 STATE_RESTART_BOOTING = 'booting'
76 STATE_VM_OK = "ok"
77 STATE_VM_TERMINATED = "terminated"
80 def domain_exists(name):
81 # See comment in XendDomain constructor.
82 xd = get_component('xen.xend.XendDomain')
83 return xd.domain_exists(name)
85 def shutdown_reason(code):
86 """Get a shutdown reason from a code.
88 @param code: shutdown code
89 @type code: int
90 @return: shutdown reason
91 @rtype: string
92 """
93 return shutdown_reasons.get(code, "?")
95 def vif_up(iplist):
96 """send an unsolicited ARP reply for all non link-local IP addresses.
98 @param iplist: IP addresses
99 """
101 IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
103 def get_ip_nonlocal_bind():
104 return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
106 def set_ip_nonlocal_bind(v):
107 print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
109 def link_local(ip):
110 return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
112 def arping(ip, gw):
113 cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
114 log.debug(cmd)
115 os.system(cmd)
117 gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
118 nlb = get_ip_nonlocal_bind()
119 if not nlb: set_ip_nonlocal_bind(1)
120 try:
121 for ip in iplist:
122 if not link_local(ip):
123 arping(ip, gateway)
124 finally:
125 if not nlb: set_ip_nonlocal_bind(0)
127 config_handlers = {}
129 def add_config_handler(name, h):
130 """Add a handler for a config field.
132 @param name: field name
133 @param h: handler: fn(vm, config, field, index)
134 """
135 config_handlers[name] = h
137 def get_config_handler(name):
138 """Get a handler for a config field.
140 returns handler or None
141 """
142 return config_handlers.get(name)
144 """Table of handlers for virtual machine images.
145 Indexed by image type.
146 """
147 image_handlers = {}
149 def add_image_handler(name, h):
150 """Add a handler for an image type
151 @param name: image type
152 @param h: handler: fn(config, name, memory, image)
153 """
154 image_handlers[name] = h
156 def get_image_handler(name):
157 """Get the handler for an image type.
158 @param name: image type
159 @return: handler or None
160 """
161 return image_handlers.get(name)
163 """Table of handlers for devices.
164 Indexed by device type.
165 """
166 device_handlers = {}
168 def add_device_handler(name, type):
169 device_handlers[name] = type
171 def get_device_handler(name):
172 return device_handlers[name]
175 def vm_create(config):
176 """Create a VM from a configuration.
177 If a vm has been partially created and there is an error it
178 is destroyed.
180 @param config configuration
181 @raise: VmError for invalid configuration
182 """
183 vm = XendDomainInfo()
184 vm.construct(config)
185 return vm
187 def vm_recreate(savedinfo, info):
188 """Create the VM object for an existing domain.
190 @param savedinfo: saved info from the domain DB
191 @type savedinfo: sxpr
192 @param info: domain info from xc
193 @type info: xc domain dict
194 """
195 log.debug('savedinfo=' + prettyprintstring(savedinfo))
196 log.debug('info=' + str(info))
197 vm = XendDomainInfo()
198 vm.recreate = True
199 vm.savedinfo = savedinfo
200 vm.setdom(info['dom'])
201 vm.memory = info['mem_kb']/1024
202 start_time = sxp.child_value(savedinfo, 'start_time')
203 if start_time is not None:
204 vm.start_time = float(start_time)
205 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
206 vm.restart_count = int(sxp.child_value(savedinfo, 'restart_count', 0))
207 restart_time = sxp.child_value(savedinfo, 'restart_time')
208 if restart_time is not None:
209 vm.restart_time = float(restart_time)
210 config = sxp.child_value(savedinfo, 'config')
211 if config:
212 vm.construct(config)
213 else:
214 vm.name = sxp.child_value(savedinfo, 'name', "Domain-%d" % info['dom'])
215 vm.recreate = False
216 vm.savedinfo = None
217 return vm
219 def vm_restore(src, progress=False):
220 """Restore a VM from a disk image.
222 src saved state to restore
223 progress progress reporting flag
224 raises VmError for invalid configuration
225 """
226 vm = XendDomainInfo()
227 ostype = "linux" #todo Set from somewhere (store in the src?).
228 restorefn = getattr(xc, "%s_restore" % ostype)
229 d = restorefn(state_file=src, progress=progress)
230 dom = int(d['dom'])
231 if dom < 0:
232 raise VmError('restore failed')
233 try:
234 vmconfig = sxp.from_string(d['vmconfig'])
235 config = sxp.child_value(vmconfig, 'config')
236 except Exception, ex:
237 raise VmError('config error: ' + str(ex))
238 vm.dom_construct(dom, config)
239 vif_up(vm.ipaddrs)
240 return vm
242 def dom_get(dom):
243 """Get info from xen for an existing domain.
245 @param dom: domain id
246 @return: info or None
247 """
248 domlist = xc.domain_getinfo(dom, 1)
249 if domlist and dom == domlist[0]['dom']:
250 return domlist[0]
251 return None
253 class XendDomainInfo:
254 """Virtual machine object."""
256 """Minimum time between domain restarts in seconds.
257 """
258 MINIMUM_RESTART_TIME = 20
260 def __init__(self):
261 self.recreate = 0
262 self.restore = 0
263 self.config = None
264 self.id = None
265 self.dom = None
266 self.cpu_weight = 1
267 self.start_time = None
268 self.name = None
269 self.memory = None
270 self.image = None
271 self.ramdisk = None
272 self.cmdline = None
274 self.channel = None
275 self.controllers = {}
277 self.configs = []
279 self.info = None
280 self.ipaddrs = []
281 self.blkif_backend = False
282 self.netif_backend = False
283 #todo: state: running, suspended
284 self.state = STATE_VM_OK
285 #todo: set to migrate info if migrating
286 self.migrate = None
288 self.restart_mode = RESTART_ONREBOOT
289 self.restart_state = None
290 self.restart_time = None
291 self.restart_count = 0
293 self.console_port = None
294 self.savedinfo = None
295 self.image_handler = None
296 self.is_vmx = False
297 self.vcpus = 1
298 self.bootloader = None
300 def setdom(self, dom):
301 """Set the domain id.
303 @param dom: domain id
304 """
305 self.dom = int(dom)
306 self.id = str(dom)
308 def getDomain(self):
309 return self.dom
311 def getName(self):
312 return self.name
314 def getChannel(self):
315 return self.channel
317 def update(self, info):
318 """Update with info from xc.domain_getinfo().
319 """
320 self.info = info
321 self.memory = self.info['mem_kb'] / 1024
323 def __str__(self):
324 s = "domain"
325 s += " id=" + self.id
326 s += " name=" + self.name
327 s += " memory=" + str(self.memory)
328 console = self.getConsole()
329 if console:
330 s += " console=" + str(console.console_port)
331 if self.image:
332 s += " image=" + self.image
333 s += ""
334 return s
336 __repr__ = __str__
338 def getDeviceTypes(self):
339 return self.controllers.keys()
341 def getDeviceControllers(self):
342 return self.controllers.values()
344 def getDeviceController(self, type, error=True):
345 ctrl = self.controllers.get(type)
346 if not ctrl and error:
347 raise XendError("invalid device type:" + type)
348 return ctrl
350 def findDeviceController(self, type):
351 return (self.getDeviceController(type, error=False)
352 or self.createDeviceController(type))
354 def createDeviceController(self, type):
355 ctrl = controller.createDevController(type, self, recreate=self.recreate)
356 self.controllers[type] = ctrl
357 return ctrl
359 def createDevice(self, type, devconfig, recreate=False):
360 ctrl = self.findDeviceController(type)
361 return ctrl.createDevice(devconfig, recreate=self.recreate)
363 def configureDevice(self, type, id, devconfig):
364 ctrl = self.getDeviceController(type)
365 return ctrl.configureDevice(id, devconfig)
367 def destroyDevice(self, type, id, change=False, reboot=False):
368 ctrl = self.getDeviceController(type)
369 return ctrl.destroyDevice(id, change=change, reboot=reboot)
371 def deleteDevice(self, type, id):
372 ctrl = self.getDeviceController(type)
373 return ctrl.deleteDevice(id)
375 def getDevice(self, type, id):
376 ctrl = self.getDeviceController(type)
377 return ctrl.getDevice(id)
379 def getDeviceByIndex(self, type, idx):
380 ctrl = self.getDeviceController(type)
381 return ctrl.getDeviceByIndex(idx)
383 def getDeviceConfig(self, type, id):
384 ctrl = self.getDeviceController(type)
385 return ctrl.getDeviceConfig(id)
387 def getDeviceIds(self, type):
388 ctrl = self.getDeviceController(type)
389 return ctrl.getDeviceIds()
391 def getDeviceIndexes(self, type):
392 ctrl = self.getDeviceController(type)
393 return ctrl.getDeviceIndexes()
395 def getDeviceConfigs(self, type):
396 ctrl = self.getDeviceController(type)
397 return ctrl.getDeviceConfigs()
399 def getDeviceSxprs(self, type):
400 ctrl = self.getDeviceController(type)
401 return ctrl.getDeviceSxprs()
403 def sxpr(self):
404 sxpr = ['domain',
405 ['id', self.id],
406 ['name', self.name],
407 ['memory', self.memory] ]
409 if self.info:
410 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
411 run = (self.info['running'] and 'r') or '-'
412 block = (self.info['blocked'] and 'b') or '-'
413 pause = (self.info['paused'] and 'p') or '-'
414 shut = (self.info['shutdown'] and 's') or '-'
415 crash = (self.info['crashed'] and 'c') or '-'
416 state = run + block + pause + shut + crash
417 sxpr.append(['state', state])
418 if self.info['shutdown']:
419 reason = shutdown_reason(self.info['shutdown_reason'])
420 sxpr.append(['shutdown_reason', reason])
421 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
422 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
423 sxpr.append(['vcpus', self.info['vcpus']])
424 sxpr.append(['cpumap', self.info['cpumap']])
425 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x),
426 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))])
428 if self.start_time:
429 up_time = time.time() - self.start_time
430 sxpr.append(['up_time', str(up_time) ])
431 sxpr.append(['start_time', str(self.start_time) ])
433 if self.channel:
434 sxpr.append(self.channel.sxpr())
435 console = self.getConsole()
436 if console:
437 sxpr.append(console.sxpr())
438 if self.restart_count:
439 sxpr.append(['restart_count', self.restart_count])
440 if self.restart_state:
441 sxpr.append(['restart_state', self.restart_state])
442 if self.restart_time:
443 sxpr.append(['restart_time', str(self.restart_time)])
444 devs = self.sxpr_devices()
445 if devs:
446 sxpr.append(devs)
447 if self.config:
448 sxpr.append(['config', self.config])
449 return sxpr
451 def sxpr_devices(self):
452 sxpr = []
453 for ty in self.getDeviceTypes():
454 devs = self.getDeviceSxprs(ty)
455 sxpr += devs
456 if sxpr:
457 sxpr.insert(0, 'devices')
458 else:
459 sxpr = None
460 return sxpr
462 def check_name(self, name):
463 """Check if a vm name is valid. Valid names contain alphabetic characters,
464 digits, or characters in '_-.:/+'.
465 The same name cannot be used for more than one vm at the same time.
467 @param name: name
468 @raise: VMerror if invalid
469 """
470 if self.recreate: return
471 if name is None or name == '':
472 raise VmError('missing vm name')
473 for c in name:
474 if c in string.digits: continue
475 if c in '_-.:/+': continue
476 if c in string.ascii_letters: continue
477 raise VmError('invalid vm name')
478 dominfo = domain_exists(name)
479 # When creating or rebooting, a domain with my name should not exist.
480 # When restoring, a domain with my name will exist, but it should have
481 # my domain id.
482 if not dominfo:
483 return
484 if dominfo.is_terminated():
485 return
486 if not self.dom or (dominfo.dom != self.dom):
487 raise VmError('vm name clash: ' + name)
489 def construct(self, config):
490 """Construct the vm instance from its configuration.
492 @param config: configuration
493 @raise: VmError on error
494 """
495 # todo - add support for scheduling params?
496 self.config = config
497 try:
498 # Initial domain create.
499 self.name = sxp.child_value(config, 'name')
500 self.check_name(self.name)
501 self.configure_cpus(config)
502 self.find_image_handler()
503 self.init_domain()
504 self.register_domain()
505 self.configure_bootloader()
507 # Create domain devices.
508 self.configure_backends()
509 self.configure_console()
510 self.configure_restart()
511 self.construct_image()
512 self.configure()
513 except Exception, ex:
514 # Catch errors, cleanup and re-raise.
515 print 'Domain construction error:', ex
516 import traceback
517 traceback.print_exc()
518 self.destroy()
519 raise
521 def register_domain(self):
522 xd = get_component('xen.xend.XendDomain')
523 xd._add_domain(self)
525 def configure_cpus(self, config):
526 try:
527 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
528 except:
529 raise VmError('invalid cpu weight')
530 self.memory = int(sxp.child_value(config, 'memory'))
531 if self.memory is None:
532 raise VmError('missing memory size')
533 cpu = sxp.child_value(config, 'cpu')
534 if self.recreate and self.dom and cpu is not None:
535 #xc.domain_pincpu(self.dom, int(cpu))
536 xc.domain_pincpu(self.dom, 0, 1<<int(cpu))
537 try:
538 image = sxp.child_value(self.config, 'image')
539 vcpus = sxp.child_value(image, 'vcpus')
540 if vcpus:
541 self.vcpus = int(vcpus)
542 except:
543 raise VmError('invalid vcpus value')
545 def find_image_handler(self):
546 """Construct the boot image for the domain.
548 @return vm
549 """
550 image = sxp.child_value(self.config, 'image')
551 if image is None:
552 raise VmError('missing image')
553 image_name = sxp.name(image)
554 if image_name is None:
555 raise VmError('missing image name')
556 if image_name == "vmx":
557 self.is_vmx = True
558 image_handler = get_image_handler(image_name)
559 if image_handler is None:
560 raise VmError('unknown image type: ' + image_name)
561 self.image_handler = image_handler
562 return self
564 def construct_image(self):
565 image = sxp.child_value(self.config, 'image')
566 self.image_handler(self, image)
567 return self
569 def config_devices(self, name):
570 """Get a list of the 'device' nodes of a given type from the config.
572 @param name: device type
573 @type name: string
574 @return: device configs
575 @rtype: list
576 """
577 devices = []
578 for d in sxp.children(self.config, 'device'):
579 dev = sxp.child0(d)
580 if dev is None: continue
581 if name == sxp.name(dev):
582 devices.append(dev)
583 return devices
585 def get_device_savedinfo(self, type, index):
586 val = None
587 if self.savedinfo is None:
588 return val
589 devices = sxp.child(self.savedinfo, 'devices')
590 if devices is None:
591 return val
592 index = str(index)
593 for d in sxp.children(devices, type):
594 dindex = sxp.child_value(d, 'index')
595 if dindex is None: continue
596 if str(dindex) == index:
597 val = d
598 break
599 return val
601 def get_device_recreate(self, type, index):
602 return self.get_device_savedinfo(type, index) or self.recreate
604 def add_config(self, val):
605 """Add configuration data to a virtual machine.
607 @param val: data to add
608 """
609 self.configs.append(val)
611 def destroy(self):
612 """Completely destroy the vm.
613 """
614 self.cleanup()
615 return self.destroy_domain()
617 def destroy_domain(self):
618 """Destroy the vm's domain.
619 The domain will not finally go away unless all vm
620 devices have been released.
621 """
622 if self.channel:
623 self.channel.close()
624 self.channel = None
625 if self.dom is None: return 0
626 try:
627 return xc.domain_destroy(dom=self.dom)
628 except Exception, err:
629 log.exception("Domain destroy failed: %s", self.name)
631 def cleanup(self):
632 """Cleanup vm resources: release devices.
633 """
634 self.state = STATE_VM_TERMINATED
635 self.release_devices()
637 def is_terminated(self):
638 """Check if a domain has been terminated.
639 """
640 return self.state == STATE_VM_TERMINATED
642 def release_devices(self):
643 """Release all vm devices.
644 """
645 reboot = self.restart_pending()
646 for ctrl in self.getDeviceControllers():
647 if ctrl.isDestroyed(): continue
648 ctrl.destroyController(reboot=reboot)
649 if not reboot:
650 self.configs = []
651 self.ipaddrs = []
653 def show(self):
654 """Print virtual machine info.
655 """
656 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
657 print "image:"
658 sxp.show(self.image)
659 print
660 for val in self.configs:
661 print "config:"
662 sxp.show(val)
663 print
664 print "]"
666 def init_domain(self):
667 """Initialize the domain memory.
668 """
669 if self.recreate:
670 return
671 if self.start_time is None:
672 self.start_time = time.time()
673 if self.restore:
674 return
675 dom = self.dom or 0
676 memory = self.memory
677 try:
678 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
679 except:
680 raise VmError('invalid cpu')
681 cpu_weight = self.cpu_weight
682 memory = memory * 1024 + self.pgtable_size(memory)
683 dom = xc.domain_create(dom= dom, mem_kb= memory,
684 cpu= cpu, cpu_weight= cpu_weight)
685 if self.bootloader:
686 try:
687 if kernel: os.unlink(kernel)
688 if ramdisk: os.unlink(ramdisk)
689 except OSError, e:
690 log.warning('unable to unlink kernel/ramdisk: %s' %(e,))
692 if dom <= 0:
693 raise VmError('Creating domain failed: name=%s memory=%d'
694 % (self.name, memory))
695 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, self.name, memory)
696 self.setdom(dom)
698 def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap):
699 """Build the domain boot image.
700 """
701 if self.recreate or self.restore: return
702 if not os.path.isfile(kernel):
703 raise VmError('Kernel image does not exist: %s' % kernel)
704 if ramdisk and not os.path.isfile(ramdisk):
705 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
706 if len(cmdline) >= 256:
707 log.warning('kernel cmdline too long, domain %d', self.dom)
708 dom = self.dom
709 buildfn = getattr(xc, '%s_build' % ostype)
710 flags = 0
711 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
712 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
713 #todo generalise this
714 if ostype == "vmx":
715 err = buildfn(dom = dom,
716 image = kernel,
717 control_evtchn = 0,
718 memsize = self.memory,
719 memmap = memmap,
720 cmdline = cmdline,
721 ramdisk = ramdisk,
722 flags = flags)
723 else:
724 log.warning('building dom with %d vcpus', self.vcpus)
725 err = buildfn(dom = dom,
726 image = kernel,
727 control_evtchn = self.channel.getRemotePort(),
728 cmdline = cmdline,
729 ramdisk = ramdisk,
730 flags = flags,
731 vcpus = self.vcpus)
732 if err != 0:
733 raise VmError('Building domain failed: type=%s dom=%d err=%d'
734 % (ostype, dom, err))
736 def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''):
737 """Create a domain. Builds the image but does not configure it.
739 @param ostype: OS type
740 @param kernel: kernel image
741 @param ramdisk: kernel ramdisk
742 @param cmdline: kernel commandline
743 """
745 self.create_channel()
746 self.build_domain(ostype, kernel, ramdisk, cmdline, memmap)
747 self.image = kernel
748 self.ramdisk = ramdisk
749 self.cmdline = cmdline
751 def create_channel(self):
752 """Create the control channel to the domain.
753 If saved info is available recreate the channel using the saved ports.
754 """
755 local = 0
756 remote = 1
757 if self.savedinfo:
758 info = sxp.child(self.savedinfo, "channel")
759 if info:
760 local = int(sxp.child_value(info, "local_port", 0))
761 remote = int(sxp.child_value(info, "remote_port", 1))
762 self.channel = channelFactory().openChannel(self.dom,
763 local_port=local,
764 remote_port=remote)
766 def create_configured_devices(self):
767 devices = sxp.children(self.config, 'device')
768 indexes = {}
769 for d in devices:
770 dev_config = sxp.child0(d)
771 if dev_config is None:
772 raise VmError('invalid device')
773 dev_type = sxp.name(dev_config)
774 ctrl_type = get_device_handler(dev_type)
775 if ctrl_type is None:
776 raise VmError('unknown device type: ' + dev_type)
777 # Keep track of device indexes by type, so we can fish
778 # out saved info for recreation.
779 idx = indexes.get(dev_type, -1)
780 idx += 1
781 indexes[ctrl_type] = idx
782 recreate = self.get_device_recreate(dev_type, idx)
783 self.createDevice(ctrl_type, dev_config, recreate=recreate)
785 def create_devices(self):
786 """Create the devices for a vm.
788 @raise: VmError for invalid devices
789 """
790 if self.rebooting():
791 for ctrl in self.getDeviceControllers():
792 ctrl.initController(reboot=True)
793 else:
794 self.create_configured_devices()
795 if self.is_vmx:
796 self.create_vmx_model()
798 def create_vmx_model(self):
799 #todo: remove special case for vmx
800 device_model = sxp.child_value(self.config, 'device_model')
801 if not device_model:
802 raise VmError("vmx: missing device model")
803 device_config = sxp.child_value(self.config, 'device_config')
804 if not device_config:
805 raise VmError("vmx: missing device config")
806 #todo: self.memory?
807 memory = sxp.child_value(self.config, "memory")
808 # Create an event channel
809 device_channel = channel.eventChannel(0, self.dom)
810 # Execute device model.
811 #todo: Error handling
812 os.system(device_model
813 + " -f %s" % device_config
814 + " -d %d" % self.dom
815 + " -p %d" % device_channel['port1']
816 + " -m %s" % memory)
818 def device_create(self, dev_config):
819 """Create a new device.
821 @param dev_config: device configuration
822 """
823 dev_type = sxp.name(dev_config)
824 dev = self.createDevice(self, dev_config, change=True)
825 self.config.append(['device', dev.getConfig()])
826 return dev.sxpr()
828 def device_configure(self, dev_config, idx):
829 """Configure an existing device.
831 @param dev_config: device configuration
832 @param idx: device index
833 """
834 type = sxp.name(dev_config)
835 dev = self.getDeviceByIndex(type, idx)
836 if not dev:
837 raise VmError('invalid device: %s %s' % (type, idx))
838 old_config = dev.getConfig()
839 new_config = dev.configure(dev_config, change=True)
840 # Patch new config into vm config.
841 new_full_config = ['device', new_config]
842 old_full_config = ['device', old_config]
843 old_index = self.config.index(old_full_config)
844 self.config[old_index] = new_full_config
845 return new_config
847 def device_refresh(self, type, idx):
848 """Refresh a device.
850 @param type: device type
851 @param idx: device index
852 """
853 dev = self.getDeviceByIndex(type, idx)
854 if not dev:
855 raise VmError('invalid device: %s %s' % (type, idx))
856 dev.refresh()
858 def device_delete(self, type, idx):
859 """Destroy and remove a device.
861 @param type: device type
862 @param idx: device index
863 """
864 dev = self.getDeviceByIndex(type, idx)
865 if not dev:
866 raise VmError('invalid device: %s %s' % (type, idx))
867 dev_config = dev.getConfig()
868 if dev_config:
869 self.config.remove(['device', dev_config])
870 self.deleteDevice(type, dev.getId())
872 def configure_bootloader(self):
873 """Configure boot loader.
874 """
875 bl = sxp.child_value(self.config, "bootloader")
876 if bl is not None:
877 self.bootloader = bl
879 def configure_console(self):
880 """Configure the vm console port.
881 """
882 x = sxp.child_value(self.config, 'console')
883 if x:
884 try:
885 port = int(x)
886 except:
887 raise VmError('invalid console:' + str(x))
888 self.console_port = port
890 def configure_restart(self):
891 """Configure the vm restart mode.
892 """
893 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
894 if r not in restart_modes:
895 raise VmError('invalid restart mode: ' + str(r))
896 self.restart_mode = r;
898 def restart_needed(self, reason):
899 """Determine if the vm needs to be restarted when shutdown
900 for the given reason.
902 @param reason: shutdown reason
903 @return True if needs restart, False otherwise
904 """
905 if self.restart_mode == RESTART_NEVER:
906 return False
907 if self.restart_mode == RESTART_ALWAYS:
908 return True
909 if self.restart_mode == RESTART_ONREBOOT:
910 return reason == 'reboot'
911 return False
913 def restart_cancel(self):
914 """Cancel a vm restart.
915 """
916 self.restart_state = None
918 def restarting(self):
919 """Put the vm into restart mode.
920 """
921 self.restart_state = STATE_RESTART_PENDING
923 def restart_pending(self):
924 """Test if the vm has a pending restart.
925 """
926 return self.restart_state == STATE_RESTART_PENDING
928 def rebooting(self):
929 return self.restart_state == STATE_RESTART_BOOTING
931 def restart_check(self):
932 """Check if domain restart is OK.
933 To prevent restart loops, raise an error if it is
934 less than MINIMUM_RESTART_TIME seconds since the last restart.
935 """
936 tnow = time.time()
937 if self.restart_time is not None:
938 tdelta = tnow - self.restart_time
939 if tdelta < self.MINIMUM_RESTART_TIME:
940 self.restart_cancel()
941 msg = 'VM %s restarting too fast' % self.name
942 log.error(msg)
943 raise VmError(msg)
944 self.restart_time = tnow
945 self.restart_count += 1
947 def restart(self):
948 """Restart the domain after it has exited.
949 Reuses the domain id and console port.
951 """
952 try:
953 self.state = STATE_VM_OK
954 self.restart_check()
955 self.restart_state = STATE_RESTART_BOOTING
956 if self.bootloader:
957 self.config = self.bootloader_config()
958 self.construct(self.config)
959 finally:
960 self.restart_state = None
962 def bootloader_config(self):
963 # if we're restarting with a bootloader, we need to run it
964 # FIXME: this assumes the disk is the first device and
965 # that we're booting from the first disk
966 blcfg = None
967 # FIXME: this assumes that we want to use the first disk
968 dev = sxp.child_value(self.config, "device")
969 if dev:
970 disk = sxp.child_value(dev, "uname")
971 fn = blkdev_uname_to_file(disk)
972 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
973 if blcfg is None:
974 msg = "Had a bootloader specified, but can't find disk"
975 log.error(msg)
976 raise VmError(msg)
977 config = sxp.merge(['vm', blconfig ], self.config)
978 return config
980 def configure_backends(self):
981 """Set configuration flags if the vm is a backend for netif or blkif.
982 Configure the backends to use for vbd and vif if specified.
983 """
984 for c in sxp.children(self.config, 'backend'):
985 v = sxp.child0(c)
986 name = sxp.name(v)
987 if name == 'blkif':
988 self.blkif_backend = True
989 elif name == 'netif':
990 self.netif_backend = True
991 elif name == 'usbif':
992 self.usbif_backend = True
993 else:
994 raise VmError('invalid backend type:' + str(name))
996 def configure(self):
997 """Configure a vm.
999 """
1000 self.configure_fields()
1001 self.create_console()
1002 self.create_devices()
1003 self.create_blkif()
1005 def create_console(self):
1006 console = self.getConsole()
1007 if not console:
1008 config = ['console']
1009 if self.console_port:
1010 config.append(['console_port', self.console_port])
1011 console = self.createDevice('console', config)
1012 return console
1014 def getConsole(self):
1015 console_ctrl = self.getDeviceController("console", error=False)
1016 if console_ctrl:
1017 return console_ctrl.getDevice(0)
1018 return None
1020 def create_blkif(self):
1021 """Create the block device interface (blkif) for the vm.
1022 The vm needs a blkif even if it doesn't have any disks
1023 at creation time, for example when it uses NFS root.
1025 """
1026 blkif = self.getDeviceController("vbd", error=False)
1027 if not blkif:
1028 blkif = self.createDeviceController("vbd")
1029 backend = blkif.getBackend(0)
1030 backend.connect(recreate=self.recreate)
1032 def dom_construct(self, dom, config):
1033 """Construct a vm for an existing domain.
1035 @param dom: domain id
1036 @param config: domain configuration
1037 """
1038 d = dom_get(dom)
1039 if not d:
1040 raise VmError("Domain not found: %d" % dom)
1041 try:
1042 self.restore = True
1043 self.setdom(dom)
1044 self.memory = d['mem_kb']/1024
1045 self.construct(config)
1046 finally:
1047 self.restore = False
1049 def configure_fields(self):
1050 """Process the vm configuration fields using the registered handlers.
1051 """
1052 index = {}
1053 for field in sxp.children(self.config):
1054 field_name = sxp.name(field)
1055 field_index = index.get(field_name, 0)
1056 field_handler = get_config_handler(field_name)
1057 # Ignore unknown fields. Warn?
1058 if field_handler:
1059 v = field_handler(self, self.config, field, field_index)
1060 else:
1061 log.warning("Unknown config field %s", field_name)
1062 index[field_name] = field_index + 1
1064 def pgtable_size(self, memory):
1065 """Return the size of memory needed for 1:1 page tables for physical
1066 mode.
1068 @param memory: size in MB
1069 @return size in KB
1070 """
1071 if self.is_vmx:
1072 # Logic x86-32 specific.
1073 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
1074 return (1 + ((memory + 3) >> 2)) * 4
1075 return 0
1077 def mem_target_set(self, target):
1078 """Set domain memory target in pages.
1079 """
1080 if self.channel:
1081 msg = messages.packMsg('mem_request_t', { 'target' : target * (1 << 8)} )
1082 self.channel.writeRequest(msg)
1084 def shutdown(self, reason, key=0):
1085 msgtype = shutdown_messages.get(reason)
1086 if not msgtype:
1087 raise XendError('invalid reason:' + reason)
1088 extra = {}
1089 if reason == 'sysrq':
1090 extra['key'] = key
1091 if self.channel:
1092 msg = messages.packMsg(msgtype, extra)
1093 self.channel.writeRequest(msg)
1096 def vm_image_linux(vm, image):
1097 """Create a VM for a linux image.
1099 @param name: vm name
1100 @param memory: vm memory
1101 @param image: image config
1102 @return: vm
1103 """
1104 kernel = sxp.child_value(image, "kernel")
1105 cmdline = ""
1106 ip = sxp.child_value(image, "ip", None)
1107 if ip:
1108 cmdline += " ip=" + ip
1109 root = sxp.child_value(image, "root")
1110 if root:
1111 cmdline += " root=" + root
1112 args = sxp.child_value(image, "args")
1113 if args:
1114 cmdline += " " + args
1115 ramdisk = sxp.child_value(image, "ramdisk", '')
1116 log.debug("creating linux domain with cmdline: %s" %(cmdline,))
1117 vm.create_domain("linux", kernel, ramdisk, cmdline)
1118 return vm
1120 def vm_image_plan9(vm, image):
1121 """Create a VM for a Plan 9 image.
1123 name vm name
1124 memory vm memory
1125 image image config
1127 returns vm
1128 """
1129 kernel = sxp.child_value(image, "kernel")
1130 cmdline = ""
1131 ip = sxp.child_value(image, "ip", "dhcp")
1132 if ip:
1133 cmdline += "ip=" + ip
1134 root = sxp.child_value(image, "root")
1135 if root:
1136 cmdline += "root=" + root
1137 args = sxp.child_value(image, "args")
1138 if args:
1139 cmdline += " " + args
1140 ramdisk = sxp.child_value(image, "ramdisk", '')
1141 vm.create_domain("plan9", kernel, ramdisk, cmdline)
1142 return vm
1144 def vm_image_vmx(vm, image):
1145 """Create a VM for the VMX environment.
1147 @param name: vm name
1148 @param memory: vm memory
1149 @param image: image config
1150 @return: vm
1151 """
1152 kernel = sxp.child_value(image, "kernel")
1153 cmdline = ""
1154 ip = sxp.child_value(image, "ip", "dhcp")
1155 if ip:
1156 cmdline += " ip=" + ip
1157 root = sxp.child_value(image, "root")
1158 if root:
1159 cmdline += " root=" + root
1160 args = sxp.child_value(image, "args")
1161 if args:
1162 cmdline += " " + args
1163 ramdisk = sxp.child_value(image, "ramdisk", '')
1164 memmap = sxp.child_value(vm.config, "memmap", '')
1165 memmap = sxp.parse(open(memmap))[0]
1166 from xen.util.memmap import memmap_parse
1167 memmap = memmap_parse(memmap)
1168 vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap)
1169 return vm
1171 def vm_field_ignore(vm, config, val, index):
1172 """Dummy config field handler used for fields with built-in handling.
1174 @param vm: virtual machine
1175 @param config: vm config
1176 @param val: config field
1177 @param index: field index
1178 """
1179 pass
1181 def vm_field_maxmem(vm, config, val, index):
1182 """Configure vm memory limit.
1184 @param vm: virtual machine
1185 @param config: vm config
1186 @param val: config field
1187 @param index: field index
1188 """
1189 maxmem = sxp.child0(val)
1190 if maxmem is None:
1191 maxmem = vm.memory
1192 try:
1193 maxmem = int(maxmem)
1194 except:
1195 raise VmError("invalid maxmem: " + str(maxmem))
1196 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1198 #============================================================================
1199 # Register image handlers.
1200 add_image_handler('linux', vm_image_linux)
1201 add_image_handler('plan9', vm_image_plan9)
1202 add_image_handler('vmx', vm_image_vmx)
1204 # Ignore the fields we already handle.
1205 add_config_handler('name', vm_field_ignore)
1206 add_config_handler('memory', vm_field_ignore)
1207 add_config_handler('cpu', vm_field_ignore)
1208 add_config_handler('cpu_weight', vm_field_ignore)
1209 add_config_handler('console', vm_field_ignore)
1210 add_config_handler('restart', vm_field_ignore)
1211 add_config_handler('image', vm_field_ignore)
1212 add_config_handler('device', vm_field_ignore)
1213 add_config_handler('backend', vm_field_ignore)
1214 add_config_handler('vcpus', vm_field_ignore)
1215 add_config_handler('bootloader', vm_field_ignore)
1217 # Register other config handlers.
1218 add_config_handler('maxmem', vm_field_maxmem)
1220 #============================================================================
1221 # Register device controllers and their device config types.
1223 from server import console
1224 controller.addDevControllerClass("console", console.ConsoleController)
1226 from server import blkif
1227 controller.addDevControllerClass("vbd", blkif.BlkifController)
1228 add_device_handler("vbd", "vbd")
1230 from server import netif
1231 controller.addDevControllerClass("vif", netif.NetifController)
1232 add_device_handler("vif", "vif")
1234 from server import pciif
1235 controller.addDevControllerClass("pci", pciif.PciController)
1236 add_device_handler("pci", "pci")
1238 from xen.xend.server import usbif
1239 controller.addDevControllerClass("usb", usbif.UsbifController)
1240 add_device_handler("usb", "usb")
1242 #============================================================================