xen-vtx-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6781:8ca0f98ba8e2

merge?
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 13 15:33:37 2005 +0000 (2005-09-13)
parents 4ad19fe76d50 4d899a738d59
children 72e4e2aab342
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.channel import EventChannel
38 from xen.util.blkif import blkdev_name_to_number, expand_dev_name
40 from xen.xend import sxp
41 from xen.xend import Blkctl
42 from xen.xend.PrettyPrint import prettyprintstring
43 from xen.xend.XendBootloader import bootloader
44 from xen.xend.XendLogging import log
45 from xen.xend.XendError import XendError, VmError
46 from xen.xend.XendRoot import get_component
48 from xen.xend.uuid import getUuid
49 from xen.xend.xenstore import DBVar, XenNode, DBMap
50 from xen.xend.xenstore.xstransact import xstransact
51 from xen.xend.xenstore.xsutil import IntroduceDomain
53 """Shutdown code for poweroff."""
54 DOMAIN_POWEROFF = 0
56 """Shutdown code for reboot."""
57 DOMAIN_REBOOT = 1
59 """Shutdown code for suspend."""
60 DOMAIN_SUSPEND = 2
62 """Shutdown code for crash."""
63 DOMAIN_CRASH = 3
65 """Map shutdown codes to strings."""
66 shutdown_reasons = {
67 DOMAIN_POWEROFF: "poweroff",
68 DOMAIN_REBOOT : "reboot",
69 DOMAIN_SUSPEND : "suspend",
70 DOMAIN_CRASH : "crash",
71 }
73 RESTART_ALWAYS = 'always'
74 RESTART_ONREBOOT = 'onreboot'
75 RESTART_NEVER = 'never'
77 restart_modes = [
78 RESTART_ALWAYS,
79 RESTART_ONREBOOT,
80 RESTART_NEVER,
81 ]
83 STATE_RESTART_PENDING = 'pending'
84 STATE_RESTART_BOOTING = 'booting'
86 STATE_VM_OK = "ok"
87 STATE_VM_TERMINATED = "terminated"
88 STATE_VM_SUSPENDED = "suspended"
91 def domain_exists(name):
92 # See comment in XendDomain constructor.
93 xd = get_component('xen.xend.XendDomain')
94 return xd.domain_lookup_by_name(name)
96 def shutdown_reason(code):
97 """Get a shutdown reason from a code.
99 @param code: shutdown code
100 @type code: int
101 @return: shutdown reason
102 @rtype: string
103 """
104 return shutdown_reasons.get(code, "?")
106 def dom_get(dom):
107 """Get info from xen for an existing domain.
109 @param dom: domain id
110 @return: info or None
111 """
112 domlist = xc.domain_getinfo(dom, 1)
113 if domlist and dom == domlist[0]['dom']:
114 return domlist[0]
115 return None
117 class XendDomainInfo:
118 """Virtual machine object."""
120 """Minimum time between domain restarts in seconds.
121 """
122 MINIMUM_RESTART_TIME = 20
124 def create(cls, parentdb, config):
125 """Create a VM from a configuration.
127 @param parentdb: parent db
128 @param config configuration
129 @raise: VmError for invalid configuration
130 """
131 uuid = getUuid()
132 db = parentdb.addChild("%s/xend" % uuid)
133 path = parentdb.getPath()
134 vm = cls(uuid, path, db)
135 vm.construct(config)
136 vm.saveToDB(sync=True)
138 return vm
140 create = classmethod(create)
142 def recreate(cls, uuid, db, info):
143 """Create the VM object for an existing domain.
145 @param db: domain db
146 @param info: domain info from xc
147 """
148 dom = info['dom']
149 path = "/".join(db.getPath().split("/")[0:-2])
150 vm = cls(uuid, path, db)
151 vm.setdom(dom)
152 try:
153 db.readDB()
154 except: pass
155 vm.importFromDB()
156 config = vm.config
157 log.debug('info=' + str(info))
158 log.debug('config=' + prettyprintstring(config))
160 vm.memory = info['mem_kb']/1024
161 vm.target = info['mem_kb'] * 1024
163 if config:
164 try:
165 vm.recreate = True
166 vm.construct(config)
167 finally:
168 vm.recreate = False
169 else:
170 vm.setName("Domain-%d" % dom)
172 vm.exportToDB(save=True)
173 return vm
175 recreate = classmethod(recreate)
177 def restore(cls, parentdb, config, uuid=None):
178 """Create a domain and a VM object to do a restore.
180 @param parentdb: parent db
181 @param config: domain configuration
182 @param uuid: uuid to use
183 """
184 if not uuid:
185 uuid = getUuid()
186 db = parentdb.addChild("%s/xend" % uuid)
187 path = parentdb.getPath()
188 vm = cls(uuid, path, db)
189 ssidref = int(sxp.child_value(config, 'ssidref'))
190 log.debug('restoring with ssidref='+str(ssidref))
191 id = xc.domain_create(ssidref = ssidref)
192 vm.setdom(id)
193 vm.clear_shutdown()
194 try:
195 vm.restore = True
196 vm.construct(config)
197 finally:
198 vm.restore = False
199 vm.exportToDB(save=True, sync=True)
200 return vm
202 restore = classmethod(restore)
204 __exports__ = [
205 DBVar('id', ty='int'),
206 DBVar('name', ty='str'),
207 DBVar('uuid', ty='str'),
208 DBVar('config', ty='sxpr'),
209 DBVar('start_time', ty='float'),
210 DBVar('state', ty='str'),
211 DBVar('restart_mode', ty='str'),
212 DBVar('restart_state', ty='str'),
213 DBVar('restart_time', ty='float'),
214 DBVar('restart_count', ty='int'),
215 DBVar('target', ty='long', path="memory/target"),
216 DBVar('device_model_pid', ty='int'),
217 ]
219 def __init__(self, uuid, path, db):
220 self.uuid = uuid
221 self.path = path + "/" + uuid
222 self.db = db
224 self.recreate = 0
225 self.restore = 0
227 self.config = None
228 self.id = None
229 self.cpu_weight = 1
230 self.start_time = None
231 self.name = None
232 self.memory = None
233 self.ssidref = None
234 self.image = None
236 self.target = None
238 self.store_channel = None
239 self.store_mfn = None
240 self.console_channel = None
241 self.console_mfn = None
242 self.controllers = {}
244 self.info = None
245 self.blkif_backend = False
246 self.netif_backend = False
247 self.netif_idx = 0
248 self.tpmif_backend = False
250 #todo: state: running, suspended
251 self.state = STATE_VM_OK
252 self.state_updated = threading.Condition()
253 self.shutdown_pending = None
255 #todo: set to migrate info if migrating
256 self.migrate = None
258 self.restart_mode = RESTART_ONREBOOT
259 self.restart_state = None
260 self.restart_time = None
261 self.restart_count = 0
263 self.vcpus = 1
264 self.vcpusdb = {}
265 self.bootloader = None
266 self.device_model_pid = 0
268 def setDB(self, db):
269 self.db = db
271 def saveToDB(self, save=False, sync=False):
272 self.db.saveDB(save=save, sync=sync)
274 def exportToDB(self, save=False, sync=False):
275 if self.image:
276 self.image.exportToDB(save=save, sync=sync)
277 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
279 def importFromDB(self):
280 self.db.importFromDB(self, fields=self.__exports__)
281 self.store_channel = self.eventChannel("store/port")
283 def setdom(self, dom):
284 """Set the domain id.
286 @param dom: domain id
287 """
288 self.id = int(dom)
289 #self.db.id = self.id
291 def getDomain(self):
292 return self.id
294 def setName(self, name):
295 self.name = name
296 self.db.name = self.name
298 def getName(self):
299 return self.name
301 def setStoreRef(self, ref):
302 self.store_mfn = ref
303 if ref:
304 xstransact.Write(self.path, "store/ring-ref", "%i" % ref)
305 else:
306 xstransact.Remove(self.path, "store/ring-ref")
308 def setStoreChannel(self, channel):
309 if self.store_channel and self.store_channel != channel:
310 self.store_channel.close()
311 self.store_channel = channel
312 if channel:
313 xstransact.Write(self.path, "store/port", "%i" % channel.port1)
314 else:
315 xstransact.Remove(self.path, "store/port")
317 def setConsoleRef(self, ref):
318 self.console_mfn = ref
319 if ref:
320 xstransact.Write(self.path, "console/ring-ref", "%i" % ref)
321 else:
322 xstransact.Remove(self.path, "console/ring-ref")
324 def update(self, info=None):
325 """Update with info from xc.domain_getinfo().
326 """
327 self.info = info or dom_get(self.id)
328 self.memory = self.info['mem_kb'] / 1024
329 self.ssidref = self.info['ssidref']
330 self.target = self.info['mem_kb'] * 1024
332 def state_set(self, state):
333 self.state_updated.acquire()
334 if self.state != state:
335 self.state = state
336 self.state_updated.notifyAll()
337 self.state_updated.release()
338 self.saveToDB()
340 def state_wait(self, state):
341 self.state_updated.acquire()
342 while self.state != state:
343 self.state_updated.wait()
344 self.state_updated.release()
346 def __str__(self):
347 s = "<domain"
348 s += " id=" + str(self.id)
349 s += " name=" + self.name
350 s += " memory=" + str(self.memory)
351 s += " ssidref=" + str(self.ssidref)
352 s += ">"
353 return s
355 __repr__ = __str__
357 def getDeviceController(self, type, error=True):
358 ctrl = self.controllers.get(type)
359 if not ctrl and error:
360 raise XendError("invalid device type:" + type)
361 return ctrl
363 def findDeviceController(self, type):
364 return (self.getDeviceController(type, error=False)
365 or self.createDeviceController(type))
367 def createDeviceController(self, type):
368 ctrl = controller.createDevController(type, self, recreate=self.recreate)
369 self.controllers[type] = ctrl
370 return ctrl
372 def createDevice(self, type, devconfig, change=False):
373 if self.recreate:
374 return
375 if type == 'vbd':
376 typedev = sxp.child_value(devconfig, 'dev')
377 if re.match('^ioemu:', typedev):
378 return;
380 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
382 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
384 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
385 self.uuid, devnum)
386 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
388 front = { 'backend' : backpath,
389 'backend-id' : "%i" % backdom.id,
390 'virtual-device' : "%i" % devnum }
391 xstransact.Write(frontpath, front)
393 (type, params) = string.split(sxp.child_value(devconfig,
394 'uname'), ':', 1)
395 back = { 'type' : type,
396 'params' : params,
397 'frontend' : frontpath,
398 'frontend-id' : "%i" % self.id }
399 xstransact.Write(backpath, back)
401 return
403 if type == 'vif':
404 from xen.xend import XendRoot
405 xroot = XendRoot.instance()
407 def _get_config_ipaddr(config):
408 val = []
409 for ipaddr in sxp.children(config, elt='ip'):
410 val.append(sxp.child0(ipaddr))
411 return val
413 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
415 devnum = self.netif_idx
416 self.netif_idx += 1
418 script = sxp.child_value(devconfig, 'script',
419 xroot.get_vif_script())
420 script = os.path.join(xroot.network_script_dir, script)
421 bridge = sxp.child_value(devconfig, 'bridge',
422 xroot.get_vif_bridge())
423 mac = sxp.child_value(devconfig, 'mac')
424 ipaddr = _get_config_ipaddr(devconfig)
426 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
427 self.uuid, devnum)
428 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
430 front = { 'backend' : backpath,
431 'backend-id' : "%i" % backdom.id,
432 'handle' : "%i" % devnum,
433 'mac' : mac }
434 xstransact.Write(frontpath, front)
436 back = { 'script' : script,
437 'domain' : self.name,
438 'mac' : mac,
439 'bridge' : bridge,
440 'frontend' : frontpath,
441 'frontend-id' : "%i" % self.id,
442 'handle' : "%i" % devnum }
443 if ipaddr:
444 back['ip'] = ' '.join(ipaddr)
445 xstransact.Write(backpath, back)
447 return
449 if type == 'vtpm':
450 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
452 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
453 log.error("The domain has a TPM with instance %d." % devnum)
455 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
456 self.uuid, devnum)
457 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
459 front = { 'backend' : backpath,
460 'backend-id' : "%i" % backdom.id,
461 'handle' : "%i" % devnum }
462 xstransact.Write(frontpath, front)
464 back = { 'instance' : "%i" % devnum,
465 'frontend' : frontpath,
466 'frontend-id' : "%i" % self.id }
467 xstransact.Write(backpath, back)
469 return
471 ctrl = self.findDeviceController(type)
472 return ctrl.createDevice(devconfig, recreate=self.recreate,
473 change=change)
475 def configureDevice(self, type, id, devconfig):
476 ctrl = self.getDeviceController(type)
477 return ctrl.configureDevice(id, devconfig)
479 def destroyDevice(self, type, id, change=False, reboot=False):
480 ctrl = self.getDeviceController(type)
481 return ctrl.destroyDevice(id, change=change, reboot=reboot)
483 def deleteDevice(self, type, id):
484 ctrl = self.getDeviceController(type)
485 return ctrl.deleteDevice(id)
487 def getDevice(self, type, id, error=True):
488 ctrl = self.getDeviceController(type)
489 return ctrl.getDevice(id, error=error)
491 def getDeviceIds(self, type):
492 ctrl = self.getDeviceController(type)
493 return ctrl.getDeviceIds()
495 def getDeviceSxprs(self, type):
496 ctrl = self.getDeviceController(type)
497 return ctrl.getDeviceSxprs()
499 def sxpr(self):
500 sxpr = ['domain',
501 ['id', self.id],
502 ['name', self.name],
503 ['memory', self.memory],
504 ['ssidref', self.ssidref],
505 ['target', self.target] ]
506 if self.uuid:
507 sxpr.append(['uuid', self.uuid])
508 if self.info:
509 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
510 run = (self.info['running'] and 'r') or '-'
511 block = (self.info['blocked'] and 'b') or '-'
512 pause = (self.info['paused'] and 'p') or '-'
513 shut = (self.info['shutdown'] and 's') or '-'
514 crash = (self.info['crashed'] and 'c') or '-'
515 state = run + block + pause + shut + crash
516 sxpr.append(['state', state])
517 if self.info['shutdown']:
518 reason = shutdown_reason(self.info['shutdown_reason'])
519 sxpr.append(['shutdown_reason', reason])
520 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
521 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
522 sxpr.append(['vcpus', self.info['vcpus']])
523 sxpr.append(['cpumap', self.info['cpumap']])
524 # build a string, using '|' to seperate items, show only up
525 # to number of vcpus in domain, and trim the trailing '|'
526 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
527 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
529 if self.start_time:
530 up_time = time.time() - self.start_time
531 sxpr.append(['up_time', str(up_time) ])
532 sxpr.append(['start_time', str(self.start_time) ])
534 if self.store_channel:
535 sxpr.append(self.store_channel.sxpr())
536 if self.store_mfn:
537 sxpr.append(['store_mfn', self.store_mfn])
538 if self.console_channel:
539 sxpr.append(['console_channel', self.console_channel.sxpr()])
540 if self.console_mfn:
541 sxpr.append(['console_mfn', self.console_mfn])
542 # already in (devices)
543 # console = self.getConsole()
544 # if console:
545 # sxpr.append(console.sxpr())
547 if self.restart_count:
548 sxpr.append(['restart_count', self.restart_count])
549 if self.restart_state:
550 sxpr.append(['restart_state', self.restart_state])
551 if self.restart_time:
552 sxpr.append(['restart_time', str(self.restart_time)])
554 devs = self.sxpr_devices()
555 if devs:
556 sxpr.append(devs)
557 if self.config:
558 sxpr.append(['config', self.config])
559 if self.device_model_pid:
560 sxpr.append(['device_model_pid',self.device_model_pid])
561 return sxpr
563 def sxpr_devices(self):
564 sxpr = []
565 for ty in self.controllers.keys():
566 devs = self.getDeviceSxprs(ty)
567 sxpr += devs
568 if sxpr:
569 sxpr.insert(0, 'devices')
570 else:
571 sxpr = None
572 return sxpr
574 def check_name(self, name):
575 """Check if a vm name is valid. Valid names contain alphabetic characters,
576 digits, or characters in '_-.:/+'.
577 The same name cannot be used for more than one vm at the same time.
579 @param name: name
580 @raise: VMerror if invalid
581 """
582 if self.recreate: return
583 if name is None or name == '':
584 raise VmError('missing vm name')
585 for c in name:
586 if c in string.digits: continue
587 if c in '_-.:/+': continue
588 if c in string.ascii_letters: continue
589 raise VmError('invalid vm name')
590 dominfo = domain_exists(name)
591 # When creating or rebooting, a domain with my name should not exist.
592 # When restoring, a domain with my name will exist, but it should have
593 # my domain id.
594 if not dominfo:
595 return
596 if dominfo.is_terminated():
597 return
598 if not self.id or (dominfo.id != self.id):
599 raise VmError('vm name clash: ' + name)
601 def construct(self, config):
602 """Construct the vm instance from its configuration.
604 @param config: configuration
605 @raise: VmError on error
606 """
607 # todo - add support for scheduling params?
608 self.config = config
609 try:
610 # Initial domain create.
611 self.setName(sxp.child_value(config, 'name'))
612 self.check_name(self.name)
613 self.init_image()
614 self.configure_cpus(config)
615 self.init_domain()
616 self.register_domain()
617 self.configure_bootloader()
619 # Create domain devices.
620 self.configure_backends()
621 self.configure_restart()
622 self.construct_image()
623 self.configure()
624 self.exportToDB(save=True)
625 except Exception, ex:
626 # Catch errors, cleanup and re-raise.
627 print 'Domain construction error:', ex
628 import traceback
629 traceback.print_exc()
630 self.destroy()
631 raise
633 def register_domain(self):
634 xd = get_component('xen.xend.XendDomain')
635 xd._add_domain(self)
636 self.exportToDB(save=True)
638 def configure_cpus(self, config):
639 try:
640 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
641 except:
642 raise VmError('invalid cpu weight')
643 self.memory = int(sxp.child_value(config, 'memory'))
644 if self.memory is None:
645 raise VmError('missing memory size')
646 self.target = self.memory * (1 << 20)
647 self.ssidref = int(sxp.child_value(config, 'ssidref'))
648 cpu = sxp.child_value(config, 'cpu')
649 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
650 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
651 try:
652 image = sxp.child_value(self.config, 'image')
653 vcpus = sxp.child_value(image, 'vcpus')
654 if vcpus:
655 self.vcpus = int(vcpus)
656 except:
657 raise VmError('invalid vcpus value')
659 def exportVCPUSToDB(self, vcpus):
660 for v in range(0,vcpus):
661 path = "/cpu/%d"%(v)
662 if not self.vcpusdb.has_key(path):
663 self.vcpusdb[path] = self.db.addChild(path)
664 db = self.vcpusdb[path]
665 log.debug("writing key availability=online to path %s in store"%(path))
666 db['availability'] = "online"
667 db.saveDB(save=True)
669 def init_image(self):
670 """Create boot image handler for the domain.
671 """
672 image = sxp.child_value(self.config, 'image')
673 if image is None:
674 raise VmError('missing image')
675 self.image = ImageHandler.create(self, image)
677 def construct_image(self):
678 """Construct the boot image for the domain.
679 """
680 self.create_channel()
681 self.image.createImage()
682 self.exportToDB()
683 if self.store_channel and self.store_mfn >= 0:
684 IntroduceDomain(self.id, self.store_mfn, self.store_channel.port1,
685 self.path)
686 # get the configured value of vcpus and update store
687 self.exportVCPUSToDB(self.vcpus)
689 def delete(self):
690 """Delete the vm's db.
691 """
692 if dom_get(self.id):
693 return
694 self.id = None
695 self.saveToDB(sync=True)
696 try:
697 # Todo: eventually will have to wait for devices to signal
698 # destruction before can delete the db.
699 if self.db:
700 self.db.delete()
701 except Exception, ex:
702 log.warning("error in domain db delete: %s", ex)
703 pass
705 def destroy_domain(self):
706 """Destroy the vm's domain.
707 The domain will not finally go away unless all vm
708 devices have been released.
709 """
710 if self.id is None:
711 return
712 try:
713 xc.domain_destroy(dom=self.id)
714 except Exception, err:
715 log.exception("Domain destroy failed: %s", self.name)
717 def cleanup(self):
718 """Cleanup vm resources: release devices.
719 """
720 self.state = STATE_VM_TERMINATED
721 self.release_devices()
722 if self.store_channel:
723 self.setStoreChannel(None)
724 if self.console_channel:
725 # notify processes using this cosole?
726 try:
727 self.console_channel.close()
728 self.console_channel = None
729 except:
730 pass
731 if self.image:
732 try:
733 self.device_model_pid = 0
734 self.image.destroy()
735 self.image = None
736 except:
737 pass
739 def destroy(self):
740 """Clenup vm and destroy domain.
741 """
742 self.destroy_domain()
743 self.cleanup()
744 self.saveToDB()
745 return 0
747 def is_terminated(self):
748 """Check if a domain has been terminated.
749 """
750 return self.state == STATE_VM_TERMINATED
752 def release_devices(self):
753 """Release all vm devices.
754 """
755 reboot = self.restart_pending()
756 for ctrl in self.controllers.values():
757 if ctrl.isDestroyed(): continue
758 ctrl.destroyController(reboot=reboot)
759 t = xstransact("%s/device" % self.path)
760 for d in t.list("vbd"):
761 t.remove(d)
762 for d in t.list("vif"):
763 t.remove(d)
764 for d in t.list("vtpm"):
765 t.remove(d)
766 t.commit()
768 def show(self):
769 """Print virtual machine info.
770 """
771 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
772 print "image:"
773 sxp.show(self.image)
774 print "]"
776 def init_domain(self):
777 """Initialize the domain memory.
778 """
779 if self.recreate:
780 return
781 if self.start_time is None:
782 self.start_time = time.time()
783 try:
784 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
785 except:
786 raise VmError('invalid cpu')
787 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
788 log.debug('init_domain> Created domain=%d name=%s memory=%d',
789 id, self.name, self.memory)
790 self.setdom(id)
792 def eventChannel(self, path=None):
793 """Create an event channel to the domain.
795 @param path under which port is stored in db
796 """
797 port = 0
798 if path:
799 try:
800 port = int(xstransact.Read(self.path, path))
801 except:
802 # if anything goes wrong, assume the port was not yet set
803 pass
804 ret = EventChannel.interdomain(0, self.id, port1=port, port2=0)
805 xstransact.Write(self.path, path, "%i" % ret.port1)
806 return ret
808 def create_channel(self):
809 """Create the channels to the domain.
810 """
811 self.store_channel = self.eventChannel("store/port")
812 self.console_channel = self.eventChannel("console/port")
815 def create_configured_devices(self):
816 devices = sxp.children(self.config, 'device')
817 for d in devices:
818 dev_config = sxp.child0(d)
819 if dev_config is None:
820 raise VmError('invalid device')
821 dev_type = sxp.name(dev_config)
823 if not controller.isDevControllerClass(dev_type):
824 raise VmError('unknown device type: ' + dev_type)
826 self.createDevice(dev_type, dev_config)
829 def create_devices(self):
830 """Create the devices for a vm.
832 @raise: VmError for invalid devices
833 """
834 if self.rebooting():
835 for ctrl in self.controllers.values():
836 ctrl.initController(reboot=True)
837 else:
838 self.create_configured_devices()
839 if not self.device_model_pid:
840 self.device_model_pid = self.image.createDeviceModel()
842 def device_create(self, dev_config):
843 """Create a new device.
845 @param dev_config: device configuration
846 """
847 dev_type = sxp.name(dev_config)
848 dev = self.createDevice(dev_type, dev_config, change=True)
849 self.config.append(['device', dev.getConfig()])
850 return dev.sxpr()
852 def device_configure(self, dev_config, id):
853 """Configure an existing device.
855 @param dev_config: device configuration
856 @param id: device id
857 """
858 type = sxp.name(dev_config)
859 dev = self.getDevice(type, id)
860 old_config = dev.getConfig()
861 new_config = dev.configure(dev_config, change=True)
862 # Patch new config into vm config.
863 new_full_config = ['device', new_config]
864 old_full_config = ['device', old_config]
865 old_index = self.config.index(old_full_config)
866 self.config[old_index] = new_full_config
867 return new_config
869 def device_refresh(self, type, id):
870 """Refresh a device.
872 @param type: device type
873 @param id: device id
874 """
875 dev = self.getDevice(type, id)
876 dev.refresh()
878 def device_delete(self, type, id):
879 """Destroy and remove a device.
881 @param type: device type
882 @param id: device id
883 """
884 dev = self.getDevice(type, id)
885 dev_config = dev.getConfig()
886 if dev_config:
887 self.config.remove(['device', dev_config])
888 self.deleteDevice(type, dev.getId())
890 def configure_bootloader(self):
891 """Configure boot loader.
892 """
893 self.bootloader = sxp.child_value(self.config, "bootloader")
895 def configure_restart(self):
896 """Configure the vm restart mode.
897 """
898 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
899 if r not in restart_modes:
900 raise VmError('invalid restart mode: ' + str(r))
901 self.restart_mode = r;
903 def restart_needed(self, reason):
904 """Determine if the vm needs to be restarted when shutdown
905 for the given reason.
907 @param reason: shutdown reason
908 @return True if needs restart, False otherwise
909 """
910 if self.restart_mode == RESTART_NEVER:
911 return False
912 if self.restart_mode == RESTART_ALWAYS:
913 return True
914 if self.restart_mode == RESTART_ONREBOOT:
915 return reason == 'reboot'
916 return False
918 def restart_cancel(self):
919 """Cancel a vm restart.
920 """
921 self.restart_state = None
923 def restarting(self):
924 """Put the vm into restart mode.
925 """
926 self.restart_state = STATE_RESTART_PENDING
928 def restart_pending(self):
929 """Test if the vm has a pending restart.
930 """
931 return self.restart_state == STATE_RESTART_PENDING
933 def rebooting(self):
934 return self.restart_state == STATE_RESTART_BOOTING
936 def restart_check(self):
937 """Check if domain restart is OK.
938 To prevent restart loops, raise an error if it is
939 less than MINIMUM_RESTART_TIME seconds since the last restart.
940 """
941 tnow = time.time()
942 if self.restart_time is not None:
943 tdelta = tnow - self.restart_time
944 if tdelta < self.MINIMUM_RESTART_TIME:
945 self.restart_cancel()
946 msg = 'VM %s restarting too fast' % self.name
947 log.error(msg)
948 raise VmError(msg)
949 self.restart_time = tnow
950 self.restart_count += 1
952 def restart(self):
953 """Restart the domain after it has exited.
954 Reuses the domain id
956 """
957 try:
958 self.clear_shutdown()
959 self.state = STATE_VM_OK
960 self.shutdown_pending = None
961 self.restart_check()
962 self.exportToDB()
963 self.restart_state = STATE_RESTART_BOOTING
964 if self.bootloader:
965 self.config = self.bootloader_config()
966 self.construct(self.config)
967 self.saveToDB()
968 finally:
969 self.restart_state = None
971 def bootloader_config(self):
972 # if we're restarting with a bootloader, we need to run it
973 # FIXME: this assumes the disk is the first device and
974 # that we're booting from the first disk
975 blcfg = None
976 # FIXME: this assumes that we want to use the first disk
977 dev = sxp.child_value(self.config, "device")
978 if dev:
979 disk = sxp.child_value(dev, "uname")
980 fn = blkdev_uname_to_file(disk)
981 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
982 if blcfg is None:
983 msg = "Had a bootloader specified, but can't find disk"
984 log.error(msg)
985 raise VmError(msg)
986 config = sxp.merge(['vm', blcfg ], self.config)
987 return config
989 def configure_backends(self):
990 """Set configuration flags if the vm is a backend for netif or blkif.
991 Configure the backends to use for vbd and vif if specified.
992 """
993 for c in sxp.children(self.config, 'backend'):
994 v = sxp.child0(c)
995 name = sxp.name(v)
996 if name == 'blkif':
997 self.blkif_backend = True
998 elif name == 'netif':
999 self.netif_backend = True
1000 elif name == 'usbif':
1001 self.usbif_backend = True
1002 elif name == 'tpmif':
1003 self.tpmif_backend = True
1004 else:
1005 raise VmError('invalid backend type:' + str(name))
1007 def configure(self):
1008 """Configure a vm.
1010 """
1011 self.configure_fields()
1012 self.create_devices()
1013 self.create_blkif()
1015 def create_blkif(self):
1016 """Create the block device interface (blkif) for the vm.
1017 The vm needs a blkif even if it doesn't have any disks
1018 at creation time, for example when it uses NFS root.
1020 """
1021 return
1022 blkif = self.getDeviceController("vbd", error=False)
1023 if not blkif:
1024 blkif = self.createDeviceController("vbd")
1025 backend = blkif.getBackend(0)
1026 backend.connect(recreate=self.recreate)
1028 def configure_fields(self):
1029 """Process the vm configuration fields using the registered handlers.
1030 """
1031 index = {}
1032 for field in sxp.children(self.config):
1033 field_name = sxp.name(field)
1034 field_index = index.get(field_name, 0)
1035 field_handler = config_handlers.get(field_name)
1036 # Ignore unknown fields. Warn?
1037 if field_handler:
1038 v = field_handler(self, self.config, field, field_index)
1039 else:
1040 log.warning("Unknown config field %s", field_name)
1041 index[field_name] = field_index + 1
1043 def mem_target_set(self, target):
1044 """Set domain memory target in bytes.
1045 """
1046 if target:
1047 self.target = target * (1 << 20)
1048 # Commit to XenStore immediately
1049 self.exportToDB()
1051 def vcpu_hotplug(self, vcpu, state):
1052 """Disable or enable VCPU in domain.
1053 """
1054 db = ""
1055 try:
1056 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1057 except:
1058 log.error("Invalid VCPU")
1059 return
1061 if self.store_channel:
1062 if int(state) == 0:
1063 db['availability'] = "offline"
1064 else:
1065 db['availability'] = "online"
1067 db.saveDB(save=True)
1069 def shutdown(self, reason):
1070 if not reason in shutdown_reasons.values():
1071 raise XendError('invalid reason:' + reason)
1072 db = self.db.addChild("/control");
1073 db['shutdown'] = reason;
1074 db.saveDB(save=True);
1075 if not reason in ['suspend']:
1076 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1078 def clear_shutdown(self):
1079 db = self.db.addChild("/control")
1080 db['shutdown'] = ""
1081 db.saveDB(save=True)
1083 def send_sysrq(self, key=0):
1084 db = self.db.addChild("/control");
1085 db['sysrq'] = '%c' % key;
1086 db.saveDB(save=True);
1088 def shutdown_time_left(self, timeout):
1089 if not self.shutdown_pending:
1090 return 0
1091 return timeout - (time.time() - self.shutdown_pending['start'])
1093 def dom0_init_store(self):
1094 if not self.store_channel:
1095 self.store_channel = self.eventChannel("store/port")
1096 if not self.store_channel:
1097 return
1098 ref = xc.init_store(self.store_channel.port2)
1099 if ref and ref >= 0:
1100 self.setStoreRef(ref)
1101 IntroduceDomain(self.id, ref, self.store_channel.port1, self.path)
1102 # get run-time value of vcpus and update store
1103 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1106 def vm_field_ignore(_, _1, _2, _3):
1107 """Dummy config field handler used for fields with built-in handling.
1108 Matches the signature required by config_handlers.
1109 """
1110 pass
1113 def vm_field_maxmem(vm, _1, val, _2):
1114 """Config field handler to configure vm memory limit. Matches the
1115 signature required by config_handlers.
1116 """
1117 maxmem = sxp.child0(val)
1118 if maxmem is None:
1119 maxmem = vm.memory
1120 try:
1121 maxmem = int(maxmem)
1122 except:
1123 raise VmError("invalid maxmem: " + str(maxmem))
1124 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1127 #============================================================================
1128 # Register image handlers.
1130 from image import \
1131 addImageHandlerClass, \
1132 ImageHandler, \
1133 LinuxImageHandler, \
1134 VmxImageHandler
1136 addImageHandlerClass(LinuxImageHandler)
1137 addImageHandlerClass(VmxImageHandler)
1140 """Table of handlers for field configuration.
1142 field_name[String]: fn(vm, config, field, index) -> value(ignored)
1143 """
1144 config_handlers = {
1146 # Ignore the fields we already handle.
1148 'name': vm_field_ignore,
1149 'memory': vm_field_ignore,
1150 'ssidref': vm_field_ignore,
1151 'cpu': vm_field_ignore,
1152 'cpu_weight': vm_field_ignore,
1153 'restart': vm_field_ignore,
1154 'image': vm_field_ignore,
1155 'device': vm_field_ignore,
1156 'backend': vm_field_ignore,
1157 'vcpus': vm_field_ignore,
1158 'bootloader': vm_field_ignore,
1160 # Register other config handlers.
1161 'maxmem': vm_field_maxmem
1165 #============================================================================
1166 # Register device controllers and their device config types.
1168 controller.addDevControllerClass("vbd", server.blkif.BlkifController)
1169 controller.addDevControllerClass("vif", server.netif.NetifController)
1170 controller.addDevControllerClass("vtpm", server.tpmif.TPMifController)
1171 controller.addDevControllerClass("pci", server.pciif.PciController)
1172 controller.addDevControllerClass("usb", server.usbif.UsbifController)