Index: xen-3.3.1-testing/tools/examples/domain-lock =================================================================== --- /dev/null +++ xen-3.3.1-testing/tools/examples/domain-lock @@ -0,0 +1,83 @@ +#!/bin/bash + +usage() { + echo "usage: domain-lock [-l|-u|-s] [-n ] [-i ] [-p ] path" + echo "" + echo "-l lock" + echo "-u unlock" + echo "-s status (default)" + echo "-n Virtual Machine name" + echo "-i Virtual Machine Id or UUID" + echo "-p Virtual Machine Server (physical host) name" + echo "path A per-VM, unique location where external lock will be managed" + exit 1 +} + +create_lock() { + local path=$1 + local name=$2 + local uuid=$3 + local host=$4 + + path=$path/lock + cat > $path < on domain +# startup and removed when domain is stopped. By default, a lock file +# is not created. Set to yes to enable lock file creation. +# Note that external locking mechanisms are no substitute for a cluster +# environment that protects shared resources, but may be useful in +# some circumstances nonetheless. +# +#(xend-domain-lock no) + +# Path where domain lock is stored if xend-domain-lock is enabled. +# Note: This path must be accessible to all VM Servers participating +# in domain locking, e.g. by specifying a shared mount point. +# Lock is placed in /. +# Default is /var/lib/xend/domains/ +# +#(xend-domain-lock-path /var/lib/xend/domains) + +# External locking utility for acquiring/releasing domain lock. +# By default /etc/xen/scripts/domain-lock will be used if +# xend-domain-lock is set to yes. Set to path of custom +# locking utility to override the default. Synopsis: +# +# lock-util <-l | -u | -s> [-n ] [-i ] [-p ] path +# +# Utility should return zero on success, non-zero on error. +# -l lock Acquire (create) lock file +# -u unlock Remove lock file +# -s status Default action. If lock file exists, print contents on +# stdout and return 0) +# -p phy-host Name of physical host (dom0) +# -n vm-name Name of domain +# -i vm-id Id or UUID of domain +# path A unique location for external lock must be specified +# +# The /etc/xen/scripts/domain-lock default utility will create +# //lock and write , , +# and (if supplied) to the lock file in that order. +# +#(xend-domain-lock-utility domain-lock) Index: xen-3.3.1-testing/tools/python/xen/xend/XendDomainInfo.py =================================================================== --- xen-3.3.1-testing.orig/tools/python/xen/xend/XendDomainInfo.py +++ xen-3.3.1-testing/tools/python/xen/xend/XendDomainInfo.py @@ -30,11 +30,13 @@ import threading import re import copy import os +import stat +import shutil import traceback from types import StringTypes import xen.lowlevel.xc -from xen.util import asserts +from xen.util import asserts, mkdir from xen.util.blkif import parse_uname import xen.util.xsm.xsm as security from xen.util import xsconstants @@ -443,6 +445,7 @@ class XendDomainInfo: if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED, XEN_API_VM_POWER_STATE_CRASHED): try: + self.acquire_running_lock(); XendTask.log_progress(0, 30, self._constructDomain) XendTask.log_progress(31, 60, self._initDomain) @@ -475,6 +478,7 @@ class XendDomainInfo: state = self._stateGet() if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED): try: + self.acquire_running_lock(); self._constructDomain() try: @@ -2331,6 +2335,11 @@ class XendDomainInfo: self._stateSet(DOM_STATE_HALTED) self.domid = None # Do not push into _stateSet()! + + try: + self.release_running_lock() + except: + log.exception("Failed to release domain lock.") finally: self.refresh_shutdown_lock.release() @@ -3552,6 +3561,74 @@ class XendDomainInfo: def has_device(self, dev_class, dev_uuid): return (dev_uuid in self.info['%s_refs' % dev_class.lower()]) + # Check if domain has a lock file present + def is_dom_locked(self, path): + status = os.system('%s -s %s' % \ + (xoptions.get_xend_domain_lock_utility(), \ + path)) + + return status == 0 + + # Return name of host contained in lock file. Host should be last + # entry in the lock file. + def get_lock_host(self, path): + fin = os.popen(xoptions.get_xend_domain_lock_utility() + \ + ' -s ' + path, 'r') + hostname = "unknown" + + try: + line = fin.readline() + hostname = line.split()[-1] + return hostname + finally: + fin.close() + + # Acquire a lock for the domain. No-op if domain locking is turned off. + def acquire_running_lock(self): + if not xoptions.get_xend_domain_lock(): + return + + path = xoptions.get_xend_domain_lock_path() + path = os.path.join(path, self.get_uuid()) + + if self.is_dom_locked(path): + raise XendError("The VM is locked and appears to be running on host %s." % self.get_lock_host(path)) + + try: + if not os.path.exists(path): + mkdir.parents(path, stat.S_IRWXU) + except: + log.exception("%s could not be created." % path) + raise XendError("%s could not be created." % path) + + status = os.system('%s -l -p %s -n %s -i %s %s' % \ + (xoptions.get_xend_domain_lock_utility(), \ + XendNode.instance().get_name(), \ + self.info['name_label'], \ + self.info['uuid'], \ + path)) + if status != 0: + raise XendError('Acquire running lock failed: %s' % status) + + # Release lock for domain. Should be called at end of domain cleanup + def release_running_lock(self): + if not xoptions.get_xend_domain_lock(): + return + + path = xoptions.get_xend_domain_lock_path() + path = os.path.join(path, self.get_uuid()) + status = os.system('%s -u %s' % \ + (xoptions.get_xend_domain_lock_utility(), \ + path)) + if status != 0: + log.exception("Release running lock failed: %s" % status) + try: + if len(os.listdir(path)) == 0: + shutil.rmtree(path) + except: + log.exception("Failed to remove unmanaged directory %s." % path) + + def __str__(self): return '' % \ (str(self.domid), self.info['name_label'], Index: xen-3.3.1-testing/tools/python/xen/xend/XendDomain.py =================================================================== --- xen-3.3.1-testing.orig/tools/python/xen/xend/XendDomain.py +++ xen-3.3.1-testing/tools/python/xen/xend/XendDomain.py @@ -1299,6 +1299,7 @@ class XendDomain: POWER_STATE_NAMES[DOM_STATE_RUNNING], POWER_STATE_NAMES[dominfo._stateGet()]) + dominfo.release_running_lock(); """ The following call may raise a XendError exception """ dominfo.testMigrateDevices(True, dst) Index: xen-3.3.1-testing/tools/python/xen/xend/XendOptions.py =================================================================== --- xen-3.3.1-testing.orig/tools/python/xen/xend/XendOptions.py +++ xen-3.3.1-testing/tools/python/xen/xend/XendOptions.py @@ -135,6 +135,17 @@ class XendOptions: """Default rotation count of qemu-dm log file.""" qemu_dm_logrotate_count = 10 + """Default for the flag indicating whether xend should create + a lock file for domains when they are started.""" + xend_domain_lock = 'no' + + """Default domain lock storage path.""" + xend_domain_lock_path_default = '/var/lib/xend/domains' + + """Default script to acquire/release domain lock""" + xend_domain_lock_utility = osdep.scripts_dir + "/domain-lock" + + def __init__(self): self.configure() @@ -358,6 +369,19 @@ class XendOptions: return self.get_config_int("qemu-dm-logrotate-count", self.qemu_dm_logrotate_count) + def get_xend_domain_lock(self): + """Get the flag indicating whether xend should create a lock file + for domains when they are started.""" + return self.get_config_bool("xend-domain-lock", self.xend_domain_lock) + + def get_xend_domain_lock_path(self): + """ Get the path for domain lock storage + """ + return self.get_config_string("xend-domain-lock-path", self.xend_domain_lock_path_default) + + def get_xend_domain_lock_utility(self): + return self.get_config_string('xend-domain-lock-utility', self.xend_domain_lock_utility) + class XendOptionsFile(XendOptions): Index: xen-3.3.1-testing/tools/examples/Makefile =================================================================== --- xen-3.3.1-testing.orig/tools/examples/Makefile +++ xen-3.3.1-testing/tools/examples/Makefile @@ -35,6 +35,7 @@ XEN_SCRIPTS += vtpm vtpm-delete XEN_SCRIPTS += xen-hotplug-cleanup XEN_SCRIPTS += external-device-migrate XEN_SCRIPTS += vscsi +XEN_SCRIPTS += domain-lock XEN_SCRIPT_DATA = xen-script-common.sh locking.sh logging.sh XEN_SCRIPT_DATA += xen-hotplug-common.sh xen-network-common.sh vif-common.sh XEN_SCRIPT_DATA += block-common.sh vtpm-common.sh vtpm-hotplug-common.sh