ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 12237:4ff83eddd1f6

[XEND] Handle permission denied when calling xc.domain_getinfo

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Fri Nov 03 10:41:20 2006 +0000 (2006-11-03)
parents ec7e7e946496
children 2408c042a276
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 os
26 import shutil
27 import socket
28 import threading
30 import xen.lowlevel.xc
33 from xen.xend import XendRoot, XendCheckpoint, XendDomainInfo
34 from xen.xend.PrettyPrint import prettyprint
35 from xen.xend.XendConfig import XendConfig
36 from xen.xend.XendError import XendError, XendInvalidDomain
37 from xen.xend.XendLogging import log
38 from xen.xend.XendConstants import XS_VMROOT
39 from xen.xend.XendConstants import DOM_STATE_HALTED, DOM_STATE_RUNNING
41 from xen.xend.xenstore.xstransact import xstransact
42 from xen.xend.xenstore.xswatch import xswatch
43 from xen.util import security
44 from xen.xend import uuid
46 xc = xen.lowlevel.xc.xc()
47 xroot = XendRoot.instance()
49 __all__ = [ "XendDomain" ]
51 CACHED_CONFIG_FILE = 'config.sxp'
52 CHECK_POINT_FILE = 'checkpoint.chk'
53 DOM0_UUID = "00000000-0000-0000-0000-000000000000"
54 DOM0_NAME = "Domain-0"
55 DOM0_ID = 0
57 class XendDomain:
58 """Index of all domains. Singleton.
60 @ivar domains: map of domains indexed by UUID Strings
61 @type domains: dict of XendDomainInfo
62 @ivar domains_managed: uuid of domains that are managed by Xend
63 @type managed_domains: list of (uuids, dom_name)
64 @ivar domains_lock: lock that must be held when manipulating self.domains
65 @type domains_lock: threaading.RLock
66 @ivar _allow_new_domains: Flag to set that allows creating of new domains.
67 @type _allow_new_domains: boolean
69 """
71 def __init__(self):
72 self.domains = {}
73 self.managed_domains = {}
74 self.domains_lock = threading.RLock()
76 # xen api instance vars
77 # TODO: nothing uses this at the moment
78 self._allow_new_domains = True
80 # This must be called only the once, by instance() below. It is separate
81 # from the constructor because XendDomainInfo calls back into this class
82 # in order to check the uniqueness of domain names. This means that
83 # instance() must be able to return a valid instance of this class even
84 # during this initialisation.
85 def init(self):
86 """Singleton initialisation function."""
88 dom_path = self._managed_path()
89 try:
90 os.stat(dom_path)
91 except OSError:
92 log.info("Making %s", dom_path)
93 os.makedirs(dom_path, 0755)
95 xstransact.Mkdir(XS_VMROOT)
96 xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID})
98 self.domains_lock.acquire()
99 try:
100 try:
101 dom0info = [d for d in self._running_domains() \
102 if d.get('domid') == DOM0_ID][0]
104 dom0info['name'] = DOM0_NAME
105 dom0 = XendDomainInfo.recreate(dom0info, True)
106 self._add_domain(dom0)
107 except IndexError:
108 raise XendError('Unable to find Domain 0')
110 self._setDom0CPUCount()
112 # This watch registration needs to be before the refresh call, so
113 # that we're sure that we haven't missed any releases, but inside
114 # the domains_lock, as we don't want the watch to fire until after
115 # the refresh call has completed.
116 xswatch("@introduceDomain", self._on_domains_changed)
117 xswatch("@releaseDomain", self._on_domains_changed)
119 self._init_domains()
120 finally:
121 self.domains_lock.release()
124 def _on_domains_changed(self, _):
125 """ Callback method when xenstore changes.
127 Calls refresh which will keep the local cache of domains
128 in sync.
130 @rtype: int
131 @return: 1
132 """
133 self.domains_lock.acquire()
134 try:
135 self._refresh()
136 finally:
137 self.domains_lock.release()
138 return 1
140 def _init_domains(self):
141 """Does the initial scan of managed and active domains to
142 populate self.domains.
144 Note: L{XendDomainInfo._checkName} will call back into XendDomain
145 to make sure domain name is not a duplicate.
147 """
148 self.domains_lock.acquire()
149 try:
150 running = self._running_domains()
151 managed = self._managed_domains()
153 # add all active domains
154 for dom in running:
155 if dom['domid'] != DOM0_ID:
156 try:
157 new_dom = XendDomainInfo.recreate(dom, False)
158 self._add_domain(new_dom)
159 except Exception:
160 log.exception("Failed to create reference to running "
161 "domain id: %d" % dom['domid'])
163 # add all managed domains as dormant domains.
164 for dom in managed:
165 dom_uuid = dom.get('uuid', uuid.createString())
166 dom['uuid'] = dom_uuid
167 dom_name = dom.get('name', 'Domain-%s' % dom_uuid)
169 try:
170 running_dom = self.domain_lookup_nr(dom_name)
171 if not running_dom:
172 # instantiate domain if not started.
173 new_dom = XendDomainInfo.createDormant(dom)
174 self._managed_domain_register(new_dom)
175 else:
176 self._managed_domain_register(running_dom)
177 except Exception:
178 log.exception("Failed to create reference to managed "
179 "domain: %s" % dom_name)
181 finally:
182 self.domains_lock.release()
185 # -----------------------------------------------------------------
186 # Getting managed domains storage path names
188 def _managed_path(self, domuuid = None):
189 """Returns the path of the directory where managed domain
190 information is stored.
192 @keyword domuuid: If not None, will return the path to the domain
193 otherwise, will return the path containing
194 the directories which represent each domain.
195 @type: None or String.
196 @rtype: String
197 @return: Path.
198 """
199 dom_path = xroot.get_xend_domains_path()
200 if domuuid:
201 dom_path = os.path.join(dom_path, domuuid)
202 return dom_path
204 def _managed_config_path(self, domuuid):
205 """Returns the path to the configuration file of a managed domain.
207 @param domname: Domain uuid
208 @type domname: String
209 @rtype: String
210 @return: path to config file.
211 """
212 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
214 def _managed_check_point_path(self, domuuid):
215 """Returns absolute path to check point file for managed domain.
217 @param domuuid: Name of managed domain
218 @type domname: String
219 @rtype: String
220 @return: Path
221 """
222 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
224 def _managed_config_remove(self, domuuid):
225 """Removes a domain configuration from managed list
227 @param domuuid: Name of managed domain
228 @type domname: String
229 @raise XendError: fails to remove the domain.
230 """
231 config_path = self._managed_path(domuuid)
232 try:
233 if os.path.exists(config_path) and os.path.isdir(config_path):
234 shutil.rmtree(config_path)
235 except IOError:
236 log.exception('managed_config_remove failed removing conf')
237 raise XendError("Unable to remove managed configuration"
238 " for domain: %s" % domuuid)
240 def managed_config_save(self, dominfo):
241 """Save a domain's configuration to disk
243 @param domninfo: Managed domain to save.
244 @type dominfo: XendDomainInfo
245 @raise XendError: fails to save configuration.
246 @rtype: None
247 """
248 if not self.is_domain_managed(dominfo):
249 return # refuse to save configuration this domain isn't managed
251 if dominfo:
252 domains_dir = self._managed_path()
253 dom_uuid = dominfo.get_uuid()
254 domain_config_dir = self._managed_path(dom_uuid)
256 # make sure the domain dir exists
257 if not os.path.exists(domains_dir):
258 os.makedirs(domains_dir, 0755)
259 elif not os.path.isdir(domains_dir):
260 log.error("xend_domain_dir is not a directory.")
261 raise XendError("Unable to save managed configuration "
262 "because %s is not a directory." %
263 domains_dir)
265 if not os.path.exists(domain_config_dir):
266 try:
267 os.makedirs(domain_config_dir, 0755)
268 except IOError:
269 log.exception("Failed to create directory: %s" %
270 domain_config_dir)
271 raise XendError("Failed to create directory: %s" %
272 domain_config_dir)
274 try:
275 sxp_cache_file = open(self._managed_config_path(dom_uuid),'w')
276 prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78)
277 sxp_cache_file.close()
278 except IOError:
279 log.error("Error occurred saving configuration file to %s" %
280 domain_config_dir)
281 raise XendError("Failed to save configuration file to: %s" %
282 domain_config_dir)
283 else:
284 log.warn("Trying to save configuration for invalid domain")
287 def _managed_domains(self):
288 """ Returns list of domains that are managed.
290 Expects to be protected by domains_lock.
292 @rtype: list of XendConfig
293 @return: List of domain configurations that are managed.
294 """
295 dom_path = self._managed_path()
296 dom_uuids = os.listdir(dom_path)
297 doms = []
298 for dom_uuid in dom_uuids:
299 try:
300 cfg_file = self._managed_config_path(dom_uuid)
301 cfg = XendConfig(filename = cfg_file)
302 doms.append(cfg)
303 except Exception:
304 log.exception('Unable to open or parse config.sxp: %s' % \
305 cfg_file)
306 return doms
308 def _managed_domain_unregister(self, dom):
309 try:
310 if self.is_domain_managed(dom):
311 del self.managed_domains[dom.get_uuid()]
312 except ValueError:
313 log.warn("Domain is not registered: %s" % dom.get_uuid())
315 def _managed_domain_register(self, dom):
316 self.managed_domains[dom.get_uuid()] = dom
318 def is_domain_managed(self, dom = None):
319 return (dom.get_uuid() in self.managed_domains)
321 # End of Managed Domain Access
322 # --------------------------------------------------------------------
324 def _running_domains(self):
325 """Get table of domains indexed by id from xc.
327 @requires: Expects to be protected by domains_lock.
328 @rtype: list of dicts
329 @return: A list of dicts representing the running domains.
330 """
331 try:
332 return xc.domain_getinfo()
333 except RuntimeError, e:
334 log.exception("Unable to get domain information.")
335 return {}
337 def _setDom0CPUCount(self):
338 """Sets the number of VCPUs dom0 has. Retreived from the
339 Xend configuration, L{XendRoot}.
341 @requires: Expects to be protected by domains_lock.
342 @rtype: None
343 """
344 dom0 = self.privilegedDomain()
346 # get max number of vcpus to use for dom0 from config
347 target = int(xroot.get_dom0_vcpus())
348 log.debug("number of vcpus to use is %d", target)
350 # target == 0 means use all processors
351 if target > 0:
352 dom0.setVCpuCount(target)
355 def _refresh(self):
356 """Refresh the domain list. Needs to be called when
357 either xenstore has changed or when a method requires
358 up to date information (like uptime, cputime stats).
360 @rtype: None
361 """
362 self.domains_lock.acquire()
363 try:
364 # update information for all running domains
365 # - like cpu_time, status, dying, etc.
366 running = self._running_domains()
367 for dom in running:
368 domid = dom['domid']
369 if domid in self.domains:
370 self.domains[domid].update(dom)
372 # remove domains that are not running from active
373 # domain list
374 running_domids = [d['domid'] for d in running]
375 for domid, dom in self.domains.items():
376 if domid not in running_domids and domid != DOM0_ID:
377 self._remove_domain(dom, domid)
379 finally:
380 self.domains_lock.release()
382 def _add_domain(self, info):
383 """Add the given domain entry to this instance's internal cache.
385 @requires: Expects to be protected by the domains_lock.
386 @param info: XendDomainInfo of a domain to be added.
387 @type info: XendDomainInfo
388 """
389 log.debug("Adding Domain: %s" % info.getDomid())
390 self.domains[info.getDomid()] = info
392 def _remove_domain(self, info, domid = None):
393 """Remove the given domain from this instance's internal cache.
395 @requires: Expects to be protected by the domains_lock.
396 @param info: XendDomainInfo of a domain to be removed.
397 @type info: XendDomainInfo
398 """
400 if info:
401 if domid == None:
402 domid = info.getDomid()
404 if info.state != DOM_STATE_HALTED:
405 info.cleanupDomain()
407 if domid in self.domains:
408 del self.domains[domid]
409 else:
410 log.warning("Attempted to remove non-existent domain.")
412 def restore_(self, config):
413 """Create a domain as part of the restore process. This is called
414 only from L{XendCheckpoint}.
416 A restore request comes into XendDomain through L{domain_restore}
417 or L{domain_restore_fd}. That request is
418 forwarded immediately to XendCheckpoint which, when it is ready, will
419 call this method. It is necessary to come through here rather than go
420 directly to L{XendDomainInfo.restore} because we need to
421 serialise the domain creation process, but cannot lock
422 domain_restore_fd as a whole, otherwise we will deadlock waiting for
423 the old domain to die.
425 @param config: Configuration of domain to restore
426 @type config: SXP Object (eg. list of lists)
427 """
428 self.domains_lock.acquire()
429 try:
430 security.refresh_ssidref(config)
431 dominfo = XendDomainInfo.restore(config)
432 self._add_domain(dominfo)
433 return dominfo
434 finally:
435 self.domains_lock.release()
438 def domain_lookup(self, domid):
439 """Look up given I{domid} in the list of managed and running
440 domains.
442 @note: Will cause a refresh before lookup up domains, for
443 a version that does not need to re-read xenstore
444 use L{domain_lookup_nr}.
446 @param domid: Domain ID or Domain Name.
447 @type domid: int or string
448 @return: Found domain.
449 @rtype: XendDomainInfo
450 @raise XendError: If domain is not found.
451 """
452 self.domains_lock.acquire()
453 try:
454 self._refresh()
455 dom = self.domain_lookup_nr(domid)
456 if not dom:
457 raise XendError("No domain named '%s'." % str(domid))
458 return dom
459 finally:
460 self.domains_lock.release()
463 def domain_lookup_nr(self, domid):
464 """Look up given I{domid} in the list of managed and running
465 domains.
467 @param domid: Domain ID or Domain Name.
468 @type domid: int or string
469 @return: Found domain.
470 @rtype: XendDomainInfo or None
471 """
472 self.domains_lock.acquire()
473 try:
474 # lookup by name
475 match = [dom for dom in self.domains.values() \
476 if dom.getName() == domid]
477 if match:
478 return match[0]
480 # lookup by id
481 try:
482 if int(domid) in self.domains:
483 return self.domains[int(domid)]
484 except ValueError:
485 pass
487 return None
488 finally:
489 self.domains_lock.release()
491 def privilegedDomain(self):
492 """ Get the XendDomainInfo of a dom0
494 @rtype: XendDomainInfo
495 """
496 self.domains_lock.acquire()
497 try:
498 return self.domains[DOM0_ID]
499 finally:
500 self.domains_lock.release()
502 def cleanup_domains(self):
503 """Clean up domains that are marked as autostop.
504 Should be called when Xend goes down. This is currently
505 called from L{xen.xend.servers.XMLRPCServer}.
507 """
508 log.debug('cleanup_domains')
509 self.domains_lock.acquire()
510 try:
511 for dom in self.domains.values():
512 if dom.getName() == DOM0_NAME:
513 continue
515 if dom.state == DOM_STATE_RUNNING:
516 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
517 if shutdownAction == 'shutdown':
518 log.debug('Shutting down domain: %s' % dom.getName())
519 dom.shutdown("poweroff")
520 elif shutdownAction == 'suspend':
521 chkfile = self._managed_check_point_path(dom.getName())
522 self.domain_save(dom.domid, chkfile)
523 finally:
524 self.domains_lock.release()
528 # ----------------------------------------------------------------
529 # Xen API
532 def set_allow_new_domains(self, allow_new_domains):
533 self._allow_new_domains = allow_new_domains
535 def allow_new_domains(self):
536 return self._allow_new_domains
538 def get_domain_refs(self):
539 result = []
540 try:
541 self.domains_lock.acquire()
542 result = [d.get_uuid() for d in self.domains.values()]
543 result += self.managed_domains.keys()
544 return result
545 finally:
546 self.domains_lock.release()
548 def get_vm_by_uuid(self, vm_uuid):
549 self.domains_lock.acquire()
550 try:
551 for dom in self.domains.values():
552 if dom.get_uuid() == vm_uuid:
553 return dom
555 if vm_uuid in self.managed_domains:
556 return self.managed_domains[vm_uuid]
558 return None
559 finally:
560 self.domains_lock.release()
562 def get_vm_with_dev_uuid(self, klass, dev_uuid):
563 self.domains_lock.acquire()
564 try:
565 for dom in self.domains.values() + self.managed_domains.values():
566 if dom.has_device(klass, dev_uuid):
567 return dom
568 return None
569 finally:
570 self.domains_lock.release()
572 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
573 self.domains_lock.acquire()
574 try:
575 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
576 if not dom:
577 return None
579 value = dom.get_device_property(klass, dev_uuid, field)
580 return value
581 except ValueError, e:
582 pass
584 return None
586 def is_valid_vm(self, vm_ref):
587 return (self.get_vm_by_uuid(vm_ref) != None)
589 def is_valid_dev(self, klass, dev_uuid):
590 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
592 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args):
593 self.domains_lock.acquire()
594 try:
595 for domid, dom in self.domains.items():
596 if dom.get_uuid == vm_uuid:
597 return fn(domid, *args)
599 if vm_uuid in self.managed_domains:
600 domid = self.managed_domains[vm_uuid].getDomid()
601 if domid == None:
602 domid = self.managed_domains[vm_uuid].getName()
603 return fn(domid, *args)
605 raise XendInvalidDomain("Domain does not exist")
606 finally:
607 self.domains_lock.release()
610 def create_domain(self, xenapi_vm):
611 self.domains_lock.acquire()
612 try:
613 try:
614 xeninfo = XendConfig(xenapi_vm = xenapi_vm)
615 dominfo = XendDomainInfo.createDormant(xeninfo)
616 log.debug("Creating new managed domain: %s: %s" %
617 (dominfo.getName(), dominfo.get_uuid()))
618 self._managed_domain_register(dominfo)
619 self.managed_config_save(dominfo)
620 return dominfo.get_uuid()
621 except XendError, e:
622 raise
623 except Exception, e:
624 raise XendError(str(e))
625 finally:
626 self.domains_lock.release()
628 def rename_domain(self, dom, new_name):
629 self.domains_lock.acquire()
630 try:
631 old_name = dom.getName()
632 dom.setName(new_name)
634 finally:
635 self.domains_lock.release()
638 #
639 # End of Xen API
640 # ----------------------------------------------------------------
642 # ------------------------------------------------------------
643 # Xen Legacy API
645 def list(self):
646 """Get list of domain objects.
648 @return: domains
649 @rtype: list of XendDomainInfo
650 """
651 self.domains_lock.acquire()
652 try:
653 self._refresh()
655 # active domains
656 active_domains = self.domains.values()
657 active_uuids = [d.get_uuid() for d in active_domains]
659 # inactive domains
660 inactive_domains = []
661 for dom_uuid, dom in self.managed_domains.items():
662 if dom_uuid not in active_uuids:
663 inactive_domains.append(dom)
665 return active_domains + inactive_domains
666 finally:
667 self.domains_lock.release()
670 def list_sorted(self):
671 """Get list of domain objects, sorted by name.
673 @return: domain objects
674 @rtype: list of XendDomainInfo
675 """
676 doms = self.list()
677 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
678 return doms
680 def list_names(self):
681 """Get list of domain names.
683 @return: domain names
684 @rtype: list of strings.
685 """
686 return [d.getName() for d in self.list_sorted()]
688 def domain_suspend(self, domname):
689 """Suspends a domain that is persistently managed by Xend
691 @param domname: Domain Name
692 @type domname: string
693 @rtype: None
694 @raise XendError: Failure during checkpointing.
695 """
697 try:
698 dominfo = self.domain_lookup_nr(domname)
699 if not dominfo:
700 raise XendInvalidDomain(domname)
702 if dominfo.getDomid() == DOM0_ID:
703 raise XendError("Cannot save privileged domain %s" % domname)
705 if dominfo.state != DOM_STATE_RUNNING:
706 raise XendError("Cannot suspend domain that is not running.")
708 if not os.path.exists(self._managed_config_path(domname)):
709 raise XendError("Domain is not managed by Xend lifecycle " +
710 "support.")
712 path = self._managed_check_point_path(domname)
713 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
714 try:
715 # For now we don't support 'live checkpoint'
716 XendCheckpoint.save(fd, dominfo, False, False, path)
717 finally:
718 os.close(fd)
719 except OSError, ex:
720 raise XendError("can't write guest state file %s: %s" %
721 (path, ex[1]))
723 def domain_resume(self, domname):
724 """Resumes a domain that is persistently managed by Xend.
726 @param domname: Domain Name
727 @type domname: string
728 @rtype: None
729 @raise XendError: If failed to restore.
730 """
731 try:
732 dominfo = self.domain_lookup_nr(domname)
734 if not dominfo:
735 raise XendInvalidDomain(domname)
737 if dominfo.getDomid() == DOM0_ID:
738 raise XendError("Cannot save privileged domain %s" % domname)
740 if dominfo.state != DOM_STATE_HALTED:
741 raise XendError("Cannot suspend domain that is not running.")
743 chkpath = self._managed_check_point_path(domname)
744 if not os.path.exists(chkpath):
745 raise XendError("Domain was not suspended by Xend")
747 # Restore that replaces the existing XendDomainInfo
748 try:
749 log.debug('Current DomainInfo state: %d' % dominfo.state)
750 XendCheckpoint.restore(self,
751 os.open(chkpath, os.O_RDONLY),
752 dominfo)
753 os.unlink(chkpath)
754 except OSError, ex:
755 raise XendError("Failed to read stored checkpoint file")
756 except IOError, ex:
757 raise XendError("Failed to delete checkpoint file")
758 except Exception, ex:
759 log.exception("Exception occurred when resuming")
760 raise XendError("Error occurred when resuming: %s" % str(ex))
763 def domain_create(self, config):
764 """Create a domain from a configuration.
766 @param config: configuration
767 @type config: SXP Object (list of lists)
768 @rtype: XendDomainInfo
769 """
770 self.domains_lock.acquire()
771 try:
772 dominfo = XendDomainInfo.create(config)
773 self._add_domain(dominfo)
774 self.domain_sched_credit_set(dominfo.getDomid(),
775 dominfo.getWeight(),
776 dominfo.getCap())
777 return dominfo
778 finally:
779 self.domains_lock.release()
782 def domain_new(self, config):
783 """Create a domain from a configuration but do not start it.
785 @param config: configuration
786 @type config: SXP Object (list of lists)
787 @rtype: XendDomainInfo
788 """
789 self.domains_lock.acquire()
790 try:
791 try:
792 xeninfo = XendConfig(sxp = config)
793 dominfo = XendDomainInfo.createDormant(xeninfo)
794 log.debug("Creating new managed domain: %s" %
795 dominfo.getName())
796 self._managed_domain_register(dominfo)
797 self.managed_config_save(dominfo)
798 # no return value because it isn't meaningful for client
799 except XendError, e:
800 raise
801 except Exception, e:
802 raise XendError(str(e))
803 finally:
804 self.domains_lock.release()
806 def domain_start(self, domid):
807 """Start a managed domain
809 @require: Domain must not be running.
810 @param domid: Domain name or domain ID.
811 @type domid: string or int
812 @rtype: None
813 @raise XendError: If domain is still running
814 @rtype: None
815 """
816 self.domains_lock.acquire()
817 try:
818 dominfo = self.domain_lookup_nr(domid)
819 if not dominfo:
820 raise XendInvalidDomain(str(domid))
822 if dominfo.state != DOM_STATE_HALTED:
823 raise XendError("Domain is already running")
825 dominfo.start(is_managed = True)
828 finally:
829 self.domains_lock.release()
832 def domain_delete(self, domid):
833 """Remove a managed domain from database
835 @require: Domain must not be running.
836 @param domid: Domain name or domain ID.
837 @type domid: string or int
838 @rtype: None
839 @raise XendError: If domain is still running
840 """
841 self.domains_lock.acquire()
842 try:
843 try:
844 dominfo = self.domain_lookup_nr(domid)
845 if not dominfo:
846 raise XendInvalidDomain(str(domid))
848 if dominfo.state != DOM_STATE_HALTED:
849 raise XendError("Domain is still running")
851 self._managed_domain_unregister(dominfo)
852 self._remove_domain(dominfo)
854 except Exception, ex:
855 raise XendError(str(ex))
856 finally:
857 self.domains_lock.release()
860 def domain_configure(self, config):
861 """Configure an existing domain.
863 @param vmconfig: vm configuration
864 @type vmconfig: SXP Object (list of lists)
865 @todo: Not implemented
866 """
867 # !!!
868 raise XendError("Unsupported")
870 def domain_restore(self, src):
871 """Restore a domain from file.
873 @param src: filename of checkpoint file to restore from
874 @type src: string
875 @return: Restored domain
876 @rtype: XendDomainInfo
877 @raise XendError: Failure to restore domain
878 """
879 try:
880 fd = os.open(src, os.O_RDONLY)
881 try:
882 return self.domain_restore_fd(fd)
883 finally:
884 os.close(fd)
885 except OSError, ex:
886 raise XendError("can't read guest state file %s: %s" %
887 (src, ex[1]))
889 def domain_restore_fd(self, fd):
890 """Restore a domain from the given file descriptor.
892 @param fd: file descriptor of the checkpoint file
893 @type fd: File object
894 @rtype: XendDomainInfo
895 @raise XendError: if failed to restore
896 """
898 try:
899 return XendCheckpoint.restore(self, fd)
900 except:
901 # I don't really want to log this exception here, but the error
902 # handling in the relocation-socket handling code (relocate.py) is
903 # poor, so we need to log this for debugging.
904 log.exception("Restore failed")
905 raise XendError("Restore failed")
907 def domain_unpause(self, domid):
908 """Unpause domain execution.
910 @param domid: Domain ID or Name
911 @type domid: int or string.
912 @rtype: None
913 @raise XendError: Failed to unpause
914 @raise XendInvalidDomain: Domain is not valid
915 """
916 try:
917 dominfo = self.domain_lookup_nr(domid)
918 if not dominfo:
919 raise XendInvalidDomain(str(domid))
921 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
922 int(dominfo.getDomid()))
924 dominfo.unpause()
925 except XendInvalidDomain:
926 log.exception("domain_unpause")
927 raise
928 except Exception, ex:
929 log.exception("domain_unpause")
930 raise XendError(str(ex))
932 def domain_pause(self, domid):
933 """Pause domain execution.
935 @param domid: Domain ID or Name
936 @type domid: int or string.
937 @rtype: None
938 @raise XendError: Failed to pause
939 @raise XendInvalidDomain: Domain is not valid
940 """
941 try:
942 dominfo = self.domain_lookup_nr(domid)
943 if not dominfo:
944 raise XendInvalidDomain(str(domid))
945 log.info("Domain %s (%d) paused.", dominfo.getName(),
946 int(dominfo.getDomid()))
947 dominfo.pause()
948 except XendInvalidDomain:
949 log.exception("domain_pause")
950 raise
951 except Exception, ex:
952 log.exception("domain_pause")
953 raise XendError(str(ex))
955 def domain_dump(self, domid, filename, live, crash):
956 """Dump domain core."""
958 dominfo = self.domain_lookup_nr(domid)
959 if not dominfo:
960 raise XendInvalidDomain(str(domid))
962 if dominfo.getDomid() == DOM0_ID:
963 raise XendError("Cannot dump core for privileged domain %s" % domid)
965 try:
966 log.info("Domain core dump requested for domain %s (%d) "
967 "live=%d crash=%d.",
968 dominfo.getName(), dominfo.getDomid(), live, crash)
969 return dominfo.dumpCore(filename)
970 except Exception, ex:
971 raise XendError(str(ex))
973 def domain_destroy(self, domid):
974 """Terminate domain immediately.
976 @param domid: Domain ID or Name
977 @type domid: int or string.
978 @rtype: None
979 @raise XendError: Failed to destroy
980 @raise XendInvalidDomain: Domain is not valid
981 """
983 dominfo = self.domain_lookup_nr(domid)
984 if dominfo and dominfo.getDomid() == DOM0_ID:
985 raise XendError("Cannot destroy privileged domain %s" % domid)
987 if dominfo:
988 val = dominfo.destroy()
989 else:
990 try:
991 val = xc.domain_destroy(int(domid))
992 except ValueError:
993 raise XendInvalidDomain(domid)
994 except Exception, e:
995 raise XendError(str(e))
997 return val
999 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1000 """Start domain migration.
1002 @param domid: Domain ID or Name
1003 @type domid: int or string.
1004 @param dst: Destination IP address
1005 @type dst: string
1006 @keyword port: relocation port on destination
1007 @type port: int
1008 @keyword live: Live migration
1009 @type live: bool
1010 @keyword resource: not used??
1011 @rtype: None
1012 @raise XendError: Failed to migrate
1013 @raise XendInvalidDomain: Domain is not valid
1014 """
1016 dominfo = self.domain_lookup_nr(domid)
1017 if not dominfo:
1018 raise XendInvalidDomain(str(domid))
1020 if dominfo.getDomid() == DOM0_ID:
1021 raise XendError("Cannot migrate privileged domain %i" % domid)
1023 """ The following call may raise a XendError exception """
1024 dominfo.testMigrateDevices(True, dst)
1026 if live:
1027 """ Make sure there's memory free for enabling shadow mode """
1028 dominfo.checkLiveMigrateMemory()
1030 if port == 0:
1031 port = xroot.get_xend_relocation_port()
1032 try:
1033 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1034 sock.connect((dst, port))
1035 except socket.error, err:
1036 raise XendError("can't connect: %s" % err[1])
1038 sock.send("receive\n")
1039 sock.recv(80)
1040 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1041 dominfo.testDeviceComplete()
1042 sock.close()
1044 def domain_save(self, domid, dst):
1045 """Start saving a domain to file.
1047 @param domid: Domain ID or Name
1048 @type domid: int or string.
1049 @param dst: Destination filename
1050 @type dst: string
1051 @rtype: None
1052 @raise XendError: Failed to save domain
1053 @raise XendInvalidDomain: Domain is not valid
1054 """
1055 try:
1056 dominfo = self.domain_lookup_nr(domid)
1057 if not dominfo:
1058 raise XendInvalidDomain(str(domid))
1060 if dominfo.getDomid() == DOM0_ID:
1061 raise XendError("Cannot save privileged domain %i" % domid)
1063 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1064 try:
1065 # For now we don't support 'live checkpoint'
1066 XendCheckpoint.save(fd, dominfo, False, False, dst)
1067 finally:
1068 os.close(fd)
1069 except OSError, ex:
1070 raise XendError("can't write guest state file %s: %s" %
1071 (dst, ex[1]))
1073 def domain_pincpu(self, domid, vcpu, cpumap):
1074 """Set which cpus vcpu can use
1076 @param domid: Domain ID or Name
1077 @type domid: int or string.
1078 @param vcpu: vcpu to pin to
1079 @type vcpu: int
1080 @param cpumap: string repr of usable cpus
1081 @type cpumap: string
1082 @rtype: 0
1083 """
1084 dominfo = self.domain_lookup_nr(domid)
1085 if not dominfo:
1086 raise XendInvalidDomain(str(domid))
1088 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1089 vcpus = [ vcpu ]
1090 if str(vcpu).lower() == "all":
1091 vcpus = range(0, int(dominfo.getVCpuCount()))
1093 # set the same cpumask for all vcpus
1094 rc = 0
1095 for v in vcpus:
1096 try:
1097 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1098 except Exception, ex:
1099 raise XendError(str(ex))
1100 return rc
1102 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1103 weight):
1104 """Set Simple EDF scheduler parameters for a domain.
1106 @param domid: Domain ID or Name
1107 @type domid: int or string.
1108 @rtype: 0
1109 """
1110 dominfo = self.domain_lookup_nr(domid)
1111 if not dominfo:
1112 raise XendInvalidDomain(str(domid))
1113 try:
1114 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1115 latency, extratime, weight)
1116 except Exception, ex:
1117 raise XendError(str(ex))
1119 def domain_cpu_sedf_get(self, domid):
1120 """Get Simple EDF scheduler parameters for a domain.
1122 @param domid: Domain ID or Name
1123 @type domid: int or string.
1124 @rtype: SXP object
1125 @return: The parameters for Simple EDF schedule for a domain.
1126 """
1127 dominfo = self.domain_lookup_nr(domid)
1128 if not dominfo:
1129 raise XendInvalidDomain(str(domid))
1130 try:
1131 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1132 # return sxpr
1133 return ['sedf',
1134 ['domain', sedf_info['domain']],
1135 ['period', sedf_info['period']],
1136 ['slice', sedf_info['slice']],
1137 ['latency', sedf_info['latency']],
1138 ['extratime', sedf_info['extratime']],
1139 ['weight', sedf_info['weight']]]
1141 except Exception, ex:
1142 raise XendError(str(ex))
1144 def domain_shadow_control(self, domid, op):
1145 """Shadow page control.
1147 @param domid: Domain ID or Name
1148 @type domid: int or string.
1149 @param op: operation
1150 @type op: int
1151 @rtype: 0
1152 """
1153 dominfo = self.domain_lookup(domid)
1154 try:
1155 return xc.shadow_control(dominfo.getDomid(), op)
1156 except Exception, ex:
1157 raise XendError(str(ex))
1159 def domain_shadow_mem_get(self, domid):
1160 """Get shadow pagetable memory allocation.
1162 @param domid: Domain ID or Name
1163 @type domid: int or string.
1164 @rtype: int
1165 @return: shadow memory in MB
1166 """
1167 dominfo = self.domain_lookup(domid)
1168 try:
1169 return xc.shadow_mem_control(dominfo.getDomid())
1170 except Exception, ex:
1171 raise XendError(str(ex))
1173 def domain_shadow_mem_set(self, domid, mb):
1174 """Set shadow pagetable memory allocation.
1176 @param domid: Domain ID or Name
1177 @type domid: int or string.
1178 @param mb: shadow memory to set in MB
1179 @type: mb: int
1180 @rtype: int
1181 @return: shadow memory in MB
1182 """
1183 dominfo = self.domain_lookup(domid)
1184 try:
1185 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1186 except Exception, ex:
1187 raise XendError(str(ex))
1189 def domain_sched_credit_get(self, domid):
1190 """Get credit scheduler parameters for a domain.
1192 @param domid: Domain ID or Name
1193 @type domid: int or string.
1194 @rtype: dict with keys 'weight' and 'cap'
1195 @return: credit scheduler parameters
1196 """
1197 dominfo = self.domain_lookup_nr(domid)
1198 if not dominfo:
1199 raise XendInvalidDomain(str(domid))
1200 try:
1201 return xc.sched_credit_domain_get(dominfo.getDomid())
1202 except Exception, ex:
1203 raise XendError(str(ex))
1205 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1206 """Set credit scheduler parameters for a domain.
1208 @param domid: Domain ID or Name
1209 @type domid: int or string.
1210 @type weight: int
1211 @type cap: int
1212 @rtype: 0
1213 """
1214 dominfo = self.domain_lookup_nr(domid)
1215 if not dominfo:
1216 raise XendInvalidDomain(str(domid))
1217 try:
1218 if weight is None:
1219 weight = int(0)
1220 elif weight < 1 or weight > 65535:
1221 raise XendError("weight is out of range")
1223 if cap is None:
1224 cap = int(~0)
1225 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1226 raise XendError("cap is out of range")
1228 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1229 except Exception, ex:
1230 raise XendError(str(ex))
1232 def domain_maxmem_set(self, domid, mem):
1233 """Set the memory limit for a domain.
1235 @param domid: Domain ID or Name
1236 @type domid: int or string.
1237 @param mem: memory limit (in MiB)
1238 @type mem: int
1239 @raise XendError: fail to set memory
1240 @rtype: 0
1241 """
1242 dominfo = self.domain_lookup_nr(domid)
1243 if not dominfo:
1244 raise XendInvalidDomain(str(domid))
1245 maxmem = int(mem) * 1024
1246 try:
1247 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1248 except Exception, ex:
1249 raise XendError(str(ex))
1251 def domain_ioport_range_enable(self, domid, first, last):
1252 """Enable access to a range of IO ports for a domain
1254 @param first: first IO port
1255 @param last: last IO port
1256 @raise XendError: failed to set range
1257 @rtype: 0
1258 """
1259 dominfo = self.domain_lookup_nr(domid)
1260 if not dominfo:
1261 raise XendInvalidDomain(str(domid))
1262 nr_ports = last - first + 1
1263 try:
1264 return xc.domain_ioport_permission(dominfo.getDomid(),
1265 first_port = first,
1266 nr_ports = nr_ports,
1267 allow_access = 1)
1268 except Exception, ex:
1269 raise XendError(str(ex))
1271 def domain_ioport_range_disable(self, domid, first, last):
1272 """Disable access to a range of IO ports for a domain
1274 @param first: first IO port
1275 @param last: last IO port
1276 @raise XendError: failed to set range
1277 @rtype: 0
1278 """
1279 dominfo = self.domain_lookup_nr(domid)
1280 if not dominfo:
1281 raise XendInvalidDomain(str(domid))
1282 nr_ports = last - first + 1
1283 try:
1284 return xc.domain_ioport_permission(dominfo.getDomid(),
1285 first_port = first,
1286 nr_ports = nr_ports,
1287 allow_access = 0)
1288 except Exception, ex:
1289 raise XendError(str(ex))
1292 def instance():
1293 """Singleton constructor. Use this instead of the class constructor.
1294 """
1295 global inst
1296 try:
1297 inst
1298 except:
1299 inst = XendDomain()
1300 inst.init()
1301 return inst