ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 12714:2e5291248789

Fix registration on managed domains on xend restart.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Thu Nov 30 19:11:51 2006 +0000 (2006-11-30)
parents cefb1f761f0b
children 36fe7ca48e54
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, VmError
37 from xen.xend.XendLogging import log
38 from xen.xend.XendAPIConstants import XEN_API_VM_POWER_STATE
39 from xen.xend.XendConstants import XS_VMROOT
40 from xen.xend.XendConstants import DOM_STATE_HALTED, DOM_STATE_PAUSED
41 from xen.xend.XendConstants import DOM_STATE_RUNNING, DOM_STATE_SUSPENDED
42 from xen.xend.XendConstants import DOM_STATE_SHUTDOWN, DOM_STATE_UNKNOWN
43 from xen.xend.XendDevices import XendDevices
45 from xen.xend.xenstore.xstransact import xstransact
46 from xen.xend.xenstore.xswatch import xswatch
47 from xen.util import security
48 from xen.xend import uuid
50 xc = xen.lowlevel.xc.xc()
51 xroot = XendRoot.instance()
53 __all__ = [ "XendDomain" ]
55 CACHED_CONFIG_FILE = 'config.sxp'
56 CHECK_POINT_FILE = 'checkpoint.chk'
57 DOM0_UUID = "00000000-0000-0000-0000-000000000000"
58 DOM0_NAME = "Domain-0"
59 DOM0_ID = 0
61 POWER_STATE_NAMES = dict([(x, XEN_API_VM_POWER_STATE[x])
62 for x in [DOM_STATE_HALTED,
63 DOM_STATE_PAUSED,
64 DOM_STATE_RUNNING,
65 DOM_STATE_SUSPENDED,
66 DOM_STATE_SHUTDOWN,
67 DOM_STATE_UNKNOWN]])
68 POWER_STATE_ALL = 'all'
71 class XendDomain:
72 """Index of all domains. Singleton.
74 @ivar domains: map of domains indexed by domid
75 @type domains: dict of XendDomainInfo
76 @ivar managed_domains: domains that are not running and managed by Xend
77 @type managed_domains: dict of XendDomainInfo indexed by uuid
78 @ivar domains_lock: lock that must be held when manipulating self.domains
79 @type domains_lock: threaading.RLock
80 @ivar _allow_new_domains: Flag to set that allows creating of new domains.
81 @type _allow_new_domains: boolean
82 """
84 def __init__(self):
85 self.domains = {}
86 self.managed_domains = {}
87 self.domains_lock = threading.RLock()
89 # xen api instance vars
90 # TODO: nothing uses this at the moment
91 self._allow_new_domains = True
93 # This must be called only the once, by instance() below. It is separate
94 # from the constructor because XendDomainInfo calls back into this class
95 # in order to check the uniqueness of domain names. This means that
96 # instance() must be able to return a valid instance of this class even
97 # during this initialisation.
98 def init(self):
99 """Singleton initialisation function."""
101 dom_path = self._managed_path()
102 try:
103 os.stat(dom_path)
104 except OSError:
105 log.info("Making %s", dom_path)
106 os.makedirs(dom_path, 0755)
108 xstransact.Mkdir(XS_VMROOT)
109 xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID})
111 self.domains_lock.acquire()
112 try:
113 try:
114 dom0info = [d for d in self._running_domains() \
115 if d.get('domid') == DOM0_ID][0]
117 dom0info['name'] = DOM0_NAME
118 dom0 = XendDomainInfo.recreate(dom0info, True)
119 self._add_domain(dom0)
120 except IndexError:
121 raise XendError('Unable to find Domain 0')
123 self._setDom0CPUCount()
125 # This watch registration needs to be before the refresh call, so
126 # that we're sure that we haven't missed any releases, but inside
127 # the domains_lock, as we don't want the watch to fire until after
128 # the refresh call has completed.
129 xswatch("@introduceDomain", self._on_domains_changed)
130 xswatch("@releaseDomain", self._on_domains_changed)
132 self._init_domains()
133 finally:
134 self.domains_lock.release()
137 def _on_domains_changed(self, _):
138 """ Callback method when xenstore changes.
140 Calls refresh which will keep the local cache of domains
141 in sync.
143 @rtype: int
144 @return: 1
145 """
146 self.domains_lock.acquire()
147 try:
148 self._refresh()
149 finally:
150 self.domains_lock.release()
151 return 1
153 def _init_domains(self):
154 """Does the initial scan of managed and active domains to
155 populate self.domains.
157 Note: L{XendDomainInfo._checkName} will call back into XendDomain
158 to make sure domain name is not a duplicate.
160 """
161 self.domains_lock.acquire()
162 try:
163 running = self._running_domains()
164 managed = self._managed_domains()
166 # add all active domains
167 for dom in running:
168 if dom['dying'] == 1:
169 log.warn('Ignoring dying domain %d from now on' %
170 dom['domid'])
171 continue
173 if dom['domid'] != DOM0_ID:
174 try:
175 new_dom = XendDomainInfo.recreate(dom, False)
176 self._add_domain(new_dom)
177 except Exception:
178 log.exception("Failed to create reference to running "
179 "domain id: %d" % dom['domid'])
181 # add all managed domains as dormant domains.
182 for dom in managed:
183 dom_uuid = dom.get('uuid')
184 if not dom_uuid:
185 continue
187 dom_name = dom.get('name_label', 'Domain-%s' % dom_uuid)
188 try:
189 running_dom = self.domain_lookup_nr(dom_name)
190 if not running_dom:
191 # instantiate domain if not started.
192 new_dom = XendDomainInfo.createDormant(dom)
193 self._managed_domain_register(new_dom)
194 else:
195 self._managed_domain_register(running_dom)
196 except Exception:
197 log.exception("Failed to create reference to managed "
198 "domain: %s" % dom_name)
200 finally:
201 self.domains_lock.release()
204 # -----------------------------------------------------------------
205 # Getting managed domains storage path names
207 def _managed_path(self, domuuid = None):
208 """Returns the path of the directory where managed domain
209 information is stored.
211 @keyword domuuid: If not None, will return the path to the domain
212 otherwise, will return the path containing
213 the directories which represent each domain.
214 @type: None or String.
215 @rtype: String
216 @return: Path.
217 """
218 dom_path = xroot.get_xend_domains_path()
219 if domuuid:
220 dom_path = os.path.join(dom_path, domuuid)
221 return dom_path
223 def _managed_config_path(self, domuuid):
224 """Returns the path to the configuration file of a managed domain.
226 @param domname: Domain uuid
227 @type domname: String
228 @rtype: String
229 @return: path to config file.
230 """
231 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
233 def _managed_check_point_path(self, domuuid):
234 """Returns absolute path to check point file for managed domain.
236 @param domuuid: Name of managed domain
237 @type domname: String
238 @rtype: String
239 @return: Path
240 """
241 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
243 def _managed_config_remove(self, domuuid):
244 """Removes a domain configuration from managed list
246 @param domuuid: Name of managed domain
247 @type domname: String
248 @raise XendError: fails to remove the domain.
249 """
250 config_path = self._managed_path(domuuid)
251 try:
252 if os.path.exists(config_path) and os.path.isdir(config_path):
253 shutil.rmtree(config_path)
254 except IOError:
255 log.exception('managed_config_remove failed removing conf')
256 raise XendError("Unable to remove managed configuration"
257 " for domain: %s" % domuuid)
259 def managed_config_save(self, dominfo):
260 """Save a domain's configuration to disk
262 @param domninfo: Managed domain to save.
263 @type dominfo: XendDomainInfo
264 @raise XendError: fails to save configuration.
265 @rtype: None
266 """
267 if not self.is_domain_managed(dominfo):
268 return # refuse to save configuration this domain isn't managed
270 if dominfo:
271 domains_dir = self._managed_path()
272 dom_uuid = dominfo.get_uuid()
273 domain_config_dir = self._managed_path(dom_uuid)
275 # make sure the domain dir exists
276 if not os.path.exists(domains_dir):
277 os.makedirs(domains_dir, 0755)
278 elif not os.path.isdir(domains_dir):
279 log.error("xend_domain_dir is not a directory.")
280 raise XendError("Unable to save managed configuration "
281 "because %s is not a directory." %
282 domains_dir)
284 if not os.path.exists(domain_config_dir):
285 try:
286 os.makedirs(domain_config_dir, 0755)
287 except IOError:
288 log.exception("Failed to create directory: %s" %
289 domain_config_dir)
290 raise XendError("Failed to create directory: %s" %
291 domain_config_dir)
293 try:
294 sxp_cache_file = open(self._managed_config_path(dom_uuid),'w')
295 prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78)
296 sxp_cache_file.close()
297 except:
298 log.exception("Error occurred saving configuration file " +
299 "to %s" % domain_config_dir)
300 try:
301 self._managed_domain_remove(dom_uuid)
302 except:
303 pass
304 raise XendError("Failed to save configuration file to: %s" %
305 domain_config_dir)
306 else:
307 log.warn("Trying to save configuration for invalid domain")
310 def _managed_domains(self):
311 """ Returns list of domains that are managed.
313 Expects to be protected by domains_lock.
315 @rtype: list of XendConfig
316 @return: List of domain configurations that are managed.
317 """
318 dom_path = self._managed_path()
319 dom_uuids = os.listdir(dom_path)
320 doms = []
321 for dom_uuid in dom_uuids:
322 try:
323 cfg_file = self._managed_config_path(dom_uuid)
324 cfg = XendConfig(filename = cfg_file)
325 if cfg.get('uuid') != dom_uuid:
326 # something is wrong with the SXP
327 log.error("UUID mismatch in stored configuration: %s" %
328 cfg_file)
329 continue
330 doms.append(cfg)
331 except Exception:
332 log.exception('Unable to open or parse config.sxp: %s' % \
333 cfg_file)
334 return doms
336 def _managed_domain_unregister(self, dom):
337 try:
338 if self.is_domain_managed(dom):
339 self._managed_config_remove(dom.get_uuid())
340 del self.managed_domains[dom.get_uuid()]
341 except ValueError:
342 log.warn("Domain is not registered: %s" % dom.get_uuid())
344 def _managed_domain_register(self, dom):
345 self.managed_domains[dom.get_uuid()] = dom
347 def is_domain_managed(self, dom = None):
348 return (dom.get_uuid() in self.managed_domains)
350 # End of Managed Domain Access
351 # --------------------------------------------------------------------
353 def _running_domains(self):
354 """Get table of domains indexed by id from xc.
356 @requires: Expects to be protected by domains_lock.
357 @rtype: list of dicts
358 @return: A list of dicts representing the running domains.
359 """
360 try:
361 return xc.domain_getinfo()
362 except RuntimeError, e:
363 log.exception("Unable to get domain information.")
364 return {}
366 def _setDom0CPUCount(self):
367 """Sets the number of VCPUs dom0 has. Retreived from the
368 Xend configuration, L{XendRoot}.
370 @requires: Expects to be protected by domains_lock.
371 @rtype: None
372 """
373 dom0 = self.privilegedDomain()
375 # get max number of vcpus to use for dom0 from config
376 target = int(xroot.get_dom0_vcpus())
377 log.debug("number of vcpus to use is %d", target)
379 # target == 0 means use all processors
380 if target > 0:
381 dom0.setVCpuCount(target)
384 def _refresh(self):
385 """Refresh the domain list. Needs to be called when
386 either xenstore has changed or when a method requires
387 up to date information (like uptime, cputime stats).
389 Expects to be protected by the domains_lock.
391 @rtype: None
392 """
394 running = self._running_domains()
395 # Add domains that are not already tracked but running in Xen,
396 # and update domain state for those that are running and tracked.
397 for dom in running:
398 domid = dom['domid']
399 if domid in self.domains:
400 self.domains[domid].update(dom)
401 elif domid not in self.domains and dom['dying'] != 1:
402 try:
403 new_dom = XendDomainInfo.recreate(dom, False)
404 self._add_domain(new_dom)
405 except VmError:
406 log.exception("Unable to recreate domain")
407 try:
408 xc.domain_destroy(domid)
409 except:
410 log.exception("Hard destruction of domain failed: %d" %
411 domid)
413 # update information for all running domains
414 # - like cpu_time, status, dying, etc.
415 # remove domains that are not running from active domain list.
416 # The list might have changed by now, because the update call may
417 # cause new domains to be added, if the domain has rebooted. We get
418 # the list again.
419 running = self._running_domains()
420 running_domids = [d['domid'] for d in running if d['dying'] != 1]
421 for domid, dom in self.domains.items():
422 if domid not in running_domids and domid != DOM0_ID:
423 self._remove_domain(dom, domid)
426 def _add_domain(self, info):
427 """Add a domain to the list of running domains
429 @requires: Expects to be protected by the domains_lock.
430 @param info: XendDomainInfo of a domain to be added.
431 @type info: XendDomainInfo
432 """
433 log.debug("Adding Domain: %s" % info.getDomid())
434 self.domains[info.getDomid()] = info
436 # update the managed domains with a new XendDomainInfo object
437 # if we are keeping track of it.
438 if info.get_uuid() in self.managed_domains:
439 self._managed_domain_register(info)
441 def _remove_domain(self, info, domid = None):
442 """Remove the domain from the list of running domains
444 @requires: Expects to be protected by the domains_lock.
445 @param info: XendDomainInfo of a domain to be removed.
446 @type info: XendDomainInfo
447 """
448 if info:
449 if domid == None:
450 domid = info.getDomid()
452 if info.state != DOM_STATE_HALTED:
453 info.cleanupDomain()
455 if domid in self.domains:
456 del self.domains[domid]
457 else:
458 log.warning("Attempted to remove non-existent domain.")
460 def restore_(self, config):
461 """Create a domain as part of the restore process. This is called
462 only from L{XendCheckpoint}.
464 A restore request comes into XendDomain through L{domain_restore}
465 or L{domain_restore_fd}. That request is
466 forwarded immediately to XendCheckpoint which, when it is ready, will
467 call this method. It is necessary to come through here rather than go
468 directly to L{XendDomainInfo.restore} because we need to
469 serialise the domain creation process, but cannot lock
470 domain_restore_fd as a whole, otherwise we will deadlock waiting for
471 the old domain to die.
473 @param config: Configuration of domain to restore
474 @type config: SXP Object (eg. list of lists)
475 """
476 self.domains_lock.acquire()
477 try:
478 security.refresh_ssidref(config)
479 dominfo = XendDomainInfo.restore(config)
480 self._add_domain(dominfo)
481 return dominfo
482 finally:
483 self.domains_lock.release()
486 def domain_lookup(self, domid):
487 """Look up given I{domid} in the list of managed and running
488 domains.
490 @note: Will cause a refresh before lookup up domains, for
491 a version that does not need to re-read xenstore
492 use L{domain_lookup_nr}.
494 @param domid: Domain ID or Domain Name.
495 @type domid: int or string
496 @return: Found domain.
497 @rtype: XendDomainInfo
498 @raise XendError: If domain is not found.
499 """
500 self.domains_lock.acquire()
501 try:
502 self._refresh()
503 dom = self.domain_lookup_nr(domid)
504 if not dom:
505 raise XendError("No domain named '%s'." % str(domid))
506 return dom
507 finally:
508 self.domains_lock.release()
511 def domain_lookup_nr(self, domid):
512 """Look up given I{domid} in the list of managed and running
513 domains.
515 @param domid: Domain ID or Domain Name.
516 @type domid: int or string
517 @return: Found domain.
518 @rtype: XendDomainInfo or None
519 """
520 self.domains_lock.acquire()
521 try:
522 # lookup by name
523 match = [dom for dom in self.domains.values() \
524 if dom.getName() == domid]
525 if match:
526 return match[0]
528 match = [dom for dom in self.managed_domains.values() \
529 if dom.getName() == domid]
530 if match:
531 return match[0]
533 # lookup by id
534 try:
535 if int(domid) in self.domains:
536 return self.domains[int(domid)]
537 except ValueError:
538 pass
540 # lookup by uuid for running domains
541 match = [dom for dom in self.domains.values() \
542 if dom.get_uuid() == domid]
543 if match:
544 return match[0]
546 # lookup by uuid for inactive managed domains
547 if domid in self.managed_domains:
548 return self.managed_domains[domid]
550 return None
551 finally:
552 self.domains_lock.release()
554 def privilegedDomain(self):
555 """ Get the XendDomainInfo of a dom0
557 @rtype: XendDomainInfo
558 """
559 self.domains_lock.acquire()
560 try:
561 return self.domains[DOM0_ID]
562 finally:
563 self.domains_lock.release()
565 def cleanup_domains(self):
566 """Clean up domains that are marked as autostop.
567 Should be called when Xend goes down. This is currently
568 called from L{xen.xend.servers.XMLRPCServer}.
570 """
571 log.debug('cleanup_domains')
572 self.domains_lock.acquire()
573 try:
574 for dom in self.domains.values():
575 if dom.getName() == DOM0_NAME:
576 continue
578 if dom.state == DOM_STATE_RUNNING:
579 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
580 if shutdownAction == 'shutdown':
581 log.debug('Shutting down domain: %s' % dom.getName())
582 dom.shutdown("poweroff")
583 elif shutdownAction == 'suspend':
584 self.domain_suspend(dom.getName())
585 finally:
586 self.domains_lock.release()
590 # ----------------------------------------------------------------
591 # Xen API
594 def set_allow_new_domains(self, allow_new_domains):
595 self._allow_new_domains = allow_new_domains
597 def allow_new_domains(self):
598 return self._allow_new_domains
600 def get_domain_refs(self):
601 result = []
602 try:
603 self.domains_lock.acquire()
604 result = [d.get_uuid() for d in self.domains.values()]
605 result += self.managed_domains.keys()
606 return result
607 finally:
608 self.domains_lock.release()
610 def get_vm_by_uuid(self, vm_uuid):
611 self.domains_lock.acquire()
612 try:
613 for dom in self.domains.values():
614 if dom.get_uuid() == vm_uuid:
615 return dom
617 if vm_uuid in self.managed_domains:
618 return self.managed_domains[vm_uuid]
620 return None
621 finally:
622 self.domains_lock.release()
624 def get_vm_with_dev_uuid(self, klass, dev_uuid):
625 self.domains_lock.acquire()
626 try:
627 for dom in self.domains.values() + self.managed_domains.values():
628 if dom.has_device(klass, dev_uuid):
629 return dom
630 return None
631 finally:
632 self.domains_lock.release()
634 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
635 self.domains_lock.acquire()
636 try:
637 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
638 if not dom:
639 return None
641 value = dom.get_device_property(klass, dev_uuid, field)
642 return value
643 except ValueError, e:
644 pass
646 return None
648 def is_valid_vm(self, vm_ref):
649 return (self.get_vm_by_uuid(vm_ref) != None)
651 def is_valid_dev(self, klass, dev_uuid):
652 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
654 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs):
655 self.domains_lock.acquire()
656 try:
657 for domid, dom in self.domains.items():
658 if dom.get_uuid == vm_uuid:
659 return fn(domid, *args, **kwargs)
661 if vm_uuid in self.managed_domains:
662 domid = self.managed_domains[vm_uuid].getDomid()
663 if domid == None:
664 domid = self.managed_domains[vm_uuid].getName()
665 return fn(domid, *args, **kwargs)
667 raise XendInvalidDomain("Domain does not exist")
668 finally:
669 self.domains_lock.release()
672 def create_domain(self, xenapi_vm):
673 self.domains_lock.acquire()
674 try:
675 try:
676 xeninfo = XendConfig(xapi = xenapi_vm)
677 dominfo = XendDomainInfo.createDormant(xeninfo)
678 log.debug("Creating new managed domain: %s: %s" %
679 (dominfo.getName(), dominfo.get_uuid()))
680 self._managed_domain_register(dominfo)
681 self.managed_config_save(dominfo)
682 return dominfo.get_uuid()
683 except XendError, e:
684 raise
685 except Exception, e:
686 raise XendError(str(e))
687 finally:
688 self.domains_lock.release()
690 def rename_domain(self, dom, new_name):
691 self.domains_lock.acquire()
692 try:
693 old_name = dom.getName()
694 dom.setName(new_name)
696 finally:
697 self.domains_lock.release()
700 #
701 # End of Xen API
702 # ----------------------------------------------------------------
704 # ------------------------------------------------------------
705 # Xen Legacy API
707 def list(self, state = DOM_STATE_RUNNING):
708 """Get list of domain objects.
710 @param: the state in which the VMs should be -- one of the
711 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
712 @return: domains
713 @rtype: list of XendDomainInfo
714 """
715 if type(state) == int:
716 state = POWER_STATE_NAMES[state]
717 state = state.lower()
719 self.domains_lock.acquire()
720 try:
721 self._refresh()
723 # active domains
724 active_domains = self.domains.values()
725 active_uuids = [d.get_uuid() for d in active_domains]
727 # inactive domains
728 inactive_domains = []
729 for dom_uuid, dom in self.managed_domains.items():
730 if dom_uuid not in active_uuids:
731 inactive_domains.append(dom)
733 if state == POWER_STATE_ALL:
734 return active_domains + inactive_domains
735 else:
736 return filter(lambda x:
737 POWER_STATE_NAMES[x.state].lower() == state,
738 active_domains + inactive_domains)
739 finally:
740 self.domains_lock.release()
743 def list_sorted(self, state = DOM_STATE_RUNNING):
744 """Get list of domain objects, sorted by name.
746 @param: the state in which the VMs should be -- one of the
747 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
748 @return: domain objects
749 @rtype: list of XendDomainInfo
750 """
751 doms = self.list(state)
752 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
753 return doms
755 def list_names(self, state = DOM_STATE_RUNNING):
756 """Get list of domain names.
758 @param: the state in which the VMs should be -- one of the
759 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
760 @return: domain names
761 @rtype: list of strings.
762 """
763 return [d.getName() for d in self.list_sorted(state)]
765 def domain_suspend(self, domname):
766 """Suspends a domain that is persistently managed by Xend
768 @param domname: Domain Name
769 @type domname: string
770 @rtype: None
771 @raise XendError: Failure during checkpointing.
772 """
774 try:
775 dominfo = self.domain_lookup_nr(domname)
776 if not dominfo:
777 raise XendInvalidDomain(domname)
779 if dominfo.getDomid() == DOM0_ID:
780 raise XendError("Cannot save privileged domain %s" % domname)
782 if dominfo.state != DOM_STATE_RUNNING:
783 raise XendError("Cannot suspend domain that is not running.")
785 dom_uuid = dominfo.get_uuid()
787 if not os.path.exists(self._managed_config_path(dom_uuid)):
788 raise XendError("Domain is not managed by Xend lifecycle " +
789 "support.")
791 path = self._managed_check_point_path(dom_uuid)
792 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
793 try:
794 # For now we don't support 'live checkpoint'
795 XendCheckpoint.save(fd, dominfo, False, False, path)
796 finally:
797 os.close(fd)
798 except OSError, ex:
799 raise XendError("can't write guest state file %s: %s" %
800 (path, ex[1]))
802 def domain_resume(self, domname, start_paused = False):
803 """Resumes a domain that is persistently managed by Xend.
805 @param domname: Domain Name
806 @type domname: string
807 @rtype: None
808 @raise XendError: If failed to restore.
809 """
810 self.domains_lock.acquire()
811 try:
812 try:
813 dominfo = self.domain_lookup_nr(domname)
815 if not dominfo:
816 raise XendInvalidDomain(domname)
818 if dominfo.getDomid() == DOM0_ID:
819 raise XendError("Cannot save privileged domain %s" % domname)
821 if dominfo.state != DOM_STATE_HALTED:
822 raise XendError("Cannot suspend domain that is not running.")
824 dom_uuid = dominfo.get_uuid()
825 chkpath = self._managed_check_point_path(dom_uuid)
826 if not os.path.exists(chkpath):
827 raise XendError("Domain was not suspended by Xend")
829 # Restore that replaces the existing XendDomainInfo
830 try:
831 log.debug('Current DomainInfo state: %d' % dominfo.state)
832 XendCheckpoint.restore(self,
833 os.open(chkpath, os.O_RDONLY),
834 dominfo,
835 paused = start_paused)
836 self._add_domain(dominfo)
837 os.unlink(chkpath)
838 except OSError, ex:
839 raise XendError("Failed to read stored checkpoint file")
840 except IOError, ex:
841 raise XendError("Failed to delete checkpoint file")
842 except Exception, ex:
843 log.exception("Exception occurred when resuming")
844 raise XendError("Error occurred when resuming: %s" % str(ex))
845 finally:
846 self.domains_lock.release()
849 def domain_create(self, config):
850 """Create a domain from a configuration.
852 @param config: configuration
853 @type config: SXP Object (list of lists)
854 @rtype: XendDomainInfo
855 """
856 self.domains_lock.acquire()
857 try:
858 self._refresh()
860 dominfo = XendDomainInfo.create(config)
861 self._add_domain(dominfo)
862 self.domain_sched_credit_set(dominfo.getDomid(),
863 dominfo.getWeight(),
864 dominfo.getCap())
865 return dominfo
866 finally:
867 self.domains_lock.release()
870 def domain_new(self, config):
871 """Create a domain from a configuration but do not start it.
873 @param config: configuration
874 @type config: SXP Object (list of lists)
875 @rtype: XendDomainInfo
876 """
877 self.domains_lock.acquire()
878 try:
879 try:
880 domconfig = XendConfig(sxp_obj = config)
881 dominfo = XendDomainInfo.createDormant(domconfig)
882 log.debug("Creating new managed domain: %s" %
883 dominfo.getName())
884 self._managed_domain_register(dominfo)
885 self.managed_config_save(dominfo)
886 # no return value because it isn't meaningful for client
887 except XendError, e:
888 raise
889 except Exception, e:
890 raise XendError(str(e))
891 finally:
892 self.domains_lock.release()
894 def domain_start(self, domid, start_paused = True):
895 """Start a managed domain
897 @require: Domain must not be running.
898 @param domid: Domain name or domain ID.
899 @type domid: string or int
900 @rtype: None
901 @raise XendError: If domain is still running
902 @rtype: None
903 """
904 self.domains_lock.acquire()
905 try:
906 self._refresh()
908 dominfo = self.domain_lookup_nr(domid)
909 if not dominfo:
910 raise XendInvalidDomain(str(domid))
912 if dominfo.state != DOM_STATE_HALTED:
913 raise XendError("Domain is already running")
915 dominfo.start(is_managed = True)
916 self._add_domain(dominfo)
917 finally:
918 self.domains_lock.release()
919 dominfo.waitForDevices()
920 if not start_paused:
921 dominfo.unpause()
924 def domain_delete(self, domid):
925 """Remove a managed domain from database
927 @require: Domain must not be running.
928 @param domid: Domain name or domain ID.
929 @type domid: string or int
930 @rtype: None
931 @raise XendError: If domain is still running
932 """
933 self.domains_lock.acquire()
934 try:
935 try:
936 dominfo = self.domain_lookup_nr(domid)
937 if not dominfo:
938 raise XendInvalidDomain(str(domid))
940 if dominfo.state != DOM_STATE_HALTED:
941 raise XendError("Domain is still running")
943 self._managed_domain_unregister(dominfo)
944 self._remove_domain(dominfo)
945 XendDevices.destroy_device_state(dominfo)
946 except Exception, ex:
947 raise XendError(str(ex))
948 finally:
949 self.domains_lock.release()
952 def domain_configure(self, config):
953 """Configure an existing domain.
955 @param vmconfig: vm configuration
956 @type vmconfig: SXP Object (list of lists)
957 @todo: Not implemented
958 """
959 # !!!
960 raise XendError("Unsupported")
962 def domain_restore(self, src, paused=False):
963 """Restore a domain from file.
965 @param src: filename of checkpoint file to restore from
966 @type src: string
967 @return: Restored domain
968 @rtype: XendDomainInfo
969 @raise XendError: Failure to restore domain
970 """
971 try:
972 fd = os.open(src, os.O_RDONLY)
973 try:
974 return self.domain_restore_fd(fd, paused=paused)
975 finally:
976 os.close(fd)
977 except OSError, ex:
978 raise XendError("can't read guest state file %s: %s" %
979 (src, ex[1]))
981 def domain_restore_fd(self, fd, paused=False):
982 """Restore a domain from the given file descriptor.
984 @param fd: file descriptor of the checkpoint file
985 @type fd: File object
986 @rtype: XendDomainInfo
987 @raise XendError: if failed to restore
988 """
990 try:
991 return XendCheckpoint.restore(self, fd, paused=paused)
992 except:
993 # I don't really want to log this exception here, but the error
994 # handling in the relocation-socket handling code (relocate.py) is
995 # poor, so we need to log this for debugging.
996 log.exception("Restore failed")
997 raise XendError("Restore failed")
999 def domain_unpause(self, domid):
1000 """Unpause domain execution.
1002 @param domid: Domain ID or Name
1003 @type domid: int or string.
1004 @rtype: None
1005 @raise XendError: Failed to unpause
1006 @raise XendInvalidDomain: Domain is not valid
1007 """
1008 try:
1009 dominfo = self.domain_lookup_nr(domid)
1010 if not dominfo:
1011 raise XendInvalidDomain(str(domid))
1012 if dominfo.getDomid() == DOM0_ID:
1013 raise XendError("Cannot unpause privileged domain %s" % domid)
1014 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
1015 int(dominfo.getDomid()))
1016 dominfo.unpause()
1017 except XendInvalidDomain:
1018 log.exception("domain_unpause")
1019 raise
1020 except Exception, ex:
1021 log.exception("domain_unpause")
1022 raise XendError(str(ex))
1024 def domain_pause(self, domid):
1025 """Pause domain execution.
1027 @param domid: Domain ID or Name
1028 @type domid: int or string.
1029 @rtype: None
1030 @raise XendError: Failed to pause
1031 @raise XendInvalidDomain: Domain is not valid
1032 """
1033 try:
1034 dominfo = self.domain_lookup_nr(domid)
1035 if not dominfo:
1036 raise XendInvalidDomain(str(domid))
1037 if dominfo.getDomid() == DOM0_ID:
1038 raise XendError("Cannot pause privileged domain %s" % domid)
1039 log.info("Domain %s (%d) paused.", dominfo.getName(),
1040 int(dominfo.getDomid()))
1041 dominfo.pause()
1042 except XendInvalidDomain:
1043 log.exception("domain_pause")
1044 raise
1045 except Exception, ex:
1046 log.exception("domain_pause")
1047 raise XendError(str(ex))
1049 def domain_dump(self, domid, filename, live, crash):
1050 """Dump domain core."""
1052 dominfo = self.domain_lookup_nr(domid)
1053 if not dominfo:
1054 raise XendInvalidDomain(str(domid))
1056 if dominfo.getDomid() == DOM0_ID:
1057 raise XendError("Cannot dump core for privileged domain %s" % domid)
1059 try:
1060 log.info("Domain core dump requested for domain %s (%d) "
1061 "live=%d crash=%d.",
1062 dominfo.getName(), dominfo.getDomid(), live, crash)
1063 return dominfo.dumpCore(filename)
1064 except Exception, ex:
1065 raise XendError(str(ex))
1067 def domain_destroy(self, domid):
1068 """Terminate domain immediately.
1070 @param domid: Domain ID or Name
1071 @type domid: int or string.
1072 @rtype: None
1073 @raise XendError: Failed to destroy
1074 @raise XendInvalidDomain: Domain is not valid
1075 """
1077 dominfo = self.domain_lookup_nr(domid)
1078 if dominfo and dominfo.getDomid() == DOM0_ID:
1079 raise XendError("Cannot destroy privileged domain %s" % domid)
1081 if dominfo:
1082 val = dominfo.destroy()
1083 else:
1084 try:
1085 val = xc.domain_destroy(int(domid))
1086 except ValueError:
1087 raise XendInvalidDomain(domid)
1088 except Exception, e:
1089 raise XendError(str(e))
1091 return val
1093 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1094 """Start domain migration.
1096 @param domid: Domain ID or Name
1097 @type domid: int or string.
1098 @param dst: Destination IP address
1099 @type dst: string
1100 @keyword port: relocation port on destination
1101 @type port: int
1102 @keyword live: Live migration
1103 @type live: bool
1104 @keyword resource: not used??
1105 @rtype: None
1106 @raise XendError: Failed to migrate
1107 @raise XendInvalidDomain: Domain is not valid
1108 """
1110 dominfo = self.domain_lookup_nr(domid)
1111 if not dominfo:
1112 raise XendInvalidDomain(str(domid))
1114 if dominfo.getDomid() == DOM0_ID:
1115 raise XendError("Cannot migrate privileged domain %s" % domid)
1117 """ The following call may raise a XendError exception """
1118 dominfo.testMigrateDevices(True, dst)
1120 if live:
1121 """ Make sure there's memory free for enabling shadow mode """
1122 dominfo.checkLiveMigrateMemory()
1124 if port == 0:
1125 port = xroot.get_xend_relocation_port()
1126 try:
1127 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1128 sock.connect((dst, port))
1129 except socket.error, err:
1130 raise XendError("can't connect: %s" % err[1])
1132 sock.send("receive\n")
1133 sock.recv(80)
1134 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1135 dominfo.testDeviceComplete()
1136 sock.close()
1138 def domain_save(self, domid, dst):
1139 """Start saving a domain to file.
1141 @param domid: Domain ID or Name
1142 @type domid: int or string.
1143 @param dst: Destination filename
1144 @type dst: string
1145 @rtype: None
1146 @raise XendError: Failed to save domain
1147 @raise XendInvalidDomain: Domain is not valid
1148 """
1149 try:
1150 dominfo = self.domain_lookup_nr(domid)
1151 if not dominfo:
1152 raise XendInvalidDomain(str(domid))
1154 if dominfo.getDomid() == DOM0_ID:
1155 raise XendError("Cannot save privileged domain %i" % domid)
1157 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1158 try:
1159 # For now we don't support 'live checkpoint'
1160 XendCheckpoint.save(fd, dominfo, False, False, dst)
1161 finally:
1162 os.close(fd)
1163 except OSError, ex:
1164 raise XendError("can't write guest state file %s: %s" %
1165 (dst, ex[1]))
1167 def domain_pincpu(self, domid, vcpu, cpumap):
1168 """Set which cpus vcpu can use
1170 @param domid: Domain ID or Name
1171 @type domid: int or string.
1172 @param vcpu: vcpu to pin to
1173 @type vcpu: int
1174 @param cpumap: string repr of usable cpus
1175 @type cpumap: string
1176 @rtype: 0
1177 """
1178 dominfo = self.domain_lookup_nr(domid)
1179 if not dominfo:
1180 raise XendInvalidDomain(str(domid))
1182 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1183 vcpus = [ vcpu ]
1184 if str(vcpu).lower() == "all":
1185 vcpus = range(0, int(dominfo.getVCpuCount()))
1187 # set the same cpumask for all vcpus
1188 rc = 0
1189 for v in vcpus:
1190 try:
1191 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1192 except Exception, ex:
1193 raise XendError(str(ex))
1194 return rc
1196 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1197 weight):
1198 """Set Simple EDF scheduler parameters for a domain.
1200 @param domid: Domain ID or Name
1201 @type domid: int or string.
1202 @rtype: 0
1203 """
1204 dominfo = self.domain_lookup_nr(domid)
1205 if not dominfo:
1206 raise XendInvalidDomain(str(domid))
1207 try:
1208 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1209 latency, extratime, weight)
1210 except Exception, ex:
1211 raise XendError(str(ex))
1213 def domain_cpu_sedf_get(self, domid):
1214 """Get Simple EDF scheduler parameters for a domain.
1216 @param domid: Domain ID or Name
1217 @type domid: int or string.
1218 @rtype: SXP object
1219 @return: The parameters for Simple EDF schedule for a domain.
1220 """
1221 dominfo = self.domain_lookup_nr(domid)
1222 if not dominfo:
1223 raise XendInvalidDomain(str(domid))
1224 try:
1225 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1226 # return sxpr
1227 return ['sedf',
1228 ['domid', sedf_info['domid']],
1229 ['period', sedf_info['period']],
1230 ['slice', sedf_info['slice']],
1231 ['latency', sedf_info['latency']],
1232 ['extratime', sedf_info['extratime']],
1233 ['weight', sedf_info['weight']]]
1235 except Exception, ex:
1236 raise XendError(str(ex))
1238 def domain_shadow_control(self, domid, op):
1239 """Shadow page control.
1241 @param domid: Domain ID or Name
1242 @type domid: int or string.
1243 @param op: operation
1244 @type op: int
1245 @rtype: 0
1246 """
1247 dominfo = self.domain_lookup(domid)
1248 try:
1249 return xc.shadow_control(dominfo.getDomid(), op)
1250 except Exception, ex:
1251 raise XendError(str(ex))
1253 def domain_shadow_mem_get(self, domid):
1254 """Get shadow pagetable memory allocation.
1256 @param domid: Domain ID or Name
1257 @type domid: int or string.
1258 @rtype: int
1259 @return: shadow memory in MB
1260 """
1261 dominfo = self.domain_lookup(domid)
1262 try:
1263 return xc.shadow_mem_control(dominfo.getDomid())
1264 except Exception, ex:
1265 raise XendError(str(ex))
1267 def domain_shadow_mem_set(self, domid, mb):
1268 """Set shadow pagetable memory allocation.
1270 @param domid: Domain ID or Name
1271 @type domid: int or string.
1272 @param mb: shadow memory to set in MB
1273 @type: mb: int
1274 @rtype: int
1275 @return: shadow memory in MB
1276 """
1277 dominfo = self.domain_lookup(domid)
1278 try:
1279 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1280 except Exception, ex:
1281 raise XendError(str(ex))
1283 def domain_sched_credit_get(self, domid):
1284 """Get credit scheduler parameters for a domain.
1286 @param domid: Domain ID or Name
1287 @type domid: int or string.
1288 @rtype: dict with keys 'weight' and 'cap'
1289 @return: credit scheduler parameters
1290 """
1291 dominfo = self.domain_lookup_nr(domid)
1292 if not dominfo:
1293 raise XendInvalidDomain(str(domid))
1294 try:
1295 return xc.sched_credit_domain_get(dominfo.getDomid())
1296 except Exception, ex:
1297 raise XendError(str(ex))
1299 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1300 """Set credit scheduler parameters for a domain.
1302 @param domid: Domain ID or Name
1303 @type domid: int or string.
1304 @type weight: int
1305 @type cap: int
1306 @rtype: 0
1307 """
1308 dominfo = self.domain_lookup_nr(domid)
1309 if not dominfo:
1310 raise XendInvalidDomain(str(domid))
1311 try:
1312 if weight is None:
1313 weight = int(0)
1314 elif weight < 1 or weight > 65535:
1315 raise XendError("weight is out of range")
1317 if cap is None:
1318 cap = int(~0)
1319 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1320 raise XendError("cap is out of range")
1322 assert type(weight) == int
1323 assert type(cap) == int
1325 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1326 except Exception, ex:
1327 log.exception(ex)
1328 raise XendError(str(ex))
1330 def domain_maxmem_set(self, domid, mem):
1331 """Set the memory limit for a domain.
1333 @param domid: Domain ID or Name
1334 @type domid: int or string.
1335 @param mem: memory limit (in MiB)
1336 @type mem: int
1337 @raise XendError: fail to set memory
1338 @rtype: 0
1339 """
1340 dominfo = self.domain_lookup_nr(domid)
1341 if not dominfo:
1342 raise XendInvalidDomain(str(domid))
1343 maxmem = int(mem) * 1024
1344 try:
1345 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1346 except Exception, ex:
1347 raise XendError(str(ex))
1349 def domain_ioport_range_enable(self, domid, first, last):
1350 """Enable access to a range of IO ports for a domain
1352 @param first: first IO port
1353 @param last: last IO port
1354 @raise XendError: failed to set range
1355 @rtype: 0
1356 """
1357 dominfo = self.domain_lookup_nr(domid)
1358 if not dominfo:
1359 raise XendInvalidDomain(str(domid))
1360 nr_ports = last - first + 1
1361 try:
1362 return xc.domain_ioport_permission(dominfo.getDomid(),
1363 first_port = first,
1364 nr_ports = nr_ports,
1365 allow_access = 1)
1366 except Exception, ex:
1367 raise XendError(str(ex))
1369 def domain_ioport_range_disable(self, domid, first, last):
1370 """Disable access to a range of IO ports for a domain
1372 @param first: first IO port
1373 @param last: last IO port
1374 @raise XendError: failed to set range
1375 @rtype: 0
1376 """
1377 dominfo = self.domain_lookup_nr(domid)
1378 if not dominfo:
1379 raise XendInvalidDomain(str(domid))
1380 nr_ports = last - first + 1
1381 try:
1382 return xc.domain_ioport_permission(dominfo.getDomid(),
1383 first_port = first,
1384 nr_ports = nr_ports,
1385 allow_access = 0)
1386 except Exception, ex:
1387 raise XendError(str(ex))
1390 def instance():
1391 """Singleton constructor. Use this instead of the class constructor.
1392 """
1393 global inst
1394 try:
1395 inst
1396 except:
1397 inst = XendDomain()
1398 inst.init()
1399 return inst