diff --git a/heartbeat/nfsserver b/heartbeat/nfsserver index bfd00a4e5..288575421 100755 --- a/heartbeat/nfsserver +++ b/heartbeat/nfsserver @@ -1,616 +1,621 @@ #!/bin/sh # nfsserver # # Description: Manages nfs server as OCF resource # by hxinwei@gmail.com # License: GNU General Public License v2 (GPLv2) and later if [ -n "$OCF_DEBUG_LIBRARY" ]; then . $OCF_DEBUG_LIBRARY else : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs fi DEFAULT_INIT_SCRIPT="/etc/init.d/nfsserver" +if ! [ -f $DEFAULT_INIT_SCRIPT ]; then + # On some systems, the script is just called nfs + DEFAULT_INIT_SCRIPT="/etc/init.d/nfs" +fi + DEFAULT_NOTIFY_CMD=`which sm-notify` DEFAULT_NOTIFY_CMD=${DEFAULT_NOTIFY_CMD:-"/sbin/sm-notify"} DEFAULT_NOTIFY_FOREGROUND="false" DEFAULT_RPCPIPEFS_DIR="/var/lib/nfs/rpc_pipefs" EXEC_MODE=0 SELINUX_ENABLED=-1 STATD_PATH="/var/lib/nfs" STATD_DIR="" nfsserver_meta_data() { cat <<END <?xml version="1.0"?> <!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"> <resource-agent name="nfsserver"> <version>1.0</version> <longdesc lang="en"> Nfsserver helps to manage the Linux nfs server as a failover-able resource in Linux-HA. It depends on Linux specific NFS implementation details, so is considered not portable to other platforms yet. </longdesc> <shortdesc lang="en">Manages an NFS server</shortdesc> <parameters> <parameter name="nfs_init_script" unique="0" required="0"> <longdesc lang="en"> The default init script shipped with the Linux distro. The nfsserver resource agent offloads the start/stop/monitor work to the init script because the procedure to start/stop/monitor nfsserver varies on different Linux distro. In the event that this option is not set, this agent will attempt to use an init script at this location, ${DEFAULT_INIT_SCRIPT}, or detect a systemd unit-file to use in the event that no init script is detected. </longdesc> <shortdesc lang="en"> Init script for nfsserver </shortdesc> <content type="string" default="auto detected" /> </parameter> <parameter name="nfs_notify_foreground" unique="0" required="0"> <longdesc lang="en"> Keeps the sm-notify attached to its controlling terminal and running in the foreground. </longdesc> <shortdesc lang="en"> Keeps the notify tool running in the foreground. </shortdesc> <content type="boolean" default="$DEFAULT_NOTIFY_FOREGROUND" /> </parameter> <parameter name="nfs_ip" unique="0" required="0"> <longdesc lang="en"> Comma separated list of floating IP addresses used to access the nfs service </longdesc> <shortdesc lang="en"> IP addresses. </shortdesc> <content type="string"/> </parameter> <parameter name="nfs_smnotify_retry_time" unique="0" required="0"> <longdesc lang="en"> Specifies the length of sm-notify retry time, in minutes, to continue retrying notifications to unresponsive hosts. If this option is not specified, sm-notify attempts to send notifications for 15 minutes. Specifying a value of 0 causes sm-notify to continue sending notifications to unresponsive peers until it is manually killed. </longdesc> <shortdesc lang="en"> Specifies the length of sm-notify retry time (minutes). </shortdesc> <content type="integer" default="" /> </parameter> <parameter name="nfs_shared_infodir" unique="0" required="1"> <longdesc lang="en"> The nfsserver resource agent will save nfs related information in this specific directory. And this directory must be able to fail-over before nfsserver itself. </longdesc> <shortdesc lang="en"> Directory to store nfs server related information. </shortdesc> <content type="string" default="" /> </parameter> <parameter name="rpcpipefs_dir" unique="0" required="0"> <longdesc lang="en"> The mount point for the sunrpc file system. Default is $DEFAULT_RPCPIPEFS_DIR. This script will mount (bind) nfs_shared_infodir on /var/lib/nfs/ (cannot be changed), and this script will mount the sunrpc file system on $DEFAULT_RPCPIPEFS_DIR (default, can be changed by this parameter). If you want to move only rpc_pipefs/ (e.g. to keep rpc_pipefs/ local) from default, please set this value. </longdesc> <shortdesc lang="en"> The mount point for the sunrpc file system. </shortdesc> <content type="string" default="$DEFAULT_RPCPIPEFS_DIR" /> </parameter> </parameters> <actions> <action name="start" timeout="40" /> <action name="stop" timeout="20s" /> <action name="monitor" depth="0" timeout="20s" interval="10" /> <action name="meta-data" timeout="5" /> <action name="validate-all" timeout="30" /> </actions> </resource-agent> END return $OCF_SUCCESS } nfsserver_usage() { cat <<END usage: $0 {start|stop|monitor|status|validate-all|meta-data} END } if [ $# -ne 1 ]; then nfsserver_usage exit $OCF_ERR_ARGS fi case $__OCF_ACTION in meta-data) nfsserver_meta_data exit $OCF_SUCCESS ;; usage|help) nfsserver_usage exit $OCF_SUCCESS ;; *) ;; esac fp="$OCF_RESKEY_nfs_shared_infodir" : ${OCF_RESKEY_nfs_notify_cmd="$DEFAULT_NOTIFY_CMD"} : ${OCF_RESKEY_nfs_notify_foreground="$DEFAULT_NOTIFY_FOREGROUND"} if [ -z ${OCF_RESKEY_rpcpipefs_dir} ]; then rpcpipefs_make_dir=$fp/rpc_pipefs rpcpipefs_umount_dir=${DEFAULT_RPCPIPEFS_DIR} else rpcpipefs_make_dir=${OCF_RESKEY_rpcpipefs_dir} rpcpipefs_umount_dir=${OCF_RESKEY_rpcpipefs_dir} fi # Use statd folder if it exists if [ -d "/var/lib/nfs/statd" ]; then STATD_DIR="statd" STATD_PATH="/var/lib/nfs/statd" fi # SELinux information. We are taking the permissions from # the current statd dir and applying it to the HA one that is # being mounted in its place. which restorecon > /dev/null 2>&1 && selinuxenabled SELINUX_ENABLED=$? if [ $SELINUX_ENABLED -eq 0 ]; then export SELINUX_LABEL="$(ls -ldZ $STATD_PATH | cut -f4 -d' ')" fi ## # EXEC_MODE values # 1 user init script or default init script # 2 systemd # # On error, this function will terminate the process # with error code $OCF_ERR_INSTALLED ## set_exec_mode() { ## # If EXEC_MODE is already set, we don't need to run this function again. ## if [ $EXEC_MODE -ne 0 ]; then return 0; fi ## # If the user defined an init script, It must exist for us to continue ## if [ -n "$OCF_RESKEY_nfs_init_script" ]; then # check_binary will exit the process if init script does not exist check_binary ${OCF_RESKEY_nfs_init_script} EXEC_MODE=1 return 0 fi ## # Check to see if the default init script exists, if so we'll use that. ## if which $DEFAULT_INIT_SCRIPT > /dev/null 2>&1; then OCF_RESKEY_nfs_init_script=$DEFAULT_INIT_SCRIPT EXEC_MODE=1 return 0 fi ## # Last of all, attempt systemd. ## if which systemctl > /dev/null 2>&1; then if systemctl list-unit-files | grep nfs-server > /dev/null && systemctl list-unit-files | grep nfs-lock > /dev/null; then EXEC_MODE=2 # when using systemd, the nfs-lock service file handles nfsv3 locking daemons for us. return 0 fi fi ocf_log err "No init script or systemd unit file detected for nfs server" exit $OCF_ERR_INSTALLED } ## # wrapper for init script and systemd calls. ## nfs_exec() { local cmd=$1 set_exec_mode case $EXEC_MODE in 1) ${OCF_RESKEY_nfs_init_script} $cmd;; 2) systemctl $cmd nfs-server.service esac } v3locking_exec() { local cmd=$1 set_exec_mode if [ $EXEC_MODE -eq 2 ]; then systemctl $cmd nfs-lock.service else case $cmd in start) locking_start;; stop) locking_stop;; status) locking_status;; esac fi } nfsserver_monitor () { fn=`mktemp` nfs_exec status > $fn 2>&1 rc=$? ocf_log debug `cat $fn` rm -f $fn #Adapte LSB status code to OCF return code if [ $rc -eq 0 ]; then # don't report success if nfs servers are up # without locking daemons. v3locking_exec "status" rc=$? if [ $rc -ne 0 ]; then ocf_log error "NFS server is up, but the locking daemons are down" rc=$OCF_ERR_GENERIC fi return $rc elif [ $rc -eq 3 ]; then return $OCF_NOT_RUNNING else return $OCF_ERR_GENERIC fi } prepare_directory () { [ -d "$fp" ] || mkdir -p $fp [ -d "$rpcpipefs_make_dir" ] || mkdir -p $rpcpipefs_make_dir [ -d "$fp/v4recovery" ] || mkdir -p $fp/v4recovery [ -d "$fp/$STATD_DIR" ] || mkdir -p "$fp/$STATD_DIR" [ -d "$fp/$STATD_DIR/sm" ] || mkdir -p "$fp/$STATD_DIR/sm" [ -d "$fp/$STATD_DIR/sm.ha" ] || mkdir -p "$fp/$STATD_DIR/sm.ha" [ -d "$fp/$STATD_DIR/sm.bak" ] || mkdir -p "$fp/$STATD_DIR/sm.bak" [ -n "`id -u rpcuser`" -a "`id -g rpcuser`" ] && chown -R rpcuser.rpcuser "$fp/$STATD_DIR" [ -f "$fp/etab" ] || touch "$fp/etab" [ -f "$fp/xtab" ] || touch "$fp/xtab" [ -f "$fp/rmtab" ] || touch "$fp/rmtab" [ $SELINUX_ENABLED -eq 0 ] && chcon -R "$SELINUX_LABEL" "$fp" } is_bound () { if mount | grep -q "on $1 type"; then return 0 fi return 1 } bind_tree () { if is_bound /var/lib/nfs; then ocf_log debug "$fp is already bound to /var/lib/nfs" return 0 fi mount --bind $fp /var/lib/nfs [ $SELINUX_ENABLED -eq 0 ] && restorecon /var/lib/nfs } unbind_tree () { if `mount | grep -q " on $rpcpipefs_umount_dir"`; then umount -t rpc_pipefs $rpcpipefs_umount_dir fi if is_bound /var/lib/nfs; then umount /var/lib/nfs fi } binary_status() { local binary=$1 local pid pid=$(pgrep ${binary}) case $? in 0) echo "$pid" return $OCF_SUCCESS;; 1) return $OCF_NOT_RUNNING;; *) return $OCF_ERR_GENERIC;; esac } locking_status() { binary_status "rpc.statd" > /dev/null 2>&1 } locking_start() { local ret=$OCF_SUCCESS ocf_log info "Starting rpc.statd." if [ -n "$OCF_RESKEY_nfs_ip" ]; then rm -rf $STATD_PATH/sm.ha.save > /dev/null 2>&1 cp -rpf $STATD_PATH/sm.ha $STATD_PATH/sm.ha.save > /dev/null 2>&1 for ip in `echo ${OCF_RESKEY_nfs_ip} | sed 's/,/ /g'`; do rpc.statd -n $ip -P $STATD_PATH/sm.ha ret=$? if [ $ret -eq 0 ]; then break fi rm -rf $STATD_PATH/sm.ha > /dev/null 2>&1 cp -rpf $STATD_PATH/sm.ha.save $STATD_PATH/sm.ha > /dev/null 2>&1 done else rpc.statd -d fi ret=$? if [ $ret -ne 0 ]; then ocf_log err "Failed to start rpc.statd" return $ret fi touch /var/lock/subsys/nfslock return $ret } terminate() { declare pids declare i=0 while : ; do pids=$(binary_status $1) [ -z "$pids" ] && return 0 kill $pids sleep 1 ((i++)) [ $i -gt 3 ] && return 1 done } killkill() { declare pids declare i=0 while : ; do pids=$(binary_status $1) [ -z "$pids" ] && return 0 kill -9 $pids sleep 1 ((i++)) [ $i -gt 3 ] && return 1 done } stop_process() { declare process=$1 ocf_log info "Stopping $process" if terminate $process; then ocf_log debug "$process is stopped" else if killkill $process; then ocf_log debug "$process is stopped" else ocf_log debug "Failed to stop $process" return 1 fi fi return 0 } locking_stop() { ret=0 # sm-notify can prevent umount of /var/lib/nfs/statd if # it is still trying to notify unresponsive clients. stop_process sm-notify if [ $? -ne 0]; then ret=$OCF_ERR_GENERIC fi stop_process rpc.statd if [ $? -ne 0]; then ret=$OCF_ERR_GENERIC fi return $ret } renotify_locks() { # run in foreground, if requested if ocf_is_true "$OCF_RESKEY_nfs_notify_foreground"; then opts="-d" fi if [ -n "$OCF_RESKEY_nfs_smnotify_retry_time" ]; then opts="$opts -m $OCF_RESKEY_nfs_smnotify_retry_time" fi # forces re-notificaiton regardless if notifies have already gone out opts="$opts -f" ocf_log info "executing sm-notify" if [ -n "$OCF_RESKEY_nfs_ip" ]; then rm -rf $STATD_PATH/sm.ha.save > /dev/null 2>&1 cp -rpf $STATD_PATH/sm.ha $STATD_PATH/sm.ha.save > /dev/null 2>&1 for ip in `echo ${OCF_RESKEY_nfs_ip} | sed 's/,/ /g'`; do sm-notify $opts -v $ip -P $STATD_PATH/sm.ha rm -rf $STATD_PATH/sm.ha > /dev/null 2>&1 cp -rpf $STATD_PATH/sm.ha.save $STATD_PATH/sm.ha > /dev/null 2>&1 done else sm-notify $opts fi } nfsserver_start () { local notifies_sent=0; local rc; if nfsserver_monitor; then ocf_log debug "NFS server is already started" return $OCF_SUCCESS fi prepare_directory bind_tree # remove the sm-notify pid so sm-notify will be allowed to run again without requiring a reboot. rm -f /var/run/sm-notify.pid rm -rf $STATD_PATH/sm.ha/* > /dev/null 2>&1 cp -rpf $STATD_PATH/sm $STATD_PATH/sm.bak /var/lib/nfs/state $STATD_PATH/sm.ha > /dev/null 2>&1 ocf_log info "Starting NFS server ..." # check to see if we need to start rpc.statd v3locking_exec "status" if [ $? -ne $OCF_SUCCESS ]; then v3locking_exec "start" rc=$? if [ $rc -ne 0 ]; then ocf_log error "Failed to start NFS server locking daemons" return $rc fi # rpc.statd sends notifies automatically notifies_sent=1 else ocf_log info "rpc.statd already up" fi fn=`mktemp` nfs_exec start > $fn 2>&1 rc=$? ocf_log debug `cat $fn` rm -f $fn if [ $rc -ne 0 ]; then ocf_log err "Failed to start NFS server" return $rc fi # notify peers using sm-notify if rpc.statd was already initialized. # Otherwise the initalization of rpc.statd started sm-notify for us. if [ $notifies_sent -eq 0 ]; then renotify_locks fi ocf_log info "NFS server started" return $OCF_SUCCESS } nfsserver_stop () { ocf_log info "Stopping NFS server ..." fn=`mktemp` nfs_exec stop > $fn 2>&1 rc=$? ocf_log debug `cat $fn` rm -f $fn v3locking_exec "stop" if [ $? -ne 0 ]; then ocf_log err "Failed to stop NFS locking daemons" rc=$OCF_ERR_GENERIC fi if [ $rc -eq 0 ]; then unbind_tree ocf_log info "NFS server stopped" return $OCF_SUCCESS fi ocf_log err "Failed to stop NFS server" return $rc } nfsserver_validate () { ## # set_exec_mode will exit if nfs server is not installed ## set_exec_mode check_binary ${OCF_RESKEY_nfs_notify_cmd} if [ x = "x$OCF_RESKEY_nfs_shared_infodir" ]; then ocf_log err "nfs_shared_infodir not set" exit $OCF_ERR_CONFIGURED fi if [ -n "$OCF_RESKEY_nfs_smnotify_retry_time" ]; then if ! ocf_is_decimal "$OCF_RESKEY_nfs_smnotify_retry_time"; then ocf_log err "Invalid nfs_smnotify_retry_time [$OCF_RESKEY_nfs_smnotify_retry_time]" exit $OCF_ERR_CONFIGURED fi fi case ${OCF_RESKEY_nfs_notify_cmd##*/} in sm-notify|rpc.statd) ;; *) ocf_log err "Invalid nfs_notify_cmd [$OCF_RESKEY_nfs_notify_cmd]" exit $OCF_ERR_CONFIGURED ;; esac return $OCF_SUCCESS } if [ -n "$OCF_RESKEY_CRM_meta_clone" ]; then ocf_log err "THIS RA DOES NOT SUPPORT CLONE MODE!" exit $OCF_ERR_CONFIGURED fi nfsserver_validate case $__OCF_ACTION in start) nfsserver_start ;; stop) nfsserver_stop ;; monitor) nfsserver_monitor ;; validate-all) exit $OCF_SUCCESS ;; *) nfsserver_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac