direct-io.hg

view tools/python/xen/xend/XendDomain.py @ 12461:76e5bf49cb37

Improve error message when trying to pause / unpause domain 0.

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author Ewan Mellor <ewan@xensource.com>
date Thu Nov 16 10:26:20 2006 +0000 (2006-11-16)
parents 989fd189f529
children 645ef4726bce
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 domid
61 @type domains: dict of XendDomainInfo
62 @ivar managed_domains: domains that are not running and managed by Xend
63 @type managed_domains: dict of XendDomainInfo indexed by uuid
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['dying'] == 1:
156 log.warn('Ignoring dying domain %d from now on' %
157 dom['domid'])
158 continue
160 if dom['domid'] != DOM0_ID:
161 try:
162 new_dom = XendDomainInfo.recreate(dom, False)
163 self._add_domain(new_dom)
164 except Exception:
165 log.exception("Failed to create reference to running "
166 "domain id: %d" % dom['domid'])
168 # add all managed domains as dormant domains.
169 for dom in managed:
170 dom_uuid = dom.get('uuid')
171 if not dom_uuid:
172 continue
174 dom_name = dom.get('name', 'Domain-%s' % dom_uuid)
175 try:
176 running_dom = self.domain_lookup_nr(dom_name)
177 if not running_dom:
178 # instantiate domain if not started.
179 new_dom = XendDomainInfo.createDormant(dom)
180 self._managed_domain_register(new_dom)
181 else:
182 self._managed_domain_register(running_dom)
183 except Exception:
184 log.exception("Failed to create reference to managed "
185 "domain: %s" % dom_name)
187 finally:
188 self.domains_lock.release()
191 # -----------------------------------------------------------------
192 # Getting managed domains storage path names
194 def _managed_path(self, domuuid = None):
195 """Returns the path of the directory where managed domain
196 information is stored.
198 @keyword domuuid: If not None, will return the path to the domain
199 otherwise, will return the path containing
200 the directories which represent each domain.
201 @type: None or String.
202 @rtype: String
203 @return: Path.
204 """
205 dom_path = xroot.get_xend_domains_path()
206 if domuuid:
207 dom_path = os.path.join(dom_path, domuuid)
208 return dom_path
210 def _managed_config_path(self, domuuid):
211 """Returns the path to the configuration file of a managed domain.
213 @param domname: Domain uuid
214 @type domname: String
215 @rtype: String
216 @return: path to config file.
217 """
218 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
220 def _managed_check_point_path(self, domuuid):
221 """Returns absolute path to check point file for managed domain.
223 @param domuuid: Name of managed domain
224 @type domname: String
225 @rtype: String
226 @return: Path
227 """
228 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
230 def _managed_config_remove(self, domuuid):
231 """Removes a domain configuration from managed list
233 @param domuuid: Name of managed domain
234 @type domname: String
235 @raise XendError: fails to remove the domain.
236 """
237 config_path = self._managed_path(domuuid)
238 try:
239 if os.path.exists(config_path) and os.path.isdir(config_path):
240 shutil.rmtree(config_path)
241 except IOError:
242 log.exception('managed_config_remove failed removing conf')
243 raise XendError("Unable to remove managed configuration"
244 " for domain: %s" % domuuid)
246 def managed_config_save(self, dominfo):
247 """Save a domain's configuration to disk
249 @param domninfo: Managed domain to save.
250 @type dominfo: XendDomainInfo
251 @raise XendError: fails to save configuration.
252 @rtype: None
253 """
254 if not self.is_domain_managed(dominfo):
255 return # refuse to save configuration this domain isn't managed
257 if dominfo:
258 domains_dir = self._managed_path()
259 dom_uuid = dominfo.get_uuid()
260 domain_config_dir = self._managed_path(dom_uuid)
262 # make sure the domain dir exists
263 if not os.path.exists(domains_dir):
264 os.makedirs(domains_dir, 0755)
265 elif not os.path.isdir(domains_dir):
266 log.error("xend_domain_dir is not a directory.")
267 raise XendError("Unable to save managed configuration "
268 "because %s is not a directory." %
269 domains_dir)
271 if not os.path.exists(domain_config_dir):
272 try:
273 os.makedirs(domain_config_dir, 0755)
274 except IOError:
275 log.exception("Failed to create directory: %s" %
276 domain_config_dir)
277 raise XendError("Failed to create directory: %s" %
278 domain_config_dir)
280 try:
281 sxp_cache_file = open(self._managed_config_path(dom_uuid),'w')
282 prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78)
283 sxp_cache_file.close()
284 except IOError:
285 log.error("Error occurred saving configuration file to %s" %
286 domain_config_dir)
287 raise XendError("Failed to save configuration file to: %s" %
288 domain_config_dir)
289 else:
290 log.warn("Trying to save configuration for invalid domain")
293 def _managed_domains(self):
294 """ Returns list of domains that are managed.
296 Expects to be protected by domains_lock.
298 @rtype: list of XendConfig
299 @return: List of domain configurations that are managed.
300 """
301 dom_path = self._managed_path()
302 dom_uuids = os.listdir(dom_path)
303 doms = []
304 for dom_uuid in dom_uuids:
305 try:
306 cfg_file = self._managed_config_path(dom_uuid)
307 cfg = XendConfig(filename = cfg_file)
308 if cfg.get('uuid') != dom_uuid:
309 # something is wrong with the SXP
310 log.error("UUID mismatch in stored configuration: %s" %
311 cfg_file)
312 continue
313 doms.append(cfg)
314 except Exception:
315 log.exception('Unable to open or parse config.sxp: %s' % \
316 cfg_file)
317 return doms
319 def _managed_domain_unregister(self, dom):
320 try:
321 if self.is_domain_managed(dom):
322 self._managed_config_remove(dom.get_uuid())
323 del self.managed_domains[dom.get_uuid()]
324 except ValueError:
325 log.warn("Domain is not registered: %s" % dom.get_uuid())
327 def _managed_domain_register(self, dom):
328 self.managed_domains[dom.get_uuid()] = dom
330 def is_domain_managed(self, dom = None):
331 return (dom.get_uuid() in self.managed_domains)
333 # End of Managed Domain Access
334 # --------------------------------------------------------------------
336 def _running_domains(self):
337 """Get table of domains indexed by id from xc.
339 @requires: Expects to be protected by domains_lock.
340 @rtype: list of dicts
341 @return: A list of dicts representing the running domains.
342 """
343 try:
344 return xc.domain_getinfo()
345 except RuntimeError, e:
346 log.exception("Unable to get domain information.")
347 return {}
349 def _setDom0CPUCount(self):
350 """Sets the number of VCPUs dom0 has. Retreived from the
351 Xend configuration, L{XendRoot}.
353 @requires: Expects to be protected by domains_lock.
354 @rtype: None
355 """
356 dom0 = self.privilegedDomain()
358 # get max number of vcpus to use for dom0 from config
359 target = int(xroot.get_dom0_vcpus())
360 log.debug("number of vcpus to use is %d", target)
362 # target == 0 means use all processors
363 if target > 0:
364 dom0.setVCpuCount(target)
367 def _refresh(self):
368 """Refresh the domain list. Needs to be called when
369 either xenstore has changed or when a method requires
370 up to date information (like uptime, cputime stats).
372 Expects to be protected by the domains_lock.
374 @rtype: None
375 """
377 # update information for all running domains
378 # - like cpu_time, status, dying, etc.
379 running = self._running_domains()
380 for dom in running:
381 domid = dom['domid']
382 if domid in self.domains and dom['dying'] != 1:
383 self.domains[domid].update(dom)
385 # remove domains that are not running from active domain list.
386 # The list might have changed by now, because the update call may
387 # cause new domains to be added, if the domain has rebooted. We get
388 # the list again.
389 running_domids = [d['domid'] for d in running if d['dying'] != 1]
390 for domid, dom in self.domains.items():
391 if domid not in running_domids and domid != DOM0_ID:
392 self._remove_domain(dom, domid)
395 def _add_domain(self, info):
396 """Add a domain to the list of running domains
398 @requires: Expects to be protected by the domains_lock.
399 @param info: XendDomainInfo of a domain to be added.
400 @type info: XendDomainInfo
401 """
402 log.debug("Adding Domain: %s" % info.getDomid())
403 self.domains[info.getDomid()] = info
405 def _remove_domain(self, info, domid = None):
406 """Remove the domain from the list of running domains
408 @requires: Expects to be protected by the domains_lock.
409 @param info: XendDomainInfo of a domain to be removed.
410 @type info: XendDomainInfo
411 """
413 if info:
414 if domid == None:
415 domid = info.getDomid()
417 if info.state != DOM_STATE_HALTED:
418 info.cleanupDomain()
420 if domid in self.domains:
421 del self.domains[domid]
422 else:
423 log.warning("Attempted to remove non-existent domain.")
425 def restore_(self, config):
426 """Create a domain as part of the restore process. This is called
427 only from L{XendCheckpoint}.
429 A restore request comes into XendDomain through L{domain_restore}
430 or L{domain_restore_fd}. That request is
431 forwarded immediately to XendCheckpoint which, when it is ready, will
432 call this method. It is necessary to come through here rather than go
433 directly to L{XendDomainInfo.restore} because we need to
434 serialise the domain creation process, but cannot lock
435 domain_restore_fd as a whole, otherwise we will deadlock waiting for
436 the old domain to die.
438 @param config: Configuration of domain to restore
439 @type config: SXP Object (eg. list of lists)
440 """
441 self.domains_lock.acquire()
442 try:
443 security.refresh_ssidref(config)
444 dominfo = XendDomainInfo.restore(config)
445 self._add_domain(dominfo)
446 return dominfo
447 finally:
448 self.domains_lock.release()
451 def domain_lookup(self, domid):
452 """Look up given I{domid} in the list of managed and running
453 domains.
455 @note: Will cause a refresh before lookup up domains, for
456 a version that does not need to re-read xenstore
457 use L{domain_lookup_nr}.
459 @param domid: Domain ID or Domain Name.
460 @type domid: int or string
461 @return: Found domain.
462 @rtype: XendDomainInfo
463 @raise XendError: If domain is not found.
464 """
465 self.domains_lock.acquire()
466 try:
467 self._refresh()
468 dom = self.domain_lookup_nr(domid)
469 if not dom:
470 raise XendError("No domain named '%s'." % str(domid))
471 return dom
472 finally:
473 self.domains_lock.release()
476 def domain_lookup_nr(self, domid):
477 """Look up given I{domid} in the list of managed and running
478 domains.
480 @param domid: Domain ID or Domain Name.
481 @type domid: int or string
482 @return: Found domain.
483 @rtype: XendDomainInfo or None
484 """
485 self.domains_lock.acquire()
486 try:
487 # lookup by name
488 match = [dom for dom in self.domains.values() \
489 if dom.getName() == domid]
490 if match:
491 return match[0]
493 match = [dom for dom in self.managed_domains.values() \
494 if dom.getName() == domid]
495 if match:
496 return match[0]
498 # lookup by id
499 try:
500 if int(domid) in self.domains:
501 return self.domains[int(domid)]
502 except ValueError:
503 pass
505 # lookup by uuid for running domains
506 match = [dom for dom in self.domains.values() \
507 if dom.get_uuid() == domid]
508 if match:
509 return match[0]
511 # lookup by uuid for inactive managed domains
512 if domid in self.managed_domains:
513 return self.managed_domains[domid]
515 return None
516 finally:
517 self.domains_lock.release()
519 def privilegedDomain(self):
520 """ Get the XendDomainInfo of a dom0
522 @rtype: XendDomainInfo
523 """
524 self.domains_lock.acquire()
525 try:
526 return self.domains[DOM0_ID]
527 finally:
528 self.domains_lock.release()
530 def cleanup_domains(self):
531 """Clean up domains that are marked as autostop.
532 Should be called when Xend goes down. This is currently
533 called from L{xen.xend.servers.XMLRPCServer}.
535 """
536 log.debug('cleanup_domains')
537 self.domains_lock.acquire()
538 try:
539 for dom in self.domains.values():
540 if dom.getName() == DOM0_NAME:
541 continue
543 if dom.state == DOM_STATE_RUNNING:
544 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
545 if shutdownAction == 'shutdown':
546 log.debug('Shutting down domain: %s' % dom.getName())
547 dom.shutdown("poweroff")
548 elif shutdownAction == 'suspend':
549 chkfile = self._managed_check_point_path(dom.getName())
550 self.domain_save(dom.domid, chkfile)
551 finally:
552 self.domains_lock.release()
556 # ----------------------------------------------------------------
557 # Xen API
560 def set_allow_new_domains(self, allow_new_domains):
561 self._allow_new_domains = allow_new_domains
563 def allow_new_domains(self):
564 return self._allow_new_domains
566 def get_domain_refs(self):
567 result = []
568 try:
569 self.domains_lock.acquire()
570 result = [d.get_uuid() for d in self.domains.values()]
571 result += self.managed_domains.keys()
572 return result
573 finally:
574 self.domains_lock.release()
576 def get_vm_by_uuid(self, vm_uuid):
577 self.domains_lock.acquire()
578 try:
579 for dom in self.domains.values():
580 if dom.get_uuid() == vm_uuid:
581 return dom
583 if vm_uuid in self.managed_domains:
584 return self.managed_domains[vm_uuid]
586 return None
587 finally:
588 self.domains_lock.release()
590 def get_vm_with_dev_uuid(self, klass, dev_uuid):
591 self.domains_lock.acquire()
592 try:
593 for dom in self.domains.values() + self.managed_domains.values():
594 if dom.has_device(klass, dev_uuid):
595 return dom
596 return None
597 finally:
598 self.domains_lock.release()
600 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
601 self.domains_lock.acquire()
602 try:
603 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
604 if not dom:
605 return None
607 value = dom.get_device_property(klass, dev_uuid, field)
608 return value
609 except ValueError, e:
610 pass
612 return None
614 def is_valid_vm(self, vm_ref):
615 return (self.get_vm_by_uuid(vm_ref) != None)
617 def is_valid_dev(self, klass, dev_uuid):
618 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
620 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args):
621 self.domains_lock.acquire()
622 try:
623 for domid, dom in self.domains.items():
624 if dom.get_uuid == vm_uuid:
625 return fn(domid, *args)
627 if vm_uuid in self.managed_domains:
628 domid = self.managed_domains[vm_uuid].getDomid()
629 if domid == None:
630 domid = self.managed_domains[vm_uuid].getName()
631 return fn(domid, *args)
633 raise XendInvalidDomain("Domain does not exist")
634 finally:
635 self.domains_lock.release()
638 def create_domain(self, xenapi_vm):
639 self.domains_lock.acquire()
640 try:
641 try:
642 xeninfo = XendConfig(xenapi_vm = xenapi_vm)
643 dominfo = XendDomainInfo.createDormant(xeninfo)
644 log.debug("Creating new managed domain: %s: %s" %
645 (dominfo.getName(), dominfo.get_uuid()))
646 self._managed_domain_register(dominfo)
647 self.managed_config_save(dominfo)
648 return dominfo.get_uuid()
649 except XendError, e:
650 raise
651 except Exception, e:
652 raise XendError(str(e))
653 finally:
654 self.domains_lock.release()
656 def rename_domain(self, dom, new_name):
657 self.domains_lock.acquire()
658 try:
659 old_name = dom.getName()
660 dom.setName(new_name)
662 finally:
663 self.domains_lock.release()
666 #
667 # End of Xen API
668 # ----------------------------------------------------------------
670 # ------------------------------------------------------------
671 # Xen Legacy API
673 def list(self):
674 """Get list of domain objects.
676 @return: domains
677 @rtype: list of XendDomainInfo
678 """
679 self.domains_lock.acquire()
680 try:
681 self._refresh()
683 # active domains
684 active_domains = self.domains.values()
685 active_uuids = [d.get_uuid() for d in active_domains]
687 # inactive domains
688 inactive_domains = []
689 for dom_uuid, dom in self.managed_domains.items():
690 if dom_uuid not in active_uuids:
691 inactive_domains.append(dom)
693 return active_domains + inactive_domains
694 finally:
695 self.domains_lock.release()
698 def list_sorted(self):
699 """Get list of domain objects, sorted by name.
701 @return: domain objects
702 @rtype: list of XendDomainInfo
703 """
704 doms = self.list()
705 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
706 return doms
708 def list_names(self):
709 """Get list of domain names.
711 @return: domain names
712 @rtype: list of strings.
713 """
714 return [d.getName() for d in self.list_sorted()]
716 def domain_suspend(self, domname):
717 """Suspends a domain that is persistently managed by Xend
719 @param domname: Domain Name
720 @type domname: string
721 @rtype: None
722 @raise XendError: Failure during checkpointing.
723 """
725 try:
726 dominfo = self.domain_lookup_nr(domname)
727 if not dominfo:
728 raise XendInvalidDomain(domname)
730 if dominfo.getDomid() == DOM0_ID:
731 raise XendError("Cannot save privileged domain %s" % domname)
733 if dominfo.state != DOM_STATE_RUNNING:
734 raise XendError("Cannot suspend domain that is not running.")
736 if not os.path.exists(self._managed_config_path(domname)):
737 raise XendError("Domain is not managed by Xend lifecycle " +
738 "support.")
740 path = self._managed_check_point_path(domname)
741 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
742 try:
743 # For now we don't support 'live checkpoint'
744 XendCheckpoint.save(fd, dominfo, False, False, path)
745 finally:
746 os.close(fd)
747 except OSError, ex:
748 raise XendError("can't write guest state file %s: %s" %
749 (path, ex[1]))
751 def domain_resume(self, domname):
752 """Resumes a domain that is persistently managed by Xend.
754 @param domname: Domain Name
755 @type domname: string
756 @rtype: None
757 @raise XendError: If failed to restore.
758 """
759 try:
760 dominfo = self.domain_lookup_nr(domname)
762 if not dominfo:
763 raise XendInvalidDomain(domname)
765 if dominfo.getDomid() == DOM0_ID:
766 raise XendError("Cannot save privileged domain %s" % domname)
768 if dominfo.state != DOM_STATE_HALTED:
769 raise XendError("Cannot suspend domain that is not running.")
771 chkpath = self._managed_check_point_path(domname)
772 if not os.path.exists(chkpath):
773 raise XendError("Domain was not suspended by Xend")
775 # Restore that replaces the existing XendDomainInfo
776 try:
777 log.debug('Current DomainInfo state: %d' % dominfo.state)
778 XendCheckpoint.restore(self,
779 os.open(chkpath, os.O_RDONLY),
780 dominfo)
781 os.unlink(chkpath)
782 except OSError, ex:
783 raise XendError("Failed to read stored checkpoint file")
784 except IOError, ex:
785 raise XendError("Failed to delete checkpoint file")
786 except Exception, ex:
787 log.exception("Exception occurred when resuming")
788 raise XendError("Error occurred when resuming: %s" % str(ex))
791 def domain_create(self, config):
792 """Create a domain from a configuration.
794 @param config: configuration
795 @type config: SXP Object (list of lists)
796 @rtype: XendDomainInfo
797 """
798 self.domains_lock.acquire()
799 try:
800 self._refresh()
802 dominfo = XendDomainInfo.create(config)
803 self._add_domain(dominfo)
804 self.domain_sched_credit_set(dominfo.getDomid(),
805 dominfo.getWeight(),
806 dominfo.getCap())
807 return dominfo
808 finally:
809 self.domains_lock.release()
812 def domain_new(self, config):
813 """Create a domain from a configuration but do not start it.
815 @param config: configuration
816 @type config: SXP Object (list of lists)
817 @rtype: XendDomainInfo
818 """
819 self.domains_lock.acquire()
820 try:
821 try:
822 xeninfo = XendConfig(sxp = config)
823 dominfo = XendDomainInfo.createDormant(xeninfo)
824 log.debug("Creating new managed domain: %s" %
825 dominfo.getName())
826 self._managed_domain_register(dominfo)
827 self.managed_config_save(dominfo)
828 # no return value because it isn't meaningful for client
829 except XendError, e:
830 raise
831 except Exception, e:
832 raise XendError(str(e))
833 finally:
834 self.domains_lock.release()
836 def domain_start(self, domid):
837 """Start a managed domain
839 @require: Domain must not be running.
840 @param domid: Domain name or domain ID.
841 @type domid: string or int
842 @rtype: None
843 @raise XendError: If domain is still running
844 @rtype: None
845 """
846 self.domains_lock.acquire()
847 try:
848 self._refresh()
850 dominfo = self.domain_lookup_nr(domid)
851 if not dominfo:
852 raise XendInvalidDomain(str(domid))
854 if dominfo.state != DOM_STATE_HALTED:
855 raise XendError("Domain is already running")
857 dominfo.start(is_managed = True)
858 self._add_domain(dominfo)
859 finally:
860 self.domains_lock.release()
863 def domain_delete(self, domid):
864 """Remove a managed domain from database
866 @require: Domain must not be running.
867 @param domid: Domain name or domain ID.
868 @type domid: string or int
869 @rtype: None
870 @raise XendError: If domain is still running
871 """
872 self.domains_lock.acquire()
873 try:
874 try:
875 dominfo = self.domain_lookup_nr(domid)
876 if not dominfo:
877 raise XendInvalidDomain(str(domid))
879 if dominfo.state != DOM_STATE_HALTED:
880 raise XendError("Domain is still running")
882 self._managed_domain_unregister(dominfo)
883 self._remove_domain(dominfo)
885 except Exception, ex:
886 raise XendError(str(ex))
887 finally:
888 self.domains_lock.release()
891 def domain_configure(self, config):
892 """Configure an existing domain.
894 @param vmconfig: vm configuration
895 @type vmconfig: SXP Object (list of lists)
896 @todo: Not implemented
897 """
898 # !!!
899 raise XendError("Unsupported")
901 def domain_restore(self, src):
902 """Restore a domain from file.
904 @param src: filename of checkpoint file to restore from
905 @type src: string
906 @return: Restored domain
907 @rtype: XendDomainInfo
908 @raise XendError: Failure to restore domain
909 """
910 try:
911 fd = os.open(src, os.O_RDONLY)
912 try:
913 return self.domain_restore_fd(fd)
914 finally:
915 os.close(fd)
916 except OSError, ex:
917 raise XendError("can't read guest state file %s: %s" %
918 (src, ex[1]))
920 def domain_restore_fd(self, fd):
921 """Restore a domain from the given file descriptor.
923 @param fd: file descriptor of the checkpoint file
924 @type fd: File object
925 @rtype: XendDomainInfo
926 @raise XendError: if failed to restore
927 """
929 try:
930 return XendCheckpoint.restore(self, fd)
931 except:
932 # I don't really want to log this exception here, but the error
933 # handling in the relocation-socket handling code (relocate.py) is
934 # poor, so we need to log this for debugging.
935 log.exception("Restore failed")
936 raise XendError("Restore failed")
938 def domain_unpause(self, domid):
939 """Unpause domain execution.
941 @param domid: Domain ID or Name
942 @type domid: int or string.
943 @rtype: None
944 @raise XendError: Failed to unpause
945 @raise XendInvalidDomain: Domain is not valid
946 """
947 try:
948 dominfo = self.domain_lookup_nr(domid)
949 if not dominfo:
950 raise XendInvalidDomain(str(domid))
951 if dominfo.getDomid() == DOM0_ID:
952 raise XendError("Cannot unpause privileged domain %s" % domid)
953 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
954 int(dominfo.getDomid()))
955 dominfo.unpause()
956 except XendInvalidDomain:
957 log.exception("domain_unpause")
958 raise
959 except Exception, ex:
960 log.exception("domain_unpause")
961 raise XendError(str(ex))
963 def domain_pause(self, domid):
964 """Pause domain execution.
966 @param domid: Domain ID or Name
967 @type domid: int or string.
968 @rtype: None
969 @raise XendError: Failed to pause
970 @raise XendInvalidDomain: Domain is not valid
971 """
972 try:
973 dominfo = self.domain_lookup_nr(domid)
974 if not dominfo:
975 raise XendInvalidDomain(str(domid))
976 if dominfo.getDomid() == DOM0_ID:
977 raise XendError("Cannot pause privileged domain %s" % domid)
978 log.info("Domain %s (%d) paused.", dominfo.getName(),
979 int(dominfo.getDomid()))
980 dominfo.pause()
981 except XendInvalidDomain:
982 log.exception("domain_pause")
983 raise
984 except Exception, ex:
985 log.exception("domain_pause")
986 raise XendError(str(ex))
988 def domain_dump(self, domid, filename, live, crash):
989 """Dump domain core."""
991 dominfo = self.domain_lookup_nr(domid)
992 if not dominfo:
993 raise XendInvalidDomain(str(domid))
995 if dominfo.getDomid() == DOM0_ID:
996 raise XendError("Cannot dump core for privileged domain %s" % domid)
998 try:
999 log.info("Domain core dump requested for domain %s (%d) "
1000 "live=%d crash=%d.",
1001 dominfo.getName(), dominfo.getDomid(), live, crash)
1002 return dominfo.dumpCore(filename)
1003 except Exception, ex:
1004 raise XendError(str(ex))
1006 def domain_destroy(self, domid):
1007 """Terminate domain immediately.
1009 @param domid: Domain ID or Name
1010 @type domid: int or string.
1011 @rtype: None
1012 @raise XendError: Failed to destroy
1013 @raise XendInvalidDomain: Domain is not valid
1014 """
1016 dominfo = self.domain_lookup_nr(domid)
1017 if dominfo and dominfo.getDomid() == DOM0_ID:
1018 raise XendError("Cannot destroy privileged domain %s" % domid)
1020 if dominfo:
1021 val = dominfo.destroy()
1022 else:
1023 try:
1024 val = xc.domain_destroy(int(domid))
1025 except ValueError:
1026 raise XendInvalidDomain(domid)
1027 except Exception, e:
1028 raise XendError(str(e))
1030 return val
1032 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1033 """Start domain migration.
1035 @param domid: Domain ID or Name
1036 @type domid: int or string.
1037 @param dst: Destination IP address
1038 @type dst: string
1039 @keyword port: relocation port on destination
1040 @type port: int
1041 @keyword live: Live migration
1042 @type live: bool
1043 @keyword resource: not used??
1044 @rtype: None
1045 @raise XendError: Failed to migrate
1046 @raise XendInvalidDomain: Domain is not valid
1047 """
1049 dominfo = self.domain_lookup_nr(domid)
1050 if not dominfo:
1051 raise XendInvalidDomain(str(domid))
1053 if dominfo.getDomid() == DOM0_ID:
1054 raise XendError("Cannot migrate privileged domain %s" % domid)
1056 """ The following call may raise a XendError exception """
1057 dominfo.testMigrateDevices(True, dst)
1059 if live:
1060 """ Make sure there's memory free for enabling shadow mode """
1061 dominfo.checkLiveMigrateMemory()
1063 if port == 0:
1064 port = xroot.get_xend_relocation_port()
1065 try:
1066 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1067 sock.connect((dst, port))
1068 except socket.error, err:
1069 raise XendError("can't connect: %s" % err[1])
1071 sock.send("receive\n")
1072 sock.recv(80)
1073 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1074 dominfo.testDeviceComplete()
1075 sock.close()
1077 def domain_save(self, domid, dst):
1078 """Start saving a domain to file.
1080 @param domid: Domain ID or Name
1081 @type domid: int or string.
1082 @param dst: Destination filename
1083 @type dst: string
1084 @rtype: None
1085 @raise XendError: Failed to save domain
1086 @raise XendInvalidDomain: Domain is not valid
1087 """
1088 try:
1089 dominfo = self.domain_lookup_nr(domid)
1090 if not dominfo:
1091 raise XendInvalidDomain(str(domid))
1093 if dominfo.getDomid() == DOM0_ID:
1094 raise XendError("Cannot save privileged domain %i" % domid)
1096 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1097 try:
1098 # For now we don't support 'live checkpoint'
1099 XendCheckpoint.save(fd, dominfo, False, False, dst)
1100 finally:
1101 os.close(fd)
1102 except OSError, ex:
1103 raise XendError("can't write guest state file %s: %s" %
1104 (dst, ex[1]))
1106 def domain_pincpu(self, domid, vcpu, cpumap):
1107 """Set which cpus vcpu can use
1109 @param domid: Domain ID or Name
1110 @type domid: int or string.
1111 @param vcpu: vcpu to pin to
1112 @type vcpu: int
1113 @param cpumap: string repr of usable cpus
1114 @type cpumap: string
1115 @rtype: 0
1116 """
1117 dominfo = self.domain_lookup_nr(domid)
1118 if not dominfo:
1119 raise XendInvalidDomain(str(domid))
1121 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1122 vcpus = [ vcpu ]
1123 if str(vcpu).lower() == "all":
1124 vcpus = range(0, int(dominfo.getVCpuCount()))
1126 # set the same cpumask for all vcpus
1127 rc = 0
1128 for v in vcpus:
1129 try:
1130 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1131 except Exception, ex:
1132 raise XendError(str(ex))
1133 return rc
1135 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1136 weight):
1137 """Set Simple EDF scheduler parameters for a domain.
1139 @param domid: Domain ID or Name
1140 @type domid: int or string.
1141 @rtype: 0
1142 """
1143 dominfo = self.domain_lookup_nr(domid)
1144 if not dominfo:
1145 raise XendInvalidDomain(str(domid))
1146 try:
1147 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1148 latency, extratime, weight)
1149 except Exception, ex:
1150 raise XendError(str(ex))
1152 def domain_cpu_sedf_get(self, domid):
1153 """Get Simple EDF scheduler parameters for a domain.
1155 @param domid: Domain ID or Name
1156 @type domid: int or string.
1157 @rtype: SXP object
1158 @return: The parameters for Simple EDF schedule for a domain.
1159 """
1160 dominfo = self.domain_lookup_nr(domid)
1161 if not dominfo:
1162 raise XendInvalidDomain(str(domid))
1163 try:
1164 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1165 # return sxpr
1166 return ['sedf',
1167 ['domid', sedf_info['domid']],
1168 ['period', sedf_info['period']],
1169 ['slice', sedf_info['slice']],
1170 ['latency', sedf_info['latency']],
1171 ['extratime', sedf_info['extratime']],
1172 ['weight', sedf_info['weight']]]
1174 except Exception, ex:
1175 raise XendError(str(ex))
1177 def domain_shadow_control(self, domid, op):
1178 """Shadow page control.
1180 @param domid: Domain ID or Name
1181 @type domid: int or string.
1182 @param op: operation
1183 @type op: int
1184 @rtype: 0
1185 """
1186 dominfo = self.domain_lookup(domid)
1187 try:
1188 return xc.shadow_control(dominfo.getDomid(), op)
1189 except Exception, ex:
1190 raise XendError(str(ex))
1192 def domain_shadow_mem_get(self, domid):
1193 """Get shadow pagetable memory allocation.
1195 @param domid: Domain ID or Name
1196 @type domid: int or string.
1197 @rtype: int
1198 @return: shadow memory in MB
1199 """
1200 dominfo = self.domain_lookup(domid)
1201 try:
1202 return xc.shadow_mem_control(dominfo.getDomid())
1203 except Exception, ex:
1204 raise XendError(str(ex))
1206 def domain_shadow_mem_set(self, domid, mb):
1207 """Set shadow pagetable memory allocation.
1209 @param domid: Domain ID or Name
1210 @type domid: int or string.
1211 @param mb: shadow memory to set in MB
1212 @type: mb: int
1213 @rtype: int
1214 @return: shadow memory in MB
1215 """
1216 dominfo = self.domain_lookup(domid)
1217 try:
1218 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1219 except Exception, ex:
1220 raise XendError(str(ex))
1222 def domain_sched_credit_get(self, domid):
1223 """Get credit scheduler parameters for a domain.
1225 @param domid: Domain ID or Name
1226 @type domid: int or string.
1227 @rtype: dict with keys 'weight' and 'cap'
1228 @return: credit scheduler parameters
1229 """
1230 dominfo = self.domain_lookup_nr(domid)
1231 if not dominfo:
1232 raise XendInvalidDomain(str(domid))
1233 try:
1234 return xc.sched_credit_domain_get(dominfo.getDomid())
1235 except Exception, ex:
1236 raise XendError(str(ex))
1238 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1239 """Set credit scheduler parameters for a domain.
1241 @param domid: Domain ID or Name
1242 @type domid: int or string.
1243 @type weight: int
1244 @type cap: int
1245 @rtype: 0
1246 """
1247 dominfo = self.domain_lookup_nr(domid)
1248 if not dominfo:
1249 raise XendInvalidDomain(str(domid))
1250 try:
1251 if weight is None:
1252 weight = int(0)
1253 elif weight < 1 or weight > 65535:
1254 raise XendError("weight is out of range")
1256 if cap is None:
1257 cap = int(~0)
1258 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1259 raise XendError("cap is out of range")
1261 assert type(weight) == int
1262 assert type(cap) == int
1264 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1265 except Exception, ex:
1266 log.exception(ex)
1267 raise XendError(str(ex))
1269 def domain_maxmem_set(self, domid, mem):
1270 """Set the memory limit for a domain.
1272 @param domid: Domain ID or Name
1273 @type domid: int or string.
1274 @param mem: memory limit (in MiB)
1275 @type mem: int
1276 @raise XendError: fail to set memory
1277 @rtype: 0
1278 """
1279 dominfo = self.domain_lookup_nr(domid)
1280 if not dominfo:
1281 raise XendInvalidDomain(str(domid))
1282 maxmem = int(mem) * 1024
1283 try:
1284 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1285 except Exception, ex:
1286 raise XendError(str(ex))
1288 def domain_ioport_range_enable(self, domid, first, last):
1289 """Enable access to a range of IO ports for a domain
1291 @param first: first IO port
1292 @param last: last IO port
1293 @raise XendError: failed to set range
1294 @rtype: 0
1295 """
1296 dominfo = self.domain_lookup_nr(domid)
1297 if not dominfo:
1298 raise XendInvalidDomain(str(domid))
1299 nr_ports = last - first + 1
1300 try:
1301 return xc.domain_ioport_permission(dominfo.getDomid(),
1302 first_port = first,
1303 nr_ports = nr_ports,
1304 allow_access = 1)
1305 except Exception, ex:
1306 raise XendError(str(ex))
1308 def domain_ioport_range_disable(self, domid, first, last):
1309 """Disable access to a range of IO ports for a domain
1311 @param first: first IO port
1312 @param last: last IO port
1313 @raise XendError: failed to set range
1314 @rtype: 0
1315 """
1316 dominfo = self.domain_lookup_nr(domid)
1317 if not dominfo:
1318 raise XendInvalidDomain(str(domid))
1319 nr_ports = last - first + 1
1320 try:
1321 return xc.domain_ioport_permission(dominfo.getDomid(),
1322 first_port = first,
1323 nr_ports = nr_ports,
1324 allow_access = 0)
1325 except Exception, ex:
1326 raise XendError(str(ex))
1329 def instance():
1330 """Singleton constructor. Use this instead of the class constructor.
1331 """
1332 global inst
1333 try:
1334 inst
1335 except:
1336 inst = XendDomain()
1337 inst.init()
1338 return inst