ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 7161:a73ab0ddf990

Call refresh in the domain_lookup and domain_lookup_by_name methods. This
should reduce the incidence of stale domain information being used by xend, but
is only a stop-gap.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@ewan
date Sun Oct 02 18:21:01 2005 +0100 (2005-10-02)
parents 4a2c162d3e7c
children bb03b43404bb 07b7411e385f
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 # Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
17 # Copyright (C) 2005 XenSource Ltd
18 #============================================================================
20 """Handler for domain operations.
21 Nothing here is persistent (across reboots).
22 Needs to be persistent for one uptime.
23 """
24 import os
25 import threading
27 import xen.lowlevel.xc
29 from xen.xend import sxp
30 from xen.xend import XendRoot
31 from xen.xend import XendCheckpoint
32 from xen.xend.XendDomainInfo import XendDomainInfo
33 from xen.xend import EventServer
34 from xen.xend.XendError import XendError
35 from xen.xend.XendLogging import log
36 from xen.xend.server import relocate
39 xc = xen.lowlevel.xc.new()
40 xroot = XendRoot.instance()
41 eserver = EventServer.instance()
44 __all__ = [ "XendDomain" ]
46 PRIV_DOMAIN = 0
48 class XendDomainDict(dict):
49 def get_by_name(self, name):
50 try:
51 return filter(lambda d: d.getName() == name, self.values())[0]
52 except IndexError, err:
53 return None
55 class XendDomain:
56 """Index of all domains. Singleton.
57 """
59 """Dict of domain info indexed by domain id."""
60 domains = None
63 ## public:
65 def __init__(self):
66 # Hack alert. Python does not support mutual imports, but XendDomainInfo
67 # needs access to the XendDomain instance to look up domains. Attempting
68 # to import XendDomain from XendDomainInfo causes unbounded recursion.
69 # So we stuff the XendDomain instance (self) into xroot's components.
70 xroot.add_component("xen.xend.XendDomain", self)
71 self.domains = XendDomainDict()
72 self.refresh_lock = threading.Condition()
73 self.watchReleaseDomain()
74 self.refresh()
75 self.dom0_setup()
77 def list(self):
78 """Get list of domain objects.
80 @return: domain objects
81 """
82 self.refresh()
83 return self.domains.values()
85 def list_sorted(self):
86 """Get list of domain objects, sorted by name.
88 @return: domain objects
89 """
90 doms = self.list()
91 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
92 return doms
94 def list_names(self):
95 """Get list of domain names.
97 @return: domain names
98 """
99 doms = self.list_sorted()
100 return map(lambda x: x.getName(), doms)
103 ## private:
105 def onReleaseDomain(self):
106 self.refresh()
108 def watchReleaseDomain(self):
109 from xen.xend.xenstore.xswatch import xswatch
110 self.releaseDomain = xswatch("@releaseDomain", self.onReleaseDomain)
112 def xen_domains(self):
113 """Get table of domains indexed by id from xc.
114 """
115 domlist = xc.domain_getinfo()
116 doms = {}
117 for d in domlist:
118 domid = d['dom']
119 doms[domid] = d
120 return doms
122 def xen_domain(self, dom):
123 """Get info about a single domain from xc.
124 Returns None if not found.
126 @param dom domain id (int)
127 """
128 dominfo = xc.domain_getinfo(dom, 1)
129 if dominfo == [] or dominfo[0]['dom'] != dom:
130 dominfo = None
131 else:
132 dominfo = dominfo[0]
133 return dominfo
136 def recreate_domain(self, xeninfo):
137 """Refresh initial domain info from db."""
139 dominfo = XendDomainInfo.recreate(xeninfo)
140 self._add_domain(dominfo)
141 return dominfo
144 def dom0_setup(self):
145 dom0 = self.domain_lookup(PRIV_DOMAIN)
146 dom0.dom0_enforce_vcpus()
149 def _add_domain(self, info, notify=True):
150 """Add a domain entry to the tables.
152 @param info: domain info object
153 @param notify: send a domain created event if true
154 """
155 if info.getDomid() in self.domains:
156 notify = False
157 self.domains[info.getDomid()] = info
158 #info.exportToDB()
159 #if notify:
160 # eserver.inject('xend.domain.create', [info.getName(),
161 # info.getDomid()])
163 def _delete_domain(self, domid, notify=True):
164 """Remove a domain from the tables.
166 @param id: domain id
167 @param notify: send a domain died event if true
168 """
169 info = self.domains.get(domid)
170 if info:
171 del self.domains[domid]
172 info.cleanupDomain()
173 info.cleanupVm()
174 if notify:
175 eserver.inject('xend.domain.died', [info.getName(),
176 info.getDomid()])
179 def refresh(self):
180 """Refresh domain list from Xen.
181 """
182 self.refresh_lock.acquire()
183 try:
184 doms = self.xen_domains()
185 for d in self.domains.values():
186 info = doms.get(d.getDomid())
187 if info:
188 d.update(info)
189 else:
190 self._delete_domain(d.getDomid())
191 for d in doms:
192 if d not in self.domains and not doms[d]['dying']:
193 try:
194 self.recreate_domain(doms[d])
195 except:
196 if d == PRIV_DOMAIN:
197 log.exception(
198 "Failed to recreate information for domain "
199 "%d. Doing nothing except crossing my "
200 "fingers.", d)
201 else:
202 log.exception(
203 "Failed to recreate information for domain "
204 "%d. Destroying it in the hope of "
205 "recovery.", d)
206 try:
207 xc.domain_destroy(dom = d)
208 except:
209 log.exception('Destruction of %d failed.', d)
210 finally:
211 self.refresh_lock.release()
214 def update_domain(self, id):
215 """Update information for a single domain.
217 @param id: domain id
218 """
219 dominfo = self.xen_domain(id)
220 if dominfo:
221 d = self.domains.get(id)
222 if d:
223 d.update(dominfo)
224 else:
225 self._delete_domain(id)
228 ## public:
230 def domain_create(self, config):
231 """Create a domain from a configuration.
233 @param config: configuration
234 @return: domain
235 """
236 dominfo = XendDomainInfo.create(config)
237 self._add_domain(dominfo)
238 return dominfo
240 def domain_configure(self, config):
241 """Configure an existing domain.
243 @param vmconfig: vm configuration
244 """
245 # !!!
246 raise XendError("Unsupported")
248 def domain_restore(self, src):
249 """Restore a domain from file.
251 @param src: source file
252 """
254 try:
255 fd = os.open(src, os.O_RDONLY)
256 dominfo = XendCheckpoint.restore(fd)
257 self._add_domain(dominfo)
258 return dominfo
259 except OSError, ex:
260 raise XendError("can't read guest state file %s: %s" %
261 (src, ex[1]))
263 def domain_get(self, id):
264 """Get up-to-date info about a domain.
266 @param id: domain id
267 @return: domain object (or None)
268 """
269 self.update_domain(id)
270 return self.domains.get(id)
273 def domain_lookup(self, id):
274 self.refresh()
275 return self.domains.get(id)
277 def domain_lookup_by_name(self, name):
278 self.refresh()
279 dominfo = self.domains.get_by_name(name)
280 if not dominfo:
281 try:
282 id = int(name)
283 dominfo = self.domain_lookup(id)
284 except ValueError:
285 pass
286 return dominfo
288 def domain_unpause(self, id):
289 """Unpause domain execution.
291 @param id: domain id
292 """
293 dominfo = self.domain_lookup(id)
294 eserver.inject('xend.domain.unpause', [dominfo.getName(),
295 dominfo.getDomid()])
296 try:
297 return xc.domain_unpause(dom=dominfo.getDomid())
298 except Exception, ex:
299 raise XendError(str(ex))
301 def domain_pause(self, id):
302 """Pause domain execution.
304 @param id: domain id
305 """
306 dominfo = self.domain_lookup(id)
307 eserver.inject('xend.domain.pause', [dominfo.getName(),
308 dominfo.getDomid()])
309 try:
310 return xc.domain_pause(dom=dominfo.getDomid())
311 except Exception, ex:
312 raise XendError(str(ex))
315 def domain_shutdown(self, domid, reason='poweroff'):
316 """Shutdown domain (nicely).
317 - poweroff: restart according to exit code and restart mode
318 - reboot: restart on exit
319 - halt: do not restart
321 Returns immediately.
323 @param id: domain id
324 @param reason: shutdown type: poweroff, reboot, suspend, halt
325 """
326 self.callInfo(domid, XendDomainInfo.shutdown, reason)
329 def domain_sysrq(self, domid, key):
330 """Send a SysRq to the specified domain."""
331 return self.callInfo(domid, XendDomainInfo.send_sysrq, key)
334 def domain_destroy(self, domid, reason='halt'):
335 """Terminate domain immediately.
336 - halt: cancel any restart for the domain
337 - reboot schedule a restart for the domain
339 @param domid: domain id
340 """
342 if domid == PRIV_DOMAIN:
343 raise XendError("Cannot destroy privileged domain %i" % domid)
345 dominfo = self.domain_lookup(domid)
346 if dominfo:
347 val = dominfo.destroy()
348 else:
349 try:
350 val = xc.domain_destroy(dom=domid)
351 except Exception, ex:
352 raise XendError(str(ex))
353 return val
355 def domain_migrate(self, id, dst, live=False, resource=0):
356 """Start domain migration.
358 @param id: domain id
359 """
360 # Need a cancel too?
361 # Don't forget to cancel restart for it.
362 dominfo = self.domain_lookup(id)
364 port = xroot.get_xend_relocation_port()
365 sock = relocate.setupRelocation(dst, port)
367 # temporarily rename domain for localhost migration
368 if dst == "localhost":
369 dominfo.setName("tmp-" + dominfo.getName())
371 try:
372 XendCheckpoint.save(self, sock.fileno(), dominfo, live)
373 except:
374 if dst == "localhost":
375 dominfo.setName(
376 string.replace(dominfo.getName(), "tmp-", "", 1))
377 raise
379 return None
381 def domain_save(self, id, dst):
382 """Start saving a domain to file.
384 @param id: domain id
385 @param dst: destination file
386 """
388 try:
389 dominfo = self.domain_lookup(id)
391 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
393 # For now we don't support 'live checkpoint'
394 return XendCheckpoint.save(self, fd, dominfo, False)
396 except OSError, ex:
397 raise XendError("can't write guest state file %s: %s" %
398 (dst, ex[1]))
400 def domain_pincpu(self, id, vcpu, cpumap):
401 """Set which cpus vcpu can use
403 @param id: domain
404 @param vcpu: vcpu number
405 @param cpumap: bitmap of usbale cpus
406 """
407 dominfo = self.domain_lookup(id)
408 try:
409 return xc.domain_pincpu(dominfo.getDomid(), vcpu, cpumap)
410 except Exception, ex:
411 raise XendError(str(ex))
413 def domain_cpu_bvt_set(self, id, mcuadv, warpback, warpvalue, warpl, warpu):
414 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
415 """
416 dominfo = self.domain_lookup(id)
417 try:
418 return xc.bvtsched_domain_set(dom=dominfo.getDomid(),
419 mcuadv=mcuadv,
420 warpback=warpback,
421 warpvalue=warpvalue,
422 warpl=warpl, warpu=warpu)
423 except Exception, ex:
424 raise XendError(str(ex))
426 def domain_cpu_bvt_get(self, id):
427 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
428 """
429 dominfo = self.domain_lookup(id)
430 try:
431 return xc.bvtsched_domain_get(dominfo.getDomid())
432 except Exception, ex:
433 raise XendError(str(ex))
436 def domain_cpu_sedf_set(self, id, period, slice, latency, extratime, weight):
437 """Set Simple EDF scheduler parameters for a domain.
438 """
439 dominfo = self.domain_lookup(id)
440 try:
441 return xc.sedf_domain_set(dominfo.getDomid(), period, slice,
442 latency, extratime, weight)
443 except Exception, ex:
444 raise XendError(str(ex))
446 def domain_cpu_sedf_get(self, id):
447 """Get Simple EDF scheduler parameters for a domain.
448 """
449 dominfo = self.domain_lookup(id)
450 try:
451 return xc.sedf_domain_get(dominfo.getDomid())
452 except Exception, ex:
453 raise XendError(str(ex))
456 def domain_device_create(self, domid, devconfig):
457 """Create a new device for the specified domain.
458 """
459 return self.callInfo(domid, XendDomainInfo.device_create, devconfig)
462 def domain_device_configure(self, domid, devconfig, devid):
463 """Configure an existing device in the specified domain.
464 @return: updated device configuration
465 """
466 return self.callInfo(domid, XendDomainInfo.device_configure,
467 devconfig, devid)
470 def domain_device_refresh(self, domid, devtype, devid):
471 """Refresh a device."""
472 return self.callInfo(domid, XendDomainInfo.device_refresh, devtype,
473 devid)
476 def domain_device_destroy(self, domid, devtype, devid):
477 """Destroy a device."""
478 return self.callInfo(domid, XendDomainInfo.destroyDevice, devtype,
479 devid)
482 def domain_devtype_ls(self, domid, devtype):
483 """Get list of device sxprs for the specified domain."""
484 return self.callInfo(domid, XendDomainInfo.getDeviceSxprs, devtype)
487 def domain_vif_limit_set(self, id, vif, credit, period):
488 """Limit the vif's transmission rate
489 """
490 dominfo = self.domain_lookup(id)
491 dev = dominfo.getDevice('vif', vif)
492 if not dev:
493 raise XendError("invalid vif")
494 return dev.setCreditLimit(credit, period)
496 def domain_shadow_control(self, id, op):
497 """Shadow page control.
499 @param id: domain
500 @param op: operation
501 """
502 dominfo = self.domain_lookup(id)
503 try:
504 return xc.shadow_control(dominfo.getDomid(), op)
505 except Exception, ex:
506 raise XendError(str(ex))
508 def domain_maxmem_set(self, id, mem):
509 """Set the memory limit for a domain.
511 @param id: domain
512 @param mem: memory limit (in MiB)
513 @return: 0 on success, -1 on error
514 """
515 dominfo = self.domain_lookup(id)
516 maxmem = int(mem) * 1024
517 try:
518 return xc.domain_setmaxmem(dominfo.getDomid(),
519 maxmem_kb = maxmem)
520 except Exception, ex:
521 raise XendError(str(ex))
523 def domain_mem_target_set(self, domid, mem):
524 """Set the memory target for a domain.
526 @param mem: memory target (in MiB)
527 """
528 self.callInfo(domid, XendDomainInfo.setMemoryTarget, mem << 10)
531 def domain_vcpu_hotplug(self, domid, vcpu, state):
532 """Enable or disable specified VCPU in specified domain
534 @param vcpu: target VCPU in domain
535 @param state: which state VCPU will become
536 """
537 self.callInfo(domid, XendDomainInfo.vcpu_hotplug, vcpu, state)
540 def domain_dumpcore(self, domid):
541 """Save a core dump for a crashed domain."""
542 self.callInfo(domid, XendDomainInfo.dumpCore)
545 ## private:
547 def callInfo(self, domid, fn, *args, **kwargs):
548 try:
549 self.refresh()
550 dominfo = self.domains.get(domid)
551 if dominfo:
552 return fn(dominfo, *args, **kwargs)
553 except XendError:
554 raise
555 except Exception, exn:
556 log.exception("")
557 raise XendError(str(exn))
560 def instance():
561 """Singleton constructor. Use this instead of the class constructor.
562 """
563 global inst
564 try:
565 inst
566 except:
567 inst = XendDomain()
568 return inst