ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 14483:ba1212ee7689

Fix parameter to XendInvalidDomain.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Mar 20 14:04:57 2007 +0000 (2007-03-20)
parents 10f5478a91cd
children 4baae9f9fdbb
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.XendConstants import TRIGGER_TYPE
47 from xen.xend.XendDevices import XendDevices
49 from xen.xend.xenstore.xstransact import xstransact
50 from xen.xend.xenstore.xswatch import xswatch
51 from xen.util import mkdir, security
52 from xen.xend import uuid
54 xc = xen.lowlevel.xc.xc()
55 xoptions = XendOptions.instance()
57 __all__ = [ "XendDomain" ]
59 CACHED_CONFIG_FILE = 'config.sxp'
60 CHECK_POINT_FILE = 'checkpoint.chk'
61 DOM0_UUID = "00000000-0000-0000-0000-000000000000"
62 DOM0_NAME = "Domain-0"
63 DOM0_ID = 0
65 POWER_STATE_NAMES = dict([(x, XEN_API_VM_POWER_STATE[x])
66 for x in [DOM_STATE_HALTED,
67 DOM_STATE_PAUSED,
68 DOM_STATE_RUNNING,
69 DOM_STATE_SUSPENDED,
70 DOM_STATE_SHUTDOWN,
71 DOM_STATE_UNKNOWN]])
72 POWER_STATE_ALL = 'all'
75 class XendDomain:
76 """Index of all domains. Singleton.
78 @ivar domains: map of domains indexed by domid
79 @type domains: dict of XendDomainInfo
80 @ivar managed_domains: domains that are not running and managed by Xend
81 @type managed_domains: dict of XendDomainInfo indexed by uuid
82 @ivar domains_lock: lock that must be held when manipulating self.domains
83 @type domains_lock: threaading.RLock
84 @ivar _allow_new_domains: Flag to set that allows creating of new domains.
85 @type _allow_new_domains: boolean
86 """
88 def __init__(self):
89 self.domains = {}
90 self.managed_domains = {}
91 self.domains_lock = threading.RLock()
93 # xen api instance vars
94 # TODO: nothing uses this at the moment
95 self._allow_new_domains = True
97 # This must be called only the once, by instance() below. It is separate
98 # from the constructor because XendDomainInfo calls back into this class
99 # in order to check the uniqueness of domain names. This means that
100 # instance() must be able to return a valid instance of this class even
101 # during this initialisation.
102 def init(self):
103 """Singleton initialisation function."""
105 dom_path = self._managed_path()
106 mkdir.parents(dom_path, stat.S_IRWXU)
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 except IndexError:
120 raise XendError('Unable to find Domain 0')
122 self._setDom0CPUCount()
124 # This watch registration needs to be before the refresh call, so
125 # that we're sure that we haven't missed any releases, but inside
126 # the domains_lock, as we don't want the watch to fire until after
127 # the refresh call has completed.
128 xswatch("@introduceDomain", self._on_domains_changed)
129 xswatch("@releaseDomain", self._on_domains_changed)
131 self._init_domains()
132 finally:
133 self.domains_lock.release()
136 def _on_domains_changed(self, _):
137 """ Callback method when xenstore changes.
139 Calls refresh which will keep the local cache of domains
140 in sync.
142 @rtype: int
143 @return: 1
144 """
145 self.domains_lock.acquire()
146 try:
147 self._refresh()
148 finally:
149 self.domains_lock.release()
150 return 1
152 def _init_domains(self):
153 """Does the initial scan of managed and active domains to
154 populate self.domains.
156 Note: L{XendDomainInfo._checkName} will call back into XendDomain
157 to make sure domain name is not a duplicate.
159 """
160 self.domains_lock.acquire()
161 try:
162 running = self._running_domains()
163 managed = self._managed_domains()
165 # add all active domains
166 for dom in running:
167 if dom['dying'] == 1:
168 log.warn('Ignoring dying domain %d from now on' %
169 dom['domid'])
170 continue
172 if dom['domid'] != DOM0_ID:
173 try:
174 new_dom = XendDomainInfo.recreate(dom, False)
175 except Exception:
176 log.exception("Failed to create reference to running "
177 "domain id: %d" % dom['domid'])
179 # add all managed domains as dormant domains.
180 for dom in managed:
181 dom_uuid = dom.get('uuid')
182 if not dom_uuid:
183 continue
185 dom_name = dom.get('name_label', 'Domain-%s' % dom_uuid)
186 try:
187 running_dom = self.domain_lookup_nr(dom_name)
188 if not running_dom:
189 # instantiate domain if not started.
190 new_dom = XendDomainInfo.createDormant(dom)
191 self._managed_domain_register(new_dom)
192 else:
193 self._managed_domain_register(running_dom)
194 except Exception:
195 log.exception("Failed to create reference to managed "
196 "domain: %s" % dom_name)
198 finally:
199 self.domains_lock.release()
202 # -----------------------------------------------------------------
203 # Getting managed domains storage path names
205 def _managed_path(self, domuuid = None):
206 """Returns the path of the directory where managed domain
207 information is stored.
209 @keyword domuuid: If not None, will return the path to the domain
210 otherwise, will return the path containing
211 the directories which represent each domain.
212 @type: None or String.
213 @rtype: String
214 @return: Path.
215 """
216 dom_path = xoptions.get_xend_domains_path()
217 if domuuid:
218 dom_path = os.path.join(dom_path, domuuid)
219 return dom_path
221 def _managed_config_path(self, domuuid):
222 """Returns the path to the configuration file of a managed domain.
224 @param domname: Domain uuid
225 @type domname: String
226 @rtype: String
227 @return: path to config file.
228 """
229 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
231 def _managed_check_point_path(self, domuuid):
232 """Returns absolute path to check point file for managed domain.
234 @param domuuid: Name of managed domain
235 @type domname: String
236 @rtype: String
237 @return: Path
238 """
239 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
241 def _managed_config_remove(self, domuuid):
242 """Removes a domain configuration from managed list
244 @param domuuid: Name of managed domain
245 @type domname: String
246 @raise XendError: fails to remove the domain.
247 """
248 config_path = self._managed_path(domuuid)
249 try:
250 if os.path.exists(config_path) and os.path.isdir(config_path):
251 shutil.rmtree(config_path)
252 except IOError:
253 log.exception('managed_config_remove failed removing conf')
254 raise XendError("Unable to remove managed configuration"
255 " for domain: %s" % domuuid)
257 def managed_config_save(self, dominfo):
258 """Save a domain's configuration to disk
260 @param domninfo: Managed domain to save.
261 @type dominfo: XendDomainInfo
262 @raise XendError: fails to save configuration.
263 @rtype: None
264 """
265 if not self.is_domain_managed(dominfo):
266 return # refuse to save configuration this domain isn't managed
268 if dominfo:
269 domains_dir = self._managed_path()
270 dom_uuid = dominfo.get_uuid()
271 domain_config_dir = self._managed_path(dom_uuid)
273 def make_or_raise(path):
274 try:
275 mkdir.parents(path, stat.S_IRWXU)
276 except:
277 log.exception("%s could not be created." % path)
278 raise XendError("%s could not be created." % path)
280 make_or_raise(domains_dir)
281 make_or_raise(domain_config_dir)
283 try:
284 fd, fn = tempfile.mkstemp()
285 f = os.fdopen(fd, 'w+b')
286 try:
287 prettyprint(dominfo.sxpr(legacy_only = False), f,
288 width = 78)
289 finally:
290 f.close()
291 try:
292 os.rename(fn, self._managed_config_path(dom_uuid))
293 except:
294 log.exception("Renaming %s" % fn)
295 os.remove(fn)
296 except:
297 log.exception("Error occurred saving configuration file " +
298 "to %s" % domain_config_dir)
299 raise XendError("Failed to save configuration file to: %s" %
300 domain_config_dir)
301 else:
302 log.warn("Trying to save configuration for invalid domain")
305 def _managed_domains(self):
306 """ Returns list of domains that are managed.
308 Expects to be protected by domains_lock.
310 @rtype: list of XendConfig
311 @return: List of domain configurations that are managed.
312 """
313 dom_path = self._managed_path()
314 dom_uuids = os.listdir(dom_path)
315 doms = []
316 for dom_uuid in dom_uuids:
317 try:
318 cfg_file = self._managed_config_path(dom_uuid)
319 cfg = XendConfig(filename = cfg_file)
320 if cfg.get('uuid') != dom_uuid:
321 # something is wrong with the SXP
322 log.error("UUID mismatch in stored configuration: %s" %
323 cfg_file)
324 continue
325 doms.append(cfg)
326 except Exception:
327 log.exception('Unable to open or parse config.sxp: %s' % \
328 cfg_file)
329 return doms
331 def _managed_domain_unregister(self, dom):
332 try:
333 if self.is_domain_managed(dom):
334 self._managed_config_remove(dom.get_uuid())
335 del self.managed_domains[dom.get_uuid()]
336 except ValueError:
337 log.warn("Domain is not registered: %s" % dom.get_uuid())
339 def _managed_domain_register(self, dom):
340 self.managed_domains[dom.get_uuid()] = dom
342 def is_domain_managed(self, dom = None):
343 return (dom.get_uuid() in self.managed_domains)
345 # End of Managed Domain Access
346 # --------------------------------------------------------------------
348 def _running_domains(self):
349 """Get table of domains indexed by id from xc.
351 @requires: Expects to be protected by domains_lock.
352 @rtype: list of dicts
353 @return: A list of dicts representing the running domains.
354 """
355 try:
356 return xc.domain_getinfo()
357 except RuntimeError, e:
358 log.exception("Unable to get domain information.")
359 return {}
361 def _setDom0CPUCount(self):
362 """Sets the number of VCPUs dom0 has. Retreived from the
363 Xend configuration, L{XendOptions}.
365 @requires: Expects to be protected by domains_lock.
366 @rtype: None
367 """
368 dom0 = self.privilegedDomain()
370 # get max number of vcpus to use for dom0 from config
371 target = int(xoptions.get_dom0_vcpus())
372 log.debug("number of vcpus to use is %d", target)
374 # target == 0 means use all processors
375 if target > 0:
376 dom0.setVCpuCount(target)
379 def _refresh(self, refresh_shutdown = True):
380 """Refresh the domain list. Needs to be called when
381 either xenstore has changed or when a method requires
382 up to date information (like uptime, cputime stats).
384 Expects to be protected by the domains_lock.
386 @rtype: None
387 """
389 running = self._running_domains()
390 # Add domains that are not already tracked but running in Xen,
391 # and update domain state for those that are running and tracked.
392 for dom in running:
393 domid = dom['domid']
394 if domid in self.domains:
395 self.domains[domid].update(dom, refresh_shutdown)
396 elif domid not in self.domains and dom['dying'] != 1:
397 try:
398 new_dom = XendDomainInfo.recreate(dom, False)
399 except VmError:
400 log.exception("Unable to recreate domain")
401 try:
402 xc.domain_destroy(domid)
403 except:
404 log.exception("Hard destruction of domain failed: %d" %
405 domid)
407 # update information for all running domains
408 # - like cpu_time, status, dying, etc.
409 # remove domains that are not running from active domain list.
410 # The list might have changed by now, because the update call may
411 # cause new domains to be added, if the domain has rebooted. We get
412 # the list again.
413 running = self._running_domains()
414 running_domids = [d['domid'] for d in running if d['dying'] != 1]
415 for domid, dom in self.domains.items():
416 if domid not in running_domids and domid != DOM0_ID:
417 self.remove_domain(dom, domid)
420 def add_domain(self, info):
421 """Add a domain to the list of running domains
423 @requires: Expects to be protected by the domains_lock.
424 @param info: XendDomainInfo of a domain to be added.
425 @type info: XendDomainInfo
426 """
427 log.debug("Adding Domain: %s" % info.getDomid())
428 self.domains[info.getDomid()] = info
430 # update the managed domains with a new XendDomainInfo object
431 # if we are keeping track of it.
432 if info.get_uuid() in self.managed_domains:
433 self._managed_domain_register(info)
435 def remove_domain(self, info, domid = None):
436 """Remove the domain from the list of running domains
438 @requires: Expects to be protected by the domains_lock.
439 @param info: XendDomainInfo of a domain to be removed.
440 @type info: XendDomainInfo
441 """
442 if info:
443 if domid == None:
444 domid = info.getDomid()
446 if info.state != DOM_STATE_HALTED:
447 info.cleanupDomain()
449 if domid in self.domains:
450 del self.domains[domid]
451 else:
452 log.warning("Attempted to remove non-existent domain.")
454 def restore_(self, config):
455 """Create a domain as part of the restore process. This is called
456 only from L{XendCheckpoint}.
458 A restore request comes into XendDomain through L{domain_restore}
459 or L{domain_restore_fd}. That request is
460 forwarded immediately to XendCheckpoint which, when it is ready, will
461 call this method. It is necessary to come through here rather than go
462 directly to L{XendDomainInfo.restore} because we need to
463 serialise the domain creation process, but cannot lock
464 domain_restore_fd as a whole, otherwise we will deadlock waiting for
465 the old domain to die.
467 @param config: Configuration of domain to restore
468 @type config: SXP Object (eg. list of lists)
469 """
470 self.domains_lock.acquire()
471 try:
472 security.refresh_ssidref(config)
473 dominfo = XendDomainInfo.restore(config)
474 return dominfo
475 finally:
476 self.domains_lock.release()
479 def domain_lookup(self, domid):
480 """Look up given I{domid} in the list of managed and running
481 domains.
483 @note: Will cause a refresh before lookup up domains, for
484 a version that does not need to re-read xenstore
485 use L{domain_lookup_nr}.
487 @param domid: Domain ID or Domain Name.
488 @type domid: int or string
489 @return: Found domain.
490 @rtype: XendDomainInfo
491 @raise XendInvalidDomain: If domain is not found.
492 """
493 self.domains_lock.acquire()
494 try:
495 self._refresh(refresh_shutdown = False)
496 dom = self.domain_lookup_nr(domid)
497 if not dom:
498 raise XendInvalidDomain(str(domid))
499 return dom
500 finally:
501 self.domains_lock.release()
504 def domain_lookup_nr(self, domid):
505 """Look up given I{domid} in the list of managed and running
506 domains.
508 @param domid: Domain ID or Domain Name.
509 @type domid: int or string
510 @return: Found domain.
511 @rtype: XendDomainInfo or None
512 """
513 self.domains_lock.acquire()
514 try:
515 # lookup by name
516 match = [dom for dom in self.domains.values() \
517 if dom.getName() == domid]
518 if match:
519 return match[0]
521 match = [dom for dom in self.managed_domains.values() \
522 if dom.getName() == domid]
523 if match:
524 return match[0]
526 # lookup by id
527 try:
528 if int(domid) in self.domains:
529 return self.domains[int(domid)]
530 except ValueError:
531 pass
533 # lookup by uuid for running domains
534 match = [dom for dom in self.domains.values() \
535 if dom.get_uuid() == domid]
536 if match:
537 return match[0]
539 # lookup by uuid for inactive managed domains
540 if domid in self.managed_domains:
541 return self.managed_domains[domid]
543 return None
544 finally:
545 self.domains_lock.release()
547 def privilegedDomain(self):
548 """ Get the XendDomainInfo of a dom0
550 @rtype: XendDomainInfo
551 """
552 self.domains_lock.acquire()
553 try:
554 return self.domains[DOM0_ID]
555 finally:
556 self.domains_lock.release()
558 def cleanup_domains(self):
559 """Clean up domains that are marked as autostop.
560 Should be called when Xend goes down. This is currently
561 called from L{xen.xend.servers.XMLRPCServer}.
563 """
564 log.debug('cleanup_domains')
565 self.domains_lock.acquire()
566 try:
567 for dom in self.domains.values():
568 if dom.getName() == DOM0_NAME:
569 continue
571 if dom.state == DOM_STATE_RUNNING:
572 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
573 if shutdownAction == 'shutdown':
574 log.debug('Shutting down domain: %s' % dom.getName())
575 dom.shutdown("poweroff")
576 elif shutdownAction == 'suspend':
577 self.domain_suspend(dom.getName())
578 finally:
579 self.domains_lock.release()
583 # ----------------------------------------------------------------
584 # Xen API
587 def set_allow_new_domains(self, allow_new_domains):
588 self._allow_new_domains = allow_new_domains
590 def allow_new_domains(self):
591 return self._allow_new_domains
593 def get_domain_refs(self):
594 result = []
595 try:
596 self.domains_lock.acquire()
597 result = [d.get_uuid() for d in self.domains.values()]
598 for d in self.managed_domains.keys():
599 if d not in result:
600 result.append(d)
601 return result
602 finally:
603 self.domains_lock.release()
605 def get_all_vms(self):
606 self.domains_lock.acquire()
607 try:
608 result = self.domains.values()
609 result += [x for x in self.managed_domains.values() if
610 x not in result]
611 return result
612 finally:
613 self.domains_lock.release()
615 def get_vm_by_uuid(self, vm_uuid):
616 self.domains_lock.acquire()
617 try:
618 for dom in self.domains.values():
619 if dom.get_uuid() == vm_uuid:
620 return dom
622 if vm_uuid in self.managed_domains:
623 return self.managed_domains[vm_uuid]
625 return None
626 finally:
627 self.domains_lock.release()
629 def get_vm_with_dev_uuid(self, klass, dev_uuid):
630 self.domains_lock.acquire()
631 try:
632 for dom in self.domains.values() + self.managed_domains.values():
633 if dom.has_device(klass, dev_uuid):
634 return dom
635 return None
636 finally:
637 self.domains_lock.release()
639 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
640 value = None
641 self.domains_lock.acquire()
642 try:
643 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
644 if dom:
645 value = dom.get_dev_property(klass, dev_uuid, field)
646 except ValueError, e:
647 pass
649 self.domains_lock.release()
651 return value
653 def is_valid_vm(self, vm_ref):
654 return (self.get_vm_by_uuid(vm_ref) != None)
656 def is_valid_dev(self, klass, dev_uuid):
657 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
659 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs):
660 dom = self.uuid_to_dom(vm_uuid)
661 fn(dom, *args, **kwargs)
663 def uuid_to_dom(self, vm_uuid):
664 self.domains_lock.acquire()
665 try:
666 for domid, dom in self.domains.items():
667 if dom.get_uuid() == vm_uuid:
668 return domid
670 if vm_uuid in self.managed_domains:
671 domid = self.managed_domains[vm_uuid].getDomid()
672 if domid is None:
673 return self.managed_domains[vm_uuid].getName()
674 else:
675 return domid
677 raise XendInvalidDomain(vm_uuid)
678 finally:
679 self.domains_lock.release()
682 def create_domain(self, xenapi_vm):
683 self.domains_lock.acquire()
684 try:
685 try:
686 xeninfo = XendConfig(xapi = xenapi_vm)
687 dominfo = XendDomainInfo.createDormant(xeninfo)
688 log.debug("Creating new managed domain: %s: %s" %
689 (dominfo.getName(), dominfo.get_uuid()))
690 self._managed_domain_register(dominfo)
691 self.managed_config_save(dominfo)
692 return dominfo.get_uuid()
693 except XendError, e:
694 raise
695 except Exception, e:
696 raise XendError(str(e))
697 finally:
698 self.domains_lock.release()
700 def rename_domain(self, dom, new_name):
701 self.domains_lock.acquire()
702 try:
703 old_name = dom.getName()
704 dom.setName(new_name)
706 finally:
707 self.domains_lock.release()
710 #
711 # End of Xen API
712 # ----------------------------------------------------------------
714 # ------------------------------------------------------------
715 # Xen Legacy API
717 def list(self, state = DOM_STATE_RUNNING):
718 """Get list of domain objects.
720 @param: the state in which the VMs should be -- one of the
721 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
722 @return: domains
723 @rtype: list of XendDomainInfo
724 """
725 if type(state) == int:
726 state = POWER_STATE_NAMES[state]
727 state = state.lower()
729 self.domains_lock.acquire()
730 try:
731 self._refresh(refresh_shutdown = False)
733 # active domains
734 active_domains = self.domains.values()
735 active_uuids = [d.get_uuid() for d in active_domains]
737 # inactive domains
738 inactive_domains = []
739 for dom_uuid, dom in self.managed_domains.items():
740 if dom_uuid not in active_uuids:
741 inactive_domains.append(dom)
743 if state == POWER_STATE_ALL:
744 return active_domains + inactive_domains
745 else:
746 return filter(lambda x:
747 POWER_STATE_NAMES[x.state].lower() == state,
748 active_domains + inactive_domains)
749 finally:
750 self.domains_lock.release()
753 def list_sorted(self, state = DOM_STATE_RUNNING):
754 """Get list of domain objects, sorted by name.
756 @param: the state in which the VMs should be -- one of the
757 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
758 @return: domain objects
759 @rtype: list of XendDomainInfo
760 """
761 doms = self.list(state)
762 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
763 return doms
765 def list_names(self, state = DOM_STATE_RUNNING):
766 """Get list of domain names.
768 @param: the state in which the VMs should be -- one of the
769 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
770 @return: domain names
771 @rtype: list of strings.
772 """
773 return [d.getName() for d in self.list_sorted(state)]
775 def domain_suspend(self, domname):
776 """Suspends a domain that is persistently managed by Xend
778 @param domname: Domain Name
779 @type domname: string
780 @rtype: None
781 @raise XendError: Failure during checkpointing.
782 """
784 try:
785 dominfo = self.domain_lookup_nr(domname)
786 if not dominfo:
787 raise XendInvalidDomain(domname)
789 if dominfo.getDomid() == DOM0_ID:
790 raise XendError("Cannot save privileged domain %s" % domname)
792 if dominfo.state != DOM_STATE_RUNNING:
793 raise VMBadState("Domain is not running",
794 POWER_STATE_NAMES[DOM_STATE_RUNNING],
795 POWER_STATE_NAMES[dominfo.state])
797 dom_uuid = dominfo.get_uuid()
799 if not os.path.exists(self._managed_config_path(dom_uuid)):
800 raise XendError("Domain is not managed by Xend lifecycle " +
801 "support.")
803 path = self._managed_check_point_path(dom_uuid)
804 oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
805 if hasattr(os, "O_LARGEFILE"):
806 oflags |= os.O_LARGEFILE
807 fd = os.open(path, oflags)
808 try:
809 # For now we don't support 'live checkpoint'
810 XendCheckpoint.save(fd, dominfo, False, False, path)
811 finally:
812 os.close(fd)
813 except OSError, ex:
814 raise XendError("can't write guest state file %s: %s" %
815 (path, ex[1]))
817 def domain_resume(self, domname, start_paused = False):
818 """Resumes a domain that is persistently managed by Xend.
820 @param domname: Domain Name
821 @type domname: string
822 @rtype: None
823 @raise XendError: If failed to restore.
824 """
825 self.domains_lock.acquire()
826 try:
827 try:
828 dominfo = self.domain_lookup_nr(domname)
830 if not dominfo:
831 raise XendInvalidDomain(domname)
833 if dominfo.getDomid() == DOM0_ID:
834 raise XendError("Cannot save privileged domain %s" % domname)
836 if dominfo.state != DOM_STATE_HALTED:
837 raise XendError("Cannot resume domain that is not halted.")
839 dom_uuid = dominfo.get_uuid()
840 chkpath = self._managed_check_point_path(dom_uuid)
841 if not os.path.exists(chkpath):
842 raise XendError("Domain was not suspended by Xend")
844 # Restore that replaces the existing XendDomainInfo
845 try:
846 log.debug('Current DomainInfo state: %d' % dominfo.state)
847 oflags = os.O_RDONLY
848 if hasattr(os, "O_LARGEFILE"):
849 oflags |= os.O_LARGEFILE
850 XendCheckpoint.restore(self,
851 os.open(chkpath, oflags),
852 dominfo,
853 paused = start_paused)
854 os.unlink(chkpath)
855 except OSError, ex:
856 raise XendError("Failed to read stored checkpoint file")
857 except IOError, ex:
858 raise XendError("Failed to delete checkpoint file")
859 except Exception, ex:
860 log.exception("Exception occurred when resuming")
861 raise XendError("Error occurred when resuming: %s" % str(ex))
862 finally:
863 self.domains_lock.release()
866 def domain_create(self, config):
867 """Create a domain from a configuration.
869 @param config: configuration
870 @type config: SXP Object (list of lists)
871 @rtype: XendDomainInfo
872 """
873 self.domains_lock.acquire()
874 try:
875 self._refresh()
877 dominfo = XendDomainInfo.create(config)
878 return dominfo
879 finally:
880 self.domains_lock.release()
883 def domain_create_from_dict(self, config_dict):
884 """Create a domain from a configuration dictionary.
886 @param config_dict: configuration
887 @rtype: XendDomainInfo
888 """
889 self.domains_lock.acquire()
890 try:
891 self._refresh()
893 dominfo = XendDomainInfo.create_from_dict(config_dict)
894 return dominfo
895 finally:
896 self.domains_lock.release()
899 def domain_new(self, config):
900 """Create a domain from a configuration but do not start it.
902 @param config: configuration
903 @type config: SXP Object (list of lists)
904 @rtype: XendDomainInfo
905 """
906 self.domains_lock.acquire()
907 try:
908 try:
909 domconfig = XendConfig(sxp_obj = config)
910 dominfo = XendDomainInfo.createDormant(domconfig)
911 log.debug("Creating new managed domain: %s" %
912 dominfo.getName())
913 self._managed_domain_register(dominfo)
914 self.managed_config_save(dominfo)
915 # no return value because it isn't meaningful for client
916 except XendError, e:
917 raise
918 except Exception, e:
919 raise XendError(str(e))
920 finally:
921 self.domains_lock.release()
923 def domain_start(self, domid, start_paused = True):
924 """Start a managed domain
926 @require: Domain must not be running.
927 @param domid: Domain name or domain ID.
928 @type domid: string or int
929 @rtype: None
930 @raise XendError: If domain is still running
931 @rtype: None
932 """
933 self.domains_lock.acquire()
934 try:
935 self._refresh()
937 dominfo = self.domain_lookup_nr(domid)
938 if not dominfo:
939 raise XendInvalidDomain(str(domid))
941 if dominfo.state != DOM_STATE_HALTED:
942 raise VMBadState("Domain is already running",
943 POWER_STATE_NAMES[DOM_STATE_HALTED],
944 POWER_STATE_NAMES[dominfo.state])
946 dominfo.start(is_managed = True)
947 finally:
948 self.domains_lock.release()
949 dominfo.waitForDevices()
950 if not start_paused:
951 dominfo.unpause()
954 def domain_delete(self, domid):
955 """Remove a managed domain from database
957 @require: Domain must not be running.
958 @param domid: Domain name or domain ID.
959 @type domid: string or int
960 @rtype: None
961 @raise XendError: If domain is still running
962 """
963 self.domains_lock.acquire()
964 try:
965 try:
966 dominfo = self.domain_lookup_nr(domid)
967 if not dominfo:
968 raise XendInvalidDomain(str(domid))
970 if dominfo.state != DOM_STATE_HALTED:
971 raise VMBadState("Domain is still running",
972 POWER_STATE_NAMES[DOM_STATE_HALTED],
973 POWER_STATE_NAMES[dominfo.state])
975 log.info("Domain %s (%s) deleted." %
976 (dominfo.getName(), dominfo.info.get('uuid')))
978 self._managed_domain_unregister(dominfo)
979 self.remove_domain(dominfo)
980 XendDevices.destroy_device_state(dominfo)
981 except Exception, ex:
982 raise XendError(str(ex))
983 finally:
984 self.domains_lock.release()
987 def domain_configure(self, config):
988 """Configure an existing domain.
990 @param vmconfig: vm configuration
991 @type vmconfig: SXP Object (list of lists)
992 @todo: Not implemented
993 """
994 # !!!
995 raise XendError("Unsupported")
997 def domain_restore(self, src, paused=False):
998 """Restore a domain from file.
1000 @param src: filename of checkpoint file to restore from
1001 @type src: string
1002 @return: Restored domain
1003 @rtype: XendDomainInfo
1004 @raise XendError: Failure to restore domain
1005 """
1006 try:
1007 oflags = os.O_RDONLY
1008 if hasattr(os, "O_LARGEFILE"):
1009 oflags |= os.O_LARGEFILE
1010 fd = os.open(src, oflags)
1011 try:
1012 return self.domain_restore_fd(fd, paused=paused)
1013 finally:
1014 os.close(fd)
1015 except OSError, ex:
1016 raise XendError("can't read guest state file %s: %s" %
1017 (src, ex[1]))
1019 def domain_restore_fd(self, fd, paused=False):
1020 """Restore a domain from the given file descriptor.
1022 @param fd: file descriptor of the checkpoint file
1023 @type fd: File object
1024 @rtype: XendDomainInfo
1025 @raise XendError: if failed to restore
1026 """
1028 try:
1029 return XendCheckpoint.restore(self, fd, paused=paused)
1030 except:
1031 # I don't really want to log this exception here, but the error
1032 # handling in the relocation-socket handling code (relocate.py) is
1033 # poor, so we need to log this for debugging.
1034 log.exception("Restore failed")
1035 raise XendError("Restore failed")
1037 def domain_unpause(self, domid):
1038 """Unpause domain execution.
1040 @param domid: Domain ID or Name
1041 @type domid: int or string.
1042 @rtype: None
1043 @raise XendError: Failed to unpause
1044 @raise XendInvalidDomain: Domain is not valid
1045 """
1046 try:
1047 dominfo = self.domain_lookup_nr(domid)
1048 if not dominfo:
1049 raise XendInvalidDomain(str(domid))
1050 if dominfo.getDomid() == DOM0_ID:
1051 raise XendError("Cannot unpause privileged domain %s" % domid)
1052 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
1053 int(dominfo.getDomid()))
1054 dominfo.unpause()
1055 except XendInvalidDomain:
1056 log.exception("domain_unpause")
1057 raise
1058 except Exception, ex:
1059 log.exception("domain_unpause")
1060 raise XendError(str(ex))
1062 def domain_pause(self, domid):
1063 """Pause domain execution.
1065 @param domid: Domain ID or Name
1066 @type domid: int or string.
1067 @rtype: None
1068 @raise XendError: Failed to pause
1069 @raise XendInvalidDomain: Domain is not valid
1070 """
1071 try:
1072 dominfo = self.domain_lookup_nr(domid)
1073 if not dominfo:
1074 raise XendInvalidDomain(str(domid))
1075 if dominfo.getDomid() == DOM0_ID:
1076 raise XendError("Cannot pause privileged domain %s" % domid)
1077 log.info("Domain %s (%d) paused.", dominfo.getName(),
1078 int(dominfo.getDomid()))
1079 dominfo.pause()
1080 except XendInvalidDomain:
1081 log.exception("domain_pause")
1082 raise
1083 except Exception, ex:
1084 log.exception("domain_pause")
1085 raise XendError(str(ex))
1087 def domain_dump(self, domid, filename, live, crash):
1088 """Dump domain core."""
1090 dominfo = self.domain_lookup_nr(domid)
1091 if not dominfo:
1092 raise XendInvalidDomain(str(domid))
1094 if dominfo.getDomid() == DOM0_ID:
1095 raise XendError("Cannot dump core for privileged domain %s" % domid)
1097 try:
1098 log.info("Domain core dump requested for domain %s (%d) "
1099 "live=%d crash=%d.",
1100 dominfo.getName(), dominfo.getDomid(), live, crash)
1101 return dominfo.dumpCore(filename)
1102 except Exception, ex:
1103 raise XendError(str(ex))
1105 def domain_destroy(self, domid):
1106 """Terminate domain immediately.
1108 @param domid: Domain ID or Name
1109 @type domid: int or string.
1110 @rtype: None
1111 @raise XendError: Failed to destroy
1112 @raise XendInvalidDomain: Domain is not valid
1113 """
1115 dominfo = self.domain_lookup_nr(domid)
1116 if dominfo and dominfo.getDomid() == DOM0_ID:
1117 raise XendError("Cannot destroy privileged domain %s" % domid)
1119 if dominfo:
1120 val = dominfo.destroy()
1121 else:
1122 try:
1123 val = xc.domain_destroy(int(domid))
1124 except ValueError:
1125 raise XendInvalidDomain(domid)
1126 except Exception, e:
1127 raise XendError(str(e))
1129 return val
1131 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1132 """Start domain migration.
1134 @param domid: Domain ID or Name
1135 @type domid: int or string.
1136 @param dst: Destination IP address
1137 @type dst: string
1138 @keyword port: relocation port on destination
1139 @type port: int
1140 @keyword live: Live migration
1141 @type live: bool
1142 @keyword resource: not used??
1143 @rtype: None
1144 @raise XendError: Failed to migrate
1145 @raise XendInvalidDomain: Domain is not valid
1146 """
1148 dominfo = self.domain_lookup_nr(domid)
1149 if not dominfo:
1150 raise XendInvalidDomain(str(domid))
1152 if dominfo.getDomid() == DOM0_ID:
1153 raise XendError("Cannot migrate privileged domain %s" % domid)
1155 """ The following call may raise a XendError exception """
1156 dominfo.testMigrateDevices(True, dst)
1158 if live:
1159 """ Make sure there's memory free for enabling shadow mode """
1160 dominfo.checkLiveMigrateMemory()
1162 if port == 0:
1163 port = xoptions.get_xend_relocation_port()
1164 try:
1165 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1166 sock.connect((dst, port))
1167 except socket.error, err:
1168 raise XendError("can't connect: %s" % err[1])
1170 sock.send("receive\n")
1171 sock.recv(80)
1172 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1173 sock.close()
1175 def domain_save(self, domid, dst, checkpoint):
1176 """Start saving a domain to file.
1178 @param domid: Domain ID or Name
1179 @type domid: int or string.
1180 @param dst: Destination filename
1181 @type dst: string
1182 @rtype: None
1183 @raise XendError: Failed to save domain
1184 @raise XendInvalidDomain: Domain is not valid
1185 """
1186 try:
1187 dominfo = self.domain_lookup_nr(domid)
1188 if not dominfo:
1189 raise XendInvalidDomain(str(domid))
1191 if dominfo.getDomid() == DOM0_ID:
1192 raise XendError("Cannot save privileged domain %i" % domid)
1194 oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
1195 if hasattr(os, "O_LARGEFILE"):
1196 oflags |= os.O_LARGEFILE
1197 fd = os.open(dst, oflags)
1198 try:
1199 XendCheckpoint.save(fd, dominfo, False, False, dst,
1200 checkpoint=checkpoint)
1201 finally:
1202 os.close(fd)
1203 except OSError, ex:
1204 raise XendError("can't write guest state file %s: %s" %
1205 (dst, ex[1]))
1207 def domain_pincpu(self, domid, vcpu, cpumap):
1208 """Set which cpus vcpu can use
1210 @param domid: Domain ID or Name
1211 @type domid: int or string.
1212 @param vcpu: vcpu to pin to
1213 @type vcpu: int
1214 @param cpumap: string repr of usable cpus
1215 @type cpumap: string
1216 @rtype: 0
1217 """
1218 dominfo = self.domain_lookup_nr(domid)
1219 if not dominfo:
1220 raise XendInvalidDomain(str(domid))
1222 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1223 vcpus = [ vcpu ]
1224 if str(vcpu).lower() == "all":
1225 vcpus = range(0, int(dominfo.getVCpuCount()))
1227 # set the same cpumask for all vcpus
1228 rc = 0
1229 for v in vcpus:
1230 try:
1231 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1232 except Exception, ex:
1233 raise XendError(str(ex))
1234 return rc
1236 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1237 weight):
1238 """Set Simple EDF scheduler parameters for a domain.
1240 @param domid: Domain ID or Name
1241 @type domid: int or string.
1242 @rtype: 0
1243 """
1244 dominfo = self.domain_lookup_nr(domid)
1245 if not dominfo:
1246 raise XendInvalidDomain(str(domid))
1247 try:
1248 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1249 latency, extratime, weight)
1250 except Exception, ex:
1251 raise XendError(str(ex))
1253 def domain_cpu_sedf_get(self, domid):
1254 """Get Simple EDF scheduler parameters for a domain.
1256 @param domid: Domain ID or Name
1257 @type domid: int or string.
1258 @rtype: SXP object
1259 @return: The parameters for Simple EDF schedule for a domain.
1260 """
1261 dominfo = self.domain_lookup_nr(domid)
1262 if not dominfo:
1263 raise XendInvalidDomain(str(domid))
1264 try:
1265 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1266 # return sxpr
1267 return ['sedf',
1268 ['domid', sedf_info['domid']],
1269 ['period', sedf_info['period']],
1270 ['slice', sedf_info['slice']],
1271 ['latency', sedf_info['latency']],
1272 ['extratime', sedf_info['extratime']],
1273 ['weight', sedf_info['weight']]]
1275 except Exception, ex:
1276 raise XendError(str(ex))
1278 def domain_shadow_control(self, domid, op):
1279 """Shadow page control.
1281 @param domid: Domain ID or Name
1282 @type domid: int or string.
1283 @param op: operation
1284 @type op: int
1285 @rtype: 0
1286 """
1287 dominfo = self.domain_lookup(domid)
1288 try:
1289 return xc.shadow_control(dominfo.getDomid(), op)
1290 except Exception, ex:
1291 raise XendError(str(ex))
1293 def domain_shadow_mem_get(self, domid):
1294 """Get shadow pagetable memory allocation.
1296 @param domid: Domain ID or Name
1297 @type domid: int or string.
1298 @rtype: int
1299 @return: shadow memory in MB
1300 """
1301 dominfo = self.domain_lookup(domid)
1302 try:
1303 return xc.shadow_mem_control(dominfo.getDomid())
1304 except Exception, ex:
1305 raise XendError(str(ex))
1307 def domain_shadow_mem_set(self, domid, mb):
1308 """Set shadow pagetable memory allocation.
1310 @param domid: Domain ID or Name
1311 @type domid: int or string.
1312 @param mb: shadow memory to set in MB
1313 @type: mb: int
1314 @rtype: int
1315 @return: shadow memory in MB
1316 """
1317 dominfo = self.domain_lookup(domid)
1318 try:
1319 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1320 except Exception, ex:
1321 raise XendError(str(ex))
1323 def domain_sched_credit_get(self, domid):
1324 """Get credit scheduler parameters for a domain.
1326 @param domid: Domain ID or Name
1327 @type domid: int or string.
1328 @rtype: dict with keys 'weight' and 'cap'
1329 @return: credit scheduler parameters
1330 """
1331 dominfo = self.domain_lookup_nr(domid)
1332 if not dominfo:
1333 raise XendInvalidDomain(str(domid))
1334 try:
1335 return xc.sched_credit_domain_get(dominfo.getDomid())
1336 except Exception, ex:
1337 raise XendError(str(ex))
1339 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1340 """Set credit scheduler parameters for a domain.
1342 @param domid: Domain ID or Name
1343 @type domid: int or string.
1344 @type weight: int
1345 @type cap: int
1346 @rtype: 0
1347 """
1348 dominfo = self.domain_lookup_nr(domid)
1349 if not dominfo:
1350 raise XendInvalidDomain(str(domid))
1351 try:
1352 if weight is None:
1353 weight = int(0)
1354 elif weight < 1 or weight > 65535:
1355 raise XendError("weight is out of range")
1357 if cap is None:
1358 cap = int(~0)
1359 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1360 raise XendError("cap is out of range")
1362 assert type(weight) == int
1363 assert type(cap) == int
1365 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1366 except Exception, ex:
1367 log.exception(ex)
1368 raise XendError(str(ex))
1370 def domain_maxmem_set(self, domid, mem):
1371 """Set the memory limit for a domain.
1373 @param domid: Domain ID or Name
1374 @type domid: int or string.
1375 @param mem: memory limit (in MiB)
1376 @type mem: int
1377 @raise XendError: fail to set memory
1378 @rtype: 0
1379 """
1380 dominfo = self.domain_lookup_nr(domid)
1381 if not dominfo:
1382 raise XendInvalidDomain(str(domid))
1383 dominfo.setMemoryMaximum(mem)
1385 def domain_ioport_range_enable(self, domid, first, last):
1386 """Enable access to a range of IO ports for a domain
1388 @param first: first IO port
1389 @param last: last IO port
1390 @raise XendError: failed to set range
1391 @rtype: 0
1392 """
1393 dominfo = self.domain_lookup_nr(domid)
1394 if not dominfo:
1395 raise XendInvalidDomain(str(domid))
1396 nr_ports = last - first + 1
1397 try:
1398 return xc.domain_ioport_permission(dominfo.getDomid(),
1399 first_port = first,
1400 nr_ports = nr_ports,
1401 allow_access = 1)
1402 except Exception, ex:
1403 raise XendError(str(ex))
1405 def domain_ioport_range_disable(self, domid, first, last):
1406 """Disable access to a range of IO ports for a domain
1408 @param first: first IO port
1409 @param last: last IO port
1410 @raise XendError: failed to set range
1411 @rtype: 0
1412 """
1413 dominfo = self.domain_lookup_nr(domid)
1414 if not dominfo:
1415 raise XendInvalidDomain(str(domid))
1416 nr_ports = last - first + 1
1417 try:
1418 return xc.domain_ioport_permission(dominfo.getDomid(),
1419 first_port = first,
1420 nr_ports = nr_ports,
1421 allow_access = 0)
1422 except Exception, ex:
1423 raise XendError(str(ex))
1425 def domain_send_trigger(self, domid, trigger_name, vcpu = 0):
1426 """Send trigger to a domain.
1428 @param domid: Domain ID or Name
1429 @type domid: int or string.
1430 @param trigger_name: trigger type name
1431 @type trigger_name: string
1432 @param vcpu: VCPU to send trigger (default is 0)
1433 @type vcpu: int
1434 @raise XendError: failed to send trigger
1435 @raise XendInvalidDomain: Domain is not valid
1436 @rtype: 0
1437 """
1438 dominfo = self.domain_lookup_nr(domid)
1439 if not dominfo:
1440 raise XendInvalidDomain(str(domid))
1441 if trigger_name.lower() in TRIGGER_TYPE:
1442 trigger = TRIGGER_TYPE[trigger_name.lower()]
1443 else:
1444 raise XendError("Invalid trigger: %s", trigger_name)
1445 try:
1446 return xc.domain_send_trigger(dominfo.getDomid(),
1447 trigger,
1448 vcpu)
1449 except Exception, ex:
1450 raise XendError(str(ex))
1453 def instance():
1454 """Singleton constructor. Use this instead of the class constructor.
1455 """
1456 global inst
1457 try:
1458 inst
1459 except:
1460 inst = XendDomain()
1461 inst.init()
1462 return inst