Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3153972
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
101 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/heartbeat/AoEtarget b/heartbeat/AoEtarget
index 47636f2cd..acd1f4a3b 100755
--- a/heartbeat/AoEtarget
+++ b/heartbeat/AoEtarget
@@ -1,247 +1,247 @@
#!/bin/bash
#
#
# AoEtarget OCF RA.
# Manages an ATA-over-Ethernet (AoE) target utilizing the vblade utility.
#
-# Copyright (c) 2009 LINBIT HA-Solutions GmbH, Florian Haas
-# All Rights Reserved.
+# (c) 2009-2010 Florian Haas, Dejan Muhamedagic,
+# and Linux-HA contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Further, this software is distributed without any warranty that it is
# free of the rightful claim of any third person regarding infringement
# or the like. Any license provided herein, whether implied or
# otherwise, applies only to this software file. Patent licenses, if
# any, provided herein do not apply to combinations of this program with
# other software, or any other product whatsoever.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
#
######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
LC_ALL="C"
LANG="C"
# Defaults
OCF_RESKEY_nic_default="eth0"
OCF_RESKEY_pid_default="${HA_RSCTMP}/AoEtarget-${OCF_RESOURCE_INSTANCE}.pid"
OCF_RESKEY_binary_default="/usr/sbin/vblade"
: ${OCF_RESKEY_nic=${OCF_RESKEY_nic_default}}
: ${OCF_RESKEY_pid=${OCF_RESKEY_pid_default}}
: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}}
#######################################################################
meta_data() {
cat <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="AoEtarget" version="0.1">
<version>1.0</version>
<longdesc lang="en">
This resource agent manages an ATA-over-Ethernet (AoE) target using vblade.
It exports any block device, or file, as an AoE target using the
specified Ethernet device, shelf, and slot number.
</longdesc>
<shortdesc lang="en">Manages ATA-over-Ethernet (AoE) target exports</shortdesc>
<parameters>
<parameter name="device" required="1">
<longdesc lang="en">
The local block device (or file) to export as an AoE target.
</longdesc>
<shortdesc lang="en">Device to export</shortdesc>
<content type="string"/>
</parameter>
<parameter name="nic" required="1">
<longdesc lang="en">
The local Ethernet interface to use for exporting this AoE target.
</longdesc>
<shortdesc lang="en">Ethernet interface</shortdesc>
<content type="string" default="${OCF_RESKEY_nic_default}"/>
</parameter>
<parameter name="shelf" required="0">
<longdesc lang="en">
The AoE shelf number to use when exporting this target.
</longdesc>
<shortdesc lang="en">AoE shelf number</shortdesc>
<content type="integer"/>
</parameter>
<parameter name="slot" required="1">
<longdesc lang="en">
The AoE slot number to use when exporting this target.
</longdesc>
<shortdesc lang="en">AoE slot number</shortdesc>
<content type="integer"/>
</parameter>
<parameter name="pid" required="0" unique="1">
<longdesc lang="en">
The file to record the daemon pid to.
</longdesc>
<shortdesc lang="en">Daemon pid file</shortdesc>
<content type="string" default="${OCF_RESKEY_pid_default}"/>
</parameter>
<parameter name="binary" required="0">
<longdesc lang="en">
Location of the vblade binary.
</longdesc>
<shortdesc lang="en">vblade binary</shortdesc>
<content type="string" default="${OCF_RESKEY_binary_default}"/>
</parameter>
</parameters>
<actions>
<action name="start" timeout="15"/>
<action name="stop" timeout="15"/>
<action name="monitor" timeout="15" interval="10" depth="0"/>
<action name="reload" timeout="15"/>
<action name="meta-data" timeout="5"/>
<action name="validate-all" timeout="15"/>
</actions>
</resource-agent>
EOF
}
#######################################################################
AoEtarget_usage() {
cat <<END
usage: $0 {start|stop|status|monitor|validate-all|meta-data}
Expects to have a fully populated OCF RA-compliant environment set.
END
}
AoEtarget_start() {
AoEtarget_monitor
if [ $? = $OCF_SUCCESS ]; then
return $OCF_SUCCESS
fi
ocf_log info "Exporting device ${OCF_RESKEY_device} on ${OCF_RESKEY_nic} as shelf ${OCF_RESKEY_shelf}, slot ${OCF_RESKEY_slot}"
${OCF_RESKEY_binary} ${OCF_RESKEY_shelf} ${OCF_RESKEY_slot} \
${OCF_RESKEY_nic} ${OCF_RESKEY_device} 2>&1 &
rc=$?
pid=$!
if [ $rc -ne 0 ]; then
return $OCF_ERR_GENERIC
fi
echo $pid > ${OCF_RESKEY_pid} && return $OCF_SUCCESS
return $OCF_ERR_GENERIC
}
AoEtarget_stop() {
AoEtarget_monitor
if [ $? -eq $OCF_SUCCESS ]; then
ocf_log info "Unxporting device ${OCF_RESKEY_device} on ${OCF_RESKEY_nic} as shelf ${OCF_RESKEY_shelf}, slot ${OCF_RESKEY_slot}"
pid=$(cat ${OCF_RESKEY_pid})
kill -TERM $pid
# loop until we're really stopped, wait for the LRM to time us
# out if not
while AoEtarget_monitor; do
sleep 1
done
fi
# Clean up pid file
rm -f ${OCF_RESKEY_pid}
return $OCF_SUCCESS
}
AoEtarget_monitor() {
ocf_pidfile_status ${OCF_RESKEY_pid} >/dev/null 2>&1
rc=$?
if [ $rc -eq 2 ]; then
# no pid file, must assume we're not running
return $OCF_NOT_RUNNING
elif [ $rc -eq 1 ]; then
# stale pid file, assume something went wrong
return $OCF_ERR_GENERIC
fi
return $OCF_RUNNING
}
AoEtarget_validate() {
# Is our binary executable?
if [ ! -x ${OCF_RESKEY_binary} ]; then
ocf_log error "${OCF_RESKEY_binary} not found or not executable"
return $OCF_ERR_INSTALLED
fi
# Do we have all required variables?
for var in device nic shelf slot pid; do
param="OCF_RESKEY_${var}"
if [ -z "${!param}" ]; then
ocf_log error "Missing resource parameter \"$var\"!"
return $OCF_ERR_CONFIGURED
fi
done
# Is the pid file directory writable?
pid_dir=`dirname "$OCF_RESKEY_pid"`
touch "$pid_dir/$$"
if [ $? != 0 ]; then
ocf_log error "Cannot create pid file in $pid_dir -- check directory permissions"
return $OCF_ERR_INSTALLED
fi
rm "$pid_dir/$$"
# Does the device we are trying to export exist?
if [ ! -e ${OCF_RESKEY_device} ]; then
ocf_log error "${OCF_RESKEY_device} does not exist"
return $OCF_ERR_INSTALLED
fi
return $OCF_SUCCESS
}
case $1 in
meta-data)
meta_data
exit $OCF_SUCCESS
;;
usage|help)
AoEtarget_usage
exit $OCF_SUCCESS
;;
esac
# Everything except usage and meta-data must pass the validate test
AoEtarget_validate || exit $?
case $__OCF_ACTION in
start)
AoEtarget_start
;;
stop)
AoEtarget_stop
;;
status|monitor)
AoEtarget_monitor
;;
reload)
ocf_log err "Reloading..."
AoEtarget_start
;;
validate-all)
AoEtarget_validate
;;
*)
AoEtarget_usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
exit $rc
diff --git a/heartbeat/Route b/heartbeat/Route
index f0550b517..0ce828b7d 100755
--- a/heartbeat/Route
+++ b/heartbeat/Route
@@ -1,296 +1,299 @@
#!/bin/sh
#
# Route OCF RA. Enables and disables network routes.
#
+# (c) 2008-2010 Florian Haas, Dejan Muhamedagic,
+# and Linux-HA contributors
+#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Further, this software is distributed without any warranty that it is
# free of the rightful claim of any third person regarding infringement
# or the like. Any license provided herein, whether implied or
# otherwise, applies only to this software file. Patent licenses, if
# any, provided herein do not apply to combinations of this program with
# other software, or any other product whatsoever.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
#
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
#######################################################################
meta_data() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="Route" version="0.1">
<version>1.0</version>
<longdesc lang="en">
Enables and disables network routes.
Supports host and net routes, routes via a gateway address,
and routes using specific source addresses.
This resource agent is useful if a node's routing table
needs to be manipulated based on node role assignment.
Consider the following example use case:
- One cluster node serves as an IPsec tunnel endpoint.
- All other nodes use the IPsec tunnel to reach hosts
in a specific remote network.
Then, here is how you would implement this scheme making use
of the Route resource agent:
- Configure an ipsec LSB resource.
- Configure a cloned Route OCF resource.
- Create an order constraint to ensure
that ipsec is started before Route.
- Create a colocation constraint between the
ipsec and Route resources, to make sure no instance
of your cloned Route resource is started on the
tunnel endpoint itself.
</longdesc>
<shortdesc lang="en">Manages network routes</shortdesc>
<parameters>
<parameter name="destination" unique="1" required="1">
<longdesc lang="en">
The destination network (or host) to be configured for the route.
Specify the netmask suffix in CIDR notation (e.g. "/24").
If no suffix is given, a host route will be created.
Specify "0.0.0.0/0" or "default" if you want this resource to set
the system default route.
</longdesc>
<shortdesc lang="en">Destination network</shortdesc>
<content type="string" />
</parameter>
<parameter name="device" unique="1">
<longdesc lang="en">
The outgoing network device to use for this route.
</longdesc>
<shortdesc lang="en">Outgoing network device</shortdesc>
<content type="string" default="" />
</parameter>
<parameter name="gateway" unique="1">
<longdesc lang="en">
The gateway IP address to use for this route.
</longdesc>
<shortdesc lang="en">Gateway IP address</shortdesc>
<content type="string" default="" />
</parameter>
<parameter name="source" unique="1">
<longdesc lang="en">
The source IP address to be configured for the route.
</longdesc>
<shortdesc lang="en">Source IP address</shortdesc>
<content type="string" default="" />
</parameter>
<parameter name="table" unique="0">
<longdesc lang="en">
The routing table to be configured for the route.
</longdesc>
<shortdesc lang="en">Routing table</shortdesc>
<content type="string" default="" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="20" />
<action name="stop" timeout="20" />
<action name="monitor" timeout="20" interval="10"
depth="0"/>
<action name="reload" timeout="20" />
<action name="meta-data" timeout="5" />
<action name="validate-all" timeout="20" />
</actions>
</resource-agent>
END
}
#######################################################################
create_route_spec() {
# Creates a route specification for use by "ip route (add|del|show)"
route_spec="to ${OCF_RESKEY_destination}"
if [ -n "${OCF_RESKEY_device}" ]; then
route_spec="${route_spec} dev ${OCF_RESKEY_device}"
fi
if [ -n "${OCF_RESKEY_gateway}" ]; then
route_spec="${route_spec} via ${OCF_RESKEY_gateway}"
fi
if [ -n "${OCF_RESKEY_source}" ]; then
route_spec="${route_spec} src ${OCF_RESKEY_source}"
fi
if [ -n "${OCF_RESKEY_table}" ]; then
route_spec="${route_spec} table ${OCF_RESKEY_table}"
fi
echo "$route_spec"
}
route_usage() {
cat <<END
usage: $0 {start|stop|status|monitor|validate-all|meta-data}
Expects to have a fully populated OCF RA-compliant environment set.
END
}
route_start() {
route_status
status=$?
if [ $status -eq $OCF_SUCCESS ]; then
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : already started."
return $OCF_SUCCESS
fi
route_spec="$(create_route_spec)"
if ip route add $route_spec; then
ocf_log info "${OCF_RESOURCE_INSTANCE} Added network route: $route_spec"
return $OCF_SUCCESS
else
ocf_log error "${OCF_RESOURCE_INSTANCE} Failed to add network route: $route_spec"
fi
return $OCF_ERR_GENERIC
}
route_stop() {
route_status
status=$?
case $status in
$OCF_SUCCESS)
route_spec="$(create_route_spec)"
if ip route del $route_spec; then
ocf_log info "${OCF_RESOURCE_INSTANCE} Removed network route: $route_spec"
return $OCF_SUCCESS
else
ocf_log error "${OCF_RESOURCE_INSTANCE} Failed to remove network route: $route_spec"
fi
;;
$OCF_NOT_RUNNING)
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : already stopped."
return $OCF_SUCCESS
;;
esac
return $OCF_ERR_GENERIC
}
route_status() {
show_output="$(ip route show $(create_route_spec) 2>/dev/null)"
if [ $? -eq 0 ]; then
if [ -n "$show_output" ]; then
# "ip route show" returned zero, and produced output on
# stdout. That is what we expect.
return $OCF_SUCCESS
else
# "ip route show" returned zero, but produced no
# output on stdout. Assume the route was cleanly
# unconfigured.
return $OCF_NOT_RUNNING
fi
else
# "ip route show" returned an error code. Assume something
# went wrong.
return $OCF_ERR_GENERIC
fi
}
route_validate() {
# If we're running as a clone, are the clone meta attrs OK?
if [ "${OCF_RESKEY_CRM_meta_clone}" ]; then
if [ "${OCF_RESKEY_CRM_meta_clone_node_max}" != 1 ]; then
ocf_log error "Misconfigured clone parameters. Must set meta attribute \"clone_node_max\" to 1, got ${OCF_RESKEY_CRM_meta_clone_node_max}."
return $OCF_ERR_ARGS
fi
fi
# Did we get a destination?
if [ -z "${OCF_RESKEY_destination}" ]; then
ocf_log error "Missing required parameter \"destination\"."
return $OCF_ERR_ARGS
fi
# Did we get either a device or a gateway address?
if [ -z "${OCF_RESKEY_device}" -a -z "${OCF_RESKEY_gateway}" ]; then
ocf_log error "Must specifiy either \"device\", or \"gateway\", or both."
return $OCF_ERR_ARGS
fi
# If a device has been configured, is it available on this system?
if [ -n "${OCF_RESKEY_device}" ]; then
if ! ip link show ${OCF_RESKEY_device} >/dev/null 2>&1; then
ocf_log error "Network device ${OCF_RESKEY_device} appears not to be available on this system."
# OCF_ERR_ARGS prevents the resource from running anywhere at all,
# maybe another node has the interface?
# OCF_ERR_INSTALLED just prevents starting on this particular node.
return $OCF_ERR_INSTALLED
fi
fi
# The following tests must return $OCF_ERR_INSTALLED, but only if
# the resource is actually running (i.e., not during probes)
if ! ocf_is_probe; then
# If a source address has been configured, is it available on
# this system?
if [ -n "${OCF_RESKEY_source}" ]; then
if ! ip address show | grep -w ${OCF_RESKEY_source} >/dev/null 2>&1; then
ocf_log error "Source address ${OCF_RESKEY_source} appears not to be available on this system."
# same reason as with _device:
return $OCF_ERR_INSTALLED
fi
fi
# If a gateway address has been configured, is it reachable?
if [ -n "${OCF_RESKEY_gateway}" ]; then
if ! ip route get ${OCF_RESKEY_gateway} >/dev/null 2>&1; then
ocf_log error "Gateway address ${OCF_RESKEY_gateway} is unreachable."
# same reason as with _device:
return $OCF_ERR_INSTALLED
fi
fi
fi
return $OCF_SUCCESS
}
# These two actions must always succeed
case $__OCF_ACTION in
meta-data) meta_data
# OCF variables are not set when querying meta-data
exit 0
;;
usage|help) route_usage
exit $OCF_SUCCESS
;;
esac
# Don't do anything if the necessary utilities aren't present
for binary in ip grep; do
check_binary $binary
done
route_validate || exit $?
case $__OCF_ACTION in
start) route_start;;
stop) route_stop;;
status|monitor) route_status;;
reload) ocf_log info "Reloading..."
route_start
;;
validate-all) ;;
*) route_usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION returned $rc"
exit $rc
diff --git a/heartbeat/VirtualDomain b/heartbeat/VirtualDomain
index 63de8571b..e6f553976 100755
--- a/heartbeat/VirtualDomain
+++ b/heartbeat/VirtualDomain
@@ -1,436 +1,439 @@
#!/bin/sh
#
# Support: linux-ha@lists.linux-ha.org
# License: GNU General Public License (GPL)
#
# Resource Agent for domains managed by the libvirt API.
# Requires a running libvirt daemon (libvirtd).
#
+# (c) 2008-2010 Florian Haas, Dejan Muhamedagic,
+# and Linux-HA contributors
+#
# usage: $0 {start|stop|status|monitor|migrate_to|migrate_from|meta-data|validate-all}
#
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
LC_ALL="C"
LANG="C"
# Defaults
OCF_RESKEY_force_stop_default=0
OCF_RESKEY_hypervisor_default="$(virsh --quiet uri)"
: ${OCF_RESKEY_force_stop=${OCF_RESKEY_force_stop_default}}
: ${OCF_RESKEY_hypervisor=${OCF_RESKEY_hypervisor_default}}
#######################################################################
usage() {
echo "usage: $0 {start|stop|status|monitor|migrate_to|migrate_from|meta-data|validate-all}"
}
meta_data() {
cat <<EOF
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="VirtualDomain">
<version>1.0</version>
<longdesc lang="en">
Resource agent for a virtual domain (a.k.a. domU, virtual machine,
virtual environment etc., depending on context) managed by libvirtd.
</longdesc>
<shortdesc lang="en">Manages virtual domains through the libvirt virtualization framework</shortdesc>
<parameters>
<parameter name="config" unique="1" required="1">
<longdesc lang="en">
Absolute path to the libvirt configuration file,
for this virtual domain.
</longdesc>
<shortdesc lang="en">Virtual domain configuration file</shortdesc>
<content type="string" default="" />
</parameter>
<parameter name="hypervisor" unique="0" required="0">
<longdesc lang="en">
Hypervisor URI to connect to. See the libvirt documentation for
details on supported URI formats. The default is system dependent.
</longdesc>
<shortdesc lang="en">Hypervisor URI</shortdesc>
<content type="string" default="${OCF_RESKEY_hypervisor_default}"/>
</parameter>
<parameter name="force_stop" unique="0" required="0">
<longdesc lang="en">
Always forcefully shut down ("destroy") the domain on stop. The default
behavior is to resort to a forceful shutdown only after a graceful
shutdown attempt has failed. You should only set this to true if
your virtual domain (or your virtualization backend) does not support
graceful shutdown.
</longdesc>
<shortdesc lang="en">Always force shutdown on stop</shortdesc>
<content type="boolean" default="${OCF_RESKEY_force_stop_default}" />
</parameter>
<parameter name="migration_transport" unique="0" required="0">
<longdesc lang="en">
Transport used to connect to the remote hypervisor while
migrating. Please refer to the libvirt documentation for details on
transports available. If this parameter is omitted, the resource will
use libvirt's default transport to connect to the remote hypervisor.
</longdesc>
<shortdesc lang="en">Remote hypervisor transport</shortdesc>
<content type="string" default="" />
</parameter>
<parameter name="monitor_scripts" unique="0" required="0">
<longdesc lang="en">
To additionally monitor services within the virtual domain, add this
parameter with a list of scripts to monitor.
Note: when monitor scripts are used, the start and migrate_from operations
will complete only when all monitor scripts have completed successfully.
Be sure to set the timeout of these operations to accommodate this delay.
</longdesc>
<shortdesc lang="en">space-separated list of monitor scripts</shortdesc>
<content type="string" default="" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="90" />
<action name="stop" timeout="90" />
<action name="status" depth="0" timeout="30" interval="10" />
<action name="monitor" depth="0" timeout="30" interval="10" />
<action name="migrate_from" timeout="60" />
<action name="migrate_to" timeout="120" />
<action name="meta-data" timeout="5" />
<action name="validate-all" timeout="5" />
</actions>
</resource-agent>
EOF
}
# Set options to be passed to virsh:
VIRSH_OPTIONS="--connect=${OCF_RESKEY_hypervisor} --quiet"
# A state file where we record the domain name:
STATEFILE="${HA_RSCTMP}/VirtualDomain-${OCF_RESOURCE_INSTANCE}.state"
VirtualDomain_Define() {
local virsh_output
local domain_name
# Note: passing in the domain name from outside the script is
# intended for testing and debugging purposes only. Don't do this
# in production, instead let the script figure out the domain name
# from the config file. You have been warned.
if [ -z "$DOMAIN_NAME" ]; then
# Spin until we have a domain name
while true; do
virsh_output=`virsh ${VIRSH_OPTIONS} define ${OCF_RESKEY_config}`
domain_name=`echo "$virsh_output" | sed -e 's/Domain \(.*\) defined from .*$/\1/'`
if [ -n $domain_name ]; then
break;
fi
sleep 1
done
echo "$domain_name" > $STATEFILE
ocf_log info "Domain name \"$domain_name\" saved to $STATEFILE."
else
ocf_log warn "Domain name ${DOMAIN_NAME} already defined, overriding configuration file ${OCF_RESKEY_config}. You should do this for testing only."
fi
}
VirtualDomain_Cleanup_Statefile() {
rm -f $STATEFILE || ocf_log warn "Failed to remove $STATEFILE during $__OCF_ACTION."
}
VirtualDomain_Status() {
rc=$OCF_ERR_GENERIC
status="no state"
while [ "$status" = "no state" ]; do
status="`virsh $VIRSH_OPTIONS domstate $DOMAIN_NAME`"
case "$status" in
"shut off")
# shut off: domain is defined, but not started
ocf_log debug "Virtual domain $DOMAIN_NAME is currently $status."
rc=$OCF_NOT_RUNNING
;;
running|paused|idle|blocked)
# running: domain is currently actively consuming cycles
# paused: domain is paused (suspended)
# idle: domain is running but idle
# blocked: synonym for idle used by legacy Xen versions
ocf_log debug "Virtual domain $DOMAIN_NAME is currently $status."
rc=$OCF_SUCCESS
;;
""|"no state")
# Empty string may be returned when virsh does not
# receive a reply from libvirtd.
# "no state" may occur when the domain is currently
# being migrated (on the migration target only), or
# whenever virsh can't reliably obtain the domain
# state.
status="no state"
if [ "$__OCF_ACTION" = "stop" ]; then
# During the stop operation, we want to bail out
# quickly, so as to be able to force-stop (destroy)
# the domain if necessary.
ocf_log error "Virtual domain $DOMAIN_NAME has no state during stop operation, bailing out."
return $OCF_ERR_GENERIC;
else
# During all other actions, we just wait and try
# again, relying on the CRM/LRM to time us out if
# this takes too long.
ocf_log info "Virtual domain $DOMAIN_NAME currently has no state, retrying."
sleep 1
fi
;;
*)
# any other output is unexpected.
ocf_log error "Virtual domain $DOMAIN_NAME has unknown status \"$status\"!"
;;
esac
done
return $rc
}
VirtualDomain_Start() {
if VirtualDomain_Status; then
ocf_log info "Virtual domain $DOMAIN_NAME already running."
return $OCF_SUCCESS
fi
virsh $VIRSH_OPTIONS start ${DOMAIN_NAME}
rc=$?
if [ $rc -ne 0 ]; then
ocf_log error "Failed to start virtual domain ${DOMAIN_NAME}."
return $OCF_ERR_GENERIC
fi
while ! VirtualDomain_Monitor; do
sleep 1
done
return $OCF_SUCCESS
}
VirtualDomain_Stop() {
local i
local status
local shutdown_timeout
VirtualDomain_Status
status=$?
case $status in
$OCF_SUCCESS)
if ! ocf_is_true $OCF_RESKEY_force_stop; then
# Issue a graceful shutdown request
ocf_log info "Issuing graceful shutdown request for domain ${DOMAIN_NAME}."
virsh $VIRSH_OPTIONS shutdown ${DOMAIN_NAME}
# The "shutdown_timeout" we use here is the operation
# timeout specified in the CIB, minus 5 seconds
shutdown_timeout=$((($OCF_RESKEY_CRM_meta_timeout/1000)-5))
# Loop on status for $shutdown_timeout seconds
for i in `seq $shutdown_timeout`; do
VirtualDomain_Status
status=$?
case $status in
$OCF_NOT_RUNNING)
# This was a graceful shutdown. Clean
# up and return.
VirtualDomain_Cleanup_Statefile
return $OCF_SUCCESS
;;
$OCF_SUCCESS)
# Domain is still running, keep
# waiting (until shutdown_timeout
# expires)
sleep 1
;;
*)
# Something went wrong. Bail out and
# resort to forced stop (destroy).
break;
esac
done
fi
;;
$OCF_NOT_RUNNING)
ocf_log info "Domain $DOMAIN_NAME already stopped."
return $OCF_SUCCESS
esac
# OK. Now if the above graceful shutdown hasn't worked, kill
# off the domain with destroy. If that too does not work, give
# up and have the LRM time us out.
ocf_log info "Issuing forced shutdown (destroy) request for domain ${DOMAIN_NAME}."
virsh $VIRSH_OPTIONS destroy ${DOMAIN_NAME} || return $OCF_ERR_GENERIC
VirtualDomain_Cleanup_Statefile
return $OCF_SUCCESS
}
VirtualDomain_Migrate_To() {
local target_node
local remoteuri
local transport_suffix
target_node="$OCF_RESKEY_CRM_meta_migrate_target"
if VirtualDomain_Status; then
# Find out the remote hypervisor to connect to. That is, turn
# something like "qemu://foo:9999/system" into
# "qemu+tcp://bar:9999/system"
if [ -n "${OCF_RESKEY_migration_transport}" ]; then
transport_suffix="+${OCF_RESKEY_migration_transport}"
fi
# Scared of that sed expression? So am I. :-)
remoteuri=$(echo ${OCF_RESKEY_hypervisor} | sed -e "s,\(.*\)://[^/:]*\(:\?[0-9]*\)/\(.*\),\1${transport_suffix}://${target_node}\2/\3,")
# OK, we know where to connect to. Now do the actual migration.
ocf_log info "$DOMAIN_NAME: Starting live migration to ${target_node} (using remote hypervisor URI ${remoteuri})."
virsh ${VIRSH_OPTIONS} migrate --live $DOMAIN_NAME ${remoteuri}
rc=$?
if [ $rc -ne 0 ]; then
ocf_log err "$DOMAIN_NAME: live migration to ${remoteuri} failed: $rc"
return $OCF_ERR_GENERIC
else
ocf_log info "$DOMAIN_NAME: live migration to ${target_node} succeeded."
VirtualDomain_Cleanup_Statefile
return $OCF_SUCCESS
fi
else
ocf_log err "$DOMAIN_NAME: migrate_to: Not active locally!"
return $OCF_ERR_GENERIC
fi
}
VirtualDomain_Migrate_From() {
while ! VirtualDomain_Monitor; do
sleep 1
done
ocf_log info "$DOMAIN_NAME: live migration from ${OCF_RESKEY_CRM_meta_migrate_source} succeeded."
return $OCF_SUCCESS
}
VirtualDomain_Monitor() {
# First, check the domain status. If that returns anything other
# than $OCF_SUCCESS, something is definitely wrong.
VirtualDomain_Status
rc=$?
if [ ${rc} -eq ${OCF_SUCCESS} ]; then
# OK, the generic status check turned out fine. Now, if we
# have monitor scripts defined, run them one after another.
for script in ${OCF_RESKEY_monitor_scripts}; do
script_output="$($script 2>&1)"
script_rc=$?
if [ ${script_rc} -ne ${OCF_SUCCESS} ]; then
# A monitor script returned a non-success exit
# code. Stop iterating over the list of scripts, log a
# warning message, and propagate $OCF_ERR_GENERIC.
ocf_log warn "Monitor command \"${script}\" for domain ${DOMAIN_NAME} returned ${script_rc} with output: ${script_output}"
rc=$OCF_ERR_GENERIC
break
else
ocf_log debug "Monitor command \"${script}\" for domain ${DOMAIN_NAME} completed successfully with output: ${script_output}"
fi
done
fi
return ${rc}
}
VirtualDomain_Validate_All() {
# Required binaries:
for binary in virsh sed; do
check_binary $binary
done
if [ -z $OCF_RESKEY_config ]; then
ocf_log error "Missing configuration parameter \"config\"."
return $OCF_ERR_CONFIGURED
fi
# check if we can read the config file (otherwise we're unable to
# deduce $DOMAIN_NAME from it, see below)
if [ ! -r $OCF_RESKEY_config ]; then
if ocf_is_probe; then
ocf_log info "Configuration file $OCF_RESKEY_config not readable during probe."
else
ocf_log error "Configuration file $OCF_RESKEY_config does not exist or is not readable."
return $OCF_ERR_INSTALLED
fi
fi
}
if [ $# -ne 1 ]; then
usage
exit $OCF_ERR_ARGS
fi
case $1 in
meta-data) meta_data
exit $OCF_SUCCESS
;;
usage) usage
exit $OCF_SUCCESS
;;
esac
# Everything except usage and meta-data must pass the validate test
VirtualDomain_Validate_All || exit $?
# During a probe, it is permissible for the config file to not be
# readable (it might be on shared storage not available during the
# probe). In that case, VirtualDomain_Define can't work and we're
# unable to get the domain name. Thus, we also can't check whether the
# domain is running. The only thing we can do here is to assume that
# it is not running.
if ocf_is_probe && [ ! -r $OCF_RESKEY_config ]; then
exit $OCF_NOT_RUNNING
fi
# Define the domain on startup, and re-define whenever someone deleted
# the state file, or touched the config.
if [ ! -e $STATEFILE ] || [ $OCF_RESKEY_config -nt $STATEFILE ]; then
VirtualDomain_Define
fi
# By now, we should definitely be able to read from the state file.
# If not, something went wrong.
if [ ! -r $STATEFILE ]; then
ocf_log err "$STATEFILE not found or unreadable. This is unexpected. Cannot determine domain name."
exit $OCF_ERR_GENERIC
fi
# Finally, retrieve the domain name from the state file.
DOMAIN_NAME=`cat $STATEFILE 2>/dev/null`
if [ -z $DOMAIN_NAME ]; then
ocf_log err "$STATEFILE is empty. This is unexpected. Cannot determine domain name."
exit $OCF_ERR_GENERIC
fi
case $1 in
start)
VirtualDomain_Start
;;
stop)
VirtualDomain_Stop
;;
migrate_to)
VirtualDomain_Migrate_To
;;
migrate_from)
VirtualDomain_Migrate_From
;;
status)
VirtualDomain_Status
;;
monitor)
VirtualDomain_Monitor
;;
validate-all)
;;
*)
usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
exit $?
diff --git a/heartbeat/exportfs b/heartbeat/exportfs
index 2b1c2eaf4..6048f09e6 100755
--- a/heartbeat/exportfs
+++ b/heartbeat/exportfs
@@ -1,265 +1,268 @@
#!/bin/sh
# exportfs
#
# Description: Manages nfs exported file system.
-# by btimby@gmail.com
+#
+# (c) 2010 Ben Timby, Florian Haas, Dejan Muhamedagic,
+# and Linux-HA contributors
+#
# License: GNU General Public License v2 (GPLv2) and later
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
exportfs_meta_data() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="exportfs">
<version>1.0</version>
<longdesc lang="en">
Exportfs uses the exportfs command to add/remove nfs exports.
It does NOT manage the nfs server daemon.
It depends on Linux specific NFS implementation details,
so is considered not portable to other platforms yet.
</longdesc>
<shortdesc lang="en">
Manages NFS exports
</shortdesc>
<parameters>
<parameter name="clientspec" unique="0" required="1">
<longdesc lang="en">
The client specification allowing remote machines to mount the directory
over NFS.
</longdesc>
<shortdesc lang="en">
Client ACL.
</shortdesc>
<content type="string" />
</parameter>
<parameter name="options" unique="0" required="0">
<longdesc lang="en">
The options to pass to exportfs for the exported directory.
</longdesc>
<shortdesc lang="en">
Export options.
</shortdesc>
<content type="string" />
</parameter>
<parameter name="directory" unique="0" required="1">
<longdesc lang="en">
The directory which you wish to export using NFS.
</longdesc>
<shortdesc lang="en">
The directory to export.
</shortdesc>
<content type="string" />
</parameter>
<parameter name="fsid" unique="1" required="1">
<longdesc lang="en">
The fsid option to pass to exportfs. This should be a unique positive integer,
avoid 0 unless you understand its special status.
This value will override any fsid provided via the options parameter.
</longdesc>
<shortdesc lang="en">
Unique fsid within cluster.
</shortdesc>
<content type="integer" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="40" />
<action name="stop" timeout="10" />
<action name="monitor" depth="0" timeout="20" interval="10" />
<action name="meta-data" timeout="5" />
<action name="validate-all" timeout="30" />
</actions>
</resource-agent>
END
return $OCF_SUCCESS
}
exportfs_usage() {
cat <<END
usage: $0 {start|stop|monitor|status|validate-all|meta-data}
END
}
backup_rmtab ()
{
grep :${OCF_RESKEY_directory}: /var/lib/nfs/rmtab > ${OCF_RESKEY_directory}/.rmtab
}
clean_rmtab ()
{
REMOVE=`echo ${OCF_RESKEY_directory} | sed 's/\//\\\\\//g'`
sed -i -e /:${REMOVE}:/d /var/lib/nfs/rmtab
}
exportfs_monitor ()
{
fn=`/bin/mktemp`
grep "${OCF_RESKEY_directory}" /var/lib/nfs/etab > $fn 2>&1
rc=$?
#Adapt grep status code to OCF return code
if [ $rc -eq 0 ]; then
ocf_log debug `cat $fn`
rm -f $fn
return $OCF_SUCCESS
elif [ $rc -eq 1 ]; then
rm -f $fn
return $OCF_NOT_RUNNING
else
rm -f $fn
return $OCF_ERR_GENERIC
fi
}
exportfs_start ()
{
ocf_log info "Exporting file system ..."
if [ ${OCF_RESKEY_options} ]; then
OPTIONS="${OCF_RESKEY_options}"
OPTPREFIX=','
fi
if [ `echo ${OPTIONS} | grep fsid` ]; then
#replace fsid provided in options list with one provided in fsid param.
OPTIONS=`echo ${OPTIONS} | sed 's/fsid=[0-9]\+/fsid=${OCF_RESKEY_fsid}/g'`
else
#tack the fsid option onto our options list.
OPTIONS="${OPTIONS}${OPTPREFIX}fsid=${OCF_RESKEY_fsid}"
fi
OPTIONS="-o ${OPTIONS}"
ocf_run exportfs ${OPTIONS} ${OCF_RESKEY_clientspec}:${OCF_RESKEY_directory} || exit $OCF_ERR_GENERIC
RETRIES=0
while [ 1 ]; do
showmount -e | grep -E "^${OCF_RESKEY_directory}[[:space:]]*${OCF_RESKEY_clientspec}$"
rc=$?
if [ $rc -eq 0 ]; then
break
fi
RETRIES=`expr ${RETRIES} + 1`
if [ ${RETRIES} -eq 4 ]; then
ocf_log debug "Export not reported by showmount -e"
ocf_log err "Export not reported by showmount -e"
return ${OCF_NOT_RUNNING}
fi
sleep 1
done
#restore saved rmtab backup from other server:
if [ -f ${OCF_RESKEY_directory}/.rmtab ]; then
cat ${OCF_RESKEY_directory}/.rmtab >> /var/lib/nfs/rmtab
rm -f ${OCF_RESKEY_directory}/.rmtab
fi
#spawn our background process to backup the rmtab each 2 seconds
/bin/sh $0 backup &
ocf_log info "File system exported"
return $OCF_SUCCESS
}
exportfs_stop ()
{
ocf_log info "Un-exporting file system ..."
ocf_run exportfs -u ${OCF_RESKEY_clientspec}:${OCF_RESKEY_directory}
rc=$?
if [ -f ${OCF_RESKEY_directory}/.exportfs_backup.pid ]; then
kill `cat ${OCF_RESKEY_directory}/.exportfs_backup.pid`
rm ${OCF_RESKEY_directory}/.exportfs_backup.pid
fi
backup_rmtab
clean_rmtab
if [ $rc -eq 0 ]; then
ocf_log info "Un-exported file system"
return $OCF_SUCCESS
fi
ocf_log err "Failed to un-export file system"
exit $OCF_ERR_GENERIC
}
exportfs_backup ()
{
echo $$ > ${OCF_RESKEY_directory}/.exportfs_backup.pid
while [ 1 ]; do
backup_rmtab
sleep 2
done
}
exportfs_validate ()
{
# Checks for required parameters
if [ -z $OCF_RESKEY_directory ]; then
ocf_log err "Missing required parameter \"directory\""
exit $OCF_ERR_CONFIGURED
fi
if [ -z $OCF_RESKEY_fsid ]; then
ocf_log err "Missing required parameter \"fsid\""
exit $OCF_ERR_CONFIGURED
fi
if [ -z $OCF_RESKEY_clientspec ]; then
ocf_log err "Missing required parameter \"clientspec\""
exit $OCF_ERR_CONFIGURED
fi
# Checks applicable only to non-probes
if ! ocf_is_probe; then
if [ ! -d $OCF_RESKEY_directory ]; then
ocf_log err "$OCF_RESKEY_directory does not exist or is not a directory"
exit $OCF_ERR_INSTALLED
fi
fi
}
if [ $# -ne 1 ]; then
exportfs_usage
exit $OCF_ERR_ARGS
fi
case $__OCF_ACTION in
meta-data) exportfs_meta_data
exit $OCF_SUCCESS
;;
usage|help) exportfs_usage
exit $OCF_SUCCESS
;;
*)
;;
esac
exportfs_validate
case $__OCF_ACTION in
start) exportfs_start
;;
stop) exportfs_stop
;;
status|monitor) exportfs_monitor
;;
backup) exportfs_backup
;;
validate-all)
# nothing to do -- we're already validated
;;
*) exportfs_usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
diff --git a/heartbeat/iSCSILogicalUnit b/heartbeat/iSCSILogicalUnit
index d0251fe47..5a8224365 100755
--- a/heartbeat/iSCSILogicalUnit
+++ b/heartbeat/iSCSILogicalUnit
@@ -1,488 +1,489 @@
#!/bin/bash
#
#
# iSCSILogicalUnit OCF RA. Exports and manages iSCSI Logical Units.
+#
+# (c) 2009-2010 Florian Haas, Dejan Muhamedagic,
+# and Linux-HA contributors
#
-# Copyright (c) 2009 LINBIT HA-Solutions GmbH, Florian Haas
-# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Further, this software is distributed without any warranty that it is
# free of the rightful claim of any third person regarding infringement
# or the like. Any license provided herein, whether implied or
# otherwise, applies only to this software file. Patent licenses, if
# any, provided herein do not apply to combinations of this program with
# other software, or any other product whatsoever.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
#
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
LC_ALL="C"
LANG="C"
# Defaults
# Set a default implementation based on software installed
if have_binary ietadm; then
OCF_RESKEY_implementation_default="iet"
elif have_binary tgtadm; then
OCF_RESKEY_implementation_default="tgt"
elif have_binary lio_node; then
OCF_RESKEY_implementation_default="lio"
fi
: ${OCF_RESKEY_implementation=${OCF_RESKEY_implementation_default}}
# Use a default SCSI ID and SCSI SN that is unique across the cluster,
# and persistent in the event of resource migration.
# SCSI IDs are limited to 24 bytes, but only 16 bytes are known to be
# supported by all iSCSI implementations this RA cares about. Thus,
# for a default, use the first 16 characters of
# $OCF_RESOURCE_INSTANCE.
OCF_RESKEY_scsi_id_default="${OCF_RESOURCE_INSTANCE:0:16}"
: ${OCF_RESKEY_scsi_id=${OCF_RESKEY_scsi_id_default}}
# To have a reasonably unique default SCSI SN, use the first 8 bytes
# of an MD5 hash of of $OCF_RESOURCE_INSTANCE
sn=`echo -n "${OCF_RESOURCE_INSTANCE}" | openssl md5`
OCF_RESKEY_scsi_sn_default=${sn:0:8}
: ${OCF_RESKEY_scsi_sn=${OCF_RESKEY_scsi_sn_default}}
#######################################################################
meta_data() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="iSCSILogicalUnit" version="0.9">
<version>0.9</version>
<longdesc lang="en">
Manages iSCSI Logical Unit. An iSCSI Logical unit is a subdivision of
an SCSI Target, exported via a daemon that speaks the iSCSI protocol.
</longdesc>
<shortdesc lang="en">Manages iSCSI Logical Units (LUs)</shortdesc>
<parameters>
<parameter name="implementation" required="0" unique="0">
<longdesc lang="en">
The iSCSI target daemon implementation. Must be one of "iet", "tgt",
or "lio". If unspecified, an implementation is selected based on the
availability of management utilities, with "iet" being tried first,
then "tgt", then "lio".
</longdesc>
<shortdesc lang="en">iSCSI target daemon implementation</shortdesc>
<content type="string" default="${OCF_RESKEY_implementation_default}"/>
</parameter>
<parameter name="target_iqn" required="1" unique="0">
<longdesc lang="en">
The iSCSI Qualified Name (IQN) that this Logical Unit belongs to.
</longdesc>
<shortdesc lang="en">iSCSI target IQN</shortdesc>
<content type="string" />
</parameter>
<parameter name="lun" required="1" unique="0">
<longdesc lang="en">
The Logical Unit number (LUN) exposed to initiators.
</longdesc>
<shortdesc lang="en">Logical Unit number (LUN)</shortdesc>
<content type="integer" />
</parameter>
<parameter name="path" required="1" unique="0">
<longdesc lang="en">
The path to the block device exposed. Some implementations allow this
to be a regular file, too.
</longdesc>
<shortdesc lang="en">Block device (or file) path</shortdesc>
<content type="string" />
</parameter>
<parameter name="scsi_id" required="0" unique="1">
<longdesc lang="en">
The SCSI ID to be configured for this Logical Unit. The default
is the resource name, truncated to 24 bytes.
</longdesc>
<shortdesc lang="en">SCSI ID</shortdesc>
<content type="string" default="${OCF_RESKEY_scsi_id_default}"/>
</parameter>
<parameter name="scsi_sn" required="0" unique="1">
<longdesc lang="en">
The SCSI serial number to be configured for this Logical Unit.
The default is a hash of the resource name, truncated to 8 bytes.
</longdesc>
<shortdesc lang="en">SCSI serial number</shortdesc>
<content type="string" default="${OCF_RESKEY_scsi_sn_default}"/>
</parameter>
<parameter name="vendor_id" required="0" unique="0">
<longdesc lang="en">
The SCSI vendor ID to be configured for this Logical Unit.
</longdesc>
<shortdesc lang="en">SCSI vendor ID</shortdesc>
<content type="string" />
</parameter>
<parameter name="product_id" required="0" unique="0">
<longdesc lang="en">
The SCSI product ID to be configured for this Logical Unit.
</longdesc>
<shortdesc lang="en">SCSI product ID</shortdesc>
<content type="string" />
</parameter>
<parameter name="additional_parameters" required="0" unique="0">
<longdesc lang="en">
Additional LU parameters. A space-separated list of "name=value" pairs
which will be passed through to the iSCSI daemon's management
interface. The supported parameters are implementation
dependent. Neither the name nor the value may contain whitespace.
</longdesc>
<shortdesc lang="en">List of iSCSI LU parameters</shortdesc>
<content type="string" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="10" />
<action name="stop" timeout="10" />
<action name="monitor" timeout="10" interval="10" depth="0" />
<action name="meta-data" timeout="5" />
<action name="validate-all" timeout="10" />
</actions>
</resource-agent>
END
}
#######################################################################
do_cmd() {
# Wrap local commands to capture their exit code and output. Some
# implementations (IET, notably) have management commands with
# very terse output. It helps to at least capture exit codes in
# the logs.
local cmd="$*"
ocf_log debug "Calling $cmd"
local cmd_out
cmd_out=$($cmd 2>&1)
ret=$?
if [ $ret -ne 0 ]; then
ocf_log err "Called \"$cmd\""
ocf_log err "Exit code $ret"
ocf_log err "Command output: \"$cmd_out\""
else
ocf_log debug "Exit code $ret"
ocf_log debug "Command output: \"$cmd_out\""
fi
echo $cmd_out
return $ret
}
iSCSILogicalUnit_usage() {
cat <<END
usage: $0 {start|stop|monitor|validate-all|meta-data}
Expects to have a fully populated OCF RA-compliant environment set.
END
}
iSCSILogicalUnit_start() {
iSCSILogicalUnit_monitor
if [ $? = $OCF_SUCCESS ]; then
return $OCF_SUCCESS
fi
local params
case $OCF_RESKEY_implementation in
iet)
params="Path=${OCF_RESKEY_path}"
# use blockio if path points to a block device, fileio
# otherwise.
if [ -b "${OCF_RESKEY_path}" ]; then
params="${params} Type=blockio"
else
params="${params} Type=fileio"
fi
# in IET, we have to set LU parameters on creation
if [ -n "${OCF_RESKEY_scsi_id}" ]; then
params="${params} ScsiId=${OCF_RESKEY_scsi_id}"
fi
if [ -n "${OCF_RESKEY_scsi_sn}" ]; then
params="${params} ScsiSN=${OCF_RESKEY_scsi_sn}"
fi
params="${params} ${OCF_RESKEY_additional_parameters}"
do_cmd ietadm --op new \
--tid=${TID} \
--lun=${OCF_RESKEY_lun} \
--params ${params// /,} && return $OCF_SUCCESS
;;
tgt)
# tgt requires that we create the LU first, then set LU
# parameters
params=""
local var
local envar
for var in scsi_id scsi_sn vendor_id product_id; do
envar="OCF_RESKEY_${var}"
if [ -n "${!envar}" ]; then
params="${params} ${var}=${!envar}"
fi
done
params="${params} ${OCF_RESKEY_additional_parameters}"
do_cmd tgtadm --lld iscsi --op new --mode logicalunit \
--tid=${TID} \
--lun=${OCF_RESKEY_lun} \
--backing-store ${OCF_RESKEY_path} || return $OCF_ERR_GENERIC
if [ -z "$params" ]; then
return $OCF_SUCCESS
else
do_cmd tgtadm --lld iscsi --op update --mode logicalunit \
--tid=${TID} \
--lun=${OCF_RESKEY_lun} \
--params ${params// /,} && return $OCF_SUCCESS
fi
;;
lio)
# For lio, we first have to create a target device, then
# add it to the Target Portal Group as an LU.
do_cmd tcm_node --createdev=iblock_0/${OCF_RESOURCE_INSTANCE} \
${OCF_RESKEY_path} || return $OCF_ERR_GENERIC
if [ -n "${OCF_RESKEY_scsi_sn}" ]; then
do_cmd tcm_node --setunitserial=iblock_0/${OCF_RESOURCE_INSTANCE} \
${OCF_RESKEY_scsi_sn} || return $OCF_ERR_GENERIC
fi
do_cmd lio_node --addlun=${OCF_RESKEY_target_iqn} 1 ${OCF_RESKEY_lun} \
${OCF_RESOURCE_INSTANCE} iblock_0/${OCF_RESOURCE_INSTANCE} || return $OCF_ERR_GENERIC
return $OCF_SUCCESS
;;
esac
return $OCF_ERR_GENERIC
}
iSCSILogicalUnit_stop() {
iSCSILogicalUnit_monitor
if [ $? = $OCF_SUCCESS ]; then
case $OCF_RESKEY_implementation in
iet)
# IET allows us to remove LUs while they are in use
do_cmd ietadm --op delete \
--tid=${TID} \
--lun=${OCF_RESKEY_lun} && return $OCF_SUCCESS
;;
tgt)
# tgt will fail to remove an LU while it is in use,
# but at the same time does not allow us to
# selectively shut down a connection that is using a
# specific LU. Thus, we need to loop here until tgtd
# decides that the LU is no longer in use, or we get
# timed out by the LRM.
while ! do_cmd tgtadm --lld iscsi --op delete --mode logicalunit \
--tid ${TID} \
--lun=${OCF_RESKEY_lun}; do
sleep 1
done
return $OCF_SUCCESS
;;
lio)
do_cmd lio_node --dellun=${OCF_RESKEY_target_iqn} 1 ${OCF_RESKEY_lun} || return $OCF_ERR_GENERIC
do_cmd tcm_node --freedev=iblock_0/${OCF_RESOURCE_INSTANCE} && return $OCF_SUCCESS
esac
else
return $OCF_SUCCESS
fi
return $OCF_ERR_GENERIC
}
iSCSILogicalUnit_monitor() {
case $OCF_RESKEY_implementation in
iet)
# Figure out and set the target ID
TID=`sed -ne "s/tid:\([[:digit:]]\+\) name:${OCF_RESKEY_target_iqn}/\1/p" < /proc/net/iet/volume`
if [ -z "${TID}" ]; then
# Our target is not configured, thus we're not
# running.
return $OCF_NOT_RUNNING
fi
# FIXME: this looks for a matching LUN and path, but does
# not actually test for the correct target ID.
grep -E -q "[[:space:]]+lun:${OCF_RESKEY_lun}.*path:${OCF_RESKEY_path}" /proc/net/iet/volume && return $OCF_SUCCESS
;;
tgt)
# Figure out and set the target ID
TID=`tgtadm --lld iscsi --op show --mode target \
| sed -ne "s/^Target \([[:digit:]]\+\): ${OCF_RESKEY_target_iqn}/\1/p"`
if [ -z "$TID" ]; then
# Our target is not configured, thus we're not
# running.
return $OCF_NOT_RUNNING
fi
# This only looks for the backing store, but does not test
# for the correct target ID and LUN.
tgtadm --lld iscsi --op show --mode target \
| grep -E -q "[[:space:]]+Backing store.*: ${OCF_RESKEY_path}" && return $OCF_SUCCESS
;;
lio)
configfs_path="/sys/kernel/config/target/iscsi/${OCF_RESKEY_target_iqn}/tpgt_1/lun/lun_${OCF_RESKEY_lun}/${OCF_RESOURCE_INSTANCE}/udev_path"
[ -e ${configfs_path} ] && [ `cat ${configfs_path}` = "${OCF_RESKEY_path}" ] && return $OCF_SUCCESS
;;
esac
return $OCF_NOT_RUNNING
}
iSCSILogicalUnit_validate() {
# Do we have all required variables?
for var in implementation target_iqn lun path; do
param="OCF_RESKEY_${var}"
if [ -z "${!param}" ]; then
ocf_log error "Missing resource parameter \"$var\"!"
return $OCF_ERR_CONFIGURED
fi
done
# Do we have all required binaries?
case $OCF_RESKEY_implementation in
iet)
check_binary ietadm
;;
tgt)
check_binary tgtadm
;;
lio)
check_binary tcm_node
check_binary lio_node
;;
*)
# and by the way, is the implementation supported?
ocf_log error "Unsupported iSCSI target implementation \"$OCF_RESKEY_implementation\"!"
return $OCF_ERR_CONFIGURED
esac
# Do we have a valid LUN?
case $OCF_RESKEY_implementation in
iet)
# IET allows LUN 0 and up
[ $OCF_RESKEY_lun -ge 0 ]
case $? in
0)
# OK
;;
1)
ocf_log err "Invalid LUN $OCF_RESKEY_lun (must be a non-negative integer)."
return $OCF_ERR_CONFIGURED
;;
*)
ocf_log err "Invalid LUN $OCF_RESKEY_lun (must be an integer)."
return $OCF_ERR_CONFIGURED
;;
esac
;;
tgt)
# tgt reserves LUN 0 for its own purposes
[ $OCF_RESKEY_lun -ge 1 ]
case $? in
0)
# OK
;;
1)
ocf_log err "Invalid LUN $OCF_RESKEY_lun (must be greater than 0)."
return $OCF_ERR_CONFIGURED
;;
*)
ocf_log err "Invalid LUN $OCF_RESKEY_lun (must be an integer)."
return $OCF_ERR_CONFIGURED
;;
esac
;;
esac
# Is the required kernel functionality available?
case $OCF_RESKEY_implementation in
iet)
[ -d /proc/net/iet ]
if [ $? -ne 0 ]; then
ocf_log err "/proc/net/iet does not exist or is not a directory -- check if required modules are loaded."
return $OCF_ERR_INSTALLED
fi
;;
tgt)
# tgt is userland only
;;
esac
# Do we have any configuration parameters that the current
# implementation does not support?
local unsupported_params
local var
local envar
case $OCF_RESKEY_implementation in
iet)
# IET does not support setting the vendor and product ID
# (it always uses "IET" and "VIRTUAL-DISK")
unsupported_params="vendor_id product_id"
;;
tgt)
;;
lio)
unsupported_params="scsi_id vendor_id product_id"
;;
esac
for var in ${unsupported_params}; do
envar=OCF_RESKEY_${var}
if [ -n "${!envar}" ]; then
ocf_log warn "Configuration parameter \"${var}\"" \
"is not supported by the iSCSI implementation" \
"and will be ignored."
fi
done
return $OCF_SUCCESS
}
case $1 in
meta-data)
meta_data
exit $OCF_SUCCESS
;;
usage|help)
iSCSILogicalUnit_usage
exit $OCF_SUCCESS
;;
esac
# Everything except usage and meta-data must pass the validate test
iSCSILogicalUnit_validate || exit $?
case $__OCF_ACTION in
start) iSCSILogicalUnit_start;;
stop) iSCSILogicalUnit_stop;;
monitor) iSCSILogicalUnit_monitor;;
reload) ocf_log err "Reloading..."
iSCSILogicalUnit_start
;;
validate-all) ;;
*) iSCSILogicalUnit_usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
exit $rc
diff --git a/heartbeat/iSCSITarget b/heartbeat/iSCSITarget
index 556f04dad..90802df73 100755
--- a/heartbeat/iSCSITarget
+++ b/heartbeat/iSCSITarget
@@ -1,594 +1,594 @@
#!/bin/bash
#
#
# iSCSITarget OCF RA. Exports and manages iSCSI targets.
#
-# Copyright (c) 2009 LINBIT HA-Solutions GmbH, Florian Haas
-# All Rights Reserved.
+# (c) 2009-2010 Florian Haas, Dejan Muhamedagic,
+# and Linux-HA contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Further, this software is distributed without any warranty that it is
# free of the rightful claim of any third person regarding infringement
# or the like. Any license provided herein, whether implied or
# otherwise, applies only to this software file. Patent licenses, if
# any, provided herein do not apply to combinations of this program with
# other software, or any other product whatsoever.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
#
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
LC_ALL="C"
LANG="C"
# Defaults
# Set a default implementation based on software installed
if have_binary ietadm; then
OCF_RESKEY_implementation_default="iet"
elif have_binary tgtadm; then
OCF_RESKEY_implementation_default="tgt"
elif have_binary lio_node; then
OCF_RESKEY_implementation_default="lio"
fi
: ${OCF_RESKEY_implementation=${OCF_RESKEY_implementation_default}}
# Listen on 0.0.0.0:3260 by default
OCF_RESKEY_portals_default="0.0.0.0:3260"
: ${OCF_RESKEY_portals=${OCF_RESKEY_portals_default}}
#######################################################################
meta_data() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="iSCSITarget" version="0.9">
<version>0.9</version>
<longdesc lang="en">
Manages iSCSI targets. An iSCSI target is a collection of SCSI Logical
Units (LUs) exported via a daemon that speaks the iSCSI protocol.
</longdesc>
<shortdesc lang="en">iSCSI target export agent</shortdesc>
<parameters>
<parameter name="implementation" required="0" unique="0">
<longdesc lang="en">
The iSCSI target daemon implementation. Must be one of "iet", "tgt",
or "lio". If unspecified, an implementation is selected based on the
availability of management utilities, with "iet" being tried first,
then "tgt", then "lio".
</longdesc>
<shortdesc lang="en">Manages an iSCSI target export</shortdesc>
<content type="string" default="${OCF_RESKEY_implementation_default}"/>
</parameter>
<parameter name="iqn" required="1" unique="1">
<longdesc lang="en">
The target iSCSI Qualified Name (IQN). Should follow the conventional
"iqn.yyyy-mm.<reversed domain name>[:identifier]" syntax.
</longdesc>
<shortdesc lang="en">iSCSI target IQN</shortdesc>
<content type="string" />
</parameter>
<parameter name="tid" required="0" unique="1">
<longdesc lang="en">
The iSCSI target ID. Required for tgt.
</longdesc>
<shortdesc lang="en">iSCSI target ID</shortdesc>
<content type="integer" />
</parameter>
<parameter name="portals" required="0" unique="0">
<longdesc lang="en">
iSCSI network portal addresses. Not supported by all
implementations. If unset, the default is to create one portal that
listens on ${OCF_RESKEY_portal_default}.
</longdesc>
<shortdesc lang="en">iSCSI portal addresses</shortdesc>
<content type="string" default="${OCF_RESKEY_portals_default}"/>
</parameter>
<parameter name="allowed_initiators" required="0" unique="0">
<longdesc lang="en">
Allowed initiators. A space-separated list of initiators allowed to
connect to this target. Initiators may be listed in any syntax
the target implementation allows. If this parameter is empty or
not set, access to this target will be allowed from any initiator.
</longdesc>
<shortdesc lang="en">List of iSCSI initiators allowed to connect
to this target</shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="incoming_username" required="0" unique="1">
<longdesc lang="en">
A username used for incoming initiator authentication. If unspecified,
allowed initiators will be able to log in without authentication.
</longdesc>
<shortdesc lang="en">Incoming account username</shortdesc>
<content type="string"/>
</parameter>
<parameter name="incoming_password" required="0" unique="0">
<longdesc lang="en">
A password used for incoming initiator authentication.
</longdesc>
<shortdesc lang="en">Incoming account password</shortdesc>
<content type="string"/>
</parameter>
<parameter name="additional_parameters" required="0" unique="0">
<longdesc lang="en">
Additional target parameters. A space-separated list of "name=value"
pairs which will be passed through to the iSCSI daemon's management
interface. The supported parameters are implementation
dependent. Neither the name nor the value may contain whitespace.
</longdesc>
<shortdesc lang="en">List of iSCSI target parameters</shortdesc>
<content type="string" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="10" />
<action name="stop" timeout="10" />
<action name="monitor" timeout="10" interval="10" depth="0" />
<action name="meta-data" timeout="5" />
<action name="validate-all" timeout="10" />
</actions>
</resource-agent>
END
}
#######################################################################
do_cmd() {
# Wrap local commands to capture their exit code and output. Some
# implementations (IET, notably) have management commands with
# very terse output. It helps to at least capture exit codes in
# the logs.
local cmd="$*"
ocf_log debug "Calling $cmd"
local cmd_out
cmd_out=$($cmd 2>&1)
ret=$?
if [ $ret -ne 0 ]; then
ocf_log err "Called \"$cmd\""
ocf_log err "Exit code $ret"
ocf_log err "Command output: \"$cmd_out\""
else
ocf_log debug "Exit code $ret"
ocf_log debug "Command output: \"$cmd_out\""
fi
echo $cmd_out
return $ret
}
iSCSITarget_usage() {
cat <<END
usage: $0 {start|stop|monitor|validate-all|meta-data}
Expects to have a fully populated OCF RA-compliant environment set.
END
}
iSCSITarget_start() {
iSCSITarget_monitor
if [ $? = $OCF_SUCCESS ]; then
return $OCF_SUCCESS
fi
local param
local name
local value
local initiator
local portal
case $OCF_RESKEY_implementation in
iet)
local lasttid
local tid
if [ "${OCF_RESKEY_tid}" ]; then
tid="${OCF_RESKEY_tid}"
else
# Figure out the last used target ID, add 1 to get the new
# target ID.
lasttid=`sed -ne "s/tid:\([[:digit:]]\+\) name:.*/\1/p" < /proc/net/iet/volume | sort -n | tail -n1`
[ -z "${lasttid}" ] && lasttid=0
tid=$((++lasttid))
fi
# Create the target.
do_cmd ietadm --op new \
--tid=${tid} \
--params Name=${OCF_RESKEY_iqn} || return $OCF_ERR_GENERIC
# Set additional parameters.
for param in ${OCF_RESKEY_additional_parameters}; do
name=${param%=*}
value=${param#*=}
do_cmd ietadm --op update \
--tid=${tid} \
--params ${name}=${value} || return $OCF_ERR_GENERIC
done
# Legacy versions of IET allow targets by default, current
# versions deny. To be safe we manage both the .allow and
# .deny files.
if [ -n "${OCF_RESKEY_allowed_initiators}" ]; then
echo "${OCF_RESKEY_iqn} ALL" >> /etc/initiators.deny
echo "${OCF_RESKEY_iqn} ${OCF_RESKEY_allowed_initiators// /,}" >> /etc/initiators.allow
else
echo "${OCF_RESKEY_iqn} ALL" >> /etc/initiators.allow
fi
# In iet, adding a new user and assigning it to a target
# is one operation.
if [ -n "${OCF_RESKEY_incoming_username}" ]; then
do_cmd ietadm --op new --user \
--tid=${tid} \
--params=IncomingUser=${OCF_RESKEY_incoming_username},Password=${OCF_RESKEY_incoming_password} \
|| return $OCF_ERR_GENERIC
fi
return $OCF_SUCCESS
;;
tgt)
local tid
tid="${OCF_RESKEY_tid}"
# Create the target.
do_cmd tgtadm --lld iscsi --op new --mode target \
--tid=${tid} \
--targetname ${OCF_RESKEY_iqn} || return $OCF_ERR_GENERIC
# Set parameters.
for param in ${OCF_RESKEY_additional_parameters}; do
name=${param%=*}
value=${param#*=}
do_cmd tgtadm --lld iscsi --op update --mode target \
--tid=${tid} \
--name=${name} --value=${value} || return $OCF_ERR_GENERIC
done
# For tgt, we always have to add access per initiator;
# access to targets is denied by default. If
# "allowed_initiators" is unset, we must use the special
# keyword ALL.
for initiator in ${OCF_RESKEY_allowed_initiators=ALL}; do
do_cmd tgtadm --lld iscsi --op bind --mode target \
--tid=${tid} \
--initiator-address=${initiator} || return $OCF_ERR_GENERIC
done
# In tgt, we must first create a user account, then assign
# it to a target using the "bind" operation.
if [ -n "${OCF_RESKEY_incoming_username}" ]; then
do_cmd tgtadm --lld iscsi --mode account --op new \
--user=${OCF_RESKEY_incoming_username} \
--password=${OCF_RESKEY_incoming_password} || return $OCF_ERR_GENERIC
do_cmd tgtadm --lld iscsi --mode account --op bind \
--tid=${tid} \
--user=${OCF_RESKEY_incoming_username} || return $OCF_ERR_GENERIC
fi
return $OCF_SUCCESS
;;
lio)
# lio distinguishes between targets and target portal
# groups (TPGs). We will always create one TPG, with the
# number 1. In lio, creating a network portal
# automatically creates the corresponding target if it
# doesn't already exist.
for portal in ${OCF_RESKEY_portals}; do
do_cmd lio_node --addnp ${OCF_RESKEY_iqn} 1 \
${portal} || return $OCF_ERR_GENERIC
done
# in lio, we can set target parameters by manipulating
# the appropriate configfs entries
for param in ${OCF_RESKEY_additional_parameters}; do
name=${param%=*}
value=${param#*=}
configfs_path="/sys/kernel/config/target/iscsi/${OCF_RESKEY_iqn}/tpgt_1/param/${name}"
if [ -e ${configfs_path} ]; then
echo ${value} > ${configfs_path} || return $OCF_ERR_GENERIC
else
ocf_log warn "Unsupported iSCSI target parameter ${name}: will be ignored."
fi
done
# lio does per-initiator filtering by default. To disable
# this, we need to switch the target to "permissive mode".
if [ -n "${OCF_RESKEY_allowed_initiators}" ]; then
for initiator in ${OCF_RESKEY_allowed_initiators}; do
do_cmd lio_node --addnodeacl ${OCF_RESKEY_iqn} 1 \
${initiator} || return $OCF_ERR_GENERIC
done
else
do_cmd lio_node --permissive ${OCF_RESKEY_iqn} 1 || return $OCF_ERR_GENERIC
# permissive mode enables read-only access by default,
# so we need to change that to RW to be in line with
# the other implementations.
echo 0 > "/sys/kernel/config/target/iscsi/${OCF_RESKEY_iqn}/tpgt_1/attrib/demo_mode_write_protect"
if [ `cat /sys/kernel/config/target/iscsi/${OCF_RESKEY_iqn}/tpgt_1/attrib/demo_mode_write_protect` -ne 0 ]; then
ocf_log err "Failed to disable write protection for target ${OCF_RESKEY_iqn}."
return $OCF_ERR_GENERIC
fi
fi
# TODO: add CHAP authentication support when it gets added
# back into LIO
do_cmd lio_node --disableauth ${OCF_RESKEY_iqn} 1 || return $OCF_ERR_GENERIC
# Finally, we need to enable the target to allow
# initiators to connect
do_cmd lio_node --enabletpg=${OCF_RESKEY_iqn} 1 || return $OCF_ERR_GENERIC
return $OCF_SUCCESS
;;
esac
return $OCF_ERR_GENERIC
}
iSCSITarget_stop() {
iSCSITarget_monitor
if [ $? = $OCF_SUCCESS ]; then
local tid
case $OCF_RESKEY_implementation in
iet)
# Figure out the target ID
tid=`sed -ne "s/tid:\([[:digit:]]\+\) name:${OCF_RESKEY_iqn}/\1/p" < /proc/net/iet/volume`
if [ -z "${tid}" ]; then
ocf_log err "Failed to retrieve target ID for IQN ${OCF_RESKEY_iqn}"
return $OCF_ERR_GENERIC
fi
# Close existing connections. There is no other way to
# do this in IET than to parse the contents of
# /proc/net/iet/session.
set -- $(sed -ne '/^tid:'${tid}' /,/^tid/ {
/^[[:space:]]*sid:\([0-9]\+\)/ {
s/^[[:space:]]*sid:\([0-9]*\).*/--sid=\1/; h;
};
/^[[:space:]]*cid:\([0-9]\+\)/ {
s/^[[:space:]]*cid:\([0-9]*\).*/--cid=\1/; G; p;
};
}' < /proc/net/iet/session)
while [[ -n $2 ]]; do
# $2 $1 looks like "--sid=X --cid=Y"
do_cmd ietadm --op delete \
--tid=${tid} $2 $1
shift 2
done
# In iet, unassigning a user from a target and
# deleting the user account is one operation.
if [ -n "${OCF_RESKEY_incoming_username}" ]; then
do_cmd ietadm --op delete --user \
--tid=${tid} \
--params=IncomingUser=${OCF_RESKEY_incoming_username} \
|| return $OCF_ERR_GENERIC
fi
# Loop on delete. Keep trying until we time out, if
# necessary.
while true; do
if ietadm --op delete --tid=${tid}; then
ocf_log debug "Removed target ${OCF_RESKEY_iqn}."
break
else
ocf_log warn "Failed to remove target ${OCF_RESKEY_iqn}, retrying."
sleep 1
fi
done
# Avoid stale /etc/initiators.{allow,deny} entries
# for this target
do_cmd sed -e "/^${OCF_RESKEY_iqn}[[:space:]]/d" \
-i /etc/initiators.deny
do_cmd sed -e "/^${OCF_RESKEY_iqn}[[:space:]]/d" \
-i /etc/initiators.allow
return $OCF_SUCCESS
;;
tgt)
tid="${OCF_RESKEY_tid}"
# Close existing connections. There is no other way to
# do this in tgt than to parse the output of "tgtadm --op
# show".
set -- $(tgtadm --lld iscsi --op show --mode target \
| sed -ne '/^Target '${tid}':/,/^Target/ {
/^[[:space:]]*I_T nexus: \([0-9]\+\)/ {
s/^.*: \([0-9]*\).*/--sid=\1/; h;
};
/^[[:space:]]*Connection: \([0-9]\+\)/ {
s/^.*: \([0-9]*\).*/--cid=\1/; G; p;
};
/^[[:space:]]*LUN information:/ q;
}')
while [[ -n $2 ]]; do
# $2 $1 looks like "--sid=X --cid=Y"
do_cmd tgtadm --lld iscsi --op delete --mode connection \
--tid=${tid} $2 $1
shift 2
done
# In tgt, we must first unbind the user account from
# the target, then remove the account itself.
if [ -n "${OCF_RESKEY_incoming_username}" ]; then
do_cmd tgtadm --lld iscsi --mode account --op unbind \
--tid=${tid} \
--user=${OCF_RESKEY_incoming_username} || return $OCF_ERR_GENERIC
do_cmd tgtadm --lld iscsi --mode account --op delete \
--user=${OCF_RESKEY_incoming_username} || return $OCF_ERR_GENERIC
fi
# Loop on delete. Keep trying until we time out, if
# necessary.
while true; do
if tgtadm --lld iscsi --op delete --mode target --tid=${tid}; then
ocf_log debug "Removed target ${OCF_RESKEY_iqn}."
break
else
ocf_log warn "Failed to remove target ${OCF_RESKEY_iqn}, retrying."
sleep 1
fi
done
# In tgt, we don't have to worry about our ACL
# entries. They are automatically removed upon target
# deletion.
return $OCF_SUCCESS
;;
lio)
# In lio, removing a target automatically removes all
# associated TPGs, network portals, and LUNs.
do_cmd lio_node --deliqn ${OCF_RESKEY_iqn} || return $OCF_ERR_GENERIC
return $OCF_SUCCESS
;;
esac
else
return $OCF_SUCCESS
fi
return $OCF_ERR_GENERIC
}
iSCSITarget_monitor() {
case $OCF_RESKEY_implementation in
iet)
grep -Eq "tid:[0-9]+ name:${OCF_RESKEY_iqn}" /proc/net/iet/volume && return $OCF_SUCCESS
;;
tgt)
tgtadm --lld iscsi --op show --mode target \
| grep -Eq "Target [0-9]+: ${OCF_RESKEY_iqn}" && return $OCF_SUCCESS
;;
lio)
# if we have no configfs entry for the target, it's
# definitely stopped
[ -d /sys/kernel/config/target/iscsi/${OCF_RESKEY_iqn} ] || return $OCF_NOT_RUNNING
# if the target is there, but its TPG is not enabled, then
# we also consider it stopped
[ `cat /sys/kernel/config/target/iscsi/${OCF_RESKEY_iqn}/tpgt_1/enable` -eq 1 ] || return $OCF_NOT_RUNNING
return $OCF_SUCCESS
;;
esac
return $OCF_NOT_RUNNING
}
iSCSITarget_validate() {
# Do we have all required variables?
local required_vars
case $OCF_RESKEY_implementation in
iet)
required_vars="implementation iqn"
;;
tgt)
required_vars="implementation iqn tid"
;;
esac
for var in ${required_vars}; do
param="OCF_RESKEY_${var}"
if [ -z "${!param}" ]; then
ocf_log error "Missing resource parameter \"$var\"!"
return $OCF_ERR_CONFIGURED
fi
done
# Do we have all required binaries?
case $OCF_RESKEY_implementation in
iet)
check_binary ietadm
;;
tgt)
check_binary tgtadm
;;
lio)
check_binary tcm_node
check_binary lio_node
;;
*)
# and by the way, is the implementation supported?
ocf_log error "Unsupported iSCSI target implementation \"$OCF_RESKEY_implementation\"!"
return $OCF_ERR_CONFIGURED
esac
# Is the required kernel functionality available?
case $OCF_RESKEY_implementation in
iet)
[ -d /proc/net/iet ]
if [ $? -ne 0 ]; then
ocf_log err "/proc/net/iet does not exist or is not a directory -- check if required modules are loaded."
return $OCF_ERR_INSTALLED
fi
;;
tgt)
# tgt is userland only
;;
lio)
# lio needs configfs to be mounted
if ! grep -Eq "^.*/sys/kernel/config[[:space:]]+configfs" /proc/mounts; then
ocf_log err "configfs not mounted at /sys/kernel/config -- check if required modules are loaded."
return $OCF_ERR_INSTALLED
fi
# check for configfs entries created by target_core_mod
if [ ! -d /sys/kernel/config/target ]; then
ocf_log err "/sys/kernel/config/target does not exist or is not a directory -- check if required modules are loaded."
fi
;;
esac
# Do we have any configuration parameters that the current
# implementation does not support?
local unsupported_params
local var
local envar
case $OCF_RESKEY_implementation in
iet|tgt)
# IET and tgt do not support binding a target portal to a
# specific IP address.
unsupported_params="portals"
;;
lio)
# TODO: Remove incoming_username and incoming_password
# from this check when LIO 3.0 gets CHAP authentication
unsupported_params="tid incoming_username incoming_password"
;;
esac
for var in ${unsupported_params}; do
envar=OCF_RESKEY_${var}
if [ -n "${!envar}" ]; then
ocf_log warn "Configuration parameter \"${var}\"" \
"is not supported by the iSCSI implementation" \
"and will be ignored."
fi
done
return $OCF_SUCCESS
}
case $1 in
meta-data)
meta_data
exit $OCF_SUCCESS
;;
usage|help)
iSCSITarget_usage
exit $OCF_SUCCESS
;;
esac
# Everything except usage and meta-data must pass the validate test
iSCSITarget_validate || exit $?
case $__OCF_ACTION in
start) iSCSITarget_start;;
stop) iSCSITarget_stop;;
monitor) iSCSITarget_monitor;;
reload) ocf_log err "Reloading..."
iSCSITarget_start
;;
validate-all) ;;
*) iSCSITarget_usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
exit $rc
diff --git a/heartbeat/mysql b/heartbeat/mysql
index fc6ae381a..f012e3f6a 100755
--- a/heartbeat/mysql
+++ b/heartbeat/mysql
@@ -1,881 +1,883 @@
#!/bin/sh
#
#
# MySQL
#
# Description: Manages a MySQL database as Linux-HA resource
-
#
-# Author: Alan Robertson : DB2 Script
-# Author: Jakub Janczak : Rewrite as MySQL
-# Author: Andrew Beekhof : Cleanup and import
-# Author: Sebastian Reitenbach : add OpenBSD defaults, more cleanup
-# Author: Narayan Newton : Add Gentoo/Debian defaults
+# Authors: Alan Robertson: DB2 Script
+# Jakub Janczak: rewrite as MySQL
+# Andrew Beekhof: cleanup and import
+# Sebastian Reitenbach: add OpenBSD defaults, more cleanup
+# Narayan Newton: add Gentoo/Debian defaults
+# Marian Marinov, Florian Haas: add replication capability
#
# Support: linux-ha@lists.linux-ha.org
# License: GNU General Public License (GPL)
-# Copyright: (C) 2002 - 2005 International Business Machines, Inc.
+#
+# (c) 2002-2005 International Business Machines, Inc.
+# 2005-2010 Linux-HA contributors
#
# An example usage in /etc/ha.d/haresources:
# node1 10.0.0.170 mysql
#
# See usage() function below for more details...
#
# OCF instance parameters:
# OCF_RESKEY_binary
# OCF_RESKEY_client_binary
# OCF_RESKEY_config
# OCF_RESKEY_datadir
# OCF_RESKEY_user
# OCF_RESKEY_group
# OCF_RESKEY_test_table
# OCF_RESKEY_test_user
# OCF_RESKEY_test_passwd
# OCF_RESKEY_enable_creation
# OCF_RESKEY_additional_parameters
# OCF_RESKEY_log
# OCF_RESKEY_pid
# OCF_RESKEY_socket
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
#######################################################################
# Fill in some defaults if no values are specified
HOSTOS=`uname`
if [ "X${HOSTOS}" = "XOpenBSD" ];then
OCF_RESKEY_binary_default="/usr/local/bin/mysqld_safe"
OCF_RESKEY_config_default="/etc/my.cnf"
OCF_RESKEY_datadir_default="/var/mysql"
OCF_RESKEY_user_default="_mysql"
OCF_RESKEY_group_default="_mysql"
OCF_RESKEY_log_default="/var/log/mysqld.log"
OCF_RESKEY_pid_default="/var/mysql/mysqld.pid"
OCF_RESKEY_socket_default="/var/run/mysql/mysql.sock"
else
OCF_RESKEY_binary_default="/usr/bin/safe_mysqld"
OCF_RESKEY_config_default="/etc/my.cnf"
OCF_RESKEY_datadir_default="/var/lib/mysql"
OCF_RESKEY_user_default="mysql"
OCF_RESKEY_group_default="mysql"
OCF_RESKEY_log_default="/var/log/mysqld.log"
OCF_RESKEY_pid_default="/var/run/mysql/mysqld.pid"
OCF_RESKEY_socket_default="/var/lib/mysql/mysql.sock"
fi
OCF_RESKEY_client_binary_default="mysql"
OCF_RESKEY_test_user_default="root"
OCF_RESKEY_test_table_default="mysql.user"
OCF_RESKEY_test_passwd_default=""
OCF_RESKEY_enable_creation_default=0
OCF_RESKEY_additional_parameters_default=""
OCF_RESKEY_replication_port_default="3306"
OCF_RESKEY_max_slave_lag_default="3600"
OCF_RESKEY_evict_outdated_slaves_default="false"
: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}}
MYSQL_BINDIR=`dirname ${OCF_RESKEY_binary}`
: ${OCF_RESKEY_client_binary=${OCF_RESKEY_client_binary_default}}
: ${OCF_RESKEY_config=${OCF_RESKEY_config_default}}
: ${OCF_RESKEY_datadir=${OCF_RESKEY_datadir_default}}
: ${OCF_RESKEY_user=${OCF_RESKEY_user_default}}
: ${OCF_RESKEY_group=${OCF_RESKEY_group_default}}
: ${OCF_RESKEY_log=${OCF_RESKEY_log_default}}
: ${OCF_RESKEY_pid=${OCF_RESKEY_pid_default}}
: ${OCF_RESKEY_socket=${OCF_RESKEY_socket_default}}
: ${OCF_RESKEY_test_user=${OCF_RESKEY_test_user_default}}
: ${OCF_RESKEY_test_table=${OCF_RESKEY_test_table_default}}
: ${OCF_RESKEY_test_passwd=${OCF_RESKEY_test_passwd_default}}
: ${OCF_RESKEY_enable_creation=${OCF_RESKEY_enable_creation_default}}
: ${OCF_RESKEY_additional_parameters=${OCF_RESKEY_additional_parameters_default}}
: ${OCF_RESKEY_replication_user=${OCF_RESKEY_replication_user_default}}
: ${OCF_RESKEY_replication_passwd=${OCF_RESKEY_replication_passwd_default}}
: ${OCF_RESKEY_replication_port=${OCF_RESKEY_replication_port_default}}
: ${OCF_RESKEY_max_slave_lag=${OCF_RESKEY_max_slave_lag_default}}
: ${OCF_RESKEY_evict_outdated_slaves=${OCF_RESKEY_evict_outdated_slaves_default}}
#######################################################################
usage() {
cat <<UEND
usage: $0 (start|stop|validate-all|meta-data|monitor|promote|demote|notify)
$0 manages a MySQL Database as an HA resource.
The 'start' operation starts the database.
The 'stop' operation stops the database.
The 'status' operation reports whether the database is running
The 'monitor' operation reports whether the database seems to be working
The 'promote' operation makes this mysql server run as master
The 'demote' operation makes this mysql server run as slave
The 'validate-all' operation reports whether the parameters are valid
UEND
}
meta_data() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="mysql">
<version>1.0</version>
<longdesc lang="en">
Resource script for MySQL.
May manage a standalone MySQL database, a clone set with externally
managed replication, or a complete master/slave replication setup.
</longdesc>
<shortdesc lang="en">Manages a MySQL database instance</shortdesc>
<parameters>
<parameter name="binary" unique="0" required="0">
<longdesc lang="en">
Location of the MySQL server binary
</longdesc>
<shortdesc lang="en">MySQL server binary</shortdesc>
<content type="string" default="${OCF_RESKEY_binary_default}" />
</parameter>
<parameter name="client_binary" unique="0" required="0">
<longdesc lang="en">
Location of the MySQL client binary
</longdesc>
<shortdesc lang="en">MySQL client binary</shortdesc>
<content type="string" default="${OCF_RESKEY_client_binary_default}" />
</parameter>
<parameter name="config" unique="0" required="0">
<longdesc lang="en">
Configuration file
</longdesc>
<shortdesc lang="en">MySQL config</shortdesc>
<content type="string" default="${OCF_RESKEY_config_default}" />
</parameter>
<parameter name="datadir" unique="0" required="0">
<longdesc lang="en">
Directory containing databases
</longdesc>
<shortdesc lang="en">MySQL datadir</shortdesc>
<content type="string" default="${OCF_RESKEY_datadir_default}" />
</parameter>
<parameter name="user" unique="0" required="0">
<longdesc lang="en">
User running MySQL daemon
</longdesc>
<shortdesc lang="en">MySQL user</shortdesc>
<content type="string" default="${OCF_RESKEY_user_default}" />
</parameter>
<parameter name="group" unique="0" required="0">
<longdesc lang="en">
Group running MySQL daemon (for logfile and directory permissions)
</longdesc>
<shortdesc lang="en">MySQL group</shortdesc>
<content type="string" default="${OCF_RESKEY_group_default}"/>
</parameter>
<parameter name="log" unique="0" required="0">
<longdesc lang="en">
The logfile to be used for mysqld.
</longdesc>
<shortdesc lang="en">MySQL log file</shortdesc>
<content type="string" default="${OCF_RESKEY_log_default}"/>
</parameter>
<parameter name="pid" unique="0" required="0">
<longdesc lang="en">
The pidfile to be used for mysqld.
</longdesc>
<shortdesc lang="en">MySQL pid file</shortdesc>
<content type="string" default="${OCF_RESKEY_pid_default}"/>
</parameter>
<parameter name="socket" unique="0" required="0">
<longdesc lang="en">
The socket to be used for mysqld.
</longdesc>
<shortdesc lang="en">MySQL socket</shortdesc>
<content type="string" default="${OCF_RESKEY_socket_default}"/>
</parameter>
<parameter name="test_table" unique="0" required="0">
<longdesc lang="en">
Table to be tested in monitor statement (in database.table notation)
</longdesc>
<shortdesc lang="en">MySQL test table</shortdesc>
<content type="string" default="${OCF_RESKEY_test_table_default}" />
</parameter>
<parameter name="test_user" unique="0" required="0">
<longdesc lang="en">
MySQL test user
</longdesc>
<shortdesc lang="en">MySQL test user</shortdesc>
<content type="string" default="${OCF_RESKEY_test_user_default}" />
</parameter>
<parameter name="test_passwd" unique="0" required="0">
<longdesc lang="en">
MySQL test user password
</longdesc>
<shortdesc lang="en">MySQL test user password</shortdesc>
<content type="string" default="${OCF_RESKEY_test_passwd_default}" />
</parameter>
<parameter name="enable_creation" unique="0" required="0">
<longdesc lang="en">
If the MySQL database does not exist, it will be created
</longdesc>
<shortdesc lang="en">Create the database if it does not exist</shortdesc>
<content type="integer" default="${OCF_RESKEY_enable_creation_default}"/>
</parameter>
<parameter name="additional_parameters" unique="0" required="0">
<longdesc lang="en">
Additional parameters which are passed to the mysqld on startup.
(e.g. --skip-external-locking or --skip-grant-tables)
</longdesc>
<shortdesc lang="en">Additional parameters to pass to mysqld</shortdesc>
<content type="string" default="${OCF_RESKEY_additional_parameters_default}"/>
</parameter>
<parameter name="replication_user" unique="0" required="0">
<longdesc lang="en">
MySQL replication user. This user is used for starting and stopping
MySQL replication, for setting and resetting the master host, and for
setting and unsetting read-only mode. Because of that, this user must
have SUPER, REPLICATION SLAVE, REPLICATION CLIENT, and PROCESS
privileges on all nodes within the cluster.
</longdesc>
<shortdesc lang="en">MySQL replication user</shortdesc>
<content type="string" default="${OCF_RESKEY_replication_user_default}" />
</parameter>
<parameter name="replication_passwd" unique="0" required="0">
<longdesc lang="en">
MySQL replication password. Used for replication client and slave.
</longdesc>
<shortdesc lang="en">MySQL replication user password</shortdesc>
<content type="string" default="${OCF_RESKEY_replication_passwd_default}" />
</parameter>
<parameter name="replication_port" unique="0" required="0">
<longdesc lang="en">
The port on which the Master MySQL instance is listening.
</longdesc>
<shortdesc lang="en">MySQL replication port</shortdesc>
<content type="string" default="${OCF_RESKEY_replication_port_default}" />
</parameter>
<parameter name="max_slave_lag" unique="0" required="0">
<longdesc lang="en">
The maximum number of seconds a replication slave is allowed to lag
behind its master. Do not set this to zero. What the cluster manager
does in case a slave exceeds this maximum lag is determined by the
evict_outdated_slaves parameter.
</longdesc>
<shortdesc lang="en">Maximum time (seconds) a MySQL slave is allowed
to lag behind a master</shortdesc>
<content type="integer" default="${OCF_RESKEY_max_slave_lag_default}"/>
</parameter>
<parameter name="evict_outdated_slaves" unique="0" required="0">
<longdesc lang="en">
If set to true, any slave which is more than max_slave_lag seconds
behind the master has its MySQL instance shut down. If this parameter
is set to false in a primitive or clone resource, it is simply
ignored. If set to false in a master/slave resource, then exceeding
the maximum slave lag will merely push down the master preference so
the lagging slave is never promoted to the new master.
</longdesc>
<shortdesc lang="en">Determines whether to shut down badly lagging
slaves</shortdesc>
<content type="boolean" default="${OCF_RESKEY_evict_outdated_slaves_default}" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="120" />
<action name="stop" timeout="120" />
<action name="status" timeout="60" />
<action name="monitor" depth="0" timeout="30" interval="10" />
<action name="promote" timeout="120" />
<action name="demote" timeout="120" />
<action name="notify" timeout="90" />
<action name="validate-all" timeout="5" />
<action name="meta-data" timeout="5" />
</actions>
</resource-agent>
END
}
#######################################################################
# Convenience variables
MYSQL=$OCF_RESKEY_client_binary
MYSQL_OPTIONS_LOCAL="-S $OCF_RESKEY_socket -O connect_timeout=1"
MYSQL_OPTIONS_REPL="--user=$OCF_RESKEY_replication_user --password=$OCF_RESKEY_replication_passwd"
CRM_MASTER="${HA_SBIN_DIR}/crm_master -l reboot "
#######################################################################
# Convenience functions
set_read_only() {
# Sets or unsets read-only mode. Accepts one boolean as its
# optional argument. If invoked without any arguments, defaults to
# enabling read only mode. Should only be set in master/slave
# setups.
# Returns $OCF_SUCCESS if the operation succeeds, or
# $OCF_ERR_GENERIC if it fails.
local ro_val
if ocf_is_true $1; then
ro_val="on"
else
ro_val="off"
fi
local mysql_options
mysql_options="$MYSQL_OPTIONS_LOCAL"
if [ -n $OCF_RESKEY_replication_user ]; then
mysql_options="$mysql_options $MYSQL_OPTIONS_REPL"
fi
ocf_run $MYSQL $mysql_options \
-e "SET GLOBAL read_only=${ro_val}"
}
is_slave() {
# Determine whether the machine is currently running as a MySQL
# slave, as determined per SHOW SLAVE STATUS. Returns 1 if SHOW
# SLAVE STATUS creates an empty result set, 0 otherwise.
local rc
local tmpfile
local mysql_options
tmpfile=`mktemp ${HA_RSCTMP}/is_slave.${OCF_RESOURCE_INSTANCE}.XXXXXX`
mysql_options="$MYSQL_OPTIONS_LOCAL --user=$OCF_RESKEY_test_user --password=$OCF_RESKEY_test_passwd"
$MYSQL $mysql_options \
-e 'SHOW SLAVE STATUS\G' > $tmpfile
# "SHOW SLAVE STATUS" returns an empty set if instance is not a
# replication slave
if [ -s $tmpfile ]; then
rm -f $tmpfile
return 0
fi
rm -f $tmpfile
return 1
}
check_slave() {
# Checks slave status
local rc
local tmpfile
local mysql_options
rc=1
tmpfile=`mktemp ${HA_RSCTMP}/check_slave.${OCF_RESOURCE_INSTANCE}.XXXXXX`
mysql_options="$MYSQL_OPTIONS_LOCAL --user=$OCF_RESKEY_test_user --password=$OCF_RESKEY_test_passwd"
$MYSQL $mysql_options \
-e 'SHOW SLAVE STATUS\G' > $tmpfile
local master_host
local master_user
local master_port
local slave_sql
local slave_io
local last_errno
local secs_behind
if [ -s $tmpfile ]; then
master_host=`sed -ne 's/^.*Master_Host: \(.*\)$/\1/p' < $tmpfile`
master_user=`sed -ne 's/^.*Master_User: \(.*\)$/\1/p' < $tmpfile`
master_port=`sed -ne 's/^.*Master_Port: \(.*\)$/\1/p' < $tmpfile`
slave_sql=`sed -ne 's/^.*Slave_SQL_Running: \(.*\)$/\1/p' < $tmpfile`
slave_io=`sed -ne 's/^.*Slave_IO_Running: \(.*\)$/\1/p' < $tmpfile`
last_errno=`sed -ne 's/^.*Last_Errno: \(.*\)$/\1/p' < $tmpfile`
secs_behind=`sed -ne 's/^.*Seconds_Behind_Master: \(.*\)$/\1/p' < $tmpfile`
if [ $last_errno -ne 0 ]; then
# Whoa. Replication ran into an error. This slave has
# diverged from its master. Make sure this resource
# doesn't restart in place.
ocf_log err "MySQL instance configured for replication, but replication has failed."
ocf_log err "See $tmpfile for details"
exit $OCF_ERR_INSTALLED
fi
if [ "$slave_io" != 'Yes' ]; then
# Not necessarily a bad thing. The master may have
# temporarily shut down, and the slave may just be
# reconnecting. A warning can't hurt, though.
ocf_log warn "MySQL Slave IO threads currently not running."
fi
if [ "$slave_sql" != 'Yes' ]; then
# We don't have a replication SQL thread running. Not a
# good thing. Try to recoved by restarting the resource in
# place.
ocf_log err "MySQL Slave SQL threads currently not running."
ocf_log err "See $tmpfile for details"
exit $OCF_ERR_GENERIC
fi
if ocf_is_true $OCF_RESKEY_evict_outdated_slaves; then
# We're supposed to bail out if we lag too far
# behind. Let's check our lag.
if [ $secs_behind -gt $OCF_RESKEY_max_slave_lag ]; then
ocf_log err "MySQL Slave is $secs_behind seconds behind master (allowed maximum: $OCF_RESKEY_max_slave_lag)."
ocf_log err "See $tmpfile for details"
exit $OCF_ERR_INSTALLED
fi
elif ocf_is_ms; then
# Even if we're not set to evict lagging slaves, we can
# still use the seconds behind master value to set our
# master preference.
local master_pref
master_pref=$((${OCF_RESKEY_max_slave_lag}-${secs_behind}))
if [ $master_pref -lt 0 ]; then
# Sanitize a below-zero preference to just zero
$master_pref=0
fi
$CRM_MASTER -v $master_pref
fi
ocf_log debug "MySQL instance running as a replication slave"
rm -f $tmpfile
else
# Instance produced an empty "SHOW SLAVE STATUS" output --
# instance is not a slave
rm -f $tmpfile
ocf_log err "check_slave invoked on an instance that is not a replication slave."
exit $OCF_ERR_GENERIC
fi
}
set_master() {
# Informs the MySQL server of the master to replicate
# from. Accepts one mandatory argument which must contain the host
# name of the new master host. The master must either be unchanged
# from the laste master the slave replicated from, or freshly
# reset with RESET MASTER.
local master_host
master_host=$1
ocf_run $MYSQL $MYSQL_OPTIONS_LOCAL $MYSQL_OPTIONS_REPL \
-e "CHANGE MASTER TO MASTER_HOST='$master_host', \
MASTER_USER='$OCF_RESKEY_replication_user', \
MASTER_PASSWORD='$OCF_RESKEY_replication_passwd'"
}
unset_master(){
# Instructs the MySQL server to stop replicating from a master
# host.
# If we're currently not configured to be replicating from any
# host, then there's nothing to do. But we do log a warning as
# no-one but the CRM should be touching the MySQL master/slave
# configuration.
if ! is_slave; then
ocf_log warn "Attempted to unset the replication master on an instance that is not configured as a replication slave"
return $OCF_SUCCESS
fi
local mysql_options
mysql_options="$MYSQL_OPTIONS_LOCAL $MYSQL_OPTIONS_REPL"
local tmpfile
tmpfile=`mktemp ${HA_RSCTMP}/unset_master.${OCF_RESOURCE_INSTANCE}.XXXXXX`
# First, stop the slave I/O thread and wait for relay log
# processing to complete
ocf_run $MYSQL $mysql_options \
-e "STOP SLAVE IO_THREAD" || exit $OCF_ERR_GENERIC
while true; do
$MYSQL $mysql_options \
-e 'SHOW PROCESSLIST\G' > $tmpfile
if grep 'Has read all relay log' $tmpfile >/dev/null; then
ocf_log info "MySQL slave has finished processing relay log"
break
fi
ocf_log info "Waiting for MySQL slave to finish processing relay log"
sleep 1
done
rm -f $tmpfile
# Now, stop all slave activity and unset the master host
ocf_run $MYSQL $mysql_options \
-e "STOP SLAVE" || exit $OCF_ERR_GENERIC
ocf_run $MYSQL $mysql_options \
-e "CHANGE MASTER TO MASTER_HOST=''" || exit $OCF_ERR_GENERIC
}
#######################################################################
# Functions invoked by resource manager actions
mysql_validate() {
check_binary $OCF_RESKEY_binary
check_binary $OCF_RESKEY_client_binary
if [ ! -f $OCF_RESKEY_config ]; then
ocf_log err "Config $OCF_RESKEY_config doesn't exist";
return $OCF_ERR_CONFIGURED;
fi
if [ ! -d $OCF_RESKEY_datadir ]; then
ocf_log err "Datadir $OCF_RESKEY_datadir doesn't exist";
return $OCF_ERR_CONFIGURED;
fi
getent passwd $OCF_RESKEY_user >/dev/null 2>&1
if [ ! $? -eq 0 ]; then
ocf_log err "User $OCF_RESKEY_user doesn't exit";
return $OCF_ERR_INSTALLED;
fi
getent group $OCF_RESKEY_group >/dev/null 2>&1
if [ ! $? -eq 0 ]; then
ocf_log err "Group $OCF_RESKEY_group doesn't exist";
return $OCF_ERR_INSTALLED;
fi
true
}
mysql_status() {
if [ ! -e $OCF_RESKEY_pid ]; then
ocf_log debug "MySQL is not running"
return $OCF_NOT_RUNNING;
fi
pid=`cat $OCF_RESKEY_pid`;
if [ -d /proc -a -d /proc/1 ]; then
[ "u$pid" != "u" -a -d /proc/$pid ]
else
kill -0 $pid >/dev/null 2>&1
fi
if [ $? -eq 0 ]; then
return $OCF_SUCCESS;
else
ocf_log debug "MySQL not running: removing old PID file"
rm -f $OCF_RESKEY_pid
return $OCF_NOT_RUNNING;
fi
}
mysql_monitor() {
local rc
mysql_status
rc=$?
# If status returned an error, return that immediately
if [ $rc -ne $OCF_SUCCESS ]; then
return $rc
fi
# Check if this instance is configured as a slave, and if so check
# slave status
if is_slave; then
check_slave
fi
if [ $OCF_CHECK_LEVEL -gt 0 ]; then
local mysql_options
mysql_options="$MYSQL_OPTIONS_LOCAL --user=$OCF_RESKEY_test_user --password=$OCF_RESKEY_test_passwd"
# Check for test table
ocf_run $MYSQL $mysql_options \
-e "SELECT COUNT(*) FROM $OCF_RESKEY_test_table"
rc=$?
if [ $rc -ne 0 ]; then
ocf_log err "Failed to select from $test_table";
return $OCF_ERR_GENERIC;
fi
fi
ocf_log info "MySQL monitor succeeded";
return $OCF_SUCCESS
}
mysql_start() {
mysql_status
if [ $? = $OCF_SUCCESS ]; then
ocf_log info "MySQL already running"
return $OCF_SUCCESS
fi
touch $OCF_RESKEY_log
chown $OCF_RESKEY_user:$OCF_RESKEY_group $OCF_RESKEY_log
chmod 0640 $OCF_RESKEY_log
[ -x /sbin/restorecon ] && /sbin/restorecon $OCF_RESKEY_log
if [ "$OCF_RESKEY_enable_creation" = 1 -a ! -d $OCF_RESKEY_datadir/mysql ] ; then
ocf_log info "Initializing MySQL database: "
$MYSQL_BINDIR/mysql_install_db --datadir=$OCF_RESKEY_datadir
rc=$?
if [ $rc -ne 0 ] ; then
ocf_log err "Initialization failed: $rc";
exit $OCF_ERR_GENERIC
fi
chown -R $OCF_RESKEY_user:$OCF_RESKEY_group $OCF_RESKEY_datadir
fi
pid_dir=`dirname $OCF_RESKEY_pid`
if ! su -s /bin/sh - $OCF_RESKEY_user -c "test -w $pid_dir"; then
ocf_log err "Directory $pid_dir for pidfile $OCF_RESKEY_pid is not writable by $OCF_RESKEY_user"
return $OCF_ERR_PERM;
fi
if [ ! -d $pid_dir ] ; then
ocf_log info "Creating PID dir: $pid_dir"
mkdir -p $pid_dir
chown $OCF_RESKEY_user:$OCF_RESKEY_group $pid_dir
fi
socket_dir=`dirname $OCF_RESKEY_socket`
if [ ! -d $socket_dir ] ; then
ocf_log info "Creating socket dir: $socket_dir"
mkdir -p $socket_dir
chown $OCF_RESKEY_user:$OCF_RESKEY_group $socket_dir
fi
# Uncomment to perform permission clensing
# - not convinced this should be enabled by default
#
#chmod 0755 $OCF_RESKEY_datadir
#chown -R $OCF_RESKEY_user $OCF_RESKEY_datadir
#chgrp -R $OCF_RESKEY_group $OCF_RESKEY_datadir
${OCF_RESKEY_binary} --defaults-file=$OCF_RESKEY_config \
--pid-file=$OCF_RESKEY_pid \
--socket=$OCF_RESKEY_socket \
--datadir=$OCF_RESKEY_datadir \
--user=$OCF_RESKEY_user $OCF_RESKEY_additional_parameters >/dev/null 2>&1 &
rc=$?
if [ $rc != 0 ]; then
ocf_log err "MySQL start command failed: $rc"
return $rc
fi
# Spin waiting for the server to come up.
# Let the CRM/LRM time us out if required
start_wait=1
while [ $start_wait = 1 ]; do
mysql_status
rc=$?
if [ $rc = $OCF_SUCCESS ]; then
start_wait=0
elif [ $rc != $OCF_NOT_RUNNING ]; then
ocf_log info "MySQL start failed: $rc"
return $rc
fi
sleep 2
done
if ocf_is_ms; then
# We're configured as a stateful resource. We must start as
# slave by default. Since we can't start as a MySQL slave (we
# don't know what master to replicate from), we simply start
# in read only mode.
set_read_only on
# We also need to set a master preference, otherwise Pacemaker
# won't ever promote us in the absence of any explicit
# preference set by the administrator. We choose a low
# greater-than-zero preference.
$CRM_MASTER -v 1
fi
ocf_log info "MySQL started"
return $OCF_SUCCESS
}
mysql_stop() {
if ocf_is_ms; then
# clear preference for becoming master
$CRM_MASTER -D
fi
if [ ! -f $OCF_RESKEY_pid ]; then
ocf_log info "MySQL is not running"
return $OCF_SUCCESS
fi
pid=`cat $OCF_RESKEY_pid 2> /dev/null `
/bin/kill $pid > /dev/null
rc=$?
if [ $rc != 0 ]; then
ocf_log err "MySQL couldn't be stopped"
return $OCF_ERR_GENERIC
fi
# stop waiting
shutdown_timeout=$((($OCF_RESKEY_CRM_meta_timeout/1000)-5))
count=0
while [ $count -lt $shutdown_timeout ]
do
mysql_status
rc=$?
if [ $rc = $OCF_NOT_RUNNING ]; then
break
fi
count=`expr $count + 1`
sleep 1
ocf_log debug "MySQL still hasn't stopped yet. Waiting..."
done
mysql_status
if [ $? != $OCF_NOT_RUNNING ]; then
ocf_log info "MySQL failed to stop after ${shutdown_timeout}s using SIGTERM. Trying SIGKILL..."
/bin/kill -KILL $pid > /dev/null
fi
ocf_log info "MySQL stopped";
rm -f /var/lock/subsys/mysqld
rm -f $OCF_RESKEY_socket
return $OCF_SUCCESS
}
mysql_promote() {
if ( ! mysql_status ); then
return $OCF_NOT_RUNNING
fi
set_read_only off || return $OCF_ERR_GENERIC
# Existing master gets a higher-than-default master preference, so
# the cluster manager does not shuffle the master role around
# unnecessarily
$CRM_MASTER -v $((${OCF_RESKEY_max_slave_lag}+1))
return $OCF_SUCCESS
}
mysql_demote() {
set_read_only on || return $OCF_ERR_GENERIC
# Return master preference to default, so the cluster manager gets
# a chance to select a new master
$CRM_MASTER -v 1
}
mysql_notify() {
# If not configured as a Stateful resource, we make no sense of
# notifications.
if ! ocf_is_ms; then
ocf_log info "This agent makes no use of notifications unless running in master/slave mode."
return $OCF_SUCCESS
fi
local type_op
type_op="${OCF_RESKEY_CRM_meta_notify_type}-${OCF_RESKEY_CRM_meta_notify_operation}"
ocf_log debug "Received $type_op notification."
case "$type_op" in
'pre-promote')
# A new master is about to being promoted. It's not in
# read-write mode yet (that only occurs when it actually
# executes the promote action), so we can now safely
# connect to it and wait for it to start replicating.
local master_host
local master_status
master_host=$OCF_RESKEY_CRM_meta_notify_promote_uname
if ( ! mysql_status ); then
return $OCF_NOT_RUNNING
fi
if [ -z "$master_host" ]; then
ocf_log err "Unable to determine master host!"
return $OCF_ERR_GENERIC
fi
if [ $master_host = `uname -n` ]; then
ocf_log info "Resetting MySQL replication configuration on new master $master_host"
ocf_run $MYSQL $MYSQL_OPTIONS_LOCAL $MYSQL_OPTIONS_REPL \
-e 'RESET MASTER'
else
ocf_log info "Changing MySQL configuration to replicate from $master_host"
set_master $master_host
fi
;;
'post-promote')
# The master has completed its promotion. Now is a good
# time to check whether our replication slave is working
# correctly.
if [ $OCF_RESKEY_CRM_meta_notify_promote_uname = `uname -n` ]; then
ocf_log info "Ignoring post-promote notification for my own promotion."
return $OCF_SUCCESS
fi
ocf_run $MYSQL $MYSQL_OPTIONS_LOCAL $MYSQL_OPTIONS_REPL \
-e 'START SLAVE';
;;
'post-demote')
if [ $OCF_RESKEY_CRM_meta_notify_demote_uname = `uname -n` ]; then
ocf_log info "Ignoring post-demote notification for my own demotion."
return $OCF_SUCCESS
fi
# The former master has just been gracefully demoted.
unset_master
;;
*)
return $OCF_SUCCESS
;;
esac
}
#######################################################################
case "$1" in
meta-data) meta_data
exit $OCF_SUCCESS;;
usage|help) usage
exit $OCF_SUCCESS;;
esac
mysql_validate
rc=$?
LSB_STATUS_STOPPED=3
if [ $rc -ne 0 ]; then
case "$1" in
stop) exit $OCF_SUCCESS;;
monitor) exit $OCF_NOT_RUNNING;;
status) exit $LSB_STATUS_STOPPED;;
*) exit $rc;;
esac
fi
# What kind of method was invoked?
case "$1" in
start) mysql_start;;
stop) mysql_stop;;
status) mysql_status;;
monitor) mysql_monitor;;
promote) mysql_promote;;
demote) mysql_demote;;
notify) mysql_notify;;
validate-all) exit $OCF_SUCCESS;;
*) usage
exit $OCF_ERR_UNIMPLEMENTED;;
esac
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Feb 26, 4:05 AM (21 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1465168
Default Alt Text
(101 KB)
Attached To
Mode
rR Resource Agents
Attached
Detach File
Event Timeline
Log In to Comment