ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 12725:36fe7ca48e54

Tidy up the creation of directories that Xend needs. This avoids potential
races in this creation.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Fri Dec 01 11:32:32 2006 +0000 (2006-12-01)
parents 2e5291248789
children 3570295a44cb
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 self._managed_domain_unregister(dominfo)
933 self._remove_domain(dominfo)
934 XendDevices.destroy_device_state(dominfo)
935 except Exception, ex:
936 raise XendError(str(ex))
937 finally:
938 self.domains_lock.release()
941 def domain_configure(self, config):
942 """Configure an existing domain.
944 @param vmconfig: vm configuration
945 @type vmconfig: SXP Object (list of lists)
946 @todo: Not implemented
947 """
948 # !!!
949 raise XendError("Unsupported")
951 def domain_restore(self, src, paused=False):
952 """Restore a domain from file.
954 @param src: filename of checkpoint file to restore from
955 @type src: string
956 @return: Restored domain
957 @rtype: XendDomainInfo
958 @raise XendError: Failure to restore domain
959 """
960 try:
961 fd = os.open(src, os.O_RDONLY)
962 try:
963 return self.domain_restore_fd(fd, paused=paused)
964 finally:
965 os.close(fd)
966 except OSError, ex:
967 raise XendError("can't read guest state file %s: %s" %
968 (src, ex[1]))
970 def domain_restore_fd(self, fd, paused=False):
971 """Restore a domain from the given file descriptor.
973 @param fd: file descriptor of the checkpoint file
974 @type fd: File object
975 @rtype: XendDomainInfo
976 @raise XendError: if failed to restore
977 """
979 try:
980 return XendCheckpoint.restore(self, fd, paused=paused)
981 except:
982 # I don't really want to log this exception here, but the error
983 # handling in the relocation-socket handling code (relocate.py) is
984 # poor, so we need to log this for debugging.
985 log.exception("Restore failed")
986 raise XendError("Restore failed")
988 def domain_unpause(self, domid):
989 """Unpause domain execution.
991 @param domid: Domain ID or Name
992 @type domid: int or string.
993 @rtype: None
994 @raise XendError: Failed to unpause
995 @raise XendInvalidDomain: Domain is not valid
996 """
997 try:
998 dominfo = self.domain_lookup_nr(domid)
999 if not dominfo:
1000 raise XendInvalidDomain(str(domid))
1001 if dominfo.getDomid() == DOM0_ID:
1002 raise XendError("Cannot unpause privileged domain %s" % domid)
1003 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
1004 int(dominfo.getDomid()))
1005 dominfo.unpause()
1006 except XendInvalidDomain:
1007 log.exception("domain_unpause")
1008 raise
1009 except Exception, ex:
1010 log.exception("domain_unpause")
1011 raise XendError(str(ex))
1013 def domain_pause(self, domid):
1014 """Pause domain execution.
1016 @param domid: Domain ID or Name
1017 @type domid: int or string.
1018 @rtype: None
1019 @raise XendError: Failed to pause
1020 @raise XendInvalidDomain: Domain is not valid
1021 """
1022 try:
1023 dominfo = self.domain_lookup_nr(domid)
1024 if not dominfo:
1025 raise XendInvalidDomain(str(domid))
1026 if dominfo.getDomid() == DOM0_ID:
1027 raise XendError("Cannot pause privileged domain %s" % domid)
1028 log.info("Domain %s (%d) paused.", dominfo.getName(),
1029 int(dominfo.getDomid()))
1030 dominfo.pause()
1031 except XendInvalidDomain:
1032 log.exception("domain_pause")
1033 raise
1034 except Exception, ex:
1035 log.exception("domain_pause")
1036 raise XendError(str(ex))
1038 def domain_dump(self, domid, filename, live, crash):
1039 """Dump domain core."""
1041 dominfo = self.domain_lookup_nr(domid)
1042 if not dominfo:
1043 raise XendInvalidDomain(str(domid))
1045 if dominfo.getDomid() == DOM0_ID:
1046 raise XendError("Cannot dump core for privileged domain %s" % domid)
1048 try:
1049 log.info("Domain core dump requested for domain %s (%d) "
1050 "live=%d crash=%d.",
1051 dominfo.getName(), dominfo.getDomid(), live, crash)
1052 return dominfo.dumpCore(filename)
1053 except Exception, ex:
1054 raise XendError(str(ex))
1056 def domain_destroy(self, domid):
1057 """Terminate domain immediately.
1059 @param domid: Domain ID or Name
1060 @type domid: int or string.
1061 @rtype: None
1062 @raise XendError: Failed to destroy
1063 @raise XendInvalidDomain: Domain is not valid
1064 """
1066 dominfo = self.domain_lookup_nr(domid)
1067 if dominfo and dominfo.getDomid() == DOM0_ID:
1068 raise XendError("Cannot destroy privileged domain %s" % domid)
1070 if dominfo:
1071 val = dominfo.destroy()
1072 else:
1073 try:
1074 val = xc.domain_destroy(int(domid))
1075 except ValueError:
1076 raise XendInvalidDomain(domid)
1077 except Exception, e:
1078 raise XendError(str(e))
1080 return val
1082 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1083 """Start domain migration.
1085 @param domid: Domain ID or Name
1086 @type domid: int or string.
1087 @param dst: Destination IP address
1088 @type dst: string
1089 @keyword port: relocation port on destination
1090 @type port: int
1091 @keyword live: Live migration
1092 @type live: bool
1093 @keyword resource: not used??
1094 @rtype: None
1095 @raise XendError: Failed to migrate
1096 @raise XendInvalidDomain: Domain is not valid
1097 """
1099 dominfo = self.domain_lookup_nr(domid)
1100 if not dominfo:
1101 raise XendInvalidDomain(str(domid))
1103 if dominfo.getDomid() == DOM0_ID:
1104 raise XendError("Cannot migrate privileged domain %s" % domid)
1106 """ The following call may raise a XendError exception """
1107 dominfo.testMigrateDevices(True, dst)
1109 if live:
1110 """ Make sure there's memory free for enabling shadow mode """
1111 dominfo.checkLiveMigrateMemory()
1113 if port == 0:
1114 port = xroot.get_xend_relocation_port()
1115 try:
1116 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1117 sock.connect((dst, port))
1118 except socket.error, err:
1119 raise XendError("can't connect: %s" % err[1])
1121 sock.send("receive\n")
1122 sock.recv(80)
1123 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1124 dominfo.testDeviceComplete()
1125 sock.close()
1127 def domain_save(self, domid, dst):
1128 """Start saving a domain to file.
1130 @param domid: Domain ID or Name
1131 @type domid: int or string.
1132 @param dst: Destination filename
1133 @type dst: string
1134 @rtype: None
1135 @raise XendError: Failed to save domain
1136 @raise XendInvalidDomain: Domain is not valid
1137 """
1138 try:
1139 dominfo = self.domain_lookup_nr(domid)
1140 if not dominfo:
1141 raise XendInvalidDomain(str(domid))
1143 if dominfo.getDomid() == DOM0_ID:
1144 raise XendError("Cannot save privileged domain %i" % domid)
1146 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1147 try:
1148 # For now we don't support 'live checkpoint'
1149 XendCheckpoint.save(fd, dominfo, False, False, dst)
1150 finally:
1151 os.close(fd)
1152 except OSError, ex:
1153 raise XendError("can't write guest state file %s: %s" %
1154 (dst, ex[1]))
1156 def domain_pincpu(self, domid, vcpu, cpumap):
1157 """Set which cpus vcpu can use
1159 @param domid: Domain ID or Name
1160 @type domid: int or string.
1161 @param vcpu: vcpu to pin to
1162 @type vcpu: int
1163 @param cpumap: string repr of usable cpus
1164 @type cpumap: string
1165 @rtype: 0
1166 """
1167 dominfo = self.domain_lookup_nr(domid)
1168 if not dominfo:
1169 raise XendInvalidDomain(str(domid))
1171 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1172 vcpus = [ vcpu ]
1173 if str(vcpu).lower() == "all":
1174 vcpus = range(0, int(dominfo.getVCpuCount()))
1176 # set the same cpumask for all vcpus
1177 rc = 0
1178 for v in vcpus:
1179 try:
1180 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1181 except Exception, ex:
1182 raise XendError(str(ex))
1183 return rc
1185 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1186 weight):
1187 """Set Simple EDF scheduler parameters for a domain.
1189 @param domid: Domain ID or Name
1190 @type domid: int or string.
1191 @rtype: 0
1192 """
1193 dominfo = self.domain_lookup_nr(domid)
1194 if not dominfo:
1195 raise XendInvalidDomain(str(domid))
1196 try:
1197 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1198 latency, extratime, weight)
1199 except Exception, ex:
1200 raise XendError(str(ex))
1202 def domain_cpu_sedf_get(self, domid):
1203 """Get Simple EDF scheduler parameters for a domain.
1205 @param domid: Domain ID or Name
1206 @type domid: int or string.
1207 @rtype: SXP object
1208 @return: The parameters for Simple EDF schedule for a domain.
1209 """
1210 dominfo = self.domain_lookup_nr(domid)
1211 if not dominfo:
1212 raise XendInvalidDomain(str(domid))
1213 try:
1214 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1215 # return sxpr
1216 return ['sedf',
1217 ['domid', sedf_info['domid']],
1218 ['period', sedf_info['period']],
1219 ['slice', sedf_info['slice']],
1220 ['latency', sedf_info['latency']],
1221 ['extratime', sedf_info['extratime']],
1222 ['weight', sedf_info['weight']]]
1224 except Exception, ex:
1225 raise XendError(str(ex))
1227 def domain_shadow_control(self, domid, op):
1228 """Shadow page control.
1230 @param domid: Domain ID or Name
1231 @type domid: int or string.
1232 @param op: operation
1233 @type op: int
1234 @rtype: 0
1235 """
1236 dominfo = self.domain_lookup(domid)
1237 try:
1238 return xc.shadow_control(dominfo.getDomid(), op)
1239 except Exception, ex:
1240 raise XendError(str(ex))
1242 def domain_shadow_mem_get(self, domid):
1243 """Get shadow pagetable memory allocation.
1245 @param domid: Domain ID or Name
1246 @type domid: int or string.
1247 @rtype: int
1248 @return: shadow memory in MB
1249 """
1250 dominfo = self.domain_lookup(domid)
1251 try:
1252 return xc.shadow_mem_control(dominfo.getDomid())
1253 except Exception, ex:
1254 raise XendError(str(ex))
1256 def domain_shadow_mem_set(self, domid, mb):
1257 """Set shadow pagetable memory allocation.
1259 @param domid: Domain ID or Name
1260 @type domid: int or string.
1261 @param mb: shadow memory to set in MB
1262 @type: mb: int
1263 @rtype: int
1264 @return: shadow memory in MB
1265 """
1266 dominfo = self.domain_lookup(domid)
1267 try:
1268 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1269 except Exception, ex:
1270 raise XendError(str(ex))
1272 def domain_sched_credit_get(self, domid):
1273 """Get credit scheduler parameters for a domain.
1275 @param domid: Domain ID or Name
1276 @type domid: int or string.
1277 @rtype: dict with keys 'weight' and 'cap'
1278 @return: credit scheduler parameters
1279 """
1280 dominfo = self.domain_lookup_nr(domid)
1281 if not dominfo:
1282 raise XendInvalidDomain(str(domid))
1283 try:
1284 return xc.sched_credit_domain_get(dominfo.getDomid())
1285 except Exception, ex:
1286 raise XendError(str(ex))
1288 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1289 """Set credit scheduler parameters for a domain.
1291 @param domid: Domain ID or Name
1292 @type domid: int or string.
1293 @type weight: int
1294 @type cap: int
1295 @rtype: 0
1296 """
1297 dominfo = self.domain_lookup_nr(domid)
1298 if not dominfo:
1299 raise XendInvalidDomain(str(domid))
1300 try:
1301 if weight is None:
1302 weight = int(0)
1303 elif weight < 1 or weight > 65535:
1304 raise XendError("weight is out of range")
1306 if cap is None:
1307 cap = int(~0)
1308 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1309 raise XendError("cap is out of range")
1311 assert type(weight) == int
1312 assert type(cap) == int
1314 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1315 except Exception, ex:
1316 log.exception(ex)
1317 raise XendError(str(ex))
1319 def domain_maxmem_set(self, domid, mem):
1320 """Set the memory limit for a domain.
1322 @param domid: Domain ID or Name
1323 @type domid: int or string.
1324 @param mem: memory limit (in MiB)
1325 @type mem: int
1326 @raise XendError: fail to set memory
1327 @rtype: 0
1328 """
1329 dominfo = self.domain_lookup_nr(domid)
1330 if not dominfo:
1331 raise XendInvalidDomain(str(domid))
1332 maxmem = int(mem) * 1024
1333 try:
1334 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1335 except Exception, ex:
1336 raise XendError(str(ex))
1338 def domain_ioport_range_enable(self, domid, first, last):
1339 """Enable access to a range of IO ports for a domain
1341 @param first: first IO port
1342 @param last: last IO port
1343 @raise XendError: failed to set range
1344 @rtype: 0
1345 """
1346 dominfo = self.domain_lookup_nr(domid)
1347 if not dominfo:
1348 raise XendInvalidDomain(str(domid))
1349 nr_ports = last - first + 1
1350 try:
1351 return xc.domain_ioport_permission(dominfo.getDomid(),
1352 first_port = first,
1353 nr_ports = nr_ports,
1354 allow_access = 1)
1355 except Exception, ex:
1356 raise XendError(str(ex))
1358 def domain_ioport_range_disable(self, domid, first, last):
1359 """Disable access to a range of IO ports for a domain
1361 @param first: first IO port
1362 @param last: last IO port
1363 @raise XendError: failed to set range
1364 @rtype: 0
1365 """
1366 dominfo = self.domain_lookup_nr(domid)
1367 if not dominfo:
1368 raise XendInvalidDomain(str(domid))
1369 nr_ports = last - first + 1
1370 try:
1371 return xc.domain_ioport_permission(dominfo.getDomid(),
1372 first_port = first,
1373 nr_ports = nr_ports,
1374 allow_access = 0)
1375 except Exception, ex:
1376 raise XendError(str(ex))
1379 def instance():
1380 """Singleton constructor. Use this instead of the class constructor.
1381 """
1382 global inst
1383 try:
1384 inst
1385 except:
1386 inst = XendDomain()
1387 inst.init()
1388 return inst