ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 5234:abe0fd18962c

bitkeeper revision 1.1614 (429c80a0GOB6P2mG94bxVMLZE34VdA)

XendDomainInfo.py:
g/c vif_up: it's the guest's responsibility to take appropriate
actions to get its network working again, i.e. send ARP packets when
it's resumed.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Tue May 31 15:20:00 2005 +0000 (2005-05-31)
parents c14443dad5a0
children 1af7f0708b54
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
14 import threading
16 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
17 import xen.util.ip
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 XendError, 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"
78 STATE_VM_SUSPENDED = "suspended"
81 def domain_exists(name):
82 # See comment in XendDomain constructor.
83 xd = get_component('xen.xend.XendDomain')
84 return xd.domain_lookup(name)
86 def shutdown_reason(code):
87 """Get a shutdown reason from a code.
89 @param code: shutdown code
90 @type code: int
91 @return: shutdown reason
92 @rtype: string
93 """
94 return shutdown_reasons.get(code, "?")
96 config_handlers = {}
98 def add_config_handler(name, h):
99 """Add a handler for a config field.
101 @param name: field name
102 @param h: handler: fn(vm, config, field, index)
103 """
104 config_handlers[name] = h
106 def get_config_handler(name):
107 """Get a handler for a config field.
109 returns handler or None
110 """
111 return config_handlers.get(name)
113 """Table of handlers for virtual machine images.
114 Indexed by image type.
115 """
116 image_handlers = {}
118 def add_image_handler(name, h):
119 """Add a handler for an image type
120 @param name: image type
121 @param h: handler: fn(config, name, memory, image)
122 """
123 image_handlers[name] = h
125 def get_image_handler(name):
126 """Get the handler for an image type.
127 @param name: image type
128 @return: handler or None
129 """
130 return image_handlers.get(name)
132 """Table of handlers for devices.
133 Indexed by device type.
134 """
135 device_handlers = {}
137 def add_device_handler(name, type):
138 device_handlers[name] = type
140 def get_device_handler(name):
141 return device_handlers[name]
144 def vm_create(config):
145 """Create a VM from a configuration.
146 If a vm has been partially created and there is an error it
147 is destroyed.
149 @param config configuration
150 @raise: VmError for invalid configuration
151 """
152 vm = XendDomainInfo()
153 vm.construct(config)
154 return vm
156 def tmp_restore_create_domain():
157 # dom input parameter is ignored
158 vm = XendDomainInfo()
159 dom = xc.domain_create()
160 vm.setdom(dom)
161 return vm
163 def vm_recreate(savedinfo, info):
164 """Create the VM object for an existing domain.
166 @param savedinfo: saved info from the domain DB
167 @type savedinfo: sxpr
168 @param info: domain info from xc
169 @type info: xc domain dict
170 """
171 log.debug('savedinfo=' + prettyprintstring(savedinfo))
172 log.debug('info=' + str(info))
173 vm = XendDomainInfo()
174 vm.recreate = True
175 vm.savedinfo = savedinfo
176 vm.setdom(info['dom'])
177 vm.memory = info['mem_kb']/1024
178 start_time = sxp.child_value(savedinfo, 'start_time')
179 if start_time is not None:
180 vm.start_time = float(start_time)
181 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
182 vm.restart_count = int(sxp.child_value(savedinfo, 'restart_count', 0))
183 restart_time = sxp.child_value(savedinfo, 'restart_time')
184 if restart_time is not None:
185 vm.restart_time = float(restart_time)
186 config = sxp.child_value(savedinfo, 'config')
187 if config:
188 vm.construct(config)
189 else:
190 vm.name = sxp.child_value(savedinfo, 'name', "Domain-%d" % info['dom'])
191 vm.recreate = False
192 vm.savedinfo = None
193 return vm
195 def vm_restore(src, progress=False):
196 """Restore a VM from a disk image.
198 src saved state to restore
199 progress progress reporting flag
200 raises VmError for invalid configuration
201 """
202 vm = XendDomainInfo()
203 ostype = "linux" #todo Set from somewhere (store in the src?).
204 restorefn = getattr(xc, "%s_restore" % ostype)
205 d = restorefn(state_file=src, progress=progress)
206 dom = int(d['dom'])
207 if dom < 0:
208 raise VmError('restore failed')
209 try:
210 vmconfig = sxp.from_string(d['vmconfig'])
211 config = sxp.child_value(vmconfig, 'config')
212 except Exception, ex:
213 raise VmError('config error: ' + str(ex))
214 vm.dom_construct(dom, config)
215 return vm
217 def dom_get(dom):
218 """Get info from xen for an existing domain.
220 @param dom: domain id
221 @return: info or None
222 """
223 domlist = xc.domain_getinfo(dom, 1)
224 if domlist and dom == domlist[0]['dom']:
225 return domlist[0]
226 return None
228 class XendDomainInfo:
229 """Virtual machine object."""
231 """Minimum time between domain restarts in seconds.
232 """
233 MINIMUM_RESTART_TIME = 20
235 def __init__(self):
236 self.recreate = 0
237 self.restore = 0
238 self.config = None
239 self.id = None
240 self.dom = None
241 self.cpu_weight = 1
242 self.start_time = None
243 self.name = None
244 self.memory = None
245 self.image = None
246 self.ramdisk = None
247 self.cmdline = None
249 self.channel = None
250 self.controllers = {}
252 self.configs = []
254 self.info = None
255 self.ipaddrs = []
256 self.blkif_backend = False
257 self.netif_backend = False
258 #todo: state: running, suspended
259 self.state = STATE_VM_OK
260 self.state_updated = threading.Condition()
261 self.shutdown_pending = None
263 #todo: set to migrate info if migrating
264 self.migrate = None
266 self.restart_mode = RESTART_ONREBOOT
267 self.restart_state = None
268 self.restart_time = None
269 self.restart_count = 0
271 self.console_port = None
272 self.savedinfo = None
273 self.image_handler = None
274 self.is_vmx = False
275 self.vcpus = 1
276 self.bootloader = None
278 def setdom(self, dom):
279 """Set the domain id.
281 @param dom: domain id
282 """
283 self.dom = int(dom)
284 self.id = str(dom)
286 def getDomain(self):
287 return self.dom
289 def getName(self):
290 return self.name
292 def getChannel(self):
293 return self.channel
295 def update(self, info):
296 """Update with info from xc.domain_getinfo().
297 """
298 self.info = info
299 self.memory = self.info['mem_kb'] / 1024
301 def state_set(self, state):
302 self.state_updated.acquire()
303 if self.state != state:
304 self.state = state
305 self.state_updated.notifyAll()
306 self.state_updated.release()
308 def state_wait(self, state):
309 self.state_updated.acquire()
310 while self.state != state:
311 self.state_updated.wait()
312 self.state_updated.release()
314 def __str__(self):
315 s = "domain"
316 s += " id=" + self.id
317 s += " name=" + self.name
318 s += " memory=" + str(self.memory)
319 console = self.getConsole()
320 if console:
321 s += " console=" + str(console.console_port)
322 if self.image:
323 s += " image=" + self.image
324 s += ""
325 return s
327 __repr__ = __str__
329 def getDeviceTypes(self):
330 return self.controllers.keys()
332 def getDeviceControllers(self):
333 return self.controllers.values()
335 def getDeviceController(self, type, error=True):
336 ctrl = self.controllers.get(type)
337 if not ctrl and error:
338 raise XendError("invalid device type:" + type)
339 return ctrl
341 def findDeviceController(self, type):
342 return (self.getDeviceController(type, error=False)
343 or self.createDeviceController(type))
345 def createDeviceController(self, type):
346 ctrl = controller.createDevController(type, self, recreate=self.recreate)
347 self.controllers[type] = ctrl
348 return ctrl
350 def createDevice(self, type, devconfig, recreate=False):
351 ctrl = self.findDeviceController(type)
352 return ctrl.createDevice(devconfig, recreate=self.recreate)
354 def configureDevice(self, type, id, devconfig):
355 ctrl = self.getDeviceController(type)
356 return ctrl.configureDevice(id, devconfig)
358 def destroyDevice(self, type, id, change=False, reboot=False):
359 ctrl = self.getDeviceController(type)
360 return ctrl.destroyDevice(id, change=change, reboot=reboot)
362 def deleteDevice(self, type, id):
363 ctrl = self.getDeviceController(type)
364 return ctrl.deleteDevice(id)
366 def getDevice(self, type, id):
367 ctrl = self.getDeviceController(type)
368 return ctrl.getDevice(id)
370 def getDeviceByIndex(self, type, idx):
371 ctrl = self.getDeviceController(type)
372 return ctrl.getDeviceByIndex(idx)
374 def getDeviceConfig(self, type, id):
375 ctrl = self.getDeviceController(type)
376 return ctrl.getDeviceConfig(id)
378 def getDeviceIds(self, type):
379 ctrl = self.getDeviceController(type)
380 return ctrl.getDeviceIds()
382 def getDeviceIndexes(self, type):
383 ctrl = self.getDeviceController(type)
384 return ctrl.getDeviceIndexes()
386 def getDeviceConfigs(self, type):
387 ctrl = self.getDeviceController(type)
388 return ctrl.getDeviceConfigs()
390 def getDeviceSxprs(self, type):
391 ctrl = self.getDeviceController(type)
392 return ctrl.getDeviceSxprs()
394 def sxpr(self):
395 sxpr = ['domain',
396 ['id', self.id],
397 ['name', self.name],
398 ['memory', self.memory] ]
400 if self.info:
401 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
402 run = (self.info['running'] and 'r') or '-'
403 block = (self.info['blocked'] and 'b') or '-'
404 pause = (self.info['paused'] and 'p') or '-'
405 shut = (self.info['shutdown'] and 's') or '-'
406 crash = (self.info['crashed'] and 'c') or '-'
407 state = run + block + pause + shut + crash
408 sxpr.append(['state', state])
409 if self.info['shutdown']:
410 reason = shutdown_reason(self.info['shutdown_reason'])
411 sxpr.append(['shutdown_reason', reason])
412 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
413 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
414 sxpr.append(['vcpus', self.info['vcpus']])
415 sxpr.append(['cpumap', self.info['cpumap']])
416 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x),
417 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))])
419 if self.start_time:
420 up_time = time.time() - self.start_time
421 sxpr.append(['up_time', str(up_time) ])
422 sxpr.append(['start_time', str(self.start_time) ])
424 if self.channel:
425 sxpr.append(self.channel.sxpr())
426 console = self.getConsole()
427 if console:
428 sxpr.append(console.sxpr())
429 if self.restart_count:
430 sxpr.append(['restart_count', self.restart_count])
431 if self.restart_state:
432 sxpr.append(['restart_state', self.restart_state])
433 if self.restart_time:
434 sxpr.append(['restart_time', str(self.restart_time)])
435 devs = self.sxpr_devices()
436 if devs:
437 sxpr.append(devs)
438 if self.config:
439 sxpr.append(['config', self.config])
440 return sxpr
442 def sxpr_devices(self):
443 sxpr = []
444 for ty in self.getDeviceTypes():
445 devs = self.getDeviceSxprs(ty)
446 sxpr += devs
447 if sxpr:
448 sxpr.insert(0, 'devices')
449 else:
450 sxpr = None
451 return sxpr
453 def check_name(self, name):
454 """Check if a vm name is valid. Valid names contain alphabetic characters,
455 digits, or characters in '_-.:/+'.
456 The same name cannot be used for more than one vm at the same time.
458 @param name: name
459 @raise: VMerror if invalid
460 """
461 if self.recreate: return
462 if name is None or name == '':
463 raise VmError('missing vm name')
464 for c in name:
465 if c in string.digits: continue
466 if c in '_-.:/+': continue
467 if c in string.ascii_letters: continue
468 raise VmError('invalid vm name')
469 dominfo = domain_exists(name)
470 # When creating or rebooting, a domain with my name should not exist.
471 # When restoring, a domain with my name will exist, but it should have
472 # my domain id.
473 if not dominfo:
474 return
475 if dominfo.is_terminated():
476 return
477 if not self.dom or (dominfo.dom != self.dom):
478 raise VmError('vm name clash: ' + name)
480 def construct(self, config):
481 """Construct the vm instance from its configuration.
483 @param config: configuration
484 @raise: VmError on error
485 """
486 # todo - add support for scheduling params?
487 self.config = config
488 try:
489 # Initial domain create.
490 self.name = sxp.child_value(config, 'name')
491 self.check_name(self.name)
492 self.configure_cpus(config)
493 self.find_image_handler()
494 self.init_domain()
495 self.register_domain()
496 self.configure_bootloader()
498 # Create domain devices.
499 self.configure_backends()
500 self.configure_console()
501 self.configure_restart()
502 self.construct_image()
503 self.configure()
504 except Exception, ex:
505 # Catch errors, cleanup and re-raise.
506 print 'Domain construction error:', ex
507 import traceback
508 traceback.print_exc()
509 self.destroy()
510 raise
512 def register_domain(self):
513 xd = get_component('xen.xend.XendDomain')
514 xd._add_domain(self)
516 def configure_cpus(self, config):
517 try:
518 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
519 except:
520 raise VmError('invalid cpu weight')
521 self.memory = int(sxp.child_value(config, 'memory'))
522 if self.memory is None:
523 raise VmError('missing memory size')
524 cpu = sxp.child_value(config, 'cpu')
525 if self.recreate and self.dom and cpu is not None and int(cpu) >= 0:
526 xc.domain_pincpu(self.dom, 0, 1<<int(cpu))
527 try:
528 image = sxp.child_value(self.config, 'image')
529 vcpus = sxp.child_value(image, 'vcpus')
530 if vcpus:
531 self.vcpus = int(vcpus)
532 except:
533 raise VmError('invalid vcpus value')
535 def find_image_handler(self):
536 """Construct the boot image for the domain.
538 @return vm
539 """
540 image = sxp.child_value(self.config, 'image')
541 if image is None:
542 raise VmError('missing image')
543 image_name = sxp.name(image)
544 if image_name is None:
545 raise VmError('missing image name')
546 if image_name == "vmx":
547 self.is_vmx = True
548 image_handler = get_image_handler(image_name)
549 if image_handler is None:
550 raise VmError('unknown image type: ' + image_name)
551 self.image_handler = image_handler
552 return self
554 def construct_image(self):
555 image = sxp.child_value(self.config, 'image')
556 self.image_handler(self, image)
557 return self
559 def config_devices(self, name):
560 """Get a list of the 'device' nodes of a given type from the config.
562 @param name: device type
563 @type name: string
564 @return: device configs
565 @rtype: list
566 """
567 devices = []
568 for d in sxp.children(self.config, 'device'):
569 dev = sxp.child0(d)
570 if dev is None: continue
571 if name == sxp.name(dev):
572 devices.append(dev)
573 return devices
575 def get_device_savedinfo(self, type, index):
576 val = None
577 if self.savedinfo is None:
578 return val
579 devices = sxp.child(self.savedinfo, 'devices')
580 if devices is None:
581 return val
582 index = str(index)
583 for d in sxp.children(devices, type):
584 dindex = sxp.child_value(d, 'index')
585 if dindex is None: continue
586 if str(dindex) == index:
587 val = d
588 break
589 return val
591 def get_device_recreate(self, type, index):
592 return self.get_device_savedinfo(type, index) or self.recreate
594 def add_config(self, val):
595 """Add configuration data to a virtual machine.
597 @param val: data to add
598 """
599 self.configs.append(val)
601 def destroy(self):
602 """Completely destroy the vm.
603 """
604 self.cleanup()
605 return self.destroy_domain()
607 def destroy_domain(self):
608 """Destroy the vm's domain.
609 The domain will not finally go away unless all vm
610 devices have been released.
611 """
612 if self.channel:
613 self.channel.close()
614 self.channel = None
615 if self.dom is None: return 0
616 try:
617 return xc.domain_destroy(dom=self.dom)
618 except Exception, err:
619 log.exception("Domain destroy failed: %s", self.name)
621 def cleanup(self):
622 """Cleanup vm resources: release devices.
623 """
624 self.state = STATE_VM_TERMINATED
625 self.release_devices()
627 def is_terminated(self):
628 """Check if a domain has been terminated.
629 """
630 return self.state == STATE_VM_TERMINATED
632 def release_devices(self):
633 """Release all vm devices.
634 """
635 reboot = self.restart_pending()
636 for ctrl in self.getDeviceControllers():
637 if ctrl.isDestroyed(): continue
638 ctrl.destroyController(reboot=reboot)
639 if not reboot:
640 self.configs = []
641 self.ipaddrs = []
643 def show(self):
644 """Print virtual machine info.
645 """
646 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
647 print "image:"
648 sxp.show(self.image)
649 print
650 for val in self.configs:
651 print "config:"
652 sxp.show(val)
653 print
654 print "]"
656 def init_domain(self):
657 """Initialize the domain memory.
658 """
659 if self.recreate:
660 return
661 if self.start_time is None:
662 self.start_time = time.time()
663 if self.restore:
664 return
665 dom = self.dom or 0
666 memory = self.memory
667 try:
668 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
669 except:
670 raise VmError('invalid cpu')
671 cpu_weight = self.cpu_weight
672 memory = memory * 1024 + self.pgtable_size(memory)
673 dom = xc.domain_create(dom= dom)
674 if self.bootloader:
675 try:
676 if kernel: os.unlink(kernel)
677 if ramdisk: os.unlink(ramdisk)
678 except OSError, e:
679 log.warning('unable to unlink kernel/ramdisk: %s' %(e,))
681 if dom <= 0:
682 raise VmError('Creating domain failed: name=%s memory=%d'
683 % (self.name, memory))
684 xc.domain_setcpuweight(dom, cpu_weight)
685 xc.domain_setmaxmem(dom, memory)
686 xc.domain_memory_increase_reservation(dom, memory)
687 if cpu != -1:
688 xc.domain_pincpu(dom, 0, 1<<int(cpu))
689 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, self.name, memory)
690 self.setdom(dom)
692 def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap):
693 """Build the domain boot image.
694 """
695 if self.recreate or self.restore: return
696 if not os.path.isfile(kernel):
697 raise VmError('Kernel image does not exist: %s' % kernel)
698 if ramdisk and not os.path.isfile(ramdisk):
699 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
700 if len(cmdline) >= 256:
701 log.warning('kernel cmdline too long, domain %d', self.dom)
702 dom = self.dom
703 buildfn = getattr(xc, '%s_build' % ostype)
704 flags = 0
705 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
706 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
707 #todo generalise this
708 if ostype == "vmx":
709 log.debug('building vmx domain')
710 err = buildfn(dom = dom,
711 image = kernel,
712 control_evtchn = 0,
713 memsize = self.memory,
714 memmap = memmap,
715 cmdline = cmdline,
716 ramdisk = ramdisk,
717 flags = flags)
718 else:
719 log.debug('building dom with %d vcpus', self.vcpus)
720 err = buildfn(dom = dom,
721 image = kernel,
722 control_evtchn = self.channel.getRemotePort(),
723 cmdline = cmdline,
724 ramdisk = ramdisk,
725 flags = flags,
726 vcpus = self.vcpus)
727 if err != 0:
728 raise VmError('Building domain failed: type=%s dom=%d err=%d'
729 % (ostype, dom, err))
731 def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''):
732 """Create a domain. Builds the image but does not configure it.
734 @param ostype: OS type
735 @param kernel: kernel image
736 @param ramdisk: kernel ramdisk
737 @param cmdline: kernel commandline
738 """
740 self.create_channel()
741 self.build_domain(ostype, kernel, ramdisk, cmdline, memmap)
742 self.image = kernel
743 self.ramdisk = ramdisk
744 self.cmdline = cmdline
746 def create_channel(self):
747 """Create the control channel to the domain.
748 If saved info is available recreate the channel using the saved ports.
749 """
750 local = 0
751 remote = 1
752 if self.savedinfo:
753 info = sxp.child(self.savedinfo, "channel")
754 if info:
755 local = int(sxp.child_value(info, "local_port", 0))
756 remote = int(sxp.child_value(info, "remote_port", 1))
757 self.channel = channelFactory().openChannel(self.dom,
758 local_port=local,
759 remote_port=remote)
761 def create_configured_devices(self):
762 devices = sxp.children(self.config, 'device')
763 indexes = {}
764 for d in devices:
765 dev_config = sxp.child0(d)
766 if dev_config is None:
767 raise VmError('invalid device')
768 dev_type = sxp.name(dev_config)
769 ctrl_type = get_device_handler(dev_type)
770 if ctrl_type is None:
771 raise VmError('unknown device type: ' + dev_type)
772 # Keep track of device indexes by type, so we can fish
773 # out saved info for recreation.
774 idx = indexes.get(dev_type, -1)
775 idx += 1
776 indexes[ctrl_type] = idx
777 recreate = self.get_device_recreate(dev_type, idx)
778 self.createDevice(ctrl_type, dev_config, recreate=recreate)
780 def create_devices(self):
781 """Create the devices for a vm.
783 @raise: VmError for invalid devices
784 """
785 if self.rebooting():
786 for ctrl in self.getDeviceControllers():
787 ctrl.initController(reboot=True)
788 else:
789 self.create_configured_devices()
790 if self.is_vmx:
791 self.create_vmx_model()
793 def create_vmx_model(self):
794 #todo: remove special case for vmx
795 device_model = sxp.child_value(self.config, 'device_model')
796 if not device_model:
797 raise VmError("vmx: missing device model")
798 device_config = sxp.child_value(self.config, 'device_config')
799 if not device_config:
800 raise VmError("vmx: missing device config")
801 #todo: self.memory?
802 memory = sxp.child_value(self.config, "memory")
803 # Create an event channel
804 device_channel = channel.eventChannel(0, self.dom)
805 # see if a vncviewer was specified
806 # XXX RN: bit of a hack. should unify this, maybe stick in config space
807 vncconnect=""
808 image = sxp.child_value(self.config, "image")
809 args = sxp.child_value(image, "args")
810 if args:
811 arg_list = string.split(args)
812 for arg in arg_list:
813 al = string.split(arg, '=')
814 if al[0] == "VNC_VIEWER":
815 vncconnect=" -v %s" % al[1]
816 break
818 # Execute device model.
819 #todo: Error handling
820 # XXX RN: note that the order of args matter!
821 os.system(device_model
822 + " -f %s" % device_config
823 + vncconnect
824 + " -d %d" % self.dom
825 + " -p %d" % device_channel['port1']
826 + " -m %s" % memory)
828 def device_create(self, dev_config):
829 """Create a new device.
831 @param dev_config: device configuration
832 """
833 dev_type = sxp.name(dev_config)
834 dev = self.createDevice(self, dev_config, change=True)
835 self.config.append(['device', dev.getConfig()])
836 return dev.sxpr()
838 def device_configure(self, dev_config, idx):
839 """Configure an existing device.
841 @param dev_config: device configuration
842 @param idx: device index
843 """
844 type = sxp.name(dev_config)
845 dev = self.getDeviceByIndex(type, idx)
846 if not dev:
847 raise VmError('invalid device: %s %s' % (type, idx))
848 old_config = dev.getConfig()
849 new_config = dev.configure(dev_config, change=True)
850 # Patch new config into vm config.
851 new_full_config = ['device', new_config]
852 old_full_config = ['device', old_config]
853 old_index = self.config.index(old_full_config)
854 self.config[old_index] = new_full_config
855 return new_config
857 def device_refresh(self, type, idx):
858 """Refresh a device.
860 @param type: device type
861 @param idx: device index
862 """
863 dev = self.getDeviceByIndex(type, idx)
864 if not dev:
865 raise VmError('invalid device: %s %s' % (type, idx))
866 dev.refresh()
868 def device_delete(self, type, idx):
869 """Destroy and remove a device.
871 @param type: device type
872 @param idx: device index
873 """
874 dev = self.getDeviceByIndex(type, idx)
875 if not dev:
876 raise VmError('invalid device: %s %s' % (type, idx))
877 dev_config = dev.getConfig()
878 if dev_config:
879 self.config.remove(['device', dev_config])
880 self.deleteDevice(type, dev.getId())
882 def configure_bootloader(self):
883 """Configure boot loader.
884 """
885 bl = sxp.child_value(self.config, "bootloader")
886 if bl is not None:
887 self.bootloader = bl
889 def configure_console(self):
890 """Configure the vm console port.
891 """
892 x = sxp.child_value(self.config, 'console')
893 if x:
894 try:
895 port = int(x)
896 except:
897 raise VmError('invalid console:' + str(x))
898 self.console_port = port
900 def configure_restart(self):
901 """Configure the vm restart mode.
902 """
903 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
904 if r not in restart_modes:
905 raise VmError('invalid restart mode: ' + str(r))
906 self.restart_mode = r;
908 def restart_needed(self, reason):
909 """Determine if the vm needs to be restarted when shutdown
910 for the given reason.
912 @param reason: shutdown reason
913 @return True if needs restart, False otherwise
914 """
915 if self.restart_mode == RESTART_NEVER:
916 return False
917 if self.restart_mode == RESTART_ALWAYS:
918 return True
919 if self.restart_mode == RESTART_ONREBOOT:
920 return reason == 'reboot'
921 return False
923 def restart_cancel(self):
924 """Cancel a vm restart.
925 """
926 self.restart_state = None
928 def restarting(self):
929 """Put the vm into restart mode.
930 """
931 self.restart_state = STATE_RESTART_PENDING
933 def restart_pending(self):
934 """Test if the vm has a pending restart.
935 """
936 return self.restart_state == STATE_RESTART_PENDING
938 def rebooting(self):
939 return self.restart_state == STATE_RESTART_BOOTING
941 def restart_check(self):
942 """Check if domain restart is OK.
943 To prevent restart loops, raise an error if it is
944 less than MINIMUM_RESTART_TIME seconds since the last restart.
945 """
946 tnow = time.time()
947 if self.restart_time is not None:
948 tdelta = tnow - self.restart_time
949 if tdelta < self.MINIMUM_RESTART_TIME:
950 self.restart_cancel()
951 msg = 'VM %s restarting too fast' % self.name
952 log.error(msg)
953 raise VmError(msg)
954 self.restart_time = tnow
955 self.restart_count += 1
957 def restart(self):
958 """Restart the domain after it has exited.
959 Reuses the domain id and console port.
961 """
962 try:
963 self.state = STATE_VM_OK
964 self.shutdown_pending = None
965 self.restart_check()
966 self.restart_state = STATE_RESTART_BOOTING
967 if self.bootloader:
968 self.config = self.bootloader_config()
969 self.construct(self.config)
970 finally:
971 self.restart_state = None
973 def bootloader_config(self):
974 # if we're restarting with a bootloader, we need to run it
975 # FIXME: this assumes the disk is the first device and
976 # that we're booting from the first disk
977 blcfg = None
978 # FIXME: this assumes that we want to use the first disk
979 dev = sxp.child_value(self.config, "device")
980 if dev:
981 disk = sxp.child_value(dev, "uname")
982 fn = blkdev_uname_to_file(disk)
983 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
984 if blcfg is None:
985 msg = "Had a bootloader specified, but can't find disk"
986 log.error(msg)
987 raise VmError(msg)
988 config = sxp.merge(['vm', blconfig ], self.config)
989 return config
991 def configure_backends(self):
992 """Set configuration flags if the vm is a backend for netif or blkif.
993 Configure the backends to use for vbd and vif if specified.
994 """
995 for c in sxp.children(self.config, 'backend'):
996 v = sxp.child0(c)
997 name = sxp.name(v)
998 if name == 'blkif':
999 self.blkif_backend = True
1000 elif name == 'netif':
1001 self.netif_backend = True
1002 elif name == 'usbif':
1003 self.usbif_backend = True
1004 else:
1005 raise VmError('invalid backend type:' + str(name))
1007 def configure(self):
1008 """Configure a vm.
1010 """
1011 self.configure_fields()
1012 self.create_console()
1013 self.create_devices()
1014 self.create_blkif()
1016 def create_console(self):
1017 console = self.getConsole()
1018 if not console:
1019 config = ['console']
1020 if self.console_port:
1021 config.append(['console_port', self.console_port])
1022 console = self.createDevice('console', config)
1023 return console
1025 def getConsole(self):
1026 console_ctrl = self.getDeviceController("console", error=False)
1027 if console_ctrl:
1028 return console_ctrl.getDevice(0)
1029 return None
1031 def create_blkif(self):
1032 """Create the block device interface (blkif) for the vm.
1033 The vm needs a blkif even if it doesn't have any disks
1034 at creation time, for example when it uses NFS root.
1036 """
1037 blkif = self.getDeviceController("vbd", error=False)
1038 if not blkif:
1039 blkif = self.createDeviceController("vbd")
1040 backend = blkif.getBackend(0)
1041 backend.connect(recreate=self.recreate)
1043 def dom_construct(self, dom, config):
1044 """Construct a vm for an existing domain.
1046 @param dom: domain id
1047 @param config: domain configuration
1048 """
1049 d = dom_get(dom)
1050 if not d:
1051 raise VmError("Domain not found: %d" % dom)
1052 try:
1053 self.restore = True
1054 self.setdom(dom)
1055 self.memory = d['mem_kb']/1024
1056 self.construct(config)
1057 finally:
1058 self.restore = False
1060 def configure_fields(self):
1061 """Process the vm configuration fields using the registered handlers.
1062 """
1063 index = {}
1064 for field in sxp.children(self.config):
1065 field_name = sxp.name(field)
1066 field_index = index.get(field_name, 0)
1067 field_handler = get_config_handler(field_name)
1068 # Ignore unknown fields. Warn?
1069 if field_handler:
1070 v = field_handler(self, self.config, field, field_index)
1071 else:
1072 log.warning("Unknown config field %s", field_name)
1073 index[field_name] = field_index + 1
1075 def pgtable_size(self, memory):
1076 """Return the size of memory needed for 1:1 page tables for physical
1077 mode.
1079 @param memory: size in MB
1080 @return size in KB
1081 """
1082 if self.is_vmx:
1083 # Logic x86-32 specific.
1084 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
1085 return (1 + ((memory + 3) >> 2)) * 4
1086 return 0
1088 def mem_target_set(self, target):
1089 """Set domain memory target in pages.
1090 """
1091 if self.channel:
1092 msg = messages.packMsg('mem_request_t', { 'target' : target * (1 << 8)} )
1093 self.channel.writeRequest(msg)
1095 def shutdown(self, reason, key=0):
1096 msgtype = shutdown_messages.get(reason)
1097 if not msgtype:
1098 raise XendError('invalid reason:' + reason)
1099 extra = {}
1100 if reason == 'sysrq':
1101 extra['key'] = key
1102 if self.channel:
1103 msg = messages.packMsg(msgtype, extra)
1104 self.channel.writeRequest(msg)
1105 if not reason in ['suspend', 'sysrq']:
1106 self.shutdown_pending = {'start':time.time(), 'reason':reason,
1107 'key':key}
1109 def shutdown_time_left(self, timeout):
1110 if not self.shutdown_pending:
1111 return 0
1112 return timeout - (time.time() - self.shutdown_pending['start'])
1114 def vm_image_linux(vm, image):
1115 """Create a VM for a linux image.
1117 @param name: vm name
1118 @param memory: vm memory
1119 @param image: image config
1120 @return: vm
1121 """
1122 kernel = sxp.child_value(image, "kernel")
1123 cmdline = ""
1124 ip = sxp.child_value(image, "ip", None)
1125 if ip:
1126 cmdline += " ip=" + ip
1127 root = sxp.child_value(image, "root")
1128 if root:
1129 cmdline += " root=" + root
1130 args = sxp.child_value(image, "args")
1131 if args:
1132 cmdline += " " + args
1133 ramdisk = sxp.child_value(image, "ramdisk", '')
1134 log.debug("creating linux domain with cmdline: %s" %(cmdline,))
1135 vm.create_domain("linux", kernel, ramdisk, cmdline)
1136 return vm
1138 def vm_image_plan9(vm, image):
1139 """Create a VM for a Plan 9 image.
1141 name vm name
1142 memory vm memory
1143 image image config
1145 returns vm
1146 """
1147 kernel = sxp.child_value(image, "kernel")
1148 cmdline = ""
1149 ip = sxp.child_value(image, "ip", "dhcp")
1150 if ip:
1151 cmdline += "ip=" + ip
1152 root = sxp.child_value(image, "root")
1153 if root:
1154 cmdline += "root=" + root
1155 args = sxp.child_value(image, "args")
1156 if args:
1157 cmdline += " " + args
1158 ramdisk = sxp.child_value(image, "ramdisk", '')
1159 log.debug("creating plan9 domain with cmdline: %s" %(cmdline,))
1160 vm.create_domain("plan9", kernel, ramdisk, cmdline)
1161 return vm
1163 def vm_image_vmx(vm, image):
1164 """Create a VM for the VMX environment.
1166 @param name: vm name
1167 @param memory: vm memory
1168 @param image: image config
1169 @return: vm
1170 """
1171 kernel = sxp.child_value(image, "kernel")
1172 cmdline = ""
1173 ip = sxp.child_value(image, "ip", "dhcp")
1174 if ip:
1175 cmdline += " ip=" + ip
1176 root = sxp.child_value(image, "root")
1177 if root:
1178 cmdline += " root=" + root
1179 args = sxp.child_value(image, "args")
1180 if args:
1181 cmdline += " " + args
1182 ramdisk = sxp.child_value(image, "ramdisk", '')
1183 memmap = sxp.child_value(vm.config, "memmap", '')
1184 memmap = sxp.parse(open(memmap))[0]
1185 from xen.util.memmap import memmap_parse
1186 memmap = memmap_parse(memmap)
1187 log.debug("creating vmx domain with cmdline: %s" %(cmdline,))
1188 vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap)
1189 return vm
1191 def vm_field_ignore(vm, config, val, index):
1192 """Dummy config field handler used for fields with built-in handling.
1194 @param vm: virtual machine
1195 @param config: vm config
1196 @param val: config field
1197 @param index: field index
1198 """
1199 pass
1201 def vm_field_maxmem(vm, config, val, index):
1202 """Configure vm memory limit.
1204 @param vm: virtual machine
1205 @param config: vm config
1206 @param val: config field
1207 @param index: field index
1208 """
1209 maxmem = sxp.child0(val)
1210 if maxmem is None:
1211 maxmem = vm.memory
1212 try:
1213 maxmem = int(maxmem)
1214 except:
1215 raise VmError("invalid maxmem: " + str(maxmem))
1216 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1218 #============================================================================
1219 # Register image handlers.
1220 add_image_handler('linux', vm_image_linux)
1221 add_image_handler('plan9', vm_image_plan9)
1222 add_image_handler('vmx', vm_image_vmx)
1224 # Ignore the fields we already handle.
1225 add_config_handler('name', vm_field_ignore)
1226 add_config_handler('memory', vm_field_ignore)
1227 add_config_handler('cpu', vm_field_ignore)
1228 add_config_handler('cpu_weight', vm_field_ignore)
1229 add_config_handler('console', vm_field_ignore)
1230 add_config_handler('restart', vm_field_ignore)
1231 add_config_handler('image', vm_field_ignore)
1232 add_config_handler('device', vm_field_ignore)
1233 add_config_handler('backend', vm_field_ignore)
1234 add_config_handler('vcpus', vm_field_ignore)
1235 add_config_handler('bootloader', vm_field_ignore)
1237 # Register other config handlers.
1238 add_config_handler('maxmem', vm_field_maxmem)
1240 #============================================================================
1241 # Register device controllers and their device config types.
1243 from server import console
1244 controller.addDevControllerClass("console", console.ConsoleController)
1246 from server import blkif
1247 controller.addDevControllerClass("vbd", blkif.BlkifController)
1248 add_device_handler("vbd", "vbd")
1250 from server import netif
1251 controller.addDevControllerClass("vif", netif.NetifController)
1252 add_device_handler("vif", "vif")
1254 from server import pciif
1255 controller.addDevControllerClass("pci", pciif.PciController)
1256 add_device_handler("pci", "pci")
1258 from xen.xend.server import usbif
1259 controller.addDevControllerClass("usb", usbif.UsbifController)
1260 add_device_handler("usb", "usb")
1262 #============================================================================