direct-io.hg

view tools/python/xen/xend/XendDomain.py @ 8500:dd5649730b32

Fix a couple of bogus dom0_op names:
setdomaininfo -> setvcpucontext
pincpudomain -> setvcpuaffinity

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri Jan 06 12:53:19 2006 +0100 (2006-01-06)
parents 669b6252deee
children 3ea1c6118fc2
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("@releaseDomain", self.onReleaseDomain)
86 self.refresh(True)
87 finally:
88 self.domains_lock.release()
91 def list(self):
92 """Get list of domain objects.
94 @return: domain objects
95 """
96 self.domains_lock.acquire()
97 try:
98 self.refresh()
99 return self.domains.values()
100 finally:
101 self.domains_lock.release()
104 def list_sorted(self):
105 """Get list of domain objects, sorted by name.
107 @return: domain objects
108 """
109 doms = self.list()
110 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
111 return doms
113 def list_names(self):
114 """Get list of domain names.
116 @return: domain names
117 """
118 doms = self.list_sorted()
119 return map(lambda x: x.getName(), doms)
122 ## private:
124 def onReleaseDomain(self, _):
125 self.domains_lock.acquire()
126 try:
127 self.refresh()
128 finally:
129 self.domains_lock.release()
130 return 1
133 def xen_domains(self):
134 """Get table of domains indexed by id from xc. Expects to be
135 protected by the domains_lock.
136 """
137 domlist = xc.domain_getinfo()
138 doms = {}
139 for d in domlist:
140 domid = d['dom']
141 doms[domid] = d
142 return doms
145 def dom0_setup(self):
146 """Expects to be protected by the domains_lock."""
147 dom0 = self.domains[PRIV_DOMAIN]
149 # get max number of vcpus to use for dom0 from config
150 target = int(xroot.get_dom0_vcpus())
151 log.debug("number of vcpus to use is %d", target)
153 # target == 0 means use all processors
154 if target > 0:
155 dom0.setVCpuCount(target)
158 def _add_domain(self, info):
159 """Add the given domain entry to this instance's internal cache.
160 Expects to be protected by the domains_lock.
161 """
162 self.domains[info.getDomid()] = info
165 def _delete_domain(self, domid):
166 """Remove the given domain from this instance's internal cache.
167 Expects to be protected by the domains_lock.
168 """
169 info = self.domains.get(domid)
170 if info:
171 del self.domains[domid]
172 info.cleanupDomain()
175 def refresh(self, initialising = False):
176 """Refresh domain list from Xen. Expects to be protected by the
177 domains_lock.
179 @param initialising True if this is the first refresh after starting
180 Xend. This does not change this method's behaviour, except for
181 logging.
182 """
183 doms = self.xen_domains()
184 for d in self.domains.values():
185 info = doms.get(d.getDomid())
186 if info:
187 d.update(info)
188 else:
189 self._delete_domain(d.getDomid())
190 for d in doms:
191 if d not in self.domains:
192 if doms[d]['dying']:
193 log.log(initialising and logging.ERROR or logging.DEBUG,
194 'Cannot recreate information for dying domain %d.'
195 ' Xend will ignore this domain from now on.',
196 doms[d]['dom'])
197 elif d == PRIV_DOMAIN:
198 log.fatal(
199 "No record of privileged domain %d! Terminating.", d)
200 sys.exit(1)
201 else:
202 try:
203 self._add_domain(
204 XendDomainInfo.recreate(doms[d], False))
205 except:
206 log.exception(
207 "Failed to recreate information for domain "
208 "%d. Destroying it in the hope of "
209 "recovery.", d)
210 try:
211 xc.domain_destroy(d)
212 except:
213 log.exception('Destruction of %d failed.', d)
216 ## public:
218 def domain_create(self, config):
219 """Create a domain from a configuration.
221 @param config: configuration
222 @return: domain
223 """
224 self.domains_lock.acquire()
225 try:
226 dominfo = XendDomainInfo.create(config)
227 self._add_domain(dominfo)
228 return dominfo
229 finally:
230 self.domains_lock.release()
233 def domain_configure(self, config):
234 """Configure an existing domain.
236 @param vmconfig: vm configuration
237 """
238 # !!!
239 raise XendError("Unsupported")
241 def domain_restore(self, src):
242 """Restore a domain from file.
244 @param src: source file
245 """
247 try:
248 fd = os.open(src, os.O_RDONLY)
249 try:
250 return self.domain_restore_fd(fd)
251 finally:
252 os.close(fd)
253 except OSError, ex:
254 raise XendError("can't read guest state file %s: %s" %
255 (src, ex[1]))
257 def domain_restore_fd(self, fd):
258 """Restore a domain from the given file descriptor."""
260 try:
261 return XendCheckpoint.restore(self, fd)
262 except:
263 # I don't really want to log this exception here, but the error
264 # handling in the relocation-socket handling code (relocate.py) is
265 # poor, so we need to log this for debugging.
266 log.exception("Restore failed")
267 raise
270 def restore_(self, config):
271 """Create a domain as part of the restore process. This is called
272 only from {@link XendCheckpoint}.
274 A restore request comes into XendDomain through {@link
275 #domain_restore} or {@link #domain_restore_fd}. That request is
276 forwarded immediately to XendCheckpoint which, when it is ready, will
277 call this method. It is necessary to come through here rather than go
278 directly to {@link XendDomainInfo.restore} because we need to
279 serialise the domain creation process, but cannot lock
280 domain_restore_fd as a whole, otherwise we will deadlock waiting for
281 the old domain to die.
282 """
283 self.domains_lock.acquire()
284 try:
285 dominfo = XendDomainInfo.restore(config)
286 self._add_domain(dominfo)
287 return dominfo
288 finally:
289 self.domains_lock.release()
292 def domain_lookup(self, domid):
293 self.domains_lock.acquire()
294 try:
295 self.refresh()
296 return self.domains.get(domid)
297 finally:
298 self.domains_lock.release()
301 def domain_lookup_nr(self, domid):
302 self.domains_lock.acquire()
303 try:
304 return self.domains.get(domid)
305 finally:
306 self.domains_lock.release()
309 def domain_lookup_by_name_or_id(self, name):
310 self.domains_lock.acquire()
311 try:
312 self.refresh()
313 return self.domain_lookup_by_name_or_id_nr(name)
314 finally:
315 self.domains_lock.release()
318 def domain_lookup_by_name_or_id_nr(self, name):
319 self.domains_lock.acquire()
320 try:
321 dominfo = self.domain_lookup_by_name_nr(name)
323 if dominfo:
324 return dominfo
325 else:
326 try:
327 return self.domains.get(int(name))
328 except ValueError:
329 return None
330 finally:
331 self.domains_lock.release()
334 def domain_lookup_by_name_nr(self, name):
335 self.domains_lock.acquire()
336 try:
337 matching = filter(lambda d: d.getName() == name,
338 self.domains.values())
339 n = len(matching)
340 if n == 1:
341 return matching[0]
342 return None
343 finally:
344 self.domains_lock.release()
347 def privilegedDomain(self):
348 self.domains_lock.acquire()
349 try:
350 return self.domains[PRIV_DOMAIN]
351 finally:
352 self.domains_lock.release()
355 def domain_unpause(self, domid):
356 """Unpause domain execution."""
357 try:
358 dominfo = self.domain_lookup(domid)
359 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
360 dominfo.getDomid())
361 return dominfo.unpause()
362 except Exception, ex:
363 raise XendError(str(ex))
366 def domain_pause(self, domid):
367 """Pause domain execution."""
368 try:
369 dominfo = self.domain_lookup(domid)
370 log.info("Domain %s (%d) paused.", dominfo.getName(),
371 dominfo.getDomid())
372 return dominfo.pause()
373 except Exception, ex:
374 raise XendError(str(ex))
377 def domain_destroy(self, domid):
378 """Terminate domain immediately."""
380 if domid == PRIV_DOMAIN:
381 raise XendError("Cannot destroy privileged domain %i" % domid)
383 dominfo = self.domain_lookup(domid)
384 if dominfo:
385 val = dominfo.destroy()
386 else:
387 try:
388 val = xc.domain_destroy(domid)
389 except Exception, ex:
390 raise XendError(str(ex))
391 return val
393 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
394 """Start domain migration."""
396 dominfo = self.domain_lookup(domid)
398 if dominfo.getDomid() == PRIV_DOMAIN:
399 raise XendError("Cannot migrate privileged domain %i" % domid)
401 if port == 0:
402 port = xroot.get_xend_relocation_port()
403 try:
404 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
405 sock.connect((dst, port))
406 except socket.error, err:
407 raise XendError("can't connect: %s" % err[1])
409 sock.send("receive\n")
410 sock.recv(80)
411 XendCheckpoint.save(sock.fileno(), dominfo, live)
414 def domain_save(self, domid, dst):
415 """Start saving a domain to file.
417 @param dst: destination file
418 """
420 try:
421 dominfo = self.domain_lookup(domid)
423 if dominfo.getDomid() == PRIV_DOMAIN:
424 raise XendError("Cannot save privileged domain %i" % domid)
426 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
427 try:
428 # For now we don't support 'live checkpoint'
429 return XendCheckpoint.save(fd, dominfo, False)
430 finally:
431 os.close(fd)
432 except OSError, ex:
433 raise XendError("can't write guest state file %s: %s" %
434 (dst, ex[1]))
436 def domain_pincpu(self, domid, vcpu, cpumap):
437 """Set which cpus vcpu can use
439 @param cpumap: string repr of list of usable cpus
440 """
441 dominfo = self.domain_lookup(domid)
442 # convert cpumap string into a list of ints
443 cpumap = map(lambda x: int(x),
444 cpumap.replace("[", "").replace("]", "").split(","))
445 try:
446 return xc.vcpu_setaffinity(dominfo.getDomid(), vcpu, cpumap)
447 except Exception, ex:
448 raise XendError(str(ex))
450 def domain_cpu_bvt_set(self, domid, mcuadv, warpback, warpvalue, warpl,
451 warpu):
452 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
453 """
454 dominfo = self.domain_lookup(domid)
455 try:
456 return xc.bvtsched_domain_set(dom=dominfo.getDomid(),
457 mcuadv=mcuadv,
458 warpback=warpback,
459 warpvalue=warpvalue,
460 warpl=warpl, warpu=warpu)
461 except Exception, ex:
462 raise XendError(str(ex))
464 def domain_cpu_bvt_get(self, domid):
465 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
466 """
467 dominfo = self.domain_lookup(domid)
468 try:
469 return xc.bvtsched_domain_get(dominfo.getDomid())
470 except Exception, ex:
471 raise XendError(str(ex))
474 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
475 weight):
476 """Set Simple EDF scheduler parameters for a domain.
477 """
478 dominfo = self.domain_lookup(domid)
479 try:
480 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
481 latency, extratime, weight)
482 except Exception, ex:
483 raise XendError(str(ex))
485 def domain_cpu_sedf_get(self, domid):
486 """Get Simple EDF scheduler parameters for a domain.
487 """
488 dominfo = self.domain_lookup(domid)
489 try:
490 return xc.sedf_domain_get(dominfo.getDomid())
491 except Exception, ex:
492 raise XendError(str(ex))
494 def domain_maxmem_set(self, domid, mem):
495 """Set the memory limit for a domain.
497 @param mem: memory limit (in MiB)
498 @return: 0 on success, -1 on error
499 """
500 dominfo = self.domain_lookup(domid)
501 maxmem = int(mem) * 1024
502 try:
503 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
504 except Exception, ex:
505 raise XendError(str(ex))
507 def domain_ioport_range_enable(self, domid, first, last):
508 """Enable access to a range of IO ports for a domain
510 @param first: first IO port
511 @param last: last IO port
512 @return: 0 on success, -1 on error
513 """
514 dominfo = self.domain_lookup(domid)
515 nr_ports = last - first + 1
516 try:
517 return xc.domain_ioport_permission(dominfo.getDomid(),
518 first_port = first,
519 nr_ports = nr_ports,
520 allow_access = 1)
521 except Exception, ex:
522 raise XendError(str(ex))
524 def domain_ioport_range_disable(self, domid, first, last):
525 """Disable access to a range of IO ports for a domain
527 @param first: first IO port
528 @param last: last IO port
529 @return: 0 on success, -1 on error
530 """
531 dominfo = self.domain_lookup(domid)
532 nr_ports = last - first + 1
533 try:
534 return xc.domain_ioport_permission(dominfo.getDomid(),
535 first_port = first,
536 nr_ports = nr_ports,
537 allow_access = 0)
538 except Exception, ex:
539 raise XendError(str(ex))
542 def instance():
543 """Singleton constructor. Use this instead of the class constructor.
544 """
545 global inst
546 try:
547 inst
548 except:
549 inst = XendDomain()
550 inst.init()
551 return inst