ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 12729:3570295a44cb

Added log message for when a VM is deleted.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Fri Dec 01 13:07:19 2006 +0000 (2006-12-01)
parents 36fe7ca48e54
children 39509c8f5c09
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 threading
31 import xen.lowlevel.xc
34 from xen.xend import XendRoot, XendCheckpoint, XendDomainInfo
35 from xen.xend.PrettyPrint import prettyprint
36 from xen.xend.XendConfig import XendConfig
37 from xen.xend.XendError import XendError, XendInvalidDomain, VmError
38 from xen.xend.XendLogging import log
39 from xen.xend.XendAPIConstants import XEN_API_VM_POWER_STATE
40 from xen.xend.XendConstants import XS_VMROOT
41 from xen.xend.XendConstants import DOM_STATE_HALTED, DOM_STATE_PAUSED
42 from xen.xend.XendConstants import DOM_STATE_RUNNING, DOM_STATE_SUSPENDED
43 from xen.xend.XendConstants import DOM_STATE_SHUTDOWN, DOM_STATE_UNKNOWN
44 from xen.xend.XendDevices import XendDevices
46 from xen.xend.xenstore.xstransact import xstransact
47 from xen.xend.xenstore.xswatch import xswatch
48 from xen.util import mkdir, security
49 from xen.xend import uuid
51 xc = xen.lowlevel.xc.xc()
52 xroot = XendRoot.instance()
54 __all__ = [ "XendDomain" ]
56 CACHED_CONFIG_FILE = 'config.sxp'
57 CHECK_POINT_FILE = 'checkpoint.chk'
58 DOM0_UUID = "00000000-0000-0000-0000-000000000000"
59 DOM0_NAME = "Domain-0"
60 DOM0_ID = 0
62 POWER_STATE_NAMES = dict([(x, XEN_API_VM_POWER_STATE[x])
63 for x in [DOM_STATE_HALTED,
64 DOM_STATE_PAUSED,
65 DOM_STATE_RUNNING,
66 DOM_STATE_SUSPENDED,
67 DOM_STATE_SHUTDOWN,
68 DOM_STATE_UNKNOWN]])
69 POWER_STATE_ALL = 'all'
72 class XendDomain:
73 """Index of all domains. Singleton.
75 @ivar domains: map of domains indexed by domid
76 @type domains: dict of XendDomainInfo
77 @ivar managed_domains: domains that are not running and managed by Xend
78 @type managed_domains: dict of XendDomainInfo indexed by uuid
79 @ivar domains_lock: lock that must be held when manipulating self.domains
80 @type domains_lock: threaading.RLock
81 @ivar _allow_new_domains: Flag to set that allows creating of new domains.
82 @type _allow_new_domains: boolean
83 """
85 def __init__(self):
86 self.domains = {}
87 self.managed_domains = {}
88 self.domains_lock = threading.RLock()
90 # xen api instance vars
91 # TODO: nothing uses this at the moment
92 self._allow_new_domains = True
94 # This must be called only the once, by instance() below. It is separate
95 # from the constructor because XendDomainInfo calls back into this class
96 # in order to check the uniqueness of domain names. This means that
97 # instance() must be able to return a valid instance of this class even
98 # during this initialisation.
99 def init(self):
100 """Singleton initialisation function."""
102 dom_path = self._managed_path()
103 mkdir.parents(dom_path, stat.S_IRWXU)
105 xstransact.Mkdir(XS_VMROOT)
106 xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID})
108 self.domains_lock.acquire()
109 try:
110 try:
111 dom0info = [d for d in self._running_domains() \
112 if d.get('domid') == DOM0_ID][0]
114 dom0info['name'] = DOM0_NAME
115 dom0 = XendDomainInfo.recreate(dom0info, True)
116 self._add_domain(dom0)
117 except IndexError:
118 raise XendError('Unable to find Domain 0')
120 self._setDom0CPUCount()
122 # This watch registration needs to be before the refresh call, so
123 # that we're sure that we haven't missed any releases, but inside
124 # the domains_lock, as we don't want the watch to fire until after
125 # the refresh call has completed.
126 xswatch("@introduceDomain", self._on_domains_changed)
127 xswatch("@releaseDomain", self._on_domains_changed)
129 self._init_domains()
130 finally:
131 self.domains_lock.release()
134 def _on_domains_changed(self, _):
135 """ Callback method when xenstore changes.
137 Calls refresh which will keep the local cache of domains
138 in sync.
140 @rtype: int
141 @return: 1
142 """
143 self.domains_lock.acquire()
144 try:
145 self._refresh()
146 finally:
147 self.domains_lock.release()
148 return 1
150 def _init_domains(self):
151 """Does the initial scan of managed and active domains to
152 populate self.domains.
154 Note: L{XendDomainInfo._checkName} will call back into XendDomain
155 to make sure domain name is not a duplicate.
157 """
158 self.domains_lock.acquire()
159 try:
160 running = self._running_domains()
161 managed = self._managed_domains()
163 # add all active domains
164 for dom in running:
165 if dom['dying'] == 1:
166 log.warn('Ignoring dying domain %d from now on' %
167 dom['domid'])
168 continue
170 if dom['domid'] != DOM0_ID:
171 try:
172 new_dom = XendDomainInfo.recreate(dom, False)
173 self._add_domain(new_dom)
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 = xroot.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 sxp_cache_file = open(self._managed_config_path(dom_uuid),'w')
284 prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78)
285 sxp_cache_file.close()
286 except:
287 log.exception("Error occurred saving configuration file " +
288 "to %s" % domain_config_dir)
289 try:
290 self._managed_domain_remove(dom_uuid)
291 except:
292 pass
293 raise XendError("Failed to save configuration file to: %s" %
294 domain_config_dir)
295 else:
296 log.warn("Trying to save configuration for invalid domain")
299 def _managed_domains(self):
300 """ Returns list of domains that are managed.
302 Expects to be protected by domains_lock.
304 @rtype: list of XendConfig
305 @return: List of domain configurations that are managed.
306 """
307 dom_path = self._managed_path()
308 dom_uuids = os.listdir(dom_path)
309 doms = []
310 for dom_uuid in dom_uuids:
311 try:
312 cfg_file = self._managed_config_path(dom_uuid)
313 cfg = XendConfig(filename = cfg_file)
314 if cfg.get('uuid') != dom_uuid:
315 # something is wrong with the SXP
316 log.error("UUID mismatch in stored configuration: %s" %
317 cfg_file)
318 continue
319 doms.append(cfg)
320 except Exception:
321 log.exception('Unable to open or parse config.sxp: %s' % \
322 cfg_file)
323 return doms
325 def _managed_domain_unregister(self, dom):
326 try:
327 if self.is_domain_managed(dom):
328 self._managed_config_remove(dom.get_uuid())
329 del self.managed_domains[dom.get_uuid()]
330 except ValueError:
331 log.warn("Domain is not registered: %s" % dom.get_uuid())
333 def _managed_domain_register(self, dom):
334 self.managed_domains[dom.get_uuid()] = dom
336 def is_domain_managed(self, dom = None):
337 return (dom.get_uuid() in self.managed_domains)
339 # End of Managed Domain Access
340 # --------------------------------------------------------------------
342 def _running_domains(self):
343 """Get table of domains indexed by id from xc.
345 @requires: Expects to be protected by domains_lock.
346 @rtype: list of dicts
347 @return: A list of dicts representing the running domains.
348 """
349 try:
350 return xc.domain_getinfo()
351 except RuntimeError, e:
352 log.exception("Unable to get domain information.")
353 return {}
355 def _setDom0CPUCount(self):
356 """Sets the number of VCPUs dom0 has. Retreived from the
357 Xend configuration, L{XendRoot}.
359 @requires: Expects to be protected by domains_lock.
360 @rtype: None
361 """
362 dom0 = self.privilegedDomain()
364 # get max number of vcpus to use for dom0 from config
365 target = int(xroot.get_dom0_vcpus())
366 log.debug("number of vcpus to use is %d", target)
368 # target == 0 means use all processors
369 if target > 0:
370 dom0.setVCpuCount(target)
373 def _refresh(self):
374 """Refresh the domain list. Needs to be called when
375 either xenstore has changed or when a method requires
376 up to date information (like uptime, cputime stats).
378 Expects to be protected by the domains_lock.
380 @rtype: None
381 """
383 running = self._running_domains()
384 # Add domains that are not already tracked but running in Xen,
385 # and update domain state for those that are running and tracked.
386 for dom in running:
387 domid = dom['domid']
388 if domid in self.domains:
389 self.domains[domid].update(dom)
390 elif domid not in self.domains and dom['dying'] != 1:
391 try:
392 new_dom = XendDomainInfo.recreate(dom, False)
393 self._add_domain(new_dom)
394 except VmError:
395 log.exception("Unable to recreate domain")
396 try:
397 xc.domain_destroy(domid)
398 except:
399 log.exception("Hard destruction of domain failed: %d" %
400 domid)
402 # update information for all running domains
403 # - like cpu_time, status, dying, etc.
404 # remove domains that are not running from active domain list.
405 # The list might have changed by now, because the update call may
406 # cause new domains to be added, if the domain has rebooted. We get
407 # the list again.
408 running = self._running_domains()
409 running_domids = [d['domid'] for d in running if d['dying'] != 1]
410 for domid, dom in self.domains.items():
411 if domid not in running_domids and domid != DOM0_ID:
412 self._remove_domain(dom, domid)
415 def _add_domain(self, info):
416 """Add a domain to the list of running domains
418 @requires: Expects to be protected by the domains_lock.
419 @param info: XendDomainInfo of a domain to be added.
420 @type info: XendDomainInfo
421 """
422 log.debug("Adding Domain: %s" % info.getDomid())
423 self.domains[info.getDomid()] = info
425 # update the managed domains with a new XendDomainInfo object
426 # if we are keeping track of it.
427 if info.get_uuid() in self.managed_domains:
428 self._managed_domain_register(info)
430 def _remove_domain(self, info, domid = None):
431 """Remove the domain from the list of running domains
433 @requires: Expects to be protected by the domains_lock.
434 @param info: XendDomainInfo of a domain to be removed.
435 @type info: XendDomainInfo
436 """
437 if info:
438 if domid == None:
439 domid = info.getDomid()
441 if info.state != DOM_STATE_HALTED:
442 info.cleanupDomain()
444 if domid in self.domains:
445 del self.domains[domid]
446 else:
447 log.warning("Attempted to remove non-existent domain.")
449 def restore_(self, config):
450 """Create a domain as part of the restore process. This is called
451 only from L{XendCheckpoint}.
453 A restore request comes into XendDomain through L{domain_restore}
454 or L{domain_restore_fd}. That request is
455 forwarded immediately to XendCheckpoint which, when it is ready, will
456 call this method. It is necessary to come through here rather than go
457 directly to L{XendDomainInfo.restore} because we need to
458 serialise the domain creation process, but cannot lock
459 domain_restore_fd as a whole, otherwise we will deadlock waiting for
460 the old domain to die.
462 @param config: Configuration of domain to restore
463 @type config: SXP Object (eg. list of lists)
464 """
465 self.domains_lock.acquire()
466 try:
467 security.refresh_ssidref(config)
468 dominfo = XendDomainInfo.restore(config)
469 self._add_domain(dominfo)
470 return dominfo
471 finally:
472 self.domains_lock.release()
475 def domain_lookup(self, domid):
476 """Look up given I{domid} in the list of managed and running
477 domains.
479 @note: Will cause a refresh before lookup up domains, for
480 a version that does not need to re-read xenstore
481 use L{domain_lookup_nr}.
483 @param domid: Domain ID or Domain Name.
484 @type domid: int or string
485 @return: Found domain.
486 @rtype: XendDomainInfo
487 @raise XendError: If domain is not found.
488 """
489 self.domains_lock.acquire()
490 try:
491 self._refresh()
492 dom = self.domain_lookup_nr(domid)
493 if not dom:
494 raise XendError("No domain named '%s'." % str(domid))
495 return dom
496 finally:
497 self.domains_lock.release()
500 def domain_lookup_nr(self, domid):
501 """Look up given I{domid} in the list of managed and running
502 domains.
504 @param domid: Domain ID or Domain Name.
505 @type domid: int or string
506 @return: Found domain.
507 @rtype: XendDomainInfo or None
508 """
509 self.domains_lock.acquire()
510 try:
511 # lookup by name
512 match = [dom for dom in self.domains.values() \
513 if dom.getName() == domid]
514 if match:
515 return match[0]
517 match = [dom for dom in self.managed_domains.values() \
518 if dom.getName() == domid]
519 if match:
520 return match[0]
522 # lookup by id
523 try:
524 if int(domid) in self.domains:
525 return self.domains[int(domid)]
526 except ValueError:
527 pass
529 # lookup by uuid for running domains
530 match = [dom for dom in self.domains.values() \
531 if dom.get_uuid() == domid]
532 if match:
533 return match[0]
535 # lookup by uuid for inactive managed domains
536 if domid in self.managed_domains:
537 return self.managed_domains[domid]
539 return None
540 finally:
541 self.domains_lock.release()
543 def privilegedDomain(self):
544 """ Get the XendDomainInfo of a dom0
546 @rtype: XendDomainInfo
547 """
548 self.domains_lock.acquire()
549 try:
550 return self.domains[DOM0_ID]
551 finally:
552 self.domains_lock.release()
554 def cleanup_domains(self):
555 """Clean up domains that are marked as autostop.
556 Should be called when Xend goes down. This is currently
557 called from L{xen.xend.servers.XMLRPCServer}.
559 """
560 log.debug('cleanup_domains')
561 self.domains_lock.acquire()
562 try:
563 for dom in self.domains.values():
564 if dom.getName() == DOM0_NAME:
565 continue
567 if dom.state == DOM_STATE_RUNNING:
568 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
569 if shutdownAction == 'shutdown':
570 log.debug('Shutting down domain: %s' % dom.getName())
571 dom.shutdown("poweroff")
572 elif shutdownAction == 'suspend':
573 self.domain_suspend(dom.getName())
574 finally:
575 self.domains_lock.release()
579 # ----------------------------------------------------------------
580 # Xen API
583 def set_allow_new_domains(self, allow_new_domains):
584 self._allow_new_domains = allow_new_domains
586 def allow_new_domains(self):
587 return self._allow_new_domains
589 def get_domain_refs(self):
590 result = []
591 try:
592 self.domains_lock.acquire()
593 result = [d.get_uuid() for d in self.domains.values()]
594 result += self.managed_domains.keys()
595 return result
596 finally:
597 self.domains_lock.release()
599 def get_vm_by_uuid(self, vm_uuid):
600 self.domains_lock.acquire()
601 try:
602 for dom in self.domains.values():
603 if dom.get_uuid() == vm_uuid:
604 return dom
606 if vm_uuid in self.managed_domains:
607 return self.managed_domains[vm_uuid]
609 return None
610 finally:
611 self.domains_lock.release()
613 def get_vm_with_dev_uuid(self, klass, dev_uuid):
614 self.domains_lock.acquire()
615 try:
616 for dom in self.domains.values() + self.managed_domains.values():
617 if dom.has_device(klass, dev_uuid):
618 return dom
619 return None
620 finally:
621 self.domains_lock.release()
623 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
624 self.domains_lock.acquire()
625 try:
626 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
627 if not dom:
628 return None
630 value = dom.get_device_property(klass, dev_uuid, field)
631 return value
632 except ValueError, e:
633 pass
635 return None
637 def is_valid_vm(self, vm_ref):
638 return (self.get_vm_by_uuid(vm_ref) != None)
640 def is_valid_dev(self, klass, dev_uuid):
641 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
643 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs):
644 self.domains_lock.acquire()
645 try:
646 for domid, dom in self.domains.items():
647 if dom.get_uuid == vm_uuid:
648 return fn(domid, *args, **kwargs)
650 if vm_uuid in self.managed_domains:
651 domid = self.managed_domains[vm_uuid].getDomid()
652 if domid == None:
653 domid = self.managed_domains[vm_uuid].getName()
654 return fn(domid, *args, **kwargs)
656 raise XendInvalidDomain("Domain does not exist")
657 finally:
658 self.domains_lock.release()
661 def create_domain(self, xenapi_vm):
662 self.domains_lock.acquire()
663 try:
664 try:
665 xeninfo = XendConfig(xapi = xenapi_vm)
666 dominfo = XendDomainInfo.createDormant(xeninfo)
667 log.debug("Creating new managed domain: %s: %s" %
668 (dominfo.getName(), dominfo.get_uuid()))
669 self._managed_domain_register(dominfo)
670 self.managed_config_save(dominfo)
671 return dominfo.get_uuid()
672 except XendError, e:
673 raise
674 except Exception, e:
675 raise XendError(str(e))
676 finally:
677 self.domains_lock.release()
679 def rename_domain(self, dom, new_name):
680 self.domains_lock.acquire()
681 try:
682 old_name = dom.getName()
683 dom.setName(new_name)
685 finally:
686 self.domains_lock.release()
689 #
690 # End of Xen API
691 # ----------------------------------------------------------------
693 # ------------------------------------------------------------
694 # Xen Legacy API
696 def list(self, state = DOM_STATE_RUNNING):
697 """Get list of domain objects.
699 @param: the state in which the VMs should be -- one of the
700 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
701 @return: domains
702 @rtype: list of XendDomainInfo
703 """
704 if type(state) == int:
705 state = POWER_STATE_NAMES[state]
706 state = state.lower()
708 self.domains_lock.acquire()
709 try:
710 self._refresh()
712 # active domains
713 active_domains = self.domains.values()
714 active_uuids = [d.get_uuid() for d in active_domains]
716 # inactive domains
717 inactive_domains = []
718 for dom_uuid, dom in self.managed_domains.items():
719 if dom_uuid not in active_uuids:
720 inactive_domains.append(dom)
722 if state == POWER_STATE_ALL:
723 return active_domains + inactive_domains
724 else:
725 return filter(lambda x:
726 POWER_STATE_NAMES[x.state].lower() == state,
727 active_domains + inactive_domains)
728 finally:
729 self.domains_lock.release()
732 def list_sorted(self, state = DOM_STATE_RUNNING):
733 """Get list of domain objects, sorted by name.
735 @param: the state in which the VMs should be -- one of the
736 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
737 @return: domain objects
738 @rtype: list of XendDomainInfo
739 """
740 doms = self.list(state)
741 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
742 return doms
744 def list_names(self, state = DOM_STATE_RUNNING):
745 """Get list of domain names.
747 @param: the state in which the VMs should be -- one of the
748 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
749 @return: domain names
750 @rtype: list of strings.
751 """
752 return [d.getName() for d in self.list_sorted(state)]
754 def domain_suspend(self, domname):
755 """Suspends a domain that is persistently managed by Xend
757 @param domname: Domain Name
758 @type domname: string
759 @rtype: None
760 @raise XendError: Failure during checkpointing.
761 """
763 try:
764 dominfo = self.domain_lookup_nr(domname)
765 if not dominfo:
766 raise XendInvalidDomain(domname)
768 if dominfo.getDomid() == DOM0_ID:
769 raise XendError("Cannot save privileged domain %s" % domname)
771 if dominfo.state != DOM_STATE_RUNNING:
772 raise XendError("Cannot suspend domain that is not running.")
774 dom_uuid = dominfo.get_uuid()
776 if not os.path.exists(self._managed_config_path(dom_uuid)):
777 raise XendError("Domain is not managed by Xend lifecycle " +
778 "support.")
780 path = self._managed_check_point_path(dom_uuid)
781 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
782 try:
783 # For now we don't support 'live checkpoint'
784 XendCheckpoint.save(fd, dominfo, False, False, path)
785 finally:
786 os.close(fd)
787 except OSError, ex:
788 raise XendError("can't write guest state file %s: %s" %
789 (path, ex[1]))
791 def domain_resume(self, domname, start_paused = False):
792 """Resumes a domain that is persistently managed by Xend.
794 @param domname: Domain Name
795 @type domname: string
796 @rtype: None
797 @raise XendError: If failed to restore.
798 """
799 self.domains_lock.acquire()
800 try:
801 try:
802 dominfo = self.domain_lookup_nr(domname)
804 if not dominfo:
805 raise XendInvalidDomain(domname)
807 if dominfo.getDomid() == DOM0_ID:
808 raise XendError("Cannot save privileged domain %s" % domname)
810 if dominfo.state != DOM_STATE_HALTED:
811 raise XendError("Cannot suspend domain that is not running.")
813 dom_uuid = dominfo.get_uuid()
814 chkpath = self._managed_check_point_path(dom_uuid)
815 if not os.path.exists(chkpath):
816 raise XendError("Domain was not suspended by Xend")
818 # Restore that replaces the existing XendDomainInfo
819 try:
820 log.debug('Current DomainInfo state: %d' % dominfo.state)
821 XendCheckpoint.restore(self,
822 os.open(chkpath, os.O_RDONLY),
823 dominfo,
824 paused = start_paused)
825 self._add_domain(dominfo)
826 os.unlink(chkpath)
827 except OSError, ex:
828 raise XendError("Failed to read stored checkpoint file")
829 except IOError, ex:
830 raise XendError("Failed to delete checkpoint file")
831 except Exception, ex:
832 log.exception("Exception occurred when resuming")
833 raise XendError("Error occurred when resuming: %s" % str(ex))
834 finally:
835 self.domains_lock.release()
838 def domain_create(self, config):
839 """Create a domain from a configuration.
841 @param config: configuration
842 @type config: SXP Object (list of lists)
843 @rtype: XendDomainInfo
844 """
845 self.domains_lock.acquire()
846 try:
847 self._refresh()
849 dominfo = XendDomainInfo.create(config)
850 self._add_domain(dominfo)
851 self.domain_sched_credit_set(dominfo.getDomid(),
852 dominfo.getWeight(),
853 dominfo.getCap())
854 return dominfo
855 finally:
856 self.domains_lock.release()
859 def domain_new(self, config):
860 """Create a domain from a configuration but do not start it.
862 @param config: configuration
863 @type config: SXP Object (list of lists)
864 @rtype: XendDomainInfo
865 """
866 self.domains_lock.acquire()
867 try:
868 try:
869 domconfig = XendConfig(sxp_obj = config)
870 dominfo = XendDomainInfo.createDormant(domconfig)
871 log.debug("Creating new managed domain: %s" %
872 dominfo.getName())
873 self._managed_domain_register(dominfo)
874 self.managed_config_save(dominfo)
875 # no return value because it isn't meaningful for client
876 except XendError, e:
877 raise
878 except Exception, e:
879 raise XendError(str(e))
880 finally:
881 self.domains_lock.release()
883 def domain_start(self, domid, start_paused = True):
884 """Start a managed domain
886 @require: Domain must not be running.
887 @param domid: Domain name or domain ID.
888 @type domid: string or int
889 @rtype: None
890 @raise XendError: If domain is still running
891 @rtype: None
892 """
893 self.domains_lock.acquire()
894 try:
895 self._refresh()
897 dominfo = self.domain_lookup_nr(domid)
898 if not dominfo:
899 raise XendInvalidDomain(str(domid))
901 if dominfo.state != DOM_STATE_HALTED:
902 raise XendError("Domain is already running")
904 dominfo.start(is_managed = True)
905 self._add_domain(dominfo)
906 finally:
907 self.domains_lock.release()
908 dominfo.waitForDevices()
909 if not start_paused:
910 dominfo.unpause()
913 def domain_delete(self, domid):
914 """Remove a managed domain from database
916 @require: Domain must not be running.
917 @param domid: Domain name or domain ID.
918 @type domid: string or int
919 @rtype: None
920 @raise XendError: If domain is still running
921 """
922 self.domains_lock.acquire()
923 try:
924 try:
925 dominfo = self.domain_lookup_nr(domid)
926 if not dominfo:
927 raise XendInvalidDomain(str(domid))
929 if dominfo.state != DOM_STATE_HALTED:
930 raise XendError("Domain is still running")
932 log.info("Domain %s (%s) deleted." %
933 (dominfo.getName(), dominfo.info.get('uuid')))
935 self._managed_domain_unregister(dominfo)
936 self._remove_domain(dominfo)
937 XendDevices.destroy_device_state(dominfo)
938 except Exception, ex:
939 raise XendError(str(ex))
940 finally:
941 self.domains_lock.release()
944 def domain_configure(self, config):
945 """Configure an existing domain.
947 @param vmconfig: vm configuration
948 @type vmconfig: SXP Object (list of lists)
949 @todo: Not implemented
950 """
951 # !!!
952 raise XendError("Unsupported")
954 def domain_restore(self, src, paused=False):
955 """Restore a domain from file.
957 @param src: filename of checkpoint file to restore from
958 @type src: string
959 @return: Restored domain
960 @rtype: XendDomainInfo
961 @raise XendError: Failure to restore domain
962 """
963 try:
964 fd = os.open(src, os.O_RDONLY)
965 try:
966 return self.domain_restore_fd(fd, paused=paused)
967 finally:
968 os.close(fd)
969 except OSError, ex:
970 raise XendError("can't read guest state file %s: %s" %
971 (src, ex[1]))
973 def domain_restore_fd(self, fd, paused=False):
974 """Restore a domain from the given file descriptor.
976 @param fd: file descriptor of the checkpoint file
977 @type fd: File object
978 @rtype: XendDomainInfo
979 @raise XendError: if failed to restore
980 """
982 try:
983 return XendCheckpoint.restore(self, fd, paused=paused)
984 except:
985 # I don't really want to log this exception here, but the error
986 # handling in the relocation-socket handling code (relocate.py) is
987 # poor, so we need to log this for debugging.
988 log.exception("Restore failed")
989 raise XendError("Restore failed")
991 def domain_unpause(self, domid):
992 """Unpause domain execution.
994 @param domid: Domain ID or Name
995 @type domid: int or string.
996 @rtype: None
997 @raise XendError: Failed to unpause
998 @raise XendInvalidDomain: Domain is not valid
999 """
1000 try:
1001 dominfo = self.domain_lookup_nr(domid)
1002 if not dominfo:
1003 raise XendInvalidDomain(str(domid))
1004 if dominfo.getDomid() == DOM0_ID:
1005 raise XendError("Cannot unpause privileged domain %s" % domid)
1006 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
1007 int(dominfo.getDomid()))
1008 dominfo.unpause()
1009 except XendInvalidDomain:
1010 log.exception("domain_unpause")
1011 raise
1012 except Exception, ex:
1013 log.exception("domain_unpause")
1014 raise XendError(str(ex))
1016 def domain_pause(self, domid):
1017 """Pause domain execution.
1019 @param domid: Domain ID or Name
1020 @type domid: int or string.
1021 @rtype: None
1022 @raise XendError: Failed to pause
1023 @raise XendInvalidDomain: Domain is not valid
1024 """
1025 try:
1026 dominfo = self.domain_lookup_nr(domid)
1027 if not dominfo:
1028 raise XendInvalidDomain(str(domid))
1029 if dominfo.getDomid() == DOM0_ID:
1030 raise XendError("Cannot pause privileged domain %s" % domid)
1031 log.info("Domain %s (%d) paused.", dominfo.getName(),
1032 int(dominfo.getDomid()))
1033 dominfo.pause()
1034 except XendInvalidDomain:
1035 log.exception("domain_pause")
1036 raise
1037 except Exception, ex:
1038 log.exception("domain_pause")
1039 raise XendError(str(ex))
1041 def domain_dump(self, domid, filename, live, crash):
1042 """Dump domain core."""
1044 dominfo = self.domain_lookup_nr(domid)
1045 if not dominfo:
1046 raise XendInvalidDomain(str(domid))
1048 if dominfo.getDomid() == DOM0_ID:
1049 raise XendError("Cannot dump core for privileged domain %s" % domid)
1051 try:
1052 log.info("Domain core dump requested for domain %s (%d) "
1053 "live=%d crash=%d.",
1054 dominfo.getName(), dominfo.getDomid(), live, crash)
1055 return dominfo.dumpCore(filename)
1056 except Exception, ex:
1057 raise XendError(str(ex))
1059 def domain_destroy(self, domid):
1060 """Terminate domain immediately.
1062 @param domid: Domain ID or Name
1063 @type domid: int or string.
1064 @rtype: None
1065 @raise XendError: Failed to destroy
1066 @raise XendInvalidDomain: Domain is not valid
1067 """
1069 dominfo = self.domain_lookup_nr(domid)
1070 if dominfo and dominfo.getDomid() == DOM0_ID:
1071 raise XendError("Cannot destroy privileged domain %s" % domid)
1073 if dominfo:
1074 val = dominfo.destroy()
1075 else:
1076 try:
1077 val = xc.domain_destroy(int(domid))
1078 except ValueError:
1079 raise XendInvalidDomain(domid)
1080 except Exception, e:
1081 raise XendError(str(e))
1083 return val
1085 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1086 """Start domain migration.
1088 @param domid: Domain ID or Name
1089 @type domid: int or string.
1090 @param dst: Destination IP address
1091 @type dst: string
1092 @keyword port: relocation port on destination
1093 @type port: int
1094 @keyword live: Live migration
1095 @type live: bool
1096 @keyword resource: not used??
1097 @rtype: None
1098 @raise XendError: Failed to migrate
1099 @raise XendInvalidDomain: Domain is not valid
1100 """
1102 dominfo = self.domain_lookup_nr(domid)
1103 if not dominfo:
1104 raise XendInvalidDomain(str(domid))
1106 if dominfo.getDomid() == DOM0_ID:
1107 raise XendError("Cannot migrate privileged domain %s" % domid)
1109 """ The following call may raise a XendError exception """
1110 dominfo.testMigrateDevices(True, dst)
1112 if live:
1113 """ Make sure there's memory free for enabling shadow mode """
1114 dominfo.checkLiveMigrateMemory()
1116 if port == 0:
1117 port = xroot.get_xend_relocation_port()
1118 try:
1119 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1120 sock.connect((dst, port))
1121 except socket.error, err:
1122 raise XendError("can't connect: %s" % err[1])
1124 sock.send("receive\n")
1125 sock.recv(80)
1126 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1127 dominfo.testDeviceComplete()
1128 sock.close()
1130 def domain_save(self, domid, dst):
1131 """Start saving a domain to file.
1133 @param domid: Domain ID or Name
1134 @type domid: int or string.
1135 @param dst: Destination filename
1136 @type dst: string
1137 @rtype: None
1138 @raise XendError: Failed to save domain
1139 @raise XendInvalidDomain: Domain is not valid
1140 """
1141 try:
1142 dominfo = self.domain_lookup_nr(domid)
1143 if not dominfo:
1144 raise XendInvalidDomain(str(domid))
1146 if dominfo.getDomid() == DOM0_ID:
1147 raise XendError("Cannot save privileged domain %i" % domid)
1149 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1150 try:
1151 # For now we don't support 'live checkpoint'
1152 XendCheckpoint.save(fd, dominfo, False, False, dst)
1153 finally:
1154 os.close(fd)
1155 except OSError, ex:
1156 raise XendError("can't write guest state file %s: %s" %
1157 (dst, ex[1]))
1159 def domain_pincpu(self, domid, vcpu, cpumap):
1160 """Set which cpus vcpu can use
1162 @param domid: Domain ID or Name
1163 @type domid: int or string.
1164 @param vcpu: vcpu to pin to
1165 @type vcpu: int
1166 @param cpumap: string repr of usable cpus
1167 @type cpumap: string
1168 @rtype: 0
1169 """
1170 dominfo = self.domain_lookup_nr(domid)
1171 if not dominfo:
1172 raise XendInvalidDomain(str(domid))
1174 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1175 vcpus = [ vcpu ]
1176 if str(vcpu).lower() == "all":
1177 vcpus = range(0, int(dominfo.getVCpuCount()))
1179 # set the same cpumask for all vcpus
1180 rc = 0
1181 for v in vcpus:
1182 try:
1183 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1184 except Exception, ex:
1185 raise XendError(str(ex))
1186 return rc
1188 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1189 weight):
1190 """Set Simple EDF scheduler parameters for a domain.
1192 @param domid: Domain ID or Name
1193 @type domid: int or string.
1194 @rtype: 0
1195 """
1196 dominfo = self.domain_lookup_nr(domid)
1197 if not dominfo:
1198 raise XendInvalidDomain(str(domid))
1199 try:
1200 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1201 latency, extratime, weight)
1202 except Exception, ex:
1203 raise XendError(str(ex))
1205 def domain_cpu_sedf_get(self, domid):
1206 """Get Simple EDF scheduler parameters for a domain.
1208 @param domid: Domain ID or Name
1209 @type domid: int or string.
1210 @rtype: SXP object
1211 @return: The parameters for Simple EDF schedule for a domain.
1212 """
1213 dominfo = self.domain_lookup_nr(domid)
1214 if not dominfo:
1215 raise XendInvalidDomain(str(domid))
1216 try:
1217 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1218 # return sxpr
1219 return ['sedf',
1220 ['domid', sedf_info['domid']],
1221 ['period', sedf_info['period']],
1222 ['slice', sedf_info['slice']],
1223 ['latency', sedf_info['latency']],
1224 ['extratime', sedf_info['extratime']],
1225 ['weight', sedf_info['weight']]]
1227 except Exception, ex:
1228 raise XendError(str(ex))
1230 def domain_shadow_control(self, domid, op):
1231 """Shadow page control.
1233 @param domid: Domain ID or Name
1234 @type domid: int or string.
1235 @param op: operation
1236 @type op: int
1237 @rtype: 0
1238 """
1239 dominfo = self.domain_lookup(domid)
1240 try:
1241 return xc.shadow_control(dominfo.getDomid(), op)
1242 except Exception, ex:
1243 raise XendError(str(ex))
1245 def domain_shadow_mem_get(self, domid):
1246 """Get shadow pagetable memory allocation.
1248 @param domid: Domain ID or Name
1249 @type domid: int or string.
1250 @rtype: int
1251 @return: shadow memory in MB
1252 """
1253 dominfo = self.domain_lookup(domid)
1254 try:
1255 return xc.shadow_mem_control(dominfo.getDomid())
1256 except Exception, ex:
1257 raise XendError(str(ex))
1259 def domain_shadow_mem_set(self, domid, mb):
1260 """Set shadow pagetable memory allocation.
1262 @param domid: Domain ID or Name
1263 @type domid: int or string.
1264 @param mb: shadow memory to set in MB
1265 @type: mb: int
1266 @rtype: int
1267 @return: shadow memory in MB
1268 """
1269 dominfo = self.domain_lookup(domid)
1270 try:
1271 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1272 except Exception, ex:
1273 raise XendError(str(ex))
1275 def domain_sched_credit_get(self, domid):
1276 """Get credit scheduler parameters for a domain.
1278 @param domid: Domain ID or Name
1279 @type domid: int or string.
1280 @rtype: dict with keys 'weight' and 'cap'
1281 @return: credit scheduler parameters
1282 """
1283 dominfo = self.domain_lookup_nr(domid)
1284 if not dominfo:
1285 raise XendInvalidDomain(str(domid))
1286 try:
1287 return xc.sched_credit_domain_get(dominfo.getDomid())
1288 except Exception, ex:
1289 raise XendError(str(ex))
1291 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1292 """Set credit scheduler parameters for a domain.
1294 @param domid: Domain ID or Name
1295 @type domid: int or string.
1296 @type weight: int
1297 @type cap: int
1298 @rtype: 0
1299 """
1300 dominfo = self.domain_lookup_nr(domid)
1301 if not dominfo:
1302 raise XendInvalidDomain(str(domid))
1303 try:
1304 if weight is None:
1305 weight = int(0)
1306 elif weight < 1 or weight > 65535:
1307 raise XendError("weight is out of range")
1309 if cap is None:
1310 cap = int(~0)
1311 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1312 raise XendError("cap is out of range")
1314 assert type(weight) == int
1315 assert type(cap) == int
1317 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1318 except Exception, ex:
1319 log.exception(ex)
1320 raise XendError(str(ex))
1322 def domain_maxmem_set(self, domid, mem):
1323 """Set the memory limit for a domain.
1325 @param domid: Domain ID or Name
1326 @type domid: int or string.
1327 @param mem: memory limit (in MiB)
1328 @type mem: int
1329 @raise XendError: fail to set memory
1330 @rtype: 0
1331 """
1332 dominfo = self.domain_lookup_nr(domid)
1333 if not dominfo:
1334 raise XendInvalidDomain(str(domid))
1335 maxmem = int(mem) * 1024
1336 try:
1337 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1338 except Exception, ex:
1339 raise XendError(str(ex))
1341 def domain_ioport_range_enable(self, domid, first, last):
1342 """Enable access to a range of IO ports for a domain
1344 @param first: first IO port
1345 @param last: last IO port
1346 @raise XendError: failed to set range
1347 @rtype: 0
1348 """
1349 dominfo = self.domain_lookup_nr(domid)
1350 if not dominfo:
1351 raise XendInvalidDomain(str(domid))
1352 nr_ports = last - first + 1
1353 try:
1354 return xc.domain_ioport_permission(dominfo.getDomid(),
1355 first_port = first,
1356 nr_ports = nr_ports,
1357 allow_access = 1)
1358 except Exception, ex:
1359 raise XendError(str(ex))
1361 def domain_ioport_range_disable(self, domid, first, last):
1362 """Disable access to a range of IO ports for a domain
1364 @param first: first IO port
1365 @param last: last IO port
1366 @raise XendError: failed to set range
1367 @rtype: 0
1368 """
1369 dominfo = self.domain_lookup_nr(domid)
1370 if not dominfo:
1371 raise XendInvalidDomain(str(domid))
1372 nr_ports = last - first + 1
1373 try:
1374 return xc.domain_ioport_permission(dominfo.getDomid(),
1375 first_port = first,
1376 nr_ports = nr_ports,
1377 allow_access = 0)
1378 except Exception, ex:
1379 raise XendError(str(ex))
1382 def instance():
1383 """Singleton constructor. Use this instead of the class constructor.
1384 """
1385 global inst
1386 try:
1387 inst
1388 except:
1389 inst = XendDomain()
1390 inst.init()
1391 return inst