ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 5691:88c2d410979f

I updated the vcpu_to_cpu string creation to include a field separator,
which gets rid of the -1 -> # hack and works for cpus > 9.

I ran into some issues with stale vcpu_to_cpu lists when running the
hotplug subprogram. I would take a vcpu offline, and then issue the
command to bring it back and the vcpu_to_cpu list would not have changed
to indicate the the vcpu actually went down. If I injected a xm list -v
(which always showed the correct mapping) then subsequent hotplug
commands would see the state change and fire off the hotplug request. I
don't know that not sending the event when not changing state saves that
much work so I took the state check out and now just send the hotplug
event directly.

> Also the whole hotplug stuff is still missing interrupt re-routing
> when a vcpu is taken down. To do this, we need an evtchn operation to
> change the vcpu affinity of a port by changing notify_vcpu_id.

I don't fully understand all of the mappings that are happening, so this
part of the patch might be way off. In any case, I've added a new
evtchn op to set the notify_vcpu_id field of a channel. I updated the
HOTPLUG_CPU code to use the new routines when bringing cpus up and down.
When taking down a cpu, I route the IPI irq channels to CPU 0, and when
the cpu comes up, it re-routes the channels back to the awakened CPU.

From: Ryan Harper <ryanh@us.ibm.com>
Signed-off-by: ian@xensource.com
author iap10@freefall.cl.cam.ac.uk
date Wed Jul 06 22:23:18 2005 +0000 (2005-07-06)
parents 864b050694f8
children 707fcf42a5ae 579d1e771025 c1a7ed266c7e
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 from xen.util.ip import check_subnet, get_current_ipgw
18 from xen.util.blkif import blkdev_uname_to_file
20 from xen.xend.server import controller
21 from xen.xend.server import SrvDaemon; xend = SrvDaemon.instance()
22 from xen.xend.server import messages
23 from xen.xend.server.channel import EventChannel, channelFactory
25 from xen.xend import sxp
26 from xen.xend.PrettyPrint import prettyprintstring
27 from xen.xend.XendBootloader import bootloader
28 from xen.xend.XendLogging import log
29 from xen.xend.XendError import XendError, VmError
30 from xen.xend.XendRoot import get_component
32 from xen.xend.uuid import getUuid
33 from xen.xend.xenstore import DBVar
35 """Flag for a block device backend domain."""
36 SIF_BLK_BE_DOMAIN = (1<<4)
38 """Flag for a net device backend domain."""
39 SIF_NET_BE_DOMAIN = (1<<5)
41 """Shutdown code for poweroff."""
42 DOMAIN_POWEROFF = 0
44 """Shutdown code for reboot."""
45 DOMAIN_REBOOT = 1
47 """Shutdown code for suspend."""
48 DOMAIN_SUSPEND = 2
50 """Shutdown code for crash."""
51 DOMAIN_CRASH = 3
53 """Map shutdown codes to strings."""
54 shutdown_reasons = {
55 DOMAIN_POWEROFF: "poweroff",
56 DOMAIN_REBOOT : "reboot",
57 DOMAIN_SUSPEND : "suspend",
58 DOMAIN_CRASH : "crash",
59 }
61 """Map shutdown reasons to the message type to use.
62 """
63 shutdown_messages = {
64 'poweroff' : 'shutdown_poweroff_t',
65 'reboot' : 'shutdown_reboot_t',
66 'suspend' : 'shutdown_suspend_t',
67 'sysrq' : 'shutdown_sysrq_t',
68 }
70 RESTART_ALWAYS = 'always'
71 RESTART_ONREBOOT = 'onreboot'
72 RESTART_NEVER = 'never'
74 restart_modes = [
75 RESTART_ALWAYS,
76 RESTART_ONREBOOT,
77 RESTART_NEVER,
78 ]
80 STATE_RESTART_PENDING = 'pending'
81 STATE_RESTART_BOOTING = 'booting'
83 STATE_VM_OK = "ok"
84 STATE_VM_TERMINATED = "terminated"
85 STATE_VM_SUSPENDED = "suspended"
88 def domain_exists(name):
89 # See comment in XendDomain constructor.
90 xd = get_component('xen.xend.XendDomain')
91 return xd.domain_lookup_by_name(name)
93 def shutdown_reason(code):
94 """Get a shutdown reason from a code.
96 @param code: shutdown code
97 @type code: int
98 @return: shutdown reason
99 @rtype: string
100 """
101 return shutdown_reasons.get(code, "?")
103 config_handlers = {}
105 def add_config_handler(name, h):
106 """Add a handler for a config field.
108 @param name: field name
109 @param h: handler: fn(vm, config, field, index)
110 """
111 config_handlers[name] = h
113 def get_config_handler(name):
114 """Get a handler for a config field.
116 returns handler or None
117 """
118 return config_handlers.get(name)
120 """Table of handlers for devices.
121 Indexed by device type.
122 """
123 device_handlers = {}
125 def add_device_handler(name, type):
126 device_handlers[name] = type
128 def get_device_handler(name):
129 return device_handlers[name]
131 def dom_get(dom):
132 """Get info from xen for an existing domain.
134 @param dom: domain id
135 @return: info or None
136 """
137 domlist = xc.domain_getinfo(dom, 1)
138 if domlist and dom == domlist[0]['dom']:
139 return domlist[0]
140 return None
142 class XendDomainInfo:
143 """Virtual machine object."""
145 """Minimum time between domain restarts in seconds.
146 """
147 MINIMUM_RESTART_TIME = 20
149 def create(cls, parentdb, config):
150 """Create a VM from a configuration.
152 @param parentdb: parent db
153 @param config configuration
154 @raise: VmError for invalid configuration
155 """
156 uuid = getUuid()
157 db = parentdb.addChild(uuid)
158 vm = cls(db)
159 vm.construct(config)
160 vm.saveToDB(sync=True)
161 return vm
163 create = classmethod(create)
165 def recreate(cls, db, info):
166 """Create the VM object for an existing domain.
168 @param db: domain db
169 @param info: domain info from xc
170 """
171 dom = info['dom']
172 vm = cls(db)
173 db.readDB()
174 vm.importFromDB()
175 config = vm.config
176 log.debug('info=' + str(info))
177 log.debug('config=' + prettyprintstring(config))
179 vm.setdom(dom)
180 vm.memory = info['mem_kb']/1024
182 if config:
183 try:
184 vm.recreate = True
185 vm.construct(config)
186 finally:
187 vm.recreate = False
188 else:
189 vm.setName("Domain-%d" % dom)
191 vm.exportToDB(save=True)
192 return vm
194 recreate = classmethod(recreate)
196 def restore(cls, parentdb, config, uuid):
197 """Create a domain and a VM object to do a restore.
199 @param parentdb: parent db
200 @param config: domain configuration
201 @param uuid: uuid to use
202 """
203 db = parentdb.addChild(uuid)
204 vm = cls(db)
205 ssidref = int(sxp.child_value(config, 'ssidref'))
206 log.debug('restoring with ssidref='+str(ssidref))
207 id = xc.domain_create(ssidref = ssidref)
208 vm.setdom(id)
209 try:
210 vm.restore = True
211 vm.construct(config)
212 finally:
213 vm.restore = False
214 vm.exportToDB(save=True, sync=True)
215 return vm
217 restore = classmethod(restore)
219 __exports__ = [
220 DBVar('id', ty='int'),
221 DBVar('name', ty='str'),
222 DBVar('uuid', ty='str'),
223 DBVar('config', ty='sxpr'),
224 DBVar('start_time', ty='float'),
225 DBVar('state', ty='str'),
226 DBVar('store_mfn', ty='long'),
227 DBVar('restart_mode', ty='str'),
228 DBVar('restart_state', ty='str'),
229 DBVar('restart_time', ty='float'),
230 DBVar('restart_count', ty='int'),
231 ]
233 def __init__(self, db):
234 self.db = db
235 self.uuid = db.getName()
237 self.recreate = 0
238 self.restore = 0
240 self.config = None
241 self.id = None
242 self.cpu_weight = 1
243 self.start_time = None
244 self.name = None
245 self.memory = None
246 self.ssidref = None
247 self.image = None
249 self.channel = None
250 self.store_channel = None
251 self.store_mfn = None
252 self.controllers = {}
254 self.info = None
255 self.blkif_backend = False
256 self.netif_backend = False
257 #todo: state: running, suspended
258 self.state = STATE_VM_OK
259 self.state_updated = threading.Condition()
260 self.shutdown_pending = None
262 #todo: set to migrate info if migrating
263 self.migrate = None
265 self.restart_mode = RESTART_ONREBOOT
266 self.restart_state = None
267 self.restart_time = None
268 self.restart_count = 0
270 self.console_port = None
271 self.vcpus = 1
272 self.bootloader = None
274 def setDB(self, db):
275 self.db = db
277 def saveToDB(self, save=False, sync=False):
278 self.db.saveDB(save=save, sync=sync)
280 def exportToDB(self, save=False, sync=False):
281 if self.channel:
282 self.channel.saveToDB(self.db.addChild("channel"), save=save)
283 if self.store_channel:
284 self.store_channel.saveToDB(self.db.addChild("store_channel"),
285 save=save)
286 if self.image:
287 self.image.exportToDB(save=save, sync=sync)
288 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
290 def importFromDB(self):
291 self.db.importFromDB(self, fields=self.__exports__)
293 def setdom(self, dom):
294 """Set the domain id.
296 @param dom: domain id
297 """
298 self.id = int(dom)
299 #self.db.id = self.id
301 def getDomain(self):
302 return self.id
304 def setName(self, name):
305 self.name = name
306 self.db.name = self.name
308 def getName(self):
309 return self.name
311 def getChannel(self):
312 return self.channel
314 def getStoreChannel(self):
315 return self.store_channel
317 def update(self, info):
318 """Update with info from xc.domain_getinfo().
319 """
320 self.info = info
321 self.memory = self.info['mem_kb'] / 1024
322 self.ssidref = self.info['ssidref']
324 def state_set(self, state):
325 self.state_updated.acquire()
326 if self.state != state:
327 self.state = state
328 self.state_updated.notifyAll()
329 self.state_updated.release()
330 self.saveToDB()
332 def state_wait(self, state):
333 self.state_updated.acquire()
334 while self.state != state:
335 self.state_updated.wait()
336 self.state_updated.release()
338 def __str__(self):
339 s = "<domain"
340 s += " id=" + str(self.id)
341 s += " name=" + self.name
342 s += " memory=" + str(self.memory)
343 s += " ssidref=" + str(self.ssidref)
344 console = self.getConsole()
345 if console:
346 s += " console=" + str(console.console_port)
347 s += ">"
348 return s
350 __repr__ = __str__
352 def getDeviceTypes(self):
353 return self.controllers.keys()
355 def getDeviceControllers(self):
356 return self.controllers.values()
358 def getDeviceController(self, type, error=True):
359 ctrl = self.controllers.get(type)
360 if not ctrl and error:
361 raise XendError("invalid device type:" + type)
362 return ctrl
364 def findDeviceController(self, type):
365 return (self.getDeviceController(type, error=False)
366 or self.createDeviceController(type))
368 def createDeviceController(self, type):
369 ctrl = controller.createDevController(type, self, recreate=self.recreate)
370 self.controllers[type] = ctrl
371 return ctrl
373 def createDevice(self, type, devconfig, change=False):
374 ctrl = self.findDeviceController(type)
375 return ctrl.createDevice(devconfig, recreate=self.recreate,
376 change=change)
378 def configureDevice(self, type, id, devconfig):
379 ctrl = self.getDeviceController(type)
380 return ctrl.configureDevice(id, devconfig)
382 def destroyDevice(self, type, id, change=False, reboot=False):
383 ctrl = self.getDeviceController(type)
384 return ctrl.destroyDevice(id, change=change, reboot=reboot)
386 def deleteDevice(self, type, id):
387 ctrl = self.getDeviceController(type)
388 return ctrl.deleteDevice(id)
390 def getDevice(self, type, id, error=True):
391 ctrl = self.getDeviceController(type)
392 return ctrl.getDevice(id, error=error)
394 def getDeviceIds(self, type):
395 ctrl = self.getDeviceController(type)
396 return ctrl.getDeviceIds()
398 def getDeviceSxprs(self, type):
399 ctrl = self.getDeviceController(type)
400 return ctrl.getDeviceSxprs()
402 def sxpr(self):
403 sxpr = ['domain',
404 ['id', self.id],
405 ['name', self.name],
406 ['memory', self.memory],
407 ['ssidref', self.ssidref] ]
408 if self.uuid:
409 sxpr.append(['uuid', self.uuid])
410 if self.info:
411 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
412 run = (self.info['running'] and 'r') or '-'
413 block = (self.info['blocked'] and 'b') or '-'
414 pause = (self.info['paused'] and 'p') or '-'
415 shut = (self.info['shutdown'] and 's') or '-'
416 crash = (self.info['crashed'] and 'c') or '-'
417 state = run + block + pause + shut + crash
418 sxpr.append(['state', state])
419 if self.info['shutdown']:
420 reason = shutdown_reason(self.info['shutdown_reason'])
421 sxpr.append(['shutdown_reason', reason])
422 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
423 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
424 sxpr.append(['vcpus', self.info['vcpus']])
425 sxpr.append(['cpumap', self.info['cpumap']])
426 # build a string, using '|' to seperate items, show only up
427 # to number of vcpus in domain, and trim the trailing '|'
428 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
429 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
431 if self.start_time:
432 up_time = time.time() - self.start_time
433 sxpr.append(['up_time', str(up_time) ])
434 sxpr.append(['start_time', str(self.start_time) ])
436 if self.channel:
437 sxpr.append(self.channel.sxpr())
438 if self.store_channel:
439 sxpr.append(self.store_channel.sxpr())
440 if self.store_mfn:
441 sxpr.append(['store_mfn', self.store_mfn])
442 console = self.getConsole()
443 if console:
444 sxpr.append(console.sxpr())
446 if self.restart_count:
447 sxpr.append(['restart_count', self.restart_count])
448 if self.restart_state:
449 sxpr.append(['restart_state', self.restart_state])
450 if self.restart_time:
451 sxpr.append(['restart_time', str(self.restart_time)])
453 devs = self.sxpr_devices()
454 if devs:
455 sxpr.append(devs)
456 if self.config:
457 sxpr.append(['config', self.config])
458 return sxpr
460 def sxpr_devices(self):
461 sxpr = []
462 for ty in self.getDeviceTypes():
463 devs = self.getDeviceSxprs(ty)
464 sxpr += devs
465 if sxpr:
466 sxpr.insert(0, 'devices')
467 else:
468 sxpr = None
469 return sxpr
471 def check_name(self, name):
472 """Check if a vm name is valid. Valid names contain alphabetic characters,
473 digits, or characters in '_-.:/+'.
474 The same name cannot be used for more than one vm at the same time.
476 @param name: name
477 @raise: VMerror if invalid
478 """
479 if self.recreate: return
480 if name is None or name == '':
481 raise VmError('missing vm name')
482 for c in name:
483 if c in string.digits: continue
484 if c in '_-.:/+': continue
485 if c in string.ascii_letters: continue
486 raise VmError('invalid vm name')
487 dominfo = domain_exists(name)
488 # When creating or rebooting, a domain with my name should not exist.
489 # When restoring, a domain with my name will exist, but it should have
490 # my domain id.
491 if not dominfo:
492 return
493 if dominfo.is_terminated():
494 return
495 if not self.id or (dominfo.id != self.id):
496 raise VmError('vm name clash: ' + name)
498 def construct(self, config):
499 """Construct the vm instance from its configuration.
501 @param config: configuration
502 @raise: VmError on error
503 """
504 # todo - add support for scheduling params?
505 self.config = config
506 try:
507 # Initial domain create.
508 self.setName(sxp.child_value(config, 'name'))
509 self.check_name(self.name)
510 self.init_image()
511 self.configure_cpus(config)
512 self.init_domain()
513 self.register_domain()
514 self.configure_bootloader()
516 # Create domain devices.
517 self.configure_backends()
518 self.configure_console()
519 self.configure_restart()
520 self.construct_image()
521 self.configure()
522 self.exportToDB(save=True)
523 except Exception, ex:
524 # Catch errors, cleanup and re-raise.
525 print 'Domain construction error:', ex
526 import traceback
527 traceback.print_exc()
528 self.destroy()
529 raise
531 def register_domain(self):
532 xd = get_component('xen.xend.XendDomain')
533 xd._add_domain(self)
534 self.exportToDB(save=True)
536 def configure_cpus(self, config):
537 try:
538 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
539 except:
540 raise VmError('invalid cpu weight')
541 self.memory = int(sxp.child_value(config, 'memory'))
542 if self.memory is None:
543 raise VmError('missing memory size')
544 self.ssidref = int(sxp.child_value(config, 'ssidref'))
545 cpu = sxp.child_value(config, 'cpu')
546 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
547 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
548 try:
549 image = sxp.child_value(self.config, 'image')
550 vcpus = sxp.child_value(image, 'vcpus')
551 if vcpus:
552 self.vcpus = int(vcpus)
553 except:
554 raise VmError('invalid vcpus value')
556 def init_image(self):
557 """Create boot image handler for the domain.
558 """
559 image = sxp.child_value(self.config, 'image')
560 if image is None:
561 raise VmError('missing image')
562 self.image = ImageHandler.create(self, image)
564 def construct_image(self):
565 """Construct the boot image for the domain.
566 """
567 self.create_channel()
568 self.image.createImage()
569 self.exportToDB()
570 if self.store_channel:
571 self.db.introduceDomain(self.id,
572 self.store_mfn,
573 self.store_channel)
575 def delete(self):
576 """Delete the vm's db.
577 """
578 if self.dom_get(self.id):
579 return
580 self.id = None
581 self.saveToDB(sync=True)
582 try:
583 # Todo: eventually will have to wait for devices to signal
584 # destruction before can delete the db.
585 if self.db:
586 self.db.delete()
587 except Exception, ex:
588 log.warning("error in domain db delete: %s", ex)
589 pass
591 def destroy_domain(self):
592 """Destroy the vm's domain.
593 The domain will not finally go away unless all vm
594 devices have been released.
595 """
596 if self.id is None:
597 return
598 try:
599 xc.domain_destroy(dom=self.id)
600 except Exception, err:
601 log.exception("Domain destroy failed: %s", self.name)
603 def cleanup(self):
604 """Cleanup vm resources: release devices.
605 """
606 self.state = STATE_VM_TERMINATED
607 self.release_devices()
608 if self.channel:
609 try:
610 self.channel.close()
611 self.channel = None
612 except:
613 pass
614 if self.store_channel:
615 try:
616 self.store_channel.close()
617 self.store_channel = None
618 except:
619 pass
620 try:
621 self.db.releaseDomain(self.id)
622 except Exception, ex:
623 log.warning("error in domain release on xenstore: %s", ex)
624 pass
625 if self.image:
626 try:
627 self.image.destroy()
628 self.image = None
629 except:
630 pass
632 def destroy(self):
633 """Clenup vm and destroy domain.
634 """
635 self.cleanup()
636 self.destroy_domain()
637 self.saveToDB()
638 return 0
640 def is_terminated(self):
641 """Check if a domain has been terminated.
642 """
643 return self.state == STATE_VM_TERMINATED
645 def release_devices(self):
646 """Release all vm devices.
647 """
648 reboot = self.restart_pending()
649 for ctrl in self.getDeviceControllers():
650 if ctrl.isDestroyed(): continue
651 ctrl.destroyController(reboot=reboot)
653 def show(self):
654 """Print virtual machine info.
655 """
656 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
657 print "image:"
658 sxp.show(self.image)
659 print "]"
661 def init_domain(self):
662 """Initialize the domain memory.
663 """
664 if self.recreate:
665 return
666 if self.start_time is None:
667 self.start_time = time.time()
668 try:
669 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
670 except:
671 raise VmError('invalid cpu')
672 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
673 log.debug('init_domain> Created domain=%d name=%s memory=%d',
674 id, self.name, self.memory)
675 self.setdom(id)
677 def openChannel(self, key, local, remote):
678 """Create a control channel to the domain.
679 If saved info is available recreate the channel.
681 @param key db key for the saved data (if any)
682 @param local default local port
683 @param remote default remote port
684 """
685 db = self.db.addChild(key)
686 chan = channelFactory().restoreFromDB(db, self.id, local, remote)
687 #todo: save here?
688 #chan.saveToDB(db)
689 return chan
691 def eventChannel(self, key):
692 """Create an event channel to the domain.
693 If saved info is available recreate the channel.
695 @param key db key for the saved data (if any)
696 """
697 db = self.db.addChild(key)
698 return EventChannel.restoreFromDB(db, 0, self.id)
700 def create_channel(self):
701 """Create the channels to the domain.
702 """
703 self.channel = self.openChannel("channel", 0, 1)
704 self.store_channel = self.eventChannel("store_channel")
706 def create_configured_devices(self):
707 devices = sxp.children(self.config, 'device')
708 for d in devices:
709 dev_config = sxp.child0(d)
710 if dev_config is None:
711 raise VmError('invalid device')
712 dev_type = sxp.name(dev_config)
713 ctrl_type = get_device_handler(dev_type)
714 if ctrl_type is None:
715 raise VmError('unknown device type: ' + dev_type)
716 self.createDevice(ctrl_type, dev_config)
718 def create_devices(self):
719 """Create the devices for a vm.
721 @raise: VmError for invalid devices
722 """
723 if self.rebooting():
724 for ctrl in self.getDeviceControllers():
725 ctrl.initController(reboot=True)
726 else:
727 self.create_configured_devices()
728 self.image.createDeviceModel()
730 def device_create(self, dev_config):
731 """Create a new device.
733 @param dev_config: device configuration
734 """
735 dev_type = sxp.name(dev_config)
736 dev = self.createDevice(self, dev_config, change=True)
737 self.config.append(['device', dev.getConfig()])
738 return dev.sxpr()
740 def device_configure(self, dev_config, id):
741 """Configure an existing device.
743 @param dev_config: device configuration
744 @param id: device id
745 """
746 type = sxp.name(dev_config)
747 dev = self.getDevice(type, id)
748 old_config = dev.getConfig()
749 new_config = dev.configure(dev_config, change=True)
750 # Patch new config into vm config.
751 new_full_config = ['device', new_config]
752 old_full_config = ['device', old_config]
753 old_index = self.config.index(old_full_config)
754 self.config[old_index] = new_full_config
755 return new_config
757 def device_refresh(self, type, id):
758 """Refresh a device.
760 @param type: device type
761 @param id: device id
762 """
763 dev = self.getDevice(type, id)
764 dev.refresh()
766 def device_delete(self, type, id):
767 """Destroy and remove a device.
769 @param type: device type
770 @param id: device id
771 """
772 dev = self.getDevice(type, id)
773 dev_config = dev.getConfig()
774 if dev_config:
775 self.config.remove(['device', dev_config])
776 self.deleteDevice(type, dev.getId())
778 def configure_bootloader(self):
779 """Configure boot loader.
780 """
781 self.bootloader = sxp.child_value(self.config, "bootloader")
783 def configure_console(self):
784 """Configure the vm console port.
785 """
786 x = sxp.child_value(self.config, 'console')
787 if x:
788 try:
789 port = int(x)
790 except:
791 raise VmError('invalid console:' + str(x))
792 self.console_port = port
794 def configure_restart(self):
795 """Configure the vm restart mode.
796 """
797 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
798 if r not in restart_modes:
799 raise VmError('invalid restart mode: ' + str(r))
800 self.restart_mode = r;
802 def restart_needed(self, reason):
803 """Determine if the vm needs to be restarted when shutdown
804 for the given reason.
806 @param reason: shutdown reason
807 @return True if needs restart, False otherwise
808 """
809 if self.restart_mode == RESTART_NEVER:
810 return False
811 if self.restart_mode == RESTART_ALWAYS:
812 return True
813 if self.restart_mode == RESTART_ONREBOOT:
814 return reason == 'reboot'
815 return False
817 def restart_cancel(self):
818 """Cancel a vm restart.
819 """
820 self.restart_state = None
822 def restarting(self):
823 """Put the vm into restart mode.
824 """
825 self.restart_state = STATE_RESTART_PENDING
827 def restart_pending(self):
828 """Test if the vm has a pending restart.
829 """
830 return self.restart_state == STATE_RESTART_PENDING
832 def rebooting(self):
833 return self.restart_state == STATE_RESTART_BOOTING
835 def restart_check(self):
836 """Check if domain restart is OK.
837 To prevent restart loops, raise an error if it is
838 less than MINIMUM_RESTART_TIME seconds since the last restart.
839 """
840 tnow = time.time()
841 if self.restart_time is not None:
842 tdelta = tnow - self.restart_time
843 if tdelta < self.MINIMUM_RESTART_TIME:
844 self.restart_cancel()
845 msg = 'VM %s restarting too fast' % self.name
846 log.error(msg)
847 raise VmError(msg)
848 self.restart_time = tnow
849 self.restart_count += 1
851 def restart(self):
852 """Restart the domain after it has exited.
853 Reuses the domain id and console port.
855 """
856 try:
857 self.state = STATE_VM_OK
858 self.shutdown_pending = None
859 self.restart_check()
860 self.exportToDB()
861 self.restart_state = STATE_RESTART_BOOTING
862 if self.bootloader:
863 self.config = self.bootloader_config()
864 self.construct(self.config)
865 self.saveToDB()
866 finally:
867 self.restart_state = None
869 def bootloader_config(self):
870 # if we're restarting with a bootloader, we need to run it
871 # FIXME: this assumes the disk is the first device and
872 # that we're booting from the first disk
873 blcfg = None
874 # FIXME: this assumes that we want to use the first disk
875 dev = sxp.child_value(self.config, "device")
876 if dev:
877 disk = sxp.child_value(dev, "uname")
878 fn = blkdev_uname_to_file(disk)
879 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
880 if blcfg is None:
881 msg = "Had a bootloader specified, but can't find disk"
882 log.error(msg)
883 raise VmError(msg)
884 config = sxp.merge(['vm', blconfig ], self.config)
885 return config
887 def configure_backends(self):
888 """Set configuration flags if the vm is a backend for netif or blkif.
889 Configure the backends to use for vbd and vif if specified.
890 """
891 for c in sxp.children(self.config, 'backend'):
892 v = sxp.child0(c)
893 name = sxp.name(v)
894 if name == 'blkif':
895 self.blkif_backend = True
896 elif name == 'netif':
897 self.netif_backend = True
898 elif name == 'usbif':
899 self.usbif_backend = True
900 else:
901 raise VmError('invalid backend type:' + str(name))
903 def configure(self):
904 """Configure a vm.
906 """
907 self.configure_fields()
908 self.create_console()
909 self.create_devices()
910 self.create_blkif()
912 def create_console(self):
913 console = self.getConsole()
914 if not console:
915 config = ['console']
916 if self.console_port:
917 config.append(['console_port', self.console_port])
918 console = self.createDevice('console', config)
919 return console
921 def getConsole(self):
922 console_ctrl = self.getDeviceController("console", error=False)
923 if console_ctrl:
924 return console_ctrl.getDevice(0)
925 return None
927 def create_blkif(self):
928 """Create the block device interface (blkif) for the vm.
929 The vm needs a blkif even if it doesn't have any disks
930 at creation time, for example when it uses NFS root.
932 """
933 blkif = self.getDeviceController("vbd", error=False)
934 if not blkif:
935 blkif = self.createDeviceController("vbd")
936 backend = blkif.getBackend(0)
937 backend.connect(recreate=self.recreate)
939 def configure_fields(self):
940 """Process the vm configuration fields using the registered handlers.
941 """
942 index = {}
943 for field in sxp.children(self.config):
944 field_name = sxp.name(field)
945 field_index = index.get(field_name, 0)
946 field_handler = get_config_handler(field_name)
947 # Ignore unknown fields. Warn?
948 if field_handler:
949 v = field_handler(self, self.config, field, field_index)
950 else:
951 log.warning("Unknown config field %s", field_name)
952 index[field_name] = field_index + 1
954 def mem_target_set(self, target):
955 """Set domain memory target in pages.
956 """
957 if self.channel:
958 msg = messages.packMsg('mem_request_t', { 'target' : target * (1 << 8)} )
959 self.channel.writeRequest(msg)
961 def vcpu_hotplug(self, vcpu, state):
962 """Disable or enable VCPU in domain.
963 """
964 log.error("Holly Shit! %d %d\n" % (vcpu, state))
965 if self.channel:
966 if int(state) == 0:
967 msg = messages.packMsg('vcpu_hotplug_off_t', { 'vcpu' : vcpu} )
968 else:
969 msg = messages.packMsg('vcpu_hotplug_on_t', { 'vcpu' : vcpu} )
971 self.channel.writeRequest(msg)
973 def shutdown(self, reason, key=0):
974 msgtype = shutdown_messages.get(reason)
975 if not msgtype:
976 raise XendError('invalid reason:' + reason)
977 extra = {}
978 if reason == 'sysrq':
979 extra['key'] = key
980 if self.channel:
981 msg = messages.packMsg(msgtype, extra)
982 self.channel.writeRequest(msg)
983 if not reason in ['suspend', 'sysrq']:
984 self.shutdown_pending = {'start':time.time(), 'reason':reason,
985 'key':key}
987 def shutdown_time_left(self, timeout):
988 if not self.shutdown_pending:
989 return 0
990 return timeout - (time.time() - self.shutdown_pending['start'])
992 def vm_field_ignore(vm, config, val, index):
993 """Dummy config field handler used for fields with built-in handling.
995 @param vm: virtual machine
996 @param config: vm config
997 @param val: config field
998 @param index: field index
999 """
1000 pass
1002 def vm_field_maxmem(vm, config, val, index):
1003 """Configure vm memory limit.
1005 @param vm: virtual machine
1006 @param config: vm config
1007 @param val: config field
1008 @param index: field index
1009 """
1010 maxmem = sxp.child0(val)
1011 if maxmem is None:
1012 maxmem = vm.memory
1013 try:
1014 maxmem = int(maxmem)
1015 except:
1016 raise VmError("invalid maxmem: " + str(maxmem))
1017 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1019 #============================================================================
1020 # Register image handlers.
1021 from image import \
1022 addImageHandlerClass, \
1023 ImageHandler, \
1024 LinuxImageHandler, \
1025 VmxImageHandler
1027 addImageHandlerClass(LinuxImageHandler)
1028 addImageHandlerClass(VmxImageHandler)
1030 # Ignore the fields we already handle.
1031 add_config_handler('name', vm_field_ignore)
1032 add_config_handler('memory', vm_field_ignore)
1033 add_config_handler('ssidref', vm_field_ignore)
1034 add_config_handler('cpu', vm_field_ignore)
1035 add_config_handler('cpu_weight', vm_field_ignore)
1036 add_config_handler('console', vm_field_ignore)
1037 add_config_handler('restart', vm_field_ignore)
1038 add_config_handler('image', vm_field_ignore)
1039 add_config_handler('device', vm_field_ignore)
1040 add_config_handler('backend', vm_field_ignore)
1041 add_config_handler('vcpus', vm_field_ignore)
1042 add_config_handler('bootloader', vm_field_ignore)
1044 # Register other config handlers.
1045 add_config_handler('maxmem', vm_field_maxmem)
1047 #============================================================================
1048 # Register device controllers and their device config types.
1050 from server import console
1051 controller.addDevControllerClass("console", console.ConsoleController)
1053 from server import blkif
1054 controller.addDevControllerClass("vbd", blkif.BlkifController)
1055 add_device_handler("vbd", "vbd")
1057 from server import netif
1058 controller.addDevControllerClass("vif", netif.NetifController)
1059 add_device_handler("vif", "vif")
1061 from server import pciif
1062 controller.addDevControllerClass("pci", pciif.PciController)
1063 add_device_handler("pci", "pci")
1065 from xen.xend.server import usbif
1066 controller.addDevControllerClass("usb", usbif.UsbifController)
1067 add_device_handler("usb", "usb")
1069 #============================================================================