ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 12642:33951d547223

Plumb the "start_paused" flag through for VM.start and VM.resume. Do not
unpause the VM on start by default. This should fix problems seen recently
whereby devices are not attached to the VM by the time they boot, as
xm create was expecting to be able to wait for the devices before unpausing.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Nov 28 17:28:43 2006 +0000 (2006-11-28)
parents bbcaa0cad3d2
children bd5ebf33f222
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', '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)
427 def _add_domain(self, info):
428 """Add a domain to the list of running domains
430 @requires: Expects to be protected by the domains_lock.
431 @param info: XendDomainInfo of a domain to be added.
432 @type info: XendDomainInfo
433 """
434 log.debug("Adding Domain: %s" % info.getDomid())
435 self.domains[info.getDomid()] = info
437 def _remove_domain(self, info, domid = None):
438 """Remove the domain from the list of running domains
440 @requires: Expects to be protected by the domains_lock.
441 @param info: XendDomainInfo of a domain to be removed.
442 @type info: XendDomainInfo
443 """
444 if info:
445 if domid == None:
446 domid = info.getDomid()
448 if info.state != DOM_STATE_HALTED:
449 info.cleanupDomain()
451 if domid in self.domains:
452 del self.domains[domid]
453 else:
454 log.warning("Attempted to remove non-existent domain.")
456 def restore_(self, config):
457 """Create a domain as part of the restore process. This is called
458 only from L{XendCheckpoint}.
460 A restore request comes into XendDomain through L{domain_restore}
461 or L{domain_restore_fd}. That request is
462 forwarded immediately to XendCheckpoint which, when it is ready, will
463 call this method. It is necessary to come through here rather than go
464 directly to L{XendDomainInfo.restore} because we need to
465 serialise the domain creation process, but cannot lock
466 domain_restore_fd as a whole, otherwise we will deadlock waiting for
467 the old domain to die.
469 @param config: Configuration of domain to restore
470 @type config: SXP Object (eg. list of lists)
471 """
472 self.domains_lock.acquire()
473 try:
474 security.refresh_ssidref(config)
475 dominfo = XendDomainInfo.restore(config)
476 self._add_domain(dominfo)
477 return dominfo
478 finally:
479 self.domains_lock.release()
482 def domain_lookup(self, domid):
483 """Look up given I{domid} in the list of managed and running
484 domains.
486 @note: Will cause a refresh before lookup up domains, for
487 a version that does not need to re-read xenstore
488 use L{domain_lookup_nr}.
490 @param domid: Domain ID or Domain Name.
491 @type domid: int or string
492 @return: Found domain.
493 @rtype: XendDomainInfo
494 @raise XendError: If domain is not found.
495 """
496 self.domains_lock.acquire()
497 try:
498 self._refresh()
499 dom = self.domain_lookup_nr(domid)
500 if not dom:
501 raise XendError("No domain named '%s'." % str(domid))
502 return dom
503 finally:
504 self.domains_lock.release()
507 def domain_lookup_nr(self, domid):
508 """Look up given I{domid} in the list of managed and running
509 domains.
511 @param domid: Domain ID or Domain Name.
512 @type domid: int or string
513 @return: Found domain.
514 @rtype: XendDomainInfo or None
515 """
516 self.domains_lock.acquire()
517 try:
518 # lookup by name
519 match = [dom for dom in self.domains.values() \
520 if dom.getName() == domid]
521 if match:
522 return match[0]
524 match = [dom for dom in self.managed_domains.values() \
525 if dom.getName() == domid]
526 if match:
527 return match[0]
529 # lookup by id
530 try:
531 if int(domid) in self.domains:
532 return self.domains[int(domid)]
533 except ValueError:
534 pass
536 # lookup by uuid for running domains
537 match = [dom for dom in self.domains.values() \
538 if dom.get_uuid() == domid]
539 if match:
540 return match[0]
542 # lookup by uuid for inactive managed domains
543 if domid in self.managed_domains:
544 return self.managed_domains[domid]
546 return None
547 finally:
548 self.domains_lock.release()
550 def privilegedDomain(self):
551 """ Get the XendDomainInfo of a dom0
553 @rtype: XendDomainInfo
554 """
555 self.domains_lock.acquire()
556 try:
557 return self.domains[DOM0_ID]
558 finally:
559 self.domains_lock.release()
561 def cleanup_domains(self):
562 """Clean up domains that are marked as autostop.
563 Should be called when Xend goes down. This is currently
564 called from L{xen.xend.servers.XMLRPCServer}.
566 """
567 log.debug('cleanup_domains')
568 self.domains_lock.acquire()
569 try:
570 for dom in self.domains.values():
571 if dom.getName() == DOM0_NAME:
572 continue
574 if dom.state == DOM_STATE_RUNNING:
575 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
576 if shutdownAction == 'shutdown':
577 log.debug('Shutting down domain: %s' % dom.getName())
578 dom.shutdown("poweroff")
579 elif shutdownAction == 'suspend':
580 self.domain_suspend(dom.getName())
581 finally:
582 self.domains_lock.release()
586 # ----------------------------------------------------------------
587 # Xen API
590 def set_allow_new_domains(self, allow_new_domains):
591 self._allow_new_domains = allow_new_domains
593 def allow_new_domains(self):
594 return self._allow_new_domains
596 def get_domain_refs(self):
597 result = []
598 try:
599 self.domains_lock.acquire()
600 result = [d.get_uuid() for d in self.domains.values()]
601 result += self.managed_domains.keys()
602 return result
603 finally:
604 self.domains_lock.release()
606 def get_vm_by_uuid(self, vm_uuid):
607 self.domains_lock.acquire()
608 try:
609 for dom in self.domains.values():
610 if dom.get_uuid() == vm_uuid:
611 return dom
613 if vm_uuid in self.managed_domains:
614 return self.managed_domains[vm_uuid]
616 return None
617 finally:
618 self.domains_lock.release()
620 def get_vm_with_dev_uuid(self, klass, dev_uuid):
621 self.domains_lock.acquire()
622 try:
623 for dom in self.domains.values() + self.managed_domains.values():
624 if dom.has_device(klass, dev_uuid):
625 return dom
626 return None
627 finally:
628 self.domains_lock.release()
630 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
631 self.domains_lock.acquire()
632 try:
633 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
634 if not dom:
635 return None
637 value = dom.get_device_property(klass, dev_uuid, field)
638 return value
639 except ValueError, e:
640 pass
642 return None
644 def is_valid_vm(self, vm_ref):
645 return (self.get_vm_by_uuid(vm_ref) != None)
647 def is_valid_dev(self, klass, dev_uuid):
648 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
650 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs):
651 self.domains_lock.acquire()
652 try:
653 for domid, dom in self.domains.items():
654 if dom.get_uuid == vm_uuid:
655 return fn(domid, *args, **kwargs)
657 if vm_uuid in self.managed_domains:
658 domid = self.managed_domains[vm_uuid].getDomid()
659 if domid == None:
660 domid = self.managed_domains[vm_uuid].getName()
661 return fn(domid, *args, **kwargs)
663 raise XendInvalidDomain("Domain does not exist")
664 finally:
665 self.domains_lock.release()
668 def create_domain(self, xenapi_vm):
669 self.domains_lock.acquire()
670 try:
671 try:
672 xeninfo = XendConfig(xenapi_vm = xenapi_vm)
673 dominfo = XendDomainInfo.createDormant(xeninfo)
674 log.debug("Creating new managed domain: %s: %s" %
675 (dominfo.getName(), dominfo.get_uuid()))
676 self._managed_domain_register(dominfo)
677 self.managed_config_save(dominfo)
678 return dominfo.get_uuid()
679 except XendError, e:
680 raise
681 except Exception, e:
682 raise XendError(str(e))
683 finally:
684 self.domains_lock.release()
686 def rename_domain(self, dom, new_name):
687 self.domains_lock.acquire()
688 try:
689 old_name = dom.getName()
690 dom.setName(new_name)
692 finally:
693 self.domains_lock.release()
696 #
697 # End of Xen API
698 # ----------------------------------------------------------------
700 # ------------------------------------------------------------
701 # Xen Legacy API
703 def list(self, state = DOM_STATE_RUNNING):
704 """Get list of domain objects.
706 @param: the state in which the VMs should be -- one of the
707 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
708 @return: domains
709 @rtype: list of XendDomainInfo
710 """
711 if type(state) == int:
712 state = POWER_STATE_NAMES[state]
713 state = state.lower()
715 self.domains_lock.acquire()
716 try:
717 self._refresh()
719 # active domains
720 active_domains = self.domains.values()
721 active_uuids = [d.get_uuid() for d in active_domains]
723 # inactive domains
724 inactive_domains = []
725 for dom_uuid, dom in self.managed_domains.items():
726 if dom_uuid not in active_uuids:
727 inactive_domains.append(dom)
729 if state == POWER_STATE_ALL:
730 return active_domains + inactive_domains
731 else:
732 return filter(lambda x:
733 POWER_STATE_NAMES[x.state].lower() == state,
734 active_domains + inactive_domains)
735 finally:
736 self.domains_lock.release()
739 def list_sorted(self, state = DOM_STATE_RUNNING):
740 """Get list of domain objects, sorted by name.
742 @param: the state in which the VMs should be -- one of the
743 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
744 @return: domain objects
745 @rtype: list of XendDomainInfo
746 """
747 doms = self.list(state)
748 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
749 return doms
751 def list_names(self, state = DOM_STATE_RUNNING):
752 """Get list of domain names.
754 @param: the state in which the VMs should be -- one of the
755 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
756 @return: domain names
757 @rtype: list of strings.
758 """
759 return [d.getName() for d in self.list_sorted(state)]
761 def domain_suspend(self, domname):
762 """Suspends a domain that is persistently managed by Xend
764 @param domname: Domain Name
765 @type domname: string
766 @rtype: None
767 @raise XendError: Failure during checkpointing.
768 """
770 try:
771 dominfo = self.domain_lookup_nr(domname)
772 if not dominfo:
773 raise XendInvalidDomain(domname)
775 if dominfo.getDomid() == DOM0_ID:
776 raise XendError("Cannot save privileged domain %s" % domname)
778 if dominfo.state != DOM_STATE_RUNNING:
779 raise XendError("Cannot suspend domain that is not running.")
781 dom_uuid = dominfo.get_uuid()
783 if not os.path.exists(self._managed_config_path(dom_uuid)):
784 raise XendError("Domain is not managed by Xend lifecycle " +
785 "support.")
787 path = self._managed_check_point_path(dom_uuid)
788 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
789 try:
790 # For now we don't support 'live checkpoint'
791 XendCheckpoint.save(fd, dominfo, False, False, path)
792 finally:
793 os.close(fd)
794 except OSError, ex:
795 raise XendError("can't write guest state file %s: %s" %
796 (path, ex[1]))
798 def domain_resume(self, domname, start_paused = False):
799 """Resumes a domain that is persistently managed by Xend.
801 @param domname: Domain Name
802 @type domname: string
803 @rtype: None
804 @raise XendError: If failed to restore.
805 """
806 self.domains_lock.acquire()
807 try:
808 try:
809 dominfo = self.domain_lookup_nr(domname)
811 if not dominfo:
812 raise XendInvalidDomain(domname)
814 if dominfo.getDomid() == DOM0_ID:
815 raise XendError("Cannot save privileged domain %s" % domname)
817 if dominfo.state != DOM_STATE_HALTED:
818 raise XendError("Cannot suspend domain that is not running.")
820 dom_uuid = dominfo.get_uuid()
821 chkpath = self._managed_check_point_path(dom_uuid)
822 if not os.path.exists(chkpath):
823 raise XendError("Domain was not suspended by Xend")
825 # Restore that replaces the existing XendDomainInfo
826 try:
827 log.debug('Current DomainInfo state: %d' % dominfo.state)
828 XendCheckpoint.restore(self,
829 os.open(chkpath, os.O_RDONLY),
830 dominfo,
831 paused = start_paused)
832 self._add_domain(dominfo)
833 os.unlink(chkpath)
834 except OSError, ex:
835 raise XendError("Failed to read stored checkpoint file")
836 except IOError, ex:
837 raise XendError("Failed to delete checkpoint file")
838 except Exception, ex:
839 log.exception("Exception occurred when resuming")
840 raise XendError("Error occurred when resuming: %s" % str(ex))
841 finally:
842 self.domains_lock.release()
845 def domain_create(self, config):
846 """Create a domain from a configuration.
848 @param config: configuration
849 @type config: SXP Object (list of lists)
850 @rtype: XendDomainInfo
851 """
852 self.domains_lock.acquire()
853 try:
854 self._refresh()
856 dominfo = XendDomainInfo.create(config)
857 self._add_domain(dominfo)
858 self.domain_sched_credit_set(dominfo.getDomid(),
859 dominfo.getWeight(),
860 dominfo.getCap())
861 return dominfo
862 finally:
863 self.domains_lock.release()
866 def domain_new(self, config):
867 """Create a domain from a configuration but do not start it.
869 @param config: configuration
870 @type config: SXP Object (list of lists)
871 @rtype: XendDomainInfo
872 """
873 self.domains_lock.acquire()
874 try:
875 try:
876 xeninfo = XendConfig(sxp = config)
877 dominfo = XendDomainInfo.createDormant(xeninfo)
878 log.debug("Creating new managed domain: %s" %
879 dominfo.getName())
880 self._managed_domain_register(dominfo)
881 self.managed_config_save(dominfo)
882 # no return value because it isn't meaningful for client
883 except XendError, e:
884 raise
885 except Exception, e:
886 raise XendError(str(e))
887 finally:
888 self.domains_lock.release()
890 def domain_start(self, domid, start_paused = True):
891 """Start a managed domain
893 @require: Domain must not be running.
894 @param domid: Domain name or domain ID.
895 @type domid: string or int
896 @rtype: None
897 @raise XendError: If domain is still running
898 @rtype: None
899 """
900 self.domains_lock.acquire()
901 try:
902 self._refresh()
904 dominfo = self.domain_lookup_nr(domid)
905 if not dominfo:
906 raise XendInvalidDomain(str(domid))
908 if dominfo.state != DOM_STATE_HALTED:
909 raise XendError("Domain is already running")
911 dominfo.start(is_managed = True, start_paused = start_paused)
912 self._add_domain(dominfo)
913 finally:
914 self.domains_lock.release()
917 def domain_delete(self, domid):
918 """Remove a managed domain from database
920 @require: Domain must not be running.
921 @param domid: Domain name or domain ID.
922 @type domid: string or int
923 @rtype: None
924 @raise XendError: If domain is still running
925 """
926 self.domains_lock.acquire()
927 try:
928 try:
929 dominfo = self.domain_lookup_nr(domid)
930 if not dominfo:
931 raise XendInvalidDomain(str(domid))
933 if dominfo.state != DOM_STATE_HALTED:
934 raise XendError("Domain is still running")
936 self._managed_domain_unregister(dominfo)
937 self._remove_domain(dominfo)
938 XendDevices.destroy_device_state(dominfo)
939 except Exception, ex:
940 raise XendError(str(ex))
941 finally:
942 self.domains_lock.release()
945 def domain_configure(self, config):
946 """Configure an existing domain.
948 @param vmconfig: vm configuration
949 @type vmconfig: SXP Object (list of lists)
950 @todo: Not implemented
951 """
952 # !!!
953 raise XendError("Unsupported")
955 def domain_restore(self, src, paused=False):
956 """Restore a domain from file.
958 @param src: filename of checkpoint file to restore from
959 @type src: string
960 @return: Restored domain
961 @rtype: XendDomainInfo
962 @raise XendError: Failure to restore domain
963 """
964 try:
965 fd = os.open(src, os.O_RDONLY)
966 try:
967 return self.domain_restore_fd(fd, paused=paused)
968 finally:
969 os.close(fd)
970 except OSError, ex:
971 raise XendError("can't read guest state file %s: %s" %
972 (src, ex[1]))
974 def domain_restore_fd(self, fd, paused=False):
975 """Restore a domain from the given file descriptor.
977 @param fd: file descriptor of the checkpoint file
978 @type fd: File object
979 @rtype: XendDomainInfo
980 @raise XendError: if failed to restore
981 """
983 try:
984 return XendCheckpoint.restore(self, fd, paused=paused)
985 except:
986 # I don't really want to log this exception here, but the error
987 # handling in the relocation-socket handling code (relocate.py) is
988 # poor, so we need to log this for debugging.
989 log.exception("Restore failed")
990 raise XendError("Restore failed")
992 def domain_unpause(self, domid):
993 """Unpause domain execution.
995 @param domid: Domain ID or Name
996 @type domid: int or string.
997 @rtype: None
998 @raise XendError: Failed to unpause
999 @raise XendInvalidDomain: Domain is not valid
1000 """
1001 try:
1002 dominfo = self.domain_lookup_nr(domid)
1003 if not dominfo:
1004 raise XendInvalidDomain(str(domid))
1005 if dominfo.getDomid() == DOM0_ID:
1006 raise XendError("Cannot unpause privileged domain %s" % domid)
1007 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
1008 int(dominfo.getDomid()))
1009 dominfo.unpause()
1010 except XendInvalidDomain:
1011 log.exception("domain_unpause")
1012 raise
1013 except Exception, ex:
1014 log.exception("domain_unpause")
1015 raise XendError(str(ex))
1017 def domain_pause(self, domid):
1018 """Pause domain execution.
1020 @param domid: Domain ID or Name
1021 @type domid: int or string.
1022 @rtype: None
1023 @raise XendError: Failed to pause
1024 @raise XendInvalidDomain: Domain is not valid
1025 """
1026 try:
1027 dominfo = self.domain_lookup_nr(domid)
1028 if not dominfo:
1029 raise XendInvalidDomain(str(domid))
1030 if dominfo.getDomid() == DOM0_ID:
1031 raise XendError("Cannot pause privileged domain %s" % domid)
1032 log.info("Domain %s (%d) paused.", dominfo.getName(),
1033 int(dominfo.getDomid()))
1034 dominfo.pause()
1035 except XendInvalidDomain:
1036 log.exception("domain_pause")
1037 raise
1038 except Exception, ex:
1039 log.exception("domain_pause")
1040 raise XendError(str(ex))
1042 def domain_dump(self, domid, filename, live, crash):
1043 """Dump domain core."""
1045 dominfo = self.domain_lookup_nr(domid)
1046 if not dominfo:
1047 raise XendInvalidDomain(str(domid))
1049 if dominfo.getDomid() == DOM0_ID:
1050 raise XendError("Cannot dump core for privileged domain %s" % domid)
1052 try:
1053 log.info("Domain core dump requested for domain %s (%d) "
1054 "live=%d crash=%d.",
1055 dominfo.getName(), dominfo.getDomid(), live, crash)
1056 return dominfo.dumpCore(filename)
1057 except Exception, ex:
1058 raise XendError(str(ex))
1060 def domain_destroy(self, domid):
1061 """Terminate domain immediately.
1063 @param domid: Domain ID or Name
1064 @type domid: int or string.
1065 @rtype: None
1066 @raise XendError: Failed to destroy
1067 @raise XendInvalidDomain: Domain is not valid
1068 """
1070 dominfo = self.domain_lookup_nr(domid)
1071 if dominfo and dominfo.getDomid() == DOM0_ID:
1072 raise XendError("Cannot destroy privileged domain %s" % domid)
1074 if dominfo:
1075 val = dominfo.destroy()
1076 else:
1077 try:
1078 val = xc.domain_destroy(int(domid))
1079 except ValueError:
1080 raise XendInvalidDomain(domid)
1081 except Exception, e:
1082 raise XendError(str(e))
1084 return val
1086 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1087 """Start domain migration.
1089 @param domid: Domain ID or Name
1090 @type domid: int or string.
1091 @param dst: Destination IP address
1092 @type dst: string
1093 @keyword port: relocation port on destination
1094 @type port: int
1095 @keyword live: Live migration
1096 @type live: bool
1097 @keyword resource: not used??
1098 @rtype: None
1099 @raise XendError: Failed to migrate
1100 @raise XendInvalidDomain: Domain is not valid
1101 """
1103 dominfo = self.domain_lookup_nr(domid)
1104 if not dominfo:
1105 raise XendInvalidDomain(str(domid))
1107 if dominfo.getDomid() == DOM0_ID:
1108 raise XendError("Cannot migrate privileged domain %s" % domid)
1110 """ The following call may raise a XendError exception """
1111 dominfo.testMigrateDevices(True, dst)
1113 if live:
1114 """ Make sure there's memory free for enabling shadow mode """
1115 dominfo.checkLiveMigrateMemory()
1117 if port == 0:
1118 port = xroot.get_xend_relocation_port()
1119 try:
1120 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1121 sock.connect((dst, port))
1122 except socket.error, err:
1123 raise XendError("can't connect: %s" % err[1])
1125 sock.send("receive\n")
1126 sock.recv(80)
1127 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1128 dominfo.testDeviceComplete()
1129 sock.close()
1131 def domain_save(self, domid, dst):
1132 """Start saving a domain to file.
1134 @param domid: Domain ID or Name
1135 @type domid: int or string.
1136 @param dst: Destination filename
1137 @type dst: string
1138 @rtype: None
1139 @raise XendError: Failed to save domain
1140 @raise XendInvalidDomain: Domain is not valid
1141 """
1142 try:
1143 dominfo = self.domain_lookup_nr(domid)
1144 if not dominfo:
1145 raise XendInvalidDomain(str(domid))
1147 if dominfo.getDomid() == DOM0_ID:
1148 raise XendError("Cannot save privileged domain %i" % domid)
1150 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1151 try:
1152 # For now we don't support 'live checkpoint'
1153 XendCheckpoint.save(fd, dominfo, False, False, dst)
1154 finally:
1155 os.close(fd)
1156 except OSError, ex:
1157 raise XendError("can't write guest state file %s: %s" %
1158 (dst, ex[1]))
1160 def domain_pincpu(self, domid, vcpu, cpumap):
1161 """Set which cpus vcpu can use
1163 @param domid: Domain ID or Name
1164 @type domid: int or string.
1165 @param vcpu: vcpu to pin to
1166 @type vcpu: int
1167 @param cpumap: string repr of usable cpus
1168 @type cpumap: string
1169 @rtype: 0
1170 """
1171 dominfo = self.domain_lookup_nr(domid)
1172 if not dominfo:
1173 raise XendInvalidDomain(str(domid))
1175 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1176 vcpus = [ vcpu ]
1177 if str(vcpu).lower() == "all":
1178 vcpus = range(0, int(dominfo.getVCpuCount()))
1180 # set the same cpumask for all vcpus
1181 rc = 0
1182 for v in vcpus:
1183 try:
1184 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1185 except Exception, ex:
1186 raise XendError(str(ex))
1187 return rc
1189 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1190 weight):
1191 """Set Simple EDF scheduler parameters for a domain.
1193 @param domid: Domain ID or Name
1194 @type domid: int or string.
1195 @rtype: 0
1196 """
1197 dominfo = self.domain_lookup_nr(domid)
1198 if not dominfo:
1199 raise XendInvalidDomain(str(domid))
1200 try:
1201 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1202 latency, extratime, weight)
1203 except Exception, ex:
1204 raise XendError(str(ex))
1206 def domain_cpu_sedf_get(self, domid):
1207 """Get Simple EDF scheduler parameters for a domain.
1209 @param domid: Domain ID or Name
1210 @type domid: int or string.
1211 @rtype: SXP object
1212 @return: The parameters for Simple EDF schedule for a domain.
1213 """
1214 dominfo = self.domain_lookup_nr(domid)
1215 if not dominfo:
1216 raise XendInvalidDomain(str(domid))
1217 try:
1218 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1219 # return sxpr
1220 return ['sedf',
1221 ['domid', sedf_info['domid']],
1222 ['period', sedf_info['period']],
1223 ['slice', sedf_info['slice']],
1224 ['latency', sedf_info['latency']],
1225 ['extratime', sedf_info['extratime']],
1226 ['weight', sedf_info['weight']]]
1228 except Exception, ex:
1229 raise XendError(str(ex))
1231 def domain_shadow_control(self, domid, op):
1232 """Shadow page control.
1234 @param domid: Domain ID or Name
1235 @type domid: int or string.
1236 @param op: operation
1237 @type op: int
1238 @rtype: 0
1239 """
1240 dominfo = self.domain_lookup(domid)
1241 try:
1242 return xc.shadow_control(dominfo.getDomid(), op)
1243 except Exception, ex:
1244 raise XendError(str(ex))
1246 def domain_shadow_mem_get(self, domid):
1247 """Get shadow pagetable memory allocation.
1249 @param domid: Domain ID or Name
1250 @type domid: int or string.
1251 @rtype: int
1252 @return: shadow memory in MB
1253 """
1254 dominfo = self.domain_lookup(domid)
1255 try:
1256 return xc.shadow_mem_control(dominfo.getDomid())
1257 except Exception, ex:
1258 raise XendError(str(ex))
1260 def domain_shadow_mem_set(self, domid, mb):
1261 """Set shadow pagetable memory allocation.
1263 @param domid: Domain ID or Name
1264 @type domid: int or string.
1265 @param mb: shadow memory to set in MB
1266 @type: mb: int
1267 @rtype: int
1268 @return: shadow memory in MB
1269 """
1270 dominfo = self.domain_lookup(domid)
1271 try:
1272 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1273 except Exception, ex:
1274 raise XendError(str(ex))
1276 def domain_sched_credit_get(self, domid):
1277 """Get credit scheduler parameters for a domain.
1279 @param domid: Domain ID or Name
1280 @type domid: int or string.
1281 @rtype: dict with keys 'weight' and 'cap'
1282 @return: credit scheduler parameters
1283 """
1284 dominfo = self.domain_lookup_nr(domid)
1285 if not dominfo:
1286 raise XendInvalidDomain(str(domid))
1287 try:
1288 return xc.sched_credit_domain_get(dominfo.getDomid())
1289 except Exception, ex:
1290 raise XendError(str(ex))
1292 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1293 """Set credit scheduler parameters for a domain.
1295 @param domid: Domain ID or Name
1296 @type domid: int or string.
1297 @type weight: int
1298 @type cap: int
1299 @rtype: 0
1300 """
1301 dominfo = self.domain_lookup_nr(domid)
1302 if not dominfo:
1303 raise XendInvalidDomain(str(domid))
1304 try:
1305 if weight is None:
1306 weight = int(0)
1307 elif weight < 1 or weight > 65535:
1308 raise XendError("weight is out of range")
1310 if cap is None:
1311 cap = int(~0)
1312 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1313 raise XendError("cap is out of range")
1315 assert type(weight) == int
1316 assert type(cap) == int
1318 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1319 except Exception, ex:
1320 log.exception(ex)
1321 raise XendError(str(ex))
1323 def domain_maxmem_set(self, domid, mem):
1324 """Set the memory limit for a domain.
1326 @param domid: Domain ID or Name
1327 @type domid: int or string.
1328 @param mem: memory limit (in MiB)
1329 @type mem: int
1330 @raise XendError: fail to set memory
1331 @rtype: 0
1332 """
1333 dominfo = self.domain_lookup_nr(domid)
1334 if not dominfo:
1335 raise XendInvalidDomain(str(domid))
1336 maxmem = int(mem) * 1024
1337 try:
1338 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1339 except Exception, ex:
1340 raise XendError(str(ex))
1342 def domain_ioport_range_enable(self, domid, first, last):
1343 """Enable access to a range of IO ports for a domain
1345 @param first: first IO port
1346 @param last: last IO port
1347 @raise XendError: failed to set range
1348 @rtype: 0
1349 """
1350 dominfo = self.domain_lookup_nr(domid)
1351 if not dominfo:
1352 raise XendInvalidDomain(str(domid))
1353 nr_ports = last - first + 1
1354 try:
1355 return xc.domain_ioport_permission(dominfo.getDomid(),
1356 first_port = first,
1357 nr_ports = nr_ports,
1358 allow_access = 1)
1359 except Exception, ex:
1360 raise XendError(str(ex))
1362 def domain_ioport_range_disable(self, domid, first, last):
1363 """Disable access to a range of IO ports for a domain
1365 @param first: first IO port
1366 @param last: last IO port
1367 @raise XendError: failed to set range
1368 @rtype: 0
1369 """
1370 dominfo = self.domain_lookup_nr(domid)
1371 if not dominfo:
1372 raise XendInvalidDomain(str(domid))
1373 nr_ports = last - first + 1
1374 try:
1375 return xc.domain_ioport_permission(dominfo.getDomid(),
1376 first_port = first,
1377 nr_ports = nr_ports,
1378 allow_access = 0)
1379 except Exception, ex:
1380 raise XendError(str(ex))
1383 def instance():
1384 """Singleton constructor. Use this instead of the class constructor.
1385 """
1386 global inst
1387 try:
1388 inst
1389 except:
1390 inst = XendDomain()
1391 inst.init()
1392 return inst