diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:52:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:52:36 +0000 |
commit | 7de03e4e519705301265c0415b3c0af85263a7ac (patch) | |
tree | 29d819c5227e3619d18a67d2a5dde963b3229dbe /rgmanager/src/resources/nfsserver.sh.in | |
parent | Initial commit. (diff) | |
download | resource-agents-7de03e4e519705301265c0415b3c0af85263a7ac.tar.xz resource-agents-7de03e4e519705301265c0415b3c0af85263a7ac.zip |
Adding upstream version 1:4.13.0.upstream/1%4.13.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'rgmanager/src/resources/nfsserver.sh.in')
-rw-r--r-- | rgmanager/src/resources/nfsserver.sh.in | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/rgmanager/src/resources/nfsserver.sh.in b/rgmanager/src/resources/nfsserver.sh.in new file mode 100644 index 0000000..e7130f0 --- /dev/null +++ b/rgmanager/src/resources/nfsserver.sh.in @@ -0,0 +1,611 @@ +#!@BASH_SHELL@ + +# +# NFS Server Script. Handles starting/stopping Servand doing +# the strange NFS stuff to get it to fail over properly. +# + +LC_ALL=C +LANG=C +PATH=/bin:/sbin:/usr/bin:/usr/sbin + +V4RECOVERY="/var/lib/nfs/v4recovery" +PROC_V4RECOVERY="/proc/fs/nfsd/nfsv4recoverydir" + +export LC_ALL LANG PATH + +. $(dirname $0)/ocf-shellfuncs + +# SELinux information +which restorecon &> /dev/null && selinuxenabled &> /dev/null +export SELINUX_ENABLED=$? +if [ $SELINUX_ENABLED ]; then + export SELINUX_LABEL="$(ls -ldZ /var/lib/nfs/statd | cut -f4 -d' ')" +fi + +# strip trailing / off so pattern matching will work consistently. +while [ "${OCF_RESKEY_path#${OCF_RESKEY_path%?}}" = "/" ] +do + OCF_RESKEY_path="${OCF_RESKEY_path%/}" +done + +log_do() +{ + ocf_log debug $* + $* &> /dev/null + ret=$? + if [ $ret -ne 0 ]; then + ocf_log debug "Failed: $*" + fi + return $ret +} + + +meta_data() +{ + cat <<EOT +<?xml version="1.0" ?> +<!DOCTYPE resource-agent SYSTEM "ra-api-1-modified.dtd"> +<resource-agent name="nfsserver" version="rgmanager 2.0"> + <version>1.0</version> + + <longdesc lang="en"> + This defines an NFS server resource. The NFS server + resource is useful for exporting NFSv4 file systems + to clients. Because of the way NFSv4 works, only + one NFSv4 resource may exist on a server at a + time. Additionally, it is not possible to use + the nfsserver resource when also using local instances + of NFS on each cluster node. + </longdesc> + + <shortdesc lang="en"> + This defines an NFS server resource. + </shortdesc> + + <parameters> + <parameter name="name" primary="1"> + <longdesc lang="en"> + Descriptive name for this server. Generally, only + one server is ever defined per service. + </longdesc> + <shortdesc lang="en"> + Name + </shortdesc> + <content type="string"/> + </parameter> + + <parameter name="path" inherit="mountpoint"> + <longdesc lang="en"> + This is the path you intend to export. Usually, this is + left blank, and the mountpoint of the parent file system + is used. This path is passed to nfsclient resources as + the export path when exportfs is called. + </longdesc> + <shortdesc lang="en"> + This is the path you intend to export. + </shortdesc> + <content type="string"/> + </parameter> + + <parameter name="nfspath"> + <longdesc lang="en"> + This is the path containing shared NFS information which + is used for NFS recovery after a failover. This + is relative to the export path, and defaults to + ".clumanager/nfs". + </longdesc> + <shortdesc lang="en"> + This is the path containing shared NFS recovery + information, relative to the path parameter. + </shortdesc> + <content type="string" default=".clumanager/nfs"/> + </parameter> + + <parameter name="statdport"> + <longdesc lang="en"> + Specifies the port number used for RPC listener sockets. If + this option is not specified, rpc.statd chooses a random + ephemeral port for each listener socket. This option can be + used to fix the port value of its listeners when SM_NOTIFY + requests must traverse a firewall between + clients and servers. + </longdesc> + <shortdesc lang="en"> + This is the port where rpc.statd should listen on. + </shortdesc> + <content type="integer" default=""/> + </parameter> + + <parameter name="krbhost"> + <longdesc lang="en"> + This is the Kerberos hostname, which should be set according + to the floating IP. + </longdesc> + <shortdesc lang="en"> + This is the Kerberos hostname. + </shortdesc> + <content type="string"/> + </parameter> + + </parameters> + + <actions> + <action name="start" timeout="5"/> + <action name="stop" timeout="5"/> + <action name="recover" timeout="5"/> + + <action name="status" timeout="5" interval="30"/> + <action name="monitor" timeout="5" interval="30"/> + + <action name="meta-data" timeout="5"/> + <action name="validate-all" timeout="30"/> + </actions> + + <special tag="rgmanager"> + <attributes maxinstances="1"/> + <child type="nfsexport" forbid="1"/> + <child type="nfsserver" forbid="1"/> + <child type="nfsclient" start="1" stop="2"/> + <child type="ip" start="2" stop="1"/> + </special> + +</resource-agent> +EOT +} + + +verify_path() +{ + if [ -z "$OCF_RESKEY_path" ]; then + ocf_log err "No server path specified." + return $OCF_ERR_ARGS + fi + + [ -d "$OCF_RESKEY_path" ] && return 0 + + ocf_log err "$OCF_RESKEY_path is not a directory" + + return $OCF_ERR_ARGS +} + + +verify_nfspath() +{ + if [ -z "$OCF_RESKEY_nfspath" ]; then + echo No NFS data path specified. + return 1 + fi + + [ -d "$OCF_RESKEY_path" ] && return 0 + + # xxx do nothing for now. + return 0 +} + + +verify_statdport() +{ + if [ -z "$OCF_RESKEY_statdport" ]; then + # this is fine, statdport is optional + return 0 + fi + + [ $OCF_RESKEY_statdport -gt 0 && $OCF_RESKEY_statdport -le 65535 ] && return 0 + + ocf_log err "$OCF_RESKEY_statdport is not a valid port number" + + return $OCF_ERR_ARGS +} + + +verify_all() +{ + verify_path || return 1 + verify_nfspath || return 1 + verify_statdport || return 1 + + return 0 +} + + +nfs_daemons() +{ + declare oper + declare val + + case $1 in + start) + ocf_log info "Starting NFS daemons" + if [ -z "$OCF_RESKEY_krbhost" ]; then + /etc/init.d/nfs start + rv=$? + else + unshare -u @BASH_SHELL@ -c "hostname $OCF_RESKEY_krbhost; /etc/init.d/nfs start" + rv=$? + unshare -u @BASH_SHELL@ -c "hostname $OCF_RESKEY_krbhost; /etc/init.d/rpcgssd start" + if [ $rv -ne 0 ]; then + ocf_log err "Failed to start rpcgssd" + return $OCF_ERR_GENERIC + fi + unshare -u @BASH_SHELL@ -c "hostname $OCF_RESKEY_krbhost; /etc/init.d/rpcidmapd start" + if [ $rv -ne 0 ]; then + ocf_log err "Failed to start rpcidmapd" + return $OCF_ERR_GENERIC + fi + fi + + if [ $rv -ne 0 ]; then + ocf_log err "Failed to start NFS daemons" + return $OCF_ERR_GENERIC + fi + + ocf_log debug "NFS daemons are running" + return $OCF_SUCCESS + ;; + stop) + ocf_log info "Stopping NFS daemons" + if [ -n "$OCF_RESKEY_krbhost"]; then + if ! /etc/init.d/rpcidmapd stop; then + ocf_log err "Failed to stop rpcidmapd" + return $OCF_ERR_GENERIC + fi + if ! /etc/init.d/rpcgssd stop; then + ocf_log err "Failed to stop rpcgssd" + return $OCF_ERR_GENERIC + fi + fi + + if ! /etc/init.d/nfs stop; then + ocf_log err "Failed to stop NFS daemons" + return $OCF_ERR_GENERIC + fi + + ocf_log debug "NFS daemons are stopped" + + return $OCF_SUCCESS + ;; + status|monitor) + declare recoverydir="$OCF_RESKEY_path/$OCF_RESKEY_nfspath/v4recovery" + val=$(cat $PROC_V4RECOVERY) + + [ "$val" = "$recoverydir" ] || ocf_log warning \ + "NFSv4 recovery directory is $val instead of $recoverydir" + /etc/init.d/nfs status + if [ $? -ne 0 ]; then + ocf_log err "NFS is not running" + return $OCF_NOT_RUNNING + fi + /etc/init.d/rpcgssd status + if [ $? -ne 0 ]; then + ocf_log err "rpcgssd is not running" + return $OCF_NOT_RUNNING + fi + /etc/init.d/rpcidmapd status + if [ $? -ne 0 ]; then + ocf_log err "rpcidmapd is not running" + return $OCF_NOT_RUNNING + fi + + ocf_log debug "NFS daemons are running" + return $OCF_SUCCESS + ;; + esac +} + + +create_tree() +{ + declare fp="$OCF_RESKEY_path/$OCF_RESKEY_nfspath" + + [ -d "$fp" ] || mkdir -p "$fp" + + [ -d "$fp/statd" ] || mkdir -p "$fp/statd" + [ -d "$fp/v4recovery" ] || mkdir -p "$fp/v4recovery" + + # + # Create our own private copy which we use for notifies. + # This way, we can be sure to advertise on possibly multiple + # IP addresses. + # + [ -d "$fp/statd/sm" ] || mkdir -p "$fp/statd/sm" + [ -d "$fp/statd/sm.bak" ] || mkdir -p "$fp/statd/sm.bak" + [ -d "$fp/statd/sm-ha" ] || mkdir -p "$fp/statd/sm-ha" + [ -n "`id -u rpcuser`" -a "`id -g rpcuser`" ] && chown -R rpcuser.rpcuser "$fp/statd" + + # Create if they don't exist + [ -f "$fp/etab" ] || touch "$fp/etab" + [ -f "$fp/xtab" ] || touch "$fp/xtab" + [ -f "$fp/rmtab" ] || touch "$fp/rmtab" + + [ $SELINUX_ENABLED ] && chcon -R "$SELINUX_LABEL" "$fp" + + # + # Generate a random state file. If this ends up being what a client + # already has in its list, that's bad, but the chances of this + # are small - and relocations should be rare. + # + dd if=/dev/urandom of=$fp/state bs=1 count=4 &> /dev/null + [ -n "`id -u rpcuser`" -a "`id -g rpcuser`" ] && chown rpcuser.rpcuser "$fp/state" +} + +setup_v4recovery() +{ + declare recoverydir="$OCF_RESKEY_path/$OCF_RESKEY_nfspath/v4recovery" + + # mounts /proc/fs/nfsd for us + lsmod | grep -q nfsd + if [ $? -ne 0 ]; then + modprobe nfsd + fi + + val=$(cat "$PROC_V4RECOVERY") + + # Ensure start-after-start works + if [ "$val" = "$recoverydir" ]; then + return 0 + fi + + # + # If the value is not default, there may be another + # cluster service here already which has replaced + # the v4 recovery directory. In that case, + # we must refuse to go any further. + # + if [ "$val" != "$V4RECOVERY" ]; then + ocf_log err "NFSv4 recovery directory has an unexpected value: $val" + return 1 + fi + + # + # Redirect nfs v4 recovery dir to shared storage + # + echo "$recoverydir" > "$PROC_V4RECOVERY" + if [ $? -ne 0 ]; then + echo "Uh oh... echo failed!?" + fi + + val="$(cat $PROC_V4RECOVERY)" + if [ "$val" != "$recoverydir" ]; then + ocf_log err "Failed to change NFSv4 recovery path" + ocf_log err "Wanted: $recoverydir; got $val" + return 1 + fi + + return 0 +} + + +cleanup_v4recovery() +{ + # + # Restore nfsv4 recovery directory to default + # + echo "$V4RECOVERY" > "$PROC_V4RECOVERY" + return $? +} + + +is_bound() +{ + mount | grep -q "$1 on $2 type none (.*bind.*)" + return $? +} + + +setup_tree() +{ + declare fp="$OCF_RESKEY_path/$OCF_RESKEY_nfspath" + + if is_bound $fp/statd /var/lib/nfs/statd; then + ocf_log debug "$fp is already bound to /var/lib/nfs/statd" + return 0 + fi + + mount -o bind "$fp/statd" /var/lib/nfs/statd + cp -a "$fp"/*tab /var/lib/nfs + [ $SELINUX_ENABLED ] && restorecon /var/lib/nfs +} + + +cleanup_tree() +{ + declare fp="$OCF_RESKEY_path/$OCF_RESKEY_nfspath" + + if is_bound "$fp/statd" /var/lib/nfs/statd; then + log_do umount /var/lib/nfs/statd || return 1 + else + ocf_log debug "$fp is not bound to /var/lib/nfs/statd" + fi + + cp -a /var/lib/nfs/*tab "$fp" + + return 0 +} + +start_locking() +{ + declare ret + declare statdport="" + [ -x /sbin/rpc.statd ] || return 1 + + # + # Synchronize these before starting statd + # + cp -f /var/lib/nfs/statd/sm-ha/* /var/lib/nfs/statd/sm 2> /dev/null + cp -f /var/lib/nfs/statd/sm/* /var/lib/nfs/statd/sm-ha 2> /dev/null + + if pidof rpc.statd &> /dev/null; then + ocf_log debug "rpc.statd is already running" + return 0 + fi + + if [ -n "$OCF_RESKEY_statdport" ]; then + statdport="-p $OCF_RESKEY_statdport" + fi + + # + # Set this resrouce script as the callout program. We are evil. + # In cases where we want to preserve lock information, this is needed + # because we can't do the "copy" that we do on the down-state... + # + ocf_log info "Starting rpc.statd" + rm -f /var/run/sm-notify.pid + rpc.statd -H $0 -d $statdport + 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=$(pidof $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=$(pidof $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 +} + +stop_locking() +{ + 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=1 + fi + + stop_process rpc.statd + if [ $? -ne 0 ]; then + ret=1 + fi + + return $ret +} + + +case $1 in +start) + # Check for and source configuration file + ocf_log info "Starting NFS Server $OCF_RESKEY_name" + create_tree || exit 1 + setup_tree || exit 1 + setup_v4recovery || exit 1 + + start_locking + nfs_daemons start + rv=$? + if [ $rv -eq 0 ]; then + ocf_log info "Started NFS Server $OCF_RESKEY_name" + exit 0 + fi + + ocf_log err "Failed to start NFS Server $OCF_RESKEY_name" + exit $rv + ;; + +status|monitor) + nfs_daemons status + exit $? + ;; + +stop) + if ! nfs_daemons stop; then + ocf_log err "Failed to stop NFS Server $OCF_RESKEY_name" + exit $OCF_ERR_GENERIC + fi + + # Copy the current notify list into our private area + ocf_log debug "Copying sm files for future notification..." + rm -f /var/lib/nfs/statd/sm-ha/* &> /dev/null + cp -f /var/lib/nfs/statd/sm/* /var/lib/nfs/statd/sm-ha &> /dev/null + + stop_locking || exit 1 + cleanup_v4recovery + cleanup_tree || exit 1 + exit 0 + ;; + +add-client) + ocf_log debug "$0 $1 $2 $3" + touch /var/lib/nfs/statd/sm/$2 + touch /var/lib/nfs/statd/sm-ha/$2 + exit 0 + ;; + +del-client) + ocf_log debug "$0 $1 $2 $3" + touch /var/lib/nfs/statd/sm/$2 + rm -f /var/lib/nfs/statd/sm-ha/$2 + exit 0 + ;; + +recover|restart) + $0 stop || exit $OCF_ERR_GENERIC + $0 start || exit $OCF_ERR_GENERIC + exit 0 + ;; + +meta-data) + meta_data + exit 0 + ;; + +validate-all) + verify_all + exit $? + ;; +*) + echo "usage: $0 {start|stop|status|monitor|restart|recover|add-client|del-client|meta-data|validate-all}" + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac + +exit 0 |