ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 5235:1af7f0708b54

bitkeeper revision 1.1615 (429c80cbvtnzneyg7GyZAmZlQNm-Yg)

XendDomainInfo.py:
g/c vm_restore: no longer used.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Tue May 31 15:20:43 2005 +0000 (2005-05-31)
parents abe0fd18962c
children 519301e369cc
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 dom_get(dom):
196 """Get info from xen for an existing domain.
198 @param dom: domain id
199 @return: info or None
200 """
201 domlist = xc.domain_getinfo(dom, 1)
202 if domlist and dom == domlist[0]['dom']:
203 return domlist[0]
204 return None
206 class XendDomainInfo:
207 """Virtual machine object."""
209 """Minimum time between domain restarts in seconds.
210 """
211 MINIMUM_RESTART_TIME = 20
213 def __init__(self):
214 self.recreate = 0
215 self.restore = 0
216 self.config = None
217 self.id = None
218 self.dom = None
219 self.cpu_weight = 1
220 self.start_time = None
221 self.name = None
222 self.memory = None
223 self.image = None
224 self.ramdisk = None
225 self.cmdline = None
227 self.channel = None
228 self.controllers = {}
230 self.configs = []
232 self.info = None
233 self.ipaddrs = []
234 self.blkif_backend = False
235 self.netif_backend = False
236 #todo: state: running, suspended
237 self.state = STATE_VM_OK
238 self.state_updated = threading.Condition()
239 self.shutdown_pending = None
241 #todo: set to migrate info if migrating
242 self.migrate = None
244 self.restart_mode = RESTART_ONREBOOT
245 self.restart_state = None
246 self.restart_time = None
247 self.restart_count = 0
249 self.console_port = None
250 self.savedinfo = None
251 self.image_handler = None
252 self.is_vmx = False
253 self.vcpus = 1
254 self.bootloader = None
256 def setdom(self, dom):
257 """Set the domain id.
259 @param dom: domain id
260 """
261 self.dom = int(dom)
262 self.id = str(dom)
264 def getDomain(self):
265 return self.dom
267 def getName(self):
268 return self.name
270 def getChannel(self):
271 return self.channel
273 def update(self, info):
274 """Update with info from xc.domain_getinfo().
275 """
276 self.info = info
277 self.memory = self.info['mem_kb'] / 1024
279 def state_set(self, state):
280 self.state_updated.acquire()
281 if self.state != state:
282 self.state = state
283 self.state_updated.notifyAll()
284 self.state_updated.release()
286 def state_wait(self, state):
287 self.state_updated.acquire()
288 while self.state != state:
289 self.state_updated.wait()
290 self.state_updated.release()
292 def __str__(self):
293 s = "domain"
294 s += " id=" + self.id
295 s += " name=" + self.name
296 s += " memory=" + str(self.memory)
297 console = self.getConsole()
298 if console:
299 s += " console=" + str(console.console_port)
300 if self.image:
301 s += " image=" + self.image
302 s += ""
303 return s
305 __repr__ = __str__
307 def getDeviceTypes(self):
308 return self.controllers.keys()
310 def getDeviceControllers(self):
311 return self.controllers.values()
313 def getDeviceController(self, type, error=True):
314 ctrl = self.controllers.get(type)
315 if not ctrl and error:
316 raise XendError("invalid device type:" + type)
317 return ctrl
319 def findDeviceController(self, type):
320 return (self.getDeviceController(type, error=False)
321 or self.createDeviceController(type))
323 def createDeviceController(self, type):
324 ctrl = controller.createDevController(type, self, recreate=self.recreate)
325 self.controllers[type] = ctrl
326 return ctrl
328 def createDevice(self, type, devconfig, recreate=False):
329 ctrl = self.findDeviceController(type)
330 return ctrl.createDevice(devconfig, recreate=self.recreate)
332 def configureDevice(self, type, id, devconfig):
333 ctrl = self.getDeviceController(type)
334 return ctrl.configureDevice(id, devconfig)
336 def destroyDevice(self, type, id, change=False, reboot=False):
337 ctrl = self.getDeviceController(type)
338 return ctrl.destroyDevice(id, change=change, reboot=reboot)
340 def deleteDevice(self, type, id):
341 ctrl = self.getDeviceController(type)
342 return ctrl.deleteDevice(id)
344 def getDevice(self, type, id):
345 ctrl = self.getDeviceController(type)
346 return ctrl.getDevice(id)
348 def getDeviceByIndex(self, type, idx):
349 ctrl = self.getDeviceController(type)
350 return ctrl.getDeviceByIndex(idx)
352 def getDeviceConfig(self, type, id):
353 ctrl = self.getDeviceController(type)
354 return ctrl.getDeviceConfig(id)
356 def getDeviceIds(self, type):
357 ctrl = self.getDeviceController(type)
358 return ctrl.getDeviceIds()
360 def getDeviceIndexes(self, type):
361 ctrl = self.getDeviceController(type)
362 return ctrl.getDeviceIndexes()
364 def getDeviceConfigs(self, type):
365 ctrl = self.getDeviceController(type)
366 return ctrl.getDeviceConfigs()
368 def getDeviceSxprs(self, type):
369 ctrl = self.getDeviceController(type)
370 return ctrl.getDeviceSxprs()
372 def sxpr(self):
373 sxpr = ['domain',
374 ['id', self.id],
375 ['name', self.name],
376 ['memory', self.memory] ]
378 if self.info:
379 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
380 run = (self.info['running'] and 'r') or '-'
381 block = (self.info['blocked'] and 'b') or '-'
382 pause = (self.info['paused'] and 'p') or '-'
383 shut = (self.info['shutdown'] and 's') or '-'
384 crash = (self.info['crashed'] and 'c') or '-'
385 state = run + block + pause + shut + crash
386 sxpr.append(['state', state])
387 if self.info['shutdown']:
388 reason = shutdown_reason(self.info['shutdown_reason'])
389 sxpr.append(['shutdown_reason', reason])
390 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
391 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
392 sxpr.append(['vcpus', self.info['vcpus']])
393 sxpr.append(['cpumap', self.info['cpumap']])
394 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x),
395 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))])
397 if self.start_time:
398 up_time = time.time() - self.start_time
399 sxpr.append(['up_time', str(up_time) ])
400 sxpr.append(['start_time', str(self.start_time) ])
402 if self.channel:
403 sxpr.append(self.channel.sxpr())
404 console = self.getConsole()
405 if console:
406 sxpr.append(console.sxpr())
407 if self.restart_count:
408 sxpr.append(['restart_count', self.restart_count])
409 if self.restart_state:
410 sxpr.append(['restart_state', self.restart_state])
411 if self.restart_time:
412 sxpr.append(['restart_time', str(self.restart_time)])
413 devs = self.sxpr_devices()
414 if devs:
415 sxpr.append(devs)
416 if self.config:
417 sxpr.append(['config', self.config])
418 return sxpr
420 def sxpr_devices(self):
421 sxpr = []
422 for ty in self.getDeviceTypes():
423 devs = self.getDeviceSxprs(ty)
424 sxpr += devs
425 if sxpr:
426 sxpr.insert(0, 'devices')
427 else:
428 sxpr = None
429 return sxpr
431 def check_name(self, name):
432 """Check if a vm name is valid. Valid names contain alphabetic characters,
433 digits, or characters in '_-.:/+'.
434 The same name cannot be used for more than one vm at the same time.
436 @param name: name
437 @raise: VMerror if invalid
438 """
439 if self.recreate: return
440 if name is None or name == '':
441 raise VmError('missing vm name')
442 for c in name:
443 if c in string.digits: continue
444 if c in '_-.:/+': continue
445 if c in string.ascii_letters: continue
446 raise VmError('invalid vm name')
447 dominfo = domain_exists(name)
448 # When creating or rebooting, a domain with my name should not exist.
449 # When restoring, a domain with my name will exist, but it should have
450 # my domain id.
451 if not dominfo:
452 return
453 if dominfo.is_terminated():
454 return
455 if not self.dom or (dominfo.dom != self.dom):
456 raise VmError('vm name clash: ' + name)
458 def construct(self, config):
459 """Construct the vm instance from its configuration.
461 @param config: configuration
462 @raise: VmError on error
463 """
464 # todo - add support for scheduling params?
465 self.config = config
466 try:
467 # Initial domain create.
468 self.name = sxp.child_value(config, 'name')
469 self.check_name(self.name)
470 self.configure_cpus(config)
471 self.find_image_handler()
472 self.init_domain()
473 self.register_domain()
474 self.configure_bootloader()
476 # Create domain devices.
477 self.configure_backends()
478 self.configure_console()
479 self.configure_restart()
480 self.construct_image()
481 self.configure()
482 except Exception, ex:
483 # Catch errors, cleanup and re-raise.
484 print 'Domain construction error:', ex
485 import traceback
486 traceback.print_exc()
487 self.destroy()
488 raise
490 def register_domain(self):
491 xd = get_component('xen.xend.XendDomain')
492 xd._add_domain(self)
494 def configure_cpus(self, config):
495 try:
496 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
497 except:
498 raise VmError('invalid cpu weight')
499 self.memory = int(sxp.child_value(config, 'memory'))
500 if self.memory is None:
501 raise VmError('missing memory size')
502 cpu = sxp.child_value(config, 'cpu')
503 if self.recreate and self.dom and cpu is not None and int(cpu) >= 0:
504 xc.domain_pincpu(self.dom, 0, 1<<int(cpu))
505 try:
506 image = sxp.child_value(self.config, 'image')
507 vcpus = sxp.child_value(image, 'vcpus')
508 if vcpus:
509 self.vcpus = int(vcpus)
510 except:
511 raise VmError('invalid vcpus value')
513 def find_image_handler(self):
514 """Construct the boot image for the domain.
516 @return vm
517 """
518 image = sxp.child_value(self.config, 'image')
519 if image is None:
520 raise VmError('missing image')
521 image_name = sxp.name(image)
522 if image_name is None:
523 raise VmError('missing image name')
524 if image_name == "vmx":
525 self.is_vmx = True
526 image_handler = get_image_handler(image_name)
527 if image_handler is None:
528 raise VmError('unknown image type: ' + image_name)
529 self.image_handler = image_handler
530 return self
532 def construct_image(self):
533 image = sxp.child_value(self.config, 'image')
534 self.image_handler(self, image)
535 return self
537 def config_devices(self, name):
538 """Get a list of the 'device' nodes of a given type from the config.
540 @param name: device type
541 @type name: string
542 @return: device configs
543 @rtype: list
544 """
545 devices = []
546 for d in sxp.children(self.config, 'device'):
547 dev = sxp.child0(d)
548 if dev is None: continue
549 if name == sxp.name(dev):
550 devices.append(dev)
551 return devices
553 def get_device_savedinfo(self, type, index):
554 val = None
555 if self.savedinfo is None:
556 return val
557 devices = sxp.child(self.savedinfo, 'devices')
558 if devices is None:
559 return val
560 index = str(index)
561 for d in sxp.children(devices, type):
562 dindex = sxp.child_value(d, 'index')
563 if dindex is None: continue
564 if str(dindex) == index:
565 val = d
566 break
567 return val
569 def get_device_recreate(self, type, index):
570 return self.get_device_savedinfo(type, index) or self.recreate
572 def add_config(self, val):
573 """Add configuration data to a virtual machine.
575 @param val: data to add
576 """
577 self.configs.append(val)
579 def destroy(self):
580 """Completely destroy the vm.
581 """
582 self.cleanup()
583 return self.destroy_domain()
585 def destroy_domain(self):
586 """Destroy the vm's domain.
587 The domain will not finally go away unless all vm
588 devices have been released.
589 """
590 if self.channel:
591 self.channel.close()
592 self.channel = None
593 if self.dom is None: return 0
594 try:
595 return xc.domain_destroy(dom=self.dom)
596 except Exception, err:
597 log.exception("Domain destroy failed: %s", self.name)
599 def cleanup(self):
600 """Cleanup vm resources: release devices.
601 """
602 self.state = STATE_VM_TERMINATED
603 self.release_devices()
605 def is_terminated(self):
606 """Check if a domain has been terminated.
607 """
608 return self.state == STATE_VM_TERMINATED
610 def release_devices(self):
611 """Release all vm devices.
612 """
613 reboot = self.restart_pending()
614 for ctrl in self.getDeviceControllers():
615 if ctrl.isDestroyed(): continue
616 ctrl.destroyController(reboot=reboot)
617 if not reboot:
618 self.configs = []
619 self.ipaddrs = []
621 def show(self):
622 """Print virtual machine info.
623 """
624 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
625 print "image:"
626 sxp.show(self.image)
627 print
628 for val in self.configs:
629 print "config:"
630 sxp.show(val)
631 print
632 print "]"
634 def init_domain(self):
635 """Initialize the domain memory.
636 """
637 if self.recreate:
638 return
639 if self.start_time is None:
640 self.start_time = time.time()
641 if self.restore:
642 return
643 dom = self.dom or 0
644 memory = self.memory
645 try:
646 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
647 except:
648 raise VmError('invalid cpu')
649 cpu_weight = self.cpu_weight
650 memory = memory * 1024 + self.pgtable_size(memory)
651 dom = xc.domain_create(dom= dom)
652 if self.bootloader:
653 try:
654 if kernel: os.unlink(kernel)
655 if ramdisk: os.unlink(ramdisk)
656 except OSError, e:
657 log.warning('unable to unlink kernel/ramdisk: %s' %(e,))
659 if dom <= 0:
660 raise VmError('Creating domain failed: name=%s memory=%d'
661 % (self.name, memory))
662 xc.domain_setcpuweight(dom, cpu_weight)
663 xc.domain_setmaxmem(dom, memory)
664 xc.domain_memory_increase_reservation(dom, memory)
665 if cpu != -1:
666 xc.domain_pincpu(dom, 0, 1<<int(cpu))
667 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, self.name, memory)
668 self.setdom(dom)
670 def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap):
671 """Build the domain boot image.
672 """
673 if self.recreate or self.restore: return
674 if not os.path.isfile(kernel):
675 raise VmError('Kernel image does not exist: %s' % kernel)
676 if ramdisk and not os.path.isfile(ramdisk):
677 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
678 if len(cmdline) >= 256:
679 log.warning('kernel cmdline too long, domain %d', self.dom)
680 dom = self.dom
681 buildfn = getattr(xc, '%s_build' % ostype)
682 flags = 0
683 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
684 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
685 #todo generalise this
686 if ostype == "vmx":
687 log.debug('building vmx domain')
688 err = buildfn(dom = dom,
689 image = kernel,
690 control_evtchn = 0,
691 memsize = self.memory,
692 memmap = memmap,
693 cmdline = cmdline,
694 ramdisk = ramdisk,
695 flags = flags)
696 else:
697 log.debug('building dom with %d vcpus', self.vcpus)
698 err = buildfn(dom = dom,
699 image = kernel,
700 control_evtchn = self.channel.getRemotePort(),
701 cmdline = cmdline,
702 ramdisk = ramdisk,
703 flags = flags,
704 vcpus = self.vcpus)
705 if err != 0:
706 raise VmError('Building domain failed: type=%s dom=%d err=%d'
707 % (ostype, dom, err))
709 def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''):
710 """Create a domain. Builds the image but does not configure it.
712 @param ostype: OS type
713 @param kernel: kernel image
714 @param ramdisk: kernel ramdisk
715 @param cmdline: kernel commandline
716 """
718 self.create_channel()
719 self.build_domain(ostype, kernel, ramdisk, cmdline, memmap)
720 self.image = kernel
721 self.ramdisk = ramdisk
722 self.cmdline = cmdline
724 def create_channel(self):
725 """Create the control channel to the domain.
726 If saved info is available recreate the channel using the saved ports.
727 """
728 local = 0
729 remote = 1
730 if self.savedinfo:
731 info = sxp.child(self.savedinfo, "channel")
732 if info:
733 local = int(sxp.child_value(info, "local_port", 0))
734 remote = int(sxp.child_value(info, "remote_port", 1))
735 self.channel = channelFactory().openChannel(self.dom,
736 local_port=local,
737 remote_port=remote)
739 def create_configured_devices(self):
740 devices = sxp.children(self.config, 'device')
741 indexes = {}
742 for d in devices:
743 dev_config = sxp.child0(d)
744 if dev_config is None:
745 raise VmError('invalid device')
746 dev_type = sxp.name(dev_config)
747 ctrl_type = get_device_handler(dev_type)
748 if ctrl_type is None:
749 raise VmError('unknown device type: ' + dev_type)
750 # Keep track of device indexes by type, so we can fish
751 # out saved info for recreation.
752 idx = indexes.get(dev_type, -1)
753 idx += 1
754 indexes[ctrl_type] = idx
755 recreate = self.get_device_recreate(dev_type, idx)
756 self.createDevice(ctrl_type, dev_config, recreate=recreate)
758 def create_devices(self):
759 """Create the devices for a vm.
761 @raise: VmError for invalid devices
762 """
763 if self.rebooting():
764 for ctrl in self.getDeviceControllers():
765 ctrl.initController(reboot=True)
766 else:
767 self.create_configured_devices()
768 if self.is_vmx:
769 self.create_vmx_model()
771 def create_vmx_model(self):
772 #todo: remove special case for vmx
773 device_model = sxp.child_value(self.config, 'device_model')
774 if not device_model:
775 raise VmError("vmx: missing device model")
776 device_config = sxp.child_value(self.config, 'device_config')
777 if not device_config:
778 raise VmError("vmx: missing device config")
779 #todo: self.memory?
780 memory = sxp.child_value(self.config, "memory")
781 # Create an event channel
782 device_channel = channel.eventChannel(0, self.dom)
783 # see if a vncviewer was specified
784 # XXX RN: bit of a hack. should unify this, maybe stick in config space
785 vncconnect=""
786 image = sxp.child_value(self.config, "image")
787 args = sxp.child_value(image, "args")
788 if args:
789 arg_list = string.split(args)
790 for arg in arg_list:
791 al = string.split(arg, '=')
792 if al[0] == "VNC_VIEWER":
793 vncconnect=" -v %s" % al[1]
794 break
796 # Execute device model.
797 #todo: Error handling
798 # XXX RN: note that the order of args matter!
799 os.system(device_model
800 + " -f %s" % device_config
801 + vncconnect
802 + " -d %d" % self.dom
803 + " -p %d" % device_channel['port1']
804 + " -m %s" % memory)
806 def device_create(self, dev_config):
807 """Create a new device.
809 @param dev_config: device configuration
810 """
811 dev_type = sxp.name(dev_config)
812 dev = self.createDevice(self, dev_config, change=True)
813 self.config.append(['device', dev.getConfig()])
814 return dev.sxpr()
816 def device_configure(self, dev_config, idx):
817 """Configure an existing device.
819 @param dev_config: device configuration
820 @param idx: device index
821 """
822 type = sxp.name(dev_config)
823 dev = self.getDeviceByIndex(type, idx)
824 if not dev:
825 raise VmError('invalid device: %s %s' % (type, idx))
826 old_config = dev.getConfig()
827 new_config = dev.configure(dev_config, change=True)
828 # Patch new config into vm config.
829 new_full_config = ['device', new_config]
830 old_full_config = ['device', old_config]
831 old_index = self.config.index(old_full_config)
832 self.config[old_index] = new_full_config
833 return new_config
835 def device_refresh(self, type, idx):
836 """Refresh a device.
838 @param type: device type
839 @param idx: device index
840 """
841 dev = self.getDeviceByIndex(type, idx)
842 if not dev:
843 raise VmError('invalid device: %s %s' % (type, idx))
844 dev.refresh()
846 def device_delete(self, type, idx):
847 """Destroy and remove a device.
849 @param type: device type
850 @param idx: device index
851 """
852 dev = self.getDeviceByIndex(type, idx)
853 if not dev:
854 raise VmError('invalid device: %s %s' % (type, idx))
855 dev_config = dev.getConfig()
856 if dev_config:
857 self.config.remove(['device', dev_config])
858 self.deleteDevice(type, dev.getId())
860 def configure_bootloader(self):
861 """Configure boot loader.
862 """
863 bl = sxp.child_value(self.config, "bootloader")
864 if bl is not None:
865 self.bootloader = bl
867 def configure_console(self):
868 """Configure the vm console port.
869 """
870 x = sxp.child_value(self.config, 'console')
871 if x:
872 try:
873 port = int(x)
874 except:
875 raise VmError('invalid console:' + str(x))
876 self.console_port = port
878 def configure_restart(self):
879 """Configure the vm restart mode.
880 """
881 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
882 if r not in restart_modes:
883 raise VmError('invalid restart mode: ' + str(r))
884 self.restart_mode = r;
886 def restart_needed(self, reason):
887 """Determine if the vm needs to be restarted when shutdown
888 for the given reason.
890 @param reason: shutdown reason
891 @return True if needs restart, False otherwise
892 """
893 if self.restart_mode == RESTART_NEVER:
894 return False
895 if self.restart_mode == RESTART_ALWAYS:
896 return True
897 if self.restart_mode == RESTART_ONREBOOT:
898 return reason == 'reboot'
899 return False
901 def restart_cancel(self):
902 """Cancel a vm restart.
903 """
904 self.restart_state = None
906 def restarting(self):
907 """Put the vm into restart mode.
908 """
909 self.restart_state = STATE_RESTART_PENDING
911 def restart_pending(self):
912 """Test if the vm has a pending restart.
913 """
914 return self.restart_state == STATE_RESTART_PENDING
916 def rebooting(self):
917 return self.restart_state == STATE_RESTART_BOOTING
919 def restart_check(self):
920 """Check if domain restart is OK.
921 To prevent restart loops, raise an error if it is
922 less than MINIMUM_RESTART_TIME seconds since the last restart.
923 """
924 tnow = time.time()
925 if self.restart_time is not None:
926 tdelta = tnow - self.restart_time
927 if tdelta < self.MINIMUM_RESTART_TIME:
928 self.restart_cancel()
929 msg = 'VM %s restarting too fast' % self.name
930 log.error(msg)
931 raise VmError(msg)
932 self.restart_time = tnow
933 self.restart_count += 1
935 def restart(self):
936 """Restart the domain after it has exited.
937 Reuses the domain id and console port.
939 """
940 try:
941 self.state = STATE_VM_OK
942 self.shutdown_pending = None
943 self.restart_check()
944 self.restart_state = STATE_RESTART_BOOTING
945 if self.bootloader:
946 self.config = self.bootloader_config()
947 self.construct(self.config)
948 finally:
949 self.restart_state = None
951 def bootloader_config(self):
952 # if we're restarting with a bootloader, we need to run it
953 # FIXME: this assumes the disk is the first device and
954 # that we're booting from the first disk
955 blcfg = None
956 # FIXME: this assumes that we want to use the first disk
957 dev = sxp.child_value(self.config, "device")
958 if dev:
959 disk = sxp.child_value(dev, "uname")
960 fn = blkdev_uname_to_file(disk)
961 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
962 if blcfg is None:
963 msg = "Had a bootloader specified, but can't find disk"
964 log.error(msg)
965 raise VmError(msg)
966 config = sxp.merge(['vm', blconfig ], self.config)
967 return config
969 def configure_backends(self):
970 """Set configuration flags if the vm is a backend for netif or blkif.
971 Configure the backends to use for vbd and vif if specified.
972 """
973 for c in sxp.children(self.config, 'backend'):
974 v = sxp.child0(c)
975 name = sxp.name(v)
976 if name == 'blkif':
977 self.blkif_backend = True
978 elif name == 'netif':
979 self.netif_backend = True
980 elif name == 'usbif':
981 self.usbif_backend = True
982 else:
983 raise VmError('invalid backend type:' + str(name))
985 def configure(self):
986 """Configure a vm.
988 """
989 self.configure_fields()
990 self.create_console()
991 self.create_devices()
992 self.create_blkif()
994 def create_console(self):
995 console = self.getConsole()
996 if not console:
997 config = ['console']
998 if self.console_port:
999 config.append(['console_port', self.console_port])
1000 console = self.createDevice('console', config)
1001 return console
1003 def getConsole(self):
1004 console_ctrl = self.getDeviceController("console", error=False)
1005 if console_ctrl:
1006 return console_ctrl.getDevice(0)
1007 return None
1009 def create_blkif(self):
1010 """Create the block device interface (blkif) for the vm.
1011 The vm needs a blkif even if it doesn't have any disks
1012 at creation time, for example when it uses NFS root.
1014 """
1015 blkif = self.getDeviceController("vbd", error=False)
1016 if not blkif:
1017 blkif = self.createDeviceController("vbd")
1018 backend = blkif.getBackend(0)
1019 backend.connect(recreate=self.recreate)
1021 def dom_construct(self, dom, config):
1022 """Construct a vm for an existing domain.
1024 @param dom: domain id
1025 @param config: domain configuration
1026 """
1027 d = dom_get(dom)
1028 if not d:
1029 raise VmError("Domain not found: %d" % dom)
1030 try:
1031 self.restore = True
1032 self.setdom(dom)
1033 self.memory = d['mem_kb']/1024
1034 self.construct(config)
1035 finally:
1036 self.restore = False
1038 def configure_fields(self):
1039 """Process the vm configuration fields using the registered handlers.
1040 """
1041 index = {}
1042 for field in sxp.children(self.config):
1043 field_name = sxp.name(field)
1044 field_index = index.get(field_name, 0)
1045 field_handler = get_config_handler(field_name)
1046 # Ignore unknown fields. Warn?
1047 if field_handler:
1048 v = field_handler(self, self.config, field, field_index)
1049 else:
1050 log.warning("Unknown config field %s", field_name)
1051 index[field_name] = field_index + 1
1053 def pgtable_size(self, memory):
1054 """Return the size of memory needed for 1:1 page tables for physical
1055 mode.
1057 @param memory: size in MB
1058 @return size in KB
1059 """
1060 if self.is_vmx:
1061 # Logic x86-32 specific.
1062 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
1063 return (1 + ((memory + 3) >> 2)) * 4
1064 return 0
1066 def mem_target_set(self, target):
1067 """Set domain memory target in pages.
1068 """
1069 if self.channel:
1070 msg = messages.packMsg('mem_request_t', { 'target' : target * (1 << 8)} )
1071 self.channel.writeRequest(msg)
1073 def shutdown(self, reason, key=0):
1074 msgtype = shutdown_messages.get(reason)
1075 if not msgtype:
1076 raise XendError('invalid reason:' + reason)
1077 extra = {}
1078 if reason == 'sysrq':
1079 extra['key'] = key
1080 if self.channel:
1081 msg = messages.packMsg(msgtype, extra)
1082 self.channel.writeRequest(msg)
1083 if not reason in ['suspend', 'sysrq']:
1084 self.shutdown_pending = {'start':time.time(), 'reason':reason,
1085 'key':key}
1087 def shutdown_time_left(self, timeout):
1088 if not self.shutdown_pending:
1089 return 0
1090 return timeout - (time.time() - self.shutdown_pending['start'])
1092 def vm_image_linux(vm, image):
1093 """Create a VM for a linux image.
1095 @param name: vm name
1096 @param memory: vm memory
1097 @param image: image config
1098 @return: vm
1099 """
1100 kernel = sxp.child_value(image, "kernel")
1101 cmdline = ""
1102 ip = sxp.child_value(image, "ip", None)
1103 if ip:
1104 cmdline += " ip=" + ip
1105 root = sxp.child_value(image, "root")
1106 if root:
1107 cmdline += " root=" + root
1108 args = sxp.child_value(image, "args")
1109 if args:
1110 cmdline += " " + args
1111 ramdisk = sxp.child_value(image, "ramdisk", '')
1112 log.debug("creating linux domain with cmdline: %s" %(cmdline,))
1113 vm.create_domain("linux", kernel, ramdisk, cmdline)
1114 return vm
1116 def vm_image_plan9(vm, image):
1117 """Create a VM for a Plan 9 image.
1119 name vm name
1120 memory vm memory
1121 image image config
1123 returns vm
1124 """
1125 kernel = sxp.child_value(image, "kernel")
1126 cmdline = ""
1127 ip = sxp.child_value(image, "ip", "dhcp")
1128 if ip:
1129 cmdline += "ip=" + ip
1130 root = sxp.child_value(image, "root")
1131 if root:
1132 cmdline += "root=" + root
1133 args = sxp.child_value(image, "args")
1134 if args:
1135 cmdline += " " + args
1136 ramdisk = sxp.child_value(image, "ramdisk", '')
1137 log.debug("creating plan9 domain with cmdline: %s" %(cmdline,))
1138 vm.create_domain("plan9", kernel, ramdisk, cmdline)
1139 return vm
1141 def vm_image_vmx(vm, image):
1142 """Create a VM for the VMX environment.
1144 @param name: vm name
1145 @param memory: vm memory
1146 @param image: image config
1147 @return: vm
1148 """
1149 kernel = sxp.child_value(image, "kernel")
1150 cmdline = ""
1151 ip = sxp.child_value(image, "ip", "dhcp")
1152 if ip:
1153 cmdline += " ip=" + ip
1154 root = sxp.child_value(image, "root")
1155 if root:
1156 cmdline += " root=" + root
1157 args = sxp.child_value(image, "args")
1158 if args:
1159 cmdline += " " + args
1160 ramdisk = sxp.child_value(image, "ramdisk", '')
1161 memmap = sxp.child_value(vm.config, "memmap", '')
1162 memmap = sxp.parse(open(memmap))[0]
1163 from xen.util.memmap import memmap_parse
1164 memmap = memmap_parse(memmap)
1165 log.debug("creating vmx domain with cmdline: %s" %(cmdline,))
1166 vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap)
1167 return vm
1169 def vm_field_ignore(vm, config, val, index):
1170 """Dummy config field handler used for fields with built-in handling.
1172 @param vm: virtual machine
1173 @param config: vm config
1174 @param val: config field
1175 @param index: field index
1176 """
1177 pass
1179 def vm_field_maxmem(vm, config, val, index):
1180 """Configure vm memory limit.
1182 @param vm: virtual machine
1183 @param config: vm config
1184 @param val: config field
1185 @param index: field index
1186 """
1187 maxmem = sxp.child0(val)
1188 if maxmem is None:
1189 maxmem = vm.memory
1190 try:
1191 maxmem = int(maxmem)
1192 except:
1193 raise VmError("invalid maxmem: " + str(maxmem))
1194 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1196 #============================================================================
1197 # Register image handlers.
1198 add_image_handler('linux', vm_image_linux)
1199 add_image_handler('plan9', vm_image_plan9)
1200 add_image_handler('vmx', vm_image_vmx)
1202 # Ignore the fields we already handle.
1203 add_config_handler('name', vm_field_ignore)
1204 add_config_handler('memory', vm_field_ignore)
1205 add_config_handler('cpu', vm_field_ignore)
1206 add_config_handler('cpu_weight', vm_field_ignore)
1207 add_config_handler('console', vm_field_ignore)
1208 add_config_handler('restart', vm_field_ignore)
1209 add_config_handler('image', vm_field_ignore)
1210 add_config_handler('device', vm_field_ignore)
1211 add_config_handler('backend', vm_field_ignore)
1212 add_config_handler('vcpus', vm_field_ignore)
1213 add_config_handler('bootloader', vm_field_ignore)
1215 # Register other config handlers.
1216 add_config_handler('maxmem', vm_field_maxmem)
1218 #============================================================================
1219 # Register device controllers and their device config types.
1221 from server import console
1222 controller.addDevControllerClass("console", console.ConsoleController)
1224 from server import blkif
1225 controller.addDevControllerClass("vbd", blkif.BlkifController)
1226 add_device_handler("vbd", "vbd")
1228 from server import netif
1229 controller.addDevControllerClass("vif", netif.NetifController)
1230 add_device_handler("vif", "vif")
1232 from server import pciif
1233 controller.addDevControllerClass("pci", pciif.PciController)
1234 add_device_handler("pci", "pci")
1236 from xen.xend.server import usbif
1237 controller.addDevControllerClass("usb", usbif.UsbifController)
1238 add_device_handler("usb", "usb")
1240 #============================================================================