ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 12148:57f59959aa80

[XEND] Fix some missing variables in XendDomain caught by pylint.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Fri Oct 27 16:45:40 2006 +0100 (2006-10-27)
parents 1f790f5fcdbb
children 505cd4708946
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 shutil
27 import socket
28 import threading
30 import xen.lowlevel.xc
33 from xen.xend import XendRoot, XendCheckpoint, XendDomainInfo
34 from xen.xend.PrettyPrint import prettyprint
35 from xen.xend.XendConfig import XendConfig
36 from xen.xend.XendError import XendError, XendInvalidDomain
37 from xen.xend.XendLogging import log
38 from xen.xend.XendConstants import XS_VMROOT
40 from xen.xend.xenstore.xstransact import xstransact
41 from xen.xend.xenstore.xswatch import xswatch
42 from xen.util import security
43 from xen.xend import uuid
45 xc = xen.lowlevel.xc.xc()
46 xroot = XendRoot.instance()
48 __all__ = [ "XendDomain" ]
50 CACHED_CONFIG_FILE = 'config.sxp'
51 CHECK_POINT_FILE = 'checkpoint.chk'
52 DOM0_UUID = "00000000-0000-0000-0000-000000000000"
53 DOM0_NAME = "Domain-0"
54 DOM0_ID = 0
56 class XendDomain:
57 """Index of all domains. Singleton.
59 @ivar domains: map of domains indexed by UUID Strings
60 @type domains: dict of XendDomainInfo
61 @ivar domains_managed: uuid of domains that are managed by Xend
62 @type managed_domains: list of (uuids, dom_name)
63 @ivar domains_lock: lock that must be held when manipulating self.domains
64 @type domains_lock: threaading.RLock
65 @ivar _allow_new_domains: Flag to set that allows creating of new domains.
66 @type _allow_new_domains: boolean
68 """
70 def __init__(self):
71 self.domains = {}
72 self.managed_domains = []
73 self.domains_lock = threading.RLock()
75 # xen api instance vars
76 # TODO: nothing uses this at the moment
77 self._allow_new_domains = True
79 # This must be called only the once, by instance() below. It is separate
80 # from the constructor because XendDomainInfo calls back into this class
81 # in order to check the uniqueness of domain names. This means that
82 # instance() must be able to return a valid instance of this class even
83 # during this initialisation.
84 def init(self):
85 """Singleton initialisation function."""
87 xstransact.Mkdir(XS_VMROOT)
88 xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID})
90 self.domains_lock.acquire()
91 try:
92 try:
93 dom0info = [d for d in self._running_domains() \
94 if d.get('domid') == DOM0_ID][0]
96 dom0info['name'] = DOM0_NAME
97 dom0 = XendDomainInfo.recreate(dom0info, True)
98 self._add_domain(dom0)
99 except IndexError:
100 raise XendError('Unable to find Domain 0')
102 self._setDom0CPUCount()
104 # This watch registration needs to be before the refresh call, so
105 # that we're sure that we haven't missed any releases, but inside
106 # the domains_lock, as we don't want the watch to fire until after
107 # the refresh call has completed.
108 xswatch("@introduceDomain", self._on_domains_changed)
109 xswatch("@releaseDomain", self._on_domains_changed)
111 self._init_domains()
112 finally:
113 self.domains_lock.release()
116 def _on_domains_changed(self, _):
117 """ Callback method when xenstore changes.
119 Calls refresh which will keep the local cache of domains
120 in sync.
122 @rtype: int
123 @return: 1
124 """
125 self.domains_lock.acquire()
126 try:
127 self._refresh()
128 finally:
129 self.domains_lock.release()
130 return 1
132 def _init_domains(self):
133 """Does the initial scan of managed and active domains to
134 populate self.domains.
136 Note: L{XendDomainInfo._checkName} will call back into XendDomain
137 to make sure domain name is not a duplicate.
139 """
140 self.domains_lock.acquire()
141 try:
142 running = self._running_domains()
143 managed = self._managed_domains()
145 # add all active domains
146 for dom in running:
147 if dom['domid'] != DOM0_ID:
148 try:
149 new_dom = XendDomainInfo.recreate(dom, False)
150 self._add_domain(new_dom)
151 except Exception:
152 log.exception("Failed to create reference to running "
153 "domain id: %d" % dom['domid'])
155 # add all managed domains as dormant domains.
156 for dom in managed:
157 dom_uuid = dom.get('uuid', uuid.createString())
158 dom['uuid'] = dom_uuid
159 dom_name = dom.get('name', 'Domain-%s' % dom_uuid)
161 try:
162 running_dom = self.domain_lookup_nr(dom_name)
163 if not running_dom:
164 # instantiate domain if not started.
165 new_dom = XendDomainInfo.createDormant(dom)
166 self._add_domain(new_dom)
167 self._managed_domain_register(new_dom)
168 else:
169 self._managed_domain_register(running_dom)
170 except Exception:
171 log.exception("Failed to create reference to managed "
172 "domain: %s" % dom_name)
174 finally:
175 self.domains_lock.release()
178 # -----------------------------------------------------------------
179 # Getting managed domains storage path names
181 def _managed_path(self, domuuid = None):
182 """Returns the path of the directory where managed domain
183 information is stored.
185 @keyword domuuid: If not None, will return the path to the domain
186 otherwise, will return the path containing
187 the directories which represent each domain.
188 @type: None or String.
189 @rtype: String
190 @return: Path.
191 """
192 dom_path = xroot.get_xend_domains_path()
193 if domuuid:
194 dom_path = os.path.join(dom_path, domuuid)
195 return dom_path
197 def _managed_config_path(self, domuuid):
198 """Returns the path to the configuration file of a managed domain.
200 @param domname: Domain uuid
201 @type domname: String
202 @rtype: String
203 @return: path to config file.
204 """
205 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
207 def _managed_check_point_path(self, domuuid):
208 """Returns absolute path to check point file for managed domain.
210 @param domuuid: Name of managed domain
211 @type domname: String
212 @rtype: String
213 @return: Path
214 """
215 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
217 def _managed_config_remove(self, domuuid):
218 """Removes a domain configuration from managed list
220 @param domuuid: Name of managed domain
221 @type domname: String
222 @raise XendError: fails to remove the domain.
223 """
224 config_path = self._managed_path(domuuid)
225 try:
226 if os.path.exists(config_path) and os.path.isdir(config_path):
227 shutil.rmtree(config_path)
228 except IOError:
229 log.exception('managed_config_remove failed removing conf')
230 raise XendError("Unable to remove managed configuration"
231 " for domain: %s" % domuuid)
233 def managed_config_save(self, dominfo):
234 """Save a domain's configuration to disk
236 @param domninfo: Managed domain to save.
237 @type dominfo: XendDomainInfo
238 @raise XendError: fails to save configuration.
239 @rtype: None
240 """
241 if not self.is_domain_managed(dominfo):
242 return # refuse to save configuration this domain isn't managed
244 if dominfo:
245 domains_dir = self._managed_path()
246 dom_uuid = dominfo.get_uuid()
247 domain_config_dir = self._managed_path(dom_uuid)
249 # make sure the domain dir exists
250 if not os.path.exists(domains_dir):
251 os.makedirs(domains_dir, 0755)
252 elif not os.path.isdir(domains_dir):
253 log.error("xend_domain_dir is not a directory.")
254 raise XendError("Unable to save managed configuration "
255 "because %s is not a directory." %
256 domains_dir)
258 if not os.path.exists(domain_config_dir):
259 try:
260 os.makedirs(domain_config_dir, 0755)
261 except IOError:
262 log.exception("Failed to create directory: %s" %
263 domain_config_dir)
264 raise XendError("Failed to create directory: %s" %
265 domain_config_dir)
267 try:
268 sxp_cache_file = open(self._managed_config_path(dom_uuid),'w')
269 prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78)
270 sxp_cache_file.close()
271 except IOError:
272 log.error("Error occurred saving configuration file to %s" %
273 domain_config_dir)
274 raise XendError("Failed to save configuration file to: %s" %
275 domain_config_dir)
276 else:
277 log.warn("Trying to save configuration for invalid domain")
280 def _managed_domains(self):
281 """ Returns list of domains that are managed.
283 Expects to be protected by domains_lock.
285 @rtype: list of XendConfig
286 @return: List of domain configurations that are managed.
287 """
288 dom_path = self._managed_path()
289 dom_uuids = os.listdir(dom_path)
290 doms = []
291 for dom_uuid in dom_uuids:
292 try:
293 cfg_file = self._managed_config_path(dom_uuid)
294 cfg = XendConfig(filename = cfg_file)
295 doms.append(cfg)
296 except Exception:
297 log.exception('Unable to open or parse config.sxp: %s' % \
298 cfg_file)
299 return doms
301 def _managed_domain_unregister(self, dom):
302 try:
303 self.managed_domains.remove((dom.get_uuid(), dom.getName()))
304 except ValueError:
305 log.warn("Domain is not registered: %s" % dom.get_uuid())
307 def _managed_domain_register(self, dom):
308 self.managed_domains.append((dom.get_uuid(), dom.getName()))
310 def _managed_domain_rename(self, dom, new_name):
311 for i in range(len(self.managed_domains)):
312 if self.managed_domains[i][0] == dom.get_uuid():
313 self.managed_domains[i][1] = new_name
314 return True
315 return False
317 def is_domain_managed(self, dom = None, dom_name = None):
318 dom_uuid = dom.get_uuid()
319 dom_name = dom.getName()
320 if dom:
321 return ((dom_uuid, dom_name) in self.managed_domains)
322 if dom_name:
323 results = [d for d in self.managed_domains if d[1] == dom_name]
324 return (len(results) > 0)
325 return False
329 # End of Managed Domain Access
330 # --------------------------------------------------------------------
332 def _running_domains(self):
333 """Get table of domains indexed by id from xc.
335 @requires: Expects to be protected by domains_lock.
336 @rtype: list of dicts
337 @return: A list of dicts representing the running domains.
338 """
339 return xc.domain_getinfo()
341 def _setDom0CPUCount(self):
342 """Sets the number of VCPUs dom0 has. Retreived from the
343 Xend configuration, L{XendRoot}.
345 @requires: Expects to be protected by domains_lock.
346 @rtype: None
347 """
348 dom0 = self.privilegedDomain()
350 # get max number of vcpus to use for dom0 from config
351 target = int(xroot.get_dom0_vcpus())
352 log.debug("number of vcpus to use is %d", target)
354 # target == 0 means use all processors
355 if target > 0:
356 dom0.setVCpuCount(target)
359 def _refresh(self):
360 """Refresh the domain list. Needs to be called when
361 either xenstore has changed or when a method requires
362 up to date information (like uptime, cputime stats).
364 @rtype: None
365 """
366 self.domains_lock.acquire()
367 try:
368 # update information for all running domains
369 # - like cpu_time, status, dying, etc.
370 running = self._running_domains()
371 for dom in running:
372 dom_info = self.domain_lookup_nr(dom['domid'])
373 if dom_info:
374 dom_info.update(dom)
376 # clean up unmanaged domains
377 for dom in self.domains.values():
378 if (dom.getDomid() == None) and \
379 not self.is_domain_managed(dom):
380 self._remove_domain(dom)
382 finally:
383 self.domains_lock.release()
385 def _add_domain(self, info, managed = False):
386 """Add the given domain entry to this instance's internal cache.
388 @requires: Expects to be protected by the domains_lock.
389 @param info: XendDomainInfo of a domain to be added.
390 @type info: XendDomainInfo
391 @keyword managed: Whether this domain is maanged by Xend
392 @type managed: boolean
393 """
394 log.debug("Adding Domain: %s" % info.get_uuid())
395 self.domains[info.get_uuid()] = info
396 if managed and not self.is_domain_managed(info):
397 self._managed_domain_register(info)
399 def _remove_domain(self, info):
400 """Remove the given domain from this instance's internal cache.
402 @requires: Expects to be protected by the domains_lock.
403 @param info: XendDomainInfo of a domain to be removed.
404 @type info: XendDomainInfo
405 """
406 if info:
407 dom_name = info.getName()
408 dom_uuid = info.get_uuid()
410 if info.state != XendDomainInfo.DOM_STATE_HALTED:
411 info.cleanupDomain()
413 if self.is_domain_managed(info):
414 self._managed_config_remove(dom_uuid)
415 self._managed_domain_unregister(info)
417 try:
418 del self.domains[dom_uuid]
419 except KeyError:
420 pass
421 else:
422 log.warning("Attempted to remove non-existent domain.")
424 def restore_(self, config):
425 """Create a domain as part of the restore process. This is called
426 only from L{XendCheckpoint}.
428 A restore request comes into XendDomain through L{domain_restore}
429 or L{domain_restore_fd}. That request is
430 forwarded immediately to XendCheckpoint which, when it is ready, will
431 call this method. It is necessary to come through here rather than go
432 directly to L{XendDomainInfo.restore} because we need to
433 serialise the domain creation process, but cannot lock
434 domain_restore_fd as a whole, otherwise we will deadlock waiting for
435 the old domain to die.
437 @param config: Configuration of domain to restore
438 @type config: SXP Object (eg. list of lists)
439 """
440 self.domains_lock.acquire()
441 try:
442 security.refresh_ssidref(config)
443 dominfo = XendDomainInfo.restore(config)
444 self._add_domain(dominfo)
445 return dominfo
446 finally:
447 self.domains_lock.release()
450 def domain_lookup(self, domid):
451 """Look up given I{domid} in the list of managed and running
452 domains.
454 @note: Will cause a refresh before lookup up domains, for
455 a version that does not need to re-read xenstore
456 use L{domain_lookup_nr}.
458 @param domid: Domain ID or Domain Name.
459 @type domid: int or string
460 @return: Found domain.
461 @rtype: XendDomainInfo
462 @raise XendError: If domain is not found.
463 """
464 self.domains_lock.acquire()
465 try:
466 self._refresh()
467 dom = self.domain_lookup_nr(domid)
468 if not dom:
469 raise XendError("No domain named '%s'." % str(domid))
470 return dom
471 finally:
472 self.domains_lock.release()
475 def domain_lookup_nr(self, domid):
476 """Look up given I{domid} in the list of managed and running
477 domains.
479 @param domid: Domain ID or Domain Name.
480 @type domid: int or string
481 @return: Found domain.
482 @rtype: XendDomainInfo or None
483 """
484 self.domains_lock.acquire()
485 try:
486 # lookup by name
487 match = [dom for dom in self.domains.values() \
488 if dom.getName() == domid]
489 if match:
490 return match[0]
492 # lookup by id
493 try:
494 match = [d for d in self.domains.values() \
495 if d.getDomid() == int(domid)]
496 if match:
497 return match[0]
498 except (ValueError, TypeError):
499 pass
501 return None
502 finally:
503 self.domains_lock.release()
505 def privilegedDomain(self):
506 """ Get the XendDomainInfo of a dom0
508 @rtype: XendDomainInfo
509 """
510 self.domains_lock.acquire()
511 try:
512 return self.domains[DOM0_UUID]
513 finally:
514 self.domains_lock.release()
516 def cleanup_domains(self):
517 """Clean up domains that are marked as autostop.
518 Should be called when Xend goes down. This is currently
519 called from L{xen.xend.servers.XMLRPCServer}.
521 """
522 log.debug('cleanup_domains')
523 self.domains_lock.acquire()
524 try:
525 for dom in self.domains.values():
526 if dom.getName() == DOM0_NAME:
527 continue
529 if dom.state == XendDomainInfo.DOM_STATE_RUNNING:
530 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
531 if shutdownAction == 'shutdown':
532 log.debug('Shutting down domain: %s' % dom.getName())
533 dom.shutdown("poweroff")
534 elif shutdownAction == 'suspend':
535 chkfile = self._managed_check_point_path(dom.getName())
536 self.domain_save(dom.domid, chkfile)
537 finally:
538 self.domains_lock.release()
542 # ----------------------------------------------------------------
543 # Xen API
546 def set_allow_new_domains(self, allow_new_domains):
547 self._allow_new_domains = allow_new_domains
549 def allow_new_domains(self):
550 return self._allow_new_domains
552 def get_domain_refs(self):
553 result = []
554 try:
555 self.domains_lock.acquire()
556 result = [d.get_uuid() for d in self.domains.values()]
557 finally:
558 self.domains_lock.release()
559 return result
561 def get_vm_by_uuid(self, vm_uuid):
562 self.domains_lock.acquire()
563 try:
564 if vm_uuid in self.domains:
565 return self.domains[vm_uuid]
566 return None
567 finally:
568 self.domains_lock.release()
570 def get_vm_with_dev_uuid(self, klass, dev_uuid):
571 self.domains_lock.acquire()
572 try:
573 for dom in self.domains.values():
574 if dom.has_device(klass, dev_uuid):
575 return dom
576 return None
577 finally:
578 self.domains_lock.release()
580 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
581 self.domains_lock.acquire()
582 try:
583 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
584 if not dom:
585 return None
587 value = dom.get_device_property(klass, dev_uuid, field)
588 return value
589 except ValueError, e:
590 pass
592 return None
594 def is_valid_vm(self, vm_ref):
595 return (self.get_vm_by_uuid(vm_ref) != None)
597 def is_valid_dev(self, klass, dev_uuid):
598 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
600 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args):
601 self.domains_lock.acquire()
602 try:
603 if vm_uuid in self.domains:
604 # problem is domid does not exist for unstarted
605 # domains, so in that case, we use the name.
606 # TODO: probably want to modify domain_lookup_nr
607 # to lookup uuids, or just ignore
608 # the legacy api and reimplement all these
609 # calls.
610 domid = self.domains[vm_uuid].getDomid()
611 if domid == None:
612 domid = self.domains[vm_uuid].getName()
613 return fn(domid, *args)
614 raise XendInvalidDomain("Domain does not exist")
615 finally:
616 self.domains_lock.release()
619 def create_domain(self, xenapi_vm):
620 self.domains_lock.acquire()
621 try:
622 try:
623 xeninfo = XendConfig(xenapi_vm = xenapi_vm)
624 dominfo = XendDomainInfo.createDormant(xeninfo)
625 log.debug("Creating new managed domain: %s: %s" %
626 (dominfo.getName(), dominfo.get_uuid()))
627 self._add_domain(dominfo, managed = True)
628 self.managed_config_save(dominfo)
629 return dominfo.get_uuid()
630 except XendError, e:
631 raise
632 except Exception, e:
633 raise XendError(str(e))
634 finally:
635 self.domains_lock.release()
637 def rename_domain(self, dom, new_name):
638 self.domains_lock.acquire()
639 try:
640 old_name = dom.getName()
641 dom.setName(new_name)
643 if self.is_domain_managed(dom):
644 self._managed_domain_rename(dom, new_name)
646 finally:
647 self.domains_lock.release()
650 #
651 # End of Xen API
652 # ----------------------------------------------------------------
654 # ------------------------------------------------------------
655 # Xen Legacy API
657 def list(self):
658 """Get list of domain objects.
660 @return: domains
661 @rtype: list of XendDomainInfo
662 """
663 self.domains_lock.acquire()
664 try:
665 self._refresh()
666 return self.domains.values()
667 finally:
668 self.domains_lock.release()
671 def list_sorted(self):
672 """Get list of domain objects, sorted by name.
674 @return: domain objects
675 @rtype: list of XendDomainInfo
676 """
677 doms = self.list()
678 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
679 return doms
681 def list_names(self):
682 """Get list of domain names.
684 @return: domain names
685 @rtype: list of strings.
686 """
687 return [d.getName() for d in self.list_sorted()]
689 def domain_suspend(self, domname):
690 """Suspends a domain that is persistently managed by Xend
692 @param domname: Domain Name
693 @type domname: string
694 @rtype: None
695 @raise XendError: Failure during checkpointing.
696 """
698 try:
699 dominfo = self.domain_lookup_nr(domname)
700 if not dominfo:
701 raise XendInvalidDomain(domname)
703 if dominfo.getDomid() == DOM0_ID:
704 raise XendError("Cannot save privileged domain %s" % domname)
706 if dominfo.state != XendDomainInfo.DOM_STATE_RUNNING:
707 raise XendError("Cannot suspend domain that is not running.")
709 if not os.path.exists(self._managed_config_path(domname)):
710 raise XendError("Domain is not managed by Xend lifecycle " +
711 "support.")
713 path = self._managed_check_point_path(domname)
714 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
715 try:
716 # For now we don't support 'live checkpoint'
717 XendCheckpoint.save(fd, dominfo, False, False, path)
718 finally:
719 os.close(fd)
720 except OSError, ex:
721 raise XendError("can't write guest state file %s: %s" %
722 (path, ex[1]))
724 def domain_resume(self, domname):
725 """Resumes a domain that is persistently managed by Xend.
727 @param domname: Domain Name
728 @type domname: string
729 @rtype: None
730 @raise XendError: If failed to restore.
731 """
732 try:
733 dominfo = self.domain_lookup_nr(domname)
735 if not dominfo:
736 raise XendInvalidDomain(domname)
738 if dominfo.getDomid() == DOM0_ID:
739 raise XendError("Cannot save privileged domain %s" % domname)
741 if dominfo.state != XendDomainInfo.DOM_STATE_HALTED:
742 raise XendError("Cannot suspend domain that is not running.")
744 chkpath = self._managed_check_point_path(domname)
745 if not os.path.exists(chkpath):
746 raise XendError("Domain was not suspended by Xend")
748 # Restore that replaces the existing XendDomainInfo
749 try:
750 log.debug('Current DomainInfo state: %d' % dominfo.state)
751 XendCheckpoint.restore(self,
752 os.open(chkpath, os.O_RDONLY),
753 dominfo)
754 os.unlink(chkpath)
755 except OSError, ex:
756 raise XendError("Failed to read stored checkpoint file")
757 except IOError, ex:
758 raise XendError("Failed to delete checkpoint file")
759 except Exception, ex:
760 log.exception("Exception occurred when resuming")
761 raise XendError("Error occurred when resuming: %s" % str(ex))
764 def domain_create(self, config):
765 """Create a domain from a configuration.
767 @param config: configuration
768 @type config: SXP Object (list of lists)
769 @rtype: XendDomainInfo
770 """
771 self.domains_lock.acquire()
772 try:
773 dominfo = XendDomainInfo.create(config)
774 self._add_domain(dominfo)
775 return dominfo
776 finally:
777 self.domains_lock.release()
780 def domain_new(self, config):
781 """Create a domain from a configuration but do not start it.
783 @param config: configuration
784 @type config: SXP Object (list of lists)
785 @rtype: XendDomainInfo
786 """
787 self.domains_lock.acquire()
788 try:
789 try:
790 xeninfo = XendConfig(sxp = config)
791 dominfo = XendDomainInfo.createDormant(xeninfo)
792 log.debug("Creating new managed domain: %s" %
793 dominfo.getName())
794 self._add_domain(dominfo, managed = True)
795 self.managed_config_save(dominfo)
796 # no return value because it isn't meaningful for client
797 except XendError, e:
798 raise
799 except Exception, e:
800 raise XendError(str(e))
801 finally:
802 self.domains_lock.release()
804 def domain_start(self, domid):
805 """Start a managed domain
807 @require: Domain must not be running.
808 @param domid: Domain name or domain ID.
809 @type domid: string or int
810 @rtype: None
811 @raise XendError: If domain is still running
812 @rtype: None
813 """
814 self.domains_lock.acquire()
815 try:
816 dominfo = self.domain_lookup_nr(domid)
817 if not dominfo:
818 raise XendInvalidDomain(str(domid))
820 if dominfo.state != XendDomainInfo.DOM_STATE_HALTED:
821 raise XendError("Domain is already running")
823 dominfo.start(is_managed = True)
826 finally:
827 self.domains_lock.release()
830 def domain_delete(self, domid):
831 """Remove a domain from database
833 @require: Domain must not be running.
834 @param domid: Domain name or domain ID.
835 @type domid: string or int
836 @rtype: None
837 @raise XendError: If domain is still running
838 """
839 self.domains_lock.acquire()
840 try:
841 try:
842 dominfo = self.domain_lookup_nr(domid)
843 if not dominfo:
844 raise XendInvalidDomain(str(domid))
846 if dominfo.state != XendDomainInfo.DOM_STATE_HALTED:
847 raise XendError("Domain is still running")
849 self._remove_domain(dominfo)
851 except Exception, ex:
852 raise XendError(str(ex))
853 finally:
854 self.domains_lock.release()
857 def domain_configure(self, config):
858 """Configure an existing domain.
860 @param vmconfig: vm configuration
861 @type vmconfig: SXP Object (list of lists)
862 @todo: Not implemented
863 """
864 # !!!
865 raise XendError("Unsupported")
867 def domain_restore(self, src):
868 """Restore a domain from file.
870 @param src: filename of checkpoint file to restore from
871 @type src: string
872 @return: Restored domain
873 @rtype: XendDomainInfo
874 @raise XendError: Failure to restore domain
875 """
876 try:
877 fd = os.open(src, os.O_RDONLY)
878 try:
879 return self.domain_restore_fd(fd)
880 finally:
881 os.close(fd)
882 except OSError, ex:
883 raise XendError("can't read guest state file %s: %s" %
884 (src, ex[1]))
886 def domain_restore_fd(self, fd):
887 """Restore a domain from the given file descriptor.
889 @param fd: file descriptor of the checkpoint file
890 @type fd: File object
891 @rtype: XendDomainInfo
892 @raise XendError: if failed to restore
893 """
895 try:
896 return XendCheckpoint.restore(self, fd)
897 except:
898 # I don't really want to log this exception here, but the error
899 # handling in the relocation-socket handling code (relocate.py) is
900 # poor, so we need to log this for debugging.
901 log.exception("Restore failed")
902 raise XendError("Restore failed")
904 def domain_unpause(self, domid):
905 """Unpause domain execution.
907 @param domid: Domain ID or Name
908 @type domid: int or string.
909 @rtype: None
910 @raise XendError: Failed to unpause
911 @raise XendInvalidDomain: Domain is not valid
912 """
913 try:
914 dominfo = self.domain_lookup_nr(domid)
915 if not dominfo:
916 raise XendInvalidDomain(str(domid))
918 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
919 int(dominfo.getDomid()))
921 dominfo.unpause()
922 except XendInvalidDomain:
923 log.exception("domain_unpause")
924 raise
925 except Exception, ex:
926 log.exception("domain_unpause")
927 raise XendError(str(ex))
929 def domain_pause(self, domid):
930 """Pause domain execution.
932 @param domid: Domain ID or Name
933 @type domid: int or string.
934 @rtype: None
935 @raise XendError: Failed to pause
936 @raise XendInvalidDomain: Domain is not valid
937 """
938 try:
939 dominfo = self.domain_lookup_nr(domid)
940 if not dominfo:
941 raise XendInvalidDomain(str(domid))
942 log.info("Domain %s (%d) paused.", dominfo.getName(),
943 int(dominfo.getDomid()))
944 dominfo.pause()
945 except XendInvalidDomain:
946 log.exception("domain_pause")
947 raise
948 except Exception, ex:
949 log.exception("domain_pause")
950 raise XendError(str(ex))
952 def domain_dump(self, domid, filename, live, crash):
953 """Dump domain core."""
955 dominfo = self.domain_lookup_nr(domid)
956 if not dominfo:
957 raise XendInvalidDomain(str(domid))
959 if dominfo.getDomid() == DOM0_ID:
960 raise XendError("Cannot dump core for privileged domain %s" % domid)
962 try:
963 log.info("Domain core dump requested for domain %s (%d) "
964 "live=%d crash=%d.",
965 dominfo.getName(), dominfo.getDomid(), live, crash)
966 return dominfo.dumpCore(filename)
967 except Exception, ex:
968 raise XendError(str(ex))
970 def domain_destroy(self, domid):
971 """Terminate domain immediately.
973 @param domid: Domain ID or Name
974 @type domid: int or string.
975 @rtype: None
976 @raise XendError: Failed to destroy
977 @raise XendInvalidDomain: Domain is not valid
978 """
980 dominfo = self.domain_lookup_nr(domid)
981 if dominfo and dominfo.getDomid() == DOM0_ID:
982 raise XendError("Cannot destroy privileged domain %s" % domid)
984 if dominfo:
985 val = dominfo.destroy()
986 else:
987 try:
988 val = xc.domain_destroy(int(domid))
989 except ValueError:
990 raise XendInvalidDomain(domid)
991 except Exception, e:
992 raise XendError(str(e))
994 return val
996 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
997 """Start domain migration.
999 @param domid: Domain ID or Name
1000 @type domid: int or string.
1001 @param dst: Destination IP address
1002 @type dst: string
1003 @keyword port: relocation port on destination
1004 @type port: int
1005 @keyword live: Live migration
1006 @type live: bool
1007 @keyword resource: not used??
1008 @rtype: None
1009 @raise XendError: Failed to migrate
1010 @raise XendInvalidDomain: Domain is not valid
1011 """
1013 dominfo = self.domain_lookup_nr(domid)
1014 if not dominfo:
1015 raise XendInvalidDomain(str(domid))
1017 if dominfo.getDomid() == DOM0_ID:
1018 raise XendError("Cannot migrate privileged domain %i" % domid)
1020 """ The following call may raise a XendError exception """
1021 dominfo.testMigrateDevices(True, dst)
1023 if live:
1024 """ Make sure there's memory free for enabling shadow mode """
1025 dominfo.checkLiveMigrateMemory()
1027 if port == 0:
1028 port = xroot.get_xend_relocation_port()
1029 try:
1030 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1031 sock.connect((dst, port))
1032 except socket.error, err:
1033 raise XendError("can't connect: %s" % err[1])
1035 sock.send("receive\n")
1036 sock.recv(80)
1037 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1038 dominfo.testDeviceComplete()
1039 sock.close()
1041 def domain_save(self, domid, dst):
1042 """Start saving a domain to file.
1044 @param domid: Domain ID or Name
1045 @type domid: int or string.
1046 @param dst: Destination filename
1047 @type dst: string
1048 @rtype: None
1049 @raise XendError: Failed to save domain
1050 @raise XendInvalidDomain: Domain is not valid
1051 """
1052 try:
1053 dominfo = self.domain_lookup_nr(domid)
1054 if not dominfo:
1055 raise XendInvalidDomain(str(domid))
1057 if dominfo.getDomid() == DOM0_ID:
1058 raise XendError("Cannot save privileged domain %i" % domid)
1060 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1061 try:
1062 # For now we don't support 'live checkpoint'
1063 XendCheckpoint.save(fd, dominfo, False, False, dst)
1064 finally:
1065 os.close(fd)
1066 except OSError, ex:
1067 raise XendError("can't write guest state file %s: %s" %
1068 (dst, ex[1]))
1070 def domain_pincpu(self, domid, vcpu, cpumap):
1071 """Set which cpus vcpu can use
1073 @param domid: Domain ID or Name
1074 @type domid: int or string.
1075 @param vcpu: vcpu to pin to
1076 @type vcpu: int
1077 @param cpumap: string repr of usable cpus
1078 @type cpumap: string
1079 @rtype: 0
1080 """
1081 dominfo = self.domain_lookup_nr(domid)
1082 if not dominfo:
1083 raise XendInvalidDomain(str(domid))
1085 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1086 vcpus = [ vcpu ]
1087 if str(vcpu).lower() == "all":
1088 vcpus = range(0, int(dominfo.getVCpuCount()))
1090 # set the same cpumask for all vcpus
1091 rc = 0
1092 for v in vcpus:
1093 try:
1094 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1095 except Exception, ex:
1096 raise XendError(str(ex))
1097 return rc
1099 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1100 weight):
1101 """Set Simple EDF scheduler parameters for a domain.
1103 @param domid: Domain ID or Name
1104 @type domid: int or string.
1105 @rtype: 0
1106 """
1107 dominfo = self.domain_lookup_nr(domid)
1108 if not dominfo:
1109 raise XendInvalidDomain(str(domid))
1110 try:
1111 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1112 latency, extratime, weight)
1113 except Exception, ex:
1114 raise XendError(str(ex))
1116 def domain_cpu_sedf_get(self, domid):
1117 """Get Simple EDF scheduler parameters for a domain.
1119 @param domid: Domain ID or Name
1120 @type domid: int or string.
1121 @rtype: SXP object
1122 @return: The parameters for Simple EDF schedule for a domain.
1123 """
1124 dominfo = self.domain_lookup_nr(domid)
1125 if not dominfo:
1126 raise XendInvalidDomain(str(domid))
1127 try:
1128 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1129 # return sxpr
1130 return ['sedf',
1131 ['domain', sedf_info['domain']],
1132 ['period', sedf_info['period']],
1133 ['slice', sedf_info['slice']],
1134 ['latency', sedf_info['latency']],
1135 ['extratime', sedf_info['extratime']],
1136 ['weight', sedf_info['weight']]]
1138 except Exception, ex:
1139 raise XendError(str(ex))
1141 def domain_shadow_control(self, domid, op):
1142 """Shadow page control.
1144 @param domid: Domain ID or Name
1145 @type domid: int or string.
1146 @param op: operation
1147 @type op: int
1148 @rtype: 0
1149 """
1150 dominfo = self.domain_lookup(domid)
1151 try:
1152 return xc.shadow_control(dominfo.getDomid(), op)
1153 except Exception, ex:
1154 raise XendError(str(ex))
1156 def domain_shadow_mem_get(self, domid):
1157 """Get shadow pagetable memory allocation.
1159 @param domid: Domain ID or Name
1160 @type domid: int or string.
1161 @rtype: int
1162 @return: shadow memory in MB
1163 """
1164 dominfo = self.domain_lookup(domid)
1165 try:
1166 return xc.shadow_mem_control(dominfo.getDomid())
1167 except Exception, ex:
1168 raise XendError(str(ex))
1170 def domain_shadow_mem_set(self, domid, mb):
1171 """Set shadow pagetable memory allocation.
1173 @param domid: Domain ID or Name
1174 @type domid: int or string.
1175 @param mb: shadow memory to set in MB
1176 @type: mb: int
1177 @rtype: int
1178 @return: shadow memory in MB
1179 """
1180 dominfo = self.domain_lookup(domid)
1181 try:
1182 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1183 except Exception, ex:
1184 raise XendError(str(ex))
1186 def domain_sched_credit_get(self, domid):
1187 """Get credit scheduler parameters for a domain.
1189 @param domid: Domain ID or Name
1190 @type domid: int or string.
1191 @rtype: dict with keys 'weight' and 'cap'
1192 @return: credit scheduler parameters
1193 """
1194 dominfo = self.domain_lookup_nr(domid)
1195 if not dominfo:
1196 raise XendInvalidDomain(str(domid))
1197 try:
1198 return xc.sched_credit_domain_get(dominfo.getDomid())
1199 except Exception, ex:
1200 raise XendError(str(ex))
1202 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1203 """Set credit scheduler parameters for a domain.
1205 @param domid: Domain ID or Name
1206 @type domid: int or string.
1207 @type weight: int
1208 @type cap: int
1209 @rtype: 0
1210 """
1211 dominfo = self.domain_lookup_nr(domid)
1212 if not dominfo:
1213 raise XendInvalidDomain(str(domid))
1214 try:
1215 if weight is None:
1216 weight = int(0)
1217 elif weight < 1 or weight > 65535:
1218 raise XendError("weight is out of range")
1220 if cap is None:
1221 cap = int(~0)
1222 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1223 raise XendError("cap is out of range")
1225 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1226 except Exception, ex:
1227 raise XendError(str(ex))
1229 def domain_maxmem_set(self, domid, mem):
1230 """Set the memory limit for a domain.
1232 @param domid: Domain ID or Name
1233 @type domid: int or string.
1234 @param mem: memory limit (in MiB)
1235 @type mem: int
1236 @raise XendError: fail to set memory
1237 @rtype: 0
1238 """
1239 dominfo = self.domain_lookup_nr(domid)
1240 if not dominfo:
1241 raise XendInvalidDomain(str(domid))
1242 maxmem = int(mem) * 1024
1243 try:
1244 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1245 except Exception, ex:
1246 raise XendError(str(ex))
1248 def domain_ioport_range_enable(self, domid, first, last):
1249 """Enable access to a range of IO ports for a domain
1251 @param first: first IO port
1252 @param last: last IO port
1253 @raise XendError: failed to set range
1254 @rtype: 0
1255 """
1256 dominfo = self.domain_lookup_nr(domid)
1257 if not dominfo:
1258 raise XendInvalidDomain(str(domid))
1259 nr_ports = last - first + 1
1260 try:
1261 return xc.domain_ioport_permission(dominfo.getDomid(),
1262 first_port = first,
1263 nr_ports = nr_ports,
1264 allow_access = 1)
1265 except Exception, ex:
1266 raise XendError(str(ex))
1268 def domain_ioport_range_disable(self, domid, first, last):
1269 """Disable access to a range of IO ports for a domain
1271 @param first: first IO port
1272 @param last: last IO port
1273 @raise XendError: failed to set range
1274 @rtype: 0
1275 """
1276 dominfo = self.domain_lookup_nr(domid)
1277 if not dominfo:
1278 raise XendInvalidDomain(str(domid))
1279 nr_ports = last - first + 1
1280 try:
1281 return xc.domain_ioport_permission(dominfo.getDomid(),
1282 first_port = first,
1283 nr_ports = nr_ports,
1284 allow_access = 0)
1285 except Exception, ex:
1286 raise XendError(str(ex))
1289 def instance():
1290 """Singleton constructor. Use this instead of the class constructor.
1291 """
1292 global inst
1293 try:
1294 inst
1295 except:
1296 inst = XendDomain()
1297 inst.init()
1298 return inst