debuggers.hg

view tools/python/xen/xend/XendDomainInfo.py @ 4655:a838a908e38e

bitkeeper revision 1.1327.2.2 (4267a9b3MhPpljnjQ5IbfLdzcW2K3w)

Remove twisted from the HTTP server and replace with a
threaded server. Add classes to provide tcp and unix servers
using threads instead of twisted. Remove use of twisted from
the consoles, event server and HTTP resources

Signed-off-by: Mike Wray <mike.wray@hp.com>
author mjw@wray-m-3.hpl.hp.com
date Thu Apr 21 13:25:07 2005 +0000 (2005-04-21)
parents c69fbe48a357
children 16efdf7bbd57 20d715ae2dfb
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
20 from server.channel import channelFactory
21 import server.SrvDaemon; xend = server.SrvDaemon.instance()
22 from server import messages
24 import sxp
25 from XendLogging import log
26 from XendError import VmError
27 from XendRoot import get_component
29 from PrettyPrint import prettyprint
31 """Flag for a block device backend domain."""
32 SIF_BLK_BE_DOMAIN = (1<<4)
34 """Flag for a net device backend domain."""
35 SIF_NET_BE_DOMAIN = (1<<5)
37 """Shutdown code for poweroff."""
38 DOMAIN_POWEROFF = 0
40 """Shutdown code for reboot."""
41 DOMAIN_REBOOT = 1
43 """Shutdown code for suspend."""
44 DOMAIN_SUSPEND = 2
46 """Map shutdown codes to strings."""
47 shutdown_reasons = {
48 DOMAIN_POWEROFF: "poweroff",
49 DOMAIN_REBOOT : "reboot",
50 DOMAIN_SUSPEND : "suspend" }
52 """Map shutdown reasons to the message type to use.
53 """
54 shutdown_messages = {
55 'poweroff' : 'shutdown_poweroff_t',
56 'reboot' : 'shutdown_reboot_t',
57 'suspend' : 'shutdown_suspend_t',
58 'sysrq' : 'shutdown_sysrq_t',
59 }
61 RESTART_ALWAYS = 'always'
62 RESTART_ONREBOOT = 'onreboot'
63 RESTART_NEVER = 'never'
65 restart_modes = [
66 RESTART_ALWAYS,
67 RESTART_ONREBOOT,
68 RESTART_NEVER,
69 ]
71 STATE_RESTART_PENDING = 'pending'
72 STATE_RESTART_BOOTING = 'booting'
74 STATE_VM_OK = "ok"
75 STATE_VM_TERMINATED = "terminated"
78 def domain_exists(name):
79 # See comment in XendDomain constructor.
80 xd = get_component('xen.xend.XendDomain')
81 return xd.domain_exists(name)
83 def shutdown_reason(code):
84 """Get a shutdown reason from a code.
86 @param code: shutdown code
87 @type code: int
88 @return: shutdown reason
89 @rtype: string
90 """
91 return shutdown_reasons.get(code, "?")
93 def vif_up(iplist):
94 """send an unsolicited ARP reply for all non link-local IP addresses.
96 @param iplist: IP addresses
97 """
99 IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
101 def get_ip_nonlocal_bind():
102 return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
104 def set_ip_nonlocal_bind(v):
105 print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
107 def link_local(ip):
108 return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
110 def arping(ip, gw):
111 cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
112 log.debug(cmd)
113 os.system(cmd)
115 gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
116 nlb = get_ip_nonlocal_bind()
117 if not nlb: set_ip_nonlocal_bind(1)
118 try:
119 for ip in iplist:
120 if not link_local(ip):
121 arping(ip, gateway)
122 finally:
123 if not nlb: set_ip_nonlocal_bind(0)
125 config_handlers = {}
127 def add_config_handler(name, h):
128 """Add a handler for a config field.
130 @param name: field name
131 @param h: handler: fn(vm, config, field, index)
132 """
133 config_handlers[name] = h
135 def get_config_handler(name):
136 """Get a handler for a config field.
138 returns handler or None
139 """
140 return config_handlers.get(name)
142 """Table of handlers for virtual machine images.
143 Indexed by image type.
144 """
145 image_handlers = {}
147 def add_image_handler(name, h):
148 """Add a handler for an image type
149 @param name: image type
150 @param h: handler: fn(config, name, memory, image)
151 """
152 image_handlers[name] = h
154 def get_image_handler(name):
155 """Get the handler for an image type.
156 @param name: image type
157 @return: handler or None
158 """
159 return image_handlers.get(name)
161 """Table of handlers for devices.
162 Indexed by device type.
163 """
164 device_handlers = {}
166 def add_device_handler(name, type):
167 device_handlers[name] = type
169 def get_device_handler(name):
170 return device_handlers[name]
173 def vm_create(config):
174 """Create a VM from a configuration.
175 If a vm has been partially created and there is an error it
176 is destroyed.
178 @param config configuration
179 @raise: VmError for invalid configuration
180 """
181 vm = XendDomainInfo()
182 vm.construct(config)
183 return vm
185 def vm_recreate(savedinfo, info):
186 """Create the VM object for an existing domain.
188 @param savedinfo: saved info from the domain DB
189 @type savedinfo: sxpr
190 @param info: domain info from xc
191 @type info: xc domain dict
192 """
193 print 'vm_recreate>'
194 print 'savedinfo=' ; prettyprint(savedinfo)
195 print 'info=', info
196 vm = XendDomainInfo()
197 vm.recreate = True
198 vm.savedinfo = savedinfo
199 vm.setdom(info['dom'])
200 vm.memory = info['mem_kb']/1024
201 start_time = sxp.child_value(savedinfo, 'start_time')
202 if start_time is not None:
203 vm.start_time = float(start_time)
204 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
205 vm.restart_count = int(sxp.child_value(savedinfo, 'restart_count', 0))
206 restart_time = sxp.child_value(savedinfo, 'restart_time')
207 if restart_time is not None:
208 vm.restart_time = float(restart_time)
209 config = sxp.child_value(savedinfo, 'config')
210 if config:
211 vm.construct(config)
212 else:
213 vm.name = sxp.child_value(savedinfo, 'name', "Domain-%d" % info['dom'])
214 vm.recreate = False
215 vm.savedinfo = None
216 return vm
218 def vm_restore(src, progress=False):
219 """Restore a VM from a disk image.
221 src saved state to restore
222 progress progress reporting flag
223 raises VmError for invalid configuration
224 """
225 vm = XendDomainInfo()
226 ostype = "linux" #todo Set from somewhere (store in the src?).
227 restorefn = getattr(xc, "%s_restore" % ostype)
228 d = restorefn(state_file=src, progress=progress)
229 dom = int(d['dom'])
230 if dom < 0:
231 raise VmError('restore failed')
232 try:
233 vmconfig = sxp.from_string(d['vmconfig'])
234 config = sxp.child_value(vmconfig, 'config')
235 except Exception, ex:
236 raise VmError('config error: ' + str(ex))
237 vm.dom_construct(dom, config)
238 vif_up(vm.ipaddrs)
239 return vm
241 def dom_get(dom):
242 """Get info from xen for an existing domain.
244 @param dom: domain id
245 @return: info or None
246 """
247 domlist = xc.domain_getinfo(dom, 1)
248 if domlist and dom == domlist[0]['dom']:
249 return domlist[0]
250 return None
252 class XendDomainInfo:
253 """Virtual machine object."""
255 """Minimum time between domain restarts in seconds.
256 """
257 MINIMUM_RESTART_TIME = 20
259 def __init__(self):
260 self.recreate = 0
261 self.restore = 0
262 self.config = None
263 self.id = None
264 self.dom = None
265 self.cpu_weight = 1
266 self.start_time = None
267 self.name = None
268 self.memory = None
269 self.image = None
270 self.ramdisk = None
271 self.cmdline = None
273 self.channel = None
274 self.controllers = {}
276 self.configs = []
278 self.info = None
279 self.ipaddrs = []
280 self.blkif_backend = False
281 self.netif_backend = False
282 #todo: state: running, suspended
283 self.state = STATE_VM_OK
284 #todo: set to migrate info if migrating
285 self.migrate = None
287 self.restart_mode = RESTART_ONREBOOT
288 self.restart_state = None
289 self.restart_time = None
290 self.restart_count = 0
292 self.console_port = None
293 self.savedinfo = None
294 self.image_handler = None
295 self.is_vmx = False
296 self.vcpus = 1
298 def setdom(self, dom):
299 """Set the domain id.
301 @param dom: domain id
302 """
303 self.dom = int(dom)
304 self.id = str(dom)
306 def getDomain(self):
307 return self.dom
309 def getName(self):
310 return self.name
312 def getChannel(self):
313 return self.channel
315 def update(self, info):
316 """Update with info from xc.domain_getinfo().
317 """
318 self.info = info
319 self.memory = self.info['mem_kb'] / 1024
321 def __str__(self):
322 s = "domain"
323 s += " id=" + self.id
324 s += " name=" + self.name
325 s += " memory=" + str(self.memory)
326 console = self.getConsole()
327 if console:
328 s += " console=" + str(console.console_port)
329 if self.image:
330 s += " image=" + self.image
331 s += ""
332 return s
334 __repr__ = __str__
336 def getDeviceTypes(self):
337 return self.controllers.keys()
339 def getDeviceControllers(self):
340 return self.controllers.values()
342 def getDeviceController(self, type, error=True):
343 ctrl = self.controllers.get(type)
344 if not ctrl and error:
345 raise XendError("invalid device type:" + type)
346 return ctrl
348 def findDeviceController(self, type):
349 return (self.getDeviceController(type, error=False)
350 or self.createDeviceController(type))
352 def createDeviceController(self, type):
353 ctrl = controller.createDevController(type, self, recreate=self.recreate)
354 self.controllers[type] = ctrl
355 return ctrl
357 def createDevice(self, type, devconfig, recreate=False):
358 ctrl = self.findDeviceController(type)
359 return ctrl.createDevice(devconfig, recreate=self.recreate)
361 def configureDevice(self, type, id, devconfig):
362 ctrl = self.getDeviceController(type)
363 return ctrl.configureDevice(id, devconfig)
365 def destroyDevice(self, type, id, change=False, reboot=False):
366 ctrl = self.getDeviceController(type)
367 return ctrl.destroyDevice(id, change=change, reboot=reboot)
369 def deleteDevice(self, type, id):
370 ctrl = self.getDeviceController(type)
371 return ctrl.deleteDevice(id)
373 def getDevice(self, type, id):
374 ctrl = self.getDeviceController(type)
375 return ctrl.getDevice(id)
377 def getDeviceByIndex(self, type, idx):
378 ctrl = self.getDeviceController(type)
379 return ctrl.getDeviceByIndex(idx)
381 def getDeviceConfig(self, type, id):
382 ctrl = self.getDeviceController(type)
383 return ctrl.getDeviceConfig(id)
385 def getDeviceIds(self, type):
386 ctrl = self.getDeviceController(type)
387 return ctrl.getDeviceIds()
389 def getDeviceIndexes(self, type):
390 ctrl = self.getDeviceController(type)
391 return ctrl.getDeviceIndexes()
393 def getDeviceConfigs(self, type):
394 ctrl = self.getDeviceController(type)
395 return ctrl.getDeviceConfigs()
397 def getDeviceSxprs(self, type):
398 ctrl = self.getDeviceController(type)
399 return ctrl.getDeviceSxprs()
401 def sxpr(self):
402 sxpr = ['domain',
403 ['id', self.id],
404 ['name', self.name],
405 ['memory', self.memory] ]
407 if self.info:
408 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
409 run = (self.info['running'] and 'r') or '-'
410 block = (self.info['blocked'] and 'b') or '-'
411 pause = (self.info['paused'] and 'p') or '-'
412 shut = (self.info['shutdown'] and 's') or '-'
413 crash = (self.info['crashed'] and 'c') or '-'
414 state = run + block + pause + shut + crash
415 sxpr.append(['state', state])
416 if self.info['shutdown']:
417 reason = shutdown_reason(self.info['shutdown_reason'])
418 sxpr.append(['shutdown_reason', reason])
419 sxpr.append(['cpu', self.info['cpu']])
420 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
422 if self.start_time:
423 up_time = time.time() - self.start_time
424 sxpr.append(['up_time', str(up_time) ])
425 sxpr.append(['start_time', str(self.start_time) ])
427 if self.channel:
428 sxpr.append(self.channel.sxpr())
429 console = self.getConsole()
430 if console:
431 sxpr.append(console.sxpr())
432 if self.restart_count:
433 sxpr.append(['restart_count', self.restart_count])
434 if self.restart_state:
435 sxpr.append(['restart_state', self.restart_state])
436 if self.restart_time:
437 sxpr.append(['restart_time', str(self.restart_time)])
438 devs = self.sxpr_devices()
439 if devs:
440 sxpr.append(devs)
441 if self.config:
442 sxpr.append(['config', self.config])
443 return sxpr
445 def sxpr_devices(self):
446 sxpr = []
447 for ty in self.getDeviceTypes():
448 devs = self.getDeviceSxprs(ty)
449 sxpr += devs
450 if sxpr:
451 sxpr.insert(0, 'devices')
452 else:
453 sxpr = None
454 return sxpr
456 def check_name(self, name):
457 """Check if a vm name is valid. Valid names contain alphabetic characters,
458 digits, or characters in '_-.:/+'.
459 The same name cannot be used for more than one vm at the same time.
461 @param name: name
462 @raise: VMerror if invalid
463 """
464 if self.recreate: return
465 if name is None or name == '':
466 raise VmError('missing vm name')
467 for c in name:
468 if c in string.digits: continue
469 if c in '_-.:/+': continue
470 if c in string.ascii_letters: continue
471 raise VmError('invalid vm name')
472 dominfo = domain_exists(name)
473 # When creating or rebooting, a domain with my name should not exist.
474 # When restoring, a domain with my name will exist, but it should have
475 # my domain id.
476 if not dominfo:
477 return
478 if dominfo.is_terminated():
479 return
480 if not self.dom or (dominfo.dom != self.dom):
481 raise VmError('vm name clash: ' + name)
483 def construct(self, config):
484 """Construct the vm instance from its configuration.
486 @param config: configuration
487 @raise: VmError on error
488 """
489 # todo - add support for scheduling params?
490 self.config = config
491 try:
492 # Initial domain create.
493 self.name = sxp.child_value(config, 'name')
494 self.check_name(self.name)
495 self.configure_cpus(config)
496 self.find_image_handler()
497 self.init_domain()
498 self.register_domain()
500 # Create domain devices.
501 self.configure_backends()
502 self.configure_console()
503 self.configure_restart()
504 self.construct_image()
505 self.configure()
506 except Exception, ex:
507 # Catch errors, cleanup and re-raise.
508 print 'Domain construction error:', ex
509 import traceback
510 traceback.print_exc()
511 self.destroy()
512 raise
514 def register_domain(self):
515 xd = get_component('xen.xend.XendDomain')
516 xd._add_domain(self)
518 def configure_cpus(self, config):
519 try:
520 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
521 except:
522 raise VmError('invalid cpu weight')
523 self.memory = int(sxp.child_value(config, 'memory'))
524 if self.memory is None:
525 raise VmError('missing memory size')
526 cpu = sxp.child_value(config, 'cpu')
527 if self.recreate and self.dom and cpu is not None:
528 xc.domain_pincpu(self.dom, int(cpu))
529 try:
530 image = sxp.child_value(self.config, 'image')
531 vcpus = sxp.child_value(image, 'vcpus')
532 if vcpus:
533 self.vcpus = int(vcpus)
534 except:
535 raise VmError('invalid vcpus value')
537 def find_image_handler(self):
538 """Construct the boot image for the domain.
540 @return vm
541 """
542 image = sxp.child_value(self.config, 'image')
543 if image is None:
544 raise VmError('missing image')
545 image_name = sxp.name(image)
546 if image_name is None:
547 raise VmError('missing image name')
548 if image_name == "vmx":
549 self.is_vmx = True
550 image_handler = get_image_handler(image_name)
551 if image_handler is None:
552 raise VmError('unknown image type: ' + image_name)
553 self.image_handler = image_handler
554 return self
556 def construct_image(self):
557 image = sxp.child_value(self.config, 'image')
558 self.image_handler(self, image)
559 return self
561 def config_devices(self, name):
562 """Get a list of the 'device' nodes of a given type from the config.
564 @param name: device type
565 @type name: string
566 @return: device configs
567 @rtype: list
568 """
569 devices = []
570 for d in sxp.children(self.config, 'device'):
571 dev = sxp.child0(d)
572 if dev is None: continue
573 if name == sxp.name(dev):
574 devices.append(dev)
575 return devices
577 def get_device_savedinfo(self, type, index):
578 val = None
579 if self.savedinfo is None:
580 return val
581 devices = sxp.child(self.savedinfo, 'devices')
582 if devices is None:
583 return val
584 index = str(index)
585 for d in sxp.children(devices, type):
586 dindex = sxp.child_value(d, 'index')
587 if dindex is None: continue
588 if str(dindex) == index:
589 val = d
590 break
591 return val
593 def get_device_recreate(self, type, index):
594 return self.get_device_savedinfo(type, index) or self.recreate
596 def add_config(self, val):
597 """Add configuration data to a virtual machine.
599 @param val: data to add
600 """
601 self.configs.append(val)
603 def destroy(self):
604 """Completely destroy the vm.
605 """
606 self.cleanup()
607 return self.destroy_domain()
609 def destroy_domain(self):
610 """Destroy the vm's domain.
611 The domain will not finally go away unless all vm
612 devices have been released.
613 """
614 if self.channel:
615 self.channel.close()
616 self.channel = None
617 if self.dom is None: return 0
618 try:
619 return xc.domain_destroy(dom=self.dom)
620 except Exception, err:
621 log.exception("Domain destroy failed: %s", self.name)
623 def cleanup(self):
624 """Cleanup vm resources: release devices.
625 """
626 self.state = STATE_VM_TERMINATED
627 self.release_devices()
629 def is_terminated(self):
630 """Check if a domain has been terminated.
631 """
632 return self.state == STATE_VM_TERMINATED
634 def release_devices(self):
635 """Release all vm devices.
636 """
637 reboot = self.restart_pending()
638 for ctrl in self.getDeviceControllers():
639 if ctrl.isDestroyed(): continue
640 ctrl.destroyController(reboot=reboot)
641 if not reboot:
642 self.configs = []
643 self.ipaddrs = []
645 def show(self):
646 """Print virtual machine info.
647 """
648 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
649 print "image:"
650 sxp.show(self.image)
651 print
652 for val in self.configs:
653 print "config:"
654 sxp.show(val)
655 print
656 print "]"
658 def init_domain(self):
659 """Initialize the domain memory.
660 """
661 if self.recreate:
662 return
663 if self.start_time is None:
664 self.start_time = time.time()
665 if self.restore:
666 return
667 dom = self.dom or 0
668 memory = self.memory
669 try:
670 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
671 except:
672 raise VmError('invalid cpu')
673 cpu_weight = self.cpu_weight
674 memory = memory * 1024 + self.pgtable_size(memory)
675 dom = xc.domain_create(dom= dom, mem_kb= memory,
676 cpu= cpu, cpu_weight= cpu_weight)
677 if dom <= 0:
678 raise VmError('Creating domain failed: name=%s memory=%d'
679 % (self.name, memory))
680 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, self.name, memory)
681 self.setdom(dom)
683 def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap):
684 """Build the domain boot image.
685 """
686 if self.recreate or self.restore: return
687 if not os.path.isfile(kernel):
688 raise VmError('Kernel image does not exist: %s' % kernel)
689 if ramdisk and not os.path.isfile(ramdisk):
690 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
691 if len(cmdline) >= 256:
692 log.warning('kernel cmdline too long, domain %d', self.dom)
693 dom = self.dom
694 buildfn = getattr(xc, '%s_build' % ostype)
695 flags = 0
696 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
697 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
698 #todo generalise this
699 if ostype == "vmx":
700 err = buildfn(dom = dom,
701 image = kernel,
702 control_evtchn = 0,
703 memsize = self.memory,
704 memmap = memmap,
705 cmdline = cmdline,
706 ramdisk = ramdisk,
707 flags = flags)
708 else:
709 log.warning('building dom with %d vcpus', self.vcpus)
710 err = buildfn(dom = dom,
711 image = kernel,
712 control_evtchn = self.channel.getRemotePort(),
713 cmdline = cmdline,
714 ramdisk = ramdisk,
715 flags = flags,
716 vcpus = self.vcpus)
717 if err != 0:
718 raise VmError('Building domain failed: type=%s dom=%d err=%d'
719 % (ostype, dom, err))
721 def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''):
722 """Create a domain. Builds the image but does not configure it.
724 @param ostype: OS type
725 @param kernel: kernel image
726 @param ramdisk: kernel ramdisk
727 @param cmdline: kernel commandline
728 """
730 self.create_channel()
731 self.build_domain(ostype, kernel, ramdisk, cmdline, memmap)
732 self.image = kernel
733 self.ramdisk = ramdisk
734 self.cmdline = cmdline
736 def create_channel(self):
737 """Create the control channel to the domain.
738 If saved info is available recreate the channel using the saved ports.
739 """
740 local = 0
741 remote = 1
742 if self.savedinfo:
743 info = sxp.child(self.savedinfo, "channel")
744 if info:
745 local = int(sxp.child_value(info, "local_port", 0))
746 remote = int(sxp.child_value(info, "remote_port", 1))
747 self.channel = channelFactory().openChannel(self.dom,
748 local_port=local,
749 remote_port=remote)
751 def create_configured_devices(self):
752 devices = sxp.children(self.config, 'device')
753 indexes = {}
754 for d in devices:
755 dev_config = sxp.child0(d)
756 if dev_config is None:
757 raise VmError('invalid device')
758 dev_type = sxp.name(dev_config)
759 ctrl_type = get_device_handler(dev_type)
760 if ctrl_type is None:
761 raise VmError('unknown device type: ' + dev_type)
762 # Keep track of device indexes by type, so we can fish
763 # out saved info for recreation.
764 idx = indexes.get(dev_type, -1)
765 idx += 1
766 indexes[ctrl_type] = idx
767 recreate = self.get_device_recreate(dev_type, idx)
768 self.createDevice(ctrl_type, dev_config, recreate=recreate)
770 def create_devices(self):
771 """Create the devices for a vm.
773 @raise: VmError for invalid devices
774 """
775 if self.rebooting():
776 for ctrl in self.getDeviceControllers():
777 ctrl.initController(reboot=True)
778 else:
779 self.create_configured_devices()
780 if self.is_vmx:
781 self.create_vmx_model()
783 def create_vmx_model(self):
784 #todo: remove special case for vmx
785 device_model = sxp.child_value(self.config, 'device_model')
786 if not device_model:
787 raise VmError("vmx: missing device model")
788 device_config = sxp.child_value(self.config, 'device_config')
789 if not device_config:
790 raise VmError("vmx: missing device config")
791 #todo: self.memory?
792 memory = sxp.child_value(self.config, "memory")
793 # Create an event channel
794 device_channel = channel.eventChannel(0, self.dom)
795 # Execute device model.
796 #todo: Error handling
797 os.system(device_model
798 + " -f %s" % device_config
799 + " -d %d" % self.dom
800 + " -p %d" % device_channel['port1']
801 + " -m %s" % memory)
803 def device_create(self, dev_config):
804 """Create a new device.
806 @param dev_config: device configuration
807 """
808 dev_type = sxp.name(dev_config)
809 dev = self.createDevice(self, dev_config, change=True)
810 self.config.append(['device', dev.getConfig()])
811 return dev.sxpr()
813 def device_configure(self, dev_config, idx):
814 """Configure an existing device.
816 @param dev_config: device configuration
817 @param idx: device index
818 """
819 type = sxp.name(dev_config)
820 dev = self.getDeviceByIndex(type, idx)
821 if not dev:
822 raise VmError('invalid device: %s %s' % (type, idx))
823 old_config = dev.getConfig()
824 new_config = dev.configure(dev_config, change=True)
825 # Patch new config into vm config.
826 new_full_config = ['device', new_config]
827 old_full_config = ['device', old_config]
828 old_index = self.config.index(old_full_config)
829 self.config[old_index] = new_full_config
830 return new_config
832 def device_refresh(self, type, idx):
833 """Refresh a device.
835 @param type: device type
836 @param idx: device index
837 """
838 dev = self.getDeviceByIndex(type, idx)
839 if not dev:
840 raise VmError('invalid device: %s %s' % (type, idx))
841 dev.refresh()
843 def device_delete(self, type, idx):
844 """Destroy and remove a device.
846 @param type: device type
847 @param idx: device index
848 """
849 dev = self.getDeviceByIndex(type, idx)
850 if not dev:
851 raise VmError('invalid device: %s %s' % (type, idx))
852 dev_config = dev.getConfig()
853 if dev_config:
854 self.config.remove(['device', dev_config])
855 self.deleteDevice(type, dev.getId())
857 def configure_console(self):
858 """Configure the vm console port.
859 """
860 x = sxp.child_value(self.config, 'console')
861 if x:
862 try:
863 port = int(x)
864 except:
865 raise VmError('invalid console:' + str(x))
866 self.console_port = port
868 def configure_restart(self):
869 """Configure the vm restart mode.
870 """
871 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
872 if r not in restart_modes:
873 raise VmError('invalid restart mode: ' + str(r))
874 self.restart_mode = r;
876 def restart_needed(self, reason):
877 """Determine if the vm needs to be restarted when shutdown
878 for the given reason.
880 @param reason: shutdown reason
881 @return True if needs restart, False otherwise
882 """
883 if self.restart_mode == RESTART_NEVER:
884 return False
885 if self.restart_mode == RESTART_ALWAYS:
886 return True
887 if self.restart_mode == RESTART_ONREBOOT:
888 return reason == 'reboot'
889 return False
891 def restart_cancel(self):
892 """Cancel a vm restart.
893 """
894 self.restart_state = None
896 def restarting(self):
897 """Put the vm into restart mode.
898 """
899 self.restart_state = STATE_RESTART_PENDING
901 def restart_pending(self):
902 """Test if the vm has a pending restart.
903 """
904 return self.restart_state == STATE_RESTART_PENDING
906 def rebooting(self):
907 return self.restart_state == STATE_RESTART_BOOTING
909 def restart_check(self):
910 """Check if domain restart is OK.
911 To prevent restart loops, raise an error if it is
912 less than MINIMUM_RESTART_TIME seconds since the last restart.
913 """
914 tnow = time.time()
915 if self.restart_time is not None:
916 tdelta = tnow - self.restart_time
917 if tdelta < self.MINIMUM_RESTART_TIME:
918 self.restart_cancel()
919 msg = 'VM %s restarting too fast' % self.name
920 log.error(msg)
921 raise VmError(msg)
922 self.restart_time = tnow
923 self.restart_count += 1
925 def restart(self):
926 """Restart the domain after it has exited.
927 Reuses the domain id and console port.
929 """
930 try:
931 self.state = STATE_VM_OK
932 self.restart_check()
933 self.restart_state = STATE_RESTART_BOOTING
934 self.construct(self.config)
935 finally:
936 self.restart_state = None
938 def configure_backends(self):
939 """Set configuration flags if the vm is a backend for netif or blkif.
940 Configure the backends to use for vbd and vif if specified.
941 """
942 for c in sxp.children(self.config, 'backend'):
943 v = sxp.child0(c)
944 name = sxp.name(v)
945 if name == 'blkif':
946 self.blkif_backend = True
947 elif name == 'netif':
948 self.netif_backend = True
949 elif name == 'usbif':
950 self.usbif_backend = True
951 else:
952 raise VmError('invalid backend type:' + str(name))
954 def configure(self):
955 """Configure a vm.
957 """
958 self.configure_fields()
959 self.create_console()
960 self.create_devices()
961 self.create_blkif()
963 def create_console(self):
964 console = self.getConsole()
965 if not console:
966 config = ['console']
967 if self.console_port:
968 config.append(['console_port', self.console_port])
969 console = self.createDevice('console', config)
970 return console
972 def getConsole(self):
973 console_ctrl = self.getDeviceController("console", error=False)
974 if console_ctrl:
975 return console_ctrl.getDevice(0)
976 return None
978 def create_blkif(self):
979 """Create the block device interface (blkif) for the vm.
980 The vm needs a blkif even if it doesn't have any disks
981 at creation time, for example when it uses NFS root.
983 """
984 blkif = self.getDeviceController("vbd", error=False)
985 if not blkif:
986 blkif = self.createDeviceController("vbd")
987 backend = blkif.getBackend(0)
988 backend.connect(recreate=self.recreate)
990 def dom_construct(self, dom, config):
991 """Construct a vm for an existing domain.
993 @param dom: domain id
994 @param config: domain configuration
995 """
996 d = dom_get(dom)
997 if not d:
998 raise VmError("Domain not found: %d" % dom)
999 try:
1000 self.restore = True
1001 self.setdom(dom)
1002 self.memory = d['mem_kb']/1024
1003 self.construct(config)
1004 finally:
1005 self.restore = False
1007 def configure_fields(self):
1008 """Process the vm configuration fields using the registered handlers.
1009 """
1010 index = {}
1011 for field in sxp.children(self.config):
1012 field_name = sxp.name(field)
1013 field_index = index.get(field_name, 0)
1014 field_handler = get_config_handler(field_name)
1015 # Ignore unknown fields. Warn?
1016 if field_handler:
1017 v = field_handler(self, self.config, field, field_index)
1018 else:
1019 log.warning("Unknown config field %s", field_name)
1020 index[field_name] = field_index + 1
1022 def pgtable_size(self, memory):
1023 """Return the size of memory needed for 1:1 page tables for physical
1024 mode.
1026 @param memory: size in MB
1027 @return size in KB
1028 """
1029 if self.is_vmx:
1030 # Logic x86-32 specific.
1031 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
1032 return (1 + ((memory + 3) >> 2)) * 4
1033 return 0
1035 def mem_target_set(self, target):
1036 """Set domain memory target in pages.
1037 """
1038 if self.channel:
1039 msg = messages.packMsg('mem_request_t', { 'target' : target * (1 << 8)} )
1040 self.channel.writeRequest(msg)
1042 def shutdown(self, reason, key=0):
1043 msgtype = shutdown_messages.get(reason)
1044 if not msgtype:
1045 raise XendError('invalid reason:' + reason)
1046 extra = {}
1047 if reason == 'sysrq':
1048 extra['key'] = key
1049 if self.channel:
1050 msg = messages.packMsg(msgtype, extra)
1051 self.channel.writeRequest(msg)
1054 def vm_image_linux(vm, image):
1055 """Create a VM for a linux image.
1057 @param name: vm name
1058 @param memory: vm memory
1059 @param image: image config
1060 @return: vm
1061 """
1062 kernel = sxp.child_value(image, "kernel")
1063 cmdline = ""
1064 ip = sxp.child_value(image, "ip", None)
1065 if ip:
1066 cmdline += " ip=" + ip
1067 root = sxp.child_value(image, "root")
1068 if root:
1069 cmdline += " root=" + root
1070 args = sxp.child_value(image, "args")
1071 if args:
1072 cmdline += " " + args
1073 ramdisk = sxp.child_value(image, "ramdisk", '')
1074 vm.create_domain("linux", kernel, ramdisk, cmdline)
1075 return vm
1077 def vm_image_plan9(vm, image):
1078 """Create a VM for a Plan 9 image.
1080 name vm name
1081 memory vm memory
1082 image image config
1084 returns vm
1085 """
1086 kernel = sxp.child_value(image, "kernel")
1087 cmdline = ""
1088 ip = sxp.child_value(image, "ip", "dhcp")
1089 if ip:
1090 cmdline += "ip=" + ip
1091 root = sxp.child_value(image, "root")
1092 if root:
1093 cmdline += "root=" + root
1094 args = sxp.child_value(image, "args")
1095 if args:
1096 cmdline += " " + args
1097 ramdisk = sxp.child_value(image, "ramdisk", '')
1098 vm.create_domain("plan9", kernel, ramdisk, cmdline)
1099 return vm
1101 def vm_image_vmx(vm, image):
1102 """Create a VM for the VMX environment.
1104 @param name: vm name
1105 @param memory: vm memory
1106 @param image: image config
1107 @return: vm
1108 """
1109 kernel = sxp.child_value(image, "kernel")
1110 cmdline = ""
1111 ip = sxp.child_value(image, "ip", "dhcp")
1112 if ip:
1113 cmdline += " ip=" + ip
1114 root = sxp.child_value(image, "root")
1115 if root:
1116 cmdline += " root=" + root
1117 args = sxp.child_value(image, "args")
1118 if args:
1119 cmdline += " " + args
1120 ramdisk = sxp.child_value(image, "ramdisk", '')
1121 memmap = sxp.child_value(vm.config, "memmap", '')
1122 memmap = sxp.parse(open(memmap))[0]
1123 from xen.util.memmap import memmap_parse
1124 memmap = memmap_parse(memmap)
1125 vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap)
1126 return vm
1128 def vm_field_ignore(vm, config, val, index):
1129 """Dummy config field handler used for fields with built-in handling.
1131 @param vm: virtual machine
1132 @param config: vm config
1133 @param val: config field
1134 @param index: field index
1135 """
1136 pass
1138 def vm_field_maxmem(vm, config, val, index):
1139 """Configure vm memory limit.
1141 @param vm: virtual machine
1142 @param config: vm config
1143 @param val: config field
1144 @param index: field index
1145 """
1146 maxmem = sxp.child0(val)
1147 if maxmem is None:
1148 maxmem = vm.memory
1149 try:
1150 maxmem = int(maxmem)
1151 except:
1152 raise VmError("invalid maxmem: " + str(maxmem))
1153 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1155 #============================================================================
1156 # Register image handlers.
1157 add_image_handler('linux', vm_image_linux)
1158 add_image_handler('plan9', vm_image_plan9)
1159 add_image_handler('vmx', vm_image_vmx)
1161 # Ignore the fields we already handle.
1162 add_config_handler('name', vm_field_ignore)
1163 add_config_handler('memory', vm_field_ignore)
1164 add_config_handler('cpu', vm_field_ignore)
1165 add_config_handler('cpu_weight', vm_field_ignore)
1166 add_config_handler('console', vm_field_ignore)
1167 add_config_handler('restart', vm_field_ignore)
1168 add_config_handler('image', vm_field_ignore)
1169 add_config_handler('device', vm_field_ignore)
1170 add_config_handler('backend', vm_field_ignore)
1171 add_config_handler('vcpus', vm_field_ignore)
1173 # Register other config handlers.
1174 add_config_handler('maxmem', vm_field_maxmem)
1176 #============================================================================
1177 # Register device controllers and their device config types.
1179 from server import console
1180 controller.addDevControllerClass("console", console.ConsoleController)
1182 from server import blkif
1183 controller.addDevControllerClass("vbd", blkif.BlkifController)
1184 add_device_handler("vbd", "vbd")
1186 from server import netif
1187 controller.addDevControllerClass("vif", netif.NetifController)
1188 add_device_handler("vif", "vif")
1190 from server import pciif
1191 controller.addDevControllerClass("pci", pciif.PciController)
1192 add_device_handler("pci", "pci")
1194 from xen.xend.server import usbif
1195 controller.addDevControllerClass("usb", usbif.UsbifController)
1196 add_device_handler("usb", "usb")
1198 #============================================================================