#!/bin/bash # # Resource script for ido2db/ndo2db daemon # # Description: Manages ido2db/ndo2db daemon as an OCF resource in # an High Availability setup. # # Author: Lennart Betz # License: GNU General Public License (GPL) # Copyright: (C) NETWAYS GmbH # # Changelog: # # 2011-07-12 0.1 initial release # 2011-07-18 0.2 add RESKEY parameters for socket and pidfile # 2011-09-13 0.3 add RESKEY parameters for envfile # 2012-12-20 0.4 add validation of lsof, pgrep, ... exist # # At this time only tested on debian, RedHat... # # # # usage: $0 {start|stop|status|monitor|validate-all|meta-data} # # The "start" arg starts ido2db/ndo2db. # # The "stop" arg stops it. # # OCF parameters: # OCF_RESKEY_binary # OCF_RESKEY_config # OCF_RESKEY_pidfile # OCF_RESKEY_sockettype # OCF_RESKEY_socket # OCF_RESKEY_port # OCF_RESKEY_envfiles # # Note:This RA requires that the ido2db/ndo2db config files has a "lock_file" # entry so that it is able to act on the correct process ########################################################################## # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ########################################################################## OCF_RESKEY_binary_default=/usr/local/icinga/bin/ido2db OCF_RESKEY_config_default=/usr/local/icinga/etc/ido2db.cfg OCF_RESKEY_pidfile_default=/usr/local/icinga/var/ido2db.lock OCF_RESKEY_sockettype_default=unix OCF_RESKEY_socket_default=/usr/local/icinga/var/ido.sock OCF_RESKEY_port_default=5668 # what commands are used and have to exist DEPENDENCIES="grep lsof pgrep" usage() { cat <<-! usage: $0 {start|stop|status|monitor|validate-all|meta-data} action: start start the ${DAEMON} daemon stop stop the ${DAEMON} daemon status return the status of the ${DAEMON} daemon, running or down methods return the set of commands we support monitor return TRUE if the ${DAEMON} daemon appears to be working. meta-data show meta data message validate-all validate the instance parameters ! exit $OCF_ERR_ARGS } meta_data() { cat < 1.0 This script manages the ${DAEMON} daemon OCF Resource Agent compliant ${DAEMON} daemon script. The ${DAEMON} binary path. Full path to the ${DAEMON} binary. The ${DAEMON} daemon configuration file name with full path. For example, "${OCF_RESKEY_config_default}" Configuration file name with full path The ${DAEMON} daemon pidfile name with full path. Isn't set, use "lock_file" from config file. For example, "${OCF_RESKEY_pidfile_default}" Pidfile name with full path. Sets the socket type of ${DAEMON}. Isn't set, use "socket_type" from config file. For example, "tcp" or "unix". Sets the socket type of ${DAEMON}. Spezified the unix socket file of ${DAEMON}. Is not set, use "socket_name" from config file. Spezified the unix socket file of ${DAEMON}. Sets the port of the the socket ${DAEMON}, just usable with socket type "tcp". For example, ${OCF_RESKEY_port_default}". Spezified the socket of ${DAEMON}. Files (one or more) which contain extra environment variables. environment settings files END exit $OCF_SUCCESS } get_pids() { PIDS=( ) # Seek by pattern, parent 1 (init) PIDS[0]=$(pgrep -P 1 -f "$PROCESS_PATTERN") # Seek by pidfile PIDS[1]=$(awk '1{print $1}' $PIDFILE 2>/dev/null) if [ -n "${PIDS[1]}" ]; then typeset exe exe=$(ls -l "/proc/${PIDS[1]}/exe" 2>/dev/null) if [ $? = 0 ]; then exe=${exe##*-> } if ! [ "$exe" = ${OCF_RESKEY_binary} ]; then PIDS[1]="" fi else PIDS[1]="" fi fi # Seek by socket file if [ $SOCKETNAME ]; then typeset pid for p in $(lsof -FpR $SOCKETNAME 2>/dev/null); do if [ "X$p" = "XR1" ]; then if [ -z ${PIDS[2]} ]; then PIDS[2]=$pid else PIDS[2]="$pid ${PIDS[2]}" fi fi pid=$(echo $p| sed 's/^p//g') done fi # Seek by socket port if [ $SOCKETPORT ]; then PIDS[2]=$(netstat -apn |awk '/tcp.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:'$SOCKETPORT' /{sub("\\/.*", "", $7); print $7; exit}') fi } pids_sane() { if [ "${PIDS[1]}" = "${PIDS[2]}" ] && [ "${PIDS[1]}" = "${PIDS[0]}" ]; then return $OCF_SUCCESS else ocf_log err "$DAEMON: PID mismatch" return $OCF_ERR_GENERIC fi } is_dead() { if [ -z "${PIDS[0]}" ] & [ -z "${PIDS[2]}" ] then return 0 else return 1 fi } monitor() { get_pids #echo ps=${PIDS[0]} pidfile=${PIDS[1]} socket=${PIDS[2]} if ! pids_sane; then return $OCF_ERR_GENERIC fi if is_dead; then return $OCF_NOT_RUNNING fi return $OCF_SUCCESS } status() { monitor } start() { status retVal=$? if [ $retVal -eq $OCF_SUCCESS ]; then ocf_log info "${DAEMON} already running" exit $OCF_SUCCESS elif [ $retVal -ne $OCF_NOT_RUNNING ]; then ocf_log err "Error. Unknown status." exit $OCF_ERR_GENERIC fi $PROCESS_PATTERN if [ $? -ne 0 ]; then ocf_log err "Error. ${DAEMON} returned error $?." exit $OCF_ERR_GENERIC fi ocf_log info "Started ${DAEMON} daemon." exit $OCF_SUCCESS } stop() { pid=`cat $PIDFILE 2>/dev/null` if [ -n "$pid" ] ; then kill $pid if [ $? -ne 0 ]; then kill -SIGKILL $pid if [ $? -ne 0 ]; then ocf_log err "Error. Could not stop ${DAEMON}." return $OCF_ERR_GENERIC fi fi rm $PIDFILE 2>/dev/null fi kill -SIGKILL $(pgrep -f "$PROCESS_PATTERN") 2>/dev/null ocf_log info "Stopped ${DAEMON}." exit $OCF_SUCCESS } validate_all() { for file in $DEPENDENCIES; do if [ ! -x "$(which $file)" ]; then ocf_log err "$DAEMON $file could not be found or isn't executable." exit $OCF_ERR_INSTALLED fi done if [ ! -x $OCF_RESKEY_binary ]; then ocf_log err "$DAEMON binary $OCF_RESKEY_binary does not exist or is not executable." exit $OCF_ERR_INSTALLED fi if [ ! -f $OCF_RESKEY_config ]; then ocf_log err "Config $OCF_RESKEY_config does not exist." exit $OCF_ERR_CONFIGURED fi return $OCF_SUCCESS } # # Main # : ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}} DAEMON=$(basename ${OCF_RESKEY_binary}) if [ -z ${OCF_RESKEY_envfiles} ]; then if [ -f /etc/sysconfig/$DAEMON ]; then . /etc/sysconfig/$DAEMON fi if [ -f /etc/default/$DAEMON ]; then . /etc/default/$DAEMON fi else for i in ${OCF_RESKEY_envfiles}; do if [ -f $i ]; then . $i else ocf_log err "Error. Could not read $i, no such file or directory." exit $OCF_ERR_CONFIGURED fi done fi : ${OCF_RESKEY_config=${OCF_RESKEY_config_default}} : ${OCF_RESKEY_sockettype=${OCF_RESKEY_sockettype_default}} : ${OCF_RESKEY_pidfile=${OCF_RESKEY_pidfile_default}} : ${OCF_RESKEY_socket=${OCF_RESKEY_socket_default}} : ${OCF_RESKEY_port=${OCF_RESKEY_port_default}} # if config file is accessable, read pidfile, sockettype, socketname and port from config file if [ ! -z ${OCF_RESKEY_config} ] && [ -f ${OCF_RESKEY_config} ]; then # read pidfile from config file PIDFILE=$(grep -v "^#" ${OCF_RESKEY_config} |grep "^lock_file=" |cut -d "=" -f 2) if [ "X$PIDFILE" = "X" ]; then ocf_log err "Error. \"lock_file\" entry without whitespaces required in the $DAEMON config file by $DAEMON OCF RA." exit $OCF_ERR_GENERIC fi if (grep -v "^#" ${OCF_RESKEY_config} |grep "^socket_type=tcp" 1>/dev/null); then OCF_RESKEY_port=$(grep -v "^#" ${OCF_RESKEY_config} |grep "^tcp_port" |cut -d "=" -f 2) if [ -n ${OCF_RESKEY_port} ]; then SOCKETPORT=5668 else SOCKETPORT=${OCF_RESKEY_port} fi else SOCKETNAME=$(grep -v "^#" ${OCF_RESKEY_config} |grep "^socket_name" |cut -d "=" -f 2) fi else PIDFILE=${OCF_RESKEY_pidfile} SOCKETNAME=${OCF_RESKEY_socket} if [ "X${OCF_RESKEY_sockettype}" = "Xtcp" ]; then SOCKETPORT=${OCF_RESKEY_port} fi fi PROCESS_PATTERN="${OCF_RESKEY_binary} -c ${OCF_RESKEY_config}" case $1 in start) validate_all start ;; stop) validate_all stop ;; status) status ;; monitor) monitor ;; validate-all) validate_all ;; meta-data) meta_data ;; notify|promote|demote) exit $OCF_ERR_UNIMPLEMENTED ;; usage) usage exit $OCF_SUCCESS ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac