xen/xend-domain-lock.patch

328 lines
11 KiB
Diff

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 <vm name>] [-i <vm uuid>] [-p <physical host>] 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 <<EOF
+$name $uuid $host
+EOF
+}
+
+remove_lock(){
+ local path=$1/lock
+
+ rm -f $path
+}
+
+get_status() {
+ local path=$1/lock
+
+ [ -f $path ] || exit 1
+ echo `cat $path`
+}
+
+mode="status"
+
+while getopts ":lusn:i:p:" opt; do
+ case $opt in
+ l )
+ mode="lock"
+ ;;
+ u )
+ mode="unlock"
+ ;;
+ s )
+ mode="status"
+ ;;
+ p )
+ vm_host=$OPTARG
+ ;;
+ n )
+ vm_name=$OPTARG
+ ;;
+ i )
+ vm_uuid=$OPTARG
+ ;;
+ \? )
+ usage
+ ;;
+ esac
+done
+
+shift $(($OPTIND - 1))
+vm_path=$1
+[ -z $vm_path ] && usage
+
+case $mode in
+ lock )
+ create_lock $vm_path $vm_name $vm_uuid $vm_host
+ ;;
+ unlock )
+ remove_lock $vm_path
+ ;;
+ status )
+ get_status $vm_path
+ ;;
+esac
Index: xen-3.3.1-testing/tools/examples/xend-config.sxp
===================================================================
--- xen-3.3.1-testing.orig/tools/examples/xend-config.sxp
+++ xen-3.3.1-testing/tools/examples/xend-config.sxp
@@ -255,4 +255,46 @@
# Path where persistent domain configuration is stored.
# Default is /var/lib/xend/domains/
+#
#(xend-domains-path /var/lib/xend/domains)
+
+# Create an external lock file when domains are started. Lock
+# file is placed in xend-domains-lock-path/<domain_uuid> 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 <xend-domain-lock-path>/<domain-uuid>.
+# 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 <vm-name>] [-i <vm-id>] [-p <phy-host>] 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
+# <xend-domain-lock-path>/<vm-uuid>/lock and write <vm-name>, <vm-id>,
+# and <vm-host> (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:
@@ -2339,6 +2343,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()
@@ -3560,6 +3569,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 '<domain id=%s name=%s memory=%s state=%s>' % \
(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