ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 12342:de7c20b6eaae

[XEND] Ignore dying domains when refreshing list of domains.

Also cleanup stored configuration if managed domain is deleted.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Nov 09 16:05:00 2006 +0000 (2006-11-09)
parents a14732833603
children 40a9f315b708
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))
952 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
953 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 log.info("Domain %s (%d) paused.", dominfo.getName(),
977 int(dominfo.getDomid()))
978 dominfo.pause()
979 except XendInvalidDomain:
980 log.exception("domain_pause")
981 raise
982 except Exception, ex:
983 log.exception("domain_pause")
984 raise XendError(str(ex))
986 def domain_dump(self, domid, filename, live, crash):
987 """Dump domain core."""
989 dominfo = self.domain_lookup_nr(domid)
990 if not dominfo:
991 raise XendInvalidDomain(str(domid))
993 if dominfo.getDomid() == DOM0_ID:
994 raise XendError("Cannot dump core for privileged domain %s" % domid)
996 try:
997 log.info("Domain core dump requested for domain %s (%d) "
998 "live=%d crash=%d.",
999 dominfo.getName(), dominfo.getDomid(), live, crash)
1000 return dominfo.dumpCore(filename)
1001 except Exception, ex:
1002 raise XendError(str(ex))
1004 def domain_destroy(self, domid):
1005 """Terminate domain immediately.
1007 @param domid: Domain ID or Name
1008 @type domid: int or string.
1009 @rtype: None
1010 @raise XendError: Failed to destroy
1011 @raise XendInvalidDomain: Domain is not valid
1012 """
1014 dominfo = self.domain_lookup_nr(domid)
1015 if dominfo and dominfo.getDomid() == DOM0_ID:
1016 raise XendError("Cannot destroy privileged domain %s" % domid)
1018 if dominfo:
1019 val = dominfo.destroy()
1020 else:
1021 try:
1022 val = xc.domain_destroy(int(domid))
1023 except ValueError:
1024 raise XendInvalidDomain(domid)
1025 except Exception, e:
1026 raise XendError(str(e))
1028 return val
1030 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1031 """Start domain migration.
1033 @param domid: Domain ID or Name
1034 @type domid: int or string.
1035 @param dst: Destination IP address
1036 @type dst: string
1037 @keyword port: relocation port on destination
1038 @type port: int
1039 @keyword live: Live migration
1040 @type live: bool
1041 @keyword resource: not used??
1042 @rtype: None
1043 @raise XendError: Failed to migrate
1044 @raise XendInvalidDomain: Domain is not valid
1045 """
1047 dominfo = self.domain_lookup_nr(domid)
1048 if not dominfo:
1049 raise XendInvalidDomain(str(domid))
1051 if dominfo.getDomid() == DOM0_ID:
1052 raise XendError("Cannot migrate privileged domain %i" % domid)
1054 """ The following call may raise a XendError exception """
1055 dominfo.testMigrateDevices(True, dst)
1057 if live:
1058 """ Make sure there's memory free for enabling shadow mode """
1059 dominfo.checkLiveMigrateMemory()
1061 if port == 0:
1062 port = xroot.get_xend_relocation_port()
1063 try:
1064 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1065 sock.connect((dst, port))
1066 except socket.error, err:
1067 raise XendError("can't connect: %s" % err[1])
1069 sock.send("receive\n")
1070 sock.recv(80)
1071 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1072 dominfo.testDeviceComplete()
1073 sock.close()
1075 def domain_save(self, domid, dst):
1076 """Start saving a domain to file.
1078 @param domid: Domain ID or Name
1079 @type domid: int or string.
1080 @param dst: Destination filename
1081 @type dst: string
1082 @rtype: None
1083 @raise XendError: Failed to save domain
1084 @raise XendInvalidDomain: Domain is not valid
1085 """
1086 try:
1087 dominfo = self.domain_lookup_nr(domid)
1088 if not dominfo:
1089 raise XendInvalidDomain(str(domid))
1091 if dominfo.getDomid() == DOM0_ID:
1092 raise XendError("Cannot save privileged domain %i" % domid)
1094 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1095 try:
1096 # For now we don't support 'live checkpoint'
1097 XendCheckpoint.save(fd, dominfo, False, False, dst)
1098 finally:
1099 os.close(fd)
1100 except OSError, ex:
1101 raise XendError("can't write guest state file %s: %s" %
1102 (dst, ex[1]))
1104 def domain_pincpu(self, domid, vcpu, cpumap):
1105 """Set which cpus vcpu can use
1107 @param domid: Domain ID or Name
1108 @type domid: int or string.
1109 @param vcpu: vcpu to pin to
1110 @type vcpu: int
1111 @param cpumap: string repr of usable cpus
1112 @type cpumap: string
1113 @rtype: 0
1114 """
1115 dominfo = self.domain_lookup_nr(domid)
1116 if not dominfo:
1117 raise XendInvalidDomain(str(domid))
1119 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1120 vcpus = [ vcpu ]
1121 if str(vcpu).lower() == "all":
1122 vcpus = range(0, int(dominfo.getVCpuCount()))
1124 # set the same cpumask for all vcpus
1125 rc = 0
1126 for v in vcpus:
1127 try:
1128 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1129 except Exception, ex:
1130 raise XendError(str(ex))
1131 return rc
1133 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1134 weight):
1135 """Set Simple EDF scheduler parameters for a domain.
1137 @param domid: Domain ID or Name
1138 @type domid: int or string.
1139 @rtype: 0
1140 """
1141 dominfo = self.domain_lookup_nr(domid)
1142 if not dominfo:
1143 raise XendInvalidDomain(str(domid))
1144 try:
1145 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1146 latency, extratime, weight)
1147 except Exception, ex:
1148 raise XendError(str(ex))
1150 def domain_cpu_sedf_get(self, domid):
1151 """Get Simple EDF scheduler parameters for a domain.
1153 @param domid: Domain ID or Name
1154 @type domid: int or string.
1155 @rtype: SXP object
1156 @return: The parameters for Simple EDF schedule for a domain.
1157 """
1158 dominfo = self.domain_lookup_nr(domid)
1159 if not dominfo:
1160 raise XendInvalidDomain(str(domid))
1161 try:
1162 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1163 # return sxpr
1164 return ['sedf',
1165 ['domain', sedf_info['domain']],
1166 ['period', sedf_info['period']],
1167 ['slice', sedf_info['slice']],
1168 ['latency', sedf_info['latency']],
1169 ['extratime', sedf_info['extratime']],
1170 ['weight', sedf_info['weight']]]
1172 except Exception, ex:
1173 raise XendError(str(ex))
1175 def domain_shadow_control(self, domid, op):
1176 """Shadow page control.
1178 @param domid: Domain ID or Name
1179 @type domid: int or string.
1180 @param op: operation
1181 @type op: int
1182 @rtype: 0
1183 """
1184 dominfo = self.domain_lookup(domid)
1185 try:
1186 return xc.shadow_control(dominfo.getDomid(), op)
1187 except Exception, ex:
1188 raise XendError(str(ex))
1190 def domain_shadow_mem_get(self, domid):
1191 """Get shadow pagetable memory allocation.
1193 @param domid: Domain ID or Name
1194 @type domid: int or string.
1195 @rtype: int
1196 @return: shadow memory in MB
1197 """
1198 dominfo = self.domain_lookup(domid)
1199 try:
1200 return xc.shadow_mem_control(dominfo.getDomid())
1201 except Exception, ex:
1202 raise XendError(str(ex))
1204 def domain_shadow_mem_set(self, domid, mb):
1205 """Set shadow pagetable memory allocation.
1207 @param domid: Domain ID or Name
1208 @type domid: int or string.
1209 @param mb: shadow memory to set in MB
1210 @type: mb: int
1211 @rtype: int
1212 @return: shadow memory in MB
1213 """
1214 dominfo = self.domain_lookup(domid)
1215 try:
1216 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1217 except Exception, ex:
1218 raise XendError(str(ex))
1220 def domain_sched_credit_get(self, domid):
1221 """Get credit scheduler parameters for a domain.
1223 @param domid: Domain ID or Name
1224 @type domid: int or string.
1225 @rtype: dict with keys 'weight' and 'cap'
1226 @return: credit scheduler parameters
1227 """
1228 dominfo = self.domain_lookup_nr(domid)
1229 if not dominfo:
1230 raise XendInvalidDomain(str(domid))
1231 try:
1232 return xc.sched_credit_domain_get(dominfo.getDomid())
1233 except Exception, ex:
1234 raise XendError(str(ex))
1236 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1237 """Set credit scheduler parameters for a domain.
1239 @param domid: Domain ID or Name
1240 @type domid: int or string.
1241 @type weight: int
1242 @type cap: int
1243 @rtype: 0
1244 """
1245 dominfo = self.domain_lookup_nr(domid)
1246 if not dominfo:
1247 raise XendInvalidDomain(str(domid))
1248 try:
1249 if weight is None:
1250 weight = int(0)
1251 elif weight < 1 or weight > 65535:
1252 raise XendError("weight is out of range")
1254 if cap is None:
1255 cap = int(~0)
1256 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1257 raise XendError("cap is out of range")
1259 assert type(weight) == int
1260 assert type(cap) == int
1262 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1263 except Exception, ex:
1264 log.exception(ex)
1265 raise XendError(str(ex))
1267 def domain_maxmem_set(self, domid, mem):
1268 """Set the memory limit for a domain.
1270 @param domid: Domain ID or Name
1271 @type domid: int or string.
1272 @param mem: memory limit (in MiB)
1273 @type mem: int
1274 @raise XendError: fail to set memory
1275 @rtype: 0
1276 """
1277 dominfo = self.domain_lookup_nr(domid)
1278 if not dominfo:
1279 raise XendInvalidDomain(str(domid))
1280 maxmem = int(mem) * 1024
1281 try:
1282 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1283 except Exception, ex:
1284 raise XendError(str(ex))
1286 def domain_ioport_range_enable(self, domid, first, last):
1287 """Enable access to a range of IO ports for a domain
1289 @param first: first IO port
1290 @param last: last IO port
1291 @raise XendError: failed to set range
1292 @rtype: 0
1293 """
1294 dominfo = self.domain_lookup_nr(domid)
1295 if not dominfo:
1296 raise XendInvalidDomain(str(domid))
1297 nr_ports = last - first + 1
1298 try:
1299 return xc.domain_ioport_permission(dominfo.getDomid(),
1300 first_port = first,
1301 nr_ports = nr_ports,
1302 allow_access = 1)
1303 except Exception, ex:
1304 raise XendError(str(ex))
1306 def domain_ioport_range_disable(self, domid, first, last):
1307 """Disable access to a range of IO ports for a domain
1309 @param first: first IO port
1310 @param last: last IO port
1311 @raise XendError: failed to set range
1312 @rtype: 0
1313 """
1314 dominfo = self.domain_lookup_nr(domid)
1315 if not dominfo:
1316 raise XendInvalidDomain(str(domid))
1317 nr_ports = last - first + 1
1318 try:
1319 return xc.domain_ioport_permission(dominfo.getDomid(),
1320 first_port = first,
1321 nr_ports = nr_ports,
1322 allow_access = 0)
1323 except Exception, ex:
1324 raise XendError(str(ex))
1327 def instance():
1328 """Singleton constructor. Use this instead of the class constructor.
1329 """
1330 global inst
1331 try:
1332 inst
1333 except:
1334 inst = XendDomain()
1335 inst.init()
1336 return inst