debuggers.hg

view tools/python/xen/xend/XendDomain.py @ 22906:700ac6445812

Now add KDB to the non-kdb tree
author Mukesh Rathor
date Thu Feb 03 15:42:41 2011 -0800 (2011-02-03)
parents 31b8844fab99
children
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
17 # Copyright (C) 2005 XenSource Ltd
18 #============================================================================
20 """Handler for domain operations.
21 Nothing here is persistent (across reboots).
22 Needs to be persistent for one uptime.
23 """
25 import os
26 import stat
27 import shutil
28 import socket
29 import tempfile
30 import threading
31 import re
33 import xen.lowlevel.xc
36 from xen.xend import XendOptions, XendCheckpoint, XendDomainInfo
37 from xen.xend.PrettyPrint import prettyprint
38 from xen.xend import XendConfig, image
39 from xen.xend.XendError import XendError, XendInvalidDomain, VmError
40 from xen.xend.XendError import VMBadState
41 from xen.xend.XendLogging import log
42 from xen.xend.XendAPIConstants import XEN_API_VM_POWER_STATE
43 from xen.xend.XendConstants import XS_VMROOT
44 from xen.xend.XendConstants import DOM_STATE_HALTED, DOM_STATE_PAUSED
45 from xen.xend.XendConstants import DOM_STATE_RUNNING, DOM_STATE_SUSPENDED
46 from xen.xend.XendConstants import DOM_STATE_SHUTDOWN, DOM_STATE_UNKNOWN
47 from xen.xend.XendConstants import DOM_STATE_CRASHED, HVM_PARAM_ACPI_S_STATE
48 from xen.xend.XendConstants import TRIGGER_TYPE, TRIGGER_S3RESUME
49 from xen.xend.XendDevices import XendDevices
50 from xen.xend.XendAPIConstants import *
52 from xen.xend.xenstore.xstransact import xstransact
53 from xen.xend.xenstore.xswatch import xswatch
54 from xen.util import mkdir, rwlock
55 from xen.xend import uuid
57 xc = xen.lowlevel.xc.xc()
58 xoptions = XendOptions.instance()
60 __all__ = [ "XendDomain" ]
62 CACHED_CONFIG_FILE = 'config.sxp'
63 CHECK_POINT_FILE = 'checkpoint.chk'
64 DOM0_UUID = "00000000-0000-0000-0000-000000000000"
65 DOM0_NAME = "Domain-0"
66 DOM0_ID = 0
68 POWER_STATE_NAMES = dict([(x, XEN_API_VM_POWER_STATE[x])
69 for x in [DOM_STATE_HALTED,
70 DOM_STATE_PAUSED,
71 DOM_STATE_RUNNING,
72 DOM_STATE_SUSPENDED,
73 DOM_STATE_SHUTDOWN,
74 DOM_STATE_CRASHED,
75 DOM_STATE_UNKNOWN]])
76 POWER_STATE_ALL = 'all'
79 class XendDomain:
80 """Index of all domains. Singleton.
82 @ivar domains: map of domains indexed by domid
83 @type domains: dict of XendDomainInfo
84 @ivar managed_domains: domains that are not running and managed by Xend
85 @type managed_domains: dict of XendDomainInfo indexed by uuid
86 @ivar domains_lock: lock that must be held when manipulating self.domains
87 @type domains_lock: threaading.RLock
88 @ivar _allow_new_domains: Flag to set that allows creating of new domains.
89 @type _allow_new_domains: boolean
90 """
92 def __init__(self):
93 self.domains = {}
94 self.managed_domains = {}
95 self.domains_lock = threading.RLock()
97 self.policy_lock = rwlock.RWLock()
99 # xen api instance vars
100 # TODO: nothing uses this at the moment
101 self._allow_new_domains = True
103 # This must be called only the once, by instance() below. It is separate
104 # from the constructor because XendDomainInfo calls back into this class
105 # in order to check the uniqueness of domain names. This means that
106 # instance() must be able to return a valid instance of this class even
107 # during this initialisation.
108 def init(self):
109 """Singleton initialisation function."""
111 dom_path = self._managed_path()
112 mkdir.parents(dom_path, stat.S_IRWXU)
114 xstransact.Mkdir(XS_VMROOT)
115 xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID})
117 self.domains_lock.acquire()
118 try:
119 try:
120 dom0info = [d for d in self._running_domains() \
121 if d.get('domid') == DOM0_ID][0]
123 dom0info['name'] = DOM0_NAME
124 dom0 = XendDomainInfo.recreate(dom0info, True)
125 except IndexError:
126 raise XendError('Unable to find Domain 0')
128 self._setDom0CPUCount()
130 # This watch registration needs to be before the refresh call, so
131 # that we're sure that we haven't missed any releases, but inside
132 # the domains_lock, as we don't want the watch to fire until after
133 # the refresh call has completed.
134 xswatch("@introduceDomain", self._on_domains_changed)
135 xswatch("@releaseDomain", self._on_domains_changed)
137 self._init_domains()
138 finally:
139 self.domains_lock.release()
142 def _on_domains_changed(self, _):
143 """ Callback method when xenstore changes.
145 Calls refresh which will keep the local cache of domains
146 in sync.
148 @rtype: int
149 @return: 1
150 """
151 self.domains_lock.acquire()
152 try:
153 self._refresh()
154 finally:
155 self.domains_lock.release()
156 return 1
158 def _init_domains(self):
159 """Does the initial scan of managed and active domains to
160 populate self.domains.
162 Note: L{XendDomainInfo._checkName} will call back into XendDomain
163 to make sure domain name is not a duplicate.
165 """
166 self.domains_lock.acquire()
167 try:
168 running = self._running_domains()
169 managed = self._managed_domains()
171 # add all active domains
172 for dom in running:
173 if dom['dying'] == 1:
174 log.warn('Ignoring dying domain %d from now on' %
175 dom['domid'])
176 continue
178 if dom['domid'] != DOM0_ID:
179 try:
180 new_dom = XendDomainInfo.recreate(dom, False)
181 except Exception:
182 log.exception("Failed to create reference to running "
183 "domain id: %d" % dom['domid'])
185 image.cleanup_stale_sentinel_fifos()
187 # add all managed domains as dormant domains.
188 for dom in managed:
189 dom_uuid = dom.get('uuid')
190 if not dom_uuid:
191 continue
193 dom_name = dom.get('name_label', 'Domain-%s' % dom_uuid)
194 try:
195 running_dom = self.domain_lookup_nr(dom_name)
196 if not running_dom:
197 # instantiate domain if not started.
198 new_dom = XendDomainInfo.createDormant(dom)
199 self._managed_domain_register(new_dom)
200 else:
201 self._managed_domain_register(running_dom)
202 for key in XendConfig.XENAPI_CFG_TYPES.keys():
203 if key not in XendConfig.LEGACY_XENSTORE_VM_PARAMS and \
204 key in dom:
205 running_dom.info[key] = dom[key]
206 # Devices information is restored from xenstore,
207 # but VDI value in devices information can be not
208 # restored because there is not VDI value in
209 # xenstore. So we restore VDI value by using the
210 # domain config file.
211 for vbd_ref in running_dom.info['vbd_refs']:
212 if dom['devices'].has_key(vbd_ref):
213 r_devtype, r_devinfo = running_dom.info['devices'][vbd_ref]
214 _, m_devinfo = dom['devices'][vbd_ref]
215 r_devinfo['VDI'] = m_devinfo.get('VDI', '')
216 running_dom.info['devices'][vbd_ref] = (r_devtype, r_devinfo)
217 except Exception:
218 log.exception("Failed to create reference to managed "
219 "domain: %s" % dom_name)
221 finally:
222 self.domains_lock.release()
225 # -----------------------------------------------------------------
226 # Getting managed domains storage path names
228 def _managed_path(self, domuuid = None):
229 """Returns the path of the directory where managed domain
230 information is stored.
232 @keyword domuuid: If not None, will return the path to the domain
233 otherwise, will return the path containing
234 the directories which represent each domain.
235 @type: None or String.
236 @rtype: String
237 @return: Path.
238 """
239 dom_path = xoptions.get_xend_domains_path()
240 if domuuid:
241 dom_path = os.path.join(dom_path, domuuid)
242 return dom_path
244 def _managed_config_path(self, domuuid):
245 """Returns the path to the configuration file of a managed domain.
247 @param domname: Domain uuid
248 @type domname: String
249 @rtype: String
250 @return: path to config file.
251 """
252 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE)
253 def domain_setpauseflag(self, dom, flag=False):
254 try:
255 dominfo = self.domain_lookup_nr(dom)
256 dominfo.paused_by_admin = flag
257 except Exception, err:
258 log.debug("error in in setpauseflag")
259 def domain_getpauseflag(self, dom):
260 try:
261 dominfo = self.domain_lookup_nr(dom)
262 return dominfo.paused_by_admin
263 except Exception, err:
264 log.debug("error in in getpauseflag")
266 def _managed_check_point_path(self, domuuid):
267 """Returns absolute path to check point file for managed domain.
269 @param domuuid: Name of managed domain
270 @type domname: String
271 @rtype: String
272 @return: Path
273 """
274 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE)
276 def _managed_config_remove(self, domuuid):
277 """Removes a domain configuration from managed list
279 @param domuuid: Name of managed domain
280 @type domname: String
281 @raise XendError: fails to remove the domain.
282 """
283 config_path = self._managed_path(domuuid)
284 try:
285 if os.path.exists(config_path) and os.path.isdir(config_path):
286 shutil.rmtree(config_path)
287 except IOError:
288 log.exception('managed_config_remove failed removing conf')
289 raise XendError("Unable to remove managed configuration"
290 " for domain: %s" % domuuid)
292 def managed_config_save(self, dominfo):
293 """Save a domain's configuration to disk
295 @param domninfo: Managed domain to save.
296 @type dominfo: XendDomainInfo
297 @raise XendError: fails to save configuration.
298 @rtype: None
299 """
300 if not self.is_domain_managed(dominfo):
301 return # refuse to save configuration this domain isn't managed
303 if dominfo:
304 domains_dir = self._managed_path()
305 dom_uuid = dominfo.get_uuid()
306 domain_config_dir = self._managed_path(dom_uuid)
308 def make_or_raise(path):
309 try:
310 mkdir.parents(path, stat.S_IRWXU)
311 except:
312 log.exception("%s could not be created." % path)
313 raise XendError("%s could not be created." % path)
315 make_or_raise(domains_dir)
316 make_or_raise(domain_config_dir)
318 try:
319 fd, fn = tempfile.mkstemp()
320 f = os.fdopen(fd, 'w+b')
321 try:
322 prettyprint(dominfo.sxpr(legacy_only = False), f,
323 width = 78)
324 finally:
325 f.close()
327 try:
328 shutil.move(fn, self._managed_config_path(dom_uuid))
329 except:
330 log.exception("Renaming %s to %s", fn,
331 self._managed_config_path(dom_uuid))
332 os.remove(fn)
333 except:
334 log.exception("Error occurred saving configuration file " +
335 "to %s" % domain_config_dir)
336 raise XendError("Failed to save configuration file to: %s" %
337 domain_config_dir)
338 else:
339 log.warn("Trying to save configuration for invalid domain")
342 def _managed_domains(self):
343 """ Returns list of domains that are managed.
345 Expects to be protected by domains_lock.
347 @rtype: list of XendConfig
348 @return: List of domain configurations that are managed.
349 """
350 dom_path = self._managed_path()
351 dom_uuids = os.listdir(dom_path)
352 doms = []
353 for dom_uuid in dom_uuids:
354 try:
355 cfg_file = self._managed_config_path(dom_uuid)
356 cfg = XendConfig.XendConfig(filename = cfg_file)
357 if cfg.get('uuid') != dom_uuid:
358 # something is wrong with the SXP
359 log.error("UUID mismatch in stored configuration: %s" %
360 cfg_file)
361 continue
362 doms.append(cfg)
363 except Exception:
364 log.exception('Unable to open or parse config.sxp: %s' % \
365 cfg_file)
366 return doms
368 def _managed_domain_unregister(self, dom):
369 try:
370 if self.is_domain_managed(dom):
371 self._managed_config_remove(dom.get_uuid())
372 del self.managed_domains[dom.get_uuid()]
373 dom.destroy_xapi_instances()
374 except ValueError:
375 log.warn("Domain is not registered: %s" % dom.get_uuid())
377 def _managed_domain_register(self, dom):
378 self.managed_domains[dom.get_uuid()] = dom
380 def is_domain_managed(self, dom = None):
381 return (dom.get_uuid() in self.managed_domains)
383 # End of Managed Domain Access
384 # --------------------------------------------------------------------
386 def _running_domains(self):
387 """Get table of domains indexed by id from xc.
389 @requires: Expects to be protected by domains_lock.
390 @rtype: list of dicts
391 @return: A list of dicts representing the running domains.
392 """
393 try:
394 return xc.domain_getinfo()
395 except RuntimeError, e:
396 log.exception("Unable to get domain information.")
397 return {}
399 def _setDom0CPUCount(self):
400 """Sets the number of VCPUs dom0 has. Retreived from the
401 Xend configuration, L{XendOptions}.
403 @requires: Expects to be protected by domains_lock.
404 @rtype: None
405 """
406 dom0 = self.privilegedDomain()
408 # get max number of vcpus to use for dom0 from config
409 target = int(xoptions.get_dom0_vcpus())
410 log.debug("number of vcpus to use is %d", target)
412 # target == 0 means use all processors
413 if target > 0:
414 dom0.setVCpuCount(target)
417 def _refresh(self, refresh_shutdown = True):
418 """Refresh the domain list. Needs to be called when
419 either xenstore has changed or when a method requires
420 up to date information (like uptime, cputime stats).
422 Expects to be protected by the domains_lock.
424 @rtype: None
425 """
427 txn = xstransact()
428 try:
429 self._refreshTxn(txn, refresh_shutdown)
430 txn.commit()
431 except:
432 txn.abort()
433 raise
435 def _refreshTxn(self, transaction, refresh_shutdown):
436 running = self._running_domains()
437 # Add domains that are not already tracked but running in Xen,
438 # and update domain state for those that are running and tracked.
439 for dom in running:
440 domid = dom['domid']
441 if domid in self.domains:
442 self.domains[domid].update(dom, refresh_shutdown, transaction)
443 elif domid not in self.domains and dom['dying'] != 1:
444 try:
445 new_dom = XendDomainInfo.recreate(dom, False)
446 except VmError:
447 log.exception("Unable to recreate domain")
448 try:
449 xc.domain_pause(domid)
450 XendDomainInfo.do_FLR(domid, dom['hvm'])
451 xc.domain_destroy(domid)
452 except:
453 log.exception("Hard destruction of domain failed: %d" %
454 domid)
456 # update information for all running domains
457 # - like cpu_time, status, dying, etc.
458 # remove domains that are not running from active domain list.
459 # The list might have changed by now, because the update call may
460 # cause new domains to be added, if the domain has rebooted. We get
461 # the list again.
462 running = self._running_domains()
463 running_domids = [d['domid'] for d in running if d['dying'] != 1]
464 for domid, dom in self.domains.items():
465 if domid not in running_domids and domid != DOM0_ID:
466 self._remove_domain(dom, domid)
469 def add_domain(self, info):
470 """Add a domain to the list of running domains
472 @requires: Expects to be protected by the domains_lock.
473 @param info: XendDomainInfo of a domain to be added.
474 @type info: XendDomainInfo
475 """
476 log.debug("Adding Domain: %s" % info.getDomid())
477 self.domains[info.getDomid()] = info
479 # update the managed domains with a new XendDomainInfo object
480 # if we are keeping track of it.
481 if info.get_uuid() in self.managed_domains:
482 self._managed_domain_register(info)
484 def remove_domain(self, info, domid = None):
485 """Remove the domain from the list of running domains, taking the
486 domains_lock first.
487 """
488 self.domains_lock.acquire()
489 try:
490 self._remove_domain(info, domid)
491 finally:
492 self.domains_lock.release()
494 def _remove_domain(self, info, domid = None):
495 """Remove the domain from the list of running domains
497 @requires: Expects to be protected by the domains_lock.
498 @param info: XendDomainInfo of a domain to be removed.
499 @type info: XendDomainInfo
500 """
501 if info:
502 if domid == None:
503 domid = info.getDomid()
505 if info._stateGet() != DOM_STATE_HALTED:
506 info.cleanupDomain()
508 if domid in self.domains:
509 del self.domains[domid]
511 info.destroy_xapi_instances()
512 else:
513 log.warning("Attempted to remove non-existent domain.")
515 def restore_(self, config):
516 """Create a domain as part of the restore process. This is called
517 only from L{XendCheckpoint}.
519 A restore request comes into XendDomain through L{domain_restore}
520 or L{domain_restore_fd}. That request is
521 forwarded immediately to XendCheckpoint which, when it is ready, will
522 call this method. It is necessary to come through here rather than go
523 directly to L{XendDomainInfo.restore} because we need to
524 serialise the domain creation process, but cannot lock
525 domain_restore_fd as a whole, otherwise we will deadlock waiting for
526 the old domain to die.
528 @param config: Configuration of domain to restore
529 @type config: SXP Object (eg. list of lists)
530 """
531 self.domains_lock.acquire()
532 try:
533 dominfo = XendDomainInfo.restore(config)
534 return dominfo
535 finally:
536 self.domains_lock.release()
539 def domain_lookup(self, domid):
540 """Look up given I{domid} in the list of managed and running
541 domains.
543 @note: Will cause a refresh before lookup up domains, for
544 a version that does not need to re-read xenstore
545 use L{domain_lookup_nr}.
547 @param domid: Domain ID or Domain Name.
548 @type domid: int or string
549 @return: Found domain.
550 @rtype: XendDomainInfo
551 @raise XendInvalidDomain: If domain is not found.
552 """
553 self.domains_lock.acquire()
554 try:
555 self._refresh(refresh_shutdown = False)
556 dom = self.domain_lookup_nr(domid)
557 if not dom:
558 raise XendInvalidDomain(str(domid))
559 return dom
560 finally:
561 self.domains_lock.release()
564 def domain_lookup_nr(self, domid):
565 """Look up given I{domid} in the list of managed and running
566 domains.
568 @param domid: Domain ID or Domain Name.
569 @type domid: int or string
570 @return: Found domain.
571 @rtype: XendDomainInfo or None
572 """
573 self.domains_lock.acquire()
574 try:
575 # lookup by name
576 match = [dom for dom in self.domains.values() \
577 if dom.getName() == domid]
578 if match:
579 return match[0]
581 match = [dom for dom in self.managed_domains.values() \
582 if dom.getName() == domid]
583 if match:
584 return match[0]
586 # lookup by id
587 try:
588 if int(domid) in self.domains:
589 return self.domains[int(domid)]
590 except ValueError:
591 pass
593 # lookup by uuid for running domains
594 match = [dom for dom in self.domains.values() \
595 if dom.get_uuid() == domid]
596 if match:
597 return match[0]
599 # lookup by uuid for inactive managed domains
600 if domid in self.managed_domains:
601 return self.managed_domains[domid]
603 return None
604 finally:
605 self.domains_lock.release()
607 def privilegedDomain(self):
608 """ Get the XendDomainInfo of a dom0
610 @rtype: XendDomainInfo
611 """
612 self.domains_lock.acquire()
613 try:
614 return self.domains[DOM0_ID]
615 finally:
616 self.domains_lock.release()
618 def autostart_domains(self):
619 """ Autostart managed domains that are marked as such. """
621 need_starting = []
623 self.domains_lock.acquire()
624 try:
625 for dom_uuid, dom in self.managed_domains.items():
626 if dom and dom._stateGet() == DOM_STATE_HALTED:
627 on_xend_start = dom.info.get('on_xend_start', 'ignore')
628 auto_power_on = dom.info.get('auto_power_on', False)
629 should_start = (on_xend_start == 'start') or auto_power_on
630 if should_start:
631 need_starting.append(dom_uuid)
632 finally:
633 self.domains_lock.release()
635 for dom_uuid in need_starting:
636 self.domain_start(dom_uuid, False)
638 def cleanup_domains(self):
639 """Clean up domains that are marked as autostop.
640 Should be called when Xend goes down. This is currently
641 called from L{xen.xend.servers.XMLRPCServer}.
643 """
644 log.debug('cleanup_domains')
645 self.domains_lock.acquire()
646 try:
647 for dom in self.domains.values():
648 if dom.getName() == DOM0_NAME:
649 continue
651 try:
652 if dom._stateGet() == DOM_STATE_RUNNING:
653 shutdownAction = dom.info.get('on_xend_stop', 'ignore')
654 if shutdownAction == 'shutdown':
655 log.debug('Shutting down domain: %s' % dom.getName())
656 dom.shutdown("poweroff")
657 elif shutdownAction == 'suspend':
658 self.domain_suspend(dom.getName())
659 else:
660 log.debug('Domain %s continues to run.' % dom.getName())
661 except:
662 log.exception('Domain %s failed to %s.' % \
663 (dom.getName(), shutdownAction))
664 finally:
665 self.domains_lock.release()
669 # ----------------------------------------------------------------
670 # Xen API
673 def set_allow_new_domains(self, allow_new_domains):
674 self._allow_new_domains = allow_new_domains
676 def allow_new_domains(self):
677 return self._allow_new_domains
679 def get_domain_refs(self):
680 result = []
681 try:
682 self.domains_lock.acquire()
683 result = [d.get_uuid() for d in self.domains.values()]
684 for d in self.managed_domains.keys():
685 if d not in result:
686 result.append(d)
687 return result
688 finally:
689 self.domains_lock.release()
691 def get_all_vms(self):
692 self.domains_lock.acquire()
693 try:
694 result = self.domains.values()
695 result += [x for x in self.managed_domains.values() if
696 x not in result]
697 return result
698 finally:
699 self.domains_lock.release()
701 def get_vm_by_uuid(self, vm_uuid):
702 self.domains_lock.acquire()
703 try:
704 for dom in self.domains.values():
705 if dom.get_uuid() == vm_uuid:
706 return dom
708 if vm_uuid in self.managed_domains:
709 return self.managed_domains[vm_uuid]
711 return None
712 finally:
713 self.domains_lock.release()
715 def get_vm_with_dev_uuid(self, klass, dev_uuid):
716 self.domains_lock.acquire()
717 try:
718 for dom in self.domains.values() + self.managed_domains.values():
719 if dom.has_device(klass, dev_uuid):
720 return dom
721 return None
722 finally:
723 self.domains_lock.release()
725 def get_dev_property_by_uuid(self, klass, dev_uuid, field):
726 value = None
727 self.domains_lock.acquire()
729 try:
730 try:
731 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
732 if dom:
733 value = dom.get_dev_property(klass, dev_uuid, field)
734 except ValueError, e:
735 pass
736 finally:
737 self.domains_lock.release()
739 return value
741 def set_dev_property_by_uuid(self, klass, dev_uuid, field, value,
742 old_val = None):
743 rc = True
744 self.domains_lock.acquire()
746 try:
747 try:
748 dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
749 if dom:
750 o_val = dom.get_dev_property(klass, dev_uuid, field)
751 log.info("o_val=%s, old_val=%s" % (o_val, old_val))
752 if old_val and old_val != o_val:
753 return False
755 dom.set_dev_property(klass, dev_uuid, field, value)
756 self.managed_config_save(dom)
757 except ValueError, e:
758 pass
759 finally:
760 self.domains_lock.release()
762 return rc
764 def is_valid_vm(self, vm_ref):
765 return (self.get_vm_by_uuid(vm_ref) != None)
767 def is_valid_dev(self, klass, dev_uuid):
768 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None)
770 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs):
771 dom = self.uuid_to_dom(vm_uuid)
772 fn(dom, *args, **kwargs)
774 def uuid_to_dom(self, vm_uuid):
775 self.domains_lock.acquire()
776 try:
777 for domid, dom in self.domains.items():
778 if dom.get_uuid() == vm_uuid:
779 return domid
781 if vm_uuid in self.managed_domains:
782 domid = self.managed_domains[vm_uuid].getDomid()
783 if domid is None:
784 return self.managed_domains[vm_uuid].getName()
785 else:
786 return domid
788 raise XendInvalidDomain(vm_uuid)
789 finally:
790 self.domains_lock.release()
793 def create_domain(self, xenapi_vm):
794 self.domains_lock.acquire()
795 try:
796 try:
797 xeninfo = XendConfig.XendConfig(xapi = xenapi_vm)
798 dominfo = XendDomainInfo.createDormant(xeninfo)
799 log.debug("Creating new managed domain: %s: %s" %
800 (dominfo.getName(), dominfo.get_uuid()))
801 self._managed_domain_register(dominfo)
802 self.managed_config_save(dominfo)
803 return dominfo.get_uuid()
804 except XendError, e:
805 raise
806 except Exception, e:
807 raise XendError(str(e))
808 finally:
809 self.domains_lock.release()
811 def rename_domain(self, dom, new_name):
812 self.domains_lock.acquire()
813 try:
814 old_name = dom.getName()
815 dom.setName(new_name)
817 finally:
818 self.domains_lock.release()
821 #
822 # End of Xen API
823 # ----------------------------------------------------------------
825 # ------------------------------------------------------------
826 # Xen Legacy API
828 def list(self, state = DOM_STATE_RUNNING):
829 """Get list of domain objects.
831 @param: the state in which the VMs should be -- one of the
832 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
833 @return: domains
834 @rtype: list of XendDomainInfo
835 """
836 if type(state) == int:
837 state = POWER_STATE_NAMES[state]
838 state = state.lower()
839 resu = False
840 count = 0
841 while True:
842 resu = self.domains_lock.acquire(0)
843 if resu or count < 20:
844 break
845 count += 1
846 try:
847 if resu:
848 self._refresh(refresh_shutdown = False)
850 # active domains
851 active_domains = self.domains.values()
852 active_uuids = [d.get_uuid() for d in active_domains]
854 # inactive domains
855 inactive_domains = []
856 for dom_uuid, dom in self.managed_domains.items():
857 if dom_uuid not in active_uuids:
858 inactive_domains.append(dom)
860 if state == POWER_STATE_ALL:
861 return active_domains + inactive_domains
862 else:
863 return filter(lambda x:
864 POWER_STATE_NAMES[x._stateGet()].lower() == state,
865 active_domains + inactive_domains)
866 finally:
867 if resu:
868 self.domains_lock.release()
871 def list_sorted(self, state = DOM_STATE_RUNNING):
872 """Get list of domain objects, sorted by name.
874 @param: the state in which the VMs should be -- one of the
875 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
876 @return: domain objects
877 @rtype: list of XendDomainInfo
878 """
879 doms = self.list(state)
880 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
881 return doms
883 def list_names(self, state = DOM_STATE_RUNNING):
884 """Get list of domain names.
886 @param: the state in which the VMs should be -- one of the
887 DOM_STATE_XYZ constants, or the corresponding name, or 'all'.
888 @return: domain names
889 @rtype: list of strings.
890 """
891 return [d.getName() for d in self.list_sorted(state)]
893 def domain_suspend(self, domname):
894 """Suspends a domain that is persistently managed by Xend
896 @param domname: Domain Name
897 @type domname: string
898 @rtype: None
899 @raise XendError: Failure during checkpointing.
900 """
902 try:
903 dominfo = self.domain_lookup_nr(domname)
904 if not dominfo:
905 raise XendInvalidDomain(domname)
907 if dominfo.getDomid() == DOM0_ID:
908 raise XendError("Cannot suspend privileged domain %s" % domname)
910 if dominfo._stateGet() != DOM_STATE_RUNNING:
911 raise VMBadState("Domain is not running",
912 POWER_STATE_NAMES[DOM_STATE_RUNNING],
913 POWER_STATE_NAMES[dominfo._stateGet()])
915 dom_uuid = dominfo.get_uuid()
917 if not os.path.exists(self._managed_config_path(dom_uuid)):
918 raise XendError("Domain is not managed by Xend lifecycle " +
919 "support.")
921 path = self._managed_check_point_path(dom_uuid)
922 oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
923 if hasattr(os, "O_LARGEFILE"):
924 oflags |= os.O_LARGEFILE
925 fd = os.open(path, oflags)
926 try:
927 # For now we don't support 'live checkpoint'
928 XendCheckpoint.save(fd, dominfo, False, False, path)
929 finally:
930 os.close(fd)
931 except OSError, ex:
932 raise XendError("can't write guest state file %s: %s" %
933 (path, ex[1]))
935 def domain_resume(self, domname, start_paused = False):
936 """Resumes a domain that is persistently managed by Xend.
938 @param domname: Domain Name
939 @type domname: string
940 @rtype: None
941 @raise XendError: If failed to restore.
942 """
943 self.domains_lock.acquire()
944 try:
945 try:
946 fd = None
947 dominfo = self.domain_lookup_nr(domname)
949 if not dominfo:
950 raise XendInvalidDomain(domname)
952 if dominfo.getDomid() == DOM0_ID:
953 raise XendError("Cannot resume privileged domain %s" % domname)
955 if dominfo._stateGet() != XEN_API_VM_POWER_STATE_SUSPENDED:
956 raise XendError("Cannot resume domain that is not suspended.")
958 dominfo.setResume(True)
960 dom_uuid = dominfo.get_uuid()
961 chkpath = self._managed_check_point_path(dom_uuid)
962 if not os.path.exists(chkpath):
963 raise XendError("Domain was not suspended by Xend")
965 # Restore that replaces the existing XendDomainInfo
966 try:
967 log.debug('Current DomainInfo state: %d' % dominfo._stateGet())
968 oflags = os.O_RDONLY
969 if hasattr(os, "O_LARGEFILE"):
970 oflags |= os.O_LARGEFILE
971 fd = os.open(chkpath, oflags)
972 XendCheckpoint.restore(self,
973 fd,
974 dominfo,
975 paused = start_paused)
976 os.unlink(chkpath)
977 except OSError, ex:
978 raise XendError("Failed to read stored checkpoint file")
979 except IOError, ex:
980 raise XendError("Failed to delete checkpoint file")
981 except Exception, ex:
982 log.exception("Exception occurred when resuming")
983 raise XendError("Error occurred when resuming: %s" % str(ex))
984 finally:
985 if fd is not None:
986 os.close(fd)
987 self.domains_lock.release()
990 def domain_create(self, config):
991 """Create a domain from a configuration.
993 @param config: configuration
994 @type config: SXP Object (list of lists)
995 @rtype: XendDomainInfo
996 """
997 self.domains_lock.acquire()
998 try:
999 self._refresh()
1001 dominfo = XendDomainInfo.create(config)
1002 return dominfo
1003 finally:
1004 self.domains_lock.release()
1007 def domain_create_from_dict(self, config_dict):
1008 """Create a domain from a configuration dictionary.
1010 @param config_dict: configuration
1011 @rtype: XendDomainInfo
1012 """
1013 self.domains_lock.acquire()
1014 try:
1015 self._refresh()
1017 dominfo = XendDomainInfo.create_from_dict(config_dict)
1018 return dominfo
1019 finally:
1020 self.domains_lock.release()
1023 def domain_new(self, config):
1024 """Create a domain from a configuration but do not start it.
1026 @param config: configuration
1027 @type config: SXP Object (list of lists)
1028 @rtype: XendDomainInfo
1029 """
1030 self.domains_lock.acquire()
1031 try:
1032 try:
1033 domconfig = XendConfig.XendConfig(sxp_obj = config)
1034 dominfo = XendDomainInfo.createDormant(domconfig)
1035 log.debug("Creating new managed domain: %s" %
1036 dominfo.getName())
1037 self._managed_domain_register(dominfo)
1038 self.managed_config_save(dominfo)
1039 # no return value because it isn't meaningful for client
1040 except XendError, e:
1041 raise
1042 except Exception, e:
1043 raise XendError(str(e))
1044 finally:
1045 self.domains_lock.release()
1047 def domain_start(self, domid, start_paused = True):
1048 """Start a managed domain
1050 @require: Domain must not be running.
1051 @param domid: Domain name or domain ID.
1052 @type domid: string or int
1053 @rtype: None
1054 @raise XendError: If domain is still running
1055 @rtype: None
1056 """
1057 self.domains_lock.acquire()
1058 try:
1059 self._refresh()
1061 dominfo = self.domain_lookup_nr(domid)
1062 if not dominfo:
1063 raise XendInvalidDomain(str(domid))
1065 if dominfo._stateGet() != DOM_STATE_HALTED:
1066 raise VMBadState("Domain is already running",
1067 POWER_STATE_NAMES[DOM_STATE_HALTED],
1068 POWER_STATE_NAMES[dominfo._stateGet()])
1070 dominfo.start(is_managed = True)
1071 finally:
1072 self.domains_lock.release()
1074 try:
1075 dominfo.waitForDevices()
1076 except Exception, ex:
1077 log.warn("Failed to setup devices for " + str(dominfo) + ": " + str(ex))
1078 dominfo.destroy()
1079 raise
1081 if not start_paused:
1082 dominfo.unpause()
1084 def domain_delete(self, domid):
1085 """Remove a managed domain from database
1087 @require: Domain must not be running.
1088 @param domid: Domain name or domain ID.
1089 @type domid: string or int
1090 @rtype: None
1091 @raise XendError: If domain is still running
1092 """
1093 self.domains_lock.acquire()
1094 try:
1095 try:
1096 dominfo = self.domain_lookup_nr(domid)
1097 if not dominfo:
1098 raise XendInvalidDomain(str(domid))
1100 if dominfo._stateGet() != XEN_API_VM_POWER_STATE_HALTED:
1101 raise VMBadState("Domain is not halted.",
1102 POWER_STATE_NAMES[DOM_STATE_HALTED],
1103 POWER_STATE_NAMES[dominfo._stateGet()])
1105 self._domain_delete_by_info(dominfo)
1106 except Exception, ex:
1107 raise XendError(str(ex))
1108 finally:
1109 self.domains_lock.release()
1112 def domain_delete_by_dominfo(self, dominfo):
1113 """Only for use by XendDomainInfo.
1114 """
1115 self.domains_lock.acquire()
1116 try:
1117 self._domain_delete_by_info(dominfo)
1118 finally:
1119 self.domains_lock.release()
1122 def _domain_delete_by_info(self, dominfo):
1123 """Expects to be protected by domains_lock.
1124 """
1125 log.info("Domain %s (%s) deleted." %
1126 (dominfo.getName(), dominfo.info.get('uuid')))
1128 self._managed_domain_unregister(dominfo)
1129 self._remove_domain(dominfo)
1130 XendDevices.destroy_device_state(dominfo)
1133 def domain_configure(self, config):
1134 """Configure an existing domain.
1136 @param vmconfig: vm configuration
1137 @type vmconfig: SXP Object (list of lists)
1138 @todo: Not implemented
1139 """
1140 # !!!
1141 raise XendError("Unsupported")
1143 def domain_restore(self, src, paused=False):
1144 """Restore a domain from file.
1146 @param src: filename of checkpoint file to restore from
1147 @type src: string
1148 @return: Restored domain
1149 @rtype: XendDomainInfo
1150 @raise XendError: Failure to restore domain
1151 """
1152 try:
1153 oflags = os.O_RDONLY
1154 if hasattr(os, "O_LARGEFILE"):
1155 oflags |= os.O_LARGEFILE
1156 fd = os.open(src, oflags)
1157 try:
1158 return self.domain_restore_fd(fd, paused=paused)
1159 finally:
1160 os.close(fd)
1161 except OSError, ex:
1162 raise XendError("can't read guest state file %s: %s" %
1163 (src, ex[1]))
1165 def domain_restore_fd(self, fd, paused=False, relocating=False):
1166 """Restore a domain from the given file descriptor.
1168 @param fd: file descriptor of the checkpoint file
1169 @type fd: File object
1170 @rtype: XendDomainInfo
1171 @raise XendError: if failed to restore
1172 """
1174 try:
1175 self.policy_lock.acquire_reader()
1177 try:
1178 dominfo = XendCheckpoint.restore(self, fd, paused=paused, relocating=relocating)
1179 if relocating and \
1180 dominfo.info.has_key("change_home_server"):
1181 chs = (dominfo.info["change_home_server"] == "True")
1182 dominfo.setChangeHomeServer(None)
1183 if chs:
1184 self.domains_lock.acquire()
1185 try:
1186 log.debug("Migrating new managed domain: %s: %s" %
1187 (dominfo.getName(), dominfo.get_uuid()))
1188 self._managed_domain_register(dominfo)
1189 self.managed_config_save(dominfo)
1190 finally:
1191 self.domains_lock.release()
1192 return dominfo
1193 except XendError, e:
1194 log.exception("Restore failed")
1195 raise
1196 except:
1197 # I don't really want to log this exception here, but the error
1198 # handling in the relocation-socket handling code (relocate.py) is
1199 # poor, so we need to log this for debugging.
1200 log.exception("Restore failed")
1201 raise XendError("Restore failed")
1202 finally:
1203 self.policy_lock.release()
1205 def domain_unpause(self, domid):
1206 """Unpause domain execution.
1208 @param domid: Domain ID or Name
1209 @type domid: int or string.
1210 @rtype: None
1211 @raise XendError: Failed to unpause
1212 @raise XendInvalidDomain: Domain is not valid
1213 """
1214 try:
1215 dominfo = self.domain_lookup_nr(domid)
1216 if not dominfo:
1217 raise XendInvalidDomain(str(domid))
1218 if dominfo.getDomid() == DOM0_ID:
1219 raise XendError("Cannot unpause privileged domain %s" % domid)
1220 if dominfo._stateGet() not in (DOM_STATE_PAUSED, DOM_STATE_RUNNING):
1221 raise VMBadState("Domain '%s' is not started" % domid,
1222 POWER_STATE_NAMES[DOM_STATE_PAUSED],
1223 POWER_STATE_NAMES[dominfo._stateGet()])
1224 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
1225 int(dominfo.getDomid()))
1226 dominfo.unpause()
1227 except XendInvalidDomain:
1228 log.exception("domain_unpause")
1229 raise
1230 except Exception, ex:
1231 log.exception("domain_unpause")
1232 raise XendError(str(ex))
1234 def domain_pause(self, domid, state=False):
1235 """Pause domain execution.
1237 @param domid: Domain ID or Name
1238 @type domid: int or string.
1239 @keyword state: If True, will return the domain state before pause
1240 @type state: bool
1241 @rtype: int if state is True
1242 @return: Domain state (DOM_STATE_*)
1243 @rtype: None if state is False
1244 @raise XendError: Failed to pause
1245 @raise XendInvalidDomain: Domain is not valid
1246 """
1247 try:
1248 dominfo = self.domain_lookup_nr(domid)
1249 if not dominfo:
1250 raise XendInvalidDomain(str(domid))
1251 if dominfo.getDomid() == DOM0_ID:
1252 raise XendError("Cannot pause privileged domain %s" % domid)
1253 ds = dominfo._stateGet()
1254 if ds not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED):
1255 raise VMBadState("Domain '%s' is not started" % domid,
1256 POWER_STATE_NAMES[DOM_STATE_RUNNING],
1257 POWER_STATE_NAMES[ds])
1258 log.info("Domain %s (%d) paused.", dominfo.getName(),
1259 int(dominfo.getDomid()))
1260 if ds == DOM_STATE_RUNNING:
1261 dominfo.pause()
1262 if state:
1263 return ds
1264 except XendInvalidDomain:
1265 log.exception("domain_pause")
1266 raise
1267 except Exception, ex:
1268 log.exception("domain_pause")
1269 raise XendError(str(ex))
1271 def domain_dump(self, domid, filename=None, live=False, crash=False, reset=False):
1272 """Dump domain core."""
1274 dominfo = self.domain_lookup_nr(domid)
1275 if not dominfo:
1276 raise XendInvalidDomain(str(domid))
1278 if dominfo.getDomid() == DOM0_ID:
1279 raise XendError("Cannot dump core for privileged domain %s" % domid)
1280 if dominfo._stateGet() not in (DOM_STATE_PAUSED, DOM_STATE_RUNNING, DOM_STATE_CRASHED):
1281 raise VMBadState("Domain '%s' is not started" % domid,
1282 POWER_STATE_NAMES[DOM_STATE_PAUSED],
1283 POWER_STATE_NAMES[dominfo._stateGet()])
1285 dopause = (not live and dominfo._stateGet() == DOM_STATE_RUNNING)
1286 if dopause:
1287 dominfo.pause()
1289 try:
1290 try:
1291 log.info("Domain core dump requested for domain %s (%d) "
1292 "live=%d crash=%d reset=%d.",
1293 dominfo.getName(), dominfo.getDomid(), live, crash, reset)
1294 dominfo.dumpCore(filename)
1295 if crash:
1296 self.domain_destroy(domid)
1297 elif reset:
1298 self.domain_reset(domid)
1299 except Exception, ex:
1300 raise XendError(str(ex))
1301 finally:
1302 if dopause and not crash and not reset:
1303 dominfo.unpause()
1305 def domain_destroy(self, domid):
1306 """Terminate domain immediately.
1308 @param domid: Domain ID or Name
1309 @type domid: int or string.
1310 @rtype: None
1311 @raise XendError: Failed to destroy
1312 @raise XendInvalidDomain: Domain is not valid
1313 """
1315 dominfo = self.domain_lookup_nr(domid)
1316 if dominfo and dominfo.getDomid() == DOM0_ID:
1317 raise XendError("Cannot destroy privileged domain %s" % domid)
1319 if dominfo:
1320 val = dominfo.destroy()
1321 else:
1322 try:
1323 xc.domain_pause(int(domid))
1324 dom = self.domains[int(domid)]
1325 XendDomainInfo.do_FLR(int(domid), dom.info.is_hvm())
1326 val = xc.domain_destroy(int(domid))
1327 except ValueError:
1328 raise XendInvalidDomain(domid)
1329 except Exception, e:
1330 raise XendError(str(e))
1332 return val
1334 def domain_migrate(self, domid, dst, live=False, port=0, node=-1, ssl=None,\
1335 chs=False):
1336 """Start domain migration.
1338 @param domid: Domain ID or Name
1339 @type domid: int or string.
1340 @param dst: Destination IP address
1341 @type dst: string
1342 @keyword live: Live migration
1343 @type live: bool
1344 @keyword port: relocation port on destination
1345 @type port: int
1346 @keyword node: use node number for target
1347 @type node: int
1348 @keyword ssl: use ssl connection
1349 @type ssl: bool
1350 @keyword chs: change home server for managed domain
1351 @type chs: bool
1352 @rtype: None
1353 @raise XendError: Failed to migrate
1354 @raise XendInvalidDomain: Domain is not valid
1355 """
1357 dominfo = self.domain_lookup_nr(domid)
1358 if not dominfo:
1359 raise XendInvalidDomain(str(domid))
1361 if dominfo.getDomid() == DOM0_ID:
1362 raise XendError("Cannot migrate privileged domain %s" % domid)
1363 if dominfo._stateGet() != DOM_STATE_RUNNING:
1364 raise VMBadState("Domain is not running",
1365 POWER_STATE_NAMES[DOM_STATE_RUNNING],
1366 POWER_STATE_NAMES[dominfo._stateGet()])
1367 if chs and not self.is_domain_managed(dominfo):
1368 raise XendError("Domain is not a managed domain")
1370 """ The following call may raise a XendError exception """
1371 dominfo.testMigrateDevices(True, dst)
1373 if live:
1374 """ Make sure there's memory free for enabling shadow mode """
1375 dominfo.checkLiveMigrateMemory()
1377 if ssl is None:
1378 ssl = xoptions.get_xend_relocation_ssl()
1380 try:
1381 dominfo.setChangeHomeServer(chs)
1382 if ssl:
1383 self._domain_migrate_by_ssl(dominfo, dst, live, port, node)
1384 else:
1385 self._domain_migrate(dominfo, dst, live, port, node)
1386 except:
1387 dominfo.setChangeHomeServer(None)
1388 raise
1390 def _domain_migrate_by_ssl(self, dominfo, dst, live, port, node):
1391 from OpenSSL import SSL
1392 from xen.web import connection
1393 if port == 0:
1394 port = xoptions.get_xend_relocation_ssl_port()
1395 try:
1396 ctx = SSL.Context(SSL.SSLv23_METHOD)
1397 sock = SSL.Connection(ctx,
1398 socket.socket(socket.AF_INET, socket.SOCK_STREAM))
1399 sock.set_connect_state()
1400 sock.connect((dst, port))
1401 sock.send("sslreceive\n")
1402 sock.recv(80)
1403 except SSL.Error, err:
1404 raise XendError("SSL error: %s" % err)
1405 except socket.error, err:
1406 raise XendError("can't connect: %s" % err)
1408 p2cread, p2cwrite = os.pipe()
1409 threading.Thread(target=connection.SSLSocketServerConnection.fd2send,
1410 args=(sock, p2cread)).start()
1412 try:
1413 try:
1414 XendCheckpoint.save(p2cwrite, dominfo, True, live, dst,
1415 node=node,sock=sock)
1416 except Exception, ex:
1417 m_dsterr = None
1418 try:
1419 sock.settimeout(3.0)
1420 dsterr = sock.recv(1024)
1421 sock.settimeout(None)
1422 if dsterr:
1423 # See send_error@relocate.py. If an error occurred
1424 # in a destination side, an error message with the
1425 # following form is returned from the destination
1426 # side.
1427 m_dsterr = \
1428 re.match(r"^\(err\s\(type\s(.+)\)\s\(value\s'(.+)'\)\)", dsterr)
1429 except:
1430 # Probably socket.timeout exception occurred.
1431 # Ignore the exception because it has nothing to do with
1432 # an exception of XendCheckpoint.save.
1433 pass
1435 if m_dsterr:
1436 raise XendError("%s (from %s)" % (m_dsterr.group(2), dst))
1437 raise
1438 finally:
1439 if not live:
1440 try:
1441 sock.shutdown(2)
1442 except:
1443 # Probably the socket is already disconnected by sock.close
1444 # in the destination side.
1445 # Ignore the exception because it has nothing to do with
1446 # an exception of XendCheckpoint.save.
1447 pass
1448 sock.close()
1450 os.close(p2cread)
1451 os.close(p2cwrite)
1453 def _domain_migrate(self, dominfo, dst, live, port, node):
1454 if port == 0:
1455 port = xoptions.get_xend_relocation_port()
1456 try:
1457 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1458 # When connecting to our ssl enabled relocation server using a
1459 # plain socket, send will success but recv will block. Add a
1460 # 30 seconds timeout to raise a socket.timeout exception to
1461 # inform the client.
1462 sock.settimeout(30.0)
1463 sock.connect((dst, port))
1464 sock.send("receive\n")
1465 sock.recv(80)
1466 sock.settimeout(None)
1467 except socket.error, err:
1468 raise XendError("can't connect: %s" % err)
1470 try:
1471 try:
1472 XendCheckpoint.save(sock.fileno(), dominfo, True, live,
1473 dst, node=node,sock=sock)
1474 except Exception, ex:
1475 m_dsterr = None
1476 try:
1477 sock.settimeout(3.0)
1478 dsterr = sock.recv(1024)
1479 sock.settimeout(None)
1480 if dsterr:
1481 # See send_error@relocate.py. If an error occurred
1482 # in a destination side, an error message with the
1483 # following form is returned from the destination
1484 # side.
1485 m_dsterr = \
1486 re.match(r"^\(err\s\(type\s(.+)\)\s\(value\s'(.+)'\)\)", dsterr)
1487 except:
1488 # Probably socket.timeout exception occurred.
1489 # Ignore the exception because it has nothing to do with
1490 # an exception of XendCheckpoint.save.
1491 pass
1493 if m_dsterr:
1494 raise XendError("%s (from %s)" % (m_dsterr.group(2), dst))
1495 raise
1496 finally:
1497 if not live:
1498 try:
1499 sock.shutdown(2)
1500 except:
1501 # Probably the socket is already disconnected by sock.close
1502 # in the destination side.
1503 # Ignore the exception because it has nothing to do with
1504 # an exception of XendCheckpoint.save.
1505 pass
1506 sock.close()
1508 def domain_save(self, domid, dst, checkpoint=False):
1509 """Start saving a domain to file.
1511 @param domid: Domain ID or Name
1512 @type domid: int or string.
1513 @param dst: Destination filename
1514 @type dst: string
1515 @rtype: None
1516 @raise XendError: Failed to save domain
1517 @raise XendInvalidDomain: Domain is not valid
1518 """
1519 try:
1520 dominfo = self.domain_lookup_nr(domid)
1521 if not dominfo:
1522 raise XendInvalidDomain(str(domid))
1524 if dominfo.getDomid() == DOM0_ID:
1525 raise XendError("Cannot save privileged domain %s" % str(domid))
1526 if dominfo._stateGet() != DOM_STATE_RUNNING:
1527 raise VMBadState("Domain is not running",
1528 POWER_STATE_NAMES[DOM_STATE_RUNNING],
1529 POWER_STATE_NAMES[dominfo._stateGet()])
1531 oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
1532 if hasattr(os, "O_LARGEFILE"):
1533 oflags |= os.O_LARGEFILE
1534 fd = os.open(dst, oflags)
1535 try:
1536 XendCheckpoint.save(fd, dominfo, False, False, dst,
1537 checkpoint=checkpoint)
1538 except Exception, e:
1539 os.close(fd)
1540 raise e
1541 os.close(fd)
1542 except OSError, ex:
1543 raise XendError("can't write guest state file %s: %s" %
1544 (dst, ex[1]))
1546 def domain_usb_add(self, domid, dev_id):
1547 dominfo = self.domain_lookup_nr(domid)
1548 if not dominfo:
1549 raise XendInvalidDomain(str(domid))
1551 usb = dominfo.info['platform'].get('usb')
1552 if not usb:
1553 raise XendError("Can't add usb device to a guest with usb disabled in configure file")
1555 hvm = dominfo.info.is_hvm()
1556 if not hvm:
1557 raise XendError("Can't add usb device to a non-hvm guest")
1559 if dominfo._stateGet() != DOM_STATE_HALTED:
1560 dominfo.image.signalDeviceModel("usb-add",
1561 "usb-added", dev_id)
1562 else:
1563 log.debug("error: Domain is not running!")
1566 def domain_usb_del(self, domid, dev_id):
1567 dominfo = self.domain_lookup_nr(domid)
1568 if not dominfo:
1569 raise XendInvalidDomain(str(domid))
1571 usb = dominfo.info['platform'].get('usb')
1572 if not usb:
1573 raise XendError("Can't add usb device to a guest with usb disabled in configure file")
1575 hvm = dominfo.info.is_hvm()
1576 if not hvm:
1577 raise XendError("Can't del usb to a non-hvm guest")
1579 if dominfo._stateGet() != DOM_STATE_HALTED:
1580 dominfo.image.signalDeviceModel("usb-del",
1581 "usb-deleted", dev_id)
1582 else:
1583 log.debug("error: Domain is not running!")
1585 def domain_pincpu(self, domid, vcpu, cpumap):
1586 """Set which cpus vcpu can use
1588 @param domid: Domain ID or Name
1589 @type domid: int or string.
1590 @param vcpu: vcpu to pin to
1591 @type vcpu: int
1592 @param cpumap: string repr of usable cpus
1593 @type cpumap: string
1594 @rtype: 0
1595 """
1596 dominfo = self.domain_lookup_nr(domid)
1597 if not dominfo:
1598 raise XendInvalidDomain(str(domid))
1600 # if vcpu is keyword 'all', apply the cpumap to all vcpus
1601 if str(vcpu).lower() == "all":
1602 vcpus = range(0, int(dominfo.getVCpuCount()))
1603 else:
1604 vcpus = [ int(vcpu) ]
1606 # set the same cpumask for all vcpus
1607 rc = 0
1608 cpus = dominfo.getCpus()
1609 cpumap = map(int, cpumap.split(","))
1610 for v in vcpus:
1611 try:
1612 if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
1613 rc = xc.vcpu_setaffinity(dominfo.getDomid(), v, cpumap)
1614 cpus[v] = cpumap
1615 except Exception, ex:
1616 log.exception(ex)
1617 raise XendError("Cannot pin vcpu: %d to cpu: %s - %s" % \
1618 (v, cpumap, str(ex)))
1619 dominfo.setCpus(cpus)
1620 self.managed_config_save(dominfo)
1622 return rc
1624 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
1625 weight):
1626 """Set Simple EDF scheduler parameters for a domain.
1628 @param domid: Domain ID or Name
1629 @type domid: int or string.
1630 @rtype: 0
1631 """
1632 dominfo = self.domain_lookup_nr(domid)
1633 if not dominfo:
1634 raise XendInvalidDomain(str(domid))
1635 try:
1636 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
1637 latency, extratime, weight)
1638 except Exception, ex:
1639 raise XendError(str(ex))
1641 def domain_cpu_sedf_get(self, domid):
1642 """Get Simple EDF scheduler parameters for a domain.
1644 @param domid: Domain ID or Name
1645 @type domid: int or string.
1646 @rtype: SXP object
1647 @return: The parameters for Simple EDF schedule for a domain.
1648 """
1649 dominfo = self.domain_lookup_nr(domid)
1650 if not dominfo:
1651 raise XendInvalidDomain(str(domid))
1652 try:
1653 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
1654 # return sxpr
1655 return ['sedf',
1656 ['domid', sedf_info['domid']],
1657 ['period', sedf_info['period']],
1658 ['slice', sedf_info['slice']],
1659 ['latency', sedf_info['latency']],
1660 ['extratime', sedf_info['extratime']],
1661 ['weight', sedf_info['weight']]]
1663 except Exception, ex:
1664 raise XendError(str(ex))
1666 def domain_shadow_control(self, domid, op):
1667 """Shadow page control.
1669 @param domid: Domain ID or Name
1670 @type domid: int or string.
1671 @param op: operation
1672 @type op: int
1673 @rtype: 0
1674 """
1675 dominfo = self.domain_lookup(domid)
1676 try:
1677 return xc.shadow_control(dominfo.getDomid(), op)
1678 except Exception, ex:
1679 raise XendError(str(ex))
1681 def domain_shadow_mem_get(self, domid):
1682 """Get shadow pagetable memory allocation.
1684 @param domid: Domain ID or Name
1685 @type domid: int or string.
1686 @rtype: int
1687 @return: shadow memory in MB
1688 """
1689 dominfo = self.domain_lookup(domid)
1690 try:
1691 return xc.shadow_mem_control(dominfo.getDomid())
1692 except Exception, ex:
1693 raise XendError(str(ex))
1695 def domain_shadow_mem_set(self, domid, mb):
1696 """Set shadow pagetable memory allocation.
1698 @param domid: Domain ID or Name
1699 @type domid: int or string.
1700 @param mb: shadow memory to set in MB
1701 @type: mb: int
1702 @rtype: int
1703 @return: shadow memory in MB
1704 """
1705 dominfo = self.domain_lookup(domid)
1706 try:
1707 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb)
1708 except Exception, ex:
1709 raise XendError(str(ex))
1711 def domain_sched_credit_get(self, domid):
1712 """Get credit scheduler parameters for a domain.
1714 @param domid: Domain ID or Name
1715 @type domid: int or string.
1716 @rtype: dict with keys 'weight' and 'cap'
1717 @return: credit scheduler parameters
1718 """
1719 dominfo = self.domain_lookup_nr(domid)
1720 if not dominfo:
1721 raise XendInvalidDomain(str(domid))
1723 if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
1724 try:
1725 return xc.sched_credit_domain_get(dominfo.getDomid())
1726 except Exception, ex:
1727 raise XendError(str(ex))
1728 else:
1729 return {'weight' : dominfo.getWeight(),
1730 'cap' : dominfo.getCap()}
1732 def domain_sched_credit_set(self, domid, weight = None, cap = None):
1733 """Set credit scheduler parameters for a domain.
1735 @param domid: Domain ID or Name
1736 @type domid: int or string.
1737 @type weight: int
1738 @type cap: int
1739 @rtype: 0
1740 """
1741 set_weight = False
1742 set_cap = False
1743 dominfo = self.domain_lookup_nr(domid)
1744 if not dominfo:
1745 raise XendInvalidDomain(str(domid))
1746 try:
1747 if weight is None:
1748 weight = int(0)
1749 elif weight < 1 or weight > 65535:
1750 raise XendError("Cpu weight out of range, valid values are "
1751 "within range from 1 to 65535")
1752 else:
1753 set_weight = True
1755 if cap is None:
1756 cap = int(~0)
1757 elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
1758 raise XendError("Cpu cap out of range, valid range is "
1759 "from 0 to %s for specified number of vcpus" %
1760 (dominfo.getVCpuCount() * 100))
1761 else:
1762 set_cap = True
1764 assert type(weight) == int
1765 assert type(cap) == int
1767 rc = 0
1768 if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
1769 rc = xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
1770 if rc == 0:
1771 if set_weight:
1772 dominfo.setWeight(weight)
1773 if set_cap:
1774 dominfo.setCap(cap)
1775 self.managed_config_save(dominfo)
1776 return rc
1777 except Exception, ex:
1778 log.exception(ex)
1779 raise XendError(str(ex))
1781 def domain_sched_credit2_get(self, domid):
1782 """Get credit2 scheduler parameters for a domain.
1784 @param domid: Domain ID or Name
1785 @type domid: int or string.
1786 @rtype: dict with keys 'weight'
1787 @return: credit2 scheduler parameters
1788 """
1789 dominfo = self.domain_lookup_nr(domid)
1790 if not dominfo:
1791 raise XendInvalidDomain(str(domid))
1793 if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
1794 try:
1795 return xc.sched_credit2_domain_get(dominfo.getDomid())
1796 except Exception, ex:
1797 raise XendError(str(ex))
1798 else:
1799 return {'weight' : dominfo.getWeight()}
1801 def domain_sched_credit2_set(self, domid, weight = None):
1802 """Set credit2 scheduler parameters for a domain.
1804 @param domid: Domain ID or Name
1805 @type domid: int or string.
1806 @type weight: int
1807 @rtype: 0
1808 """
1809 set_weight = False
1810 dominfo = self.domain_lookup_nr(domid)
1811 if not dominfo:
1812 raise XendInvalidDomain(str(domid))
1813 try:
1814 if weight is None:
1815 weight = int(0)
1816 elif weight < 1 or weight > 65535:
1817 raise XendError("weight is out of range")
1818 else:
1819 set_weight = True
1821 assert type(weight) == int
1823 rc = 0
1824 if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
1825 rc = xc.sched_credit2_domain_set(dominfo.getDomid(), weight)
1826 if rc == 0:
1827 if set_weight:
1828 dominfo.setWeight(weight)
1829 self.managed_config_save(dominfo)
1830 return rc
1831 except Exception, ex:
1832 log.exception(ex)
1833 raise XendError(str(ex))
1835 def domain_maxmem_set(self, domid, mem):
1836 """Set the memory limit for a domain.
1838 @param domid: Domain ID or Name
1839 @type domid: int or string.
1840 @param mem: memory limit (in MiB)
1841 @type mem: int
1842 @raise XendError: fail to set memory
1843 @rtype: 0
1844 """
1845 dominfo = self.domain_lookup_nr(domid)
1846 if not dominfo:
1847 raise XendInvalidDomain(str(domid))
1848 dominfo.setMemoryMaximum(mem)
1850 def domain_ioport_range_enable(self, domid, first, last):
1851 """Enable access to a range of IO ports for a domain
1853 @param first: first IO port
1854 @param last: last IO port
1855 @raise XendError: failed to set range
1856 @rtype: 0
1857 """
1858 dominfo = self.domain_lookup_nr(domid)
1859 if not dominfo:
1860 raise XendInvalidDomain(str(domid))
1861 nr_ports = last - first + 1
1862 try:
1863 return xc.domain_ioport_permission(dominfo.getDomid(),
1864 first_port = first,
1865 nr_ports = nr_ports,
1866 allow_access = 1)
1867 except Exception, ex:
1868 raise XendError(str(ex))
1870 def domain_ioport_range_disable(self, domid, first, last):
1871 """Disable access to a range of IO ports for a domain
1873 @param first: first IO port
1874 @param last: last IO port
1875 @raise XendError: failed to set range
1876 @rtype: 0
1877 """
1878 dominfo = self.domain_lookup_nr(domid)
1879 if not dominfo:
1880 raise XendInvalidDomain(str(domid))
1881 nr_ports = last - first + 1
1882 try:
1883 return xc.domain_ioport_permission(dominfo.getDomid(),
1884 first_port = first,
1885 nr_ports = nr_ports,
1886 allow_access = 0)
1887 except Exception, ex:
1888 raise XendError(str(ex))
1890 def domain_send_trigger(self, domid, trigger_name, vcpu = 0):
1891 """Send trigger to a domain.
1893 @param domid: Domain ID or Name
1894 @type domid: int or string.
1895 @param trigger_name: trigger type name
1896 @type trigger_name: string
1897 @param vcpu: VCPU to send trigger (default is 0)
1898 @type vcpu: int
1899 @raise XendError: failed to send trigger
1900 @raise XendInvalidDomain: Domain is not valid
1901 @rtype: 0
1902 """
1903 dominfo = self.domain_lookup_nr(domid)
1904 if not dominfo:
1905 raise XendInvalidDomain(str(domid))
1906 if dominfo._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
1907 raise VMBadState("Domain '%s' is not started" % domid,
1908 POWER_STATE_NAMES[DOM_STATE_RUNNING],
1909 POWER_STATE_NAMES[dominfo._stateGet()])
1910 if trigger_name.lower() in TRIGGER_TYPE.keys():
1911 trigger = TRIGGER_TYPE[trigger_name.lower()]
1912 else:
1913 raise XendError("Invalid trigger: %s" % trigger_name)
1914 if trigger == TRIGGER_S3RESUME:
1915 xc.hvm_set_param(dominfo.getDomid(), HVM_PARAM_ACPI_S_STATE, 0)
1916 return None
1917 try:
1918 return xc.domain_send_trigger(dominfo.getDomid(),
1919 trigger,
1920 vcpu)
1921 except Exception, ex:
1922 raise XendError(str(ex))
1924 def domain_reset(self, domid):
1925 """Terminate domain immediately, and then create domain.
1927 @param domid: Domain ID or Name
1928 @type domid: int or string.
1929 @rtype: None
1930 @raise XendError: Failed to destroy or create
1931 @raise XendInvalidDomain: Domain is not valid
1932 """
1934 dominfo = self.domain_lookup_nr(domid)
1935 if not dominfo:
1936 raise XendInvalidDomain(str(domid))
1937 if dominfo and dominfo.getDomid() == DOM0_ID:
1938 raise XendError("Cannot reset privileged domain %s" % domid)
1939 if dominfo._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
1940 raise VMBadState("Domain '%s' is not started" % domid,
1941 POWER_STATE_NAMES[DOM_STATE_RUNNING],
1942 POWER_STATE_NAMES[dominfo._stateGet()])
1943 try:
1944 dominfo.resetDomain()
1945 except Exception, ex:
1946 raise XendError(str(ex))
1949 def instance():
1950 """Singleton constructor. Use this instead of the class constructor.
1951 """
1952 global inst
1953 try:
1954 inst
1955 except:
1956 inst = XendDomain()
1957 inst.init()
1958 return inst