ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6500:c8549bb0d311

Don't clear console entries.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Tue Aug 30 19:47:39 2005 +0000 (2005-08-30)
parents b043928b0873
children 1fc6473ecc01
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 #============================================================================
18 """Representation of a single domain.
19 Includes support for domain construction, using
20 open-ended configurations.
22 Author: Mike Wray <mike.wray@hp.com>
24 """
26 import string, re
27 import os
28 import time
29 import threading
31 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
32 from xen.util.ip import check_subnet, get_current_ipgw
33 from xen.util.blkif import blkdev_uname_to_file
35 from xen.xend.server import controller
36 from xen.xend.server import SrvDaemon; xend = SrvDaemon.instance()
37 from xen.xend.server import messages
38 from xen.xend.server.channel import EventChannel, channelFactory
39 from xen.util.blkif import blkdev_name_to_number, expand_dev_name
41 from xen.xend import sxp
42 from xen.xend import Blkctl
43 from xen.xend.PrettyPrint import prettyprintstring
44 from xen.xend.XendBootloader import bootloader
45 from xen.xend.XendLogging import log
46 from xen.xend.XendError import XendError, VmError
47 from xen.xend.XendRoot import get_component
49 from xen.xend.uuid import getUuid
50 from xen.xend.xenstore import DBVar, XenNode, DBMap
52 """Shutdown code for poweroff."""
53 DOMAIN_POWEROFF = 0
55 """Shutdown code for reboot."""
56 DOMAIN_REBOOT = 1
58 """Shutdown code for suspend."""
59 DOMAIN_SUSPEND = 2
61 """Shutdown code for crash."""
62 DOMAIN_CRASH = 3
64 """Map shutdown codes to strings."""
65 shutdown_reasons = {
66 DOMAIN_POWEROFF: "poweroff",
67 DOMAIN_REBOOT : "reboot",
68 DOMAIN_SUSPEND : "suspend",
69 DOMAIN_CRASH : "crash",
70 }
72 RESTART_ALWAYS = 'always'
73 RESTART_ONREBOOT = 'onreboot'
74 RESTART_NEVER = 'never'
76 restart_modes = [
77 RESTART_ALWAYS,
78 RESTART_ONREBOOT,
79 RESTART_NEVER,
80 ]
82 STATE_RESTART_PENDING = 'pending'
83 STATE_RESTART_BOOTING = 'booting'
85 STATE_VM_OK = "ok"
86 STATE_VM_TERMINATED = "terminated"
87 STATE_VM_SUSPENDED = "suspended"
90 def domain_exists(name):
91 # See comment in XendDomain constructor.
92 xd = get_component('xen.xend.XendDomain')
93 return xd.domain_lookup_by_name(name)
95 def shutdown_reason(code):
96 """Get a shutdown reason from a code.
98 @param code: shutdown code
99 @type code: int
100 @return: shutdown reason
101 @rtype: string
102 """
103 return shutdown_reasons.get(code, "?")
105 config_handlers = {}
107 def add_config_handler(name, h):
108 """Add a handler for a config field.
110 @param name: field name
111 @param h: handler: fn(vm, config, field, index)
112 """
113 config_handlers[name] = h
115 def get_config_handler(name):
116 """Get a handler for a config field.
118 returns handler or None
119 """
120 return config_handlers.get(name)
122 """Table of handlers for devices.
123 Indexed by device type.
124 """
125 device_handlers = {}
127 def add_device_handler(name, type):
128 device_handlers[name] = type
130 def get_device_handler(name):
131 return device_handlers[name]
133 def dom_get(dom):
134 """Get info from xen for an existing domain.
136 @param dom: domain id
137 @return: info or None
138 """
139 domlist = xc.domain_getinfo(dom, 1)
140 if domlist and dom == domlist[0]['dom']:
141 return domlist[0]
142 return None
144 class XendDomainInfo:
145 """Virtual machine object."""
147 """Minimum time between domain restarts in seconds.
148 """
149 MINIMUM_RESTART_TIME = 20
151 def create(cls, parentdb, config):
152 """Create a VM from a configuration.
154 @param parentdb: parent db
155 @param config configuration
156 @raise: VmError for invalid configuration
157 """
158 uuid = getUuid()
159 db = parentdb.addChild(uuid)
160 vm = cls(db)
161 vm.construct(config)
162 vm.saveToDB(sync=True)
164 return vm
166 create = classmethod(create)
168 def recreate(cls, db, info):
169 """Create the VM object for an existing domain.
171 @param db: domain db
172 @param info: domain info from xc
173 """
174 dom = info['dom']
175 vm = cls(db)
176 vm.setdom(dom)
177 db.readDB()
178 vm.importFromDB()
179 config = vm.config
180 log.debug('info=' + str(info))
181 log.debug('config=' + prettyprintstring(config))
183 vm.memory = info['mem_kb']/1024
184 vm.target = info['mem_kb'] * 1024
186 if config:
187 try:
188 vm.recreate = True
189 vm.construct(config)
190 finally:
191 vm.recreate = False
192 else:
193 vm.setName("Domain-%d" % dom)
195 vm.exportToDB(save=True)
196 return vm
198 recreate = classmethod(recreate)
200 def restore(cls, parentdb, config, uuid=None):
201 """Create a domain and a VM object to do a restore.
203 @param parentdb: parent db
204 @param config: domain configuration
205 @param uuid: uuid to use
206 """
207 if not uuid:
208 uuid = getUuid()
209 db = parentdb.addChild(uuid)
210 vm = cls(db)
211 ssidref = int(sxp.child_value(config, 'ssidref'))
212 log.debug('restoring with ssidref='+str(ssidref))
213 id = xc.domain_create(ssidref = ssidref)
214 vm.setdom(id)
215 vm.clear_shutdown()
216 try:
217 vm.restore = True
218 vm.construct(config)
219 finally:
220 vm.restore = False
221 vm.exportToDB(save=True, sync=True)
222 return vm
224 restore = classmethod(restore)
226 __exports__ = [
227 DBVar('id', ty='int'),
228 DBVar('name', ty='str'),
229 DBVar('uuid', ty='str'),
230 DBVar('config', ty='sxpr'),
231 DBVar('start_time', ty='float'),
232 DBVar('state', ty='str'),
233 DBVar('store_mfn', ty='long'),
234 DBVar('console_mfn', ty='long'),
235 DBVar('restart_mode', ty='str'),
236 DBVar('restart_state', ty='str'),
237 DBVar('restart_time', ty='float'),
238 DBVar('restart_count', ty='int'),
239 DBVar('target', ty='long', path="memory/target"),
240 DBVar('device_model_pid', ty='int'),
241 ]
243 def __init__(self, db):
244 self.db = db
245 self.uuid = db.getName()
247 self.recreate = 0
248 self.restore = 0
250 self.config = None
251 self.id = None
252 self.cpu_weight = 1
253 self.start_time = None
254 self.name = None
255 self.memory = None
256 self.ssidref = None
257 self.image = None
259 self.target = None
261 self.channel = None
262 self.store_channel = None
263 self.store_mfn = None
264 self.console_channel = None
265 self.console_mfn = None
266 self.controllers = {}
268 self.info = None
269 self.blkif_backend = False
270 self.netif_backend = False
271 self.netif_idx = 0
273 #todo: state: running, suspended
274 self.state = STATE_VM_OK
275 self.state_updated = threading.Condition()
276 self.shutdown_pending = None
278 #todo: set to migrate info if migrating
279 self.migrate = None
281 self.restart_mode = RESTART_ONREBOOT
282 self.restart_state = None
283 self.restart_time = None
284 self.restart_count = 0
286 self.vcpus = 1
287 self.vcpusdb = {}
288 self.bootloader = None
289 self.device_model_pid = 0
291 def setDB(self, db):
292 self.db = db
294 def saveToDB(self, save=False, sync=False):
295 self.db.saveDB(save=save, sync=sync)
297 def exportToDB(self, save=False, sync=False):
298 if self.channel:
299 self.channel.saveToDB(self.db.addChild("channel"), save=save)
300 if self.store_channel:
301 self.store_channel.saveToDB(self.db.addChild("store_channel"),
302 save=save)
303 if self.console_channel:
304 self.console_channel.saveToDB(self.db.addChild("console_channel"),
305 save=save)
306 if self.image:
307 self.image.exportToDB(save=save, sync=sync)
308 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
310 def importFromDB(self):
311 self.db.importFromDB(self, fields=self.__exports__)
312 self.store_channel = self.eventChannel("store_channel")
314 def setdom(self, dom):
315 """Set the domain id.
317 @param dom: domain id
318 """
319 self.id = int(dom)
320 #self.db.id = self.id
322 def getDomain(self):
323 return self.id
325 def setName(self, name):
326 self.name = name
327 self.db.name = self.name
329 def getName(self):
330 return self.name
332 def getChannel(self):
333 return self.channel
335 def getStoreChannel(self):
336 return self.store_channel
338 def getConsoleChannel(self):
339 return self.console_channel
341 def update(self, info):
342 """Update with info from xc.domain_getinfo().
343 """
344 self.info = info
345 self.memory = self.info['mem_kb'] / 1024
346 self.ssidref = self.info['ssidref']
347 self.target = self.info['mem_kb'] * 1024
349 def state_set(self, state):
350 self.state_updated.acquire()
351 if self.state != state:
352 self.state = state
353 self.state_updated.notifyAll()
354 self.state_updated.release()
355 self.saveToDB()
357 def state_wait(self, state):
358 self.state_updated.acquire()
359 while self.state != state:
360 self.state_updated.wait()
361 self.state_updated.release()
363 def __str__(self):
364 s = "<domain"
365 s += " id=" + str(self.id)
366 s += " name=" + self.name
367 s += " memory=" + str(self.memory)
368 s += " ssidref=" + str(self.ssidref)
369 s += ">"
370 return s
372 __repr__ = __str__
374 def getDeviceTypes(self):
375 return self.controllers.keys()
377 def getDeviceControllers(self):
378 return self.controllers.values()
380 def getDeviceController(self, type, error=True):
381 ctrl = self.controllers.get(type)
382 if not ctrl and error:
383 raise XendError("invalid device type:" + type)
384 return ctrl
386 def findDeviceController(self, type):
387 return (self.getDeviceController(type, error=False)
388 or self.createDeviceController(type))
390 def createDeviceController(self, type):
391 ctrl = controller.createDevController(type, self, recreate=self.recreate)
392 self.controllers[type] = ctrl
393 return ctrl
395 def createDevice(self, type, devconfig, change=False):
396 if type == 'vbd':
397 typedev = sxp.child_value(devconfig, 'dev')
398 if re.match('^ioemu:', typedev):
399 return;
400 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
402 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
404 # create backend db
405 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
406 (type, self.uuid, devnum))
408 # create frontend db
409 db = self.db.addChild("/device/%s/%d" % (type, devnum))
411 db['virtual-device'] = "%i" % devnum
412 #db['backend'] = sxp.child_value(devconfig, 'backend', '0')
413 db['backend'] = backdb.getPath()
414 db['backend-id'] = "%i" % backdom.id
416 backdb['frontend'] = db.getPath()
417 (type, params) = string.split(sxp.child_value(devconfig, 'uname'), ':', 1)
418 node = Blkctl.block('bind', type, params)
419 backdb['frontend-id'] = "%i" % self.id
420 backdb['physical-device'] = "%li" % blkdev_name_to_number(node)
421 backdb.saveDB(save=True)
423 # Ok, super gross, this really doesn't belong in the frontend db...
424 db['type'] = type
425 db['node'] = node
426 db['params'] = params
427 db.saveDB(save=True)
429 return
431 if type == 'vif':
432 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
434 log.error(devconfig)
436 devnum = self.netif_idx
437 self.netif_idx += 1
439 # create backend db
440 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
441 (type, self.uuid, devnum))
443 # create frontend db
444 db = self.db.addChild("/device/%s/%d" % (type, devnum))
446 backdb['frontend'] = db.getPath()
447 backdb['frontend-id'] = "%i" % self.id
448 backdb['handle'] = "%i" % devnum
449 backdb.saveDB(save=True)
451 db['backend'] = backdb.getPath()
452 db['backend-id'] = "%i" % backdom.id
453 db['handle'] = "%i" % devnum
454 log.error(sxp.child_value(devconfig, 'mac'))
455 db['mac'] = sxp.child_value(devconfig, 'mac')
457 db.saveDB(save=True)
459 return
461 ctrl = self.findDeviceController(type)
462 return ctrl.createDevice(devconfig, recreate=self.recreate,
463 change=change)
465 def configureDevice(self, type, id, devconfig):
466 ctrl = self.getDeviceController(type)
467 return ctrl.configureDevice(id, devconfig)
469 def destroyDevice(self, type, id, change=False, reboot=False):
470 ctrl = self.getDeviceController(type)
471 return ctrl.destroyDevice(id, change=change, reboot=reboot)
473 def deleteDevice(self, type, id):
474 ctrl = self.getDeviceController(type)
475 return ctrl.deleteDevice(id)
477 def getDevice(self, type, id, error=True):
478 ctrl = self.getDeviceController(type)
479 return ctrl.getDevice(id, error=error)
481 def getDeviceIds(self, type):
482 ctrl = self.getDeviceController(type)
483 return ctrl.getDeviceIds()
485 def getDeviceSxprs(self, type):
486 ctrl = self.getDeviceController(type)
487 return ctrl.getDeviceSxprs()
489 def sxpr(self):
490 sxpr = ['domain',
491 ['id', self.id],
492 ['name', self.name],
493 ['memory', self.memory],
494 ['ssidref', self.ssidref],
495 ['target', self.target] ]
496 if self.uuid:
497 sxpr.append(['uuid', self.uuid])
498 if self.info:
499 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
500 run = (self.info['running'] and 'r') or '-'
501 block = (self.info['blocked'] and 'b') or '-'
502 pause = (self.info['paused'] and 'p') or '-'
503 shut = (self.info['shutdown'] and 's') or '-'
504 crash = (self.info['crashed'] and 'c') or '-'
505 state = run + block + pause + shut + crash
506 sxpr.append(['state', state])
507 if self.info['shutdown']:
508 reason = shutdown_reason(self.info['shutdown_reason'])
509 sxpr.append(['shutdown_reason', reason])
510 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
511 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
512 sxpr.append(['vcpus', self.info['vcpus']])
513 sxpr.append(['cpumap', self.info['cpumap']])
514 # build a string, using '|' to seperate items, show only up
515 # to number of vcpus in domain, and trim the trailing '|'
516 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
517 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
519 if self.start_time:
520 up_time = time.time() - self.start_time
521 sxpr.append(['up_time', str(up_time) ])
522 sxpr.append(['start_time', str(self.start_time) ])
524 if self.channel:
525 sxpr.append(self.channel.sxpr())
526 if self.store_channel:
527 sxpr.append(self.store_channel.sxpr())
528 if self.store_mfn:
529 sxpr.append(['store_mfn', self.store_mfn])
530 if self.console_channel:
531 sxpr.append(['console_channel', self.console_channel.sxpr()])
532 if self.console_mfn:
533 sxpr.append(['console_mfn', self.console_mfn])
534 # already in (devices)
535 # console = self.getConsole()
536 # if console:
537 # sxpr.append(console.sxpr())
539 if self.restart_count:
540 sxpr.append(['restart_count', self.restart_count])
541 if self.restart_state:
542 sxpr.append(['restart_state', self.restart_state])
543 if self.restart_time:
544 sxpr.append(['restart_time', str(self.restart_time)])
546 devs = self.sxpr_devices()
547 if devs:
548 sxpr.append(devs)
549 if self.config:
550 sxpr.append(['config', self.config])
551 if self.device_model_pid:
552 sxpr.append(['device_model_pid',self.device_model_pid])
553 return sxpr
555 def sxpr_devices(self):
556 sxpr = []
557 for ty in self.getDeviceTypes():
558 devs = self.getDeviceSxprs(ty)
559 sxpr += devs
560 if sxpr:
561 sxpr.insert(0, 'devices')
562 else:
563 sxpr = None
564 return sxpr
566 def check_name(self, name):
567 """Check if a vm name is valid. Valid names contain alphabetic characters,
568 digits, or characters in '_-.:/+'.
569 The same name cannot be used for more than one vm at the same time.
571 @param name: name
572 @raise: VMerror if invalid
573 """
574 if self.recreate: return
575 if name is None or name == '':
576 raise VmError('missing vm name')
577 for c in name:
578 if c in string.digits: continue
579 if c in '_-.:/+': continue
580 if c in string.ascii_letters: continue
581 raise VmError('invalid vm name')
582 dominfo = domain_exists(name)
583 # When creating or rebooting, a domain with my name should not exist.
584 # When restoring, a domain with my name will exist, but it should have
585 # my domain id.
586 if not dominfo:
587 return
588 if dominfo.is_terminated():
589 return
590 if not self.id or (dominfo.id != self.id):
591 raise VmError('vm name clash: ' + name)
593 def construct(self, config):
594 """Construct the vm instance from its configuration.
596 @param config: configuration
597 @raise: VmError on error
598 """
599 # todo - add support for scheduling params?
600 self.config = config
601 try:
602 # Initial domain create.
603 self.setName(sxp.child_value(config, 'name'))
604 self.check_name(self.name)
605 self.init_image()
606 self.configure_cpus(config)
607 self.init_domain()
608 self.register_domain()
609 self.configure_bootloader()
611 # Create domain devices.
612 self.configure_backends()
613 self.configure_restart()
614 self.construct_image()
615 self.configure()
616 self.exportToDB(save=True)
617 except Exception, ex:
618 # Catch errors, cleanup and re-raise.
619 print 'Domain construction error:', ex
620 import traceback
621 traceback.print_exc()
622 self.destroy()
623 raise
625 def register_domain(self):
626 xd = get_component('xen.xend.XendDomain')
627 xd._add_domain(self)
628 self.exportToDB(save=True)
630 def configure_cpus(self, config):
631 try:
632 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
633 except:
634 raise VmError('invalid cpu weight')
635 self.memory = int(sxp.child_value(config, 'memory'))
636 if self.memory is None:
637 raise VmError('missing memory size')
638 self.target = self.memory * (1 << 20)
639 self.ssidref = int(sxp.child_value(config, 'ssidref'))
640 cpu = sxp.child_value(config, 'cpu')
641 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
642 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
643 try:
644 image = sxp.child_value(self.config, 'image')
645 vcpus = sxp.child_value(image, 'vcpus')
646 if vcpus:
647 self.vcpus = int(vcpus)
648 except:
649 raise VmError('invalid vcpus value')
651 def exportVCPUSToDB(self, vcpus):
652 for v in range(0,vcpus):
653 path = "/cpu/%d"%(v)
654 if not self.vcpusdb.has_key(path):
655 self.vcpusdb[path] = self.db.addChild(path)
656 db = self.vcpusdb[path]
657 log.debug("writing key availability=online to path %s in store"%(path))
658 db['availability'] = "online"
659 db.saveDB(save=True)
661 def init_image(self):
662 """Create boot image handler for the domain.
663 """
664 image = sxp.child_value(self.config, 'image')
665 if image is None:
666 raise VmError('missing image')
667 self.image = ImageHandler.create(self, image)
669 def construct_image(self):
670 """Construct the boot image for the domain.
671 """
672 self.create_channel()
673 self.image.createImage()
674 self.exportToDB()
675 if self.store_channel and self.store_mfn >= 0:
676 self.db.introduceDomain(self.id,
677 self.store_mfn,
678 self.store_channel)
679 # get the configured value of vcpus and update store
680 self.exportVCPUSToDB(self.vcpus)
682 def delete(self):
683 """Delete the vm's db.
684 """
685 if dom_get(self.id):
686 return
687 self.id = None
688 self.saveToDB(sync=True)
689 try:
690 # Todo: eventually will have to wait for devices to signal
691 # destruction before can delete the db.
692 if self.db:
693 self.db.delete()
694 except Exception, ex:
695 log.warning("error in domain db delete: %s", ex)
696 pass
698 def destroy_domain(self):
699 """Destroy the vm's domain.
700 The domain will not finally go away unless all vm
701 devices have been released.
702 """
703 if self.id is None:
704 return
705 try:
706 xc.domain_destroy(dom=self.id)
707 except Exception, err:
708 log.exception("Domain destroy failed: %s", self.name)
710 def cleanup(self):
711 """Cleanup vm resources: release devices.
712 """
713 self.state = STATE_VM_TERMINATED
714 self.release_devices()
715 if self.channel:
716 try:
717 self.channel.close()
718 self.channel = None
719 except:
720 pass
721 if self.store_channel:
722 try:
723 self.store_channel.close()
724 self.store_channel = None
725 except:
726 pass
727 try:
728 self.db.releaseDomain(self.id)
729 except Exception, ex:
730 log.warning("error in domain release on xenstore: %s", ex)
731 pass
732 if self.console_channel:
733 # notify processes using this cosole?
734 try:
735 self.console_channel.close()
736 self.console_channel = None
737 except:
738 pass
739 if self.image:
740 try:
741 self.device_model_pid = 0
742 self.image.destroy()
743 self.image = None
744 except:
745 pass
747 def destroy(self):
748 """Clenup vm and destroy domain.
749 """
750 self.cleanup()
751 self.destroy_domain()
752 self.saveToDB()
753 return 0
755 def is_terminated(self):
756 """Check if a domain has been terminated.
757 """
758 return self.state == STATE_VM_TERMINATED
760 def release_devices(self):
761 """Release all vm devices.
762 """
763 reboot = self.restart_pending()
764 for ctrl in self.getDeviceControllers():
765 if ctrl.isDestroyed(): continue
766 ctrl.destroyController(reboot=reboot)
767 ddb = self.db.addChild("/device")
768 for type in ddb.keys():
769 if type == 'vbd':
770 typedb = ddb.addChild(type)
771 for dev in typedb.keys():
772 devdb = typedb.addChild(str(dev))
773 Blkctl.block('unbind', devdb['type'].getData(),
774 devdb['node'].getData())
775 typedb[dev].delete()
776 typedb.saveDB(save=True)
777 if type == 'vif':
778 typedb = ddb.addChild(type)
779 for dev in typedb.keys():
780 typedb[dev].delete()
781 typedb.saveDB(save=True)
783 def show(self):
784 """Print virtual machine info.
785 """
786 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
787 print "image:"
788 sxp.show(self.image)
789 print "]"
791 def init_domain(self):
792 """Initialize the domain memory.
793 """
794 if self.recreate:
795 return
796 if self.start_time is None:
797 self.start_time = time.time()
798 try:
799 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
800 except:
801 raise VmError('invalid cpu')
802 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
803 log.debug('init_domain> Created domain=%d name=%s memory=%d',
804 id, self.name, self.memory)
805 self.setdom(id)
807 def openChannel(self, key, local, remote):
808 """Create a control channel to the domain.
809 If saved info is available recreate the channel.
811 @param key db key for the saved data (if any)
812 @param local default local port
813 @param remote default remote port
814 """
815 db = self.db.addChild(key)
816 chan = channelFactory().restoreFromDB(db, self.id, local, remote)
817 #todo: save here?
818 #chan.saveToDB(db)
819 return chan
821 def eventChannel(self, key):
822 """Create an event channel to the domain.
823 If saved info is available recreate the channel.
825 @param key db key for the saved data (if any)
826 """
827 db = self.db.addChild(key)
828 return EventChannel.restoreFromDB(db, 0, self.id)
830 def create_channel(self):
831 """Create the channels to the domain.
832 """
833 self.channel = self.openChannel("channel", 0, 1)
834 self.store_channel = self.eventChannel("store_channel")
835 self.console_channel = self.eventChannel("console_channel")
837 def create_configured_devices(self):
838 devices = sxp.children(self.config, 'device')
839 for d in devices:
840 dev_config = sxp.child0(d)
841 if dev_config is None:
842 raise VmError('invalid device')
843 dev_type = sxp.name(dev_config)
844 ctrl_type = get_device_handler(dev_type)
845 if ctrl_type is None:
846 raise VmError('unknown device type: ' + dev_type)
847 self.createDevice(ctrl_type, dev_config)
849 def create_devices(self):
850 """Create the devices for a vm.
852 @raise: VmError for invalid devices
853 """
854 if self.rebooting():
855 for ctrl in self.getDeviceControllers():
856 ctrl.initController(reboot=True)
857 else:
858 self.create_configured_devices()
859 if not self.device_model_pid:
860 self.device_model_pid = self.image.createDeviceModel()
862 def device_create(self, dev_config):
863 """Create a new device.
865 @param dev_config: device configuration
866 """
867 dev_type = sxp.name(dev_config)
868 dev = self.createDevice(dev_type, dev_config, change=True)
869 self.config.append(['device', dev.getConfig()])
870 return dev.sxpr()
872 def device_configure(self, dev_config, id):
873 """Configure an existing device.
875 @param dev_config: device configuration
876 @param id: device id
877 """
878 type = sxp.name(dev_config)
879 dev = self.getDevice(type, id)
880 old_config = dev.getConfig()
881 new_config = dev.configure(dev_config, change=True)
882 # Patch new config into vm config.
883 new_full_config = ['device', new_config]
884 old_full_config = ['device', old_config]
885 old_index = self.config.index(old_full_config)
886 self.config[old_index] = new_full_config
887 return new_config
889 def device_refresh(self, type, id):
890 """Refresh a device.
892 @param type: device type
893 @param id: device id
894 """
895 dev = self.getDevice(type, id)
896 dev.refresh()
898 def device_delete(self, type, id):
899 """Destroy and remove a device.
901 @param type: device type
902 @param id: device id
903 """
904 dev = self.getDevice(type, id)
905 dev_config = dev.getConfig()
906 if dev_config:
907 self.config.remove(['device', dev_config])
908 self.deleteDevice(type, dev.getId())
910 def configure_bootloader(self):
911 """Configure boot loader.
912 """
913 self.bootloader = sxp.child_value(self.config, "bootloader")
915 def configure_restart(self):
916 """Configure the vm restart mode.
917 """
918 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
919 if r not in restart_modes:
920 raise VmError('invalid restart mode: ' + str(r))
921 self.restart_mode = r;
923 def restart_needed(self, reason):
924 """Determine if the vm needs to be restarted when shutdown
925 for the given reason.
927 @param reason: shutdown reason
928 @return True if needs restart, False otherwise
929 """
930 if self.restart_mode == RESTART_NEVER:
931 return False
932 if self.restart_mode == RESTART_ALWAYS:
933 return True
934 if self.restart_mode == RESTART_ONREBOOT:
935 return reason == 'reboot'
936 return False
938 def restart_cancel(self):
939 """Cancel a vm restart.
940 """
941 self.restart_state = None
943 def restarting(self):
944 """Put the vm into restart mode.
945 """
946 self.restart_state = STATE_RESTART_PENDING
948 def restart_pending(self):
949 """Test if the vm has a pending restart.
950 """
951 return self.restart_state == STATE_RESTART_PENDING
953 def rebooting(self):
954 return self.restart_state == STATE_RESTART_BOOTING
956 def restart_check(self):
957 """Check if domain restart is OK.
958 To prevent restart loops, raise an error if it is
959 less than MINIMUM_RESTART_TIME seconds since the last restart.
960 """
961 tnow = time.time()
962 if self.restart_time is not None:
963 tdelta = tnow - self.restart_time
964 if tdelta < self.MINIMUM_RESTART_TIME:
965 self.restart_cancel()
966 msg = 'VM %s restarting too fast' % self.name
967 log.error(msg)
968 raise VmError(msg)
969 self.restart_time = tnow
970 self.restart_count += 1
972 def restart(self):
973 """Restart the domain after it has exited.
974 Reuses the domain id
976 """
977 try:
978 self.state = STATE_VM_OK
979 self.shutdown_pending = None
980 self.restart_check()
981 self.exportToDB()
982 self.restart_state = STATE_RESTART_BOOTING
983 if self.bootloader:
984 self.config = self.bootloader_config()
985 self.construct(self.config)
986 self.saveToDB()
987 finally:
988 self.restart_state = None
990 def bootloader_config(self):
991 # if we're restarting with a bootloader, we need to run it
992 # FIXME: this assumes the disk is the first device and
993 # that we're booting from the first disk
994 blcfg = None
995 # FIXME: this assumes that we want to use the first disk
996 dev = sxp.child_value(self.config, "device")
997 if dev:
998 disk = sxp.child_value(dev, "uname")
999 fn = blkdev_uname_to_file(disk)
1000 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
1001 if blcfg is None:
1002 msg = "Had a bootloader specified, but can't find disk"
1003 log.error(msg)
1004 raise VmError(msg)
1005 config = sxp.merge(['vm', blconfig ], self.config)
1006 return config
1008 def configure_backends(self):
1009 """Set configuration flags if the vm is a backend for netif or blkif.
1010 Configure the backends to use for vbd and vif if specified.
1011 """
1012 for c in sxp.children(self.config, 'backend'):
1013 v = sxp.child0(c)
1014 name = sxp.name(v)
1015 if name == 'blkif':
1016 self.blkif_backend = True
1017 elif name == 'netif':
1018 self.netif_backend = True
1019 elif name == 'usbif':
1020 self.usbif_backend = True
1021 else:
1022 raise VmError('invalid backend type:' + str(name))
1024 def configure(self):
1025 """Configure a vm.
1027 """
1028 self.configure_fields()
1029 self.create_devices()
1030 self.create_blkif()
1031 self.publish_console()
1033 def create_blkif(self):
1034 """Create the block device interface (blkif) for the vm.
1035 The vm needs a blkif even if it doesn't have any disks
1036 at creation time, for example when it uses NFS root.
1038 """
1039 return
1040 blkif = self.getDeviceController("vbd", error=False)
1041 if not blkif:
1042 blkif = self.createDeviceController("vbd")
1043 backend = blkif.getBackend(0)
1044 backend.connect(recreate=self.recreate)
1046 def publish_console(self):
1047 db = DBMap(db=XenNode("/console/%d" % self.id))
1048 db['domain'] = self.db.getPath()
1049 db.saveDB(save=True)
1051 def configure_fields(self):
1052 """Process the vm configuration fields using the registered handlers.
1053 """
1054 index = {}
1055 for field in sxp.children(self.config):
1056 field_name = sxp.name(field)
1057 field_index = index.get(field_name, 0)
1058 field_handler = get_config_handler(field_name)
1059 # Ignore unknown fields. Warn?
1060 if field_handler:
1061 v = field_handler(self, self.config, field, field_index)
1062 else:
1063 log.warning("Unknown config field %s", field_name)
1064 index[field_name] = field_index + 1
1066 def mem_target_set(self, target):
1067 """Set domain memory target in bytes.
1068 """
1069 if target:
1070 self.target = target * (1 << 20)
1071 # Commit to XenStore immediately
1072 self.exportToDB()
1074 def vcpu_hotplug(self, vcpu, state):
1075 """Disable or enable VCPU in domain.
1076 """
1077 db = ""
1078 try:
1079 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1080 except:
1081 log.error("Invalid VCPU")
1082 return
1084 if self.store_channel:
1085 if int(state) == 0:
1086 db['availability'] = "offline"
1087 else:
1088 db['availability'] = "online"
1090 db.saveDB(save=True)
1092 def shutdown(self, reason):
1093 if not reason in shutdown_reasons.values():
1094 raise XendError('invalid reason:' + reason)
1095 db = self.db.addChild("/control");
1096 db['shutdown'] = reason;
1097 db.saveDB(save=True);
1098 if not reason in ['suspend']:
1099 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1101 def clear_shutdown(self):
1102 db = self.db.addChild("/control")
1103 db['shutdown'] = ""
1104 db.saveDB(save=True)
1106 def send_sysrq(self, key=0):
1107 db = self.db.addChild("/control");
1108 db['sysrq'] = '%c' % key;
1109 db.saveDB(save=True);
1111 def shutdown_time_left(self, timeout):
1112 if not self.shutdown_pending:
1113 return 0
1114 return timeout - (time.time() - self.shutdown_pending['start'])
1116 def dom0_init_store(self):
1117 if not self.store_channel:
1118 self.store_channel = self.eventChannel("store_channel")
1119 self.store_mfn = xc.init_store(self.store_channel.port2)
1120 if self.store_mfn >= 0:
1121 self.db.introduceDomain(self.id, self.store_mfn,
1122 self.store_channel)
1123 self.exportToDB(save=True, sync=True)
1124 # get run-time value of vcpus and update store
1125 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1127 def vm_field_ignore(vm, config, val, index):
1128 """Dummy config field handler used for fields with built-in handling.
1130 @param vm: virtual machine
1131 @param config: vm config
1132 @param val: config field
1133 @param index: field index
1134 """
1135 pass
1137 def vm_field_maxmem(vm, config, val, index):
1138 """Configure vm memory limit.
1140 @param vm: virtual machine
1141 @param config: vm config
1142 @param val: config field
1143 @param index: field index
1144 """
1145 maxmem = sxp.child0(val)
1146 if maxmem is None:
1147 maxmem = vm.memory
1148 try:
1149 maxmem = int(maxmem)
1150 except:
1151 raise VmError("invalid maxmem: " + str(maxmem))
1152 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1154 #============================================================================
1155 # Register image handlers.
1156 from image import \
1157 addImageHandlerClass, \
1158 ImageHandler, \
1159 LinuxImageHandler, \
1160 VmxImageHandler
1162 addImageHandlerClass(LinuxImageHandler)
1163 addImageHandlerClass(VmxImageHandler)
1165 # Ignore the fields we already handle.
1166 add_config_handler('name', vm_field_ignore)
1167 add_config_handler('memory', vm_field_ignore)
1168 add_config_handler('ssidref', vm_field_ignore)
1169 add_config_handler('cpu', vm_field_ignore)
1170 add_config_handler('cpu_weight', vm_field_ignore)
1171 add_config_handler('restart', vm_field_ignore)
1172 add_config_handler('image', vm_field_ignore)
1173 add_config_handler('device', vm_field_ignore)
1174 add_config_handler('backend', vm_field_ignore)
1175 add_config_handler('vcpus', vm_field_ignore)
1176 add_config_handler('bootloader', vm_field_ignore)
1178 # Register other config handlers.
1179 add_config_handler('maxmem', vm_field_maxmem)
1181 #============================================================================
1182 # Register device controllers and their device config types.
1184 from server import blkif
1185 controller.addDevControllerClass("vbd", blkif.BlkifController)
1186 add_device_handler("vbd", "vbd")
1188 from server import netif
1189 controller.addDevControllerClass("vif", netif.NetifController)
1190 add_device_handler("vif", "vif")
1192 from server import pciif
1193 controller.addDevControllerClass("pci", pciif.PciController)
1194 add_device_handler("pci", "pci")
1196 from xen.xend.server import usbif
1197 controller.addDevControllerClass("usb", usbif.UsbifController)
1198 add_device_handler("usb", "usb")
1200 #============================================================================