ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 13676:fd57cef459dc

Allow 'xm create' to set scheduling parameters.
Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author kaf24@localhost.localdomain
date Sun Jan 28 09:37:18 2007 +0000 (2007-01-28)
parents bffe67212133
children 5d9b72e640e0
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
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 self.domain_sched_credit_set(dominfo.getDomid(),
872 dominfo.getWeight(),
873 dominfo.getCap())
874 return dominfo
875 finally:
876 self.domains_lock.release()
879 def domain_create_from_dict(self, config_dict):
880 """Create a domain from a configuration dictionary.
882 @param config_dict: configuration
883 @rtype: XendDomainInfo
884 """
885 self.domains_lock.acquire()
886 try:
887 self._refresh()
889 dominfo = XendDomainInfo.create_from_dict(config_dict)
890 self.domain_sched_credit_set(dominfo.getDomid(),
891 dominfo.getWeight(),
892 dominfo.getCap())
893 return dominfo
894 finally:
895 self.domains_lock.release()
898 def domain_new(self, config):
899 """Create a domain from a configuration but do not start it.
901 @param config: configuration
902 @type config: SXP Object (list of lists)
903 @rtype: XendDomainInfo
904 """
905 self.domains_lock.acquire()
906 try:
907 try:
908 domconfig = XendConfig(sxp_obj = config)
909 dominfo = XendDomainInfo.createDormant(domconfig)
910 log.debug("Creating new managed domain: %s" %
911 dominfo.getName())
912 self._managed_domain_register(dominfo)
913 self.managed_config_save(dominfo)
914 # no return value because it isn't meaningful for client
915 except XendError, e:
916 raise
917 except Exception, e:
918 raise XendError(str(e))
919 finally:
920 self.domains_lock.release()
922 def domain_start(self, domid, start_paused = True):
923 """Start a managed domain
925 @require: Domain must not be running.
926 @param domid: Domain name or domain ID.
927 @type domid: string or int
928 @rtype: None
929 @raise XendError: If domain is still running
930 @rtype: None
931 """
932 self.domains_lock.acquire()
933 try:
934 self._refresh()
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 VMBadState("Domain is already running",
942 POWER_STATE_NAMES[DOM_STATE_HALTED],
943 POWER_STATE_NAMES[dominfo.state])
945 dominfo.start(is_managed = True)
946 self.domain_sched_credit_set(dominfo.getDomid(),
947 dominfo.getWeight(),
948 dominfo.getCap())
949 finally:
950 self.domains_lock.release()
951 dominfo.waitForDevices()
952 if not start_paused:
953 dominfo.unpause()
956 def domain_delete(self, domid):
957 """Remove a managed domain from database
959 @require: Domain must not be running.
960 @param domid: Domain name or domain ID.
961 @type domid: string or int
962 @rtype: None
963 @raise XendError: If domain is still running
964 """
965 self.domains_lock.acquire()
966 try:
967 try:
968 dominfo = self.domain_lookup_nr(domid)
969 if not dominfo:
970 raise XendInvalidDomain(str(domid))
972 if dominfo.state != DOM_STATE_HALTED:
973 raise VMBadState("Domain is still running",
974 POWER_STATE_NAMES[DOM_STATE_HALTED],
975 POWER_STATE_NAMES[dominfo.state])
977 log.info("Domain %s (%s) deleted." %
978 (dominfo.getName(), dominfo.info.get('uuid')))
980 self._managed_domain_unregister(dominfo)
981 self.remove_domain(dominfo)
982 XendDevices.destroy_device_state(dominfo)
983 except Exception, ex:
984 raise XendError(str(ex))
985 finally:
986 self.domains_lock.release()
989 def domain_configure(self, config):
990 """Configure an existing domain.
992 @param vmconfig: vm configuration
993 @type vmconfig: SXP Object (list of lists)
994 @todo: Not implemented
995 """
996 # !!!
997 raise XendError("Unsupported")
999 def domain_restore(self, src, paused=False):
1000 """Restore a domain from file.
1002 @param src: filename of checkpoint file to restore from
1003 @type src: string
1004 @return: Restored domain
1005 @rtype: XendDomainInfo
1006 @raise XendError: Failure to restore domain
1007 """
1008 try:
1009 fd = os.open(src, os.O_RDONLY)
1010 try:
1011 return self.domain_restore_fd(fd, paused=paused)
1012 finally:
1013 os.close(fd)
1014 except OSError, ex:
1015 raise XendError("can't read guest state file %s: %s" %
1016 (src, ex[1]))
1018 def domain_restore_fd(self, fd, paused=False):
1019 """Restore a domain from the given file descriptor.
1021 @param fd: file descriptor of the checkpoint file
1022 @type fd: File object
1023 @rtype: XendDomainInfo
1024 @raise XendError: if failed to restore
1025 """
1027 try:
1028 return XendCheckpoint.restore(self, fd, paused=paused)
1029 except:
1030 # I don't really want to log this exception here, but the error
1031 # handling in the relocation-socket handling code (relocate.py) is
1032 # poor, so we need to log this for debugging.
1033 log.exception("Restore failed")
1034 raise XendError("Restore failed")
1036 def domain_unpause(self, domid):
1037 """Unpause domain execution.
1039 @param domid: Domain ID or Name
1040 @type domid: int or string.
1041 @rtype: None
1042 @raise XendError: Failed to unpause
1043 @raise XendInvalidDomain: Domain is not valid
1044 """
1045 try:
1046 dominfo = self.domain_lookup_nr(domid)
1047 if not dominfo:
1048 raise XendInvalidDomain(str(domid))
1049 if dominfo.getDomid() == DOM0_ID:
1050 raise XendError("Cannot unpause privileged domain %s" % domid)
1051 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
1052 int(dominfo.getDomid()))
1053 dominfo.unpause()
1054 except XendInvalidDomain:
1055 log.exception("domain_unpause")
1056 raise
1057 except Exception, ex:
1058 log.exception("domain_unpause")
1059 raise XendError(str(ex))
1061 def domain_pause(self, domid):
1062 """Pause domain execution.
1064 @param domid: Domain ID or Name
1065 @type domid: int or string.
1066 @rtype: None
1067 @raise XendError: Failed to pause
1068 @raise XendInvalidDomain: Domain is not valid
1069 """
1070 try:
1071 dominfo = self.domain_lookup_nr(domid)
1072 if not dominfo:
1073 raise XendInvalidDomain(str(domid))
1074 if dominfo.getDomid() == DOM0_ID:
1075 raise XendError("Cannot pause privileged domain %s" % domid)
1076 log.info("Domain %s (%d) paused.", dominfo.getName(),
1077 int(dominfo.getDomid()))
1078 dominfo.pause()
1079 except XendInvalidDomain:
1080 log.exception("domain_pause")
1081 raise
1082 except Exception, ex:
1083 log.exception("domain_pause")
1084 raise XendError(str(ex))
1086 def domain_dump(self, domid, filename, live, crash):
1087 """Dump domain core."""
1089 dominfo = self.domain_lookup_nr(domid)
1090 if not dominfo:
1091 raise XendInvalidDomain(str(domid))
1093 if dominfo.getDomid() == DOM0_ID:
1094 raise XendError("Cannot dump core for privileged domain %s" % domid)
1096 try:
1097 log.info("Domain core dump requested for domain %s (%d) "
1098 "live=%d crash=%d.",
1099 dominfo.getName(), dominfo.getDomid(), live, crash)
1100 return dominfo.dumpCore(filename)
1101 except Exception, ex:
1102 raise XendError(str(ex))
1104 def domain_destroy(self, domid):
1105 """Terminate domain immediately.
1107 @param domid: Domain ID or Name
1108 @type domid: int or string.
1109 @rtype: None
1110 @raise XendError: Failed to destroy
1111 @raise XendInvalidDomain: Domain is not valid
1112 """
1114 dominfo = self.domain_lookup_nr(domid)
1115 if dominfo and dominfo.getDomid() == DOM0_ID:
1116 raise XendError("Cannot destroy privileged domain %s" % domid)
1118 if dominfo:
1119 val = dominfo.destroy()
1120 else:
1121 try:
1122 val = xc.domain_destroy(int(domid))
1123 except ValueError:
1124 raise XendInvalidDomain(domid)
1125 except Exception, e:
1126 raise XendError(str(e))
1128 return val
1130 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1131 """Start domain migration.
1133 @param domid: Domain ID or Name
1134 @type domid: int or string.
1135 @param dst: Destination IP address
1136 @type dst: string
1137 @keyword port: relocation port on destination
1138 @type port: int
1139 @keyword live: Live migration
1140 @type live: bool
1141 @keyword resource: not used??
1142 @rtype: None
1143 @raise XendError: Failed to migrate
1144 @raise XendInvalidDomain: Domain is not valid
1145 """
1147 dominfo = self.domain_lookup_nr(domid)
1148 if not dominfo:
1149 raise XendInvalidDomain(str(domid))
1151 if dominfo.getDomid() == DOM0_ID:
1152 raise XendError("Cannot migrate privileged domain %s" % domid)
1154 """ The following call may raise a XendError exception """
1155 dominfo.testMigrateDevices(True, dst)
1157 if live:
1158 """ Make sure there's memory free for enabling shadow mode """
1159 dominfo.checkLiveMigrateMemory()
1161 if port == 0:
1162 port = xoptions.get_xend_relocation_port()
1163 try:
1164 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1165 sock.connect((dst, port))
1166 except socket.error, err:
1167 raise XendError("can't connect: %s" % err[1])
1169 sock.send("receive\n")
1170 sock.recv(80)
1171 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1172 sock.close()
1174 def domain_save(self, domid, dst):
1175 """Start saving a domain to file.
1177 @param domid: Domain ID or Name
1178 @type domid: int or string.
1179 @param dst: Destination filename
1180 @type dst: string
1181 @rtype: None
1182 @raise XendError: Failed to save domain
1183 @raise XendInvalidDomain: Domain is not valid
1184 """
1185 try:
1186 dominfo = self.domain_lookup_nr(domid)
1187 if not dominfo:
1188 raise XendInvalidDomain(str(domid))
1190 if dominfo.getDomid() == DOM0_ID:
1191 raise XendError("Cannot save privileged domain %i" % domid)
1193 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1194 try:
1195 # For now we don't support 'live checkpoint'
1196 XendCheckpoint.save(fd, dominfo, False, False, dst)
1197 finally:
1198 os.close(fd)
1199 except OSError, ex:
1200 raise XendError("can't write guest state file %s: %s" %
1201 (dst, ex[1]))
1203 def domain_pincpu(self, domid, vcpu, cpumap):
1204 """Set which cpus vcpu can use
1206 @param domid: Domain ID or Name
1207 @type domid: int or string.
1208 @param vcpu: vcpu to pin to
1209 @type vcpu: int
1210 @param cpumap: string repr of usable cpus
1211 @type cpumap: string
1212 @rtype: 0
1213 """
1214 dominfo = self.domain_lookup_nr(domid)
1215 if not dominfo:
1216 raise XendInvalidDomain(str(domid))
1218 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1219 vcpus = [ vcpu ]
1220 if str(vcpu).lower() == "all":
1221 vcpus = range(0, int(dominfo.getVCpuCount()))
1223 # set the same cpumask for all vcpus
1224 rc = 0
1225 for v in vcpus:
1226 try:
1227 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1228 except Exception, ex:
1229 raise XendError(str(ex))
1230 return rc
1232 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1233 weight):
1234 """Set Simple EDF scheduler parameters for a domain.
1236 @param domid: Domain ID or Name
1237 @type domid: int or string.
1238 @rtype: 0
1239 """
1240 dominfo = self.domain_lookup_nr(domid)
1241 if not dominfo:
1242 raise XendInvalidDomain(str(domid))
1243 try:
1244 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1245 latency, extratime, weight)
1246 except Exception, ex:
1247 raise XendError(str(ex))
1249 def domain_cpu_sedf_get(self, domid):
1250 """Get Simple EDF scheduler parameters for a domain.
1252 @param domid: Domain ID or Name
1253 @type domid: int or string.
1254 @rtype: SXP object
1255 @return: The parameters for Simple EDF schedule for a domain.
1256 """
1257 dominfo = self.domain_lookup_nr(domid)
1258 if not dominfo:
1259 raise XendInvalidDomain(str(domid))
1260 try:
1261 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1262 # return sxpr
1263 return ['sedf',
1264 ['domid', sedf_info['domid']],
1265 ['period', sedf_info['period']],
1266 ['slice', sedf_info['slice']],
1267 ['latency', sedf_info['latency']],
1268 ['extratime', sedf_info['extratime']],
1269 ['weight', sedf_info['weight']]]
1271 except Exception, ex:
1272 raise XendError(str(ex))
1274 def domain_shadow_control(self, domid, op):
1275 """Shadow page control.
1277 @param domid: Domain ID or Name
1278 @type domid: int or string.
1279 @param op: operation
1280 @type op: int
1281 @rtype: 0
1282 """
1283 dominfo = self.domain_lookup(domid)
1284 try:
1285 return xc.shadow_control(dominfo.getDomid(), op)
1286 except Exception, ex:
1287 raise XendError(str(ex))
1289 def domain_shadow_mem_get(self, domid):
1290 """Get shadow pagetable memory allocation.
1292 @param domid: Domain ID or Name
1293 @type domid: int or string.
1294 @rtype: int
1295 @return: shadow memory in MB
1296 """
1297 dominfo = self.domain_lookup(domid)
1298 try:
1299 return xc.shadow_mem_control(dominfo.getDomid())
1300 except Exception, ex:
1301 raise XendError(str(ex))
1303 def domain_shadow_mem_set(self, domid, mb):
1304 """Set shadow pagetable memory allocation.
1306 @param domid: Domain ID or Name
1307 @type domid: int or string.
1308 @param mb: shadow memory to set in MB
1309 @type: mb: int
1310 @rtype: int
1311 @return: shadow memory in MB
1312 """
1313 dominfo = self.domain_lookup(domid)
1314 try:
1315 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1316 except Exception, ex:
1317 raise XendError(str(ex))
1319 def domain_sched_credit_get(self, domid):
1320 """Get credit scheduler parameters for a domain.
1322 @param domid: Domain ID or Name
1323 @type domid: int or string.
1324 @rtype: dict with keys 'weight' and 'cap'
1325 @return: credit scheduler parameters
1326 """
1327 dominfo = self.domain_lookup_nr(domid)
1328 if not dominfo:
1329 raise XendInvalidDomain(str(domid))
1330 try:
1331 return xc.sched_credit_domain_get(dominfo.getDomid())
1332 except Exception, ex:
1333 raise XendError(str(ex))
1335 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1336 """Set credit scheduler parameters for a domain.
1338 @param domid: Domain ID or Name
1339 @type domid: int or string.
1340 @type weight: int
1341 @type cap: int
1342 @rtype: 0
1343 """
1344 dominfo = self.domain_lookup_nr(domid)
1345 if not dominfo:
1346 raise XendInvalidDomain(str(domid))
1347 try:
1348 if weight is None:
1349 weight = int(0)
1350 elif weight < 1 or weight > 65535:
1351 raise XendError("weight is out of range")
1353 if cap is None:
1354 cap = int(~0)
1355 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1356 raise XendError("cap is out of range")
1358 assert type(weight) == int
1359 assert type(cap) == int
1361 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1362 except Exception, ex:
1363 log.exception(ex)
1364 raise XendError(str(ex))
1366 def domain_maxmem_set(self, domid, mem):
1367 """Set the memory limit for a domain.
1369 @param domid: Domain ID or Name
1370 @type domid: int or string.
1371 @param mem: memory limit (in MiB)
1372 @type mem: int
1373 @raise XendError: fail to set memory
1374 @rtype: 0
1375 """
1376 dominfo = self.domain_lookup_nr(domid)
1377 if not dominfo:
1378 raise XendInvalidDomain(str(domid))
1379 dominfo.setMemoryMaximum(mem)
1381 def domain_ioport_range_enable(self, domid, first, last):
1382 """Enable access to a range of IO ports for a domain
1384 @param first: first IO port
1385 @param last: last IO port
1386 @raise XendError: failed to set range
1387 @rtype: 0
1388 """
1389 dominfo = self.domain_lookup_nr(domid)
1390 if not dominfo:
1391 raise XendInvalidDomain(str(domid))
1392 nr_ports = last - first + 1
1393 try:
1394 return xc.domain_ioport_permission(dominfo.getDomid(),
1395 first_port = first,
1396 nr_ports = nr_ports,
1397 allow_access = 1)
1398 except Exception, ex:
1399 raise XendError(str(ex))
1401 def domain_ioport_range_disable(self, domid, first, last):
1402 """Disable access to a range of IO ports for a domain
1404 @param first: first IO port
1405 @param last: last IO port
1406 @raise XendError: failed to set range
1407 @rtype: 0
1408 """
1409 dominfo = self.domain_lookup_nr(domid)
1410 if not dominfo:
1411 raise XendInvalidDomain(str(domid))
1412 nr_ports = last - first + 1
1413 try:
1414 return xc.domain_ioport_permission(dominfo.getDomid(),
1415 first_port = first,
1416 nr_ports = nr_ports,
1417 allow_access = 0)
1418 except Exception, ex:
1419 raise XendError(str(ex))
1422 def instance():
1423 """Singleton constructor. Use this instead of the class constructor.
1424 """
1425 global inst
1426 try:
1427 inst
1428 except:
1429 inst = XendDomain()
1430 inst.init()
1431 return inst