ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 10039:36e0159c001b

This patch adds a boolean parameter 'network' to the save method of the
XendCheckpoint class to differentiate between network suspend/resume
(network=True) and local suspend/resume (network=False).
Instead of passing the 'live' parameter to the migration methods, this
'network' parameter is passed now. This ends up being merely a renaming
of the parameter.
A check with the xm-test suite against this change has resulted in no
additional errors.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Wed May 10 16:52:55 2006 +0100 (2006-05-10)
parents 681a18bf049e
children e539abd27a0f
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 privilegedDomain(self):
351 self.domains_lock.acquire()
352 try:
353 return self.domains[PRIV_DOMAIN]
354 finally:
355 self.domains_lock.release()
358 def domain_unpause(self, domid):
359 """Unpause domain execution."""
360 try:
361 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
362 if not dominfo:
363 raise XendInvalidDomain(str(domid))
364 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
365 dominfo.getDomid())
366 return dominfo.unpause()
367 except Exception, ex:
368 raise XendError(str(ex))
371 def domain_pause(self, domid):
372 """Pause domain execution."""
373 try:
374 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
375 if not dominfo:
376 raise XendInvalidDomain(str(domid))
377 log.info("Domain %s (%d) paused.", dominfo.getName(),
378 dominfo.getDomid())
379 return dominfo.pause()
380 except Exception, ex:
381 raise XendError(str(ex))
384 def domain_destroy(self, domid):
385 """Terminate domain immediately."""
387 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
388 if dominfo and dominfo.getDomid() == PRIV_DOMAIN:
389 raise XendError("Cannot destroy privileged domain %s" % domid)
391 if dominfo:
392 val = dominfo.destroy()
393 else:
394 try:
395 val = xc.domain_destroy(domid)
396 except Exception, ex:
397 raise XendError(str(ex))
398 return val
400 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
401 """Start domain migration."""
403 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
404 if not dominfo:
405 raise XendInvalidDomain(str(domid))
407 if dominfo.getDomid() == PRIV_DOMAIN:
408 raise XendError("Cannot migrate privileged domain %i" % domid)
410 """ The following call may raise a XendError exception """
411 dominfo.testMigrateDevices(True, dst)
413 if port == 0:
414 port = xroot.get_xend_relocation_port()
415 try:
416 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
417 sock.connect((dst, port))
418 except socket.error, err:
419 raise XendError("can't connect: %s" % err[1])
421 sock.send("receive\n")
422 sock.recv(80)
423 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
426 def domain_save(self, domid, dst):
427 """Start saving a domain to file.
429 @param dst: destination file
430 """
432 try:
433 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
434 if not dominfo:
435 raise XendInvalidDomain(str(domid))
437 if dominfo.getDomid() == PRIV_DOMAIN:
438 raise XendError("Cannot save privileged domain %i" % domid)
440 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
441 try:
442 # For now we don't support 'live checkpoint'
443 return XendCheckpoint.save(fd, dominfo, False, False, dst)
444 finally:
445 os.close(fd)
446 except OSError, ex:
447 raise XendError("can't write guest state file %s: %s" %
448 (dst, ex[1]))
450 def domain_pincpu(self, domid, vcpu, cpumap):
451 """Set which cpus vcpu can use
453 @param cpumap: string repr of list of usable cpus
454 """
455 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
456 if not dominfo:
457 raise XendInvalidDomain(str(domid))
459 try:
460 return xc.vcpu_setaffinity(dominfo.getDomid(), vcpu, cpumap)
461 except Exception, ex:
462 raise XendError(str(ex))
464 def domain_cpu_bvt_set(self, domid, mcuadv, warpback, warpvalue, warpl,
465 warpu):
466 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
467 """
468 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
469 if not dominfo:
470 raise XendInvalidDomain(str(domid))
471 try:
472 return xc.bvtsched_domain_set(dom=dominfo.getDomid(),
473 mcuadv=mcuadv,
474 warpback=warpback,
475 warpvalue=warpvalue,
476 warpl=warpl, warpu=warpu)
477 except Exception, ex:
478 raise XendError(str(ex))
480 def domain_cpu_bvt_get(self, domid):
481 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
482 """
483 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
484 if not dominfo:
485 raise XendInvalidDomain(str(domid))
486 try:
487 return xc.bvtsched_domain_get(dominfo.getDomid())
488 except Exception, ex:
489 raise XendError(str(ex))
492 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
493 weight):
494 """Set Simple EDF scheduler parameters for a domain.
495 """
496 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
497 if not dominfo:
498 raise XendInvalidDomain(str(domid))
499 try:
500 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
501 latency, extratime, weight)
502 except Exception, ex:
503 raise XendError(str(ex))
505 def domain_cpu_sedf_get(self, domid):
506 """Get Simple EDF scheduler parameters for a domain.
507 """
508 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
509 if not dominfo:
510 raise XendInvalidDomain(str(domid))
511 try:
512 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
513 # return sxpr
514 return ['sedf',
515 ['domain', sedf_info['domain']],
516 ['period', sedf_info['period']],
517 ['slice', sedf_info['slice']],
518 ['latency', sedf_info['latency']],
519 ['extratime', sedf_info['extratime']],
520 ['weight', sedf_info['weight']]]
522 except Exception, ex:
523 raise XendError(str(ex))
525 def domain_maxmem_set(self, domid, mem):
526 """Set the memory limit for a domain.
528 @param mem: memory limit (in MiB)
529 @return: 0 on success, -1 on error
530 """
531 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
532 if not dominfo:
533 raise XendInvalidDomain(str(domid))
534 maxmem = int(mem) * 1024
535 try:
536 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
537 except Exception, ex:
538 raise XendError(str(ex))
540 def domain_ioport_range_enable(self, domid, first, last):
541 """Enable access to a range of IO ports for a domain
543 @param first: first IO port
544 @param last: last IO port
545 @return: 0 on success, -1 on error
546 """
547 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
548 if not dominfo:
549 raise XendInvalidDomain(str(domid))
550 nr_ports = last - first + 1
551 try:
552 return xc.domain_ioport_permission(dominfo.getDomid(),
553 first_port = first,
554 nr_ports = nr_ports,
555 allow_access = 1)
556 except Exception, ex:
557 raise XendError(str(ex))
559 def domain_ioport_range_disable(self, domid, first, last):
560 """Disable access to a range of IO ports for a domain
562 @param first: first IO port
563 @param last: last IO port
564 @return: 0 on success, -1 on error
565 """
566 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
567 if not dominfo:
568 raise XendInvalidDomain(str(domid))
569 nr_ports = last - first + 1
570 try:
571 return xc.domain_ioport_permission(dominfo.getDomid(),
572 first_port = first,
573 nr_ports = nr_ports,
574 allow_access = 0)
575 except Exception, ex:
576 raise XendError(str(ex))
579 def instance():
580 """Singleton constructor. Use this instead of the class constructor.
581 """
582 global inst
583 try:
584 inst
585 except:
586 inst = XendDomain()
587 inst.init()
588 return inst