ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 12325:42769ff008b5

Ignore dying domains on recreate, and log the fact.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Wed Nov 08 18:27:31 2006 +0000 (2006-11-08)
parents 69d8263d5f85
children 023aa2926e79
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 managed_domains: 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['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', uuid.createString())
171 dom['uuid'] = dom_uuid
172 dom_name = dom.get('name', 'Domain-%s' % dom_uuid)
174 try:
175 running_dom = self.domain_lookup_nr(dom_name)
176 if not running_dom:
177 # instantiate domain if not started.
178 new_dom = XendDomainInfo.createDormant(dom)
179 self._managed_domain_register(new_dom)
180 else:
181 self._managed_domain_register(running_dom)
182 except Exception:
183 log.exception("Failed to create reference to managed "
184 "domain: %s" % dom_name)
186 finally:
187 self.domains_lock.release()
190 # -----------------------------------------------------------------
191 # Getting managed domains storage path names
193 def _managed_path(self, domuuid = None):
194 """Returns the path of the directory where managed domain
195 information is stored.
197 @keyword domuuid: If not None, will return the path to the domain
198 otherwise, will return the path containing
199 the directories which represent each domain.
200 @type: None or String.
201 @rtype: String
202 @return: Path.
203 """
204 dom_path = xroot.get_xend_domains_path()
205 if domuuid:
206 dom_path = os.path.join(dom_path, domuuid)
207 return dom_path
209 def _managed_config_path(self, domuuid):
210 """Returns the path to the configuration file of a managed domain.
212 @param domname: Domain uuid
213 @type domname: String
214 @rtype: String
215 @return: path to config file.
216 """
217 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
219 def _managed_check_point_path(self, domuuid):
220 """Returns absolute path to check point file for managed domain.
222 @param domuuid: Name of managed domain
223 @type domname: String
224 @rtype: String
225 @return: Path
226 """
227 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
229 def _managed_config_remove(self, domuuid):
230 """Removes a domain configuration from managed list
232 @param domuuid: Name of managed domain
233 @type domname: String
234 @raise XendError: fails to remove the domain.
235 """
236 config_path = self._managed_path(domuuid)
237 try:
238 if os.path.exists(config_path) and os.path.isdir(config_path):
239 shutil.rmtree(config_path)
240 except IOError:
241 log.exception('managed_config_remove failed removing conf')
242 raise XendError("Unable to remove managed configuration"
243 " for domain: %s" % domuuid)
245 def managed_config_save(self, dominfo):
246 """Save a domain's configuration to disk
248 @param domninfo: Managed domain to save.
249 @type dominfo: XendDomainInfo
250 @raise XendError: fails to save configuration.
251 @rtype: None
252 """
253 if not self.is_domain_managed(dominfo):
254 return # refuse to save configuration this domain isn't managed
256 if dominfo:
257 domains_dir = self._managed_path()
258 dom_uuid = dominfo.get_uuid()
259 domain_config_dir = self._managed_path(dom_uuid)
261 # make sure the domain dir exists
262 if not os.path.exists(domains_dir):
263 os.makedirs(domains_dir, 0755)
264 elif not os.path.isdir(domains_dir):
265 log.error("xend_domain_dir is not a directory.")
266 raise XendError("Unable to save managed configuration "
267 "because %s is not a directory." %
268 domains_dir)
270 if not os.path.exists(domain_config_dir):
271 try:
272 os.makedirs(domain_config_dir, 0755)
273 except IOError:
274 log.exception("Failed to create directory: %s" %
275 domain_config_dir)
276 raise XendError("Failed to create directory: %s" %
277 domain_config_dir)
279 try:
280 sxp_cache_file = open(self._managed_config_path(dom_uuid),'w')
281 prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78)
282 sxp_cache_file.close()
283 except IOError:
284 log.error("Error occurred saving configuration file to %s" %
285 domain_config_dir)
286 raise XendError("Failed to save configuration file to: %s" %
287 domain_config_dir)
288 else:
289 log.warn("Trying to save configuration for invalid domain")
292 def _managed_domains(self):
293 """ Returns list of domains that are managed.
295 Expects to be protected by domains_lock.
297 @rtype: list of XendConfig
298 @return: List of domain configurations that are managed.
299 """
300 dom_path = self._managed_path()
301 dom_uuids = os.listdir(dom_path)
302 doms = []
303 for dom_uuid in dom_uuids:
304 try:
305 cfg_file = self._managed_config_path(dom_uuid)
306 cfg = XendConfig(filename = cfg_file)
307 doms.append(cfg)
308 except Exception:
309 log.exception('Unable to open or parse config.sxp: %s' % \
310 cfg_file)
311 return doms
313 def _managed_domain_unregister(self, dom):
314 try:
315 if self.is_domain_managed(dom):
316 del self.managed_domains[dom.get_uuid()]
317 except ValueError:
318 log.warn("Domain is not registered: %s" % dom.get_uuid())
320 def _managed_domain_register(self, dom):
321 self.managed_domains[dom.get_uuid()] = dom
323 def is_domain_managed(self, dom = None):
324 return (dom.get_uuid() in self.managed_domains)
326 # End of Managed Domain Access
327 # --------------------------------------------------------------------
329 def _running_domains(self):
330 """Get table of domains indexed by id from xc.
332 @requires: Expects to be protected by domains_lock.
333 @rtype: list of dicts
334 @return: A list of dicts representing the running domains.
335 """
336 try:
337 return xc.domain_getinfo()
338 except RuntimeError, e:
339 log.exception("Unable to get domain information.")
340 return {}
342 def _setDom0CPUCount(self):
343 """Sets the number of VCPUs dom0 has. Retreived from the
344 Xend configuration, L{XendRoot}.
346 @requires: Expects to be protected by domains_lock.
347 @rtype: None
348 """
349 dom0 = self.privilegedDomain()
351 # get max number of vcpus to use for dom0 from config
352 target = int(xroot.get_dom0_vcpus())
353 log.debug("number of vcpus to use is %d", target)
355 # target == 0 means use all processors
356 if target > 0:
357 dom0.setVCpuCount(target)
360 def _refresh(self):
361 """Refresh the domain list. Needs to be called when
362 either xenstore has changed or when a method requires
363 up to date information (like uptime, cputime stats).
365 Expects to be protected by the domains_lock.
367 @rtype: None
368 """
370 # update information for all running domains
371 # - like cpu_time, status, dying, etc.
372 running = self._running_domains()
373 for dom in running:
374 domid = dom['domid']
375 if domid in self.domains:
376 self.domains[domid].update(dom)
378 # remove domains that are not running from active domain list.
379 # The list might have changed by now, because the update call may
380 # cause new domains to be added, if the domain has rebooted. We get
381 # the list again.
382 running = self._running_domains()
383 running_domids = [d['domid'] for d in running]
384 for domid, dom in self.domains.items():
385 if domid not in running_domids and domid != DOM0_ID:
386 self._remove_domain(dom, domid)
389 def _add_domain(self, info):
390 """Add the given domain entry to this instance's internal cache.
392 @requires: Expects to be protected by the domains_lock.
393 @param info: XendDomainInfo of a domain to be added.
394 @type info: XendDomainInfo
395 """
396 log.debug("Adding Domain: %s" % info.getDomid())
397 self.domains[info.getDomid()] = info
399 def _remove_domain(self, info, domid = None):
400 """Remove the given domain from this instance's internal cache.
402 @requires: Expects to be protected by the domains_lock.
403 @param info: XendDomainInfo of a domain to be removed.
404 @type info: XendDomainInfo
405 """
407 if info:
408 if domid == None:
409 domid = info.getDomid()
411 if info.state != DOM_STATE_HALTED:
412 info.cleanupDomain()
414 if domid in self.domains:
415 del self.domains[domid]
416 else:
417 log.warning("Attempted to remove non-existent domain.")
419 def restore_(self, config):
420 """Create a domain as part of the restore process. This is called
421 only from L{XendCheckpoint}.
423 A restore request comes into XendDomain through L{domain_restore}
424 or L{domain_restore_fd}. That request is
425 forwarded immediately to XendCheckpoint which, when it is ready, will
426 call this method. It is necessary to come through here rather than go
427 directly to L{XendDomainInfo.restore} because we need to
428 serialise the domain creation process, but cannot lock
429 domain_restore_fd as a whole, otherwise we will deadlock waiting for
430 the old domain to die.
432 @param config: Configuration of domain to restore
433 @type config: SXP Object (eg. list of lists)
434 """
435 self.domains_lock.acquire()
436 try:
437 security.refresh_ssidref(config)
438 dominfo = XendDomainInfo.restore(config)
439 self._add_domain(dominfo)
440 return dominfo
441 finally:
442 self.domains_lock.release()
445 def domain_lookup(self, domid):
446 """Look up given I{domid} in the list of managed and running
447 domains.
449 @note: Will cause a refresh before lookup up domains, for
450 a version that does not need to re-read xenstore
451 use L{domain_lookup_nr}.
453 @param domid: Domain ID or Domain Name.
454 @type domid: int or string
455 @return: Found domain.
456 @rtype: XendDomainInfo
457 @raise XendError: If domain is not found.
458 """
459 self.domains_lock.acquire()
460 try:
461 self._refresh()
462 dom = self.domain_lookup_nr(domid)
463 if not dom:
464 raise XendError("No domain named '%s'." % str(domid))
465 return dom
466 finally:
467 self.domains_lock.release()
470 def domain_lookup_nr(self, domid):
471 """Look up given I{domid} in the list of managed and running
472 domains.
474 @param domid: Domain ID or Domain Name.
475 @type domid: int or string
476 @return: Found domain.
477 @rtype: XendDomainInfo or None
478 """
479 self.domains_lock.acquire()
480 try:
481 # lookup by name
482 match = [dom for dom in self.domains.values() \
483 if dom.getName() == domid]
484 if match:
485 return match[0]
487 # lookup by id
488 try:
489 if int(domid) in self.domains:
490 return self.domains[int(domid)]
491 except ValueError:
492 pass
494 return None
495 finally:
496 self.domains_lock.release()
498 def privilegedDomain(self):
499 """ Get the XendDomainInfo of a dom0
501 @rtype: XendDomainInfo
502 """
503 self.domains_lock.acquire()
504 try:
505 return self.domains[DOM0_ID]
506 finally:
507 self.domains_lock.release()
509 def cleanup_domains(self):
510 """Clean up domains that are marked as autostop.
511 Should be called when Xend goes down. This is currently
512 called from L{xen.xend.servers.XMLRPCServer}.
514 """
515 log.debug('cleanup_domains')
516 self.domains_lock.acquire()
517 try:
518 for dom in self.domains.values():
519 if dom.getName() == DOM0_NAME:
520 continue
522 if dom.state == DOM_STATE_RUNNING:
523 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
524 if shutdownAction == 'shutdown':
525 log.debug('Shutting down domain: %s' % dom.getName())
526 dom.shutdown("poweroff")
527 elif shutdownAction == 'suspend':
528 chkfile = self._managed_check_point_path(dom.getName())
529 self.domain_save(dom.domid, chkfile)
530 finally:
531 self.domains_lock.release()
535 # ----------------------------------------------------------------
536 # Xen API
539 def set_allow_new_domains(self, allow_new_domains):
540 self._allow_new_domains = allow_new_domains
542 def allow_new_domains(self):
543 return self._allow_new_domains
545 def get_domain_refs(self):
546 result = []
547 try:
548 self.domains_lock.acquire()
549 result = [d.get_uuid() for d in self.domains.values()]
550 result += self.managed_domains.keys()
551 return result
552 finally:
553 self.domains_lock.release()
555 def get_vm_by_uuid(self, vm_uuid):
556 self.domains_lock.acquire()
557 try:
558 for dom in self.domains.values():
559 if dom.get_uuid() == vm_uuid:
560 return dom
562 if vm_uuid in self.managed_domains:
563 return self.managed_domains[vm_uuid]
565 return None
566 finally:
567 self.domains_lock.release()
569 def get_vm_with_dev_uuid(self, klass, dev_uuid):
570 self.domains_lock.acquire()
571 try:
572 for dom in self.domains.values() + self.managed_domains.values():
573 if dom.has_device(klass, dev_uuid):
574 return dom
575 return None
576 finally:
577 self.domains_lock.release()
579 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
580 self.domains_lock.acquire()
581 try:
582 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
583 if not dom:
584 return None
586 value = dom.get_device_property(klass, dev_uuid, field)
587 return value
588 except ValueError, e:
589 pass
591 return None
593 def is_valid_vm(self, vm_ref):
594 return (self.get_vm_by_uuid(vm_ref) != None)
596 def is_valid_dev(self, klass, dev_uuid):
597 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
599 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args):
600 self.domains_lock.acquire()
601 try:
602 for domid, dom in self.domains.items():
603 if dom.get_uuid == vm_uuid:
604 return fn(domid, *args)
606 if vm_uuid in self.managed_domains:
607 domid = self.managed_domains[vm_uuid].getDomid()
608 if domid == None:
609 domid = self.managed_domains[vm_uuid].getName()
610 return fn(domid, *args)
612 raise XendInvalidDomain("Domain does not exist")
613 finally:
614 self.domains_lock.release()
617 def create_domain(self, xenapi_vm):
618 self.domains_lock.acquire()
619 try:
620 try:
621 xeninfo = XendConfig(xenapi_vm = xenapi_vm)
622 dominfo = XendDomainInfo.createDormant(xeninfo)
623 log.debug("Creating new managed domain: %s: %s" %
624 (dominfo.getName(), dominfo.get_uuid()))
625 self._managed_domain_register(dominfo)
626 self.managed_config_save(dominfo)
627 return dominfo.get_uuid()
628 except XendError, e:
629 raise
630 except Exception, e:
631 raise XendError(str(e))
632 finally:
633 self.domains_lock.release()
635 def rename_domain(self, dom, new_name):
636 self.domains_lock.acquire()
637 try:
638 old_name = dom.getName()
639 dom.setName(new_name)
641 finally:
642 self.domains_lock.release()
645 #
646 # End of Xen API
647 # ----------------------------------------------------------------
649 # ------------------------------------------------------------
650 # Xen Legacy API
652 def list(self):
653 """Get list of domain objects.
655 @return: domains
656 @rtype: list of XendDomainInfo
657 """
658 self.domains_lock.acquire()
659 try:
660 self._refresh()
662 # active domains
663 active_domains = self.domains.values()
664 active_uuids = [d.get_uuid() for d in active_domains]
666 # inactive domains
667 inactive_domains = []
668 for dom_uuid, dom in self.managed_domains.items():
669 if dom_uuid not in active_uuids:
670 inactive_domains.append(dom)
672 return active_domains + inactive_domains
673 finally:
674 self.domains_lock.release()
677 def list_sorted(self):
678 """Get list of domain objects, sorted by name.
680 @return: domain objects
681 @rtype: list of XendDomainInfo
682 """
683 doms = self.list()
684 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
685 return doms
687 def list_names(self):
688 """Get list of domain names.
690 @return: domain names
691 @rtype: list of strings.
692 """
693 return [d.getName() for d in self.list_sorted()]
695 def domain_suspend(self, domname):
696 """Suspends a domain that is persistently managed by Xend
698 @param domname: Domain Name
699 @type domname: string
700 @rtype: None
701 @raise XendError: Failure during checkpointing.
702 """
704 try:
705 dominfo = self.domain_lookup_nr(domname)
706 if not dominfo:
707 raise XendInvalidDomain(domname)
709 if dominfo.getDomid() == DOM0_ID:
710 raise XendError("Cannot save privileged domain %s" % domname)
712 if dominfo.state != DOM_STATE_RUNNING:
713 raise XendError("Cannot suspend domain that is not running.")
715 if not os.path.exists(self._managed_config_path(domname)):
716 raise XendError("Domain is not managed by Xend lifecycle " +
717 "support.")
719 path = self._managed_check_point_path(domname)
720 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
721 try:
722 # For now we don't support 'live checkpoint'
723 XendCheckpoint.save(fd, dominfo, False, False, path)
724 finally:
725 os.close(fd)
726 except OSError, ex:
727 raise XendError("can't write guest state file %s: %s" %
728 (path, ex[1]))
730 def domain_resume(self, domname):
731 """Resumes a domain that is persistently managed by Xend.
733 @param domname: Domain Name
734 @type domname: string
735 @rtype: None
736 @raise XendError: If failed to restore.
737 """
738 try:
739 dominfo = self.domain_lookup_nr(domname)
741 if not dominfo:
742 raise XendInvalidDomain(domname)
744 if dominfo.getDomid() == DOM0_ID:
745 raise XendError("Cannot save privileged domain %s" % domname)
747 if dominfo.state != DOM_STATE_HALTED:
748 raise XendError("Cannot suspend domain that is not running.")
750 chkpath = self._managed_check_point_path(domname)
751 if not os.path.exists(chkpath):
752 raise XendError("Domain was not suspended by Xend")
754 # Restore that replaces the existing XendDomainInfo
755 try:
756 log.debug('Current DomainInfo state: %d' % dominfo.state)
757 XendCheckpoint.restore(self,
758 os.open(chkpath, os.O_RDONLY),
759 dominfo)
760 os.unlink(chkpath)
761 except OSError, ex:
762 raise XendError("Failed to read stored checkpoint file")
763 except IOError, ex:
764 raise XendError("Failed to delete checkpoint file")
765 except Exception, ex:
766 log.exception("Exception occurred when resuming")
767 raise XendError("Error occurred when resuming: %s" % str(ex))
770 def domain_create(self, config):
771 """Create a domain from a configuration.
773 @param config: configuration
774 @type config: SXP Object (list of lists)
775 @rtype: XendDomainInfo
776 """
777 self.domains_lock.acquire()
778 try:
779 self._refresh()
781 dominfo = XendDomainInfo.create(config)
782 self._add_domain(dominfo)
783 self.domain_sched_credit_set(dominfo.getDomid(),
784 dominfo.getWeight(),
785 dominfo.getCap())
786 return dominfo
787 finally:
788 self.domains_lock.release()
791 def domain_new(self, config):
792 """Create a domain from a configuration but do not start it.
794 @param config: configuration
795 @type config: SXP Object (list of lists)
796 @rtype: XendDomainInfo
797 """
798 self.domains_lock.acquire()
799 try:
800 try:
801 xeninfo = XendConfig(sxp = config)
802 dominfo = XendDomainInfo.createDormant(xeninfo)
803 log.debug("Creating new managed domain: %s" %
804 dominfo.getName())
805 self._managed_domain_register(dominfo)
806 self.managed_config_save(dominfo)
807 # no return value because it isn't meaningful for client
808 except XendError, e:
809 raise
810 except Exception, e:
811 raise XendError(str(e))
812 finally:
813 self.domains_lock.release()
815 def domain_start(self, domid):
816 """Start a managed domain
818 @require: Domain must not be running.
819 @param domid: Domain name or domain ID.
820 @type domid: string or int
821 @rtype: None
822 @raise XendError: If domain is still running
823 @rtype: None
824 """
825 self.domains_lock.acquire()
826 try:
827 self._refresh()
829 dominfo = self.domain_lookup_nr(domid)
830 if not dominfo:
831 raise XendInvalidDomain(str(domid))
833 if dominfo.state != DOM_STATE_HALTED:
834 raise XendError("Domain is already running")
836 dominfo.start(is_managed = True)
838 finally:
839 self.domains_lock.release()
842 def domain_delete(self, domid):
843 """Remove a managed domain from database
845 @require: Domain must not be running.
846 @param domid: Domain name or domain ID.
847 @type domid: string or int
848 @rtype: None
849 @raise XendError: If domain is still running
850 """
851 self.domains_lock.acquire()
852 try:
853 try:
854 dominfo = self.domain_lookup_nr(domid)
855 if not dominfo:
856 raise XendInvalidDomain(str(domid))
858 if dominfo.state != DOM_STATE_HALTED:
859 raise XendError("Domain is still running")
861 self._managed_domain_unregister(dominfo)
862 self._remove_domain(dominfo)
864 except Exception, ex:
865 raise XendError(str(ex))
866 finally:
867 self.domains_lock.release()
870 def domain_configure(self, config):
871 """Configure an existing domain.
873 @param vmconfig: vm configuration
874 @type vmconfig: SXP Object (list of lists)
875 @todo: Not implemented
876 """
877 # !!!
878 raise XendError("Unsupported")
880 def domain_restore(self, src):
881 """Restore a domain from file.
883 @param src: filename of checkpoint file to restore from
884 @type src: string
885 @return: Restored domain
886 @rtype: XendDomainInfo
887 @raise XendError: Failure to restore domain
888 """
889 try:
890 fd = os.open(src, os.O_RDONLY)
891 try:
892 return self.domain_restore_fd(fd)
893 finally:
894 os.close(fd)
895 except OSError, ex:
896 raise XendError("can't read guest state file %s: %s" %
897 (src, ex[1]))
899 def domain_restore_fd(self, fd):
900 """Restore a domain from the given file descriptor.
902 @param fd: file descriptor of the checkpoint file
903 @type fd: File object
904 @rtype: XendDomainInfo
905 @raise XendError: if failed to restore
906 """
908 try:
909 return XendCheckpoint.restore(self, fd)
910 except:
911 # I don't really want to log this exception here, but the error
912 # handling in the relocation-socket handling code (relocate.py) is
913 # poor, so we need to log this for debugging.
914 log.exception("Restore failed")
915 raise XendError("Restore failed")
917 def domain_unpause(self, domid):
918 """Unpause domain execution.
920 @param domid: Domain ID or Name
921 @type domid: int or string.
922 @rtype: None
923 @raise XendError: Failed to unpause
924 @raise XendInvalidDomain: Domain is not valid
925 """
926 try:
927 dominfo = self.domain_lookup_nr(domid)
928 if not dominfo:
929 raise XendInvalidDomain(str(domid))
931 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
932 int(dominfo.getDomid()))
934 dominfo.unpause()
935 except XendInvalidDomain:
936 log.exception("domain_unpause")
937 raise
938 except Exception, ex:
939 log.exception("domain_unpause")
940 raise XendError(str(ex))
942 def domain_pause(self, domid):
943 """Pause domain execution.
945 @param domid: Domain ID or Name
946 @type domid: int or string.
947 @rtype: None
948 @raise XendError: Failed to pause
949 @raise XendInvalidDomain: Domain is not valid
950 """
951 try:
952 dominfo = self.domain_lookup_nr(domid)
953 if not dominfo:
954 raise XendInvalidDomain(str(domid))
955 log.info("Domain %s (%d) paused.", dominfo.getName(),
956 int(dominfo.getDomid()))
957 dominfo.pause()
958 except XendInvalidDomain:
959 log.exception("domain_pause")
960 raise
961 except Exception, ex:
962 log.exception("domain_pause")
963 raise XendError(str(ex))
965 def domain_dump(self, domid, filename, live, crash):
966 """Dump domain core."""
968 dominfo = self.domain_lookup_nr(domid)
969 if not dominfo:
970 raise XendInvalidDomain(str(domid))
972 if dominfo.getDomid() == DOM0_ID:
973 raise XendError("Cannot dump core for privileged domain %s" % domid)
975 try:
976 log.info("Domain core dump requested for domain %s (%d) "
977 "live=%d crash=%d.",
978 dominfo.getName(), dominfo.getDomid(), live, crash)
979 return dominfo.dumpCore(filename)
980 except Exception, ex:
981 raise XendError(str(ex))
983 def domain_destroy(self, domid):
984 """Terminate domain immediately.
986 @param domid: Domain ID or Name
987 @type domid: int or string.
988 @rtype: None
989 @raise XendError: Failed to destroy
990 @raise XendInvalidDomain: Domain is not valid
991 """
993 dominfo = self.domain_lookup_nr(domid)
994 if dominfo and dominfo.getDomid() == DOM0_ID:
995 raise XendError("Cannot destroy privileged domain %s" % domid)
997 if dominfo:
998 val = dominfo.destroy()
999 else:
1000 try:
1001 val = xc.domain_destroy(int(domid))
1002 except ValueError:
1003 raise XendInvalidDomain(domid)
1004 except Exception, e:
1005 raise XendError(str(e))
1007 return val
1009 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1010 """Start domain migration.
1012 @param domid: Domain ID or Name
1013 @type domid: int or string.
1014 @param dst: Destination IP address
1015 @type dst: string
1016 @keyword port: relocation port on destination
1017 @type port: int
1018 @keyword live: Live migration
1019 @type live: bool
1020 @keyword resource: not used??
1021 @rtype: None
1022 @raise XendError: Failed to migrate
1023 @raise XendInvalidDomain: Domain is not valid
1024 """
1026 dominfo = self.domain_lookup_nr(domid)
1027 if not dominfo:
1028 raise XendInvalidDomain(str(domid))
1030 if dominfo.getDomid() == DOM0_ID:
1031 raise XendError("Cannot migrate privileged domain %i" % domid)
1033 """ The following call may raise a XendError exception """
1034 dominfo.testMigrateDevices(True, dst)
1036 if live:
1037 """ Make sure there's memory free for enabling shadow mode """
1038 dominfo.checkLiveMigrateMemory()
1040 if port == 0:
1041 port = xroot.get_xend_relocation_port()
1042 try:
1043 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1044 sock.connect((dst, port))
1045 except socket.error, err:
1046 raise XendError("can't connect: %s" % err[1])
1048 sock.send("receive\n")
1049 sock.recv(80)
1050 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1051 dominfo.testDeviceComplete()
1052 sock.close()
1054 def domain_save(self, domid, dst):
1055 """Start saving a domain to file.
1057 @param domid: Domain ID or Name
1058 @type domid: int or string.
1059 @param dst: Destination filename
1060 @type dst: string
1061 @rtype: None
1062 @raise XendError: Failed to save domain
1063 @raise XendInvalidDomain: Domain is not valid
1064 """
1065 try:
1066 dominfo = self.domain_lookup_nr(domid)
1067 if not dominfo:
1068 raise XendInvalidDomain(str(domid))
1070 if dominfo.getDomid() == DOM0_ID:
1071 raise XendError("Cannot save privileged domain %i" % domid)
1073 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1074 try:
1075 # For now we don't support 'live checkpoint'
1076 XendCheckpoint.save(fd, dominfo, False, False, dst)
1077 finally:
1078 os.close(fd)
1079 except OSError, ex:
1080 raise XendError("can't write guest state file %s: %s" %
1081 (dst, ex[1]))
1083 def domain_pincpu(self, domid, vcpu, cpumap):
1084 """Set which cpus vcpu can use
1086 @param domid: Domain ID or Name
1087 @type domid: int or string.
1088 @param vcpu: vcpu to pin to
1089 @type vcpu: int
1090 @param cpumap: string repr of usable cpus
1091 @type cpumap: string
1092 @rtype: 0
1093 """
1094 dominfo = self.domain_lookup_nr(domid)
1095 if not dominfo:
1096 raise XendInvalidDomain(str(domid))
1098 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1099 vcpus = [ vcpu ]
1100 if str(vcpu).lower() == "all":
1101 vcpus = range(0, int(dominfo.getVCpuCount()))
1103 # set the same cpumask for all vcpus
1104 rc = 0
1105 for v in vcpus:
1106 try:
1107 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1108 except Exception, ex:
1109 raise XendError(str(ex))
1110 return rc
1112 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1113 weight):
1114 """Set Simple EDF scheduler parameters for a domain.
1116 @param domid: Domain ID or Name
1117 @type domid: int or string.
1118 @rtype: 0
1119 """
1120 dominfo = self.domain_lookup_nr(domid)
1121 if not dominfo:
1122 raise XendInvalidDomain(str(domid))
1123 try:
1124 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1125 latency, extratime, weight)
1126 except Exception, ex:
1127 raise XendError(str(ex))
1129 def domain_cpu_sedf_get(self, domid):
1130 """Get Simple EDF scheduler parameters for a domain.
1132 @param domid: Domain ID or Name
1133 @type domid: int or string.
1134 @rtype: SXP object
1135 @return: The parameters for Simple EDF schedule for a domain.
1136 """
1137 dominfo = self.domain_lookup_nr(domid)
1138 if not dominfo:
1139 raise XendInvalidDomain(str(domid))
1140 try:
1141 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1142 # return sxpr
1143 return ['sedf',
1144 ['domain', sedf_info['domain']],
1145 ['period', sedf_info['period']],
1146 ['slice', sedf_info['slice']],
1147 ['latency', sedf_info['latency']],
1148 ['extratime', sedf_info['extratime']],
1149 ['weight', sedf_info['weight']]]
1151 except Exception, ex:
1152 raise XendError(str(ex))
1154 def domain_shadow_control(self, domid, op):
1155 """Shadow page control.
1157 @param domid: Domain ID or Name
1158 @type domid: int or string.
1159 @param op: operation
1160 @type op: int
1161 @rtype: 0
1162 """
1163 dominfo = self.domain_lookup(domid)
1164 try:
1165 return xc.shadow_control(dominfo.getDomid(), op)
1166 except Exception, ex:
1167 raise XendError(str(ex))
1169 def domain_shadow_mem_get(self, domid):
1170 """Get shadow pagetable memory allocation.
1172 @param domid: Domain ID or Name
1173 @type domid: int or string.
1174 @rtype: int
1175 @return: shadow memory in MB
1176 """
1177 dominfo = self.domain_lookup(domid)
1178 try:
1179 return xc.shadow_mem_control(dominfo.getDomid())
1180 except Exception, ex:
1181 raise XendError(str(ex))
1183 def domain_shadow_mem_set(self, domid, mb):
1184 """Set shadow pagetable memory allocation.
1186 @param domid: Domain ID or Name
1187 @type domid: int or string.
1188 @param mb: shadow memory to set in MB
1189 @type: mb: int
1190 @rtype: int
1191 @return: shadow memory in MB
1192 """
1193 dominfo = self.domain_lookup(domid)
1194 try:
1195 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1196 except Exception, ex:
1197 raise XendError(str(ex))
1199 def domain_sched_credit_get(self, domid):
1200 """Get credit scheduler parameters for a domain.
1202 @param domid: Domain ID or Name
1203 @type domid: int or string.
1204 @rtype: dict with keys 'weight' and 'cap'
1205 @return: credit scheduler parameters
1206 """
1207 dominfo = self.domain_lookup_nr(domid)
1208 if not dominfo:
1209 raise XendInvalidDomain(str(domid))
1210 try:
1211 return xc.sched_credit_domain_get(dominfo.getDomid())
1212 except Exception, ex:
1213 raise XendError(str(ex))
1215 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1216 """Set credit scheduler parameters for a domain.
1218 @param domid: Domain ID or Name
1219 @type domid: int or string.
1220 @type weight: int
1221 @type cap: int
1222 @rtype: 0
1223 """
1224 dominfo = self.domain_lookup_nr(domid)
1225 if not dominfo:
1226 raise XendInvalidDomain(str(domid))
1227 try:
1228 if weight is None:
1229 weight = int(0)
1230 elif weight < 1 or weight > 65535:
1231 raise XendError("weight is out of range")
1233 if cap is None:
1234 cap = int(~0)
1235 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1236 raise XendError("cap is out of range")
1238 assert type(weight) == int
1239 assert type(cap) == int
1241 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1242 except Exception, ex:
1243 log.exception(ex)
1244 raise XendError(str(ex))
1246 def domain_maxmem_set(self, domid, mem):
1247 """Set the memory limit for a domain.
1249 @param domid: Domain ID or Name
1250 @type domid: int or string.
1251 @param mem: memory limit (in MiB)
1252 @type mem: int
1253 @raise XendError: fail to set memory
1254 @rtype: 0
1255 """
1256 dominfo = self.domain_lookup_nr(domid)
1257 if not dominfo:
1258 raise XendInvalidDomain(str(domid))
1259 maxmem = int(mem) * 1024
1260 try:
1261 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1262 except Exception, ex:
1263 raise XendError(str(ex))
1265 def domain_ioport_range_enable(self, domid, first, last):
1266 """Enable access to a range of IO ports for a domain
1268 @param first: first IO port
1269 @param last: last IO port
1270 @raise XendError: failed to set range
1271 @rtype: 0
1272 """
1273 dominfo = self.domain_lookup_nr(domid)
1274 if not dominfo:
1275 raise XendInvalidDomain(str(domid))
1276 nr_ports = last - first + 1
1277 try:
1278 return xc.domain_ioport_permission(dominfo.getDomid(),
1279 first_port = first,
1280 nr_ports = nr_ports,
1281 allow_access = 1)
1282 except Exception, ex:
1283 raise XendError(str(ex))
1285 def domain_ioport_range_disable(self, domid, first, last):
1286 """Disable access to a range of IO ports for a domain
1288 @param first: first IO port
1289 @param last: last IO port
1290 @raise XendError: failed to set range
1291 @rtype: 0
1292 """
1293 dominfo = self.domain_lookup_nr(domid)
1294 if not dominfo:
1295 raise XendInvalidDomain(str(domid))
1296 nr_ports = last - first + 1
1297 try:
1298 return xc.domain_ioport_permission(dominfo.getDomid(),
1299 first_port = first,
1300 nr_ports = nr_ports,
1301 allow_access = 0)
1302 except Exception, ex:
1303 raise XendError(str(ex))
1306 def instance():
1307 """Singleton constructor. Use this instead of the class constructor.
1308 """
1309 global inst
1310 try:
1311 inst
1312 except:
1313 inst = XendDomain()
1314 inst.init()
1315 return inst