diff --git a/heartbeat/.ocf-binaries.in b/heartbeat/.ocf-binaries.in index ba133faa0..dce07163b 100644 --- a/heartbeat/.ocf-binaries.in +++ b/heartbeat/.ocf-binaries.in @@ -1,78 +1,78 @@ # Make sure PATH contains all the usual suspects PATH="$PATH:/sbin:/bin:/usr/sbin:/usr/bin" # Include /usr/ucb for finding whoami on Solaris PATH="$PATH:/usr/ucb" export PATH # Binaries and binary options for use in Resource Agents : ${AWK:=@AWK@} : ${EGREP:="@EGREP@"} : ${IFCONFIG_A_OPT:="@IFCONFIG_A_OPT@"} : ${MAILCMD:=@MAILCMD@} : ${PING:=@PING@} : ${RPM:=@RPM@} : ${SH:=@SHELL@} : ${TEST:=@TEST@} : ${TESTPROG:=@TEST@} # Entries that should probably be removed : ${BASENAME:=basename} : ${BLOCKDEV:=blockdev} : ${CAT:=cat} : ${FSCK:=fsck} : ${FUSER:=fuser} : ${GETENT:=getent} : ${GREP:=grep} : ${IFCONFIG:=ifconfig} : ${IPTABLES:=iptables} : ${IP2UTIL:=ip} : ${MDADM:=mdadm} : ${MODPROBE:=modprobe} : ${MOUNT:=mount} : ${MSGFMT:=msgfmt} : ${NETSTAT:=netstat} : ${PERL:=perl} : ${PYTHON:=python} : ${RAIDSTART:=raidstart} : ${RAIDSTOP:=raidstop} : ${ROUTE:=route} : ${UMOUNT:=umount} : ${REBOOT:=reboot} : ${POWEROFF_CMD:=poweroff} : ${WGET:=wget} : ${WHOAMI:=whoami} : ${STRINGSCMD:=strings} : ${SCP:=scp} : ${SSH:=ssh} : ${SWIG:=swig} : ${GZIP_PROG:=gzip} : ${TAR:=tar} : ${MD5:=md5} : ${DRBDADM:=drbdadm} : ${DRBDSETUP:=drbdsetup} check_binary () { if have_binary "$1" then : else if [ "$OCF_NOT_RUNNING" = 7 ]; then # Chances are we have a fully setup OCF environment ocf_log err "Setup problem: Couldn't find utility $1" else echo "Setup problem: Couldn't find utility $1" fi exit $OCF_ERR_INSTALLED fi } have_binary () { bin=`echo $1 | sed -e 's/ -.*//'` - if [ -x "`which $bin`" ] >/dev/null 2>&1 ; then + if [ -x "`which $bin 2>/dev/null`" ]; then return 0 fi return 1 } diff --git a/heartbeat/IPaddr2 b/heartbeat/IPaddr2 index e018fa1bb..a2ff818af 100644 --- a/heartbeat/IPaddr2 +++ b/heartbeat/IPaddr2 @@ -1,849 +1,849 @@ #!/bin/sh # # $Id: IPaddr2.in,v 1.24 2006/08/09 13:01:54 lars Exp $ # # OCF Resource Agent compliant IPaddr2 script. # # Based on work by Tuomo Soini, ported to the OCF RA API by Lars # Marowsky-Brée. Implements Cluster Alias IP functionality too. # # Cluster Alias IP cleanup, fixes and testing by Michael Schwartzkopff # # # Copyright (c) 2003 Tuomo Soini # Copyright (c) 2004-2006 SUSE LINUX AG, Lars Marowsky-Brée # 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. # # # TODO: # - There ought to be an ocf_run_cmd function which does all logging, # timeout handling etc for us # - Make this the standard IP address agent on Linux; the other # platforms simply should ignore the additional parameters OR can use # the legacy heartbeat resource script... # - Check LVS <-> clusterip incompatibilities. # # OCF parameters are as below # OCF_RESKEY_ip # OCF_RESKEY_broadcast # OCF_RESKEY_nic # OCF_RESKEY_cidr_netmask # OCF_RESKEY_iflabel # OCF_RESKEY_mac # OCF_RESKEY_clusterip_hash # OCF_RESKEY_arp_interval # OCF_RESKEY_arp_count # OCF_RESKEY_arp_bg # OCF_RESKEY_arp_mac # # OCF_RESKEY_CRM_meta_clone # OCF_RESKEY_CRM_meta_clone_max ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs SENDARP=$HA_BIN/send_arp FINDIF=$HA_BIN/findif VLDIR=$HA_RSCTMP/IPaddr SENDARPPIDDIR=$HA_RSCTMP/send_arp CIP_lockfile=$HA_RSCTMP/IPaddr2-CIP-${OCF_RESKEY_ip} ####################################################################### meta_data() { cat < 1.0 This Linux-specific resource manages IP alias IP addresses. It can add an IP alias, or remove one. In addition, it can implement Cluster Alias IP functionality if invoked as a clone resource. Manages virtual IPv4 addresses The IPv4 address to be configured in dotted quad notation, for example "192.168.1.1". IPv4 address The base network interface on which the IP address will be brought online. If left empty, the script will try and determine this from the routing table. Do NOT specify an alias interface in the form eth0:1 or anything here; rather, specify the base interface only. Network interface The netmask for the interface in CIDR format (e.g., 24 and not 255.255.255.0) If unspecified, the script will also try to determine this from the routing table. CIDR netmask Broadcast address associated with the IP. If left empty, the script will determine this from the netmask. Broadcast address You can specify an additional label for your IP address here. This label is appended to your interface name. If a label is specified in nic name, this parameter has no effect. Interface label Enable support for LVS Direct Routing configurations. In case a IP address is stopped, only move it to the loopback device to allow the local node to continue to service requests, but no longer advertise it on the network. Enable support for LVS DR Set the interface MAC address explicitly. Currently only used in case of the Cluster IP Alias. Leave empty to chose automatically. Cluster IP MAC address Specify the hashing algorithm used for the Cluster IP functionality. Cluster IP hashing function If true, add the clone ID to the supplied value of ip to create a unique address to manage Create a unique address for cloned instances Specify the interval between unsolicited ARP packets in milliseconds. ARP packet interval in ms Number of unsolicited ARP packets to send. ARP packet count Whether or not to send the arp packets in the background. ARP from background MAC address to send the ARP packets too. You really shouldn't be touching this. ARP MAC END exit $OCF_SUCCESS } ip_init() { if [ X`uname -s` != "XLinux" ]; then ocf_log err "IPaddr2 only supported Linux." exit $OCF_ERR_INSTALLED fi if case $__OCF_ACTION in start|stop) ocf_is_root;; *) true;; esac then : YAY! else ocf_log err "You must be root for $__OCF_ACTION operation." exit $OCF_ERR_PERM fi BASEIP="$OCF_RESKEY_ip" BRDCAST="$OCF_RESKEY_broadcast" NIC="$OCF_RESKEY_nic" # Note: We had a version out there for a while which used # netmask instead of cidr_netmask. Don't remove this aliasing code! if [ ! -z "$OCF_RESKEY_netmask" -a -z "$OCF_RESKEY_cidr_netmask" ] then OCF_RESKEY_cidr_netmask=$OCF_RESKEY_netmask export OCF_RESKEY_cidr_netmask fi NETMASK="$OCF_RESKEY_cidr_netmask" IFLABEL="$OCF_RESKEY_iflabel" IF_MAC="$OCF_RESKEY_mac" LVS_SUPPORT=0 if [ x"${OCF_RESKEY_lvs_support}" = x"true" \ -o x"${OCF_RESKEY_lvs_support}" = x"on" \ -o x"${OCF_RESKEY_lvs_support}" = x"1" ]; then LVS_SUPPORT=1 fi IP_INC_GLOBAL=${OCF_RESKEY_CRM_meta_clone_max:-1} IP_INC_NO=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + 1` IP_CIP_HASH="${OCF_RESKEY_clusterip_hash:-'sourceip-sourceport'}" if [ $LVS_SUPPORT -gt 0 ] && [ $IP_INC_GLOBAL -gt 1 ]; then ocf_log err "LVS and load sharing do not go together well" exit $OCF_ERR_ARGS fi ARP_INTERVAL_MS=${OCF_RESKEY_arp_interval:-200} ARP_REPEAT=${OCF_RESKEY_arp_count:-5} ARP_BACKGROUND=${OCF_RESKEY_arp_bg:-yes} ARP_NETMASK=${OCF_RESKEY_arp_mac:-ffffffffffff} if ocf_is_decimal "$IP_INC_GLOBAL" && [ $IP_INC_GLOBAL -gt 0 ]; then : else ocf_log err "Invalid OCF_RESKEY_incarnations_max_global [$IP_INC_GLOBAL], should be positive integer" exit $OCF_ERR_ARGS fi # Validation is performed in ip_validate()... # # $FINDIF now takes its parameters from the environment # if NICINFO=`$FINDIF -C` then NICINFO=`echo $NICINFO | sed -e 's/netmask\ //;s/broadcast\ //'` NIC=`echo "$NICINFO" | cut -d" " -f1` NETMASK=`echo "$NICINFO" | cut -d" " -f2` BRDCAST=`echo "$NICINFO" | cut -d" " -f3` else ocf_log err "[$FINDIF -C] failed" exit $OCF_ERR_ARGS fi SENDARPPIDFILE="$SENDARPPIDDIR/send_arp-$BASEIP" case $NIC in *:*) IFLABEL=$NIC NIC=`echo $NIC | sed 's/:.*//'` ;; *) if [ -n "$IFLABEL" ]; then IFLABEL=${NIC}:${IFLABEL} fi ;; esac IP_CIP= if [ "$IP_INC_GLOBAL" -gt 1 ]; then IP_CIP="yes" if [ -z "$IF_MAC" ]; then # Choose a MAC # 1. Concatenate some input together # 2. This doesn't need to be a cryptographically # secure hash. # 3. Drop everything after the first 6 octets (12 chars) # 4. Delimit the octets with ':' # 5. Make sure the first octet is odd, # so the result is a multicast MAC IF_MAC=`echo $BASEIP $NETMASK $BRDCAST | \ md5sum | \ sed -e 's#\(............\).*#\1#' \ -e 's#..#&:#g; s#:$##' \ -e 's#^\(.\)[02468aAcCeE]#\11#'` fi IP_CIP_FILE="/proc/net/ipt_CLUSTERIP/$BASEIP" fi } # # Find out which interface serves the given IP address # The argument is an IP address, and its output # is an interface name (e.g., "eth0"). # find_interface() { # # List interfaces but exclude FreeS/WAN ipsecN virtual interfaces # - local iface=`$IP2UTIL -o -f inet addr show | grep "\ $BASEIP/" \ + local iface=`$IP2UTIL -o -f inet addr show | grep "\ $BASEIP/$NETMASK" \ | cut -d ' ' -f2 | grep -v '^ipsec[0-9][0-9]*$'` echo $iface return 0 } # # Delete an interface # delete_interface () { ipaddr="$1" iface="$2" netmask="$3" CMD="$IP2UTIL -f inet addr delete $ipaddr/$netmask dev $iface" ocf_log info "$CMD" $CMD if [ $? -ne 0 ]; then return $OCF_ERR_GENERIC fi CMD="$IP2UTIL -o -f inet addr show $iface" ocf_log info "$CMD" ADDR=`$CMD` if [ $? -ne 0 -o ! -z "$ADDR" ]; then return $? fi CMD="$IP2UTIL link set $iface down" ocf_log info "$CMD" $CMD return $? } # # Add an interface # add_interface () { ipaddr="$1" netmask="$2" broadcast="$3" iface="$4" label="$5" CMD="$IP2UTIL -f inet addr add $ipaddr/$netmask brd $broadcast dev $iface" if [ ! -z "$label" ]; then CMD="$CMD label $label" fi ocf_log info "$CMD" $CMD if [ $? -ne 0 ]; then return $OCF_ERR_GENERIC fi CMD="$IP2UTIL link set $iface up" ocf_log info "$CMD" $CMD return $? } # # Delete a route # delete_route () { prefix="$1" iface="$2" CMD="$IP2UTIL route delete $prefix dev $iface" ocf_log info "$CMD" $CMD return $? } # On Linux systems the (hidden) loopback interface may # conflict with the requested IP address. If so, this # unoriginal code will remove the offending loopback address # and save it in VLDIR so it can be added back in later # when the IPaddr is released. # # TODO: This is very ugly and should be controlled by an additional # instance parameter. Or even: multi-state, with the IP only being # "active" on the master!? # remove_conflicting_loopback() { ipaddr="$1" netmask="$2" broadcast="$3" ifname="$4" ocf_log info "Removing conflicting loopback $ifname." if [ -d "$VLDIR/" ] || mkdir -p "$VLDIR/"; then : Directory $VLDIR now exists else ocf_log err "Could not create \"$VLDIR/\" conflicting" \ " loopback $ifname cannot be restored." fi if echo "$ipaddr $netmask $broadcast $ifname" > "$VLDIR/$ipaddr" then : Saved loopback information in $VLDIR/$ipaddr else ocf_log err "Could not save conflicting loopback $ifname." \ "it will not be restored." fi delete_interface "$ipaddr" "$ifname" "$netmask" # Forcibly remove the route (if it exists) to the loopback. delete_route "$ipaddr" "$ifname" } # # On Linux systems the (hidden) loopback interface may # need to be restored if it has been taken down previously # by remove_conflicting_loopback() # restore_loopback() { ipaddr="$1" if [ -s "$VLDIR/$ipaddr" ]; then ifinfo=`cat "$VLDIR/$ipaddr"` ocf_log info "Restoring loopback IP Address " \ "$ifinfo." add_interface $ifinfo rm -f "$VLDIR/$ipaddr" fi } # # Run send_arp to note peers about new mac address # run_send_arp() { ARGS="-i $ARP_INTERVAL_MS -r $ARP_REPEAT -p $SENDARPPIDFILE $NIC $BASEIP auto not_used not_used" if [ "x$IP_CIP" = "xyes" ] ; then if [ x = "x$IF_MAC" ] ; then MY_MAC=auto else MY_MAC=`echo ${IF_MAC} | sed -e 's/://'` fi ARGS="-i $ARP_INTERVAL_MS -r $ARP_REPEAT -p $SENDARPPIDFILE $NIC $BASEIP $MY_MAC not_used not_used" fi ocf_log info "$SENDARP $ARGS" case $ARP_BACKGROUND in yes) ($SENDARP $ARGS || ocf_log err "Could not send gratuitous arps" &) >&2 ;; *) $SENDARP $ARGS || ocf_log err "Could not send gratuitous arps" ;; esac } # # Run ipoibarping to note peers about new Infiniband address # run_send_ib_arp() { SENDIBARP="/sbin/ipoibarping" if [ ! -x $SENDIBARP ] ; then ocf_log err "Could not send gratuitous Infiniband arps: file not found: $SENDIBPARP" return fi ARGS="-q -c $ARP_REPEAT -U -I $NIC $BASEIP" ocf_log info "$SENDIBARP $ARGS" case $ARP_BACKGROUND in yes) ($SENDIBARP $ARGS || ocf_log err "Could not send gratuitous arps" &) >&2 ;; *) $SENDIBARP $ARGS || ocf_log err "Could not send gratuitous arps" ;; esac } # Do we already serve this IP address? # # returns: # ok = served (for CIP: + hash bucket) # partial = served and no hash bucket (CIP only) # no = nothing # ip_served() { cur_nic="`find_interface $BASEIP`" if [ -z "$cur_nic" ]; then echo "no" return 0 fi if [ -z "$IP_CIP" ]; then case $cur_nic in lo*) if [ "$LVS_SUPPORT" = "1" ]; then echo "no" return 0 fi ;; esac echo "ok" return 0 fi # Special handling for the CIP: if egrep -q "(^|,)${IP_INC_NO}(,|$)" $IP_CIP_FILE ; then echo "ok" return 0 else echo "partial" return 0 fi exit $OCF_ERR_GENERIC } ####################################################################### ip_usage() { cat <$IP_CIP_FILE fi if [ "$ip_status" = "no" ]; then if [ "$LVS_SUPPORT" = "1" ]; then case `find_interface $BASEIP` in lo*) remove_conflicting_loopback $BASEIP 32 255.255.255.255 lo ;; esac fi add_interface $BASEIP $NETMASK $BRDCAST $NIC $IFLABEL if [ $? -ne 0 ]; then ocf_log err "$CMD failed." exit $OCF_ERR_GENERIC fi fi case $NIC in lo*) : no need to run send_arp on loopback ;; ib*) run_send_ib_arp ;; *) if [ -x $SENDARP ]; then run_send_arp fi ;; esac exit $OCF_SUCCESS } ip_stop() { local ip_del_if="yes" if [ -n "$IP_CIP" ]; then # Cluster IPs need special processing when the last bucket # is removed from the node... take a lock to make sure only one # process executes that code ocf_take_lock $CIP_lockfile ocf_release_lock_on_exit $CIP_lockfile fi if [ -f "$SENDARPPIDFILE" ] ; then kill `cat "$SENDARPPIDFILE"` if [ $? -ne 0 ]; then ocf_log warn "Could not kill previously running send_arp for $BASEIP" else ocf_log info "killed previously running send_arp for $BASEIP" rm -f "$SENDARPPIDFILE" fi fi local ip_status=`ip_served` if [ $ip_status = "no" ]; then : Requested interface not in use exit $OCF_SUCCESS fi if [ -n "$IP_CIP" ]; then if [ $ip_status = "partial" ]; then exit $OCF_SUCCESS fi echo "-$IP_INC_NO" >$IP_CIP_FILE if [ "x$(cat $IP_CIP_FILE)" = "x" ]; then ocf_log info $BASEIP, $IP_CIP_HASH i=1 while [ $i -le $IP_INC_GLOBAL ]; do ocf_log info $i $IPTABLES -D INPUT -d $BASEIP -i $NIC -j CLUSTERIP \ --new \ --clustermac $IF_MAC \ --total-nodes $IP_INC_GLOBAL \ --local-node $i \ --hashmode $IP_CIP_HASH i=`expr $i + 1` done else ip_del_if="no" fi fi if [ "$ip_del_if" = "yes" ]; then delete_interface $BASEIP $NIC $NETMASK if [ $? -ne 0 ]; then exit $OCF_ERR_GENERIC fi if [ "$LVS_SUPPORT" = 1 ]; then restore_loopback "$BASEIP" fi fi exit $OCF_SUCCESS } ip_monitor() { # TODO: Implement more elaborate monitoring like checking for # interface health maybe via a daemon like FailSafe etc... local ip_status=`ip_served` case $ip_status in ok) return $OCF_SUCCESS ;; partial|no) exit $OCF_NOT_RUNNING ;; *) # Errors on this interface? return $OCF_ERR_GENERIC ;; esac } ip_validate() { check_binary $IP2UTIL if [ -n "$IP_CIP" ]; then check_binary $IPTABLES check_binary $MODPROBE fi ip_init # $BASEIP, $NETMASK, $NIC , $IP_INC_GLOBAL, and $BRDCAST have been checked within ip_init, # do not bother here. if ocf_is_decimal "$ARP_INTERVAL_MS" && [ $ARP_INTERVAL_MS -gt 0 ]; then : else ocf_log err "Invalid OCF_RESKEY_arp_interval [$ARP_INTERVAL_MS]" exit $OCF_ERR_ARGS fi if ocf_is_decimal "$ARP_REPEAT" && [ $ARP_REPEAT -gt 0 ]; then : else ocf_log err "Invalid OCF_RESKEY_arp_count [$ARP_REPEAT]" exit $OCF_ERR_ARGS fi if [ -n "$IP_CIP" ]; then local valid=1 case $IP_CIP_HASH in sourceip|sourceip-sourceport|sourceip-sourceport-destport) ;; *) ocf_log err "Invalid OCF_RESKEY_clusterip_hash [$IP_CIP_HASH]" exit $OCF_ERR_ARGS ;; esac if [ "$LVS_SUPPORT" = 1 ]; then ecf_log err "LVS and load sharing not advised to try" exit $OCF_ERR_ARGS fi case $IF_MAC in [0-9a-zA-Z][1379bBdDfF][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]) ;; *) valid=0 ;; esac if [ $valid -eq 0 ]; then ocf_log err "Invalid IF_MAC [$IF_MAC]" exit $OCF_ERR_ARGS fi fi } case "$OCF_RESKEY_unique_clone_address" in true|TRUE|True|yes|YES|Yes|1) prefix=`echo $OCF_RESKEY_ip | awk -F. '{print $1"."$2"."$3}'` suffix=`echo $OCF_RESKEY_ip | awk -F. '{print $4}'` suffix=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + $suffix` OCF_RESKEY_ip="$prefix.$suffix" ;; *) ;; esac case $__OCF_ACTION in meta-data) meta_data ;; usage|help) ip_usage exit $OCF_SUCCESS ;; esac ip_validate case $__OCF_ACTION in start) ip_start ;; stop) ip_stop ;; status) ip_status=`ip_served` if [ $ip_status = "ok" ]; then echo "running" exit $OCF_SUCCESS else echo "stopped" exit $OCF_NOT_RUNNING fi ;; monitor) ip_monitor ;; validate-all) ;; *) ip_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac diff --git a/heartbeat/Makefile.am b/heartbeat/Makefile.am index 4d67ea507..fcdfd2a1e 100644 --- a/heartbeat/Makefile.am +++ b/heartbeat/Makefile.am @@ -1,103 +1,105 @@ # Makefile.am for OCF RAs # # Author: Sun Jing Dong # Copyright (C) 2004 IBM # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = $(ocf_SCRIPTS) ra-api-1.dtd INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/linux-ha ocfdir = $(OCF_RA_DIR)/heartbeat dtddir = $(datadir)/$(PACKAGE) dtd_SCRIPTS = ra-api-1.dtd if USE_IPV6ADDR ocf_PROGRAMS = IPv6addr else ocf_PROGRAMS = endif IPv6addr_SOURCES = IPv6addr.c IPv6addr_LDADD = -lplumb $(LIBNETLIBS) ocf_SCRIPTS = ClusterMon \ Dummy \ IPaddr \ IPaddr2 \ drbd \ anything \ AoEtarget \ apache \ AudibleAlarm \ db2 \ Delay \ eDir88 \ EvmsSCC \ Evmsd \ Filesystem \ ids \ iscsi \ ICP \ IPsrcaddr \ + iSCSITarget \ + iSCSILogicalUnit \ LinuxSCSI \ LVM \ MailTo \ ManageRAID \ ManageVE \ mysql \ mysql-proxy \ nfsserver \ oracle \ oralsnr \ pingd \ portblock \ pgsql \ Pure-FTPd \ Raid1 \ Route \ rsyncd \ SAPDatabase \ SAPInstance \ SendArp \ ServeRAID \ SphinxSearchDaemon \ Squid \ Stateful \ SysInfo \ scsi2reservation \ sfex \ tomcat \ VIPArip \ VirtualDomain \ vmware \ WAS \ WAS6 \ WinPopup \ Xen \ Xinetd \ .ocf-shellfuncs \ .ocf-binaries \ .ocf-directories \ .ocf-returncodes # Legacy locations commondir = $(libdir)/heartbeat common_SCRIPTS = ocf-shellfuncs ocf-returncodes diff --git a/heartbeat/SAPDatabase b/heartbeat/SAPDatabase index aa15b67e3..334c9ee71 100644 --- a/heartbeat/SAPDatabase +++ b/heartbeat/SAPDatabase @@ -1,1004 +1,998 @@ #!/bin/sh # # SAPDatabase # # Description: Manages any type of SAP supported database instance # as a High-Availability OCF compliant resource. # # Author: Alexander Krauth, October 2006 # Support: linux@sap.com # License: GNU General Public License (GPL) # Copyright: (c) 2006, 2007 Alexander Krauth # # An example usage: # See usage() function below for more details... # # OCF instance parameters: # OCF_RESKEY_SID # OCF_RESKEY_DIR_EXECUTABLE (optional, well known directories will be searched by default) # OCF_RESKEY_DBTYPE # OCF_RESKEY_NETSERVICENAME (optional, non standard name of Oracle Listener) # OCF_RESKEY_DBJ2EE_ONLY (optional, default is false) # OCF_RESKEY_JAVA_HOME (optional, only needed if DBJ2EE_ONLY is true and JAVA_HOME enviroment variable is not set) # OCF_RESKEY_STRICT_MONITORING (optional, activate application level monitoring - with Oracle a failover will occur in case of an archiver stuck) # OCF_RESKEY_AUTOMATIC_RECOVER (optional, automatic startup recovery, default is false) # OCF_RESKEY_DIR_BOOTSTRAP (optional, if non standard J2EE server directory) # OCF_RESKEY_DIR_SECSTORE (optional, if non standard J2EE secure store directory) # OCF_RESKEY_DB_JARS (optional, if maintained in bootstrap.properties, mandatory for WebAS Java 7.10) # OCF_RESKEY_PRE_START_USEREXIT (optional, lists a script which can be executed before the resource is started) # OCF_RESKEY_POST_START_USEREXIT (optional, lists a script which can be executed after the resource is started) # OCF_RESKEY_PRE_STOP_USEREXIT (optional, lists a script which can be executed before the resource is stopped) # OCF_RESKEY_POST_STOP_USEREXIT (optional, lists a script which can be executed after the resource is stopped) # # ToDo: # Remove all the database dependend stuff from the agent and use # saphostcontrol daemon as soon as SAP will release it. # ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### SH=/bin/sh usage() { methods=`sapdatabase_methods` methods=`echo $methods | tr ' ' '|'` cat <<-! usage: $0 ($methods) $0 manages a SAP database of any type as an HA resource. Currently Oracle, MaxDB and DB/2 UDB are supported. ABAP databases as well as JAVA only databases are supported. The 'start' operation starts the instance. The 'stop' operation stops the instance. The 'status' operation reports whether the instance is running The 'monitor' operation reports whether the instance seems to be working The 'recover' operation tries to recover the instance after a crash (instance will be stopped first!) The 'validate-all' operation reports whether the parameters are valid The 'methods' operation reports on the methods $0 supports ! } meta_data() { cat < -1.91 +1.92 Resource script for SAP databases. It manages a SAP database of any type as an HA resource. SAP database resource agent The unique SAP system identifier. e.g. P01 SAP system ID The full qualified path where to find sapstartsrv and sapcontrol. path of sapstartsrv and sapcontrol The name of the database vendor you use. Set either: ORA,DB6,ADA database vendor The Oracle TNS listener name. listener name If you do not have a ABAP stack installed in the SAP database, set this to TRUE only JAVA stack installed This is only needed if the DBJ2EE_ONLY parameter is set to true. Enter the path to the Java SDK which is used by the SAP WebAS Java Path to Java SDK This controls how the resource agent monitors the database. If set to true, it will use SAP tools to test the connect to the database. Do not use with Oracle, because it will result in unwanted failovers in case of an archiver stuck Activates application level monitoring The SAPDatabase resource agent tries to recover a failed start attempt automaticaly one time. This is done by running a forced abort of the RDBMS and/or executing recovery commands. Enable or disable automatic startup recovery The full qualified path where to find the J2EE instance bootstrap directory. e.g. /usr/sap/P01/J00/j2ee/cluster/bootstrap path to j2ee bootstrap directory The full qualified path where to find the J2EE security store directory. e.g. /usr/sap/P01/SYS/global/security/lib/tools path to j2ee secure store directory The full qualified filename of the jdbc driver for the database connection test. It will be automaticaly read from the bootstrap.properties file in Java engine 6.40 and 7.00. For Java engine 7.10 the parameter is mandatory. file name of the jdbc driver The full qualified path where to find a script or program which should be executed before this resource gets started. path to a pre-start script The full qualified path where to find a script or program which should be executed after this resource got started. path to a post-start script The full qualified path where to find a script or program which should be executed before this resource gets stopped. path to a pre-start script The full qualified path where to find a script or program which should be executed after this resource got stopped. path to a post-start script END } trap_handler() { rm -f $TEMPFILE exit $OCF_ERR_GENERIC } # # listener_start: Start the given listener # listener_start() { orasid="ora`echo $SID | tr [:upper:] [:lower:]`" rc=$OCF_SUCCESS output=`echo "lsnrctl start $NETSERVICENAME" | su - $orasid 2>&1` if [ $? -eq 0 ] then ocf_log info "Oracle Listener $NETSERVICENAME started: $output" rc=$OCF_SUCCESS else ocf_log err "Oracle Listener $NETSERVICENAME start failed: $output" rc=$OCF_ERR_GENERIC fi return $rc } # # listener_stop: Stop the given listener # listener_stop() { orasid="ora`echo $SID | tr [:upper:] [:lower:]`" rc=$OCF_SUCCESS if listener_status then : listener is running, trying to stop it later... else return $OCF_SUCCESS fi output=`echo "lsnrctl stop $NETSERVICENAME" | su - $orasid 2>&1` if [ $? -eq 0 ] then ocf_log info "Oracle Listener $NETSERVICENAME stopped: $output" else ocf_log err "Oracle Listener $NETSERVICENAME stop failed: $output" rc=$OCF_ERR_GENERIC fi return $rc } # # listener_status: is the given listener running? # listener_status() { orasid="ora`echo $SID | tr [:upper:] [:lower:]`" # Note: ps cuts off it's output at column $COLUMNS, so "ps -ef" can not be used here # as the output might be to long. cnt=`ps efo args --user $orasid | grep $NETSERVICENAME | grep -c tnslsnr` if [ $cnt -eq 1 ] then rc=$OCF_SUCCESS else ocf_log info "listener process not running for $NETSERVICENAME for $SID" rc=$OCF_ERR_GENERIC fi return $rc } # # x_server_start: Start the given x_server # x_server_start() { rc=$OCF_SUCCESS output=`echo "x_server start" | su - $sidadm 2>&1` if [ $? -eq 0 ] then ocf_log info "MaxDB x_server start: $output" rc=$OCF_SUCCESS else ocf_log err "MaxDB x_server start failed: $output" rc=$OCF_ERR_GENERIC fi return $rc } # # x_server_stop: Stop the x_server # x_server_stop() { rc=$OCF_SUCCESS output=`echo "x_server stop" | su - $sidadm 2>&1` if [ $? -eq 0 ] then ocf_log info "MaxDB x_server stop: $output" else ocf_log err "MaxDB x_server stop failed: $output" rc=$OCF_ERR_GENERIC fi return $rc } # # x_server_status: is the x_server running? # x_server_status() { sdbuser=`grep "^SdbOwner" /etc/opt/sdb | awk -F'=' '{print $2}'` # Note: ps cuts off it's output at column $COLUMNS, so "ps -ef" can not be used here # as the output might be to long. cnt=`ps efo args --user $sdbuser | grep -c vserver` - if [ $cnt -eq 1 ] + if [ $cnt -ge 1 ] then rc=$OCF_SUCCESS else ocf_log info "x_server process not running" rc=$OCF_ERR_GENERIC fi return $rc } # # oracle_stop: Stop the Oracle database without any condition # oracle_stop() { echo '#!/bin/sh LOG=$HOME/stopdb.log date > $LOG if [ -x "${ORACLE_HOME}/bin/sqlplus" ] then SRVMGRDBA_EXE="${ORACLE_HOME}/bin/sqlplus" else echo "Can not find executable sqlplus" >> $LOG exit 1 fi $SRVMGRDBA_EXE /NOLOG >> $LOG << ! connect / as sysdba shutdown immediate exit ! rc=$? cat $LOG exit $rc' > $TEMPFILE chmod 700 $TEMPFILE chown $sidadm $TEMPFILE su - $sidadm -c $TEMPFILE retcode=$? rm -f $TEMPFILE if [ $retcode -eq 0 ]; then sapdatabase_status if [ $? -ne $OCF_NOT_RUNNING ]; then retcode=1 fi fi return $retcode } # # maxdb_stop: Stop the MaxDB database without any condition # maxdb_stop() { # x_Server must be running to stop database x_server_status if [ $? -ne $OCF_SUCCESS ]; then x_server_start; fi if [ $DBJ2EE_ONLY -eq 1 ]; then userkey=c_J2EE else userkey=c fi echo "#!/bin/sh LOG=\$HOME/stopdb.log date > \$LOG echo \"Stop database with xuserkey >$userkey<\" >> \$LOG dbmcli -U ${userkey} db_offline >> \$LOG 2>&1 exit \$?" > $TEMPFILE chmod 700 $TEMPFILE chown $sidadm $TEMPFILE su - $sidadm -c $TEMPFILE retcode=$? rm -f $TEMPFILE if [ $retcode -eq 0 ]; then sapdatabase_status if [ $? -ne $OCF_NOT_RUNNING ]; then retcode=1 fi fi return $retcode } # # db6udb_stop: Stop the DB2/UDB database without any condition # db6udb_stop() { echo '#!/bin/sh LOG=$HOME/stopdb.log date > $LOG echo "Shut down the database" >> $LOG $INSTHOME/sqllib/bin/db2 deactivate database $DB2DBDFT |tee -a $LOG 2>&1 $INSTHOME/sqllib/adm/db2stop force |tee -a $LOG 2>&1 exit $?' > $TEMPFILE chmod 700 $TEMPFILE chown $sidadm $TEMPFILE su - $sidadm -c $TEMPFILE retcode=$? rm -f $TEMPFILE if [ $retcode -eq 0 ]; then sapdatabase_status if [ $? -ne $OCF_NOT_RUNNING ]; then retcode=1 fi fi return $retcode } # # oracle_recover: try to clean up oracle after a crash # oracle_recover() { echo '#!/bin/sh LOG=$HOME/recover.log date > $LOG echo "Logfile written by heartbeat SAPDatabase resource agent" >> $LOG if [ -x "${ORACLE_HOME}/bin/sqlplus" ] then SRVMGRDBA_EXE="${ORACLE_HOME}/bin/sqlplus" else echo "Can not find executable sqlplus" >> $LOG exit 1 fi $SRVMGRDBA_EXE /NOLOG >> $LOG << ! connect / as sysdba shutdown abort startup mount WHENEVER SQLERROR EXIT SQL.SQLCODE WHENEVER OSERROR EXIT FAILURE alter database recover automatic database; alter database open; exit ! rc=$? cat $LOG exit $rc' > $TEMPFILE chmod 700 $TEMPFILE chown $sidadm $TEMPFILE su - $sidadm -c $TEMPFILE retcode=$? rm -f $TEMPFILE return $retcode } # # maxdb_recover: try to clean up MaxDB after a crash # maxdb_recover() { # x_Server must be running to stop database x_server_status if [ $? -ne $OCF_SUCCESS ]; then x_server_start; fi if [ $DBJ2EE_ONLY -eq 1 ]; then userkey=c_J2EE else userkey=c fi echo "#!/bin/sh LOG=\$HOME/recover.log date > \$LOG echo \"Logfile written by heartbeat SAPDatabase resource agent\" >> \$LOG echo \"Cleanup database with xuserkey >$userkey<\" >> \$LOG echo \"db_stop\" >> \$LOG 2>&1 dbmcli -U ${userkey} db_stop >> \$LOG 2>&1 echo \"db_clear\" >> \$LOG 2>&1 dbmcli -U ${userkey} db_clear >> \$LOG 2>&1 echo \"db_online\" >> \$LOG 2>&1 dbmcli -U ${userkey} db_online >> \$LOG 2>&1 rc=\$? cat \$LOG exit \$rc" > $TEMPFILE chmod 700 $TEMPFILE chown $sidadm $TEMPFILE su - $sidadm -c $TEMPFILE retcode=$? rm -f $TEMPFILE return $retcode } # # db6udb_recover: try to recover DB/2 after a crash # db6udb_recover() { db2sid="db2`echo $SID | tr [:upper:] [:lower:]`" echo '#!/bin/sh LOG=$HOME/recover.log date > $LOG echo "Logfile written by heartbeat SAPDatabase resource agent" >> $LOG $INSTHOME/sqllib/bin/db2_kill >> $LOG 2>&1 $INSTHOME/sqllib/adm/db2start >> $LOG 2>&1 $INSTHOME/sqllib/bin/db2 activate database $DB2DBDFT >> $LOG 2>&1 rc=$? cat $LOG exit $rc' > $TEMPFILE chmod 700 $TEMPFILE chown $db2sid $TEMPFILE su - $db2sid -c $TEMPFILE retcode=$? rm -f $TEMPFILE return $retcode } # # methods: What methods/operations do we support? # sapdatabase_methods() { cat <<-! start stop status monitor recover validate-all methods meta-data usage ! } # # sapuserexit : Many SAP customers need some additional processes/tools to run their SAP systems. # This specialties do not allow a totally generic SAP cluster resource agent. # Someone should write a resource agent for each additional process you need, if it # is required to monitor that process within the cluster manager. To enable # you to extent this resource agent without developing a new one, this user exit # was introduced. # sapuserexit() { NAME="$1" VALUE="$2" if [ -n "$VALUE" ] then if [ -x "$VALUE" ] then ocf_log info "Calling userexit ${NAME} with customer script file ${VALUE}" eval "$VALUE" >& /dev/null ocf_log info "Exiting userexit ${NAME} with customer script file ${VALUE}, returncode: $?" else ocf_log warn "Attribute ${NAME} is set to ${VALUE}, but this file is not executable" fi fi return 0 } # # sapdatabase_start : Start the SAP database # sapdatabase_start() { sapuserexit PRE_START_USEREXIT "$OCF_RESKEY_PRE_START_USEREXIT" case $DBTYPE in ADA) x_server_start ;; ORA) listener_start ;; esac output=`su - $sidadm -c $SAPSTARTDB` rc=$? if [ $DBJ2EE_ONLY -eq 1 ] then sapdatabase_monitor 1 rc=$? fi if [ $rc -ne 0 -a $OCF_RESKEY_AUTOMATIC_RECOVER -eq 1 ] then ocf_log warn "SAP database $SID start failed: $output" ocf_log warn "Try to recover database $SID" output='' sapdatabase_recover rc=$? fi if [ $rc -eq 0 ] then ocf_log info "SAP database $SID started: $output" rc=$OCF_SUCCESS sapuserexit POST_START_USEREXIT "$OCF_RESKEY_POST_START_USEREXIT" else ocf_log err "SAP database $SID start failed: $output" rc=$OCF_ERR_GENERIC fi return $rc } # # sapdatabase_stop: Stop the SAP database # sapdatabase_stop() { sapuserexit PRE_STOP_USEREXIT "$OCF_RESKEY_PRE_STOP_USEREXIT" # use of the stopdb kernel script is not possible, because there are to may checks in that # script. We want to stop the database regardless of anything. #output=`su - $sidadm -c $SAPSTOPDB` case $DBTYPE in ORA) output=`oracle_stop` ;; ADA) output=`maxdb_stop` ;; DB6) output=`db6udb_stop` ;; esac if [ $? -eq 0 ] then ocf_log info "SAP database $SID stopped: $output" rc=$OCF_SUCCESS else ocf_log err "SAP database $SID stop failed: $output" rc=$OCF_ERR_GENERIC fi case $DBTYPE in ORA) listener_stop ;; ADA) x_server_stop ;; esac sapuserexit POST_STOP_USEREXIT "$OCF_RESKEY_POST_STOP_USEREXIT" return $rc } # # sapdatabase_monitor: Can the given database instance do anything useful? # sapdatabase_monitor() { strict=$1 rc=$OCF_SUCCESS case $DBTYPE in ADA) x_server_status if [ $? -ne $OCF_SUCCESS ]; then x_server_start; fi ;; ORA) listener_status if [ $? -ne $OCF_SUCCESS ]; then listener_start; fi ;; esac if [ $strict -eq 0 ] then sapdatabase_status rc=$? else if [ $DBJ2EE_ONLY -eq 0 ] then output=`echo "$SAPDBCONNECT -d -w /dev/null" | su $sidadm 2>&1` if [ $? -le 4 ] then rc=$OCF_SUCCESS else rc=$OCF_NOT_RUNNING fi else MYCP="" EXECMD="" # WebAS Java 6.40+7.00 IAIK_JCE="$SECSTORE"/iaik_jce.jar IAIK_JCE_EXPORT="$SECSTORE"/iaik_jce_export.jar EXCEPTION="$BOOTSTRAP"/exception.jar LOGGING="$BOOTSTRAP"/logging.jar OPENSQLSTA="$BOOTSTRAP"/opensqlsta.jar TC_SEC_SECSTOREFS="$BOOTSTRAP"/tc_sec_secstorefs.jar JDDI="$BOOTSTRAP"/../server0/bin/ext/jdbdictionary/jddi.jar ANTLR="$BOOTSTRAP"/../server0/bin/ext/antlr/antlr.jar FRAME="$BOOTSTRAP"/../server0/bin/system/frame.jar # only start jdbcconnect when all jars available if [ -f "$EXCEPTION" -a -f "$LOGGING" -a -f "$OPENSQLSTA" -a -f "$TC_SEC_SECSTOREFS" -a -f "$JDDI" -a -f "$ANTLR" -a -f "$FRAME" -a -f "$SAPDBCONNECT" ] then MYCP=".:$FRAME:$ANTLR:$JDDI:$IAIK_JCE_EXPORT:$IAIK_JCE:$EXCEPTION:$LOGGING:$OPENSQLSTA:$TC_SEC_SECSTOREFS:$DB_JARS:$SAPDBCONNECT" EXECMD="com.sap.inst.jdbc.connect.JdbcCon -sec $SID:$SID" else # WebAS Java 7.10 LAUNCHER=${BOOTSTRAP}/sap.com~tc~bl~offline_launcher~impl.jar if [ -f "$DB_JARS" -a -f "$SAPDBCONNECT" -a -f "$LAUNCHER" ] then MYCP="$LAUNCHER" EXECMD="com.sap.engine.offline.OfflineToolStart com.sap.inst.jdbc.connect.JdbcCon ${SAPDBCONNECT}:${SECSTORE}:${DB_JARS}:${BOOTSTRAP} -sec $SID:$SID" fi fi if [ -n "$EXECMD" ] then output=`eval ${JAVA_HOME}/bin/java -cp $MYCP $EXECMD` if [ $? -le 0 ] then rc=$OCF_SUCCESS else rc=$OCF_NOT_RUNNING fi else output="Cannot find all jar files needed for database monitoring." rc=$OCF_ERR_GENERIC fi fi fi if [ $rc -ne $OCF_SUCCESS ] then ocf_log err "The SAP database $SID ist not running: $output" fi return $rc } # # sapdatabase_status: Are there any database processes on this host ? # sapdatabase_status() { case $DBTYPE in ADA) SEARCH="$SID/db/pgm/kernel" SUSER=`grep "^SdbOwner" /etc/opt/sdb | awk -F'=' '{print $2}'` SNUM=2 ;; ORA) SEARCH="ora_[a-z][a-z][a-z][a-z]_" SUSER="ora`echo $SID | tr [:upper:] [:lower:]`" SNUM=4 ;; DB6) SEARCH="db2[a-z][a-z][a-z][a-z][a-z]" SUSER="db2`echo $SID | tr [:upper:] [:lower:]`" SNUM=5 ;; esac # Note: ps cuts off it's output at column $COLUMNS, so "ps -ef" can not be used here # as the output might be to long. cnt=`ps efo args --user $SUSER 2> /dev/null | grep -c "$SEARCH"` if [ $cnt -ge $SNUM ] then rc=$OCF_SUCCESS else # ocf_log info "Database Instance $SID is not running on `hostname`" rc=$OCF_NOT_RUNNING fi return $rc } # # sapdatabase_recover: # sapdatabase_recover() { case $DBTYPE in ORA) recoutput=`oracle_recover` ;; ADA) recoutput=`maxdb_recover` ;; DB6) recoutput=`db6udb_recover` ;; esac sapdatabase_monitor 1 retcode=$? if [ $retcode -eq $OCF_SUCCESS ] then ocf_log info "Recover of SAP database $SID was successful: $recoutput" else ocf_log err "Recover of SAP database $SID failed: $recoutput" fi return $retcode } # # sapdatabase_validate: Check the symantic of the input parameters # sapdatabase_validate() { rc=$OCF_SUCCESS if [ `echo "$SID" | grep -c '^[A-Z][A-Z0-9][A-Z0-9]$'` -ne 1 ] then ocf_log err "Parsing parameter SID: '$SID' is not a valid system ID!" rc=$OCF_ERR_ARGS fi case "$DBTYPE" in ORA|ADA|DB6) ;; *) ocf_log err "Parsing parameter DBTYPE: '$DBTYPE' is not a supported database type!" rc=$OCF_ERR_ARGS ;; esac return $rc } # # 'main' starts here... # if ( [ $# -ne 1 ] ) then usage exit $OCF_ERR_ARGS fi # Set a tempfile and make sure to clean it up again TEMPFILE="/tmp/SAPDatabase.$$.tmp" trap trap_handler INT TERM # These operations don't require OCF instance parameters to be set case "$1" in meta-data) meta_data exit $OCF_SUCCESS;; usage) usage exit $OCF_SUCCESS;; methods) sapdatabase_methods exit $?;; *);; esac US=`id -u -n` US=`echo $US` if [ $US != root ] then ocf_log err "$0 must be run as root" exit $OCF_ERR_PERM fi # mandatory parameter check if [ -z "$OCF_RESKEY_SID" ]; then ocf_log err "Please set OCF_RESKEY_SID to the SAP system id!" exit $OCF_ERR_ARGS fi SID=`echo "$OCF_RESKEY_SID"` if [ -z "$OCF_RESKEY_DBTYPE" ]; then ocf_log err "Please set OCF_RESKEY_DBTYPE to the database vendor specific tag (ORA,ADA,DB6)!" exit $OCF_ERR_ARGS fi DBTYPE=`echo "$OCF_RESKEY_DBTYPE" | tr "[a-z]" "[A-Z]"` # optional OCF parameters, we try to guess which directories are correct EXESTARTDB="startdb" EXESTOPDB="stopdb" EXEDBCONNECT="R3trans" if [ -z "$OCF_RESKEY_DBJ2EE_ONLY" ]; then DBJ2EE_ONLY=0 else case "$OCF_RESKEY_DBJ2EE_ONLY" in 1|true|TRUE|yes|YES) DBJ2EE_ONLY=1 EXESTARTDB="startj2eedb" EXESTOPDB="stopj2eedb" EXEDBCONNECT="jdbcconnect.jar" ;; 0|false|FALSE|no|NO) DBJ2EE_ONLY=0;; *) ocf_log err "Parsing parameter DBJ2EE_ONLY: '$DBJ2EE_ONLY' is not a boolean value!" exit $OCF_ERR_ARGS ;; esac fi if [ -z "$OCF_RESKEY_NETSERVICENAME" ]; then case "$DBTYPE" in ORA|ora) NETSERVICENAME="LISTENER";; *) NETSERVICENAME="";; esac else NETSERVICENAME="$OCF_RESKEY_NETSERVICENAME" fi if [ -z "$OCF_RESKEY_STRICT_MONITORING" ]; then OCF_RESKEY_STRICT_MONITORING=0 else case "$OCF_RESKEY_STRICT_MONITORING" in 1|true|TRUE|yes|YES) OCF_RESKEY_STRICT_MONITORING=1;; 0|false|FALSE|no|NO) OCF_RESKEY_STRICT_MONITORING=0;; *) ocf_log err "Parsing parameter STRICT_MONITORING: '$OCF_RESKEY_STRICT_MONITORING' is not a boolean value!" exit $OCF_ERR_ARGS ;; esac fi PATHLIST=" $OCF_RESKEY_DIR_EXECUTABLE /usr/sap/$SID/*/exe /usr/sap/$SID/SYS/exe/run /sapmnt/$SID/exe " DIR_EXECUTABLE="" for EXEPATH in $PATHLIST do - SAPSTARTDB=`which $EXEPATH/$EXESTARTDB 2> /dev/null` - if [ $? -eq 0 ] + if [ -x $EXEPATH/$EXESTARTDB -a -x $EXEPATH/$EXESTOPDB -a -x $EXEPATH/$EXEDBCONNECT ] then - MYPATH=`echo "$SAPSTARTDB" | head -1` - MYPATH=`dirname "$MYPATH"` - if [ -x $MYPATH/$EXESTARTDB -a -x $MYPATH/$EXESTOPDB -a -x $MYPATH/$EXEDBCONNECT ] - then - DIR_EXECUTABLE=$MYPATH - SAPSTARTDB=$MYPATH/$EXESTARTDB - SAPSTOPDB=$MYPATH/$EXESTOPDB - SAPDBCONNECT=$MYPATH/$EXEDBCONNECT - break - fi + DIR_EXECUTABLE=$EXEPATH + SAPSTARTDB=$EXEPATH/$EXESTARTDB + SAPSTOPDB=$EXEPATH/$EXESTOPDB + SAPDBCONNECT=$EXEPATH/$EXEDBCONNECT + break fi done if [ -z "$DIR_EXECUTABLE" ] then ocf_log warn "Cannot find $EXESTARTDB,$EXESTOPDB and $EXEDBCONNECT executable, please set DIR_EXECUTABLE parameter!" exit $OCF_NOT_RUNNING fi if [ $DBJ2EE_ONLY -eq 1 ] then if [ -n "$OCF_RESKEY_DIR_BOOTSTRAP" ] then BOOTSTRAP="$OCF_RESKEY_DIR_BOOTSTRAP" else BOOTSTRAP=`echo /usr/sap/$SID/*/j2ee/cluster/bootstrap | head -1` fi if [ -n "$OCF_RESKEY_DIR_SECSTORE" ] then SECSTORE="$OCF_RESKEY_DIR_SECSTORE" else SECSTORE=/usr/sap/$SID/SYS/global/security/lib/tools fi if [ -n "$OCF_RESKEY_JAVA_HOME" ] then JAVA_HOME="$OCF_RESKEY_JAVA_HOME" PATH=$JAVA_HOME/bin:$PATH else if [ -n "$JAVA_HOME" ] then PATH=$JAVA_HOME/bin:$PATH else ocf_log err "Cannot find JAVA_HOME directory, please set JAVA_HOME parameter!" exit $OCF_NOT_RUNNING fi fi if [ -n "$OCF_RESKEY_DB_JARS" ] then DB_JARS=$OCF_RESKEY_DB_JARS else if [ -f "$BOOTSTRAP"/bootstrap.properties ]; then DB_JARS=`cat $BOOTSTRAP/bootstrap.properties | grep -i rdbms.driverLocation | sed -e 's/\\\:/:/g' | awk -F= '{print $2}'` fi fi fi if [ -z "$OCF_RESKEY_AUTOMATIC_RECOVER" ] then OCF_RESKEY_AUTOMATIC_RECOVER=0 else case "$OCF_RESKEY_AUTOMATIC_RECOVER" in 1|true|TRUE|yes|YES) OCF_RESKEY_AUTOMATIC_RECOVER=1;; 0|false|FALSE|no|NO) OCF_RESKEY_AUTOMATIC_RECOVER=0;; esac fi # as root user we need the library path to the SAP kernel to be able to call executables if [ `echo $LD_LIBRARY_PATH | grep -c "^$DIR_EXECUTABLE\>"` -eq 0 ]; then LD_LIBRARY_PATH=$DIR_EXECUTABLE:$LD_LIBRARY_PATH; export LD_LIBRARY_PATH fi sidadm="`echo $SID | tr [:upper:] [:lower:]`adm" # What kind of method was invoked? case "$1" in start) sapdatabase_start exit $?;; stop) sapdatabase_stop exit $?;; monitor) sapdatabase_monitor $OCF_RESKEY_STRICT_MONITORING exit $?;; status) sapdatabase_status exit $?;; recover) sapdatabase_recover exit $?;; validate-all) sapdatabase_validate exit $?;; *) sapdatabase_methods exit $OCF_ERR_UNIMPLEMENTED;; esac diff --git a/heartbeat/SAPInstance b/heartbeat/SAPInstance index dcaf1c71c..582cf1f87 100644 --- a/heartbeat/SAPInstance +++ b/heartbeat/SAPInstance @@ -1,584 +1,771 @@ #!/bin/sh # # SAPInstance # # Description: Manages a single SAP Instance as a High-Availability # resource. One SAP Instance is defined by one # SAP Instance-Profile. start/stop handels all services # of the START-Profile, status and monitor care only # about essential services. # # Author: Alexander Krauth, June 2006 # Support: linux@sap.com # License: GNU General Public License (GPL) -# Copyright: (c) 2006, 2007 Alexander Krauth +# Copyright: (c) 2006-2008 Alexander Krauth # # An example usage: # See usage() function below for more details... # # OCF instance parameters: # OCF_RESKEY_InstanceName # OCF_RESKEY_DIR_EXECUTABLE (optional, well known directories will be searched by default) # OCF_RESKEY_DIR_PROFILE (optional, well known directories will be searched by default) # OCF_RESKEY_START_PROFILE (optional, well known directories will be searched by default) # OCF_RESKEY_START_WAITTIME (optional, to solve timing problems during J2EE-Addin start) # OCF_RESKEY_AUTOMATIC_RECOVER (optional, automatic startup recovery using cleanipc, default is false) +# OCF_RESKEY_MONITOR_SERVICES (optional, default is to monitor critical services only) +# OCF_RESKEY_ERS_InstanceName (optional, InstanceName of the ERS instance in a Master/Slave configuration) +# OCF_RESKEY_ERS_START_PROFILE (optional, START_PROFILE of the ERS instance in a Master/Slave configuration) # OCF_RESKEY_PRE_START_USEREXIT (optional, lists a script which can be executed before the resource is started) # OCF_RESKEY_POST_START_USEREXIT (optional, lists a script which can be executed after the resource is started) # OCF_RESKEY_PRE_STOP_USEREXIT (optional, lists a script which can be executed before the resource is stopped) # OCF_RESKEY_POST_STOP_USEREXIT (optional, lists a script which can be executed after the resource is stopped) # ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### SH=/bin/sh -usage() { +sapinstance_usage() { methods=`sapinstance_methods` methods=`echo $methods | tr ' ' '|'` cat <<-! usage: $0 ($methods) $0 manages a SAP Instance as an HA resource. - The 'start' operation starts the instance. - The 'stop' operation stops the instance. + The 'start' operation starts the instance or the ERS instance in a Master/Slave configuration + The 'stop' operation stops the instance The 'status' operation reports whether the instance is running The 'monitor' operation reports whether the instance seems to be working + The 'promote' operation starts the primary instance in a Master/Slave configuration + The 'demote' operation stops the primary instance and starts the ERS instance + The 'notify' operation always returns SUCCESS The 'validate-all' operation reports whether the parameters are valid The 'methods' operation reports on the methods $0 supports ! } -meta_data() { +sapinstance_meta_data() { cat < -1.91 +2.01 Resource script for SAP. It manages a SAP Instance as an HA resource. SAP instance resource agent The full qualified SAP instance name. e.g. P01_DVEBMGS00_sapp01ci instance name: SID_INSTANCE_VIR-HOSTNAME The full qualified path where to find sapstartsrv and sapcontrol. path of sapstartsrv and sapcontrol The full qualified path where to find the SAP START profile. path of start profile The name of the SAP START profile. start profile name After that time in seconds a monitor operation is executed by the resource agent. Does the monitor return SUCCESS, the start is handled as SUCCESS. This is useful to resolve timing problems with e.g. the J2EE-Addin instance. Check the successful start after that time (do not wait for J2EE-Addin) The SAPInstance resource agent tries to recover a failed start attempt automaticaly one time. This is done by killing runing instance processes and executing cleanipc. Enable or disable automatic startup recovery + + + + + + + + + + + + + + + The full qualified path where to find a script or program which should be executed before this resource gets started. path to a pre-start script The full qualified path where to find a script or program which should be executed after this resource got started. path to a post-start script The full qualified path where to find a script or program which should be executed before this resource gets stopped. path to a pre-start script The full qualified path where to find a script or program which should be executed after this resource got stopped. path to a post-start script - + + END } # # methods: What methods/operations do we support? # sapinstance_methods() { cat <<-! start - recover stop status monitor + promote + demote + notify validate-all methods meta-data usage ! } + +# +# is_clone : find out if we are configured to run in a Master/Slave configuration +# +is_clone() { + if [ -n "$OCF_RESKEY_CRM_meta_clone_max" ] \ + && [ "$OCF_RESKEY_CRM_meta_clone_max" -gt 0 ] + then + if [ "$OCF_RESKEY_CRM_meta_clone_max" -ne 2 ] || \ + [ "$OCF_RESKEY_CRM_meta_clone_node_max" -ne 1 ] || \ + [ "$OCF_RESKEY_CRM_meta_master_node_max" -ne 1 ] || \ + [ "$OCF_RESKEY_CRM_meta_master_max" -ne 1 ] + then + ocf_log err "Clone options misconfigured. (expect: clone_max=2,clone_node_max=1,master_node_max=1,master_max=1)" + exit $OCF_ERR_CONFIGURED + fi + + if [ -z "$OCF_RESKEY_ERS_InstanceName" ] + then + ocf_log err "In a Master/Slave configuration the ERS_InstanceName parameter is mandatory." + exit $OCF_ERR_ARGS + fi + else + return 0 + fi + return 1 +} + + +# +# sapinstance_init : Define global variables with default values, if optional parameters are not set +# +# +sapinstance_init() { + + myInstanceName="$1" + + SID=`echo "$myInstanceName" | cut -d_ -f1` + InstanceName=`echo "$myInstanceName" | cut -d_ -f2` + InstanceNr=`echo "$InstanceName" | sed 's/.*\([0-9][0-9]\)$/\1/'` + SAPVIRHOST=`echo "$myInstanceName" | cut -d_ -f3` + + # optional OCF parameters, we try to guess which directories are correct + if [ -z "$OCF_RESKEY_DIR_EXECUTABLE" ] + then + if [ -x /usr/sap/$SID/$InstanceName/exe/sapstartsrv -a -x /usr/sap/$SID/$InstanceName/exe/sapcontrol ] + then + DIR_EXECUTABLE="/usr/sap/$SID/$InstanceName/exe" + SAPSTARTSRV="/usr/sap/$SID/$InstanceName/exe/sapstartsrv" + SAPCONTROL="/usr/sap/$SID/$InstanceName/exe/sapcontrol" + elif [ -x /usr/sap/$SID/SYS/exe/run/sapstartsrv -a -x /usr/sap/$SID/SYS/exe/run/sapcontrol ] + then + DIR_EXECUTABLE="/usr/sap/$SID/SYS/exe/run" + SAPSTARTSRV="/usr/sap/$SID/SYS/exe/run/sapstartsrv" + SAPCONTROL="/usr/sap/$SID/SYS/exe/run/sapcontrol" + else + ocf_log warn "Cannot find sapstartsrv and sapcontrol executable, please set DIR_EXECUTABLE parameter!" + exit $OCF_NOT_RUNNING + fi + else + DIR_EXECUTABLE="$OCF_RESKEY_DIR_EXECUTABLE" + SAPSTARTSRV="$OCF_RESKEY_DIR_EXECUTABLE/sapstartsrv" + SAPCONTROL="$OCF_RESKEY_DIR_EXECUTABLE/sapcontrol" + fi + + if [ -z "$OCF_RESKEY_DIR_PROFILE" ] + then + if [ -d /usr/sap/$SID/SYS/profile/ ] + then + DIR_PROFILE="/usr/sap/$SID/SYS/profile" + else + ocf_log warn "Expected /usr/sap/$SID/SYS/profile/ to be a directory, please set DIR_PROFILE parameter!" + exit $OCF_NOT_RUNNING + fi + else + DIR_PROFILE="$OCF_RESKEY_DIR_PROFILE" + fi + + if [ "$myInstanceName" != "$OCF_RESKEY_InstanceName" ] + then + currentSTART_PROFILE=$OCF_RESKEY_ERS_START_PROFILE + else + currentSTART_PROFILE=$OCF_RESKEY_START_PROFILE + fi + + if [ -z "$currentSTART_PROFILE" ] + then + SAPSTARTPROFILE="$DIR_PROFILE/START_${InstanceName}_${SAPVIRHOST}" + if [ ! -r $SAPSTARTPROFILE ] + then + ocf_log warn "Expected $SAPSTARTPROFILE to be the instance START profile, please set START_PROFILE parameter!" + exit $OCF_NOT_RUNNING + fi + else + SAPSTARTPROFILE="$currentSTART_PROFILE" + fi + + if [ -z "$OCF_RESKEY_START_WAITTIME" ] + then + export OCF_RESKEY_START_WAITTIME=3600 + fi + + if [ -z "$OCF_RESKEY_AUTOMATIC_RECOVER" ] + then + OCF_RESKEY_AUTOMATIC_RECOVER=0 + else + case "$OCF_RESKEY_AUTOMATIC_RECOVER" in + 1|true|TRUE|yes|YES) export OCF_RESKEY_AUTOMATIC_RECOVER=1;; + 0|false|FALSE|no|NO) export OCF_RESKEY_AUTOMATIC_RECOVER=0;; + esac + fi + + if [ -z "$OCF_RESKEY_MONITOR_SERVICES" ] + then + export OCF_RESKEY_MONITOR_SERVICES="disp+work|msg_server|enserver|enrepserver|jcontrol|jstart" + fi + + # as root user we need the library path to the SAP kernel to be able to call sapcontrol + if [ `echo $LD_LIBRARY_PATH | grep -c "^$DIR_EXECUTABLE\>"` -eq 0 ]; then + LD_LIBRARY_PATH=$DIR_EXECUTABLE:$LD_LIBRARY_PATH; export LD_LIBRARY_PATH + fi + + sidadm="`echo $SID | tr [:upper:] [:lower:]`adm" + + return $OCF_SUCCESS +} + + # # check_sapstartsrv : Before using sapcontrol we make sure that the sapstartsrv is running for the correct instance. # We cannot use sapinit and the /usr/sap/sapservices file in case of an enquerep instance, # because then we have two instances with the same instance number. # check_sapstartsrv() { restart=0 runninginst="" chkrc=$OCF_SUCCESS output=`$SAPCONTROL -nr $InstanceNr -function ParameterValue INSTANCE_NAME -format script` if [ $? -eq 0 ] then runninginst=`echo "$output" | grep '^0 : ' | cut -d' ' -f3` if [ "$runninginst" != "$InstanceName" ] then ocf_log warn "sapstartsrv is running for instance $runninginst, that service will be killed" restart=1 fi else ocf_log warn "sapstartsrv is not running for instance $SID-$InstanceName, it will be started now" restart=1 fi if [ -z "$runninginst" ]; then runninginst=$InstanceName; fi if [ $restart -eq 1 ] then pkill -9 -f "sapstartsrv.*$runninginst" $SAPSTARTSRV pf=$SAPSTARTPROFILE -D -u $sidadm # now make sure the daemon has been started and is able to respond srvrc=1 while [ $srvrc -eq 1 -a `pgrep -f "sapstartsrv.*$runninginst" | wc -l` -gt 0 ] do sleep 1 $SAPCONTROL -nr $InstanceNr -function GetProcessList > /dev/null 2>&1 srvrc=$? done if [ $srvrc -ne 1 ] then ocf_log info "sapstartsrv for instance $SID-$InstanceName was restarted !" chkrc=$OCF_SUCCESS else ocf_log error "sapstartsrv for instance $SID-$InstanceName could not be started!" chkrc=$OCF_NOT_RUNNING fi fi return $chkrc } # # sapuserexit : Many SAP customers need some additional processes/tools to run their SAP systems. # This specialties do not allow a totally generic SAP cluster resource agent. # Someone should write a resource agent for each additional process you need, if it # is required to monitor that process within the cluster manager. To enable # you to extent this resource agent without developing a new one, this user exit # was introduced. # sapuserexit() { NAME="$1" VALUE="$2" if [ -n "$VALUE" ] then if [ -x "$VALUE" ] then ocf_log info "Calling userexit ${NAME} with customer script file ${VALUE}" eval "$VALUE" >& /dev/null ocf_log info "Exiting userexit ${NAME} with customer script file ${VALUE}, returncode: $?" else ocf_log warn "Attribute ${NAME} is set to ${VALUE}, but this file is not executable" fi fi return 0 } # # cleanup_instance : remove resources (processes and shared memory) from a crashed instance) # cleanup_instance() { pkill -9 -f -U $sidadm $InstanceName $DIR_EXECUTABLE/cleanipc $InstanceNr remove return 0 } # # sapinstance_start : Start the SAP instance # sapinstance_start() { sapuserexit PRE_START_USEREXIT "$OCF_RESKEY_PRE_START_USEREXIT" rc=$OCF_NOT_RUNNING loopcount=0 while [ $loopcount -lt 2 ] do loopcount=$(($loopcount + 1)) check_sapstartsrv output=`$SAPCONTROL -nr $InstanceNr -function Start` rc=$? ocf_log info "Starting SAP Instance $SID-$InstanceName: $output" if [ $rc -ne 0 ] then ocf_log err "SAP Instance $SID-$InstanceName start failed." return $OCF_ERR_GENERIC fi startrc=1 while [ $startrc -gt 0 ] do waittime_start=`date +%s` output=`$SAPCONTROL -nr $InstanceNr -function WaitforStarted $OCF_RESKEY_START_WAITTIME 10` startrc=$? waittime_stop=`date +%s` if [ $startrc -ne 0 ] then if [ $(($waittime_stop - $waittime_start)) -ge $OCF_RESKEY_START_WAITTIME ] then sapinstance_monitor NOLOG if [ $? -eq $OCF_SUCCESS ] then output="START_WAITTIME ($OCF_RESKEY_START_WAITTIME) has elapsed, but instance monitor returned SUCCESS. Instance considered running." startrc=0; loopcount=2 fi else if [ $loopcount -eq 1 -a $OCF_RESKEY_AUTOMATIC_RECOVER -eq 1 ] then ocf_log warn "SAP Instance $SID-$InstanceName start failed: $output" ocf_log warn "Try to recover $SID-$InstanceName" cleanup_instance else loopcount=2 fi startrc=-1 fi else loopcount=2 fi done done if [ $startrc -eq 0 ] then ocf_log info "SAP Instance $SID-$InstanceName started: $output" rc=$OCF_SUCCESS sapuserexit POST_START_USEREXIT "$OCF_RESKEY_POST_START_USEREXIT" else ocf_log err "SAP Instance $SID-$InstanceName start failed: $output" rc=$OCF_NOT_RUNNING fi return $rc } # # sapinstance_recover: Try startup of failed instance by cleaning up resources # sapinstance_recover() { cleanup_instance sapinstance_start return $? } # # sapinstance_stop: Stop the SAP instance # sapinstance_stop() { sapuserexit PRE_STOP_USEREXIT "$OCF_RESKEY_PRE_STOP_USEREXIT" check_sapstartsrv output=`$SAPCONTROL -nr $InstanceNr -function Stop` if [ $? -eq 0 ] then output=`$SAPCONTROL -nr $InstanceNr -function WaitforStopped 3600 1` if [ $? -eq 0 ] then ocf_log info "SAP Instance $SID-$InstanceName stopped: $output" rc=$OCF_SUCCESS else ocf_log err "SAP Instance $SID-$InstanceName stop failed: $output" rc=$OCF_ERR_GENERIC fi else ocf_log err "SAP Instance $SID-$InstanceName stop failed: $output" rc=$OCF_ERR_GENERIC fi sapuserexit POST_STOP_USEREXIT "$OCF_RESKEY_POST_STOP_USEREXIT" return $rc } # # sapinstance_monitor: Can the given SAP instance do anything useful? # sapinstance_monitor() { MONLOG=$1 check_sapstartsrv rc=$? if [ $rc -eq $OCF_SUCCESS ] then count=0 LOCALHOST=`hostname` output=`$SAPCONTROL -nr $InstanceNr -host $LOCALHOST -function GetProcessList -format script` # we have to parse the output, because the returncode doesn't tell anything about the instance status for SERVNO in `echo "$output" | grep '^[0-9] ' | cut -d' ' -f1 | sort -u` do COLOR=`echo "$output" | grep "^$SERVNO dispstatus: " | cut -d' ' -f3` SERVICE=`echo "$output" | grep "^$SERVNO name: " | cut -d' ' -f3` STATE=0 case $COLOR in GREEN|YELLOW) STATE=$OCF_SUCCESS;; *) STATE=$OCF_NOT_RUNNING;; esac - case $SERVICE in - disp+work|msg_server|enserver|enrepserver|jcontrol|jstart) - if [ $STATE -eq $OCF_NOT_RUNNING ] - then - if [ "$MONLOG" != "NOLOG" ] - then - ocf_log err "SAP instance service $SERVICE is not running with status $COLOR !" - fi - rc=$STATE - fi - count=1;; - *);; - esac + SEARCH=`echo "$OCF_RESKEY_MONITOR_SERVICES" | sed 's/\+/\\\+/g' | sed 's/\./\\\./g'` + if [ `echo "$SERVICE" | egrep -c "$SEARCH"` -eq 1 ] + then + if [ $STATE -eq $OCF_NOT_RUNNING ] + then + if [ "$MONLOG" != "NOLOG" ] + then + ocf_log err "SAP instance service $SERVICE is not running with status $COLOR !" + fi + rc=$STATE + fi + count=1 + fi done if [ $count -eq 0 -a $rc -eq $OCF_SUCCESS ] then if [ "$MONLOG" != "NOLOG" ] then ocf_log err "The SAP instance does not run any services which this RA could monitor!" fi rc=$OCF_ERR_ARGS fi fi - + return $rc } # # sapinstance_validate: Check the symantic of the input parameters # sapinstance_validate() { rc=$OCF_SUCCESS if [ `echo "$SID" | grep -c '^[A-Z][A-Z0-9][A-Z0-9]$'` -ne 1 ] then ocf_log err "Parsing instance profile name: '$SID' is not a valid system ID!" rc=$OCF_ERR_ARGS fi if [ `echo "$InstanceName" | grep -c '^[A-Z].*[0-9][0-9]$'` -ne 1 ] then ocf_log err "Parsing instance profile name: '$InstanceName' is not a valid instance name!" rc=$OCF_ERR_ARGS fi if [ `echo "$InstanceNr" | grep -c '^[0-9][0-9]$'` -ne 1 ] then ocf_log err "Parsing instance profile name: '$InstanceNr' is not a valid instance number!" rc=$OCF_ERR_ARGS fi if [ `echo "$SAPVIRHOST" | grep -c '^[A-Za-z][A-Za-z0-9_-]*$'` -ne 1 ] then ocf_log err "Parsing instance profile name: '$SAPVIRHOST' is not a valid hostname!" rc=$OCF_ERR_ARGS fi return $rc } +# +# sapinstance_start_clone +# +sapinstance_start_clone() { + sapinstance_init $OCF_RESKEY_ERS_InstanceName + ${HA_SBIN_DIR}/crm_master -v 100 -l reboot + sapinstance_start + return $? +} + + +# +# sapinstance_stop_clone +# +sapinstance_stop_clone() { + sapinstance_init $OCF_RESKEY_ERS_InstanceName + ${HA_SBIN_DIR}/crm_master -v 0 -l reboot + sapinstance_stop + return $? +} + + +# +# sapinstance_monitor_clone +# +sapinstance_monitor_clone() { + # resource agents running in Master mode must return other returncodes than default + if [ "$OCF_RESKEY_CRM_meta_op_target_rc" -eq $OCF_RUNNING_MASTER ] + then + sapinstance_init $OCF_RESKEY_InstanceName + sapinstance_monitor + case "$?" in + $OCF_SUCCESS) return $OCF_RUNNING_MASTER;; + $OCF_NOT_RUNNING) ${HA_SBIN_DIR}/crm_master -v 10 -l reboot + return $OCF_FAILED_MASTER;; + *) return $?;; + esac + else + sapinstance_init $OCF_RESKEY_ERS_InstanceName + sapinstance_monitor + return $? + fi +} + + +# +# sapinstance_promote_clone: In a Master/Slave configuration get Master by starting the SCS instance and stopping the ERS instance +# The order is important here to behave correct from the application levels view +# +sapinstance_promote_clone() { + sapinstance_init $OCF_RESKEY_InstanceName + ocf_log info "Promoting $SID-$InstanceName to running Master." + sapinstance_start + rc=$? + + if [ $rc -eq $OCF_SUCCESS ]; then + sapinstance_init $OCF_RESKEY_ERS_InstanceName + sapinstance_stop + rc=$? + fi + + return $rc +} + + +# +# sapinstance_demote_clone: In a Master/Slave configuration get Slave by stopping the SCS instance and starting the ERS instance +# +sapinstance_demote_clone() { + sapinstance_init $OCF_RESKEY_InstanceName + ocf_log info "Demoting $SID-$InstanceName to a slave." + sapinstance_stop + rc=$? + + if [ $rc -eq $OCF_SUCCESS ]; then + sapinstance_init $OCF_RESKEY_ERS_InstanceName + sapinstance_start + rc=$? + fi + + return $rc +} + + +# +# sapinstance_notify: After promotion of one master in the cluster, we make sure that all clones reset thier master +# value back to 100. This is because a failed monitor on a master might have degree one clone +# instance to score 10. +# +sapinstance_notify() { + local n_type="$OCF_RESKEY_CRM_meta_notify_type" + local n_op="$OCF_RESKEY_CRM_meta_notify_operation" + + if [ "${n_type}_${n_op}" = "post_promote" ]; then + ${HA_SBIN_DIR}/crm_master -v 100 -l reboot + fi +} + + # # 'main' starts here... # +## GLOBALS +SID="" +sidadm="" +InstanceName="" +InstanceNr="" +SAPVIRHOST="" +DIR_EXECUTABLE="" +SAPSTARTSRV="" +SAPCONTROL="" +DIR_PROFILE="" +SAPSTARTPROFILE="" +CLONE=0 + + if ( [ $# -ne 1 ] ) then - usage + sapinstance_usage exit $OCF_ERR_ARGS fi -# These operations don't require OCF instance parameters to be set -case "$1" in - meta-data) meta_data - exit $OCF_SUCCESS;; - - usage) usage - exit $OCF_SUCCESS;; - - methods) sapinstance_methods - exit $?;; +ACTION=$1 +if [ "$ACTION" = "status" ]; then + ACTION=monitor +fi +# These operations don't require OCF instance parameters to be set +case "$ACTION" in + usage|methods) sapinstance_$ACTION + exit $OCF_SUCCESS;; + meta-data) sapinstance_meta_data + exit $OCF_SUCCESS;; + notify) sapinstance_notify + exit $OCF_SUCCESS;; *);; esac US=`id -u -n` US=`echo $US` if [ $US != root ] then ocf_log err "$0 must be run as root" exit $OCF_ERR_PERM fi # parameter check if [ -z "$OCF_RESKEY_InstanceName" ] then ocf_log err "Please set OCF_RESKEY_InstanceName to the name to the SAP instance profile!" exit $OCF_ERR_ARGS fi -SID=`echo "$OCF_RESKEY_InstanceName" | cut -d_ -f1` -InstanceName=`echo "$OCF_RESKEY_InstanceName" | cut -d_ -f2` -InstanceNr=`echo "$InstanceName" | sed 's/.*\([0-9][0-9]\)$/\1/'` -SAPVIRHOST=`echo "$OCF_RESKEY_InstanceName" | cut -d_ -f3` - -# optional OCF parameters, we try to guess which directories are correct -if [ -z "$OCF_RESKEY_DIR_EXECUTABLE" ] -then - if [ -x /usr/sap/$SID/$InstanceName/exe/sapstartsrv -a -x /usr/sap/$SID/$InstanceName/exe/sapcontrol ] - then - DIR_EXECUTABLE="/usr/sap/$SID/$InstanceName/exe" - SAPSTARTSRV="/usr/sap/$SID/$InstanceName/exe/sapstartsrv" - SAPCONTROL="/usr/sap/$SID/$InstanceName/exe/sapcontrol" - elif [ -x /usr/sap/$SID/SYS/exe/run/sapstartsrv -a -x /usr/sap/$SID/SYS/exe/run/sapcontrol ] - then - DIR_EXECUTABLE="/usr/sap/$SID/SYS/exe/run" - SAPSTARTSRV="/usr/sap/$SID/SYS/exe/run/sapstartsrv" - SAPCONTROL="/usr/sap/$SID/SYS/exe/run/sapcontrol" - else - ocf_log warn "Cannot find sapstartsrv and sapcontrol executable, please set DIR_EXECUTABLE parameter!" - exit $OCF_NOT_RUNNING - fi -else - DIR_EXECUTABLE="$OCF_RESKEY_DIR_EXECUTABLE" - SAPSTARTSRV="$OCF_RESKEY_DIR_EXECUTABLE/sapstartsrv" - SAPCONTROL="$OCF_RESKEY_DIR_EXECUTABLE/sapcontrol" -fi - -if [ -z "$OCF_RESKEY_DIR_PROFILE" ] -then - if [ -d /usr/sap/$SID/SYS/profile/ ] - then - DIR_PROFILE="/usr/sap/$SID/SYS/profile" - else - ocf_log warn "Expected /usr/sap/$SID/SYS/profile/ to be a directory, please set DIR_PROFILE parameter!" - exit $OCF_NOT_RUNNING - fi -else - DIR_PROFILE="$OCF_RESKEY_DIR_PROFILE" -fi - -if [ -z "$OCF_RESKEY_START_PROFILE" ] -then - SAPSTARTPROFILE="$DIR_PROFILE/START_${InstanceName}_${SAPVIRHOST}" - if [ ! -r $SAPSTARTPROFILE ] - then - ocf_log warn "Expected $SAPSTARTPROFILE to be the instance START profile, please set START_PROFILE parameter!" - exit $OCF_NOT_RUNNING - fi -else - SAPSTARTPROFILE="$OCF_RESKEY_START_PROFILE" -fi - -if [ -z "$OCF_RESKEY_START_WAITTIME" ] -then - OCF_RESKEY_START_WAITTIME=3600 -fi - - -if [ -z "$OCF_RESKEY_AUTOMATIC_RECOVER" ] +is_clone; CLONE=$? +if [ ${CLONE} -eq 1 ] then - OCF_RESKEY_AUTOMATIC_RECOVER=0 + CLACT=_clone else - case "$OCF_RESKEY_AUTOMATIC_RECOVER" in - 1|true|TRUE|yes|YES) OCF_RESKEY_AUTOMATIC_RECOVER=1;; - 0|false|FALSE|no|NO) OCF_RESKEY_AUTOMATIC_RECOVER=0;; - esac + sapinstance_init $OCF_RESKEY_InstanceName fi -# as root user we need the library path to the SAP kernel to be able to call sapcontrol -if [ `echo $LD_LIBRARY_PATH | grep -c "^$DIR_EXECUTABLE\>"` -eq 0 ]; then - LD_LIBRARY_PATH=$DIR_EXECUTABLE:$LD_LIBRARY_PATH; export LD_LIBRARY_PATH -fi -sidadm="`echo $SID | tr [:upper:] [:lower:]`adm" - # What kind of method was invoked? -case "$1" in - - start) sapinstance_start - exit $?;; - - recover) sapinstance_recover - exit $?;; - - stop) sapinstance_stop - exit $?;; - - status|monitor) - sapinstance_monitor - exit $?;; - - validate-all) sapinstance_validate - exit $?;; - - *) sapinstance_methods - exit $OCF_ERR_UNIMPLEMENTED;; +case "$ACTION" in + start|stop|monitor|promote|demote) sapinstance_$ACTION$CLACT + exit $?;; + validate-all) sapinstance_validate + exit $?;; + *) sapinstance_methods + exit $OCF_ERR_UNIMPLEMENTED;; esac diff --git a/heartbeat/apache b/heartbeat/apache index 13cb5d481..63c6c2813 100644 --- a/heartbeat/apache +++ b/heartbeat/apache @@ -1,657 +1,657 @@ #!/bin/sh # # High-Availability Apache/IBMhttp control script # # apache (aka IBMhttpd) # # Description: starts/stops apache web servers. # # Author: Alan Robertson # Sun Jiang Dong # # Support: linux-ha@lists.linux-ha.org # # License: GNU General Public License (GPL) # # Copyright: (C) 2002-2005 International Business Machines # # # An example usage in /etc/ha.d/haresources: # node1 10.0.0.170 apache::/opt/IBMHTTPServer/conf/httpd.conf # node1 10.0.0.170 IBMhttpd # # Our parsing of the Apache config files is very rudimentary. # It'll work with lots of different configurations - but not every # possible configuration. # # Patches are being accepted ;-) # # OCF parameters: # OCF_RESKEY_configfile # OCF_RESKEY_httpd # OCF_RESKEY_port # OCF_RESKEY_statusurl # OCF_RESKEY_options # OCF_RESKEY_testregex # OCF_RESKEY_envfiles . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs HA_VARRUNDIR=${HA_VARRUN} ####################################################################### # # Configuration options - usually you don't need to change these # ####################################################################### # IBMHTTPD=/opt/IBMHTTPServer/bin/httpd HTTPDLIST="/sbin/httpd2 /usr/sbin/httpd2 /usr/sbin/apache2 /sbin/httpd /usr/sbin/httpd /usr/sbin/apache $IBMHTTPD" MPM=/usr/share/apache2/find_mpm if [ -x $MPM ] then HTTPDLIST="$HTTPDLIST `$MPM 2>/dev/null`" fi WGETOPTS="-O- -q -L" LOCALHOST="http://localhost" HTTPDOPTS="-DSTATUS" DEFAULT_IBMCONFIG=/opt/IBMHTTPServer/conf/httpd.conf DEFAULT_NORMCONFIG="/etc/apache2/httpd.conf" # # You can also set # HTTPD # PORT # STATUSURL # CONFIGFILE # in this section if what we're doing doesn't work for you... # # End of Configuration options ####################################################################### CMD=`basename $0` # The config-file-pathname is the pathname to the configuration # file for this web server. Various appropriate defaults are # assumed if no config file is specified. If this command is # invoked as *IBM*, then the default config file name is # $DEFAULT_IBMCONFIG, otherwise the default config file # will be $DEFAULT_NORMCONFIG. usage() { cat <<-! usage: $0 action action: start start the web server stop stop the web server status return the status of web server, run or down monitor return TRUE if the web server appears to be working. For this to be supported you must configure mod_status and give it a server-status URL. You have to have installed $WGET for this to work. meta-data show meta data message validate-all validate the instance parameters ! exit $1 } source_envfiles() { for f; do [ -f "$f" -a -r "$f" ] && . "$f" done } apachecat() { awk ' function procline() { split($0,a); if( a[1]=="Include" ) { procinclude(a[2]); } else { if( a[1]=="ServerRoot" ) { rootdir=a[2]; gsub("\"","",rootdir); } print; } } function printfile(infile, a) { while( (getline 0 ) { procline(); } close(infile); } function allfiles(dir, cmd,f) { cmd="find -L "dir" -type f"; while( ( cmd | getline f ) > 0 ) { printfile(f); } close(cmd); } function listfiles(pattern, cmd,f) { cmd="ls "pattern" 2>/dev/null"; while( ( cmd | getline f ) > 0 ) { printfile(f); } close(cmd); } function procinclude(spec) { if( rootdir!="" && spec!~/^\// ) { spec=rootdir"/"spec; } if( isdir(spec) ) { allfiles(spec); # read all files in a directory (and subdirs) } else { listfiles(spec); # there could be jokers } } function isdir(s) { return !system("test -d \""s"\""); } { procline(); } ' $1 | sed 's/#.*//;s/[[:blank:]]*$//;s/^[[:blank:]]*//' | grep -v '^$' } # # set parameters (as shell vars) from our apache config file # get_apache_params() { configfile=$1 shift 1 vars=`echo $@ | sed 's/ /,/g'` eval ` apachecat $configfile | awk -v vars="$vars" ' BEGIN{ split(vars,v,","); for( i in v ) vl[i]=tolower(v[i]); } { for( i in v ) if( tolower($1)==vl[i] ) { print v[i]"="$2 delete vl[i] break } } '` } # # Return the location(s) that are handled by the given handler # FindLocationForHandler() { PerlScript='while (<>) { /"]+)/i && ($loc=$1); '"/SetHandler +$2"'/i && print "$loc\n"; }' apachecat $1 | perl -e "$PerlScript" } # # Check if the port is valid # CheckPort() { ocf_is_decimal "$1" && [ $1 -gt 0 ] } # # Get all the parameters we need from the Apache config file # GetParams() { ConfigFile=$1 if [ ! -f $ConfigFile ]; then return 1 fi get_apache_params $ConfigFile ServerRoot PidFile Port Listen case $PidFile in /*) ;; [[:alnum:]]*) PidFile=$ServerRoot/$PidFile;; *) PidFile=$HA_VARRUNDIR/${httpd_basename}.pid;; esac if CheckPort "$PORT"; then : else PORT=$Port if CheckPort "$PORT"; then : else # Final resort PORT=80 fi fi # # It's difficult to figure out whether the server supports # the status operation. # (we start our server with -DSTATUS - just in case :-)) # # Typically (but not necessarily) the status URL is /server-status # # For us to think status will work, we have to have the following things: # # - $WGET has to exist and be executable # - The server-status handler has to be mapped to some URL somewhere # # We assume that: # # - the "main" web server at $PORT will also support it if we can find it # somewhere in the file # - it will be supported at the same URL as the one we find in the file # # If this doesn't work for you, then set the statusurl attribute. # if [ "X$STATUSURL" = "X" ] then if have_binary $WGET then StatusURL=`FindLocationForHandler $1 server-status | tail -1` if [ "x$Listen" != "x" ] then echo $Listen | grep ':' >/dev/null || # Listen can be only port spec Listen="localhost:$Listen" STATUSURL="http://${Listen}$StatusURL" case $WGET in - *wget*) WGETOPTS="$WGETOPTS --bind-address=127.0.0.1";; + *wget*) WGETOPTS="$WGETOPTS --no-proxy --bind-address=127.0.0.1";; esac else STATUSURL="${LOCALHOST}:${PORT}$StatusURL" fi fi fi test "$PidFile" } # # return TRUE if a process with given PID is running # ProcessRunning() { ApachePID=$1 # Use /proc if it looks like it's here... if [ -d /proc -a -d /proc/1 ] then [ -d /proc/$ApachePID ] else # This assumes we're running as root... kill -0 "$ApachePID" >/dev/null 2>&1 fi } silent_status() { if [ -f $PidFile ] then ProcessRunning `cat $PidFile` else : No pid file false fi } start_apache() { if silent_status then ocf_log info "$CMD already running (pid $ApachePID)" return $OCF_SUCCESS fi ocf_run $HTTPD $HTTPDOPTS $OPTIONS -f $CONFIGFILE tries=0 while : # wait until the user set timeout do monitor_apache ec=$? if [ $ec -eq $OCF_NOT_RUNNING ] then tries=`expr $tries + 1` ocf_log info "waiting for apache $CONFIGFILE to come up" sleep 1 else break fi done return $ec } stop_apache() { if silent_status then if kill $ApachePID then tries=0 while ProcessRunning $ApachePID && [ $tries -lt 10 ] do sleep 1 kill $ApachePID >/dev/null 2>&1 ocf_log info "Killing apache PID $ApachePID" tries=`expr $tries + 1` done else ocf_log warn "Killing apache PID $ApachePID FAILED." fi if ProcessRunning $ApachePID then ocf_log info "$CMD still running ($ApachePID)." false else ocf_log info "$CMD stopped." fi else ocf_log info "$CMD is not running." fi for sig in SIGTERM SIGHUP SIGKILL ; do if pgrep -f $HTTPD.*$CONFIGFILE >/dev/null 2>&1 ; then pkill -$sig -f $HTTPD.*$CONFIGFILE >/dev/null 2>&1 ocf_log info "apache children were signalled ($sig)" sleep 1 else break fi done } status_apache() { silent_status rc=$? if [ $rc -eq 0 ] then ocf_log info "$CMD is running (pid $ApachePID)." return $OCF_SUCCESS else ocf_log info "$CMD is stopped." return $OCF_NOT_RUNNING fi } monitor_apache() { if ! have_binary $WGET then ocf_log err "Monitoring not supported by $OCF_RESOURCE_INSTANCE" ocf_log info "Please make sure that wget is available" return $OCF_ERR_CONFIGURED elif [ -z "$STATUSURL" ]; then ocf_log err "Monitoring not supported by $CONFIGFILE" ocf_log info "Please set the statusurl parameter" return $OCF_ERR_CONFIGURED fi if silent_status then ocf_run sh -c "$WGET $WGETOPTS $STATUSURL | tr '\012' ' ' | grep -Ei \"$TESTREGEX\" >/dev/null" else ocf_log info "$CMD not running" return $OCF_NOT_RUNNING fi } metadata_apache(){ cat < 1.0 This is the resource agent for the Apache web server. Thie resource agent operates both version 1.x and version 2.x Apache servers. The start operation ends with a loop in which monitor is repeatedly called to make sure that the server started and that it is operational. Hence, if the monitor operation does not succeed within the start operation timeout, the apache resource will end with an error status. The monitor operation by default loads the server status page which depends on the mod_status module and the corresponding configuration file (usually /etc/apache2/mod_status.conf). Make sure that the server status page works and that the access is allowed *only* from localhost (address 127.0.0.1). See the statusurl and testregex attributes for more details. See also http://httpd.apache.org/ Apache web server The full pathname of the Apache configuration file. This file is parsed to provide defaults for various other resource agent parameters. configuration file path The full pathname of the httpd binary (optional). httpd binary path A port number that we can probe for status information using the statusurl. This will default to the port number found in the configuration file, or 80, if none can be found in the configuration file. httpd port The URL to monitor (the apache server status page by default). If left unspecified, it will be inferred from the apache configuration file. If you set this, make sure that it succeeds *only* from the localhost (127.0.0.1). Otherwise, it may happen that the cluster complains about the resource being active on multiple nodes. url name Regular expression to match in the output of statusurl. It is case insensitive. monitor regular expression Extra options to apply when starting apache. See man httpd(8). command line options Files (one or more) which contain extra environment variables, such as /etc/apache2/envvars. environment settings files END exit $OCF_SUCCESS } validate_all_apache() { if CheckPort $PORT; then # We are sure to succeed here, since we forced $PORT to be valid in GetParams() : OK else ocf_log err "Port number $PORT is invalid!" exit $OCF_ERR_ARGS fi if [ -z $STATUSURL ]; then : OK to be empty else case $STATUSURL in http://*/*) ;; *) ocf_log err "Invalid STATUSURL $STATUSURL" exit $OCF_ERR_ARGS ;; esac fi if [ ! -x $HTTPD ]; then ocf_log err "HTTPD $HTTPD not found or is not an executable!" exit $OCF_ERR_ARGS fi if [ ! -f $CONFIGFILE ]; then # We are sure to succeed here, since we have parsed $CONFIGFILE before getting here ocf_log err "Configuration file $CONFIGFILE not found!" exit $OCF_ERR_CONFIGURED fi return $OCF_SUCCESS } if [ $# -eq 1 ] then COMMAND=$1 HTTPD="$OCF_RESKEY_httpd" PORT="$OCF_RESKEY_port" STATUSURL="$OCF_RESKEY_statusurl" CONFIGFILE="$OCF_RESKEY_configfile" OPTIONS="$OCF_RESKEY_options" TESTREGEX=${OCF_RESKEY_testregex:-'[[:space:]]*'} source_envfiles $OCF_RESKEY_envfiles else usage $OCF_ERR_ARGS fi LSB_STATUS_STOPPED=3 if [ "X$HTTPD" = X -o ! -f "$HTTPD" -o ! -x "$HTTPD" ] then case $0 in *IBM*) HTTPD=$IBMHTTPD DefaultConfig=$DEFAULT_IBMCONFIG;; *) HTTPD= for h in $HTTPDLIST do if [ -f $h -a -x $h ] then HTTPD=$h break fi done # It is possible that we still do not have a valid httpd at this stage if [ -z "$HTTPD" ] then case $COMMAND in stop) exit $OCF_SUCCESS;; monitor) exit $OCF_NOT_RUNNING;; status) exit $LSB_STATUS_STOPPED;; meta-data) metadata_apache;; esac ocf_log err "No valid httpd found! Please revise your item" exit $OCF_ERR_INSTALLED fi # Let the user know that the $HTTPD used is not the one (s)he specified via $OCF_RESKEY_httpd if [ "X$OCF_RESKEY_httpd" != X ] then ocf_log info "Using $HTTPD as HTTPD" fi DefaultConfig=$DEFAULT_NORMCONFIG;; esac fi httpd_basename=`basename $HTTPD` case $httpd_basename in *-*) httpd_basename=`echo "$httpd_basename" | sed -e 's%\-.*%%'`;; esac case "$CONFIGFILE" in "") CONFIGFILE=$DefaultConfig;; *) ;; esac if [ ! -f "$CONFIGFILE" ] then case $COMMAND in stop) ocf_log warn "$CONFIGFILE not found - apache considered stopped" exit $OCF_SUCCESS;; monitor) exit $OCF_NOT_RUNNING;; status) exit $LSB_STATUS_STOPPED;; esac fi if [ "X$COMMAND" = Xmeta-data ] || GetParams $CONFIGFILE then : OK else ocf_log err "Cannot parse config file [$CONFIGFILE]" exit $OCF_ERR_CONFIGURED fi case $COMMAND in start) start_apache;; stop) stop_apache;; status) status_apache;; monitor) monitor_apache;; meta-data) metadata_apache;; validate-all) validate_all_apache;; *) usage $OCF_ERR_UNIMPLEMENTED;; esac diff --git a/heartbeat/drbd b/heartbeat/drbd index e0b2afabb..fc204637d 100644 --- a/heartbeat/drbd +++ b/heartbeat/drbd @@ -1,585 +1,586 @@ #!/bin/bash # # # OCF Resource Agent compliant drbd resource script. # # Copyright (c) 2004 - 2007 SUSE LINUX Products GmbH, Lars Marowsky-Bree # 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. # # # OCF instance parameters # OCF_RESKEY_drbd_resource # OCF_RESKEY_drbdconf ####################################################################### # Initialization: if [ -n "$OCF_DEBUG_LIBRARY" ]; then . $OCF_DEBUG_LIBRARY else . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs fi ####################################################################### meta_data() { cat < 1.1 Master/Slave OCF Resource Agent for DRBD This resource agent manages a Distributed Replicated Block Device (DRBD) object as a master/slave resource. DRBD is a mechanism for replicating storage; please see the documentation for setup details. The name of the drbd resource from the drbd.conf file. drbd resource name Full path to the drbd.conf file. Path to drbd.conf Whether or not to override the hostname with the clone number. This can be used to create floating peer configurations; drbd will be told to use node_<cloneno> as the hostname instead of the real uname, which can then be used in drbd.conf. Override drbd hostname + END exit $OCF_SUCCESS } do_cmd() { local cmd="$*" ocf_log debug "$RESOURCE: Calling $cmd" local cmd_out cmd_out=$($cmd 2>&1) ret=$? if [ $ret -ne 0 ]; then ocf_log err "$RESOURCE: Called $cmd" ocf_log err "$RESOURCE: Exit code $ret" ocf_log err "$RESOURCE: Command output: $cmd_out" else ocf_log debug "$RESOURCE: Exit code $ret" ocf_log debug "$RESOURCE: Command output: $cmd_out" fi echo $cmd_out return $ret } do_drbdadm() { local cmd="$DRBDADM -c $DRBDCONF $*" ocf_log debug "$RESOURCE: Calling $cmd" local cmd_out cmd_out=$($cmd 2>&1) ret=$? # Trim the garbage drbdadm likes to print when using the node # override feature: local cmd_ret=$(echo $cmd_out | sed -e 's/found __DRBD_NODE__.*</dev/null ; then : else ocf_log err "Invalid configuration file $DRBDCONF" return $OCF_ERR_CONFIGURED fi if [ "$OCF_RESKEY_CRM_meta_clone_max" -ne 2 ] \ || [ "$OCF_RESKEY_CRM_meta_clone_node_max" -ne 1 ] \ || [ "$OCF_RESKEY_CRM_meta_master_node_max" -ne 1 ] \ || [ "$OCF_RESKEY_CRM_meta_master_max" -ne 1 ] ; then ocf_log err "Clone options misconfigured." exit $OCF_ERR_CONFIGURED fi return $OCF_SUCCESS } if [ $# -ne 1 ]; then echo "Incorrect parameter count." drbd_usage exit $OCF_ERR_ARGS fi : ${OCF_RESKEY_CRM_meta_interval=0} ACTION=$1 case $ACTION in meta-data) meta_data ;; validate-all) drbd_init drbd_validate_all ;; start|stop|monitor|promote|demote|notify) if ocf_is_root ; then : ; else ocf_log err "You must be root to perform this operation." exit $OCF_ERR_PERM fi drbd_init drbd_$ACTION exit $? ;; usage|help) drbd_usage exit $OCF_SUCCESS ;; *) drbd_usage exit $OCF_ERR_ARGS ;; esac diff --git a/heartbeat/iSCSILogicalUnit b/heartbeat/iSCSILogicalUnit new file mode 100644 index 000000000..4ad55f5b6 --- /dev/null +++ b/heartbeat/iSCSILogicalUnit @@ -0,0 +1,452 @@ +#!/bin/bash +# +# +# iSCSILogicalUnit OCF RA. Exports and manages iSCSI Logical Units. +# +# 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_ROOT}/resource.d/heartbeat/.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" +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, so take the first 24 bytes of +# $OCF_RESOURCE_INSTANCE +OCF_RESKEY_scsi_id_default="${OCF_RESOURCE_INSTANCE:0:24}" +: ${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 < + + +0.9 + + +Manages iSCSI targets. An iSCSI target is a collection of SCSI Logical +Units (LUs) exported via a daemon that speaks the iSCSI protocol. + +iSCSI target export agent + + + + +The iSCSI target daemon implementation. Must be one of "iet" or "tgt". +If unspecified, an implementation is selected based on the +availability of management utilities, with "iet" being tried first, +then "tgt". + +iSCSI target daemon implementation + + + + + +The iSCSI Qualified Name (IQN) that this Logical Unit belongs to. + +iSCSI target IQN + + + + + +The Logical Unit number (LUN) exposed to initiators. + +Logical Unit number (LUN) + + + + + +The path to the block device exposed. Some implementations allow this +to be a regular file, too. + +Block device (or file) path + + + + + +The SCSI ID to be configured for this Logical Unit. The default +is the resource name, truncated to 24 bytes. + +SCSI ID + + + + + +The SCSI serial number to be configured for this Logical Unit. +The default is a hash of the resource name, truncated to 8 bytes. + +SCSI serial number + + + + + +The SCSI vendor ID to be configured for this Logical Unit. + +SCSI vendor ID + + + + + +The SCSI product ID to be configured for this Logical Unit. + +SCSI product ID + + + + + +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. + +List of iSCSI LU parameters + + + + + + + + + + + + + +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 < + + +0.9 + + +Manages iSCSI targets. An iSCSI target is a collection of SCSI Logical +Units (LUs) exported via a daemon that speaks the iSCSI protocol. + +iSCSI target export agent + + + + +The iSCSI target daemon implementation. Must be one of "iet" or "tgt". +If unspecified, an implementation is selected based on the +availability of management utilities, with "iet" being tried first, +then "tgt". + +iSCSI target daemon implementation + + + + + +The target iSCSI Qualified Name (IQN). Should follow the conventional +"iqn.yyyy-mm.<reversed domain name>[:identifier]" syntax. + +iSCSI target IQN + + + + + +The iSCSI target ID. Required for tgt. + +iSCSI target ID + + + + + +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. + +List of iSCSI initiators allowed to connect +to this target + + + + + +A username used for incoming initiator authentication. If unspecified, +allowed initiators will be able to log in without authentication. + +Incoming account username + + + + + +A password used for incoming initiator authentication. + +Incoming account password + + + + + +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. + +List of iSCSI target parameters + + + + + + + + + + + + + +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 <> /etc/initiators.deny + echo "${OCF_RESKEY_iqn} ${OCF_RESKEY_allowed_initiators// /,}" >> /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 + ;; + 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 + do_cmd ietadm --op delete \ + --tid=${tid} || return $OCF_ERR_GENERIC + # 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 + do_cmd tgtadm --lld iscsi --op delete --mode target \ + --tid=${tid} && return $OCF_SUCCESS + # In tgt, we don't have to worry about our ACL + # entries. They are automatically removed upon target + # deletion. + ;; + 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 + ;; + 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 + ;; + *) + # 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 + ;; + esac + + 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/iscsi b/heartbeat/iscsi index 725771c37..259681715 100755 --- a/heartbeat/iscsi +++ b/heartbeat/iscsi @@ -1,376 +1,376 @@ #!/bin/sh # # iSCSI OCF resource agent # Description: manage iSCSI disks (add/remove) using open-iscsi # # Copyright Dejan Muhamedagic # (C) 2007 Novell Inc. 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. # # See usage() and meta_data() below for more details... # # OCF instance parameters: # OCF_RESKEY_portal: the iSCSI portal address or host name (required) # OCF_RESKEY_target: the iSCSI target (required) # OCF_RESKEY_iscsiadm: iscsiadm program path (optional) # OCF_RESKEY_discovery_type: discovery type (optional; default: sendtargets) # # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs usage() { methods=`iscsi_methods` methods=`echo $methods | tr ' ' '|'` cat <<-! usage: $0 {$methods} $0 manages an iSCSI target The 'start' operation starts (adds) the iSCSI target. The 'stop' operation stops (removes) the iSCSI target. The 'status' operation reports whether the iSCSI target is connected The 'monitor' operation reports whether the iSCSI target is connected The 'validate-all' operation reports whether the parameters are valid The 'methods' operation reports on the methods $0 supports ! } meta_data() { cat < 1.0 OCF Resource Agent for iSCSI. Add (start) or remove (stop) iSCSI targets. iscsi resource agent The iSCSI portal address in the form: {ip_address|hostname}[":"port] portal The iSCSI target. target Discovery type. Currently, with open-iscsi, only the sendtargets type is supported. discovery_type iscsiadm program path. iscsiadm If the next resource depends on the udev creating a device then we wait until it is finished. On a normally loaded host this should be done quickly, but you may be unlucky. If you are not using udev set this to "no", otherwise we will spin in a loop until a timeout occurs. udev END } iscsi_methods() { cat <<-! start stop status monitor validate-all methods meta-data usage ! } # # open-iscsi interface # open_iscsi_daemon() { if ps -e -o cmd | grep -qs '[i]scsid'; then return 0 else ocf_log err "iscsid not running; please start open-iscsi utilities" return 1 fi } open_iscsi_setup() { discovery=open_iscsi_discovery add_disk=open_iscsi_add remove_disk=open_iscsi_remove disk_status=open_iscsi_status iscsiadm=${OCF_RESKEY_iscsiadm:-"iscsiadm"} which $iscsiadm >/dev/null || { ocf_log err "iscsiadm not available; please set OCF_RESKEY_iscsiadm" return $OCF_ERR_INSTALLED } open_iscsi_daemon || return $OCF_ERR_INSTALLED } # # discovery return codes: # 0: ok (variable portal set) # 1: target not found # 2: target found but can't connect it unambigously # 3: iscsiadm returned error open_iscsi_discovery() { output=`$iscsiadm -m discovery -p $OCF_RESKEY_portal -t $discovery_type` if [ $? -ne 0 -o x = "x$output" ]; then [ x != "x$output" ] && echo "$output" return 3 fi portal=`echo "$output" | awk -v target="$OCF_RESKEY_target" ' $NF==target{ if( NF==3 ) portal=$2; # sles compat mode else portal=$1; sub(",.*","",portal); print portal; }'` case `echo "$portal" | wc -w` in 0) #target not found echo "$output" ocf_log err "target $OCF_RESKEY_target not found at portal $OCF_RESKEY_portal" return 1 ;; 1) #we're ok return 0 ;; *) # handle multihome hosts reporting multiple portals for p in $portal; do if [ "$OCF_RESKEY_portal" = "$p" ]; then portal="$OCF_RESKEY_portal" return 0 fi done echo "$output" ocf_log err "sorry, can't handle multihomed hosts unless you specify the portal exactly" return 2 ;; esac } open_iscsi_add() { $iscsiadm -m node -p $1 -T $2 -l } open_iscsi_remove() { $iscsiadm -m node -p $1 -T $2 -u } open_iscsi_status() { $iscsiadm -m session 2>/dev/null | grep -qs "$2$" } # # NB: this is udev specific! # wait_for_udev() { dev=/dev/disk/by-path/ip-$portal-iscsi-$OCF_RESKEY_target while :; do ls $dev* >/dev/null 2>&1 && break ocf_log warning "waiting for udev to create $dev" sleep 1 done } iscsi_status() { if $disk_status $portal $OCF_RESKEY_target; then return $OCF_SUCCESS else return $OCF_NOT_RUNNING fi } iscsi_start() { if iscsi_status; then ocf_log info "iscsi $portal $OCF_RESKEY_target already running" return $OCF_SUCCESS else $add_disk $portal $OCF_RESKEY_target || return $OCF_ERR_GENERIC - case "$OCF_RESKEY_udev" in + case "$udev" in [Yy]es) wait_for_udev || return $OCF_ERR_GENERIC ;; *) ;; esac if iscsi_status; then return $OCF_SUCCESS else return $OCF_ERR_GENERIC fi fi } iscsi_stop() { if iscsi_status; then $remove_disk $portal $OCF_RESKEY_target || return $OCF_ERR_GENERIC if iscsi_status; then return $OCF_ERR_GENERIC else return $OCF_SUCCESS fi else ocf_log info "iscsi $portal $OCF_RESKEY_target already stopped" return $OCF_SUCCESS fi } iscsi_monitor() { if $disk_status $portal $OCF_RESKEY_target; then return $OCF_SUCCESS else return $OCF_NOT_RUNNING fi } # # 'main' starts here... # if [ $# -ne 1 ]; then usage exit $OCF_ERR_ARGS fi # These operations don't require OCF instance parameters to be set case "$1" in meta-data) meta_data exit $OCF_SUCCESS;; usage) usage exit $OCF_SUCCESS;; methods) iscsi_methods exit $OCF_SUCCESS;; esac if [ x = "x$OCF_RESKEY_target" ]; then ocf_log err "no target attribute" exit $OCF_ERR_ARGS fi case `uname` in Linux) setup=open_iscsi_setup ;; *) ocf_log info "platform `uname` may not be supported" setup=open_iscsi_setup ;; esac LSB_STATUS_STOPPED=3 $setup rc=$? if [ $rc -ne 0 ]; then ocf_log info "iscsi initiator utilities not installed or not setup" case "$1" in stop) exit $OCF_SUCCESS;; monitor) exit $OCF_NOT_RUNNING;; status) exit $LSB_STATUS_STOPPED;; *) exit $rc;; esac fi if [ `id -u` != 0 ]; then ocf_log err "$0 must be run as root" exit $OCF_ERR_PERM fi discovery_type=${OCF_RESKEY_discovery_type:-"sendtargets"} udev=${OCF_RESKEY_udev:-"yes"} $discovery # discover and setup the real portal string (address) case $? in 0) ;; 1) [ "$1" = stop ] && exit $OCF_SUCCESS [ "$1" = monitor ] && exit $OCF_NOT_RUNNING [ "$1" = status ] && exit $LSB_STATUS_STOPPED exit $OCF_ERR_GENERIC ;; [23]) exit $OCF_ERR_GENERIC;; esac # which method was invoked? case "$1" in start) iscsi_start ;; stop) iscsi_stop ;; status) if iscsi_status then echo iscsi target $OCF_RESKEY_target running exit $OCF_SUCCESS else echo iscsi target $OCF_RESKEY_target stopped exit $OCF_NOT_RUNNING fi ;; monitor) iscsi_status ;; validate-all) # everything already validated # just exit successfully here. exit $OCF_SUCCESS;; *) iscsi_methods exit $OCF_ERR_UNIMPLEMENTED;; esac # # vim:tabstop=4:shiftwidth=4:textwidth=0:wrapmargin=0 diff --git a/heartbeat/mysql-proxy b/heartbeat/mysql-proxy index a435d64de..852023ff4 100644 --- a/heartbeat/mysql-proxy +++ b/heartbeat/mysql-proxy @@ -1,366 +1,365 @@ #!/bin/sh # # Resource script for MySQL Proxy # # Description: Manages MySQL Proxy as an OCF resource in # an high-availability setup. # # Tested with mysql-proxy 0.7.0 on Debian 5.0. # Based on the mysql and Pure-Ftpd OCF resource agents. # # Author: Raoul Bhatia : Original Author # License: GNU General Public License (GPL) # # -# usage: $0 {start|stop|status|monitor|validate-all|meta-data} +# usage: $0 {start|stop|reload|status|monitor|validate-all|meta-data} # # The "start" arg starts a MySQL Proxy instance # # The "stop" arg stops it. # # TODO # * add error checking like in mysql ocf ra (e.g. socketdir) # * verify if mysql-proxy supports multiple --proxy-address(es) # # Test via # */usr/sbin/ocf-tester -n mp /usr/lib/ocf/resource.d/heartbeat/mysql-proxy # */usr/sbin/ocf-tester -n ms -o binary="/usr/sbin/mysql-proxy" -o defaults_file="" -o parameters="--proxy-skip-profiling" \ # -o admin_address="127.0.0.1:4041" -o proxy_backend_addresses="192.168.100.200:42006" \ # -o proxy_address="/var/run/mysqld/mysqld.sock" /usr/lib/ocf/resource.d/heartbeat/mysql-proxy # # * adding two mysql-instances (mysql-proxy-tcp and mysql-proxy-socket) and killing mysql-proxy-tcp # beware, that as of mysql-proxy 0.7.0 (and possibly later), the socket is not automatically removed # # # OCF parameters: # OCF_RESKEY_binary # OCF_RESKEY_defaults_file # OCF_RESKEY_proxy_backend_addresses # OCF_RESKEY_proxy_read_only_backend_addresses # OCF_RESKEY_proxy_address # OCF_RESKEY_admin_address # OCF_RESKEY_parameters # OCF_RESKEY_pidfile # ########################################################################## # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs : ${OCF_RESKEY_binary="/usr/sbin/mysql-proxy"} : ${OCF_RESKEY_defaults_file=""} : ${OCF_RESKEY_proxy_backend_addresses="127.0.0.1:3306"} : ${OCF_RESKEY_proxy_read_only_backend_addresses=""} : ${OCF_RESKEY_proxy_address=":4040"} : ${OCF_RESKEY_admin_address="127.0.0.1:4041"} : ${OCF_RESKEY_parameters=""} : ${OCF_RESKEY_pidfile="${HA_RSCTMP}/mysql-proxy-${OCF_RESOURCE_INSTANCE}.pid"} USAGE="Usage: $0 {start|stop|reload|status|monitor|validate-all|meta-data}"; ########################################################################## usage() { echo $USAGE >&2 } meta_data() { cat < 0.1 This script manages MySQL Proxy as an OCF resource in a high-availability setup. Tested with MySQL Proxy 0.7.0 on Debian 5.0. OCF Resource Agent compliant MySQL Proxy script. Full path to the MySQL Proxy binary. For example, "/usr/sbin/mysql-proxy". Full path to MySQL Proxy binary Full path to a MySQL Proxy configuration file. For example, "/etc/mysql-proxy.conf". Full path to configuration file Address:port of the remote backend-servers (default: 127.0.0.1:3306). Address:port of the remote slave-server (default: not set). Listening address:port of the proxy-server (default: :4040). You can also specify a socket like "/tmp/mysql-proxy.sock". Listening address:port of the admin-server (default: 127.0.0.1:4041). The MySQL Proxy daemon may be called with additional parameters. Specify any of them here. PID file PID file END exit $OCF_SUCCESS } isRunning() { kill -0 "$1" 2>/dev/null } mysqlproxy_status() { if [ -f "${pidfile}" ]; then # MySQL Proxy is probably running PID=`head -n 1 "${pidfile}"` if [ ! -z "$PID" ] ; then isRunning "$PID" return $? fi fi # MySQL Proxy is not running false } mysqlproxy_start() { # if MySQL Proxy is running return success if mysqlproxy_status ; then ocf_log info "MySQL Proxy already running." exit $OCF_SUCCESS fi # check that the MySQL Proxy binary exists and can be executed if [ ! -x "$binary" ]; then ocf_log err "MySQL Proxy binary '$binary' does not exist or cannot be executed." exit $OCF_ERR_GENERIC fi # check if the MySQL Proxy defaults-file exist PARAM_PREFIX='' if [ -f "$defaults_file" ]; then PARAM_PREFIX="--defaults-file=$defaults_file " fi # split multiple proxy-address options. # currently unsupported but let us hope for the future ;) for pa in $proxy_address; do [ -z "$pa" ] && continue OPTIONS=" $OPTIONS --proxy-address=$pa" done # split multiple proxy-backend-addresses options. for pba in $proxy_backend_addresses; do [ -z "$pba" ] && continue OPTIONS=" $OPTIONS --proxy-backend-addresses=$pba" done # split multiple proxy-backend-addresses options. for proba in $proxy_read_only_backend_addresses; do [ -z "$proba" ] && continue OPTIONS=" $OPTIONS --proxy-read-only-backend-addresses=$proba" done # build $OPTIONS and add addmin-address and pidfile OPTIONS="$PARAM_PREFIX $OPTIONS --admin-address=$admin_address --pid-file=${pidfile} $PARAM_SUFFIX" # start MySQL Proxy #start-stop-daemon --start --quiet --pidfile $pidfile --make-pidfile --name mysql-proxy --startas $binary -b -- $OPTIONS $binary --daemon $OPTIONS ret=$? if [ $ret -ne 0 ]; then ocf_log err "MySQL Proxy returned error." $ret exit $OCF_ERR_GENERIC fi exit $OCF_SUCCESS } mysqlproxy_stop() { - LA=`mysqlproxy_status` if mysqlproxy_status ; then #start-stop-daemon --stop --quiet --retry 3 --exec $binary --pidfile $pidfile /bin/kill `cat "${pidfile}"` ret=$? if [ $ret -ne 0 ]; then ocf_log err "MySQL Proxy returned an error while stopping." $ret exit $OCF_ERR_GENERIC fi # grant some time for shutdown and recheck sleep 1 if mysqlproxy_status ; then ocf_log err "MySQL Proxy failed to stop." exit $OCF_ERR_GENERIC fi # remove dangling socketfile, if specified for pa in $proxy_address; do if [ -S "$pa" ]; then ocf_log info "Removing dangling socket file '$pa'." rm -f "$pa" fi done # remove dangling pidfile if [ -f "${pidfile}" ]; then ocf_log info "Removing dangling pidfile '${pidfile}'." rm -f "${pidfile}" fi fi exit $OCF_SUCCESS } mysqlproxy_reload() { if mysqlproxy_status; then ocf_log info "Reloading MySQL Proxy." kill -HUP `cat ${pidfile}` fi } mysqlproxy_monitor() { if mysqlproxy_status ; then return $OCF_SUCCESS fi return $OCF_NOT_RUNNING } mysqlproxy_validate_all() { # @TODO return $OCF_SUCCESS } # # Main # if [ $# -ne 1 ]; then usage exit $OCF_ERR_ARGS fi pidfile=$OCF_RESKEY_pidfile binary=$OCF_RESKEY_binary defaults_file=$OCF_RESKEY_defaults_file proxy_backend_addresses=$OCF_RESKEY_proxy_backend_addresses proxy_read_only_backend_addresses=$OCF_RESKEY_proxy_read_only_backend_addresses admin_address=$OCF_RESKEY_admin_address proxy_address=$OCF_RESKEY_proxy_address # debugging stuff #echo OCF_RESKEY_binary=$OCF_RESKEY_binary >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE #echo OCF_RESKEY_defaults_file=$OCF_RESKEY_defaults_file >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE #echo OCF_RESKEY_proxy_backend_addresses=$OCF_RESKEY_proxy_backend_addresses >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE #echo OCF_RESKEY_proxy_read_only_backend_addresses=$OCF_RESKEY_proxy_read_only_backend_addresses >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE #echo OCF_RESKEY_proxy_address=$OCF_RESKEY_proxy_address >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE #echo OCF_RESKEY_admin_address=$OCF_RESKEY_admin_address >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE #echo OCF_RESKEY_parameters=$OCF_RESKEY_parameters >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE #echo OCF_RESKEY_pidfile=$OCF_RESKEY_pidfile >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE case $1 in start) mysqlproxy_start ;; stop) mysqlproxy_stop ;; reload) mysqlproxy_reload ;; status) if mysqlproxy_status; then ocf_log info "MySQL Proxy is running." exit $OCF_SUCCESS else ocf_log info "MySQL Proxy is stopped." exit $OCF_NOT_RUNNING fi ;; monitor) mysqlproxy_monitor exit $? ;; validate-all) mysqlproxy_validate_all exit $? ;; meta-data) meta_data ;; usage) usage exit $OCF_SUCCESS ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac diff --git a/heartbeat/pgsql b/heartbeat/pgsql index 089ab1bac..dc79b947e 100644 --- a/heartbeat/pgsql +++ b/heartbeat/pgsql @@ -1,423 +1,452 @@ #!/bin/sh # # Description: Manages a PostrgreSQL Server as an OCF High-Availability # resource under Heartbeat/LinuxHA control # # # Author: Serge Dubrouski (sergeyfd@gmail.com) # Copyright 2006 Serge Dubrouski # License: GNU General Public License (GPL) # # OCF parameters: # OCF_RESKEY_pgctl - Path to pg_ctl. Default /usr/bin/pg_ctl # OCF_RESKEY_start_opt - Startup options, options passed to postgress with -o # OCF_RESKEY_ctl_opt - Additional options for pg_ctl (-w, -W etc...) # OCF_RESKEY_psql - Path to psql. Default is /usr/bin/psql # OCF_RESKEY_pgdata - PGDATA directory. Default is /var/lib/pgsql/data # OCF_RESKEY_pgdba - userID that manages DB. Default is postgres # OCF_RESKEY_pghost - Host/IP Address where PostgreSQL is listening # OCF_RESKEY_pgport - Port where PostgreSQL is listening # OCF_RESKEY_pgdb - database to monitor. Default is template1 # OCF_RESKEY_logfile - Path to PostgreSQL log file. Default is /dev/null # OCF_RESKEY_stop_escalate - Stop waiting time. Default is 30 ############################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs unset LC_ALL; export LC_ALL unset LANGUAGE; export LANGUAGE usage() { cat <<-! >&1 usage: $0 start|stop|status|monitor|meta-data|validate-all|methods $0 manages a PostgreSQL Server as an HA resource. The 'start' operation starts the PostgreSQL server. The 'stop' operation stops the PostgreSQL server. The 'status' operation reports whether the PostgreSQL is up. The 'monitor' operation reports whether the PostgreSQL is running. The 'validate-all' operation reports whether the parameters are valid. The 'methods' operation reports on the methods $0 supports. ! return $OCF_ERR_ARGS } meta_data() { cat < 1.0 Resource script for PostgreSQL. It manages a PostgreSQL as an HA resource. pgsql resource agent Path to pg_ctl command. pgctl Start options (-o start_opt in pgi_ctl). "-i -p 5432" for example. start_opt Additional pg_ctl options (-w, -W etc..). Default is "" ctl_opt Path to psql command. psql Path PostgreSQL data directory. pgdata User that owns PostgreSQL. pgdba Hostname/IP Addreess where PosrgeSQL is listening pghost Port where PosrgeSQL is listening pgport Database that will be used for monitoring. pgdb Path to PostgreSQL server log output file. logfile Number of retries (using -m fast) before resorting to -m immediate stop escalation END } # # Run the given command in the Resource owner environment... # runasowner() { su $OCF_RESKEY_pgdba -c "$*" } # # methods: What methods/operations do we support? # pgsql_methods() { cat <<-! start stop status monitor methods meta-data validate-all ! } #pgsql_start: Starts PostgreSQL pgsql_start() { if pgsql_status then ocf_log info "PostgreSQL is already running. PID=`cat $PIDFILE`" return $OCF_SUCCESS fi if [ -x $OCF_RESKEY_pgctl ] then # Remove postmastre.pid if it exists rm -f $PIDFILE # Check if we need to create a log file if ! check_log_file $OCF_RESKEY_logfile then ocf_log err "PostgreSQL can't write to the log file: $OCF_RESKEY_logfile" return $OCF_ERR_GENERIC fi - if runasowner "$OCF_RESKEY_pgctl $OCF_RESKEY_ctl_opt -D $OCF_RESKEY_pgdata -l $OCF_RESKEY_logfile -o "\'$OCF_RESKEY_start_opt\'" start > /dev/null 2>&1" + output=`runasowner "$OCF_RESKEY_pgctl $OCF_RESKEY_ctl_opt -D $OCF_RESKEY_pgdata -l $OCF_RESKEY_logfile -o "\'$OCF_RESKEY_start_opt\'" start" 2>&1` + + if [ $? -eq 0 ] then # Probably started..... ocf_log info "PostgreSQL start command sent." else - ocf_log err "Can't start PostgreSQL."; return $OCF_ERR_GENERIC + ocf_log err "Can't start PostgreSQL." + echo "$output" + return $OCF_ERR_GENERIC fi else ocf_log err "$OCF_RESKEY_pgctl not found!" return $OCF_ERR_GENERIC fi - rc=$OCF_ERR_GENERIC - while [ $rc -ne 0 ]; do - pgsql_monitor - rc=$? + while : + do + pgsql_monitor warn + rc=$? + if [ $rc -eq 0 ]; then + break; + fi sleep 1 ocf_log debug "PostgreSQL still hasn't started yet. Waiting..." done + ocf_log info "PostgreSQL is started." return $OCF_SUCCESS } #pgsql_stop: Stop PostgreSQL pgsql_stop() { if ! pgsql_status then #Already stopped return $OCF_SUCCESS fi - + # Stop PostgreSQL do not wait for clients to disconnect - runasowner "$OCF_RESKEY_pgctl -D $OCF_RESKEY_pgdata stop -m fast > /dev/null 2>&1" + output=`runasowner "$OCF_RESKEY_pgctl -D $OCF_RESKEY_pgdata stop -m fast" 2>&1` # stop waiting count=0 while [ $count -lt $OCF_RESKEY_stop_escalate ] do if ! pgsql_status then #PostgreSQL stopped break; fi count=`expr $count + 1` sleep 1 done if pgsql_status then #PostgreSQL is still up. Use another shutdown mode. ocf_log info "PostgreSQL failed to stop after ${OCF_RESKEY_stop_escalate}s using -m fast. Trying -m immediate..." - runasowner "$OCF_RESKEY_pgctl -D $OCF_RESKEY_pgdata stop -m immediate > /dev/null 2>&1" + echo "$output" + output=`runasowner "$OCF_RESKEY_pgctl -D $OCF_RESKEY_pgdata stop -m immediate" 2>&1` + echo "$output" fi - rc=$OCF_ERR_GENERIC - while [ $rc != $OCF_NOT_RUNNING ]; do + while : + do pgsql_monitor - rc=$? + rc=$? + if [ $rc -eq $OCF_NOT_RUNNING ]; then + # An unnecessary debug log is prevented. + break; + fi sleep 1 ocf_log debug "PostgreSQL still hasn't stopped yet. Waiting..." done # Remove postmastre.pid if it exists rm -f $PIDFILE return $OCF_SUCCESS } # # pgsql_status: is PostgreSQL up? # pgsql_status() { if [ -f $PIDFILE ] then PID=`head -n 1 $PIDFILE` kill -0 $PID >/dev/null 2>&1 && fuser $OCF_RESKEY_pgdata 2>&1 | grep $PID >/dev/null 2>&1 return $? fi # No PID file false } # # pgsql_monitor # pgsql_monitor() { + # Set the log level of the error message + loglevel=${1:-err} + if ! pgsql_status then ocf_log info "PostgreSQL is down" return $OCF_NOT_RUNNING fi if [ "x" = "x$OCF_RESKEY_pghost" ] then - runasowner "$OCF_RESKEY_psql -p $OCF_RESKEY_pgport -U $OCF_RESKEY_pgdba $OCF_RESKEY_pgdb -c 'select now();' >/dev/null 2>&1" + output=`runasowner "$OCF_RESKEY_psql -p $OCF_RESKEY_pgport -U $OCF_RESKEY_pgdba $OCF_RESKEY_pgdb -c 'select now();'" 2>&1` else - runasowner "$OCF_RESKEY_psql -h $OCF_RESKEY_pghost -p $OCF_RESKEY_pgport -U $OCF_RESKEY_pgdba $OCF_RESKEY_pgdb -c 'select now();' >/dev/null 2>&1" + output=`runasowner "$OCF_RESKEY_psql -h $OCF_RESKEY_pghost -p $OCF_RESKEY_pgport -U $OCF_RESKEY_pgdba $OCF_RESKEY_pgdb -c 'select now();'" 2>&1` fi - if [ $? -ne 0 ] + rc=$? + if [ $rc -ne 0 ] then - ocf_log err "PostgreSQL $OCF_RESKEY_pgdb isn't running" + ocf_log $loglevel "PostgreSQL $OCF_RESKEY_pgdb isn't running" + if [ $rc -eq 1 ] + then + ocf_log err "Fatal error(out of memory or file not found, etc.) occurred while executing the psql command." + elif [ $rc -eq 2 ] + then + ocf_log $loglevel "Connection error(connection to the server went bad and the session was not interactive) occurred while executing the psql command." + elif [ $rc -eq 3 ] + then + ocf_log err "Script error(the variable ON_ERROR_STOP was set) occurred while executing the psql command." + fi + echo "$output" return $OCF_ERR_GENERIC fi return $OCF_SUCCESS } # Validate most critical parameters pgsql_validate_all() { if ! have_binary $SH then return $OCF_ERR_INSTALLED fi if ! have_binary $OCF_RESKEY_pgctl then return $OCF_ERR_INSTALLED fi if ! have_binary $OCF_RESKEY_psql then return $OCF_ERR_INSTALLED fi return $OCF_SUCCESS } # # Check if we need to create a log file # check_log_file() { if [ ! -f "$1" ] then touch $1 > /dev/null 2>&1 chown $OCF_RESKEY_pgdba:$(getent passwd $OCF_RESKEY_pgdba | cut -d ":" -f 4) $1 fi #Check if $OCF_RESKEY_pgdba can write to the log file if ! runasowner "test -w $1" then return 1 fi return 0 } # # 'main' starts here... # if [ $# -ne 1 ] then usage exit $OCF_ERR_GENERIC fi : ${OCF_RESKEY_pgctl=/usr/bin/pg_ctl} : ${OCF_RESKEY_psql=/usr/bin/psql} : ${OCF_RESKEY_pgdata=/var/lib/pgsql/data} : ${OCF_RESKEY_pgdba=postgres} : ${OCF_RESKEY_pgport=5432} : ${OCF_RESKEY_start_opt="-p $OCF_RESKEY_pgport"} : ${OCF_RESKEY_pgdb=template1} : ${OCF_RESKEY_logfile=/dev/null} : ${OCF_RESKEY_stop_escalate=30} PIDFILE=${OCF_RESKEY_pgdata}/postmaster.pid case "$1" in methods) pgsql_methods exit $?;; meta-data) meta_data exit $OCF_SUCCESS;; validate-all) pgsql_validate_all exit $?;; esac if ! pgsql_validate_all then case "$1" in stop) exit $OCF_SUCCESS;; monitor) exit $OCF_NOT_RUNNING;; status) exit $OCF_NOT_RUNNING;; *) exit $OCF_ERR_INSTALLED;; esac fi US=`id -u -n` if [ $US != root -a $US != $OCF_RESKEY_pgdba ] then ocf_log err "$0 must be run as root or $OCF_RESKEY_pgdba" exit $OCF_ERR_GENERIC fi # What kind of method was invoked? case "$1" in status) if pgsql_status then ocf_log info "PostgreSQL is up" exit $OCF_SUCCESS else ocf_log info "PostgreSQL is down" exit $OCF_NOT_RUNNING fi;; monitor) pgsql_monitor exit $?;; start) pgsql_start exit $?;; stop) pgsql_stop exit $?;; *) exit $OCF_ERR_UNIMPLEMENTED;; esac diff --git a/heartbeat/portblock b/heartbeat/portblock index ff6cdc2e3..3702d26da 100644 --- a/heartbeat/portblock +++ b/heartbeat/portblock @@ -1,360 +1,352 @@ #!/bin/sh # # portblock: iptables temporary portblocking control # # Author: Sun Jiang Dong # # License: GNU General Public License (GPL) # # Copyright: (C) 2005 International Business Machines # # OCF parameters are as below: # OCF_RESKEY_protocol # OCF_RESKEY_portno # OCF_RESKEY_action ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs . ${HA_DIR}/shellfuncs ####################################################################### CMD=`basename $0` usage() { - cat <<-!USAGE >&2 + cat <&2 usage: $CMD {start|stop|status|monitor|meta-data|validate-all} $CMD is used to temporarily block ports using iptables. It can be used to turn off a port before bringing up an IP address, and enable it after a service is started. To do that for samba, the following resource line can be used: $CMD::tcp::137,138::block \\ 10.10.10.20 \\ nmbd smbd \\ $CMD::tcp::137,138::unblock This will do the follwing things: - DROP all incoming packets for TCP ports 137 and 138 - Bring up the IP alias 10.10.10.20 - start the nmbd and smbd services - Re-enable TCP ports 137 and 138 (enable normal firewall rules on those ports) This prevents clients from getting ICMP port unreachable if they try to reconnect to the service after the alias is enabled but before nmbd and smbd are running. These packets will cause some clients to give up attempting to reconnect to the server. NOTE: iptables is linux-specific... - !USAGE +END } meta_data() { cat < 1.0 Resource script for portblock. It is used to temporarily block ports using iptables. portblock resource agent The protocol used to be blocked/unblocked. protocol The port number used to be blocked/unblocked. portno The action (block/unblock) to be done on the protocol::portno. action END } # # Because this is the normal usage, we consider "block" # resources to be pseudo-resources -- that is, their status can't # be reliably determined through external means. # This is because we expect an "unblock" resource to come along # and disable us -- but we're still in some sense active... # -# So, we track the state here using the pseudo_resource() function. -# -# The psuedo_resource function should be moved into the functions -# available to resources so other resource scripts could use it... -# -# - -# pseudo_resource filename operation -pseudo_resource() -{ - ha_pseudo_resource $* -} #iptables_spec {udp|tcp} portno,portno iptables_spec() { echo -D INPUT -p $1 -m multiport --dports $2 -j DROP } #active_grep_pat {udp|tcp} portno,portno active_grep_pat() { w="[ ][ ]*" any="0\\.0\\.0\\.0/0" echo "^DROP${w}${1}${w}--${w}${any}${w}${any}${w}multiport${w}dports${w}${2} " } #chain_isactive {udp|tcp} portno,portno chain_isactive() { PAT=`active_grep_pat "$1" "$2"` $IPTABLES -n -L INPUT | grep "$PAT" >/dev/null } SayActive() { echo "$CMD DROP rule for INPUT chain [$*] is running (OK)" - return 0 } SayConsideredActive() { echo "$CMD DROP rule for INPUT chain [$*] considered to be running (OK)" - return 0 } SayInactive() { echo "$CMD DROP rule for INPUT chain [$*] is inactive" - return 1 } #IptablesStatus {udp|tcp} portno,portno {block|unblock} -IptablesStatus() -{ - activewords="$CMD $1 $2 is running (OK)" - if - chain_isactive "$1" "$2" - then - case $3 in - block) SayActive $*;; - *) SayInactive $*;; - esac - else - case $3 in - block) - if - pseudo_resource "$RSCNAME" status - then - SayConsideredActive $* +IptablesStatus() { + local rc + rc=$OCF_ERR_GENERIC + activewords="$CMD $1 $2 is running (OK)" + if chain_isactive "$1" "$2"; then + case $3 in + block) + SayActive $* + rc=$OCF_SUCCESS + ;; + *) + SayInactive $* + rc=$OCF_NOT_RUNNING + ;; + esac + else + case $3 in + block) + if ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" status; then + SayConsideredActive $* + rc=$OCF_SUCCESS else - SayInactive $* - fi;; - - *) SayActive $*;; - esac - fi - - return $? + SayInactive $* + rc=$OCF_NOT_RUNNING + fi + ;; + + *) + SayActive $* + rc=$OCF_SUCCESS + ;; + esac + fi + + return $rc } #IptablesBLOCK {udp|tcp} portno,portno IptablesBLOCK() { if chain_isactive "$1" "$2" then : OK -- chain already active else $IPTABLES -I INPUT -p "$1" -m multiport --dports "$2" -j DROP fi return $? } #IptablesUNBLOCK {udp|tcp} portno,portno IptablesUNBLOCK() { if chain_isactive "$1" "$2" then $IPTABLES -D INPUT -p "$1" -m multiport --dports "$2" -j DROP else : Chain Not active fi return $? } #IptablesStart {udp|tcp} portno,portno {block|unblock} IptablesStart() { - pseudo_resource "$RSCNAME" start + ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" start case $3 in block) IptablesBLOCK "$@";; unblock) IptablesUNBLOCK "$@";; *) usage; return 1; esac return $? } #IptablesStop {udp|tcp} portno,portno {block|unblock} IptablesStop() { - pseudo_resource "$RSCNAME" stop + ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" stop case $3 in block) IptablesUNBLOCK "$@";; unblock) IptablesBLOCK "$@";; *) usage; return 1;; esac return $? } # # Check if the port is valid, this function code is not decent, but works # CheckPort() { # Examples of valid port: "1080", "1", "0080" # Examples of invalid port: "1080bad", "0", "0000", "" case "$1" in *[!0-9]*) #got invalid char false;; *[1-9]*) #no invalid char, and has non-zero digit, so is a good port true;; *) #empty string, or string of 0's false;; esac } IptablesValidateAll() { check_binary $IPTABLES case $protocol in tcp|udp) ;; *) ocf_log err "Invalid protocol $protocol!" exit $OCF_ERR_ARGS ;; esac if CheckPort "$portno"; then : else ocf_log err "Invalid port number $portno!" exit $OCF_ERR_ARGS fi case $action in block|unblock) ;; *) ocf_log err "Invalid action $action!" exit $OCF_ERR_ARGS ;; esac return $OCF_SUCCESS } 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 if [ -z "$OCF_RESKEY_protocol" ]; then ocf_log err "Please set OCF_RESKEY_protocol" exit $OCF_ERR_ARGS fi if [ -z "$OCF_RESKEY_portno" ]; then ocf_log err "Please set OCF_RESKEY_portno" exit $OCF_ERR_ARGS fi if [ -z "$OCF_RESKEY_action" ]; then ocf_log err "Please set OCF_RESKEY_action" exit $OCF_ERR_ARGS fi protocol=$OCF_RESKEY_protocol portno=$OCF_RESKEY_portno action=$OCF_RESKEY_action -RSCNAME=${CMD}_${protocol}_${portno}_${action} - case $1 in start) IptablesStart $protocol $portno $action ;; stop) IptablesStop $protocol $portno $action ;; status|monitor) IptablesStatus $protocol $portno $action ;; validate-all) IptablesValidateAll ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $?