ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 9466:af1ff22a5633

Watch for @introduceDomain, for symmetry with the existing watch on
@releaseDomain.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Tue Mar 28 11:26:29 2006 +0100 (2006-03-28)
parents 5c9c44fc1c39
children 7a3f07a33487
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
38 from xen.xend.XendLogging import log
39 from xen.xend.xenstore.xstransact import xstransact
40 from xen.xend.xenstore.xswatch import xswatch
43 xc = xen.lowlevel.xc.xc()
44 xroot = XendRoot.instance()
47 __all__ = [ "XendDomain" ]
49 PRIV_DOMAIN = 0
50 VMROOT = '/vm/'
53 class XendDomain:
54 """Index of all domains. Singleton.
55 """
57 ## public:
59 def __init__(self):
60 self.domains = {}
61 self.domains_lock = threading.RLock()
64 # This must be called only the once, by instance() below. It is separate
65 # from the constructor because XendDomainInfo calls back into this class
66 # in order to check the uniqueness of domain names. This means that
67 # instance() must be able to return a valid instance of this class even
68 # during this initialisation.
69 def init(self):
70 xstransact.Mkdir(VMROOT)
71 xstransact.SetPermissions(VMROOT, { 'dom' : PRIV_DOMAIN })
73 self.domains_lock.acquire()
74 try:
75 self._add_domain(
76 XendDomainInfo.recreate(self.xen_domains()[PRIV_DOMAIN],
77 True))
78 self.dom0_setup()
80 # This watch registration needs to be before the refresh call, so
81 # that we're sure that we haven't missed any releases, but inside
82 # the domains_lock, as we don't want the watch to fire until after
83 # the refresh call has completed.
84 xswatch("@introduceDomain", self.onChangeDomain)
85 xswatch("@releaseDomain", self.onChangeDomain)
87 self.refresh(True)
88 finally:
89 self.domains_lock.release()
92 def list(self):
93 """Get list of domain objects.
95 @return: domain objects
96 """
97 self.domains_lock.acquire()
98 try:
99 self.refresh()
100 return self.domains.values()
101 finally:
102 self.domains_lock.release()
105 def list_sorted(self):
106 """Get list of domain objects, sorted by name.
108 @return: domain objects
109 """
110 doms = self.list()
111 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
112 return doms
114 def list_names(self):
115 """Get list of domain names.
117 @return: domain names
118 """
119 doms = self.list_sorted()
120 return map(lambda x: x.getName(), doms)
123 ## private:
125 def onChangeDomain(self, _):
126 self.domains_lock.acquire()
127 try:
128 self.refresh()
129 finally:
130 self.domains_lock.release()
131 return 1
134 def xen_domains(self):
135 """Get table of domains indexed by id from xc. Expects to be
136 protected by the domains_lock.
137 """
138 domlist = xc.domain_getinfo()
139 doms = {}
140 for d in domlist:
141 domid = d['dom']
142 doms[domid] = d
143 return doms
146 def dom0_setup(self):
147 """Expects to be protected by the domains_lock."""
148 dom0 = self.domains[PRIV_DOMAIN]
150 # get max number of vcpus to use for dom0 from config
151 target = int(xroot.get_dom0_vcpus())
152 log.debug("number of vcpus to use is %d", target)
154 # target == 0 means use all processors
155 if target > 0:
156 dom0.setVCpuCount(target)
159 def _add_domain(self, info):
160 """Add the given domain entry to this instance's internal cache.
161 Expects to be protected by the domains_lock.
162 """
163 self.domains[info.getDomid()] = info
166 def _delete_domain(self, domid):
167 """Remove the given domain from this instance's internal cache.
168 Expects to be protected by the domains_lock.
169 """
170 info = self.domains.get(domid)
171 if info:
172 del self.domains[domid]
173 info.cleanupDomain()
176 def refresh(self, initialising = False):
177 """Refresh domain list from Xen. Expects to be protected by the
178 domains_lock.
180 @param initialising True if this is the first refresh after starting
181 Xend. This does not change this method's behaviour, except for
182 logging.
183 """
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:
193 if doms[d]['dying']:
194 log.log(initialising and logging.ERROR or logging.DEBUG,
195 'Cannot recreate information for dying domain %d.'
196 ' Xend will ignore this domain from now on.',
197 doms[d]['dom'])
198 elif d == PRIV_DOMAIN:
199 log.fatal(
200 "No record of privileged domain %d! Terminating.", d)
201 sys.exit(1)
202 else:
203 try:
204 self._add_domain(
205 XendDomainInfo.recreate(doms[d], False))
206 except:
207 log.exception(
208 "Failed to recreate information for domain "
209 "%d. Destroying it in the hope of "
210 "recovery.", d)
211 try:
212 xc.domain_destroy(d)
213 except:
214 log.exception('Destruction of %d failed.', d)
217 ## public:
219 def domain_create(self, config):
220 """Create a domain from a configuration.
222 @param config: configuration
223 @return: domain
224 """
225 self.domains_lock.acquire()
226 try:
227 dominfo = XendDomainInfo.create(config)
228 self._add_domain(dominfo)
229 return dominfo
230 finally:
231 self.domains_lock.release()
234 def domain_configure(self, config):
235 """Configure an existing domain.
237 @param vmconfig: vm configuration
238 """
239 # !!!
240 raise XendError("Unsupported")
242 def domain_restore(self, src):
243 """Restore a domain from file.
245 @param src: source file
246 """
248 try:
249 fd = os.open(src, os.O_RDONLY)
250 try:
251 return self.domain_restore_fd(fd)
252 finally:
253 os.close(fd)
254 except OSError, ex:
255 raise XendError("can't read guest state file %s: %s" %
256 (src, ex[1]))
258 def domain_restore_fd(self, fd):
259 """Restore a domain from the given file descriptor."""
261 try:
262 return XendCheckpoint.restore(self, fd)
263 except:
264 # I don't really want to log this exception here, but the error
265 # handling in the relocation-socket handling code (relocate.py) is
266 # poor, so we need to log this for debugging.
267 log.exception("Restore failed")
268 raise
271 def restore_(self, config):
272 """Create a domain as part of the restore process. This is called
273 only from {@link XendCheckpoint}.
275 A restore request comes into XendDomain through {@link
276 #domain_restore} or {@link #domain_restore_fd}. That request is
277 forwarded immediately to XendCheckpoint which, when it is ready, will
278 call this method. It is necessary to come through here rather than go
279 directly to {@link XendDomainInfo.restore} because we need to
280 serialise the domain creation process, but cannot lock
281 domain_restore_fd as a whole, otherwise we will deadlock waiting for
282 the old domain to die.
283 """
284 self.domains_lock.acquire()
285 try:
286 dominfo = XendDomainInfo.restore(config)
287 self._add_domain(dominfo)
288 return dominfo
289 finally:
290 self.domains_lock.release()
293 def domain_lookup(self, domid):
294 self.domains_lock.acquire()
295 try:
296 self.refresh()
297 return self.domains.get(domid)
298 finally:
299 self.domains_lock.release()
302 def domain_lookup_nr(self, domid):
303 self.domains_lock.acquire()
304 try:
305 return self.domains.get(domid)
306 finally:
307 self.domains_lock.release()
310 def domain_lookup_by_name_or_id(self, name):
311 self.domains_lock.acquire()
312 try:
313 self.refresh()
314 return self.domain_lookup_by_name_or_id_nr(name)
315 finally:
316 self.domains_lock.release()
319 def domain_lookup_by_name_or_id_nr(self, name):
320 self.domains_lock.acquire()
321 try:
322 dominfo = self.domain_lookup_by_name_nr(name)
324 if dominfo:
325 return dominfo
326 else:
327 try:
328 return self.domains.get(int(name))
329 except ValueError:
330 return None
331 finally:
332 self.domains_lock.release()
335 def domain_lookup_by_name_nr(self, name):
336 self.domains_lock.acquire()
337 try:
338 matching = filter(lambda d: d.getName() == name,
339 self.domains.values())
340 n = len(matching)
341 if n == 1:
342 return matching[0]
343 return None
344 finally:
345 self.domains_lock.release()
348 def privilegedDomain(self):
349 self.domains_lock.acquire()
350 try:
351 return self.domains[PRIV_DOMAIN]
352 finally:
353 self.domains_lock.release()
356 def domain_unpause(self, domid):
357 """Unpause domain execution."""
358 try:
359 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
360 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
361 dominfo.getDomid())
362 return dominfo.unpause()
363 except Exception, ex:
364 raise XendError(str(ex))
367 def domain_pause(self, domid):
368 """Pause domain execution."""
369 try:
370 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
371 log.info("Domain %s (%d) paused.", dominfo.getName(),
372 dominfo.getDomid())
373 return dominfo.pause()
374 except Exception, ex:
375 raise XendError(str(ex))
378 def domain_destroy(self, domid):
379 """Terminate domain immediately."""
381 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
382 if dominfo and dominfo.getDomid() == PRIV_DOMAIN:
383 raise XendError("Cannot destroy privileged domain %s" % domid)
385 if dominfo:
386 val = dominfo.destroy()
387 else:
388 try:
389 val = xc.domain_destroy(domid)
390 except Exception, ex:
391 raise XendError(str(ex))
392 return val
394 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
395 """Start domain migration."""
397 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
399 if dominfo.getDomid() == PRIV_DOMAIN:
400 raise XendError("Cannot migrate privileged domain %i" % domid)
402 if port == 0:
403 port = xroot.get_xend_relocation_port()
404 try:
405 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
406 sock.connect((dst, port))
407 except socket.error, err:
408 raise XendError("can't connect: %s" % err[1])
410 sock.send("receive\n")
411 sock.recv(80)
412 XendCheckpoint.save(sock.fileno(), dominfo, live)
415 def domain_save(self, domid, dst):
416 """Start saving a domain to file.
418 @param dst: destination file
419 """
421 try:
422 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
424 if dominfo.getDomid() == PRIV_DOMAIN:
425 raise XendError("Cannot save privileged domain %i" % domid)
427 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
428 try:
429 # For now we don't support 'live checkpoint'
430 return XendCheckpoint.save(fd, dominfo, False)
431 finally:
432 os.close(fd)
433 except OSError, ex:
434 raise XendError("can't write guest state file %s: %s" %
435 (dst, ex[1]))
437 def domain_pincpu(self, domid, vcpu, cpumap):
438 """Set which cpus vcpu can use
440 @param cpumap: string repr of list of usable cpus
441 """
442 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
443 # convert cpumap string into a list of ints
444 cpumap = map(lambda x: int(x),
445 cpumap.replace("[", "").replace("]", "").split(","))
446 try:
447 return xc.vcpu_setaffinity(dominfo.getDomid(), vcpu, cpumap)
448 except Exception, ex:
449 raise XendError(str(ex))
451 def domain_cpu_bvt_set(self, domid, mcuadv, warpback, warpvalue, warpl,
452 warpu):
453 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
454 """
455 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
456 try:
457 return xc.bvtsched_domain_set(dom=dominfo.getDomid(),
458 mcuadv=mcuadv,
459 warpback=warpback,
460 warpvalue=warpvalue,
461 warpl=warpl, warpu=warpu)
462 except Exception, ex:
463 raise XendError(str(ex))
465 def domain_cpu_bvt_get(self, domid):
466 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
467 """
468 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
469 try:
470 return xc.bvtsched_domain_get(dominfo.getDomid())
471 except Exception, ex:
472 raise XendError(str(ex))
475 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
476 weight):
477 """Set Simple EDF scheduler parameters for a domain.
478 """
479 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
480 try:
481 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
482 latency, extratime, weight)
483 except Exception, ex:
484 raise XendError(str(ex))
486 def domain_cpu_sedf_get(self, domid):
487 """Get Simple EDF scheduler parameters for a domain.
488 """
489 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
490 try:
492 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
493 # return sxpr
494 return ['sedf',
495 ['domain', sedf_info['domain']],
496 ['period', sedf_info['period']],
497 ['slice', sedf_info['slice']],
498 ['latency', sedf_info['latency']],
499 ['extratime', sedf_info['extratime']],
500 ['weight', sedf_info['weight']]]
502 except Exception, ex:
503 raise XendError(str(ex))
505 def domain_maxmem_set(self, domid, mem):
506 """Set the memory limit for a domain.
508 @param mem: memory limit (in MiB)
509 @return: 0 on success, -1 on error
510 """
511 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
512 maxmem = int(mem) * 1024
513 try:
514 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
515 except Exception, ex:
516 raise XendError(str(ex))
518 def domain_ioport_range_enable(self, domid, first, last):
519 """Enable access to a range of IO ports for a domain
521 @param first: first IO port
522 @param last: last IO port
523 @return: 0 on success, -1 on error
524 """
525 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
526 nr_ports = last - first + 1
527 try:
528 return xc.domain_ioport_permission(dominfo.getDomid(),
529 first_port = first,
530 nr_ports = nr_ports,
531 allow_access = 1)
532 except Exception, ex:
533 raise XendError(str(ex))
535 def domain_ioport_range_disable(self, domid, first, last):
536 """Disable access to a range of IO ports for a domain
538 @param first: first IO port
539 @param last: last IO port
540 @return: 0 on success, -1 on error
541 """
542 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
543 nr_ports = last - first + 1
544 try:
545 return xc.domain_ioport_permission(dominfo.getDomid(),
546 first_port = first,
547 nr_ports = nr_ports,
548 allow_access = 0)
549 except Exception, ex:
550 raise XendError(str(ex))
553 def instance():
554 """Singleton constructor. Use this instead of the class constructor.
555 """
556 global inst
557 try:
558 inst
559 except:
560 inst = XendDomain()
561 inst.init()
562 return inst