ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 10711:9ddc5be227c1

[XEND] Improve xm pause/unpause error reporting.
Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author kfraser@localhost.localdomain
date Mon Jul 10 15:18:12 2006 +0100 (2006-07-10)
parents 800261a88275
children c45f1f3a926b
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 """
25 import logging
26 import os
27 import socket
28 import sys
29 import threading
31 import xen.lowlevel.xc
33 import XendDomainInfo
35 from xen.xend import XendRoot
36 from xen.xend import XendCheckpoint
37 from xen.xend.XendError import XendError, XendInvalidDomain
38 from xen.xend.XendLogging import log
39 from xen.xend.xenstore.xstransact import xstransact
40 from xen.xend.xenstore.xswatch import xswatch
41 from xen.util import security
44 xc = xen.lowlevel.xc.xc()
45 xroot = XendRoot.instance()
48 __all__ = [ "XendDomain" ]
50 PRIV_DOMAIN = 0
51 VMROOT = '/vm/'
54 class XendDomain:
55 """Index of all domains. Singleton.
56 """
58 ## public:
60 def __init__(self):
61 self.domains = {}
62 self.domains_lock = threading.RLock()
65 # This must be called only the once, by instance() below. It is separate
66 # from the constructor because XendDomainInfo calls back into this class
67 # in order to check the uniqueness of domain names. This means that
68 # instance() must be able to return a valid instance of this class even
69 # during this initialisation.
70 def init(self):
71 xstransact.Mkdir(VMROOT)
72 xstransact.SetPermissions(VMROOT, { 'dom' : PRIV_DOMAIN })
74 self.domains_lock.acquire()
75 try:
76 self._add_domain(
77 XendDomainInfo.recreate(self.xen_domains()[PRIV_DOMAIN],
78 True))
79 self.dom0_setup()
81 # This watch registration needs to be before the refresh call, so
82 # that we're sure that we haven't missed any releases, but inside
83 # the domains_lock, as we don't want the watch to fire until after
84 # the refresh call has completed.
85 xswatch("@introduceDomain", self.onChangeDomain)
86 xswatch("@releaseDomain", self.onChangeDomain)
88 self.refresh(True)
89 finally:
90 self.domains_lock.release()
93 def list(self):
94 """Get list of domain objects.
96 @return: domain objects
97 """
98 self.domains_lock.acquire()
99 try:
100 self.refresh()
101 return self.domains.values()
102 finally:
103 self.domains_lock.release()
106 def list_sorted(self):
107 """Get list of domain objects, sorted by name.
109 @return: domain objects
110 """
111 doms = self.list()
112 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
113 return doms
115 def list_names(self):
116 """Get list of domain names.
118 @return: domain names
119 """
120 doms = self.list_sorted()
121 return map(lambda x: x.getName(), doms)
124 ## private:
126 def onChangeDomain(self, _):
127 self.domains_lock.acquire()
128 try:
129 self.refresh()
130 finally:
131 self.domains_lock.release()
132 return 1
135 def xen_domains(self):
136 """Get table of domains indexed by id from xc. Expects to be
137 protected by the domains_lock.
138 """
139 domlist = xc.domain_getinfo()
140 doms = {}
141 for d in domlist:
142 domid = d['dom']
143 doms[domid] = d
144 return doms
147 def dom0_setup(self):
148 """Expects to be protected by the domains_lock."""
149 dom0 = self.domains[PRIV_DOMAIN]
151 # get max number of vcpus to use for dom0 from config
152 target = int(xroot.get_dom0_vcpus())
153 log.debug("number of vcpus to use is %d", target)
155 # target == 0 means use all processors
156 if target > 0:
157 dom0.setVCpuCount(target)
160 def _add_domain(self, info):
161 """Add the given domain entry to this instance's internal cache.
162 Expects to be protected by the domains_lock.
163 """
164 self.domains[info.getDomid()] = info
167 def _delete_domain(self, domid):
168 """Remove the given domain from this instance's internal cache.
169 Expects to be protected by the domains_lock.
170 """
171 info = self.domains.get(domid)
172 if info:
173 del self.domains[domid]
174 info.cleanupDomain()
177 def refresh(self, initialising = False):
178 """Refresh domain list from Xen. Expects to be protected by the
179 domains_lock.
181 @param initialising True if this is the first refresh after starting
182 Xend. This does not change this method's behaviour, except for
183 logging.
184 """
185 doms = self.xen_domains()
186 for d in self.domains.values():
187 info = doms.get(d.getDomid())
188 if info:
189 d.update(info)
190 else:
191 self._delete_domain(d.getDomid())
192 for d in doms:
193 if d not in self.domains:
194 if doms[d]['dying']:
195 log.log(initialising and logging.ERROR or logging.DEBUG,
196 'Cannot recreate information for dying domain %d.'
197 ' Xend will ignore this domain from now on.',
198 doms[d]['dom'])
199 elif d == PRIV_DOMAIN:
200 log.fatal(
201 "No record of privileged domain %d! Terminating.", d)
202 sys.exit(1)
203 else:
204 try:
205 self._add_domain(
206 XendDomainInfo.recreate(doms[d], False))
207 except:
208 log.exception(
209 "Failed to recreate information for domain "
210 "%d. Destroying it in the hope of "
211 "recovery.", d)
212 try:
213 xc.domain_destroy(d)
214 except:
215 log.exception('Destruction of %d failed.', d)
218 ## public:
220 def domain_create(self, config):
221 """Create a domain from a configuration.
223 @param config: configuration
224 @return: domain
225 """
226 self.domains_lock.acquire()
227 try:
228 dominfo = XendDomainInfo.create(config)
229 self._add_domain(dominfo)
230 return dominfo
231 finally:
232 self.domains_lock.release()
235 def domain_configure(self, config):
236 """Configure an existing domain.
238 @param vmconfig: vm configuration
239 """
240 # !!!
241 raise XendError("Unsupported")
243 def domain_restore(self, src):
244 """Restore a domain from file.
246 @param src: source file
247 """
249 try:
250 fd = os.open(src, os.O_RDONLY)
251 try:
252 return self.domain_restore_fd(fd)
253 finally:
254 os.close(fd)
255 except OSError, ex:
256 raise XendError("can't read guest state file %s: %s" %
257 (src, ex[1]))
259 def domain_restore_fd(self, fd):
260 """Restore a domain from the given file descriptor."""
262 try:
263 return XendCheckpoint.restore(self, fd)
264 except:
265 # I don't really want to log this exception here, but the error
266 # handling in the relocation-socket handling code (relocate.py) is
267 # poor, so we need to log this for debugging.
268 log.exception("Restore failed")
269 raise XendError("Restore failed")
272 def restore_(self, config):
273 """Create a domain as part of the restore process. This is called
274 only from {@link XendCheckpoint}.
276 A restore request comes into XendDomain through {@link
277 #domain_restore} or {@link #domain_restore_fd}. That request is
278 forwarded immediately to XendCheckpoint which, when it is ready, will
279 call this method. It is necessary to come through here rather than go
280 directly to {@link XendDomainInfo.restore} because we need to
281 serialise the domain creation process, but cannot lock
282 domain_restore_fd as a whole, otherwise we will deadlock waiting for
283 the old domain to die.
284 """
285 self.domains_lock.acquire()
286 try:
287 security.refresh_ssidref(config)
288 dominfo = XendDomainInfo.restore(config)
289 self._add_domain(dominfo)
290 return dominfo
291 finally:
292 self.domains_lock.release()
295 def domain_lookup(self, domid):
296 self.domains_lock.acquire()
297 try:
298 self.refresh()
299 return self.domains.get(domid)
300 finally:
301 self.domains_lock.release()
304 def domain_lookup_nr(self, domid):
305 self.domains_lock.acquire()
306 try:
307 return self.domains.get(domid)
308 finally:
309 self.domains_lock.release()
312 def domain_lookup_by_name_or_id(self, name):
313 self.domains_lock.acquire()
314 try:
315 self.refresh()
316 return self.domain_lookup_by_name_or_id_nr(name)
317 finally:
318 self.domains_lock.release()
321 def domain_lookup_by_name_or_id_nr(self, name):
322 self.domains_lock.acquire()
323 try:
324 dominfo = self.domain_lookup_by_name_nr(name)
326 if dominfo:
327 return dominfo
328 else:
329 try:
330 return self.domains.get(int(name))
331 except ValueError:
332 return None
333 finally:
334 self.domains_lock.release()
337 def domain_lookup_by_name_nr(self, name):
338 self.domains_lock.acquire()
339 try:
340 matching = filter(lambda d: d.getName() == name,
341 self.domains.values())
342 n = len(matching)
343 if n == 1:
344 return matching[0]
345 return None
346 finally:
347 self.domains_lock.release()
350 def domain_lookup_by_uuid_nr(self, uuid):
351 self.domains_lock.acquire()
352 try:
353 matching = filter(lambda d: d.getUuid() == uuid,
354 self.domains.values())
355 n = len(matching)
356 if n == 1:
357 return matching[0]
358 return None
359 finally:
360 self.domains_lock.release()
363 def privilegedDomain(self):
364 self.domains_lock.acquire()
365 try:
366 return self.domains[PRIV_DOMAIN]
367 finally:
368 self.domains_lock.release()
371 def domain_unpause(self, domid):
372 """Unpause domain execution."""
374 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
375 if not dominfo:
376 raise XendInvalidDomain(str(domid))
378 if dominfo.getDomid() == PRIV_DOMAIN:
379 raise XendError("Cannot unpause privileged domain %s" % domid)
381 try:
382 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
383 dominfo.getDomid())
384 return dominfo.unpause()
385 except Exception, ex:
386 raise XendError(str(ex))
389 def domain_pause(self, domid):
390 """Pause domain execution."""
392 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
393 if not dominfo:
394 raise XendInvalidDomain(str(domid))
396 if dominfo.getDomid() == PRIV_DOMAIN:
397 raise XendError("Cannot pause privileged domain %s" % domid)
399 try:
400 log.info("Domain %s (%d) paused.", dominfo.getName(),
401 dominfo.getDomid())
402 return dominfo.pause()
403 except Exception, ex:
404 raise XendError(str(ex))
407 def domain_destroy(self, domid):
408 """Terminate domain immediately."""
410 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
411 if dominfo and dominfo.getDomid() == PRIV_DOMAIN:
412 raise XendError("Cannot destroy privileged domain %s" % domid)
414 if dominfo:
415 val = dominfo.destroy()
416 else:
417 try:
418 val = xc.domain_destroy(domid)
419 except Exception, ex:
420 raise XendError(str(ex))
421 return val
423 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
424 """Start domain migration."""
426 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
427 if not dominfo:
428 raise XendInvalidDomain(str(domid))
430 if dominfo.getDomid() == PRIV_DOMAIN:
431 raise XendError("Cannot migrate privileged domain %i" % domid)
433 """ The following call may raise a XendError exception """
434 dominfo.testMigrateDevices(True, dst)
436 if port == 0:
437 port = xroot.get_xend_relocation_port()
438 try:
439 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
440 sock.connect((dst, port))
441 except socket.error, err:
442 raise XendError("can't connect: %s" % err[1])
444 sock.send("receive\n")
445 sock.recv(80)
446 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
449 def domain_save(self, domid, dst):
450 """Start saving a domain to file.
452 @param dst: destination file
453 """
455 try:
456 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
457 if not dominfo:
458 raise XendInvalidDomain(str(domid))
460 if dominfo.getDomid() == PRIV_DOMAIN:
461 raise XendError("Cannot save privileged domain %i" % domid)
463 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
464 try:
465 # For now we don't support 'live checkpoint'
466 return XendCheckpoint.save(fd, dominfo, False, False, dst)
467 finally:
468 os.close(fd)
469 except OSError, ex:
470 raise XendError("can't write guest state file %s: %s" %
471 (dst, ex[1]))
473 def domain_pincpu(self, domid, vcpu, cpumap):
474 """Set which cpus vcpu can use
476 @param cpumap: string repr of list of usable cpus
477 """
478 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
479 if not dominfo:
480 raise XendInvalidDomain(str(domid))
482 try:
483 return xc.vcpu_setaffinity(dominfo.getDomid(), vcpu, cpumap)
484 except Exception, ex:
485 raise XendError(str(ex))
487 def domain_cpu_bvt_set(self, domid, mcuadv, warpback, warpvalue, warpl,
488 warpu):
489 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
490 """
491 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
492 if not dominfo:
493 raise XendInvalidDomain(str(domid))
494 try:
495 return xc.bvtsched_domain_set(dom=dominfo.getDomid(),
496 mcuadv=mcuadv,
497 warpback=warpback,
498 warpvalue=warpvalue,
499 warpl=warpl, warpu=warpu)
500 except Exception, ex:
501 raise XendError(str(ex))
503 def domain_cpu_bvt_get(self, domid):
504 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
505 """
506 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
507 if not dominfo:
508 raise XendInvalidDomain(str(domid))
509 try:
510 return xc.bvtsched_domain_get(dominfo.getDomid())
511 except Exception, ex:
512 raise XendError(str(ex))
515 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
516 weight):
517 """Set Simple EDF scheduler parameters for a domain.
518 """
519 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
520 if not dominfo:
521 raise XendInvalidDomain(str(domid))
522 try:
523 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
524 latency, extratime, weight)
525 except Exception, ex:
526 raise XendError(str(ex))
528 def domain_cpu_sedf_get(self, domid):
529 """Get Simple EDF scheduler parameters for a domain.
530 """
531 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
532 if not dominfo:
533 raise XendInvalidDomain(str(domid))
534 try:
535 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
536 # return sxpr
537 return ['sedf',
538 ['domain', sedf_info['domain']],
539 ['period', sedf_info['period']],
540 ['slice', sedf_info['slice']],
541 ['latency', sedf_info['latency']],
542 ['extratime', sedf_info['extratime']],
543 ['weight', sedf_info['weight']]]
545 except Exception, ex:
546 raise XendError(str(ex))
548 def domain_sched_credit_get(self, domid):
549 """Get credit scheduler parameters for a domain.
550 """
551 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
552 if not dominfo:
553 raise XendInvalidDomain(str(domid))
554 try:
555 return xc.sched_credit_domain_get(dominfo.getDomid())
556 except Exception, ex:
557 raise XendError(str(ex))
559 def domain_sched_credit_set(self, domid, weight, cap):
560 """Set credit scheduler parameters for a domain.
561 """
562 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
563 if not dominfo:
564 raise XendInvalidDomain(str(domid))
565 try:
566 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
567 except Exception, ex:
568 raise XendError(str(ex))
570 def domain_maxmem_set(self, domid, mem):
571 """Set the memory limit for a domain.
573 @param mem: memory limit (in MiB)
574 @return: 0 on success, -1 on error
575 """
576 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
577 if not dominfo:
578 raise XendInvalidDomain(str(domid))
579 maxmem = int(mem) * 1024
580 try:
581 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
582 except Exception, ex:
583 raise XendError(str(ex))
585 def domain_ioport_range_enable(self, domid, first, last):
586 """Enable access to a range of IO ports for a domain
588 @param first: first IO port
589 @param last: last IO port
590 @return: 0 on success, -1 on error
591 """
592 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
593 if not dominfo:
594 raise XendInvalidDomain(str(domid))
595 nr_ports = last - first + 1
596 try:
597 return xc.domain_ioport_permission(dominfo.getDomid(),
598 first_port = first,
599 nr_ports = nr_ports,
600 allow_access = 1)
601 except Exception, ex:
602 raise XendError(str(ex))
604 def domain_ioport_range_disable(self, domid, first, last):
605 """Disable access to a range of IO ports for a domain
607 @param first: first IO port
608 @param last: last IO port
609 @return: 0 on success, -1 on error
610 """
611 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
612 if not dominfo:
613 raise XendInvalidDomain(str(domid))
614 nr_ports = last - first + 1
615 try:
616 return xc.domain_ioport_permission(dominfo.getDomid(),
617 first_port = first,
618 nr_ports = nr_ports,
619 allow_access = 0)
620 except Exception, ex:
621 raise XendError(str(ex))
624 def instance():
625 """Singleton constructor. Use this instead of the class constructor.
626 """
627 global inst
628 try:
629 inst
630 except:
631 inst = XendDomain()
632 inst.init()
633 return inst