direct-io.hg

view tools/python/xen/xend/XendDomain.py @ 2693:2584528df9e1

bitkeeper revision 1.1159.123.2 (4177d169N58TtQXn_XJO4xNBKbMQUw)

Merge freefall.cl.cam.ac.uk:/auto/groups/xeno/BK/xeno.bk
into freefall.cl.cam.ac.uk:/local/scratch/kaf24/xeno
author kaf24@freefall.cl.cam.ac.uk
date Thu Oct 21 15:10:33 2004 +0000 (2004-10-21)
parents c084a63b2b64 d8e27145f1eb
children 41e31d1ac03f 5233708cfa46
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 XendMigrate
22 import EventServer
23 from XendError import XendError
24 from XendLogging import log
27 from xen.xend.server import SrvDaemon
28 xend = SrvDaemon.instance()
30 eserver = EventServer.instance()
32 __all__ = [ "XendDomain" ]
34 class XendDomain:
35 """Index of all domains. Singleton.
36 """
38 """Path to domain database."""
39 dbpath = "domain"
41 """Table of domain info indexed by domain id."""
42 domain_by_id = {}
43 domain_by_name = {}
45 """Table of domains to restart, indexed by domain id."""
46 restarts_by_id = {}
47 restarts_by_name = {}
49 """Table of delayed calls."""
50 schedule = {}
52 def __init__(self):
53 # Hack alert. Python does not support mutual imports, but XendDomainInfo
54 # needs access to the XendDomain instance to look up domains. Attempting
55 # to import XendDomain from XendDomainInfo causes unbounded recursion.
56 # So we stuff the XendDomain instance (self) into xroot's components.
57 xroot.add_component("xen.xend.XendDomain", self)
58 # Table of domain info indexed by domain id.
59 self.db = XendDB.XendDB(self.dbpath)
60 self.domain_db = self.db.fetchall("")
61 if xroot.get_rebooted():
62 log.info('XendDomain> rebooted: removing all domain info')
63 self.rm_all()
64 eserver.subscribe('xend.virq', self.onVirq)
65 self.initial_refresh()
67 def onVirq(self, event, val):
68 """Event handler for virq.
69 """
70 self.reap()
72 def schedule_later(self, _delay, _name, _fn, *args):
73 """Schedule a function to be called later (if not already scheduled).
75 @param _delay: delay in seconds
76 @param _name: schedule name
77 @param _fn: function
78 @param args: arguments
79 """
80 if self.schedule.get(_name): return
81 self.schedule[_name] = reactor.callLater(_delay, _fn, *args)
83 def schedule_cancel(self, name):
84 """Cancel a scheduled function call.
86 @param name: schedule name to cancel
87 """
88 callid = self.schedule.get(name)
89 if not callid:
90 return
91 if callid.active():
92 callid.cancel()
93 del self.schedule[name]
95 def reap_schedule(self, delay=0):
96 """Schedule reap to be called later.
98 @param delay: delay in seconds
99 """
100 self.schedule_later(delay, 'reap', self.reap)
102 def reap_cancel(self):
103 """Cancel any scheduled reap.
104 """
105 self.schedule_cancel('reap')
107 def refresh_schedule(self, delay=0):
108 """Schedule refresh to be called later.
110 @param delay: delay in seconds
111 """
112 self.schedule_later(delay, 'refresh', self.refresh)
114 def refresh_cancel(self):
115 """Cancel any scheduled refresh.
116 """
117 self.schedule_cancel('refresh')
119 def domain_restarts_schedule(self, delay=0):
120 """Schedule domain_restarts to be called later.
122 @param delay: delay in seconds
123 """
124 self.schedule_later(delay, 'domain_restarts', self.domain_restarts)
126 def domain_restarts_cancel(self):
127 """Cancel any scheduled call of domain_restarts.
128 """
129 self.schedule_cancel('domain_restarts')
131 def rm_all(self):
132 """Remove all domain info. Used after reboot.
133 """
134 for (k, v) in self.domain_db.items():
135 self._delete_domain(k, notify=0)
137 def initial_refresh(self):
138 """Refresh initial domain info from domain_db.
139 """
141 def cb_all_ok(val):
142 self.refresh()
144 domlist = xc.domain_getinfo()
145 doms = {}
146 for d in domlist:
147 domid = str(d['dom'])
148 doms[domid] = d
149 dlist = []
150 for config in self.domain_db.values():
151 domid = str(sxp.child_value(config, 'id'))
152 if domid in doms:
153 d_dom = self._new_domain(config, doms[domid])
154 dlist.append(d_dom)
155 else:
156 self._delete_domain(domid)
157 d_all = defer.DeferredList(dlist, fireOnOneErrback=1)
158 d_all.addCallback(cb_all_ok)
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 def cbok(dominfo):
183 self.domain_by_id[dominfo.id] = dominfo
184 self.domain_by_name[dominfo.name] = dominfo
185 if dominfo.restart_pending():
186 self.domain_restart_add(dominfo)
188 deferred = XendDomainInfo.vm_recreate(savedinfo, info)
189 deferred.addCallback(cbok)
190 return deferred
192 def _add_domain(self, info, notify=1):
193 """Add a domain entry to the tables.
195 @param info: domain info object
196 @param notify: send a domain created event if true
197 """
198 self.domain_by_id[info.id] = info
199 self.domain_db[info.id] = info.sxpr()
200 for k, d in self.domain_by_name.items():
201 if k != d.name:
202 del self.domain_by_name[k]
203 if info.name:
204 self.domain_by_name[info.name] = info
205 self.sync_domain(info.id)
206 if notify: eserver.inject('xend.domain.create', [info.name, info.id])
208 def _delete_domain(self, id, notify=1):
209 """Remove a domain from the tables.
211 @param id: domain id
212 @param notify: send a domain died event if true
213 """
214 for (k, info) in self.domain_by_name.items():
215 if info.id == id:
216 del self.domain_by_name[k]
217 if id in self.domain_by_id:
218 info = self.domain_by_id[id]
219 del self.domain_by_id[id]
220 if notify: eserver.inject('xend.domain.died', [info.name, info.id])
221 if id in self.domain_db:
222 del self.domain_db[id]
223 self.db.delete(id)
225 def reap(self):
226 """Look for domains that have crashed or stopped.
227 Tidy them up.
228 """
229 self.reap_cancel()
230 domlist = xc.domain_getinfo()
231 casualties = []
232 for d in domlist:
233 dead = 0
234 dead = dead or (d['crashed'] or d['shutdown'])
235 dead = dead or (d['dying'] and
236 not(d['running'] or d['paused'] or d['blocked']))
237 if dead:
238 casualties.append(d)
239 destroyed = 0
240 for d in casualties:
241 id = str(d['dom'])
242 dominfo = self.domain_by_id.get(id)
243 name = (dominfo and dominfo.name) or '??'
244 log.debug('XendDomain>reap> domain died name=%s id=%s', name, id)
245 if d['shutdown']:
246 reason = XendDomainInfo.shutdown_reason(d['shutdown_reason'])
247 log.debug('XendDomain>reap> shutdown id=%s reason=%s', id, reason)
248 if reason in ['suspend']:
249 if dominfo and dominfo.is_terminated():
250 log.debug('XendDomain>reap> Suspended domain died id=%s', id)
251 else:
252 eserver.inject('xend.domain.suspended', [name, id])
253 continue
254 if reason in ['poweroff', 'reboot']:
255 eserver.inject('xend.domain.exit', [name, id, reason])
256 self.domain_restart_schedule(id, reason)
257 else:
258 eserver.inject('xend.domain.exit', [name, id, 'crash'])
259 destroyed += 1
260 self.final_domain_destroy(id)
261 if self.domain_restarts_exist():
262 self.domain_restarts_schedule()
263 if destroyed:
264 self.refresh_schedule(delay=1)
266 def refresh(self):
267 """Refresh domain list from Xen.
268 """
269 self.refresh_cancel()
270 domlist = xc.domain_getinfo()
271 # Index the domlist by id.
272 # Add entries for any domains we don't know about.
273 doms = {}
274 for d in domlist:
275 id = str(d['dom'])
276 doms[id] = d
277 if id not in self.domain_by_id:
278 savedinfo = None
279 deferred = XendDomainInfo.vm_recreate(savedinfo, d)
280 def cbok(dominfo):
281 self._add_domain(dominfo)
282 deferred.addCallback(cbok)
283 # Remove entries for domains that no longer exist.
284 for d in self.domain_by_id.values():
285 info = doms.get(d.id)
286 if info:
287 d.update(info)
288 else:
289 self._delete_domain(d.id)
290 self.reap_schedule(delay=1)
292 def update_domain(self, id):
293 """Update the saved info for a domain.
295 @param id: domain id
296 """
297 dominfo = self.domain_by_id.get(id)
298 if dominfo:
299 self.domain_db[id] = dominfo.sxpr()
300 self.sync_domain(id)
302 def refresh_domain(self, id):
303 """Refresh information for a single domain.
305 @param id: domain id
306 """
307 dom = int(id)
308 dominfo = xc.domain_getinfo(dom, 1)
309 if dominfo == [] or dominfo[0]['dom'] != dom:
310 try:
311 self._delete_domain(id)
312 except:
313 log.exception('refresh_domain> error')
314 raise
315 pass
316 else:
317 d = self.domain_by_id.get(id)
318 if d:
319 d.update(dominfo[0])
321 def domain_ls(self):
322 """Get list of domain names.
324 @return: domain names
325 """
326 self.refresh()
327 return self.domain_by_name.keys()
329 def domain_ls_ids(self):
330 """Get list of domain ids.
332 @return: domain names
333 """
334 self.refresh()
335 return self.domain_by_id.keys()
337 def domains(self):
338 """Get list of domain objects.
340 @return: domain objects
341 """
342 self.refresh()
343 return self.domain_by_id.values()
345 def domain_create(self, config):
346 """Create a domain from a configuration.
348 @param config: configuration
349 @return: deferred
350 """
351 def cbok(dominfo):
352 self._add_domain(dominfo)
353 return dominfo
354 deferred = XendDomainInfo.vm_create(config)
355 deferred.addCallback(cbok)
356 return deferred
358 def domain_restart(self, dominfo):
359 """Restart a domain.
361 @param dominfo: domain object
362 @return: deferred
363 """
364 def cbok(dominfo):
365 self._add_domain(dominfo)
366 return dominfo
367 log.info("Restarting domain: id=%s name=%s", dominfo.id, dominfo.name)
368 eserver.inject("xend.domain.restart",
369 [dominfo.name, dominfo.id, "begin"])
370 deferred = dominfo.restart()
371 deferred.addCallback(cbok)
372 return deferred
374 def domain_configure(self, id, vmconfig):
375 """Configure an existing domain. This is intended for internal
376 use by domain restore and migrate.
378 @param id: domain id
379 @param vmconfig: vm configuration
380 @return: deferred
381 """
382 config = sxp.child_value(vmconfig, 'config')
383 dominfo = self.domain_lookup(id)
384 log.debug('domain_configure> id=%s config=%s', str(id), str(config))
385 if dominfo.config:
386 raise XendError("Domain already configured: " + dominfo.id)
387 def cbok(dominfo):
388 self._add_domain(dominfo)
389 return dominfo
390 deferred = dominfo.dom_construct(dominfo.dom, config)
391 deferred.addCallback(cbok)
392 return deferred
394 def domain_restore(self, src, progress=0):
395 """Restore a domain from file.
397 @param src: source file
398 @param progress: output progress if true
399 @return: deferred
400 """
402 if 0:
403 def cbok(dominfo):
404 self._add_domain(dominfo)
405 return dominfo
406 deferred = XendDomainInfo.vm_restore(src, progress=progress)
407 deferred.addCallback(cbok)
408 else:
409 xmigrate = XendMigrate.instance()
410 deferred = xmigrate.restore_begin(src)
411 return deferred
413 def domain_get(self, id):
414 """Get up-to-date info about a domain.
416 @param id: domain id
417 @return: domain object (or None)
418 """
419 id = str(id)
420 self.refresh_domain(id)
421 return self.domain_by_id.get(id)
423 def domain_lookup(self, name):
424 name = str(name)
425 dominfo = self.domain_by_name.get(name) or self.domain_by_id.get(name)
426 if dominfo:
427 return dominfo
428 raise XendError('invalid domain:' + name)
430 def domain_exists(self, name):
431 name = str(name)
432 return self.domain_by_name.get(name) or self.domain_by_id.get(name)
434 def domain_unpause(self, id):
435 """Unpause domain execution.
437 @param id: domain id
438 """
439 dominfo = self.domain_lookup(id)
440 eserver.inject('xend.domain.unpause', [dominfo.name, dominfo.id])
441 try:
442 return xc.domain_unpause(dom=dominfo.dom)
443 except Exception, ex:
444 raise XendError(str(ex))
446 def domain_pause(self, id):
447 """Pause domain execution.
449 @param id: domain id
450 """
451 dominfo = self.domain_lookup(id)
452 eserver.inject('xend.domain.pause', [dominfo.name, dominfo.id])
453 try:
454 return xc.domain_pause(dom=dominfo.dom)
455 except Exception, ex:
456 raise XendError(str(ex))
458 def domain_shutdown(self, id, reason='poweroff'):
459 """Shutdown domain (nicely).
460 - poweroff: restart according to exit code and restart mode
461 - reboot: restart on exit
462 - halt: do not restart
464 Returns immediately.
466 @param id: domain id
467 @param reason: shutdown type: poweroff, reboot, suspend, halt
468 """
469 dominfo = self.domain_lookup(id)
470 if reason == 'halt':
471 self.domain_restart_cancel(dominfo.id)
472 else:
473 self.domain_restart_schedule(dominfo.id, reason, force=1)
474 eserver.inject('xend.domain.shutdown', [dominfo.name, dominfo.id, reason])
475 if reason == 'halt':
476 reason = 'poweroff'
477 val = xend.domain_shutdown(dominfo.id, reason)
478 self.refresh_schedule()
479 return val
481 def domain_restart_schedule(self, id, reason, force=0):
482 """Schedule a restart for a domain if it needs one.
484 @param id: domain id
485 @param reason: shutdown reason
486 """
487 log.debug('domain_restart_schedule> %s %s %d', id, reason, force)
488 dominfo = self.domain_lookup(id)
489 if not dominfo:
490 return
491 if dominfo.id in self.restarts_by_id:
492 return
493 restart = (force and reason == 'reboot') or dominfo.restart_needed(reason)
494 if restart:
495 dominfo.restarting()
496 self.domain_restart_add(dominfo)
498 def domain_restart_add(self, dominfo):
499 self.restarts_by_name[dominfo.name] = dominfo
500 self.restarts_by_id[dominfo.id] = dominfo
501 log.info('Scheduling restart for domain: name=%s id=%s', dominfo.name, dominfo.id)
502 eserver.inject("xend.domain.restart",
503 [dominfo.name, dominfo.id, "schedule"])
504 self.domain_restarts_schedule()
506 def domain_restart_cancel(self, id):
507 """Cancel any restart scheduled for a domain.
509 @param id: domain id
510 """
511 dominfo = self.restarts_by_id.get(id) or self.restarts_by_name.get(id)
512 if dominfo:
513 log.info('Cancelling restart for domain: name=%s id=%s', dominfo.name, dominfo.id)
514 eserver.inject("xend.domain.restart",
515 [dominfo.name, dominfo.id, "cancel"])
516 dominfo.restart_cancel()
517 del self.restarts_by_id[dominfo.id]
518 del self.restarts_by_name[dominfo.name]
520 def domain_restarts(self):
521 """Execute any scheduled domain restarts for domains that have gone.
522 """
523 self.domain_restarts_cancel()
524 for dominfo in self.restarts_by_id.values():
525 if dominfo.id in self.domain_by_id:
526 # Don't execute restart for domains still running.
527 continue
528 # Remove it from the restarts.
529 del self.restarts_by_id[dominfo.id]
530 del self.restarts_by_name[dominfo.name]
531 try:
532 def cbok(dominfo):
533 log.info('Restarted domain name=%s id=%s', dominfo.name, dominfo.id)
534 eserver.inject("xend.domain.restart",
535 [dominfo.name, dominfo.id, "success"])
536 self.domain_unpause(dominfo.id)
537 def cberr(err):
538 log.exception("Delayed exception restarting domain: name=%s id=%s",
539 dominfo.name, dominfo.id)
540 eserver.inject("xend.domain.restart",
541 [dominfo.name, dominfo.id, "fail"])
543 deferred = self.domain_restart(dominfo)
544 deferred.addCallback(cbok)
545 deferred.addErrback(cberr)
546 except:
547 log.exception("Exception restarting domain: name=%s id=%s",
548 dominfo.name, dominfo.id)
549 eserver.inject("xend.domain.restart",
550 [dominfo.name, dominfo.id, "fail"])
551 if self.domain_restarts_exist():
552 # Run again later if any restarts remain.
553 self.refresh_schedule(delay=5)
555 def domain_restarts_exist(self):
556 return len(self.restarts_by_id)
558 def final_domain_destroy(self, id):
559 """Final destruction of a domain..
561 @param id: domain id
562 """
563 dominfo = self.domain_lookup(id)
564 log.info('Destroying domain: name=%s', dominfo.name)
565 eserver.inject('xend.domain.destroy', [dominfo.name, dominfo.id])
566 if dominfo:
567 val = dominfo.destroy()
568 else:
569 #todo
570 val = xc.domain_destroy(dom=dominfo.dom)
571 return val
573 def domain_destroy(self, id, reason='halt'):
574 """Terminate domain immediately.
575 - halt: cancel any restart for the domain
576 - reboot schedule a restart for the domain
578 @param id: domain id
579 """
580 if reason == 'halt':
581 self.domain_restart_cancel(id)
582 elif reason == 'reboot':
583 self.domain_restart_schedule(id, reason, force=1)
584 val = self.final_domain_destroy(id)
585 self.refresh_schedule()
586 return val
588 def domain_migrate(self, id, dst, live):
589 """Start domain migration.
591 @param id: domain id
592 @return: deferred
593 """
594 # Need a cancel too?
595 # Don't forget to cancel restart for it.
596 dominfo = self.domain_lookup(id)
597 xmigrate = XendMigrate.instance()
598 val = xmigrate.migrate_begin(dominfo, dst, live=live)
599 return val
601 def domain_save(self, id, dst, progress=0):
602 """Start saving a domain to file.
604 @param id: domain id
605 @param dst: destination file
606 @param progress: output progress if true
607 @return: deferred
608 """
609 dominfo = self.domain_lookup(id)
610 xmigrate = XendMigrate.instance()
611 return xmigrate.save_begin(dominfo, dst)
613 def domain_pincpu(self, id, cpu):
614 """Pin a domain to a cpu.
616 @param id: domain
617 @param cpu: cpu number
618 """
619 dominfo = self.domain_lookup(id)
620 try:
621 return xc.domain_pincpu(int(dominfo.id), cpu)
622 except Exception, ex:
623 raise XendError(str(ex))
625 def domain_cpu_bvt_set(self, id, mcuadv, warpback, warpvalue, warpl, warpu):
626 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
627 """
628 dominfo = self.domain_lookup(id)
629 try:
630 return xc.bvtsched_domain_set(dom=dominfo.dom, mcuadv=mcuadv,
631 warpback=warpback, warpvalue=warpvalue,
632 warpl=warpl, warpu=warpu)
633 except Exception, ex:
634 raise XendError(str(ex))
636 def domain_cpu_bvt_get(self, id):
637 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
638 """
639 dominfo = self.domain_lookup(id)
640 try:
641 return xc.bvtsched_domain_get(dominfo.dom)
642 except Exception, ex:
643 raise XendError(str(ex))
645 def domain_cpu_atropos_set(self, id, period, slice, latency, xtratime):
646 """Set Atropos scheduler parameters for a domain.
647 """
648 dominfo = self.domain_lookup(id)
649 try:
650 return xc.atropos_domain_set(dominfo.dom, period, slice, latency, xtratime)
651 except Exception, ex:
652 raise XendError(str(ex))
654 def domain_cpu_atropos_get(self, id):
655 """Get Atropos scheduler parameters for a domain.
656 """
657 dominfo = self.domain_lookup(id)
658 try:
659 return xc.atropos_domain_get(dominfo.dom)
660 except Exception, ex:
661 raise XendError(str(ex))
663 def domain_device_create(self, id, devconfig):
664 """Create a new device for a domain.
666 @param id: domain id
667 @param devconfig: device configuration
668 @return: deferred
669 """
670 dominfo = self.domain_lookup(id)
671 self.refresh_schedule()
672 val = dominfo.device_create(devconfig)
673 self.update_domain(dominfo.id)
674 return val
676 def domain_device_configure(self, id, devconfig, idx):
677 """Configure an existing device for a domain.
679 @param id: domain id
680 @param devconfig: device configuration
681 @param idx: device index
682 @return: updated device configuration
683 """
684 dominfo = self.domain_lookup(id)
685 self.refresh_schedule()
686 val = dominfo.device_configure(devconfig, idx)
687 self.update_domain(dominfo.id)
688 return val
691 def domain_device_destroy(self, id, type, idx):
692 """Destroy a device.
694 @param id: domain id
695 @param idx: device index
696 @param type: device type
697 """
698 dominfo = self.domain_lookup(id)
699 self.refresh_schedule()
700 val = dominfo.device_destroy(type, idx)
701 self.update_domain(dominfo.id)
702 return val
704 def domain_devtype_ls(self, id, type):
705 """Get list of device indexes for a domain.
707 @param id: domain
708 @param type: device type
709 @return: device indexes
710 """
711 dominfo = self.domain_lookup(id)
712 devs = dominfo.get_devices(type)
713 return devs
715 def domain_devtype_get(self, id, type, idx):
716 """Get a device from a domain.
718 @param id: domain
719 @param type: device type
720 @param idx: device index
721 @return: device object (or None)
722 """
723 dominfo = self.domain_lookup(id)
724 return dominfo.get_device_by_index(type, idx)
726 def domain_vif_ls(self, id):
727 """Get list of virtual network interface (vif) indexes for a domain.
729 @param id: domain
730 @return: vif indexes
731 """
732 return self.domain_devtype_ls(id, 'vif')
734 def domain_vif_get(self, id, vif):
735 """Get a virtual network interface (vif) from a domain.
737 @param id: domain
738 @param vif: vif index
739 @return: vif device object (or None)
740 """
741 return self.domain_devtype_get(id, 'vif', vif)
743 def domain_vbd_ls(self, id):
744 """Get list of virtual block device (vbd) indexes for a domain.
746 @param id: domain
747 @return: vbd indexes
748 """
749 return self.domain_devtype_ls(id, 'vbd')
751 def domain_vbd_get(self, id, vbd):
752 """Get a virtual block device (vbd) from a domain.
754 @param id: domain
755 @param vbd: vbd index
756 @return: vbd device (or None)
757 """
758 return self.domain_devtype_get(id, 'vbd', vbd)
760 def domain_shadow_control(self, id, op):
761 """Shadow page control.
763 @param id: domain
764 @param op: operation
765 """
766 dominfo = self.domain_lookup(id)
767 try:
768 return xc.shadow_control(dominfo.dom, op)
769 except Exception, ex:
770 raise XendError(str(ex))
772 def domain_maxmem_set(self, id, mem):
773 """Set the memory limit for a domain.
775 @param dom: domain
776 @param mem: memory limit (in MB)
777 @return: 0 on success, -1 on error
778 """
779 dominfo = self.domain_lookup(id)
780 maxmem = int(mem) * 1024
781 try:
782 return xc.domain_setmaxmem(dominfo.dom, maxmem_kb = maxmem)
783 except Exception, ex:
784 raise XendError(str(ex))
786 def domain_mem_target_set(self, id, target):
787 return xend.domain_mem_target_set(id, target)
791 def instance():
792 """Singleton constructor. Use this instead of the class constructor.
793 """
794 global inst
795 try:
796 inst
797 except:
798 inst = XendDomain()
799 return inst