ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 1770:1331621fe269

bitkeeper revision 1.1076 (40f3f7b4Cj4P09-PODiTO2MMFlKicw)

Add device create and device destroy for an existing domain.
author mjw@wray-m-3.hpl.hp.com
date Tue Jul 13 14:54:44 2004 +0000 (2004-07-13)
parents 095e969226c4
children 524a61d40438
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
3 """Handler for domain operations.
4 Nothing here is persistent (across reboots).
5 Needs to be persistent for one uptime.
6 """
7 import sys
8 import traceback
10 from twisted.internet import defer
11 #defer.Deferred.debug = 1
12 from twisted.internet import reactor
14 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
16 import sxp
17 import XendRoot
18 xroot = XendRoot.instance()
19 import XendDB
20 import XendDomainInfo
21 import XendConsole
22 import XendMigrate
23 import EventServer
25 from xen.xend.server import SrvDaemon
26 xend = SrvDaemon.instance()
28 eserver = EventServer.instance()
30 __all__ = [ "XendDomain" ]
32 class XendDomain:
33 """Index of all domains. Singleton.
34 """
36 """Path to domain database."""
37 dbpath = "domain"
39 """Table of domain info indexed by domain id."""
40 domain = {}
42 """Table of configs for domain restart, indexed by domain id."""
43 restarts = {}
45 """Table of delayed calls."""
46 schedule = {}
48 def __init__(self):
49 self.xconsole = XendConsole.instance()
50 # Table of domain info indexed by domain id.
51 self.db = XendDB.XendDB(self.dbpath)
52 self.domain_db = self.db.fetchall("")
53 if xroot.get_rebooted():
54 print 'XendDomain> rebooted: removing all domain info'
55 self.rm_all()
56 eserver.subscribe('xend.virq', self.onVirq)
57 self.initial_refresh()
59 def onVirq(self, event, val):
60 """Event handler for virq.
61 """
62 print 'XendDomain> virq', val
63 self.reap()
65 def schedule_later(self, _delay, _name, _fn, *args):
66 """Schedule a function to be called later (if not already scheduled).
68 @param _delay: delay in seconds
69 @param _name: schedule name
70 @param _fn: function
71 @param args: arguments
72 """
73 if self.schedule.get(_name): return
74 self.schedule[_name] = reactor.callLater(_delay, _fn, *args)
76 def schedule_cancel(self, name):
77 """Cancel a scheduled function call.
79 @param name: schedule name to cancel
80 """
81 callid = self.schedule.get(name)
82 if not callid:
83 return
84 if callid.active():
85 callid.cancel()
86 del self.schedule[name]
88 def reap_schedule(self, delay=0):
89 """Schedule reap to be called later.
91 @param delay: delay in seconds
92 """
93 self.schedule_later(delay, 'reap', self.reap)
95 def reap_cancel(self):
96 """Cancel any scheduled reap.
97 """
98 self.schedule_cancel('reap')
100 def refresh_schedule(self, delay=0):
101 """Schedule refresh to be called later.
103 @param delay: delay in seconds
104 """
105 self.schedule_later(delay, 'refresh', self.refresh)
107 def refresh_cancel(self):
108 """Cancel any scheduled refresh.
109 """
110 self.schedule_cancel('refresh')
112 def domain_restarts_schedule(self, delay=0):
113 """Schedule domain_restarts to be called later.
115 @param delay: delay in seconds
116 """
117 self.schedule_later(delay, 'domain_restarts', self.domain_restarts)
119 def domain_restarts_cancel(self):
120 """Cancel any scheduled call of domain_restarts.
121 """
122 self.schedule_cancel('domain_restarts')
124 def rm_all(self):
125 """Remove all domain info. Used after reboot.
126 """
127 for (k, v) in self.domain_db.items():
128 self._delete_domain(k, notify=0)
130 def initial_refresh(self):
131 """Refresh initial domain info from domain_db.
132 """
133 for d in self.domain_db.values(): print 'db dom=', d
134 domlist = xc.domain_getinfo()
135 for d in domlist: print 'xc dom=', d
136 doms = {}
137 for d in domlist:
138 domid = str(d['dom'])
139 doms[domid] = d
140 dlist = []
141 for config in self.domain_db.values():
142 domid = str(sxp.child_value(config, 'id'))
143 print "dom=", domid, "config=", config
144 if domid in doms:
145 print "dom=", domid, "new"
146 deferred = self._new_domain(config, doms[domid])
147 dlist.append(deferred)
148 else:
149 print "dom=", domid, "del"
150 self._delete_domain(domid)
151 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
152 def cbok(val):
153 #print "doms:"
154 #for d in self.domain.values(): print 'dom', d
155 self.refresh()
156 print "XendDomain>initial_refresh> doms:"
157 for d in self.domain.values(): print 'dom', d
158 deferred.addCallback(cbok)
160 def sync(self):
161 """Sync domain db to disk.
162 """
163 self.db.saveall("", self.domain_db)
165 def sync_domain(self, dom):
166 """Sync info for a domain to disk.
168 dom domain id (string)
169 """
170 self.db.save(dom, self.domain_db[dom])
172 def close(self):
173 pass
175 def _new_domain(self, savedinfo, info):
176 """Create a domain entry from saved info.
178 @param savedinfo: saved info from the db
179 @param info: domain info from xen
180 @return: deferred
181 """
182 config = sxp.child_value(savedinfo, 'config')
183 deferred = XendDomainInfo.vm_recreate(config, info)
184 def fn(dominfo):
185 self.domain[dominfo.id] = dominfo
186 deferred.addCallback(fn)
187 return deferred
189 def _add_domain(self, id, info, notify=1):
190 """Add a domain entry to the tables.
192 @param id: domain id
193 @param info: domain info object
194 @param notify: send a domain created event if true
195 """
196 self.domain[id] = info
197 self.domain_db[id] = info.sxpr()
198 self.sync_domain(id)
199 if notify: eserver.inject('xend.domain.created', id)
201 def _delete_domain(self, id, notify=1):
202 """Remove a domain from the tables.
204 @param id: domain id
205 @param notify: send a domain died event if true
206 """
207 if id in self.domain:
208 if notify: eserver.inject('xend.domain.died', id)
209 del self.domain[id]
210 if id in self.domain_db:
211 del self.domain_db[id]
212 self.db.delete(id)
214 def reap(self):
215 """Look for domains that have crashed or stopped.
216 Tidy them up.
217 """
218 self.reap_cancel()
219 domlist = xc.domain_getinfo()
220 casualties = []
221 for d in domlist:
222 dead = 0
223 dead = dead or (d['crashed'] or d['shutdown'])
224 dead = dead or (d['dying'] and
225 not(d['running'] or d['paused'] or d['blocked']))
226 if dead:
227 casualties.append(d)
228 for d in casualties:
229 id = str(d['dom'])
230 print 'XendDomain>reap> died id=', id, d
231 if d['shutdown']:
232 reason = XendDomainInfo.shutdown_reason(d['shutdown_reason'])
233 print 'XendDomain>reap> shutdown id=', id, reason
234 if reason in ['poweroff', 'reboot']:
235 self.domain_restart_schedule(id, reason)
236 self.final_domain_destroy(id)
237 if len(self.restarts):
238 self.domain_restarts_schedule()
240 def refresh(self):
241 """Refresh domain list from Xen.
242 """
243 self.refresh_cancel()
244 print 'XendDomain>refresh>'
245 domlist = xc.domain_getinfo()
246 # Index the domlist by id.
247 # Add entries for any domains we don't know about.
248 doms = {}
249 for d in domlist:
250 id = str(d['dom'])
251 doms[id] = d
252 if id not in self.domain:
253 config = None
254 deferred = XendDomainInfo.vm_recreate(config, d)
255 def fn(dominfo):
256 self._add_domain(dominfo.id, dominfo)
257 deferred.addCallback(fn)
258 # Remove entries for domains that no longer exist.
259 for d in self.domain.values():
260 dominfo = doms.get(d.id)
261 if dominfo:
262 d.update(dominfo)
263 else:
264 self._delete_domain(d.id)
265 self.reap_schedule(1)
267 def refresh_domain(self, id):
268 """Refresh information for a single domain.
270 @param id: domain id
271 """
272 dom = int(id)
273 dominfo = xc.domain_getinfo(dom, 1)
274 if dominfo == [] or dominfo[0]['dom'] != dom:
275 try:
276 self._delete_domain(id)
277 except:
278 print 'refresh_domain: error'
279 raise
280 pass
281 else:
282 d = self.domain.get(id)
283 if d:
284 d.update(dominfo[0])
286 def domain_ls(self):
287 """Get list of domain ids.
289 @return: domain ids
290 """
291 self.refresh()
292 return self.domain.keys()
294 def domains(self):
295 """Get list of domain objects.
297 @return: domain objects
298 """
299 self.refresh()
300 return self.domain.values()
302 def domain_create(self, config):
303 """Create a domain from a configuration.
305 @param config: configuration
306 @return: deferred
307 """
308 deferred = XendDomainInfo.vm_create(config)
309 def fn(dominfo):
310 self._add_domain(dominfo.id, dominfo)
311 return dominfo
312 deferred.addCallback(fn)
313 return deferred
315 def domain_configure(self, id, config):
316 """Configure an existing domain. This is intended for internal
317 use by domain restore and migrate.
319 @param id: domain id
320 @param config: configuration
321 @return: deferred
322 """
323 print 'domain_configure>', id, config
324 dom = int(id)
325 dominfo = self.domain_get(dom)
326 if not dominfo:
327 raise ValueError("Invalid domain: " + str(id))
328 if dominfo.config:
329 raise ValueError("Domain already configured: " + str(id))
330 def fn(dominfo):
331 self._add_domain(dominfo.id, dominfo)
332 return dominfo
333 deferred = dominfo.construct(config)
334 deferred.addCallback(fn)
335 return deferred
337 def domain_restore(self, src, progress=0):
338 """Restore a domain from file.
340 @param src: source file
341 @param progress: output progress if true
342 @return: deferred
343 """
345 def fn(dominfo):
346 self._add_domain(dominfo.id, dominfo)
347 return dominfo
348 deferred = XendDomainInfo.vm_restore(src, progress=progress)
349 deferred.addCallback(fn)
350 return deferred
352 def domain_get(self, id):
353 """Get up-to-date info about a domain.
355 @param id: domain id
356 @return: domain object (or None)
357 """
358 id = str(id)
359 self.refresh_domain(id)
360 return self.domain.get(id)
362 def domain_unpause(self, id):
363 """Unpause domain execution.
365 @param id: domain id
366 """
367 dom = int(id)
368 eserver.inject('xend.domain.unpause', id)
369 return xc.domain_unpause(dom=dom)
371 def domain_pause(self, id):
372 """Pause domain execution.
374 @param id: domain id
375 """
376 dom = int(id)
377 eserver.inject('xend.domain.pause', id)
378 return xc.domain_pause(dom=dom)
380 def domain_shutdown(self, id, reason='poweroff'):
381 """Shutdown domain (nicely).
382 - poweroff: domain will restart if has autorestart set.
383 - reboot: domain will restart.
384 - halt: domain will not restart (even if has autorestart set).
386 Returns immediately.
388 @param id: domain id
389 @param reason: shutdown type: poweroff, reboot, suspend, halt
390 """
391 dom = int(id)
392 if dom <= 0:
393 return 0
394 if reason == 'halt':
395 self.domain_restart_cancel(id)
396 else:
397 self.domain_restart_schedule(id, reason)
398 eserver.inject('xend.domain.shutdown', [id, reason])
399 if reason == 'halt':
400 reason = 'poweroff'
401 val = xend.domain_shutdown(dom, reason)
402 self.refresh_schedule()
403 return val
405 def domain_restart_schedule(self, id, reason):
406 """Schedule a restart for a domain if it needs one.
408 @param id: domain id
409 @param reason: shutdown reason
410 """
411 print 'domain_restart_schedule>', id, reason
412 dominfo = self.domain.get(id)
413 if not dominfo or id in self.restarts:
414 # Don't schedule if unknown or already there.
415 print 'domain_restart_schedule> no domain'
416 return
417 restart = ((reason == 'reboot') or
418 (reason == 'poweroff' and dominfo.autorestart))
419 if restart:
420 # Clear autorestart flag to avoid multiple restarts.
421 dominfo.autorestart = 0
422 self.restarts[id] = dominfo.config
423 print 'Scheduling restart for domain:', id, dominfo.name
424 self.domain_restarts_schedule()
426 def domain_restart_cancel(self, id):
427 """Cancel any restart scheduled for a domain.
429 @param id: domain id
430 """
431 print 'domain_restart_cancel>', id
432 dominfo = self.domain.get(id)
433 if dominfo:
434 dominfo.autorestart = 0
435 if id in self.restarts:
436 del self.restarts[id]
438 def domain_restarts(self):
439 """Execute any scheduled domain restarts for domains that have gone.
440 """
441 print 'domain_restarts>'
442 self.domain_restarts_cancel()
443 for id in self.restarts.keys():
444 if id in self.domain:
445 print 'domain_restarts> still running:', id
446 # Don't execute restart for domains still running.
447 continue
448 config = self.restarts[id]
449 # Remove it from the restarts.
450 del self.restarts[id]
451 try:
452 print 'domain_restarts> restart:', id, config
453 def cbok(dominfo):
454 print 'Restarted domain', id, 'as', dominfo.id
455 self.domain_unpause(dominfo.id)
456 def cberr(err):
457 print >>sys.stderr, "Delayed exception restarting domain: ", err
458 deferred = self.domain_create(config)
459 deferred.addCallback(cbok)
460 deferred.addErrback(cberr)
461 except:
462 print >>sys.stderr, "XendDomain> Exception restarting domain"
463 traceback.print_exc(sys.stderr)
464 if len(self.restarts):
465 self.refresh_schedule(delay=5)
467 def final_domain_destroy(self, id):
468 """Final destruction of a domain..
470 @param id: domain id
471 """
472 dom = int(id)
473 if dom <= 0:
474 return 0
475 eserver.inject('xend.domain.destroy', id)
476 dominfo = self.domain.get(id)
477 if dominfo:
478 val = dominfo.destroy()
479 else:
480 val = xc.domain_destroy(dom=dom)
481 return val
483 def domain_destroy(self, id):
484 """Terminate domain immediately.
485 Cancels any restart for the domain.
487 @param id: domain id
488 """
489 self.domain_restart_cancel(id)
490 val = self.final_domain_destroy(id)
491 self.refresh_schedule()
492 return val
494 def domain_migrate(self, id, dst):
495 """Start domain migration.
497 @param id: domain id
498 @return: deferred
499 """
500 # Need a cancel too?
501 # Don't forget to cancel restart for it.
502 print 'domain_migrate>', id, dst
503 dom = int(id)
504 xmigrate = XendMigrate.instance()
505 val = xmigrate.migrate_begin(dom, dst)
506 print 'domain_migrate<', val
507 return val
509 def domain_save(self, id, dst, progress=0):
510 """Start saving a domain to file.
512 @param id: domain id
513 @param dst: destination file
514 @param progress: output progress if true
515 @return: deferred
516 """
517 dom = int(id)
518 xmigrate = XendMigrate.instance()
519 return xmigrate.save_begin(dom, dst)
521 def domain_pincpu(self, dom, cpu):
522 """Pin a domain to a cpu.
524 @param dom: domain
525 @param cpu: cpu number
526 """
527 dom = int(dom)
528 return xc.domain_pincpu(dom, cpu)
530 def domain_cpu_bvt_set(self, dom, mcuadv, warp, warpl, warpu):
531 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
532 """
533 dom = int(dom)
534 return xc.bvtsched_domain_set(dom=dom, mcuadv=mcuadv,
535 warp=warp, warpl=warpl, warpu=warpu)
537 def domain_cpu_bvt_get(self, dom):
538 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
539 """
540 dom = int(dom)
541 return xc.bvtsched_domain_get(dom)
543 def domain_cpu_fbvt_set(self, dom, mcuadv, warp, warpl, warpu):
544 """Set FBVT (Fair Borrowed Virtual Time) scheduler parameters for a domain.
545 """
546 dom = int(dom)
547 return xc.fbvtsched_domain_set(dom=dom, mcuadv=mcuadv,
548 warp=warp, warpl=warpl, warpu=warpu)
550 def domain_cpu_fbvt_get(self, dom):
551 """Get FBVT (Fair Borrowed Virtual Time) scheduler parameters for a domain.
552 """
553 dom = int(dom)
554 return xc.fbvtsched_domain_get(dom)
556 def domain_cpu_atropos_set(self, dom, period, slice, latency, xtratime):
557 """Set Atropos scheduler parameters for a domain.
558 """
559 dom = int(dom)
560 return xc.atropos_domain_set(dom, period, slice, latency, xtratime)
562 def domain_cpu_atropos_get(self, dom):
563 """Get Atropos scheduler parameters for a domain.
564 """
565 dom = int(dom)
566 return xc.atropos_domain_get(dom)
568 def domain_device_create(self, dom, devconfig):
569 dom = int(dom)
570 dominfo = self.domain_get(dom)
571 if not dominfo:
572 raise ValueError("invalid domain:" + str(dom))
573 return dominfo.device_create(devconfig)
576 def domain_device_destroy(self, dom, type, idx):
577 dom = int(dom)
578 dominfo = self.domain_get(dom)
579 if not dominfo:
580 raise ValueError("invalid domain:" + str(dom))
581 return dominfo.device_destroy(type, idx)
583 def domain_devtype_ls(self, dom, type):
584 """Get list of device indexes for a domain.
586 @param dom: domain
587 @param type: device type
588 @return: device indexes
589 """
590 dominfo = self.domain_get(dom)
591 if not dominfo: return None
592 devs = dominfo.get_devices(type)
593 return range(0, len(devs))
595 def domain_devtype_get(self, dom, type, idx):
596 """Get a device from a domain.
598 @param dom: domain
599 @param type: device type
600 @param idx: device index
601 @return: device object (or None)
602 """
603 dominfo = self.domain_get(dom)
604 if not dominfo: return None
605 return dominfo.get_device_by_index(type, idx)
607 def domain_vif_ls(self, dom):
608 """Get list of virtual network interface (vif) indexes for a domain.
610 @param dom: domain
611 @return: vif indexes
612 """
613 return self.domain_devtype_ls(dom, 'vif')
615 def domain_vif_get(self, dom, vif):
616 """Get a virtual network interface (vif) from a domain.
618 @param dom: domain
619 @param vif: vif index
620 @return: vif device object (or None)
621 """
622 return self.domain_devtype_get(dom, 'vif', vif)
624 def domain_vbd_ls(self, dom):
625 """Get list of virtual block device (vbd) indexes for a domain.
627 @param dom: domain
628 @return: vbd indexes
629 """
630 return self.domain_devtype_ls(dom, 'vbd')
632 def domain_vbd_get(self, dom, vbd):
633 """Get a virtual block device (vbd) from a domain.
635 @param dom: domain
636 @param vbd: vbd index
637 @return: vbd device (or None)
638 """
639 return self.domain_devtype_get(dom, 'vbd', vbd)
641 def domain_shadow_control(self, dom, op):
642 """Shadow page control.
644 @param dom: domain
645 @param op: operation
646 """
647 dom = int(dom)
648 return xc.shadow_control(dom, op)
651 def instance():
652 """Singleton constructor. Use this instead of the class constructor.
653 """
654 global inst
655 try:
656 inst
657 except:
658 inst = XendDomain()
659 return inst