ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 7165:bb03b43404bb

The stopgap in changeset 7152:a73ab0ddf990 was broken -- revert the change to
domain_lookup_by_name, as it causes an infinite loop on startup. Leave the
domain_lookup change.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@ewan
date Sun Oct 02 23:56:13 2005 +0100 (2005-10-02)
parents a73ab0ddf990
children cc9988317416
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 dominfo = self.domains.get_by_name(name)
279 if not dominfo:
280 try:
281 id = int(name)
282 dominfo = self.domain_lookup(id)
283 except ValueError:
284 pass
285 return dominfo
287 def domain_unpause(self, id):
288 """Unpause domain execution.
290 @param id: domain id
291 """
292 dominfo = self.domain_lookup(id)
293 eserver.inject('xend.domain.unpause', [dominfo.getName(),
294 dominfo.getDomid()])
295 try:
296 return xc.domain_unpause(dom=dominfo.getDomid())
297 except Exception, ex:
298 raise XendError(str(ex))
300 def domain_pause(self, id):
301 """Pause domain execution.
303 @param id: domain id
304 """
305 dominfo = self.domain_lookup(id)
306 eserver.inject('xend.domain.pause', [dominfo.getName(),
307 dominfo.getDomid()])
308 try:
309 return xc.domain_pause(dom=dominfo.getDomid())
310 except Exception, ex:
311 raise XendError(str(ex))
314 def domain_shutdown(self, domid, reason='poweroff'):
315 """Shutdown domain (nicely).
316 - poweroff: restart according to exit code and restart mode
317 - reboot: restart on exit
318 - halt: do not restart
320 Returns immediately.
322 @param id: domain id
323 @param reason: shutdown type: poweroff, reboot, suspend, halt
324 """
325 self.callInfo(domid, XendDomainInfo.shutdown, reason)
328 def domain_sysrq(self, domid, key):
329 """Send a SysRq to the specified domain."""
330 return self.callInfo(domid, XendDomainInfo.send_sysrq, key)
333 def domain_destroy(self, domid, reason='halt'):
334 """Terminate domain immediately.
335 - halt: cancel any restart for the domain
336 - reboot schedule a restart for the domain
338 @param domid: domain id
339 """
341 if domid == PRIV_DOMAIN:
342 raise XendError("Cannot destroy privileged domain %i" % domid)
344 dominfo = self.domain_lookup(domid)
345 if dominfo:
346 val = dominfo.destroy()
347 else:
348 try:
349 val = xc.domain_destroy(dom=domid)
350 except Exception, ex:
351 raise XendError(str(ex))
352 return val
354 def domain_migrate(self, id, dst, live=False, resource=0):
355 """Start domain migration.
357 @param id: domain id
358 """
359 # Need a cancel too?
360 # Don't forget to cancel restart for it.
361 dominfo = self.domain_lookup(id)
363 port = xroot.get_xend_relocation_port()
364 sock = relocate.setupRelocation(dst, port)
366 # temporarily rename domain for localhost migration
367 if dst == "localhost":
368 dominfo.setName("tmp-" + dominfo.getName())
370 try:
371 XendCheckpoint.save(self, sock.fileno(), dominfo, live)
372 except:
373 if dst == "localhost":
374 dominfo.setName(
375 string.replace(dominfo.getName(), "tmp-", "", 1))
376 raise
378 return None
380 def domain_save(self, id, dst):
381 """Start saving a domain to file.
383 @param id: domain id
384 @param dst: destination file
385 """
387 try:
388 dominfo = self.domain_lookup(id)
390 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
392 # For now we don't support 'live checkpoint'
393 return XendCheckpoint.save(self, fd, dominfo, False)
395 except OSError, ex:
396 raise XendError("can't write guest state file %s: %s" %
397 (dst, ex[1]))
399 def domain_pincpu(self, id, vcpu, cpumap):
400 """Set which cpus vcpu can use
402 @param id: domain
403 @param vcpu: vcpu number
404 @param cpumap: bitmap of usbale cpus
405 """
406 dominfo = self.domain_lookup(id)
407 try:
408 return xc.domain_pincpu(dominfo.getDomid(), vcpu, cpumap)
409 except Exception, ex:
410 raise XendError(str(ex))
412 def domain_cpu_bvt_set(self, id, mcuadv, warpback, warpvalue, warpl, warpu):
413 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
414 """
415 dominfo = self.domain_lookup(id)
416 try:
417 return xc.bvtsched_domain_set(dom=dominfo.getDomid(),
418 mcuadv=mcuadv,
419 warpback=warpback,
420 warpvalue=warpvalue,
421 warpl=warpl, warpu=warpu)
422 except Exception, ex:
423 raise XendError(str(ex))
425 def domain_cpu_bvt_get(self, id):
426 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
427 """
428 dominfo = self.domain_lookup(id)
429 try:
430 return xc.bvtsched_domain_get(dominfo.getDomid())
431 except Exception, ex:
432 raise XendError(str(ex))
435 def domain_cpu_sedf_set(self, id, period, slice, latency, extratime, weight):
436 """Set Simple EDF scheduler parameters for a domain.
437 """
438 dominfo = self.domain_lookup(id)
439 try:
440 return xc.sedf_domain_set(dominfo.getDomid(), period, slice,
441 latency, extratime, weight)
442 except Exception, ex:
443 raise XendError(str(ex))
445 def domain_cpu_sedf_get(self, id):
446 """Get Simple EDF scheduler parameters for a domain.
447 """
448 dominfo = self.domain_lookup(id)
449 try:
450 return xc.sedf_domain_get(dominfo.getDomid())
451 except Exception, ex:
452 raise XendError(str(ex))
455 def domain_device_create(self, domid, devconfig):
456 """Create a new device for the specified domain.
457 """
458 return self.callInfo(domid, XendDomainInfo.device_create, devconfig)
461 def domain_device_configure(self, domid, devconfig, devid):
462 """Configure an existing device in the specified domain.
463 @return: updated device configuration
464 """
465 return self.callInfo(domid, XendDomainInfo.device_configure,
466 devconfig, devid)
469 def domain_device_refresh(self, domid, devtype, devid):
470 """Refresh a device."""
471 return self.callInfo(domid, XendDomainInfo.device_refresh, devtype,
472 devid)
475 def domain_device_destroy(self, domid, devtype, devid):
476 """Destroy a device."""
477 return self.callInfo(domid, XendDomainInfo.destroyDevice, devtype,
478 devid)
481 def domain_devtype_ls(self, domid, devtype):
482 """Get list of device sxprs for the specified domain."""
483 return self.callInfo(domid, XendDomainInfo.getDeviceSxprs, devtype)
486 def domain_vif_limit_set(self, id, vif, credit, period):
487 """Limit the vif's transmission rate
488 """
489 dominfo = self.domain_lookup(id)
490 dev = dominfo.getDevice('vif', vif)
491 if not dev:
492 raise XendError("invalid vif")
493 return dev.setCreditLimit(credit, period)
495 def domain_shadow_control(self, id, op):
496 """Shadow page control.
498 @param id: domain
499 @param op: operation
500 """
501 dominfo = self.domain_lookup(id)
502 try:
503 return xc.shadow_control(dominfo.getDomid(), op)
504 except Exception, ex:
505 raise XendError(str(ex))
507 def domain_maxmem_set(self, id, mem):
508 """Set the memory limit for a domain.
510 @param id: domain
511 @param mem: memory limit (in MiB)
512 @return: 0 on success, -1 on error
513 """
514 dominfo = self.domain_lookup(id)
515 maxmem = int(mem) * 1024
516 try:
517 return xc.domain_setmaxmem(dominfo.getDomid(),
518 maxmem_kb = maxmem)
519 except Exception, ex:
520 raise XendError(str(ex))
522 def domain_mem_target_set(self, domid, mem):
523 """Set the memory target for a domain.
525 @param mem: memory target (in MiB)
526 """
527 self.callInfo(domid, XendDomainInfo.setMemoryTarget, mem << 10)
530 def domain_vcpu_hotplug(self, domid, vcpu, state):
531 """Enable or disable specified VCPU in specified domain
533 @param vcpu: target VCPU in domain
534 @param state: which state VCPU will become
535 """
536 self.callInfo(domid, XendDomainInfo.vcpu_hotplug, vcpu, state)
539 def domain_dumpcore(self, domid):
540 """Save a core dump for a crashed domain."""
541 self.callInfo(domid, XendDomainInfo.dumpCore)
544 ## private:
546 def callInfo(self, domid, fn, *args, **kwargs):
547 try:
548 self.refresh()
549 dominfo = self.domains.get(domid)
550 if dominfo:
551 return fn(dominfo, *args, **kwargs)
552 except XendError:
553 raise
554 except Exception, exn:
555 log.exception("")
556 raise XendError(str(exn))
559 def instance():
560 """Singleton constructor. Use this instead of the class constructor.
561 """
562 global inst
563 try:
564 inst
565 except:
566 inst = XendDomain()
567 return inst