ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6835:5cbb2ecce16a

Move id back into main domain dir and rename to domid.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 14 15:10:22 2005 +0000 (2005-09-14)
parents e2f0a6fdb7d9
children 2277377dfc3a
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
30 import errno
32 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
33 from xen.util.ip import check_subnet, get_current_ipgw
34 from xen.util.blkif import blkdev_uname_to_file
36 from xen.xend.server import controller
37 from xen.xend.server import SrvDaemon; xend = SrvDaemon.instance()
38 from xen.xend.server.channel import EventChannel
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
51 from xen.xend.xenstore.xstransact import xstransact
52 from xen.xend.xenstore.xsutil import IntroduceDomain
54 """Shutdown code for poweroff."""
55 DOMAIN_POWEROFF = 0
57 """Shutdown code for reboot."""
58 DOMAIN_REBOOT = 1
60 """Shutdown code for suspend."""
61 DOMAIN_SUSPEND = 2
63 """Shutdown code for crash."""
64 DOMAIN_CRASH = 3
66 """Map shutdown codes to strings."""
67 shutdown_reasons = {
68 DOMAIN_POWEROFF: "poweroff",
69 DOMAIN_REBOOT : "reboot",
70 DOMAIN_SUSPEND : "suspend",
71 DOMAIN_CRASH : "crash",
72 }
74 RESTART_ALWAYS = 'always'
75 RESTART_ONREBOOT = 'onreboot'
76 RESTART_NEVER = 'never'
78 restart_modes = [
79 RESTART_ALWAYS,
80 RESTART_ONREBOOT,
81 RESTART_NEVER,
82 ]
84 STATE_RESTART_PENDING = 'pending'
85 STATE_RESTART_BOOTING = 'booting'
87 STATE_VM_OK = "ok"
88 STATE_VM_TERMINATED = "terminated"
89 STATE_VM_SUSPENDED = "suspended"
92 def domain_exists(name):
93 # See comment in XendDomain constructor.
94 xd = get_component('xen.xend.XendDomain')
95 return xd.domain_lookup_by_name(name)
97 def shutdown_reason(code):
98 """Get a shutdown reason from a code.
100 @param code: shutdown code
101 @type code: int
102 @return: shutdown reason
103 @rtype: string
104 """
105 return shutdown_reasons.get(code, "?")
107 def dom_get(dom):
108 """Get info from xen for an existing domain.
110 @param dom: domain id
111 @return: info or None
112 """
113 domlist = xc.domain_getinfo(dom, 1)
114 if domlist and dom == domlist[0]['dom']:
115 return domlist[0]
116 return None
118 class XendDomainInfo:
119 """Virtual machine object."""
121 """Minimum time between domain restarts in seconds.
122 """
123 MINIMUM_RESTART_TIME = 20
125 def create(cls, parentdb, config):
126 """Create a VM from a configuration.
128 @param parentdb: parent db
129 @param config configuration
130 @raise: VmError for invalid configuration
131 """
132 uuid = getUuid()
133 db = parentdb.addChild("%s/xend" % uuid)
134 path = parentdb.getPath()
135 vm = cls(uuid, path, db)
136 vm.construct(config)
137 vm.saveToDB(sync=True)
139 return vm
141 create = classmethod(create)
143 def recreate(cls, uuid, domid, db, info):
144 """Create the VM object for an existing domain.
146 @param db: domain db
147 @param info: domain info from xc
148 """
149 path = "/".join(db.getPath().split("/")[0:-2])
150 vm = cls(uuid, path, db)
151 vm.setDomid(domid)
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" % domid)
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.setDomid(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('name', ty='str'),
206 DBVar('config', ty='sxpr'),
207 DBVar('start_time', ty='float'),
208 DBVar('state', ty='str'),
209 DBVar('restart_mode', ty='str'),
210 DBVar('restart_state', ty='str'),
211 DBVar('restart_time', ty='float'),
212 DBVar('restart_count', ty='int'),
213 DBVar('device_model_pid', ty='int'),
214 ]
216 def __init__(self, uuid, path, db):
217 self.uuid = uuid
218 self.path = path + "/" + uuid
220 self.db = db
222 self.recreate = 0
223 self.restore = 0
225 self.config = None
226 self.domid = None
227 self.cpu_weight = 1
228 self.start_time = None
229 self.name = None
230 self.memory = None
231 self.ssidref = None
232 self.image = None
234 self.target = None
236 self.store_channel = None
237 self.store_mfn = None
238 self.console_channel = None
239 self.console_mfn = None
240 self.controllers = {}
242 self.info = None
243 self.blkif_backend = False
244 self.netif_backend = False
245 self.netif_idx = 0
246 self.tpmif_backend = False
248 #todo: state: running, suspended
249 self.state = STATE_VM_OK
250 self.state_updated = threading.Condition()
251 self.shutdown_pending = None
253 #todo: set to migrate info if migrating
254 self.migrate = None
256 self.restart_mode = RESTART_ONREBOOT
257 self.restart_state = None
258 self.restart_time = None
259 self.restart_count = 0
261 self.vcpus = 1
262 self.bootloader = None
263 self.device_model_pid = 0
265 xstransact.Write(self.path, "uuid", self.uuid)
267 def setDB(self, db):
268 self.db = db
270 def saveToDB(self, save=False, sync=False):
271 self.db.saveDB(save=save, sync=sync)
273 def exportToDB(self, save=False, sync=False):
274 if self.image:
275 self.image.exportToDB(save=save, sync=sync)
276 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
278 def importFromDB(self):
279 self.db.importFromDB(self, fields=self.__exports__)
280 self.store_channel = self.eventChannel("store/port")
282 def setDomid(self, domid):
283 """Set the domain id.
285 @param dom: domain id
286 """
287 self.domid = domid
288 xstransact.Write(self.path, "domid", "%i" % self.domid)
290 def getDomain(self):
291 return self.domid
293 def setName(self, name):
294 self.name = name
295 self.db.name = self.name
297 def getName(self):
298 return self.name
300 def setStoreRef(self, ref):
301 self.store_mfn = ref
302 if ref:
303 xstransact.Write(self.path, "store/ring-ref", "%i" % ref)
304 else:
305 xstransact.Remove(self.path, "store/ring-ref")
307 def setStoreChannel(self, channel):
308 if self.store_channel and self.store_channel != channel:
309 self.store_channel.close()
310 self.store_channel = channel
311 if channel:
312 xstransact.Write(self.path, "store/port", "%i" % channel.port1)
313 else:
314 xstransact.Remove(self.path, "store/port")
316 def setConsoleRef(self, ref):
317 self.console_mfn = ref
318 if ref:
319 xstransact.Write(self.path, "console/ring-ref", "%i" % ref)
320 else:
321 xstransact.Remove(self.path, "console/ring-ref")
323 def setMemoryTarget(self, target):
324 self.memory_target = target
325 if target:
326 xstransact.Write(self.path, "memory/target", "%i" % target)
327 else:
328 xstransact.Remove(self.path, "memory/target")
330 def update(self, info=None):
331 """Update with info from xc.domain_getinfo().
332 """
333 self.info = info or dom_get(self.domid)
334 self.memory = self.info['mem_kb'] / 1024
335 self.ssidref = self.info['ssidref']
337 def state_set(self, state):
338 self.state_updated.acquire()
339 if self.state != state:
340 self.state = state
341 self.state_updated.notifyAll()
342 self.state_updated.release()
343 self.saveToDB()
345 def state_wait(self, state):
346 self.state_updated.acquire()
347 while self.state != state:
348 self.state_updated.wait()
349 self.state_updated.release()
351 def __str__(self):
352 s = "<domain"
353 s += " id=" + str(self.domid)
354 s += " name=" + self.name
355 s += " memory=" + str(self.memory)
356 s += " ssidref=" + str(self.ssidref)
357 s += ">"
358 return s
360 __repr__ = __str__
362 def getDeviceController(self, type, error=True):
363 ctrl = self.controllers.get(type)
364 if not ctrl and error:
365 raise XendError("invalid device type:" + type)
366 return ctrl
368 def findDeviceController(self, type):
369 return (self.getDeviceController(type, error=False)
370 or self.createDeviceController(type))
372 def createDeviceController(self, type):
373 ctrl = controller.createDevController(type, self, recreate=self.recreate)
374 self.controllers[type] = ctrl
375 return ctrl
377 def createDevice(self, type, devconfig, change=False):
378 if self.recreate:
379 return
380 if type == 'vbd':
381 typedev = sxp.child_value(devconfig, 'dev')
382 if re.match('^ioemu:', typedev):
383 return;
385 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
387 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
389 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
390 self.uuid, devnum)
391 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
393 front = { 'backend' : backpath,
394 'backend-id' : "%i" % backdom.domid,
395 'virtual-device' : "%i" % devnum }
396 xstransact.Write(frontpath, front)
398 (type, params) = string.split(sxp.child_value(devconfig,
399 'uname'), ':', 1)
400 back = { 'type' : type,
401 'params' : params,
402 'frontend' : frontpath,
403 'frontend-id' : "%i" % self.domid }
404 xstransact.Write(backpath, back)
406 return
408 if type == 'vif':
409 from xen.xend import XendRoot
410 xroot = XendRoot.instance()
412 def _get_config_ipaddr(config):
413 val = []
414 for ipaddr in sxp.children(config, elt='ip'):
415 val.append(sxp.child0(ipaddr))
416 return val
418 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
420 devnum = self.netif_idx
421 self.netif_idx += 1
423 script = sxp.child_value(devconfig, 'script',
424 xroot.get_vif_script())
425 script = os.path.join(xroot.network_script_dir, script)
426 bridge = sxp.child_value(devconfig, 'bridge',
427 xroot.get_vif_bridge())
428 mac = sxp.child_value(devconfig, 'mac')
429 ipaddr = _get_config_ipaddr(devconfig)
431 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
432 self.uuid, devnum)
433 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
435 front = { 'backend' : backpath,
436 'backend-id' : "%i" % backdom.domid,
437 'handle' : "%i" % devnum,
438 'mac' : mac }
439 xstransact.Write(frontpath, front)
441 back = { 'script' : script,
442 'domain' : self.name,
443 'mac' : mac,
444 'bridge' : bridge,
445 'frontend' : frontpath,
446 'frontend-id' : "%i" % self.domid,
447 'handle' : "%i" % devnum }
448 if ipaddr:
449 back['ip'] = ' '.join(ipaddr)
450 xstransact.Write(backpath, back)
452 return
454 if type == 'vtpm':
455 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
457 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
458 log.error("The domain has a TPM with instance %d." % devnum)
460 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
461 self.uuid, devnum)
462 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
464 front = { 'backend' : backpath,
465 'backend-id' : "%i" % backdom.domid,
466 'handle' : "%i" % devnum }
467 xstransact.Write(frontpath, front)
469 back = { 'instance' : "%i" % devnum,
470 'frontend' : frontpath,
471 'frontend-id' : "%i" % self.domid }
472 xstransact.Write(backpath, back)
474 return
476 ctrl = self.findDeviceController(type)
477 return ctrl.createDevice(devconfig, recreate=self.recreate,
478 change=change)
480 def configureDevice(self, type, id, devconfig):
481 ctrl = self.getDeviceController(type)
482 return ctrl.configureDevice(id, devconfig)
484 def destroyDevice(self, type, id, change=False, reboot=False):
485 ctrl = self.getDeviceController(type)
486 return ctrl.destroyDevice(id, change=change, reboot=reboot)
488 def deleteDevice(self, type, id):
489 ctrl = self.getDeviceController(type)
490 return ctrl.deleteDevice(id)
492 def getDevice(self, type, id, error=True):
493 ctrl = self.getDeviceController(type)
494 return ctrl.getDevice(id, error=error)
496 def getDeviceIds(self, type):
497 ctrl = self.getDeviceController(type)
498 return ctrl.getDeviceIds()
500 def getDeviceSxprs(self, type):
501 ctrl = self.getDeviceController(type)
502 return ctrl.getDeviceSxprs()
504 def sxpr(self):
505 sxpr = ['domain',
506 ['domid', self.domid],
507 ['name', self.name],
508 ['memory', self.memory],
509 ['ssidref', self.ssidref],
510 ['target', self.target] ]
511 if self.uuid:
512 sxpr.append(['uuid', self.uuid])
513 if self.info:
514 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
515 run = (self.info['running'] and 'r') or '-'
516 block = (self.info['blocked'] and 'b') or '-'
517 pause = (self.info['paused'] and 'p') or '-'
518 shut = (self.info['shutdown'] and 's') or '-'
519 crash = (self.info['crashed'] and 'c') or '-'
520 state = run + block + pause + shut + crash
521 sxpr.append(['state', state])
522 if self.info['shutdown']:
523 reason = shutdown_reason(self.info['shutdown_reason'])
524 sxpr.append(['shutdown_reason', reason])
525 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
526 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
527 sxpr.append(['vcpus', self.info['vcpus']])
528 sxpr.append(['cpumap', self.info['cpumap']])
529 # build a string, using '|' to seperate items, show only up
530 # to number of vcpus in domain, and trim the trailing '|'
531 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
532 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
534 if self.start_time:
535 up_time = time.time() - self.start_time
536 sxpr.append(['up_time', str(up_time) ])
537 sxpr.append(['start_time', str(self.start_time) ])
539 if self.store_channel:
540 sxpr.append(self.store_channel.sxpr())
541 if self.store_mfn:
542 sxpr.append(['store_mfn', self.store_mfn])
543 if self.console_channel:
544 sxpr.append(['console_channel', self.console_channel.sxpr()])
545 if self.console_mfn:
546 sxpr.append(['console_mfn', self.console_mfn])
547 # already in (devices)
548 # console = self.getConsole()
549 # if console:
550 # sxpr.append(console.sxpr())
552 if self.restart_count:
553 sxpr.append(['restart_count', self.restart_count])
554 if self.restart_state:
555 sxpr.append(['restart_state', self.restart_state])
556 if self.restart_time:
557 sxpr.append(['restart_time', str(self.restart_time)])
559 devs = self.sxpr_devices()
560 if devs:
561 sxpr.append(devs)
562 if self.config:
563 sxpr.append(['config', self.config])
564 if self.device_model_pid:
565 sxpr.append(['device_model_pid',self.device_model_pid])
566 return sxpr
568 def sxpr_devices(self):
569 sxpr = []
570 for ty in self.controllers.keys():
571 devs = self.getDeviceSxprs(ty)
572 sxpr += devs
573 if sxpr:
574 sxpr.insert(0, 'devices')
575 else:
576 sxpr = None
577 return sxpr
579 def check_name(self, name):
580 """Check if a vm name is valid. Valid names contain alphabetic characters,
581 digits, or characters in '_-.:/+'.
582 The same name cannot be used for more than one vm at the same time.
584 @param name: name
585 @raise: VMerror if invalid
586 """
587 if self.recreate: return
588 if name is None or name == '':
589 raise VmError('missing vm name')
590 for c in name:
591 if c in string.digits: continue
592 if c in '_-.:/+': continue
593 if c in string.ascii_letters: continue
594 raise VmError('invalid vm name')
595 dominfo = domain_exists(name)
596 # When creating or rebooting, a domain with my name should not exist.
597 # When restoring, a domain with my name will exist, but it should have
598 # my domain id.
599 if not dominfo:
600 return
601 if dominfo.is_terminated():
602 return
603 if not self.domid or (dominfo.domid != self.domid):
604 raise VmError('vm name clash: ' + name)
606 def construct(self, config):
607 """Construct the vm instance from its configuration.
609 @param config: configuration
610 @raise: VmError on error
611 """
612 # todo - add support for scheduling params?
613 self.config = config
614 try:
615 # Initial domain create.
616 self.setName(sxp.child_value(config, 'name'))
617 self.check_name(self.name)
618 self.init_image()
619 self.configure_cpus(config)
620 self.init_domain()
621 self.register_domain()
622 self.configure_bootloader()
624 # Create domain devices.
625 self.configure_backends()
626 self.configure_restart()
627 self.construct_image()
628 self.configure()
629 self.exportToDB(save=True)
630 except Exception, ex:
631 # Catch errors, cleanup and re-raise.
632 print 'Domain construction error:', ex
633 import traceback
634 traceback.print_exc()
635 self.destroy()
636 raise
638 def register_domain(self):
639 xd = get_component('xen.xend.XendDomain')
640 xd._add_domain(self)
641 self.exportToDB(save=True)
643 def configure_cpus(self, config):
644 try:
645 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
646 except:
647 raise VmError('invalid cpu weight')
648 self.memory = int(sxp.child_value(config, 'memory'))
649 if self.memory is None:
650 raise VmError('missing memory size')
651 self.setMemoryTarget(self.memory * (1 << 20))
652 self.ssidref = int(sxp.child_value(config, 'ssidref'))
653 cpu = sxp.child_value(config, 'cpu')
654 if self.recreate and self.domid and cpu is not None and int(cpu) >= 0:
655 xc.domain_pincpu(self.domid, 0, 1<<int(cpu))
656 try:
657 image = sxp.child_value(self.config, 'image')
658 vcpus = sxp.child_value(image, 'vcpus')
659 if vcpus:
660 self.vcpus = int(vcpus)
661 except:
662 raise VmError('invalid vcpus value')
664 def configure_vcpus(self, vcpus):
665 d = {}
666 for v in range(0, vcpus):
667 d["cpu/%d/availability" % v] = "online"
668 xstransact.Write(self.path, d)
670 def init_image(self):
671 """Create boot image handler for the domain.
672 """
673 image = sxp.child_value(self.config, 'image')
674 if image is None:
675 raise VmError('missing image')
676 self.image = ImageHandler.create(self, image)
678 def construct_image(self):
679 """Construct the boot image for the domain.
680 """
681 self.create_channel()
682 self.image.createImage()
683 self.exportToDB()
684 if self.store_channel and self.store_mfn >= 0:
685 IntroduceDomain(self.domid, self.store_mfn,
686 self.store_channel.port1, self.path)
687 # get the configured value of vcpus and update store
688 self.configure_vcpus(self.vcpus)
690 def delete(self):
691 """Delete the vm's db.
692 """
693 if dom_get(self.domid):
694 return
695 self.domid = None
696 self.saveToDB(sync=True)
697 try:
698 # Todo: eventually will have to wait for devices to signal
699 # destruction before can delete the db.
700 if self.db:
701 self.db.delete()
702 except Exception, ex:
703 log.warning("error in domain db delete: %s", ex)
704 pass
706 def destroy_domain(self):
707 """Destroy the vm's domain.
708 The domain will not finally go away unless all vm
709 devices have been released.
710 """
711 if self.domid is None:
712 return
713 try:
714 xc.domain_destroy(dom=self.domid)
715 except Exception, err:
716 log.exception("Domain destroy failed: %s", self.name)
718 def cleanup(self):
719 """Cleanup vm resources: release devices.
720 """
721 self.state = STATE_VM_TERMINATED
722 self.release_devices()
723 if self.store_channel:
724 self.setStoreChannel(None)
725 if self.console_channel:
726 # notify processes using this cosole?
727 try:
728 self.console_channel.close()
729 self.console_channel = None
730 except:
731 pass
732 if self.image:
733 try:
734 self.device_model_pid = 0
735 self.image.destroy()
736 self.image = None
737 except:
738 pass
740 def destroy(self):
741 """Clenup vm and destroy domain.
742 """
743 self.destroy_domain()
744 self.cleanup()
745 self.saveToDB()
746 return 0
748 def is_terminated(self):
749 """Check if a domain has been terminated.
750 """
751 return self.state == STATE_VM_TERMINATED
753 def release_devices(self):
754 """Release all vm devices.
755 """
756 reboot = self.restart_pending()
757 for ctrl in self.controllers.values():
758 if ctrl.isDestroyed(): continue
759 ctrl.destroyController(reboot=reboot)
760 t = xstransact("%s/device" % self.path)
761 for d in t.list("vbd"):
762 t.remove(d)
763 for d in t.list("vif"):
764 t.remove(d)
765 for d in t.list("vtpm"):
766 t.remove(d)
767 t.commit()
769 def show(self):
770 """Print virtual machine info.
771 """
772 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.domid, self.name, self.memory, self.ssidref)
773 print "image:"
774 sxp.show(self.image)
775 print "]"
777 def init_domain(self):
778 """Initialize the domain memory.
779 """
780 if self.recreate:
781 return
782 if self.start_time is None:
783 self.start_time = time.time()
784 try:
785 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
786 except:
787 raise VmError('invalid cpu')
788 id = self.image.initDomain(self.domid, self.memory, self.ssidref, cpu, self.cpu_weight)
789 log.debug('init_domain> Created domain=%d name=%s memory=%d',
790 id, self.name, self.memory)
791 self.setDomid(id)
793 def eventChannel(self, path=None):
794 """Create an event channel to the domain.
796 @param path under which port is stored in db
797 """
798 port = 0
799 if path:
800 try:
801 port = int(xstransact.Read(self.path, path))
802 except:
803 # if anything goes wrong, assume the port was not yet set
804 pass
805 ret = EventChannel.interdomain(0, self.domid, port1=port, port2=0)
806 xstransact.Write(self.path, path, "%i" % ret.port1)
807 return ret
809 def create_channel(self):
810 """Create the channels to the domain.
811 """
812 self.store_channel = self.eventChannel("store/port")
813 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 vcpu_hotplug(self, vcpu, state):
1044 """Disable or enable VCPU in domain.
1045 """
1046 if vcpu > self.vcpus:
1047 log.error("Invalid VCPU %d" % vcpu)
1048 return
1049 if int(state) == 0:
1050 availability = "offline"
1051 else:
1052 availability = "online"
1053 xstransact.Write(self.path, "cpu/%d/availability" % vcpu, availability)
1055 def shutdown(self, reason):
1056 if not reason in shutdown_reasons.values():
1057 raise XendError('invalid reason:' + reason)
1058 xstransact.Write(self.path, "control/shutdown", reason)
1059 if not reason in ['suspend']:
1060 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1062 def clear_shutdown(self):
1063 xstransact.Remove(self.path, "control/shutdown")
1065 def send_sysrq(self, key=0):
1066 xstransact.Write(self.path, "control/sysrq", '%c' % key)
1068 def shutdown_time_left(self, timeout):
1069 if not self.shutdown_pending:
1070 return 0
1071 return timeout - (time.time() - self.shutdown_pending['start'])
1073 def dom0_init_store(self):
1074 if not self.store_channel:
1075 self.store_channel = self.eventChannel("store/port")
1076 if not self.store_channel:
1077 return
1078 ref = xc.init_store(self.store_channel.port2)
1079 if ref and ref >= 0:
1080 self.setStoreRef(ref)
1081 try:
1082 IntroduceDomain(self.domid, ref, self.store_channel.port1,
1083 self.path)
1084 except RuntimeError, ex:
1085 if ex.args[0] == errno.EISCONN:
1086 pass
1087 else:
1088 raise
1089 # get run-time value of vcpus and update store
1090 self.configure_vcpus(dom_get(self.domid)['vcpus'])
1093 def vm_field_ignore(_, _1, _2, _3):
1094 """Dummy config field handler used for fields with built-in handling.
1095 Matches the signature required by config_handlers.
1096 """
1097 pass
1100 def vm_field_maxmem(vm, _1, val, _2):
1101 """Config field handler to configure vm memory limit. Matches the
1102 signature required by config_handlers.
1103 """
1104 maxmem = sxp.child0(val)
1105 if maxmem is None:
1106 maxmem = vm.memory
1107 try:
1108 maxmem = int(maxmem)
1109 except:
1110 raise VmError("invalid maxmem: " + str(maxmem))
1111 xc.domain_setmaxmem(vm.domid, maxmem_kb = maxmem * 1024)
1114 #============================================================================
1115 # Register image handlers.
1117 from image import \
1118 addImageHandlerClass, \
1119 ImageHandler, \
1120 LinuxImageHandler, \
1121 VmxImageHandler
1123 addImageHandlerClass(LinuxImageHandler)
1124 addImageHandlerClass(VmxImageHandler)
1127 """Table of handlers for field configuration.
1129 field_name[String]: fn(vm, config, field, index) -> value(ignored)
1130 """
1131 config_handlers = {
1133 # Ignore the fields we already handle.
1135 'name': vm_field_ignore,
1136 'memory': vm_field_ignore,
1137 'ssidref': vm_field_ignore,
1138 'cpu': vm_field_ignore,
1139 'cpu_weight': vm_field_ignore,
1140 'restart': vm_field_ignore,
1141 'image': vm_field_ignore,
1142 'device': vm_field_ignore,
1143 'backend': vm_field_ignore,
1144 'vcpus': vm_field_ignore,
1145 'bootloader': vm_field_ignore,
1147 # Register other config handlers.
1148 'maxmem': vm_field_maxmem
1152 #============================================================================
1153 # Register device controllers and their device config types.
1155 from xen.xend.server import blkif, netif, tpmif, pciif, usbif
1156 controller.addDevControllerClass("vbd", blkif.BlkifController)
1157 controller.addDevControllerClass("vif", netif.NetifController)
1158 controller.addDevControllerClass("vtpm", tpmif.TPMifController)
1159 controller.addDevControllerClass("pci", pciif.PciController)
1160 controller.addDevControllerClass("usb", usbif.UsbifController)