debuggers.hg

view tools/python/xen/xend/XendDomain.py @ 13698:5d9b72e640e0

Fix 'xm create' and 'xm start' when SEDF scheduler is used.

If the Xen is running with the sedf scheduler, the following functions
do not call the domain_sched_credit_set().
- domain_create()
- domain_create_from_dict()
- domain_start()

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author kfraser@localhost.localdomain
date Mon Jan 29 10:55:20 2007 +0000 (2007-01-29)
parents fd57cef459dc
children ce97d6714be4
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 stat
27 import shutil
28 import socket
29 import tempfile
30 import threading
32 import xen.lowlevel.xc
35 from xen.xend import XendOptions, XendCheckpoint, XendDomainInfo, XendNode
36 from xen.xend.PrettyPrint import prettyprint
37 from xen.xend.XendConfig import XendConfig
38 from xen.xend.XendError import XendError, XendInvalidDomain, VmError
39 from xen.xend.XendError import VMBadState
40 from xen.xend.XendLogging import log
41 from xen.xend.XendAPIConstants import XEN_API_VM_POWER_STATE
42 from xen.xend.XendConstants import XS_VMROOT
43 from xen.xend.XendConstants import DOM_STATE_HALTED, DOM_STATE_PAUSED
44 from xen.xend.XendConstants import DOM_STATE_RUNNING, DOM_STATE_SUSPENDED
45 from xen.xend.XendConstants import DOM_STATE_SHUTDOWN, DOM_STATE_UNKNOWN
46 from xen.xend.XendDevices import XendDevices
48 from xen.xend.xenstore.xstransact import xstransact
49 from xen.xend.xenstore.xswatch import xswatch
50 from xen.util import mkdir, security
51 from xen.xend import uuid
53 xc = xen.lowlevel.xc.xc()
54 xoptions = XendOptions.instance()
56 __all__ = [ "XendDomain" ]
58 CACHED_CONFIG_FILE = 'config.sxp'
59 CHECK_POINT_FILE = 'checkpoint.chk'
60 DOM0_UUID = "00000000-0000-0000-0000-000000000000"
61 DOM0_NAME = "Domain-0"
62 DOM0_ID = 0
64 POWER_STATE_NAMES = dict([(x, XEN_API_VM_POWER_STATE[x])
65 for x in [DOM_STATE_HALTED,
66 DOM_STATE_PAUSED,
67 DOM_STATE_RUNNING,
68 DOM_STATE_SUSPENDED,
69 DOM_STATE_SHUTDOWN,
70 DOM_STATE_UNKNOWN]])
71 POWER_STATE_ALL = 'all'
74 class XendDomain:
75 """Index of all domains. Singleton.
77 @ivar domains: map of domains indexed by domid
78 @type domains: dict of XendDomainInfo
79 @ivar managed_domains: domains that are not running and managed by Xend
80 @type managed_domains: dict of XendDomainInfo indexed by uuid
81 @ivar domains_lock: lock that must be held when manipulating self.domains
82 @type domains_lock: threaading.RLock
83 @ivar _allow_new_domains: Flag to set that allows creating of new domains.
84 @type _allow_new_domains: boolean
85 """
87 def __init__(self):
88 self.domains = {}
89 self.managed_domains = {}
90 self.domains_lock = threading.RLock()
92 # xen api instance vars
93 # TODO: nothing uses this at the moment
94 self._allow_new_domains = True
96 # This must be called only the once, by instance() below. It is separate
97 # from the constructor because XendDomainInfo calls back into this class
98 # in order to check the uniqueness of domain names. This means that
99 # instance() must be able to return a valid instance of this class even
100 # during this initialisation.
101 def init(self):
102 """Singleton initialisation function."""
104 dom_path = self._managed_path()
105 mkdir.parents(dom_path, stat.S_IRWXU)
107 xstransact.Mkdir(XS_VMROOT)
108 xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID})
110 self.domains_lock.acquire()
111 try:
112 try:
113 dom0info = [d for d in self._running_domains() \
114 if d.get('domid') == DOM0_ID][0]
116 dom0info['name'] = DOM0_NAME
117 dom0 = XendDomainInfo.recreate(dom0info, True)
118 except IndexError:
119 raise XendError('Unable to find Domain 0')
121 self._setDom0CPUCount()
123 # This watch registration needs to be before the refresh call, so
124 # that we're sure that we haven't missed any releases, but inside
125 # the domains_lock, as we don't want the watch to fire until after
126 # the refresh call has completed.
127 xswatch("@introduceDomain", self._on_domains_changed)
128 xswatch("@releaseDomain", self._on_domains_changed)
130 self._init_domains()
131 finally:
132 self.domains_lock.release()
135 def _on_domains_changed(self, _):
136 """ Callback method when xenstore changes.
138 Calls refresh which will keep the local cache of domains
139 in sync.
141 @rtype: int
142 @return: 1
143 """
144 self.domains_lock.acquire()
145 try:
146 self._refresh()
147 finally:
148 self.domains_lock.release()
149 return 1
151 def _init_domains(self):
152 """Does the initial scan of managed and active domains to
153 populate self.domains.
155 Note: L{XendDomainInfo._checkName} will call back into XendDomain
156 to make sure domain name is not a duplicate.
158 """
159 self.domains_lock.acquire()
160 try:
161 running = self._running_domains()
162 managed = self._managed_domains()
164 # add all active domains
165 for dom in running:
166 if dom['dying'] == 1:
167 log.warn('Ignoring dying domain %d from now on' %
168 dom['domid'])
169 continue
171 if dom['domid'] != DOM0_ID:
172 try:
173 new_dom = XendDomainInfo.recreate(dom, False)
174 except Exception:
175 log.exception("Failed to create reference to running "
176 "domain id: %d" % dom['domid'])
178 # add all managed domains as dormant domains.
179 for dom in managed:
180 dom_uuid = dom.get('uuid')
181 if not dom_uuid:
182 continue
184 dom_name = dom.get('name_label', 'Domain-%s' % dom_uuid)
185 try:
186 running_dom = self.domain_lookup_nr(dom_name)
187 if not running_dom:
188 # instantiate domain if not started.
189 new_dom = XendDomainInfo.createDormant(dom)
190 self._managed_domain_register(new_dom)
191 else:
192 self._managed_domain_register(running_dom)
193 except Exception:
194 log.exception("Failed to create reference to managed "
195 "domain: %s" % dom_name)
197 finally:
198 self.domains_lock.release()
201 # -----------------------------------------------------------------
202 # Getting managed domains storage path names
204 def _managed_path(self, domuuid = None):
205 """Returns the path of the directory where managed domain
206 information is stored.
208 @keyword domuuid: If not None, will return the path to the domain
209 otherwise, will return the path containing
210 the directories which represent each domain.
211 @type: None or String.
212 @rtype: String
213 @return: Path.
214 """
215 dom_path = xoptions.get_xend_domains_path()
216 if domuuid:
217 dom_path = os.path.join(dom_path, domuuid)
218 return dom_path
220 def _managed_config_path(self, domuuid):
221 """Returns the path to the configuration file of a managed domain.
223 @param domname: Domain uuid
224 @type domname: String
225 @rtype: String
226 @return: path to config file.
227 """
228 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
230 def _managed_check_point_path(self, domuuid):
231 """Returns absolute path to check point file for managed domain.
233 @param domuuid: Name of managed domain
234 @type domname: String
235 @rtype: String
236 @return: Path
237 """
238 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
240 def _managed_config_remove(self, domuuid):
241 """Removes a domain configuration from managed list
243 @param domuuid: Name of managed domain
244 @type domname: String
245 @raise XendError: fails to remove the domain.
246 """
247 config_path = self._managed_path(domuuid)
248 try:
249 if os.path.exists(config_path) and os.path.isdir(config_path):
250 shutil.rmtree(config_path)
251 except IOError:
252 log.exception('managed_config_remove failed removing conf')
253 raise XendError("Unable to remove managed configuration"
254 " for domain: %s" % domuuid)
256 def managed_config_save(self, dominfo):
257 """Save a domain's configuration to disk
259 @param domninfo: Managed domain to save.
260 @type dominfo: XendDomainInfo
261 @raise XendError: fails to save configuration.
262 @rtype: None
263 """
264 if not self.is_domain_managed(dominfo):
265 return # refuse to save configuration this domain isn't managed
267 if dominfo:
268 domains_dir = self._managed_path()
269 dom_uuid = dominfo.get_uuid()
270 domain_config_dir = self._managed_path(dom_uuid)
272 def make_or_raise(path):
273 try:
274 mkdir.parents(path, stat.S_IRWXU)
275 except:
276 log.exception("%s could not be created." % path)
277 raise XendError("%s could not be created." % path)
279 make_or_raise(domains_dir)
280 make_or_raise(domain_config_dir)
282 try:
283 fd, fn = tempfile.mkstemp()
284 f = os.fdopen(fd, 'w+b')
285 try:
286 prettyprint(dominfo.sxpr(legacy_only = False), f,
287 width = 78)
288 finally:
289 f.close()
290 try:
291 os.rename(fn, self._managed_config_path(dom_uuid))
292 except:
293 log.exception("Renaming %s" % fn)
294 os.remove(fn)
295 except:
296 log.exception("Error occurred saving configuration file " +
297 "to %s" % domain_config_dir)
298 raise XendError("Failed to save configuration file to: %s" %
299 domain_config_dir)
300 else:
301 log.warn("Trying to save configuration for invalid domain")
304 def _managed_domains(self):
305 """ Returns list of domains that are managed.
307 Expects to be protected by domains_lock.
309 @rtype: list of XendConfig
310 @return: List of domain configurations that are managed.
311 """
312 dom_path = self._managed_path()
313 dom_uuids = os.listdir(dom_path)
314 doms = []
315 for dom_uuid in dom_uuids:
316 try:
317 cfg_file = self._managed_config_path(dom_uuid)
318 cfg = XendConfig(filename = cfg_file)
319 if cfg.get('uuid') != dom_uuid:
320 # something is wrong with the SXP
321 log.error("UUID mismatch in stored configuration: %s" %
322 cfg_file)
323 continue
324 doms.append(cfg)
325 except Exception:
326 log.exception('Unable to open or parse config.sxp: %s' % \
327 cfg_file)
328 return doms
330 def _managed_domain_unregister(self, dom):
331 try:
332 if self.is_domain_managed(dom):
333 self._managed_config_remove(dom.get_uuid())
334 del self.managed_domains[dom.get_uuid()]
335 except ValueError:
336 log.warn("Domain is not registered: %s" % dom.get_uuid())
338 def _managed_domain_register(self, dom):
339 self.managed_domains[dom.get_uuid()] = dom
341 def is_domain_managed(self, dom = None):
342 return (dom.get_uuid() in self.managed_domains)
344 # End of Managed Domain Access
345 # --------------------------------------------------------------------
347 def _running_domains(self):
348 """Get table of domains indexed by id from xc.
350 @requires: Expects to be protected by domains_lock.
351 @rtype: list of dicts
352 @return: A list of dicts representing the running domains.
353 """
354 try:
355 return xc.domain_getinfo()
356 except RuntimeError, e:
357 log.exception("Unable to get domain information.")
358 return {}
360 def _setDom0CPUCount(self):
361 """Sets the number of VCPUs dom0 has. Retreived from the
362 Xend configuration, L{XendOptions}.
364 @requires: Expects to be protected by domains_lock.
365 @rtype: None
366 """
367 dom0 = self.privilegedDomain()
369 # get max number of vcpus to use for dom0 from config
370 target = int(xoptions.get_dom0_vcpus())
371 log.debug("number of vcpus to use is %d", target)
373 # target == 0 means use all processors
374 if target > 0:
375 dom0.setVCpuCount(target)
378 def _refresh(self, refresh_shutdown = True):
379 """Refresh the domain list. Needs to be called when
380 either xenstore has changed or when a method requires
381 up to date information (like uptime, cputime stats).
383 Expects to be protected by the domains_lock.
385 @rtype: None
386 """
388 running = self._running_domains()
389 # Add domains that are not already tracked but running in Xen,
390 # and update domain state for those that are running and tracked.
391 for dom in running:
392 domid = dom['domid']
393 if domid in self.domains:
394 self.domains[domid].update(dom, refresh_shutdown)
395 elif domid not in self.domains and dom['dying'] != 1:
396 try:
397 new_dom = XendDomainInfo.recreate(dom, False)
398 except VmError:
399 log.exception("Unable to recreate domain")
400 try:
401 xc.domain_destroy(domid)
402 except:
403 log.exception("Hard destruction of domain failed: %d" %
404 domid)
406 # update information for all running domains
407 # - like cpu_time, status, dying, etc.
408 # remove domains that are not running from active domain list.
409 # The list might have changed by now, because the update call may
410 # cause new domains to be added, if the domain has rebooted. We get
411 # the list again.
412 running = self._running_domains()
413 running_domids = [d['domid'] for d in running if d['dying'] != 1]
414 for domid, dom in self.domains.items():
415 if domid not in running_domids and domid != DOM0_ID:
416 self.remove_domain(dom, domid)
419 def add_domain(self, info):
420 """Add a domain to the list of running domains
422 @requires: Expects to be protected by the domains_lock.
423 @param info: XendDomainInfo of a domain to be added.
424 @type info: XendDomainInfo
425 """
426 log.debug("Adding Domain: %s" % info.getDomid())
427 self.domains[info.getDomid()] = info
429 # update the managed domains with a new XendDomainInfo object
430 # if we are keeping track of it.
431 if info.get_uuid() in self.managed_domains:
432 self._managed_domain_register(info)
434 def remove_domain(self, info, domid = None):
435 """Remove the domain from the list of running domains
437 @requires: Expects to be protected by the domains_lock.
438 @param info: XendDomainInfo of a domain to be removed.
439 @type info: XendDomainInfo
440 """
441 if info:
442 if domid == None:
443 domid = info.getDomid()
445 if info.state != DOM_STATE_HALTED:
446 info.cleanupDomain()
448 if domid in self.domains:
449 del self.domains[domid]
450 else:
451 log.warning("Attempted to remove non-existent domain.")
453 def restore_(self, config):
454 """Create a domain as part of the restore process. This is called
455 only from L{XendCheckpoint}.
457 A restore request comes into XendDomain through L{domain_restore}
458 or L{domain_restore_fd}. That request is
459 forwarded immediately to XendCheckpoint which, when it is ready, will
460 call this method. It is necessary to come through here rather than go
461 directly to L{XendDomainInfo.restore} because we need to
462 serialise the domain creation process, but cannot lock
463 domain_restore_fd as a whole, otherwise we will deadlock waiting for
464 the old domain to die.
466 @param config: Configuration of domain to restore
467 @type config: SXP Object (eg. list of lists)
468 """
469 self.domains_lock.acquire()
470 try:
471 security.refresh_ssidref(config)
472 dominfo = XendDomainInfo.restore(config)
473 return dominfo
474 finally:
475 self.domains_lock.release()
478 def domain_lookup(self, domid):
479 """Look up given I{domid} in the list of managed and running
480 domains.
482 @note: Will cause a refresh before lookup up domains, for
483 a version that does not need to re-read xenstore
484 use L{domain_lookup_nr}.
486 @param domid: Domain ID or Domain Name.
487 @type domid: int or string
488 @return: Found domain.
489 @rtype: XendDomainInfo
490 @raise XendError: If domain is not found.
491 """
492 self.domains_lock.acquire()
493 try:
494 self._refresh(refresh_shutdown = False)
495 dom = self.domain_lookup_nr(domid)
496 if not dom:
497 raise XendError("No domain named '%s'." % str(domid))
498 return dom
499 finally:
500 self.domains_lock.release()
503 def domain_lookup_nr(self, domid):
504 """Look up given I{domid} in the list of managed and running
505 domains.
507 @param domid: Domain ID or Domain Name.
508 @type domid: int or string
509 @return: Found domain.
510 @rtype: XendDomainInfo or None
511 """
512 self.domains_lock.acquire()
513 try:
514 # lookup by name
515 match = [dom for dom in self.domains.values() \
516 if dom.getName() == domid]
517 if match:
518 return match[0]
520 match = [dom for dom in self.managed_domains.values() \
521 if dom.getName() == domid]
522 if match:
523 return match[0]
525 # lookup by id
526 try:
527 if int(domid) in self.domains:
528 return self.domains[int(domid)]
529 except ValueError:
530 pass
532 # lookup by uuid for running domains
533 match = [dom for dom in self.domains.values() \
534 if dom.get_uuid() == domid]
535 if match:
536 return match[0]
538 # lookup by uuid for inactive managed domains
539 if domid in self.managed_domains:
540 return self.managed_domains[domid]
542 return None
543 finally:
544 self.domains_lock.release()
546 def privilegedDomain(self):
547 """ Get the XendDomainInfo of a dom0
549 @rtype: XendDomainInfo
550 """
551 self.domains_lock.acquire()
552 try:
553 return self.domains[DOM0_ID]
554 finally:
555 self.domains_lock.release()
557 def cleanup_domains(self):
558 """Clean up domains that are marked as autostop.
559 Should be called when Xend goes down. This is currently
560 called from L{xen.xend.servers.XMLRPCServer}.
562 """
563 log.debug('cleanup_domains')
564 self.domains_lock.acquire()
565 try:
566 for dom in self.domains.values():
567 if dom.getName() == DOM0_NAME:
568 continue
570 if dom.state == DOM_STATE_RUNNING:
571 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
572 if shutdownAction == 'shutdown':
573 log.debug('Shutting down domain: %s' % dom.getName())
574 dom.shutdown("poweroff")
575 elif shutdownAction == 'suspend':
576 self.domain_suspend(dom.getName())
577 finally:
578 self.domains_lock.release()
582 # ----------------------------------------------------------------
583 # Xen API
586 def set_allow_new_domains(self, allow_new_domains):
587 self._allow_new_domains = allow_new_domains
589 def allow_new_domains(self):
590 return self._allow_new_domains
592 def get_domain_refs(self):
593 result = []
594 try:
595 self.domains_lock.acquire()
596 result = [d.get_uuid() for d in self.domains.values()]
597 for d in self.managed_domains.keys():
598 if d not in result:
599 result.append(d)
600 return result
601 finally:
602 self.domains_lock.release()
604 def get_all_vms(self):
605 self.domains_lock.acquire()
606 try:
607 result = self.domains.values()
608 result += [x for x in self.managed_domains.values() if
609 x not in result]
610 return result
611 finally:
612 self.domains_lock.release()
614 def get_vm_by_uuid(self, vm_uuid):
615 self.domains_lock.acquire()
616 try:
617 for dom in self.domains.values():
618 if dom.get_uuid() == vm_uuid:
619 return dom
621 if vm_uuid in self.managed_domains:
622 return self.managed_domains[vm_uuid]
624 return None
625 finally:
626 self.domains_lock.release()
628 def get_vm_with_dev_uuid(self, klass, dev_uuid):
629 self.domains_lock.acquire()
630 try:
631 for dom in self.domains.values() + self.managed_domains.values():
632 if dom.has_device(klass, dev_uuid):
633 return dom
634 return None
635 finally:
636 self.domains_lock.release()
638 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
639 value = None
640 self.domains_lock.acquire()
641 try:
642 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
643 if dom:
644 value = dom.get_dev_property(klass, dev_uuid, field)
645 except ValueError, e:
646 pass
648 self.domains_lock.release()
650 return value
652 def is_valid_vm(self, vm_ref):
653 return (self.get_vm_by_uuid(vm_ref) != None)
655 def is_valid_dev(self, klass, dev_uuid):
656 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
658 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs):
659 dom = self.uuid_to_dom(vm_uuid)
660 fn(dom, *args, **kwargs)
662 def uuid_to_dom(self, vm_uuid):
663 self.domains_lock.acquire()
664 try:
665 for domid, dom in self.domains.items():
666 if dom.get_uuid() == vm_uuid:
667 return domid
669 if vm_uuid in self.managed_domains:
670 domid = self.managed_domains[vm_uuid].getDomid()
671 if domid is None:
672 return self.managed_domains[vm_uuid].getName()
673 else:
674 return domid
676 raise XendInvalidDomain("Domain does not exist")
677 finally:
678 self.domains_lock.release()
681 def create_domain(self, xenapi_vm):
682 self.domains_lock.acquire()
683 try:
684 try:
685 xeninfo = XendConfig(xapi = xenapi_vm)
686 dominfo = XendDomainInfo.createDormant(xeninfo)
687 log.debug("Creating new managed domain: %s: %s" %
688 (dominfo.getName(), dominfo.get_uuid()))
689 self._managed_domain_register(dominfo)
690 self.managed_config_save(dominfo)
691 return dominfo.get_uuid()
692 except XendError, e:
693 raise
694 except Exception, e:
695 raise XendError(str(e))
696 finally:
697 self.domains_lock.release()
699 def rename_domain(self, dom, new_name):
700 self.domains_lock.acquire()
701 try:
702 old_name = dom.getName()
703 dom.setName(new_name)
705 finally:
706 self.domains_lock.release()
709 #
710 # End of Xen API
711 # ----------------------------------------------------------------
713 # ------------------------------------------------------------
714 # Xen Legacy API
716 def list(self, state = DOM_STATE_RUNNING):
717 """Get list of domain objects.
719 @param: the state in which the VMs should be -- one of the
720 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
721 @return: domains
722 @rtype: list of XendDomainInfo
723 """
724 if type(state) == int:
725 state = POWER_STATE_NAMES[state]
726 state = state.lower()
728 self.domains_lock.acquire()
729 try:
730 self._refresh(refresh_shutdown = False)
732 # active domains
733 active_domains = self.domains.values()
734 active_uuids = [d.get_uuid() for d in active_domains]
736 # inactive domains
737 inactive_domains = []
738 for dom_uuid, dom in self.managed_domains.items():
739 if dom_uuid not in active_uuids:
740 inactive_domains.append(dom)
742 if state == POWER_STATE_ALL:
743 return active_domains + inactive_domains
744 else:
745 return filter(lambda x:
746 POWER_STATE_NAMES[x.state].lower() == state,
747 active_domains + inactive_domains)
748 finally:
749 self.domains_lock.release()
752 def list_sorted(self, state = DOM_STATE_RUNNING):
753 """Get list of domain objects, sorted by name.
755 @param: the state in which the VMs should be -- one of the
756 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
757 @return: domain objects
758 @rtype: list of XendDomainInfo
759 """
760 doms = self.list(state)
761 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
762 return doms
764 def list_names(self, state = DOM_STATE_RUNNING):
765 """Get list of domain names.
767 @param: the state in which the VMs should be -- one of the
768 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
769 @return: domain names
770 @rtype: list of strings.
771 """
772 return [d.getName() for d in self.list_sorted(state)]
774 def domain_suspend(self, domname):
775 """Suspends a domain that is persistently managed by Xend
777 @param domname: Domain Name
778 @type domname: string
779 @rtype: None
780 @raise XendError: Failure during checkpointing.
781 """
783 try:
784 dominfo = self.domain_lookup_nr(domname)
785 if not dominfo:
786 raise XendInvalidDomain(domname)
788 if dominfo.getDomid() == DOM0_ID:
789 raise XendError("Cannot save privileged domain %s" % domname)
791 if dominfo.state != DOM_STATE_RUNNING:
792 raise VMBadState("Domain is not running",
793 POWER_STATE_NAMES[DOM_STATE_RUNNING],
794 POWER_STATE_NAMES[dominfo.state])
796 dom_uuid = dominfo.get_uuid()
798 if not os.path.exists(self._managed_config_path(dom_uuid)):
799 raise XendError("Domain is not managed by Xend lifecycle " +
800 "support.")
802 path = self._managed_check_point_path(dom_uuid)
803 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
804 try:
805 # For now we don't support 'live checkpoint'
806 XendCheckpoint.save(fd, dominfo, False, False, path)
807 finally:
808 os.close(fd)
809 except OSError, ex:
810 raise XendError("can't write guest state file %s: %s" %
811 (path, ex[1]))
813 def domain_resume(self, domname, start_paused = False):
814 """Resumes a domain that is persistently managed by Xend.
816 @param domname: Domain Name
817 @type domname: string
818 @rtype: None
819 @raise XendError: If failed to restore.
820 """
821 self.domains_lock.acquire()
822 try:
823 try:
824 dominfo = self.domain_lookup_nr(domname)
826 if not dominfo:
827 raise XendInvalidDomain(domname)
829 if dominfo.getDomid() == DOM0_ID:
830 raise XendError("Cannot save privileged domain %s" % domname)
832 if dominfo.state != DOM_STATE_HALTED:
833 raise XendError("Cannot resume domain that is not halted.")
835 dom_uuid = dominfo.get_uuid()
836 chkpath = self._managed_check_point_path(dom_uuid)
837 if not os.path.exists(chkpath):
838 raise XendError("Domain was not suspended by Xend")
840 # Restore that replaces the existing XendDomainInfo
841 try:
842 log.debug('Current DomainInfo state: %d' % dominfo.state)
843 XendCheckpoint.restore(self,
844 os.open(chkpath, os.O_RDONLY),
845 dominfo,
846 paused = start_paused)
847 os.unlink(chkpath)
848 except OSError, ex:
849 raise XendError("Failed to read stored checkpoint file")
850 except IOError, ex:
851 raise XendError("Failed to delete checkpoint file")
852 except Exception, ex:
853 log.exception("Exception occurred when resuming")
854 raise XendError("Error occurred when resuming: %s" % str(ex))
855 finally:
856 self.domains_lock.release()
859 def domain_create(self, config):
860 """Create a domain from a configuration.
862 @param config: configuration
863 @type config: SXP Object (list of lists)
864 @rtype: XendDomainInfo
865 """
866 self.domains_lock.acquire()
867 try:
868 self._refresh()
870 dominfo = XendDomainInfo.create(config)
871 if XendNode.instance().xenschedinfo() == 'credit':
872 self.domain_sched_credit_set(dominfo.getDomid(),
873 dominfo.getWeight(),
874 dominfo.getCap())
875 return dominfo
876 finally:
877 self.domains_lock.release()
880 def domain_create_from_dict(self, config_dict):
881 """Create a domain from a configuration dictionary.
883 @param config_dict: configuration
884 @rtype: XendDomainInfo
885 """
886 self.domains_lock.acquire()
887 try:
888 self._refresh()
890 dominfo = XendDomainInfo.create_from_dict(config_dict)
891 if XendNode.instance().xenschedinfo() == 'credit':
892 self.domain_sched_credit_set(dominfo.getDomid(),
893 dominfo.getWeight(),
894 dominfo.getCap())
895 return dominfo
896 finally:
897 self.domains_lock.release()
900 def domain_new(self, config):
901 """Create a domain from a configuration but do not start it.
903 @param config: configuration
904 @type config: SXP Object (list of lists)
905 @rtype: XendDomainInfo
906 """
907 self.domains_lock.acquire()
908 try:
909 try:
910 domconfig = XendConfig(sxp_obj = config)
911 dominfo = XendDomainInfo.createDormant(domconfig)
912 log.debug("Creating new managed domain: %s" %
913 dominfo.getName())
914 self._managed_domain_register(dominfo)
915 self.managed_config_save(dominfo)
916 # no return value because it isn't meaningful for client
917 except XendError, e:
918 raise
919 except Exception, e:
920 raise XendError(str(e))
921 finally:
922 self.domains_lock.release()
924 def domain_start(self, domid, start_paused = True):
925 """Start a managed domain
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 @rtype: None
933 """
934 self.domains_lock.acquire()
935 try:
936 self._refresh()
938 dominfo = self.domain_lookup_nr(domid)
939 if not dominfo:
940 raise XendInvalidDomain(str(domid))
942 if dominfo.state != DOM_STATE_HALTED:
943 raise VMBadState("Domain is already running",
944 POWER_STATE_NAMES[DOM_STATE_HALTED],
945 POWER_STATE_NAMES[dominfo.state])
947 dominfo.start(is_managed = True)
948 if XendNode.instance().xenschedinfo() == 'credit':
949 self.domain_sched_credit_set(dominfo.getDomid(),
950 dominfo.getWeight(),
951 dominfo.getCap())
952 finally:
953 self.domains_lock.release()
954 dominfo.waitForDevices()
955 if not start_paused:
956 dominfo.unpause()
959 def domain_delete(self, domid):
960 """Remove a managed domain from database
962 @require: Domain must not be running.
963 @param domid: Domain name or domain ID.
964 @type domid: string or int
965 @rtype: None
966 @raise XendError: If domain is still running
967 """
968 self.domains_lock.acquire()
969 try:
970 try:
971 dominfo = self.domain_lookup_nr(domid)
972 if not dominfo:
973 raise XendInvalidDomain(str(domid))
975 if dominfo.state != DOM_STATE_HALTED:
976 raise VMBadState("Domain is still running",
977 POWER_STATE_NAMES[DOM_STATE_HALTED],
978 POWER_STATE_NAMES[dominfo.state])
980 log.info("Domain %s (%s) deleted." %
981 (dominfo.getName(), dominfo.info.get('uuid')))
983 self._managed_domain_unregister(dominfo)
984 self.remove_domain(dominfo)
985 XendDevices.destroy_device_state(dominfo)
986 except Exception, ex:
987 raise XendError(str(ex))
988 finally:
989 self.domains_lock.release()
992 def domain_configure(self, config):
993 """Configure an existing domain.
995 @param vmconfig: vm configuration
996 @type vmconfig: SXP Object (list of lists)
997 @todo: Not implemented
998 """
999 # !!!
1000 raise XendError("Unsupported")
1002 def domain_restore(self, src, paused=False):
1003 """Restore a domain from file.
1005 @param src: filename of checkpoint file to restore from
1006 @type src: string
1007 @return: Restored domain
1008 @rtype: XendDomainInfo
1009 @raise XendError: Failure to restore domain
1010 """
1011 try:
1012 fd = os.open(src, os.O_RDONLY)
1013 try:
1014 return self.domain_restore_fd(fd, paused=paused)
1015 finally:
1016 os.close(fd)
1017 except OSError, ex:
1018 raise XendError("can't read guest state file %s: %s" %
1019 (src, ex[1]))
1021 def domain_restore_fd(self, fd, paused=False):
1022 """Restore a domain from the given file descriptor.
1024 @param fd: file descriptor of the checkpoint file
1025 @type fd: File object
1026 @rtype: XendDomainInfo
1027 @raise XendError: if failed to restore
1028 """
1030 try:
1031 return XendCheckpoint.restore(self, fd, paused=paused)
1032 except:
1033 # I don't really want to log this exception here, but the error
1034 # handling in the relocation-socket handling code (relocate.py) is
1035 # poor, so we need to log this for debugging.
1036 log.exception("Restore failed")
1037 raise XendError("Restore failed")
1039 def domain_unpause(self, domid):
1040 """Unpause domain execution.
1042 @param domid: Domain ID or Name
1043 @type domid: int or string.
1044 @rtype: None
1045 @raise XendError: Failed to unpause
1046 @raise XendInvalidDomain: Domain is not valid
1047 """
1048 try:
1049 dominfo = self.domain_lookup_nr(domid)
1050 if not dominfo:
1051 raise XendInvalidDomain(str(domid))
1052 if dominfo.getDomid() == DOM0_ID:
1053 raise XendError("Cannot unpause privileged domain %s" % domid)
1054 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
1055 int(dominfo.getDomid()))
1056 dominfo.unpause()
1057 except XendInvalidDomain:
1058 log.exception("domain_unpause")
1059 raise
1060 except Exception, ex:
1061 log.exception("domain_unpause")
1062 raise XendError(str(ex))
1064 def domain_pause(self, domid):
1065 """Pause domain execution.
1067 @param domid: Domain ID or Name
1068 @type domid: int or string.
1069 @rtype: None
1070 @raise XendError: Failed to pause
1071 @raise XendInvalidDomain: Domain is not valid
1072 """
1073 try:
1074 dominfo = self.domain_lookup_nr(domid)
1075 if not dominfo:
1076 raise XendInvalidDomain(str(domid))
1077 if dominfo.getDomid() == DOM0_ID:
1078 raise XendError("Cannot pause privileged domain %s" % domid)
1079 log.info("Domain %s (%d) paused.", dominfo.getName(),
1080 int(dominfo.getDomid()))
1081 dominfo.pause()
1082 except XendInvalidDomain:
1083 log.exception("domain_pause")
1084 raise
1085 except Exception, ex:
1086 log.exception("domain_pause")
1087 raise XendError(str(ex))
1089 def domain_dump(self, domid, filename, live, crash):
1090 """Dump domain core."""
1092 dominfo = self.domain_lookup_nr(domid)
1093 if not dominfo:
1094 raise XendInvalidDomain(str(domid))
1096 if dominfo.getDomid() == DOM0_ID:
1097 raise XendError("Cannot dump core for privileged domain %s" % domid)
1099 try:
1100 log.info("Domain core dump requested for domain %s (%d) "
1101 "live=%d crash=%d.",
1102 dominfo.getName(), dominfo.getDomid(), live, crash)
1103 return dominfo.dumpCore(filename)
1104 except Exception, ex:
1105 raise XendError(str(ex))
1107 def domain_destroy(self, domid):
1108 """Terminate domain immediately.
1110 @param domid: Domain ID or Name
1111 @type domid: int or string.
1112 @rtype: None
1113 @raise XendError: Failed to destroy
1114 @raise XendInvalidDomain: Domain is not valid
1115 """
1117 dominfo = self.domain_lookup_nr(domid)
1118 if dominfo and dominfo.getDomid() == DOM0_ID:
1119 raise XendError("Cannot destroy privileged domain %s" % domid)
1121 if dominfo:
1122 val = dominfo.destroy()
1123 else:
1124 try:
1125 val = xc.domain_destroy(int(domid))
1126 except ValueError:
1127 raise XendInvalidDomain(domid)
1128 except Exception, e:
1129 raise XendError(str(e))
1131 return val
1133 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1134 """Start domain migration.
1136 @param domid: Domain ID or Name
1137 @type domid: int or string.
1138 @param dst: Destination IP address
1139 @type dst: string
1140 @keyword port: relocation port on destination
1141 @type port: int
1142 @keyword live: Live migration
1143 @type live: bool
1144 @keyword resource: not used??
1145 @rtype: None
1146 @raise XendError: Failed to migrate
1147 @raise XendInvalidDomain: Domain is not valid
1148 """
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 migrate privileged domain %s" % domid)
1157 """ The following call may raise a XendError exception """
1158 dominfo.testMigrateDevices(True, dst)
1160 if live:
1161 """ Make sure there's memory free for enabling shadow mode """
1162 dominfo.checkLiveMigrateMemory()
1164 if port == 0:
1165 port = xoptions.get_xend_relocation_port()
1166 try:
1167 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1168 sock.connect((dst, port))
1169 except socket.error, err:
1170 raise XendError("can't connect: %s" % err[1])
1172 sock.send("receive\n")
1173 sock.recv(80)
1174 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1175 sock.close()
1177 def domain_save(self, domid, dst):
1178 """Start saving a domain to file.
1180 @param domid: Domain ID or Name
1181 @type domid: int or string.
1182 @param dst: Destination filename
1183 @type dst: string
1184 @rtype: None
1185 @raise XendError: Failed to save domain
1186 @raise XendInvalidDomain: Domain is not valid
1187 """
1188 try:
1189 dominfo = self.domain_lookup_nr(domid)
1190 if not dominfo:
1191 raise XendInvalidDomain(str(domid))
1193 if dominfo.getDomid() == DOM0_ID:
1194 raise XendError("Cannot save privileged domain %i" % domid)
1196 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1197 try:
1198 # For now we don't support 'live checkpoint'
1199 XendCheckpoint.save(fd, dominfo, False, False, dst)
1200 finally:
1201 os.close(fd)
1202 except OSError, ex:
1203 raise XendError("can't write guest state file %s: %s" %
1204 (dst, ex[1]))
1206 def domain_pincpu(self, domid, vcpu, cpumap):
1207 """Set which cpus vcpu can use
1209 @param domid: Domain ID or Name
1210 @type domid: int or string.
1211 @param vcpu: vcpu to pin to
1212 @type vcpu: int
1213 @param cpumap: string repr of usable cpus
1214 @type cpumap: string
1215 @rtype: 0
1216 """
1217 dominfo = self.domain_lookup_nr(domid)
1218 if not dominfo:
1219 raise XendInvalidDomain(str(domid))
1221 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1222 vcpus = [ vcpu ]
1223 if str(vcpu).lower() == "all":
1224 vcpus = range(0, int(dominfo.getVCpuCount()))
1226 # set the same cpumask for all vcpus
1227 rc = 0
1228 for v in vcpus:
1229 try:
1230 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1231 except Exception, ex:
1232 raise XendError(str(ex))
1233 return rc
1235 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1236 weight):
1237 """Set Simple EDF scheduler parameters for a domain.
1239 @param domid: Domain ID or Name
1240 @type domid: int or string.
1241 @rtype: 0
1242 """
1243 dominfo = self.domain_lookup_nr(domid)
1244 if not dominfo:
1245 raise XendInvalidDomain(str(domid))
1246 try:
1247 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1248 latency, extratime, weight)
1249 except Exception, ex:
1250 raise XendError(str(ex))
1252 def domain_cpu_sedf_get(self, domid):
1253 """Get Simple EDF scheduler parameters for a domain.
1255 @param domid: Domain ID or Name
1256 @type domid: int or string.
1257 @rtype: SXP object
1258 @return: The parameters for Simple EDF schedule for a domain.
1259 """
1260 dominfo = self.domain_lookup_nr(domid)
1261 if not dominfo:
1262 raise XendInvalidDomain(str(domid))
1263 try:
1264 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1265 # return sxpr
1266 return ['sedf',
1267 ['domid', sedf_info['domid']],
1268 ['period', sedf_info['period']],
1269 ['slice', sedf_info['slice']],
1270 ['latency', sedf_info['latency']],
1271 ['extratime', sedf_info['extratime']],
1272 ['weight', sedf_info['weight']]]
1274 except Exception, ex:
1275 raise XendError(str(ex))
1277 def domain_shadow_control(self, domid, op):
1278 """Shadow page control.
1280 @param domid: Domain ID or Name
1281 @type domid: int or string.
1282 @param op: operation
1283 @type op: int
1284 @rtype: 0
1285 """
1286 dominfo = self.domain_lookup(domid)
1287 try:
1288 return xc.shadow_control(dominfo.getDomid(), op)
1289 except Exception, ex:
1290 raise XendError(str(ex))
1292 def domain_shadow_mem_get(self, domid):
1293 """Get shadow pagetable memory allocation.
1295 @param domid: Domain ID or Name
1296 @type domid: int or string.
1297 @rtype: int
1298 @return: shadow memory in MB
1299 """
1300 dominfo = self.domain_lookup(domid)
1301 try:
1302 return xc.shadow_mem_control(dominfo.getDomid())
1303 except Exception, ex:
1304 raise XendError(str(ex))
1306 def domain_shadow_mem_set(self, domid, mb):
1307 """Set shadow pagetable memory allocation.
1309 @param domid: Domain ID or Name
1310 @type domid: int or string.
1311 @param mb: shadow memory to set in MB
1312 @type: mb: int
1313 @rtype: int
1314 @return: shadow memory in MB
1315 """
1316 dominfo = self.domain_lookup(domid)
1317 try:
1318 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1319 except Exception, ex:
1320 raise XendError(str(ex))
1322 def domain_sched_credit_get(self, domid):
1323 """Get credit scheduler parameters for a domain.
1325 @param domid: Domain ID or Name
1326 @type domid: int or string.
1327 @rtype: dict with keys 'weight' and 'cap'
1328 @return: credit scheduler parameters
1329 """
1330 dominfo = self.domain_lookup_nr(domid)
1331 if not dominfo:
1332 raise XendInvalidDomain(str(domid))
1333 try:
1334 return xc.sched_credit_domain_get(dominfo.getDomid())
1335 except Exception, ex:
1336 raise XendError(str(ex))
1338 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1339 """Set credit scheduler parameters for a domain.
1341 @param domid: Domain ID or Name
1342 @type domid: int or string.
1343 @type weight: int
1344 @type cap: int
1345 @rtype: 0
1346 """
1347 dominfo = self.domain_lookup_nr(domid)
1348 if not dominfo:
1349 raise XendInvalidDomain(str(domid))
1350 try:
1351 if weight is None:
1352 weight = int(0)
1353 elif weight < 1 or weight > 65535:
1354 raise XendError("weight is out of range")
1356 if cap is None:
1357 cap = int(~0)
1358 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1359 raise XendError("cap is out of range")
1361 assert type(weight) == int
1362 assert type(cap) == int
1364 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1365 except Exception, ex:
1366 log.exception(ex)
1367 raise XendError(str(ex))
1369 def domain_maxmem_set(self, domid, mem):
1370 """Set the memory limit for a domain.
1372 @param domid: Domain ID or Name
1373 @type domid: int or string.
1374 @param mem: memory limit (in MiB)
1375 @type mem: int
1376 @raise XendError: fail to set memory
1377 @rtype: 0
1378 """
1379 dominfo = self.domain_lookup_nr(domid)
1380 if not dominfo:
1381 raise XendInvalidDomain(str(domid))
1382 dominfo.setMemoryMaximum(mem)
1384 def domain_ioport_range_enable(self, domid, first, last):
1385 """Enable access to a range of IO ports for a domain
1387 @param first: first IO port
1388 @param last: last IO port
1389 @raise XendError: failed to set range
1390 @rtype: 0
1391 """
1392 dominfo = self.domain_lookup_nr(domid)
1393 if not dominfo:
1394 raise XendInvalidDomain(str(domid))
1395 nr_ports = last - first + 1
1396 try:
1397 return xc.domain_ioport_permission(dominfo.getDomid(),
1398 first_port = first,
1399 nr_ports = nr_ports,
1400 allow_access = 1)
1401 except Exception, ex:
1402 raise XendError(str(ex))
1404 def domain_ioport_range_disable(self, domid, first, last):
1405 """Disable access to a range of IO ports for a domain
1407 @param first: first IO port
1408 @param last: last IO port
1409 @raise XendError: failed to set range
1410 @rtype: 0
1411 """
1412 dominfo = self.domain_lookup_nr(domid)
1413 if not dominfo:
1414 raise XendInvalidDomain(str(domid))
1415 nr_ports = last - first + 1
1416 try:
1417 return xc.domain_ioport_permission(dominfo.getDomid(),
1418 first_port = first,
1419 nr_ports = nr_ports,
1420 allow_access = 0)
1421 except Exception, ex:
1422 raise XendError(str(ex))
1425 def instance():
1426 """Singleton constructor. Use this instead of the class constructor.
1427 """
1428 global inst
1429 try:
1430 inst
1431 except:
1432 inst = XendDomain()
1433 inst.init()
1434 return inst