direct-io.hg

view tools/python/xen/xend/XendDomain.py @ 12192:201a824f8240

Create /var/lib/xend/domains on startup if it doesn't exist.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Wed Nov 01 13:50:02 2006 +0000 (2006-11-01)
parents 33e9c88aab02
children ec7e7e946496
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 dom_path = self._managed_path()
88 try:
89 os.stat(dom_path)
90 except OSError:
91 log.info("Making %s", dom_path)
92 os.makedirs(dom_path, 0755)
94 xstransact.Mkdir(XS_VMROOT)
95 xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID})
97 self.domains_lock.acquire()
98 try:
99 try:
100 dom0info = [d for d in self._running_domains() \
101 if d.get('domid') == DOM0_ID][0]
103 dom0info['name'] = DOM0_NAME
104 dom0 = XendDomainInfo.recreate(dom0info, True)
105 self._add_domain(dom0)
106 except IndexError:
107 raise XendError('Unable to find Domain 0')
109 self._setDom0CPUCount()
111 # This watch registration needs to be before the refresh call, so
112 # that we're sure that we haven't missed any releases, but inside
113 # the domains_lock, as we don't want the watch to fire until after
114 # the refresh call has completed.
115 xswatch("@introduceDomain", self._on_domains_changed)
116 xswatch("@releaseDomain", self._on_domains_changed)
118 self._init_domains()
119 finally:
120 self.domains_lock.release()
123 def _on_domains_changed(self, _):
124 """ Callback method when xenstore changes.
126 Calls refresh which will keep the local cache of domains
127 in sync.
129 @rtype: int
130 @return: 1
131 """
132 self.domains_lock.acquire()
133 try:
134 self._refresh()
135 finally:
136 self.domains_lock.release()
137 return 1
139 def _init_domains(self):
140 """Does the initial scan of managed and active domains to
141 populate self.domains.
143 Note: L{XendDomainInfo._checkName} will call back into XendDomain
144 to make sure domain name is not a duplicate.
146 """
147 self.domains_lock.acquire()
148 try:
149 running = self._running_domains()
150 managed = self._managed_domains()
152 # add all active domains
153 for dom in running:
154 if dom['domid'] != DOM0_ID:
155 try:
156 new_dom = XendDomainInfo.recreate(dom, False)
157 self._add_domain(new_dom)
158 except Exception:
159 log.exception("Failed to create reference to running "
160 "domain id: %d" % dom['domid'])
162 # add all managed domains as dormant domains.
163 for dom in managed:
164 dom_uuid = dom.get('uuid', uuid.createString())
165 dom['uuid'] = dom_uuid
166 dom_name = dom.get('name', 'Domain-%s' % dom_uuid)
168 try:
169 running_dom = self.domain_lookup_nr(dom_name)
170 if not running_dom:
171 # instantiate domain if not started.
172 new_dom = XendDomainInfo.createDormant(dom)
173 self._add_domain(new_dom)
174 self._managed_domain_register(new_dom)
175 else:
176 self._managed_domain_register(running_dom)
177 except Exception:
178 log.exception("Failed to create reference to managed "
179 "domain: %s" % dom_name)
181 finally:
182 self.domains_lock.release()
185 # -----------------------------------------------------------------
186 # Getting managed domains storage path names
188 def _managed_path(self, domuuid = None):
189 """Returns the path of the directory where managed domain
190 information is stored.
192 @keyword domuuid: If not None, will return the path to the domain
193 otherwise, will return the path containing
194 the directories which represent each domain.
195 @type: None or String.
196 @rtype: String
197 @return: Path.
198 """
199 dom_path = xroot.get_xend_domains_path()
200 if domuuid:
201 dom_path = os.path.join(dom_path, domuuid)
202 return dom_path
204 def _managed_config_path(self, domuuid):
205 """Returns the path to the configuration file of a managed domain.
207 @param domname: Domain uuid
208 @type domname: String
209 @rtype: String
210 @return: path to config file.
211 """
212 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
214 def _managed_check_point_path(self, domuuid):
215 """Returns absolute path to check point file for managed domain.
217 @param domuuid: Name of managed domain
218 @type domname: String
219 @rtype: String
220 @return: Path
221 """
222 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
224 def _managed_config_remove(self, domuuid):
225 """Removes a domain configuration from managed list
227 @param domuuid: Name of managed domain
228 @type domname: String
229 @raise XendError: fails to remove the domain.
230 """
231 config_path = self._managed_path(domuuid)
232 try:
233 if os.path.exists(config_path) and os.path.isdir(config_path):
234 shutil.rmtree(config_path)
235 except IOError:
236 log.exception('managed_config_remove failed removing conf')
237 raise XendError("Unable to remove managed configuration"
238 " for domain: %s" % domuuid)
240 def managed_config_save(self, dominfo):
241 """Save a domain's configuration to disk
243 @param domninfo: Managed domain to save.
244 @type dominfo: XendDomainInfo
245 @raise XendError: fails to save configuration.
246 @rtype: None
247 """
248 if not self.is_domain_managed(dominfo):
249 return # refuse to save configuration this domain isn't managed
251 if dominfo:
252 domains_dir = self._managed_path()
253 dom_uuid = dominfo.get_uuid()
254 domain_config_dir = self._managed_path(dom_uuid)
256 # make sure the domain dir exists
257 if not os.path.exists(domains_dir):
258 os.makedirs(domains_dir, 0755)
259 elif not os.path.isdir(domains_dir):
260 log.error("xend_domain_dir is not a directory.")
261 raise XendError("Unable to save managed configuration "
262 "because %s is not a directory." %
263 domains_dir)
265 if not os.path.exists(domain_config_dir):
266 try:
267 os.makedirs(domain_config_dir, 0755)
268 except IOError:
269 log.exception("Failed to create directory: %s" %
270 domain_config_dir)
271 raise XendError("Failed to create directory: %s" %
272 domain_config_dir)
274 try:
275 sxp_cache_file = open(self._managed_config_path(dom_uuid),'w')
276 prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78)
277 sxp_cache_file.close()
278 except IOError:
279 log.error("Error occurred saving configuration file to %s" %
280 domain_config_dir)
281 raise XendError("Failed to save configuration file to: %s" %
282 domain_config_dir)
283 else:
284 log.warn("Trying to save configuration for invalid domain")
287 def _managed_domains(self):
288 """ Returns list of domains that are managed.
290 Expects to be protected by domains_lock.
292 @rtype: list of XendConfig
293 @return: List of domain configurations that are managed.
294 """
295 dom_path = self._managed_path()
296 dom_uuids = os.listdir(dom_path)
297 doms = []
298 for dom_uuid in dom_uuids:
299 try:
300 cfg_file = self._managed_config_path(dom_uuid)
301 cfg = XendConfig(filename = cfg_file)
302 doms.append(cfg)
303 except Exception:
304 log.exception('Unable to open or parse config.sxp: %s' % \
305 cfg_file)
306 return doms
308 def _managed_domain_unregister(self, dom):
309 try:
310 self.managed_domains.remove((dom.get_uuid(), dom.getName()))
311 except ValueError:
312 log.warn("Domain is not registered: %s" % dom.get_uuid())
314 def _managed_domain_register(self, dom):
315 self.managed_domains.append((dom.get_uuid(), dom.getName()))
317 def _managed_domain_rename(self, dom, new_name):
318 for i in range(len(self.managed_domains)):
319 if self.managed_domains[i][0] == dom.get_uuid():
320 self.managed_domains[i][1] = new_name
321 return True
322 return False
324 def is_domain_managed(self, dom = None, dom_name = None):
325 dom_uuid = dom.get_uuid()
326 dom_name = dom.getName()
327 if dom:
328 return ((dom_uuid, dom_name) in self.managed_domains)
329 if dom_name:
330 results = [d for d in self.managed_domains if d[1] == dom_name]
331 return (len(results) > 0)
332 return False
336 # End of Managed Domain Access
337 # --------------------------------------------------------------------
339 def _running_domains(self):
340 """Get table of domains indexed by id from xc.
342 @requires: Expects to be protected by domains_lock.
343 @rtype: list of dicts
344 @return: A list of dicts representing the running domains.
345 """
346 return xc.domain_getinfo()
348 def _setDom0CPUCount(self):
349 """Sets the number of VCPUs dom0 has. Retreived from the
350 Xend configuration, L{XendRoot}.
352 @requires: Expects to be protected by domains_lock.
353 @rtype: None
354 """
355 dom0 = self.privilegedDomain()
357 # get max number of vcpus to use for dom0 from config
358 target = int(xroot.get_dom0_vcpus())
359 log.debug("number of vcpus to use is %d", target)
361 # target == 0 means use all processors
362 if target > 0:
363 dom0.setVCpuCount(target)
366 def _refresh(self):
367 """Refresh the domain list. Needs to be called when
368 either xenstore has changed or when a method requires
369 up to date information (like uptime, cputime stats).
371 @rtype: None
372 """
373 self.domains_lock.acquire()
374 try:
375 # update information for all running domains
376 # - like cpu_time, status, dying, etc.
377 running = self._running_domains()
378 for dom in running:
379 dom_info = self.domain_lookup_nr(dom['domid'])
380 if dom_info:
381 dom_info.update(dom)
383 # clean up unmanaged domains
384 for dom in self.domains.values():
385 if (dom.getDomid() == None) and \
386 not self.is_domain_managed(dom):
387 self._remove_domain(dom)
389 finally:
390 self.domains_lock.release()
392 def _add_domain(self, info, managed = False):
393 """Add the given domain entry to this instance's internal cache.
395 @requires: Expects to be protected by the domains_lock.
396 @param info: XendDomainInfo of a domain to be added.
397 @type info: XendDomainInfo
398 @keyword managed: Whether this domain is maanged by Xend
399 @type managed: boolean
400 """
401 log.debug("Adding Domain: %s" % info.get_uuid())
402 self.domains[info.get_uuid()] = info
403 if managed and not self.is_domain_managed(info):
404 self._managed_domain_register(info)
406 def _remove_domain(self, info):
407 """Remove the given domain from this instance's internal cache.
409 @requires: Expects to be protected by the domains_lock.
410 @param info: XendDomainInfo of a domain to be removed.
411 @type info: XendDomainInfo
412 """
413 if info:
414 dom_name = info.getName()
415 dom_uuid = info.get_uuid()
417 if info.state != XendDomainInfo.DOM_STATE_HALTED:
418 info.cleanupDomain()
420 if self.is_domain_managed(info):
421 self._managed_config_remove(dom_uuid)
422 self._managed_domain_unregister(info)
424 try:
425 del self.domains[dom_uuid]
426 except KeyError:
427 pass
428 else:
429 log.warning("Attempted to remove non-existent domain.")
431 def restore_(self, config):
432 """Create a domain as part of the restore process. This is called
433 only from L{XendCheckpoint}.
435 A restore request comes into XendDomain through L{domain_restore}
436 or L{domain_restore_fd}. That request is
437 forwarded immediately to XendCheckpoint which, when it is ready, will
438 call this method. It is necessary to come through here rather than go
439 directly to L{XendDomainInfo.restore} because we need to
440 serialise the domain creation process, but cannot lock
441 domain_restore_fd as a whole, otherwise we will deadlock waiting for
442 the old domain to die.
444 @param config: Configuration of domain to restore
445 @type config: SXP Object (eg. list of lists)
446 """
447 self.domains_lock.acquire()
448 try:
449 security.refresh_ssidref(config)
450 dominfo = XendDomainInfo.restore(config)
451 self._add_domain(dominfo)
452 return dominfo
453 finally:
454 self.domains_lock.release()
457 def domain_lookup(self, domid):
458 """Look up given I{domid} in the list of managed and running
459 domains.
461 @note: Will cause a refresh before lookup up domains, for
462 a version that does not need to re-read xenstore
463 use L{domain_lookup_nr}.
465 @param domid: Domain ID or Domain Name.
466 @type domid: int or string
467 @return: Found domain.
468 @rtype: XendDomainInfo
469 @raise XendError: If domain is not found.
470 """
471 self.domains_lock.acquire()
472 try:
473 self._refresh()
474 dom = self.domain_lookup_nr(domid)
475 if not dom:
476 raise XendError("No domain named '%s'." % str(domid))
477 return dom
478 finally:
479 self.domains_lock.release()
482 def domain_lookup_nr(self, domid):
483 """Look up given I{domid} in the list of managed and running
484 domains.
486 @param domid: Domain ID or Domain Name.
487 @type domid: int or string
488 @return: Found domain.
489 @rtype: XendDomainInfo or None
490 """
491 self.domains_lock.acquire()
492 try:
493 # lookup by name
494 match = [dom for dom in self.domains.values() \
495 if dom.getName() == domid]
496 if match:
497 return match[0]
499 # lookup by id
500 try:
501 match = [d for d in self.domains.values() \
502 if d.getDomid() == int(domid)]
503 if match:
504 return match[0]
505 except (ValueError, TypeError):
506 pass
508 return None
509 finally:
510 self.domains_lock.release()
512 def privilegedDomain(self):
513 """ Get the XendDomainInfo of a dom0
515 @rtype: XendDomainInfo
516 """
517 self.domains_lock.acquire()
518 try:
519 return self.domains[DOM0_UUID]
520 finally:
521 self.domains_lock.release()
523 def cleanup_domains(self):
524 """Clean up domains that are marked as autostop.
525 Should be called when Xend goes down. This is currently
526 called from L{xen.xend.servers.XMLRPCServer}.
528 """
529 log.debug('cleanup_domains')
530 self.domains_lock.acquire()
531 try:
532 for dom in self.domains.values():
533 if dom.getName() == DOM0_NAME:
534 continue
536 if dom.state == XendDomainInfo.DOM_STATE_RUNNING:
537 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
538 if shutdownAction == 'shutdown':
539 log.debug('Shutting down domain: %s' % dom.getName())
540 dom.shutdown("poweroff")
541 elif shutdownAction == 'suspend':
542 chkfile = self._managed_check_point_path(dom.getName())
543 self.domain_save(dom.domid, chkfile)
544 finally:
545 self.domains_lock.release()
549 # ----------------------------------------------------------------
550 # Xen API
553 def set_allow_new_domains(self, allow_new_domains):
554 self._allow_new_domains = allow_new_domains
556 def allow_new_domains(self):
557 return self._allow_new_domains
559 def get_domain_refs(self):
560 result = []
561 try:
562 self.domains_lock.acquire()
563 result = [d.get_uuid() for d in self.domains.values()]
564 finally:
565 self.domains_lock.release()
566 return result
568 def get_vm_by_uuid(self, vm_uuid):
569 self.domains_lock.acquire()
570 try:
571 if vm_uuid in self.domains:
572 return self.domains[vm_uuid]
573 return None
574 finally:
575 self.domains_lock.release()
577 def get_vm_with_dev_uuid(self, klass, dev_uuid):
578 self.domains_lock.acquire()
579 try:
580 for dom in self.domains.values():
581 if dom.has_device(klass, dev_uuid):
582 return dom
583 return None
584 finally:
585 self.domains_lock.release()
587 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
588 self.domains_lock.acquire()
589 try:
590 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
591 if not dom:
592 return None
594 value = dom.get_device_property(klass, dev_uuid, field)
595 return value
596 except ValueError, e:
597 pass
599 return None
601 def is_valid_vm(self, vm_ref):
602 return (self.get_vm_by_uuid(vm_ref) != None)
604 def is_valid_dev(self, klass, dev_uuid):
605 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
607 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args):
608 self.domains_lock.acquire()
609 try:
610 if vm_uuid in self.domains:
611 # problem is domid does not exist for unstarted
612 # domains, so in that case, we use the name.
613 # TODO: probably want to modify domain_lookup_nr
614 # to lookup uuids, or just ignore
615 # the legacy api and reimplement all these
616 # calls.
617 domid = self.domains[vm_uuid].getDomid()
618 if domid == None:
619 domid = self.domains[vm_uuid].getName()
620 return fn(domid, *args)
621 raise XendInvalidDomain("Domain does not exist")
622 finally:
623 self.domains_lock.release()
626 def create_domain(self, xenapi_vm):
627 self.domains_lock.acquire()
628 try:
629 try:
630 xeninfo = XendConfig(xenapi_vm = xenapi_vm)
631 dominfo = XendDomainInfo.createDormant(xeninfo)
632 log.debug("Creating new managed domain: %s: %s" %
633 (dominfo.getName(), dominfo.get_uuid()))
634 self._add_domain(dominfo, managed = True)
635 self.managed_config_save(dominfo)
636 return dominfo.get_uuid()
637 except XendError, e:
638 raise
639 except Exception, e:
640 raise XendError(str(e))
641 finally:
642 self.domains_lock.release()
644 def rename_domain(self, dom, new_name):
645 self.domains_lock.acquire()
646 try:
647 old_name = dom.getName()
648 dom.setName(new_name)
650 if self.is_domain_managed(dom):
651 self._managed_domain_rename(dom, new_name)
653 finally:
654 self.domains_lock.release()
657 #
658 # End of Xen API
659 # ----------------------------------------------------------------
661 # ------------------------------------------------------------
662 # Xen Legacy API
664 def list(self):
665 """Get list of domain objects.
667 @return: domains
668 @rtype: list of XendDomainInfo
669 """
670 self.domains_lock.acquire()
671 try:
672 self._refresh()
673 return self.domains.values()
674 finally:
675 self.domains_lock.release()
678 def list_sorted(self):
679 """Get list of domain objects, sorted by name.
681 @return: domain objects
682 @rtype: list of XendDomainInfo
683 """
684 doms = self.list()
685 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
686 return doms
688 def list_names(self):
689 """Get list of domain names.
691 @return: domain names
692 @rtype: list of strings.
693 """
694 return [d.getName() for d in self.list_sorted()]
696 def domain_suspend(self, domname):
697 """Suspends a domain that is persistently managed by Xend
699 @param domname: Domain Name
700 @type domname: string
701 @rtype: None
702 @raise XendError: Failure during checkpointing.
703 """
705 try:
706 dominfo = self.domain_lookup_nr(domname)
707 if not dominfo:
708 raise XendInvalidDomain(domname)
710 if dominfo.getDomid() == DOM0_ID:
711 raise XendError("Cannot save privileged domain %s" % domname)
713 if dominfo.state != XendDomainInfo.DOM_STATE_RUNNING:
714 raise XendError("Cannot suspend domain that is not running.")
716 if not os.path.exists(self._managed_config_path(domname)):
717 raise XendError("Domain is not managed by Xend lifecycle " +
718 "support.")
720 path = self._managed_check_point_path(domname)
721 fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
722 try:
723 # For now we don't support 'live checkpoint'
724 XendCheckpoint.save(fd, dominfo, False, False, path)
725 finally:
726 os.close(fd)
727 except OSError, ex:
728 raise XendError("can't write guest state file %s: %s" %
729 (path, ex[1]))
731 def domain_resume(self, domname):
732 """Resumes a domain that is persistently managed by Xend.
734 @param domname: Domain Name
735 @type domname: string
736 @rtype: None
737 @raise XendError: If failed to restore.
738 """
739 try:
740 dominfo = self.domain_lookup_nr(domname)
742 if not dominfo:
743 raise XendInvalidDomain(domname)
745 if dominfo.getDomid() == DOM0_ID:
746 raise XendError("Cannot save privileged domain %s" % domname)
748 if dominfo.state != XendDomainInfo.DOM_STATE_HALTED:
749 raise XendError("Cannot suspend domain that is not running.")
751 chkpath = self._managed_check_point_path(domname)
752 if not os.path.exists(chkpath):
753 raise XendError("Domain was not suspended by Xend")
755 # Restore that replaces the existing XendDomainInfo
756 try:
757 log.debug('Current DomainInfo state: %d' % dominfo.state)
758 XendCheckpoint.restore(self,
759 os.open(chkpath, os.O_RDONLY),
760 dominfo)
761 os.unlink(chkpath)
762 except OSError, ex:
763 raise XendError("Failed to read stored checkpoint file")
764 except IOError, ex:
765 raise XendError("Failed to delete checkpoint file")
766 except Exception, ex:
767 log.exception("Exception occurred when resuming")
768 raise XendError("Error occurred when resuming: %s" % str(ex))
771 def domain_create(self, config):
772 """Create a domain from a configuration.
774 @param config: configuration
775 @type config: SXP Object (list of lists)
776 @rtype: XendDomainInfo
777 """
778 self.domains_lock.acquire()
779 try:
780 dominfo = XendDomainInfo.create(config)
781 self._add_domain(dominfo)
782 self.domain_sched_credit_set(dominfo.getDomid(),
783 dominfo.getWeight(),
784 dominfo.getCap())
785 return dominfo
786 finally:
787 self.domains_lock.release()
790 def domain_new(self, config):
791 """Create a domain from a configuration but do not start it.
793 @param config: configuration
794 @type config: SXP Object (list of lists)
795 @rtype: XendDomainInfo
796 """
797 self.domains_lock.acquire()
798 try:
799 try:
800 xeninfo = XendConfig(sxp = config)
801 dominfo = XendDomainInfo.createDormant(xeninfo)
802 log.debug("Creating new managed domain: %s" %
803 dominfo.getName())
804 self._add_domain(dominfo, managed = True)
805 self.managed_config_save(dominfo)
806 # no return value because it isn't meaningful for client
807 except XendError, e:
808 raise
809 except Exception, e:
810 raise XendError(str(e))
811 finally:
812 self.domains_lock.release()
814 def domain_start(self, domid):
815 """Start a managed domain
817 @require: Domain must not be running.
818 @param domid: Domain name or domain ID.
819 @type domid: string or int
820 @rtype: None
821 @raise XendError: If domain is still running
822 @rtype: None
823 """
824 self.domains_lock.acquire()
825 try:
826 dominfo = self.domain_lookup_nr(domid)
827 if not dominfo:
828 raise XendInvalidDomain(str(domid))
830 if dominfo.state != XendDomainInfo.DOM_STATE_HALTED:
831 raise XendError("Domain is already running")
833 dominfo.start(is_managed = True)
836 finally:
837 self.domains_lock.release()
840 def domain_delete(self, domid):
841 """Remove a domain from database
843 @require: Domain must not be running.
844 @param domid: Domain name or domain ID.
845 @type domid: string or int
846 @rtype: None
847 @raise XendError: If domain is still running
848 """
849 self.domains_lock.acquire()
850 try:
851 try:
852 dominfo = self.domain_lookup_nr(domid)
853 if not dominfo:
854 raise XendInvalidDomain(str(domid))
856 if dominfo.state != XendDomainInfo.DOM_STATE_HALTED:
857 raise XendError("Domain is still running")
859 self._remove_domain(dominfo)
861 except Exception, ex:
862 raise XendError(str(ex))
863 finally:
864 self.domains_lock.release()
867 def domain_configure(self, config):
868 """Configure an existing domain.
870 @param vmconfig: vm configuration
871 @type vmconfig: SXP Object (list of lists)
872 @todo: Not implemented
873 """
874 # !!!
875 raise XendError("Unsupported")
877 def domain_restore(self, src):
878 """Restore a domain from file.
880 @param src: filename of checkpoint file to restore from
881 @type src: string
882 @return: Restored domain
883 @rtype: XendDomainInfo
884 @raise XendError: Failure to restore domain
885 """
886 try:
887 fd = os.open(src, os.O_RDONLY)
888 try:
889 return self.domain_restore_fd(fd)
890 finally:
891 os.close(fd)
892 except OSError, ex:
893 raise XendError("can't read guest state file %s: %s" %
894 (src, ex[1]))
896 def domain_restore_fd(self, fd):
897 """Restore a domain from the given file descriptor.
899 @param fd: file descriptor of the checkpoint file
900 @type fd: File object
901 @rtype: XendDomainInfo
902 @raise XendError: if failed to restore
903 """
905 try:
906 return XendCheckpoint.restore(self, fd)
907 except:
908 # I don't really want to log this exception here, but the error
909 # handling in the relocation-socket handling code (relocate.py) is
910 # poor, so we need to log this for debugging.
911 log.exception("Restore failed")
912 raise XendError("Restore failed")
914 def domain_unpause(self, domid):
915 """Unpause domain execution.
917 @param domid: Domain ID or Name
918 @type domid: int or string.
919 @rtype: None
920 @raise XendError: Failed to unpause
921 @raise XendInvalidDomain: Domain is not valid
922 """
923 try:
924 dominfo = self.domain_lookup_nr(domid)
925 if not dominfo:
926 raise XendInvalidDomain(str(domid))
928 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
929 int(dominfo.getDomid()))
931 dominfo.unpause()
932 except XendInvalidDomain:
933 log.exception("domain_unpause")
934 raise
935 except Exception, ex:
936 log.exception("domain_unpause")
937 raise XendError(str(ex))
939 def domain_pause(self, domid):
940 """Pause domain execution.
942 @param domid: Domain ID or Name
943 @type domid: int or string.
944 @rtype: None
945 @raise XendError: Failed to pause
946 @raise XendInvalidDomain: Domain is not valid
947 """
948 try:
949 dominfo = self.domain_lookup_nr(domid)
950 if not dominfo:
951 raise XendInvalidDomain(str(domid))
952 log.info("Domain %s (%d) paused.", dominfo.getName(),
953 int(dominfo.getDomid()))
954 dominfo.pause()
955 except XendInvalidDomain:
956 log.exception("domain_pause")
957 raise
958 except Exception, ex:
959 log.exception("domain_pause")
960 raise XendError(str(ex))
962 def domain_dump(self, domid, filename, live, crash):
963 """Dump domain core."""
965 dominfo = self.domain_lookup_nr(domid)
966 if not dominfo:
967 raise XendInvalidDomain(str(domid))
969 if dominfo.getDomid() == DOM0_ID:
970 raise XendError("Cannot dump core for privileged domain %s" % domid)
972 try:
973 log.info("Domain core dump requested for domain %s (%d) "
974 "live=%d crash=%d.",
975 dominfo.getName(), dominfo.getDomid(), live, crash)
976 return dominfo.dumpCore(filename)
977 except Exception, ex:
978 raise XendError(str(ex))
980 def domain_destroy(self, domid):
981 """Terminate domain immediately.
983 @param domid: Domain ID or Name
984 @type domid: int or string.
985 @rtype: None
986 @raise XendError: Failed to destroy
987 @raise XendInvalidDomain: Domain is not valid
988 """
990 dominfo = self.domain_lookup_nr(domid)
991 if dominfo and dominfo.getDomid() == DOM0_ID:
992 raise XendError("Cannot destroy privileged domain %s" % domid)
994 if dominfo:
995 val = dominfo.destroy()
996 else:
997 try:
998 val = xc.domain_destroy(int(domid))
999 except ValueError:
1000 raise XendInvalidDomain(domid)
1001 except Exception, e:
1002 raise XendError(str(e))
1004 return val
1006 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
1007 """Start domain migration.
1009 @param domid: Domain ID or Name
1010 @type domid: int or string.
1011 @param dst: Destination IP address
1012 @type dst: string
1013 @keyword port: relocation port on destination
1014 @type port: int
1015 @keyword live: Live migration
1016 @type live: bool
1017 @keyword resource: not used??
1018 @rtype: None
1019 @raise XendError: Failed to migrate
1020 @raise XendInvalidDomain: Domain is not valid
1021 """
1023 dominfo = self.domain_lookup_nr(domid)
1024 if not dominfo:
1025 raise XendInvalidDomain(str(domid))
1027 if dominfo.getDomid() == DOM0_ID:
1028 raise XendError("Cannot migrate privileged domain %i" % domid)
1030 """ The following call may raise a XendError exception """
1031 dominfo.testMigrateDevices(True, dst)
1033 if live:
1034 """ Make sure there's memory free for enabling shadow mode """
1035 dominfo.checkLiveMigrateMemory()
1037 if port == 0:
1038 port = xroot.get_xend_relocation_port()
1039 try:
1040 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1041 sock.connect((dst, port))
1042 except socket.error, err:
1043 raise XendError("can't connect: %s" % err[1])
1045 sock.send("receive\n")
1046 sock.recv(80)
1047 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
1048 dominfo.testDeviceComplete()
1049 sock.close()
1051 def domain_save(self, domid, dst):
1052 """Start saving a domain to file.
1054 @param domid: Domain ID or Name
1055 @type domid: int or string.
1056 @param dst: Destination filename
1057 @type dst: string
1058 @rtype: None
1059 @raise XendError: Failed to save domain
1060 @raise XendInvalidDomain: Domain is not valid
1061 """
1062 try:
1063 dominfo = self.domain_lookup_nr(domid)
1064 if not dominfo:
1065 raise XendInvalidDomain(str(domid))
1067 if dominfo.getDomid() == DOM0_ID:
1068 raise XendError("Cannot save privileged domain %i" % domid)
1070 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
1071 try:
1072 # For now we don't support 'live checkpoint'
1073 XendCheckpoint.save(fd, dominfo, False, False, dst)
1074 finally:
1075 os.close(fd)
1076 except OSError, ex:
1077 raise XendError("can't write guest state file %s: %s" %
1078 (dst, ex[1]))
1080 def domain_pincpu(self, domid, vcpu, cpumap):
1081 """Set which cpus vcpu can use
1083 @param domid: Domain ID or Name
1084 @type domid: int or string.
1085 @param vcpu: vcpu to pin to
1086 @type vcpu: int
1087 @param cpumap: string repr of usable cpus
1088 @type cpumap: string
1089 @rtype: 0
1090 """
1091 dominfo = self.domain_lookup_nr(domid)
1092 if not dominfo:
1093 raise XendInvalidDomain(str(domid))
1095 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1096 vcpus = [ vcpu ]
1097 if str(vcpu).lower() == "all":
1098 vcpus = range(0, int(dominfo.getVCpuCount()))
1100 # set the same cpumask for all vcpus
1101 rc = 0
1102 for v in vcpus:
1103 try:
1104 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
1105 except Exception, ex:
1106 raise XendError(str(ex))
1107 return rc
1109 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1110 weight):
1111 """Set Simple EDF scheduler parameters for a domain.
1113 @param domid: Domain ID or Name
1114 @type domid: int or string.
1115 @rtype: 0
1116 """
1117 dominfo = self.domain_lookup_nr(domid)
1118 if not dominfo:
1119 raise XendInvalidDomain(str(domid))
1120 try:
1121 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1122 latency, extratime, weight)
1123 except Exception, ex:
1124 raise XendError(str(ex))
1126 def domain_cpu_sedf_get(self, domid):
1127 """Get Simple EDF scheduler parameters for a domain.
1129 @param domid: Domain ID or Name
1130 @type domid: int or string.
1131 @rtype: SXP object
1132 @return: The parameters for Simple EDF schedule for a domain.
1133 """
1134 dominfo = self.domain_lookup_nr(domid)
1135 if not dominfo:
1136 raise XendInvalidDomain(str(domid))
1137 try:
1138 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1139 # return sxpr
1140 return ['sedf',
1141 ['domain', sedf_info['domain']],
1142 ['period', sedf_info['period']],
1143 ['slice', sedf_info['slice']],
1144 ['latency', sedf_info['latency']],
1145 ['extratime', sedf_info['extratime']],
1146 ['weight', sedf_info['weight']]]
1148 except Exception, ex:
1149 raise XendError(str(ex))
1151 def domain_shadow_control(self, domid, op):
1152 """Shadow page control.
1154 @param domid: Domain ID or Name
1155 @type domid: int or string.
1156 @param op: operation
1157 @type op: int
1158 @rtype: 0
1159 """
1160 dominfo = self.domain_lookup(domid)
1161 try:
1162 return xc.shadow_control(dominfo.getDomid(), op)
1163 except Exception, ex:
1164 raise XendError(str(ex))
1166 def domain_shadow_mem_get(self, domid):
1167 """Get shadow pagetable memory allocation.
1169 @param domid: Domain ID or Name
1170 @type domid: int or string.
1171 @rtype: int
1172 @return: shadow memory in MB
1173 """
1174 dominfo = self.domain_lookup(domid)
1175 try:
1176 return xc.shadow_mem_control(dominfo.getDomid())
1177 except Exception, ex:
1178 raise XendError(str(ex))
1180 def domain_shadow_mem_set(self, domid, mb):
1181 """Set shadow pagetable memory allocation.
1183 @param domid: Domain ID or Name
1184 @type domid: int or string.
1185 @param mb: shadow memory to set in MB
1186 @type: mb: int
1187 @rtype: int
1188 @return: shadow memory in MB
1189 """
1190 dominfo = self.domain_lookup(domid)
1191 try:
1192 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1193 except Exception, ex:
1194 raise XendError(str(ex))
1196 def domain_sched_credit_get(self, domid):
1197 """Get credit scheduler parameters for a domain.
1199 @param domid: Domain ID or Name
1200 @type domid: int or string.
1201 @rtype: dict with keys 'weight' and 'cap'
1202 @return: credit scheduler parameters
1203 """
1204 dominfo = self.domain_lookup_nr(domid)
1205 if not dominfo:
1206 raise XendInvalidDomain(str(domid))
1207 try:
1208 return xc.sched_credit_domain_get(dominfo.getDomid())
1209 except Exception, ex:
1210 raise XendError(str(ex))
1212 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1213 """Set credit scheduler parameters for a domain.
1215 @param domid: Domain ID or Name
1216 @type domid: int or string.
1217 @type weight: int
1218 @type cap: int
1219 @rtype: 0
1220 """
1221 dominfo = self.domain_lookup_nr(domid)
1222 if not dominfo:
1223 raise XendInvalidDomain(str(domid))
1224 try:
1225 if weight is None:
1226 weight = int(0)
1227 elif weight < 1 or weight > 65535:
1228 raise XendError("weight is out of range")
1230 if cap is None:
1231 cap = int(~0)
1232 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1233 raise XendError("cap is out of range")
1235 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1236 except Exception, ex:
1237 raise XendError(str(ex))
1239 def domain_maxmem_set(self, domid, mem):
1240 """Set the memory limit for a domain.
1242 @param domid: Domain ID or Name
1243 @type domid: int or string.
1244 @param mem: memory limit (in MiB)
1245 @type mem: int
1246 @raise XendError: fail to set memory
1247 @rtype: 0
1248 """
1249 dominfo = self.domain_lookup_nr(domid)
1250 if not dominfo:
1251 raise XendInvalidDomain(str(domid))
1252 maxmem = int(mem) * 1024
1253 try:
1254 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
1255 except Exception, ex:
1256 raise XendError(str(ex))
1258 def domain_ioport_range_enable(self, domid, first, last):
1259 """Enable access to a range of IO ports for a domain
1261 @param first: first IO port
1262 @param last: last IO port
1263 @raise XendError: failed to set range
1264 @rtype: 0
1265 """
1266 dominfo = self.domain_lookup_nr(domid)
1267 if not dominfo:
1268 raise XendInvalidDomain(str(domid))
1269 nr_ports = last - first + 1
1270 try:
1271 return xc.domain_ioport_permission(dominfo.getDomid(),
1272 first_port = first,
1273 nr_ports = nr_ports,
1274 allow_access = 1)
1275 except Exception, ex:
1276 raise XendError(str(ex))
1278 def domain_ioport_range_disable(self, domid, first, last):
1279 """Disable access to a range of IO ports for a domain
1281 @param first: first IO port
1282 @param last: last IO port
1283 @raise XendError: failed to set range
1284 @rtype: 0
1285 """
1286 dominfo = self.domain_lookup_nr(domid)
1287 if not dominfo:
1288 raise XendInvalidDomain(str(domid))
1289 nr_ports = last - first + 1
1290 try:
1291 return xc.domain_ioport_permission(dominfo.getDomid(),
1292 first_port = first,
1293 nr_ports = nr_ports,
1294 allow_access = 0)
1295 except Exception, ex:
1296 raise XendError(str(ex))
1299 def instance():
1300 """Singleton constructor. Use this instead of the class constructor.
1301 """
1302 global inst
1303 try:
1304 inst
1305 except:
1306 inst = XendDomain()
1307 inst.init()
1308 return inst