summaryrefslogtreecommitdiffstats
path: root/heartbeat/nfsserver
diff options
context:
space:
mode:
Diffstat (limited to 'heartbeat/nfsserver')
-rwxr-xr-xheartbeat/nfsserver1068
1 files changed, 1068 insertions, 0 deletions
diff --git a/heartbeat/nfsserver b/heartbeat/nfsserver
new file mode 100755
index 0000000..8838195
--- /dev/null
+++ b/heartbeat/nfsserver
@@ -0,0 +1,1068 @@
+#!/bin/sh
+# nfsserver
+#
+# Description: Manages nfs server as OCF resource
+# by hxinwei@gmail.com
+# License: GNU General Public License v2 (GPLv2) and later
+
+
+# I don't know for certain whether all services actuall _need_ this,
+# I know that at least nfs-server needs it.
+# The rgmanager resource agent in rgmanager/src/resources/nfsserver.sh.in
+# did the unshare for gssd and idmapd as well, even though it seems unclear why.
+# Let's start with just the nfs-server, and add others if/when we have clear
+# indication they need it.
+#NFSD_RELATED_SYSTEMD_SERVICE_FOR_UNSHARE_UTS_NAMESPACE="nfs-idmapd.service nfs-mountd.service nfs-server.service nfsdcld.service rpc-gssd.service rpc-statd.service rpc-statd-notify.service rpcbind.service"
+NFSD_RELATED_SYSTEMD_SERVICE_FOR_UNSHARE_UTS_NAMESPACE="nfs-server.service"
+SYSTEMD_ENVIRONMENT_FILE_NFS_SERVER_SCOPE=/run/sysconfig/nfs-server-scope
+SYSTEMD_UNSHARE_UTS_DROPIN=51-resource-agents-unshare-uts.conf
+
+if [ -n "$OCF_DEBUG_LIBRARY" ]; then
+ . $OCF_DEBUG_LIBRARY
+else
+ : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+fi
+
+if is_redhat_based; then
+ . ${OCF_FUNCTIONS_DIR}/nfsserver-redhat.sh
+fi
+
+DEFAULT_INIT_SCRIPT_LIST="/etc/init.d/nfsserver /etc/init.d/nfs /etc/init.d/nfs-kernel-server"
+DEFAULT_INIT_SCRIPT="/etc/init.d/nfsserver"
+for script in $DEFAULT_INIT_SCRIPT_LIST
+do
+ if [ -f $script -a -x $script ]; then
+ DEFAULT_INIT_SCRIPT=$script
+ break
+ fi
+done
+
+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>1.0</version>
+
+<longdesc lang="en">
+Nfsserver helps one 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="nfsv4_only" unique="0" required="0">
+<longdesc lang="en">
+Run in NFSv4 only mode (rpc-statd and rpcbind services masked).
+</longdesc>
+<shortdesc lang="en">
+NFSv4 only mode.
+</shortdesc>
+<content type="boolean" default="false" />
+</parameter>
+
+<parameter name="nfs_no_notify" unique="0" required="0">
+<longdesc lang="en">
+Do not send reboot notifications to NFSv3 clients during server startup.
+</longdesc>
+<shortdesc lang="en">
+Disable NFSv3 server reboot notifications
+</shortdesc>
+<content type="boolean" default="false" />
+</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_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_server_scope" unique="0" required="0">
+<longdesc lang="en">
+RFC8881, 8.4.2.1 State Reclaim:
+
+If the server scope is different, the client should not attempt to
+reclaim locks. In this situation, no lock reclaim is possible.
+Any attempt to re-obtain the locks with non-reclaim operations is
+problematic since there is no guarantee that the existing
+filehandles will be recognized by the new server, or that if
+recognized, they denote the same objects. It is best to treat the
+locks as having been revoked by the reconfiguration event.
+
+For lock reclaim to even be attempted, we have to define and set the same
+server scope for NFSD on all cluster nodes in the NFS failover cluster.
+
+This agent won't "guess" a suitable server scope name for you, you need to
+explicitly specify this. But without it, NFSv4 lock reclaim after failover
+won't work properly. Suggested value: the failover "service IP".
+</longdesc>
+<shortdesc lang="en">
+RFC8881 NFS server scope for (lock) state reclaim after failover.
+</shortdesc>
+<content type="string"/>
+</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_shared_infodir" unique="0" required="0">
+<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>
+
+$(
+is_redhat_based && nfsserver_redhat_meta_data
+)
+
+</parameters>
+
+<actions>
+<action name="start" timeout="40s" />
+<action name="stop" timeout="20s" />
+<action name="monitor" depth="0" timeout="20s" interval="10s" />
+<action name="meta-data" timeout="5s" />
+<action name="validate-all" timeout="30s" />
+</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"}
+: ${OCF_RESKEY_rpcpipefs_dir="$DEFAULT_RPCPIPEFS_DIR"}
+OCF_RESKEY_rpcpipefs_dir=${OCF_RESKEY_rpcpipefs_dir%/}
+
+# 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 -dZ $STATD_PATH | grep -o '\S\+:\S\+:\S\+')"
+fi
+
+##
+# EXEC_MODE values
+# 1 user init script or default init script
+# 2 systemd (with nfs-lock.service)
+# 3 systemd (with rpc-statd.service)
+#
+# 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
+
+ if which systemctl > /dev/null 2>&1; then
+ if systemctl --no-legend list-unit-files 'nfs-*' | grep nfs-server > /dev/null; then
+
+ ##
+ # Attempt systemd (with nfs-lock.service).
+ ##
+ if systemctl --no-legend list-unit-files 'nfs-*' | 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
+
+ ##
+ # Attempt systemd (with rpc-statd.service).
+ ##
+ if systemctl --no-legend list-unit-files 'rpc-*' | grep rpc-statd > /dev/null; then
+ EXEC_MODE=3
+ return 0
+ fi
+ fi
+ fi
+
+ ocf_exit_reason "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
+ local svc=$2
+ set_exec_mode
+
+ case $EXEC_MODE in
+ 1) if [ -z "$OCF_RESKEY_nfs_server_scope" ] ; then
+ ${OCF_RESKEY_nfs_init_script} $cmd
+ else
+ unshare -u /bin/sh -c "hostname ${OCF_RESKEY_nfs_server_scope}; exec ${OCF_RESKEY_nfs_init_script} $cmd"
+ fi ;;
+ 2) if ! echo $svc | grep -q "\."; then
+ svc="${svc}.service"
+ fi
+ systemctl -n0 $cmd $svc
+ ;;
+ 3) if ! echo $svc | grep -q "\."; then
+ svc="${svc}.service"
+ fi
+ systemctl -n0 $cmd $svc
+ ;;
+ esac
+}
+
+v3locking_exec()
+{
+ local cmd=$1
+ set_exec_mode
+
+ if [ $EXEC_MODE -eq 2 ]; then
+ nfs_exec $cmd nfs-lock.service
+ elif [ $EXEC_MODE -eq 3 ]; then
+ nfs_exec $cmd rpc-statd.service
+ else
+ case $cmd in
+ start) locking_start;;
+ stop) locking_stop;;
+ status) locking_status;;
+ esac
+ fi
+}
+
+nfsserver_systemd_monitor()
+{
+ local threads_num
+ local rc
+ local fn
+
+ if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ ocf_log debug "Status: rpcbind"
+ rpcinfo > /dev/null 2>&1
+ rc=$?
+ if [ "$rc" -ne "0" ]; then
+ ocf_exit_reason "rpcbind is not running"
+ return $OCF_NOT_RUNNING
+ fi
+
+ ocf_log debug "Status: nfs-mountd"
+ ps axww | grep -q "[r]pc.mountd"
+ rc=$?
+ if [ "$rc" -ne "0" ]; then
+ ocf_exit_reason "nfs-mountd is not running"
+ return $OCF_NOT_RUNNING
+ fi
+ fi
+
+ ocf_log debug "Status: nfs-idmapd"
+ fn=`mktemp`
+ nfs_exec status nfs-idmapd > $fn 2>&1
+ rc=$?
+ ocf_log debug "$(cat $fn)"
+ rm -f $fn
+ if [ "$rc" -ne "0" ]; then
+ ocf_exit_reason "nfs-idmapd is not running"
+ return $OCF_NOT_RUNNING
+ fi
+
+ if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ ocf_log debug "Status: rpc-statd"
+ rpcinfo -t localhost 100024 > /dev/null 2>&1
+ rc=$?
+ if [ "$rc" -ne "0" ]; then
+ ocf_exit_reason "rpc-statd is not running"
+ return $OCF_NOT_RUNNING
+ fi
+ fi
+
+ nfs_exec is-active nfs-server
+ rc=$?
+
+ # Now systemctl is-active can't detect the failure of kernel process like nfsd.
+ # So, if the return value of systemctl is-active is 0, check the threads number
+ # to make sure the process is running really.
+ # /proc/fs/nfsd/threads has the numbers of the nfsd threads.
+ if [ $rc -eq 0 ]; then
+ threads_num=`cat /proc/fs/nfsd/threads 2>/dev/null`
+ if [ $? -eq 0 ]; then
+ if [ $threads_num -gt 0 ]; then
+ return $OCF_SUCCESS
+ else
+ return 3
+ fi
+ else
+ return $OCF_ERR_GENERIC
+ fi
+ fi
+
+ return $rc
+}
+
+nfsserver_monitor ()
+{
+ local fn
+
+ set_exec_mode
+ fn=`mktemp`
+ case $EXEC_MODE in
+ 1) nfs_exec status nfs-server > $fn 2>&1;;
+ [23]) nfsserver_systemd_monitor > $fn 2>&1;;
+ esac
+ 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.
+ ocf_is_true "$OCF_RESKEY_nfsv4_only" || v3locking_exec "status"
+ rc=$?
+ if [ $rc -ne 0 ]; then
+ ocf_exit_reason "NFS server is up, but the locking daemons are down"
+ rc=$OCF_ERR_GENERIC
+ fi
+ return $rc
+ elif [ $rc -eq 3 ] || [ $rc -eq $OCF_NOT_RUNNING ]; then
+ return $OCF_NOT_RUNNING
+ else
+ return $OCF_ERR_GENERIC
+ fi
+}
+
+prepare_directory ()
+{
+ if [ -z "$fp" ]; then
+ fp="/var/lib/nfs"
+ fi
+
+ [ -d "$OCF_RESKEY_rpcpipefs_dir" ] || mkdir -p $OCF_RESKEY_rpcpipefs_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 2>/dev/null`" -a "`id -g rpcuser 2>/dev/null`" ] &&
+ 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"
+
+ dd if=/dev/urandom of=$fp/$STATD_DIR/state bs=1 count=4 >/dev/null 2>&1
+ [ -n "`id -u rpcuser 2>/dev/null`" -a "`id -g rpcuser 2>/dev/null`" ] && chown rpcuser.rpcuser "$fp/$STATD_DIR/state"
+ [ $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 [ -z "$fp" ]; then
+ return
+ fi
+
+ [ -d "$fp" ] || mkdir -p $fp
+
+ if is_bound /var/lib/nfs; then
+ ocf_log debug "$fp is already bound to /var/lib/nfs"
+ return 0
+ fi
+
+ case $EXEC_MODE in
+ [23]) if nfs_exec status var-lib-nfs-rpc_pipefs.mount > /dev/null 2>&1; then
+ ocf_log debug "/var/lib/nfs/rpc_pipefs already mounted. Unmounting in preparation to bind mount nfs dir"
+ systemctl stop var-lib-nfs-rpc_pipefs.mount
+ fi
+ ;;
+ esac
+
+ mount --bind $fp /var/lib/nfs
+ [ $SELINUX_ENABLED -eq 0 ] && restorecon /var/lib/nfs
+}
+
+unbind_tree ()
+{
+ local i=1
+ while `mount | grep -q " on $OCF_RESKEY_rpcpipefs_dir "` && [ "$i" -le 10 ]; do
+ ocf_log info "Stop: umount ($i/10 attempts)"
+ umount -t rpc_pipefs $OCF_RESKEY_rpcpipefs_dir
+ sleep 1
+ i=$((i + 1))
+ done
+
+ if mount | grep -q " on $OCF_RESKEY_rpcpipefs_dir "; then
+ ocf_log err "Failed to unmount $OCF_RESKEY_rpcpipefs_dir"
+ return $OCF_ERR_GENERIC
+ fi
+
+ if is_bound /var/lib/nfs; then
+ if ! umount /var/lib/nfs; then
+ ocf_log err "Failed to unmount /var/lib/nfs"
+ return $OCF_ERR_GENERIC
+ fi
+ fi
+
+ return $OCF_SUCCESS
+}
+
+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."
+
+ rpc.statd $STATDARG
+
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ ocf_log err "Failed to start rpc.statd"
+ return $ret
+ fi
+ [ -d /var/lock/subsys ] && touch /var/lock/subsys/nfslock
+
+ return $ret
+}
+
+terminate()
+{
+ local pids
+ local i=0
+
+ while : ; do
+ pids=$(binary_status $1)
+ [ -z "$pids" ] && return 0
+ kill $pids
+ sleep 1
+ i=$((i + 1))
+ [ $i -gt 3 ] && return 1
+ done
+}
+
+
+killkill()
+{
+ local pids
+ local i=0
+
+ while : ; do
+ pids=$(binary_status $1)
+ [ -z "$pids" ] && return 0
+ kill -9 $pids
+ sleep 1
+ i=$((i + 1))
+ [ $i -gt 3 ] && return 1
+ done
+}
+
+stop_process()
+{
+ local 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
+}
+
+notify_locks()
+{
+ if ocf_is_true "$OCF_RESKEY_nfs_no_notify"; then
+ # we've been asked not to notify clients
+ return;
+ fi
+
+ # 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
+
+ if [ -n "$OCF_RESKEY_statd_outgoing_port" ]; then
+ opts="$opts -p $OCF_RESKEY_statd_outgoing_port"
+ 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
+ for ip in `echo ${OCF_RESKEY_nfs_ip} | sed 's/,/ /g'`; do
+ cp -rpfn $STATD_PATH/sm.ha/* $STATD_PATH/ > /dev/null 2>&1
+ sm-notify $opts -v $ip
+ done
+ else
+ sm-notify $opts
+ fi
+}
+
+# Problem: https://github.com/ClusterLabs/resource-agents/issues/1644
+# RFC8881, 8.4.2.1 State Reclaim:
+#
+# | If the server scope is different, the client should not attempt to
+# | reclaim locks. In this situation, no lock reclaim is possible.
+# | Any attempt to re-obtain the locks with non-reclaim operations is
+# | problematic since there is no guarantee that the existing
+# | filehandles will be recognized by the new server, or that if
+# | recognized, they denote the same objects. It is best to treat the
+# | locks as having been revoked by the reconfiguration event.
+#
+# That's why for lock reclaim to even be attempted, we have to define and set
+# the same server scope for NFSD on all cluster nodes in the NFS failover
+# cluster. And in linux, that is done by setting the uts nodename for the
+# command that starts the nfsd kernel threads.
+#
+inject_unshare_uts_name_into_systemd_units ()
+{
+ local END_TAG="# END OF DROP-IN FOR NFS SERVER SCOPE"
+ local services
+ services=$(systemctl list-unit-files --no-legend $NFSD_RELATED_SYSTEMD_SERVICE_FOR_UNSHARE_UTS_NAMESPACE | cut -d ' ' -f1)
+
+ local svc dir dropin edited_exec_start do_reload=false
+ local old_umask=$(umask)
+ umask 0022
+ for svc in $services ; do
+ dir=/run/systemd/system/$svc.d
+ dropin=$dir/$SYSTEMD_UNSHARE_UTS_DROPIN
+ grep -sqF "$END_TAG" "$dropin" && continue
+
+ test -d "$dir" || mkdir -p "$dir"
+ test -e "$dropin" && rm -f "$dropin"
+
+ # NOTE: additional ExecStart= might exist in the drop-in files, eg. openSUSE
+ edited_exec_start=$(systemctl cat $svc | sed -ne "s#^ExecStart=\\([-+:!@]*\\)\\(.\+\\)#ExecStart=\\1/usr/bin/unshare --uts /bin/sh -c 'hostname \${NFS_SERVER_SCOPE}; exec \"\$@\"' -- \\2#p" | tail -1)
+
+ cat > "$dropin" <<___
+[Service]
+EnvironmentFile=$SYSTEMD_ENVIRONMENT_FILE_NFS_SERVER_SCOPE
+# reset list of exec start, then re-populate with unshared uts namespace
+ExecStart=
+$edited_exec_start
+$END_TAG
+___
+ do_reload=true
+ ocf_log debug "injected unshare --uts into $dropin"
+ done
+
+ mkdir -p "${SYSTEMD_ENVIRONMENT_FILE_NFS_SERVER_SCOPE%/*}"
+ echo "NFS_SERVER_SCOPE=$OCF_RESKEY_nfs_server_scope" > "$SYSTEMD_ENVIRONMENT_FILE_NFS_SERVER_SCOPE"
+ umask $old_umask
+
+ $do_reload && systemctl daemon-reload
+}
+
+remove_unshare_uts_dropins ()
+{
+ local services
+ services=$(systemctl list-unit-files --no-legend $NFSD_RELATED_SYSTEMD_SERVICE_FOR_UNSHARE_UTS_NAMESPACE | cut -d ' ' -f1)
+
+ local svc dir dropin do_reload=false
+ for svc in $services ; do
+ dir=/run/systemd/system/$svc.d
+ dropin=$dir/$SYSTEMD_UNSHARE_UTS_DROPIN
+ test -e "$dropin" || continue
+ rm -f "$dropin"
+ do_reload=true
+ ocf_log debug "removed unshare --uts from $svc"
+ done
+ rm -f "${SYSTEMD_ENVIRONMENT_FILE_NFS_SERVER_SCOPE}"
+ $do_reload && systemctl daemon-reload
+}
+
+nfsserver_start ()
+{
+ local rc;
+ local fn
+
+ if nfsserver_monitor; then
+ ocf_log debug "NFS server is already started"
+ return $OCF_SUCCESS
+ fi
+
+ is_redhat_based && set_env_args
+ bind_tree
+ prepare_directory
+
+ # Debian (and other systems) may provide "init scripts",
+ # which will only redirect back to systemd.
+ # If we just unshare --uts the init script invocation,
+ # the uts namespace is useless in that case.
+ # If systemd is running, mangle the nfs-server.service unit,
+ # independent of the "EXEC_MODE" we detected.
+ if $systemd_is_running ; then
+ if [ -z "$OCF_RESKEY_nfs_server_scope" ] ; then
+ remove_unshare_uts_dropins
+ else
+ inject_unshare_uts_name_into_systemd_units
+ fi
+ fi
+
+ if ! `mount | grep -q " on $OCF_RESKEY_rpcpipefs_dir "`; then
+ mount -t rpc_pipefs sunrpc $OCF_RESKEY_rpcpipefs_dir
+ fi
+
+ # 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
+ #
+ # Synchronize these before starting statd
+ #
+ cp -rpfn $STATD_PATH/sm.ha/* $STATD_PATH/ > /dev/null 2>&1
+ 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 ..."
+
+ # mounts /proc/fs/nfsd for us
+ lsmod | grep -q nfsd
+ if [ $? -ne 0 ]; then
+ modprobe nfsd
+ fi
+
+ # systemd
+ case $EXEC_MODE in
+ [23]) if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ nfs_exec start rpcbind
+ local i=1
+ while : ; do
+ ocf_log info "Start: rpcbind i: $i"
+ rpcinfo > /dev/null 2>&1
+ rc=$?
+ if [ "$rc" -eq "0" ]; then
+ break
+ fi
+ sleep 1
+ i=$((i + 1))
+ done
+ fi
+ ;;
+ esac
+
+ if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ # 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_exit_reason "Failed to start NFS server locking daemons"
+ return $rc
+ fi
+ else
+ ocf_log info "rpc.statd already up"
+ fi
+ fi
+
+ # systemd
+ case $EXEC_MODE in
+ [23]) if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ nfs_exec start nfs-mountd
+ local i=1
+ while : ; do
+ ocf_log info "Start: nfs-mountd i: $i"
+ ps axww | grep -q "[r]pc.mountd"
+ rc=$?
+ if [ "$rc" -eq "0" ]; then
+ break
+ fi
+ sleep 1
+ i=$((i + 1))
+ done
+ fi
+
+ nfs_exec start nfs-idmapd
+ local i=1
+ while : ; do
+ ocf_log info "Start: nfs-idmapd i: $i"
+ fn=`mktemp`
+ nfs_exec status nfs-idmapd > $fn 2>&1
+ rc=$?
+ ocf_log debug "$(cat $fn)"
+ rm -f $fn
+ if [ "$rc" -eq "0" ]; then
+ break
+ fi
+ sleep 1
+ i=$((i + 1))
+ done
+
+ if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ nfs_exec start rpc-statd
+ local i=1
+ while : ; do
+ ocf_log info "Start: rpc-statd i: $i"
+ rpcinfo -t localhost 100024 > /dev/null 2>&1
+ rc=$?
+ if [ "$rc" -eq "0" ]; then
+ break
+ fi
+ sleep 1
+ i=$((i + 1))
+ done
+ fi
+ esac
+
+
+ fn=`mktemp`
+ nfs_exec start nfs-server > $fn 2>&1
+ rc=$?
+ ocf_log debug "$(cat $fn)"
+ rm -f $fn
+
+ if [ $rc -ne 0 ]; then
+ ocf_exit_reason "Failed to start NFS server"
+ return $rc
+ fi
+
+ tfn="/proc/fs/nfsd/threads"
+ if [ ! -f "$tfn" ] || [ "$(cat $tfn)" -le "0" ]; then
+ ocf_exit_reason "Failed to start NFS server: /proc/fs/nfsd/threads"
+ return $OCF_ERR_GENERIC
+ fi
+
+ notify_locks
+
+ ocf_log info "NFS server started"
+ return $OCF_SUCCESS
+}
+
+nfsserver_stop ()
+{
+ local fn
+
+ ocf_log info "Stopping NFS server ..."
+
+ # backup the current sm state information to the ha folder before stopping.
+ # the ha folder will be synced after startup, restoring the statd client state
+ 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
+
+ fn=`mktemp`
+ nfs_exec stop nfs-server > $fn 2>&1
+ rc=$?
+ ocf_log debug "$(cat $fn)"
+ rm -f $fn
+
+ if [ $rc -ne 0 ]; then
+ ocf_exit_reason "Failed to stop NFS server"
+ return $rc
+ fi
+
+ # systemd
+ case $EXEC_MODE in
+ [23]) ocf_log info "Stop: threads"
+ tfn="/proc/fs/nfsd/threads"
+ while [ -f "$tfn" ] && [ "$(cat $tfn)" -gt "0" ]; do
+ ocf_log err "NFS server failed to stop: /proc/fs/nfsd/threads"
+ sleep 1
+ done
+
+ if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ nfs_exec stop rpc-statd > /dev/null 2>&1
+ ocf_log info "Stop: rpc-statd"
+ rpcinfo -t localhost 100024 > /dev/null 2>&1
+ rc=$?
+ if [ "$rc" -eq "0" ]; then
+ ocf_exit_reason "Failed to stop rpc-statd"
+ return $OCF_ERR_GENERIC
+ fi
+ fi
+
+ nfs_exec stop nfs-idmapd > /dev/null 2>&1
+ ocf_log info "Stop: nfs-idmapd"
+ fn=`mktemp`
+ nfs_exec status nfs-idmapd > $fn 2>&1
+ rc=$?
+ ocf_log debug "$(cat $fn)"
+ rm -f $fn
+ if [ "$rc" -eq "0" ]; then
+ ocf_exit_reason "Failed to stop nfs-idmapd"
+ return $OCF_ERR_GENERIC
+ fi
+
+ if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ nfs_exec stop nfs-mountd > /dev/null 2>&1
+ ocf_log info "Stop: nfs-mountd"
+ ps axww | grep -q "[r]pc.mountd"
+ rc=$?
+ if [ "$rc" -eq "0" ]; then
+ ocf_exit_reason "Failed to stop nfs-mountd"
+ return $OCF_ERR_GENERIC
+ fi
+ fi
+
+ if systemctl --no-legend list-unit-files "nfsdcld*" | grep -q nfsdcld; then
+ nfs_exec stop nfsdcld > /dev/null 2>&1
+ ocf_log info "Stop: nfsdcld"
+ fn=`mktemp`
+ nfs_exec status nfsdcld > $fn 2>&1
+ rc=$?
+ ocf_log debug "$(cat $fn)"
+ rm -f $fn
+ if [ "$rc" -eq "0" ]; then
+ ocf_exit_reason "Failed to stop nfsdcld"
+ return $OCF_ERR_GENERIC
+ fi
+ fi
+ esac
+
+
+ if ! ocf_is_true "$OCF_RESKEY_nfsv4_only"; then
+ v3locking_exec "stop"
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "Failed to stop NFS locking daemons"
+ rc=$OCF_ERR_GENERIC
+ fi
+ fi
+
+ # systemd
+ case $EXEC_MODE in
+ [23]) nfs_exec stop rpc-gssd > /dev/null 2>&1
+ ocf_log info "Stop: rpc-gssd"
+ esac
+
+ unbind_tree
+ rc=$?
+ if [ "$rc" -ne $OCF_SUCCESS ]; then
+ ocf_exit_reason "Failed to unmount a bind mount"
+ else
+ ocf_log info "NFS server stopped"
+ fi
+
+ if $systemd_is_running; then
+ remove_unshare_uts_dropins
+ fi
+
+ 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 [ -n "$OCF_RESKEY_CRM_meta_clone" ] && [ -n "$OCF_RESKEY_nfs_shared_infodir" ]; then
+ ocf_exit_reason "This RA does not support clone mode when a shared info directory is in use."
+ 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_exit_reason "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_exit_reason "Invalid nfs_notify_cmd [$OCF_RESKEY_nfs_notify_cmd]"
+ exit $OCF_ERR_CONFIGURED
+ ;;
+ esac
+
+ return $OCF_SUCCESS
+}
+
+nfsserver_validate
+systemd_is_running && systemd_is_running=true || systemd_is_running=false
+
+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
+