ctdb-doc: Fix monitoring bug in example NFS Ganesha call-out
[samba.git] / ctdb / doc / examples / nfs-ganesha-callout
1 #!/bin/sh
2
3 # This is an example CTDB NFS callout script for Ganesha.  It is based
4 # on the last version of 60.ganesha shipped with CTDB.  As such, it
5 # does not try to monitor RPC services that were not monitored by
6 # 60.ganesha - this might be a useful improvement.  It has also not
7 # been properly tested.
8
9 # You should check your version of NFS Ganesha to see if it ships with
10 # a newer callout.
11
12 # To use this:
13 #
14 # * Set CTDB_NFS_CALLOUT in your CTDB configuration to point to this
15 #   script
16 #
17 # * Rename the following files in nfs-checks.d so that they no longer
18 #   have the ".check" suffix:
19 #     * 10.status.check
20 #     * 20.nfs.check
21 #     * 30.nlockmgr.check
22 #     * 40.mountd.check
23 #     * 50.rquotad.check
24 #
25 # * Install 20.nfs-ganesha.check to nfs-checks.d/20.nfs.check
26 #
27 # * It is recommended, but not required, to install the grace_period
28 #   script (usually shipped in a utils package for NFS-Ganesha) to
29 #   /usr/bin/grace_period
30
31 # I (Martin Schwenke) hereby relicense all of my contributions to this
32 # callout (and, previously, to 60.ganesha) to a license compatible
33 # with NFS Ganesha (right now this is LGPLv3, but I'm flexible).
34 # There may be other contributions to be considered for relicensing,
35 # particularly those in commit 28cbe527d47822f870e8252495ab2a1c8fddd12f.
36
37 ######################################################################
38
39 # Exit on 1st error
40 set -e
41
42 # Filesystem type and mount point for the (typically clustered)
43 # volume that will contain the NFS-Ganesha state.
44 state_fs="${CTDB_NFS_STATE_FS_TYPE:-gpfs}"
45 state_dir="${CTDB_NFS_STATE_MNT}" # No sane default.
46
47 # To change the following, edit the default values below.  Do not set
48 # these - they aren't configuration variables, just hooks for testing.
49 nfs_exports_file="${CTDB_NFS_EXPORTS_FILE:-/etc/ganesha/ganesha.conf}"
50 nfs_service="${CTDB_NFS_SERVICE:-nfs-ganesha}"
51 ganesha_rec_subdir=${CTDB_GANESHA_REC_SUBDIR:-.ganesha}
52 procfs=${PROCFS_PATH:-/proc}
53
54 case "$state_fs" in
55 gpfs)
56         GANRECDIR="/var/lib/nfs/ganesha"
57         ;;
58 glusterfs)
59         if [ -z "${state_dir}" ]; then
60                 echo "CTDB_NFS_STATE_MNT not defined for GlusterFS"
61                 exit 1
62         fi
63         host=$(hostname)
64         NODESTATEDIR="$state_dir/nfs-ganesha/$host"
65         GANSTATEDIR="$state_dir/nfs-ganesha/.noderefs"
66         NODESTATELN="$GANSTATEDIR/$host"
67         ;;
68 esac
69
70
71 ##################################################
72
73 usage ()
74 {
75         _c=$(basename "$0")
76         cat <<EOF
77 usage: $_c { shutdown | startup }
78        $_c { stop | start | check } nfs
79        $_c { releaseip | takeip }
80        $_c { monitor-list-shares }
81 EOF
82     exit 1
83 }
84
85
86 ##################################################
87 # Basic service stop and start
88
89 basic_stop ()
90 {
91         case "$1" in
92         nfs)
93                 service "$nfs_service" stop
94                 ;;
95         *)
96                 usage
97         esac
98 }
99
100 basic_start ()
101 {
102         case "$1" in
103         nfs)
104                 service "$nfs_service" start
105                 ;;
106         *)
107                 usage
108         esac
109 }
110
111 ##################################################
112 # "stop" and "start" options for restarting
113
114 service_stop ()
115 {
116     case "$1" in
117         nfs)
118             basic_stop "nfs"
119             ;;
120         nlockmgr)
121             # Do nothing - used by statd-callout
122             :
123             ;;
124         *)
125             usage
126     esac
127 }
128
129 service_start ()
130 {
131         case "$1" in
132         nfs)
133                 basic_start "nfs"
134                 ;;
135         nlockmgr)
136                 # Do nothing - used by statd-callout
137                 :
138                 ;;
139         *)
140                 usage
141         esac
142 }
143
144 ##################################################
145 # Nitty gritty - monitoring and IP handling
146
147 # Check that a symlink exists, create it otherwise.
148 # Usage: check_ln <TARGET> <LINK>
149 check_ln ()
150 {
151         if [ ! -L "${2}" ] ; then
152                 rm -vrf "${2}"
153         else
154                 _t=$(readlink "${2}")
155                 if [ "$_t" != "${1}" ] ; then
156                         rm -v "${2}"
157                 fi
158         fi
159         # This is not an "else".  It also re-creates the link if it was
160         # removed above!
161         if [ ! -e "${2}" ]; then
162                 ln -sfv "${1}" "${2}"
163         fi
164 }
165
166 # Return 'active' if the shared filesystem is accessible.
167 get_cluster_fs_state ()
168 {
169         case $state_fs in
170         gpfs)
171                 /usr/lpp/mmfs/bin/mmgetstate | awk 'NR == 4 { print $3 }'
172                 ;;
173         glusterfs)
174                 # Since we're past create_ganesha_recdirs(), we're active.
175                 echo "active"
176                 ;;
177         *)
178                 echo "File system $state_fs not supported"
179                 exit 1
180                 ;;
181         esac
182 }
183
184 create_ganesha_recdirs ()
185 {
186         if ! _mounts=$(mount | grep "$state_fs"); then
187                 echo "Failed to find mounts of type $state_fs"
188                 exit 1
189         fi
190         if [ -z "$_mounts" ]; then
191                 echo "startup $state_fs not ready"
192                 exit 0
193         fi
194
195         case $state_fs in
196         gpfs)
197                 _mntpt=$(echo "$_mounts" | sort | awk 'NR == 1 {print $3}')
198                 _link_dst="${_mntpt}/${ganesha_rec_subdir}"
199                 mkdir -vp "$_link_dst"
200                 check_ln "$_link_dst" "$GANRECDIR"
201                 ;;
202         glusterfs)
203                 [ -d /var/lib/nfs.backup ] || \
204                         mv /var/lib/nfs /var/lib/nfs.backup
205                 check_ln "$NODESTATEDIR" /var/lib/nfs
206
207                 mkdir -p "${NODESTATEDIR}/ganesha/v4recov"
208                 mkdir -p "${NODESTATEDIR}/ganesha/v4old"
209                 mkdir -p "${NODESTATEDIR}/statd/sm"
210                 mkdir -p "${NODESTATEDIR}/statd/sm.bak"
211                 touch "${NODESTATEDIR}/state"
212                 touch "${NODESTATEDIR}/statd/state"
213
214                 mkdir -p "$GANSTATEDIR"
215                 check_ln "$NODESTATEDIR" "$NODESTATELN"
216                 for _dir in "${GANSTATEDIR}/"* ; do
217                         # Handle no directories case
218                         if [ ! -d "$_dir" ] ; then
219                                 break
220                         fi
221
222                         _node="${_dir##*/}" # basename
223                         if [ "${_node}" != "${host}" ]; then
224                                 check_ln "${GANSTATEDIR}/${_node}/ganesha" \
225                                          "${NODESTATEDIR}/ganesha/${_node}"
226                                 check_ln "${GANSTATEDIR}/${_node}/statd" \
227                                          "${NODESTATEDIR}/statd/${_node}"
228                         fi
229                 done
230                 ;;
231         esac
232 }
233
234 service_check ()
235 {
236         create_ganesha_recdirs
237
238         # Always succeed if cluster filesystem is not active
239         _cluster_fs_state=$(get_cluster_fs_state)
240         if [ "$_cluster_fs_state" != "active" ] ; then
241                 return 0
242         fi
243
244         # Check that NFS Ganesha is running, according to PID file
245         _pidfile="/var/run/ganesha.pid"
246         _ganesha="/usr/bin/ganesha.nfsd"
247         if ! { read -r _pid < "$_pidfile" && \
248                 grep "$_ganesha" "${procfs}/${_pid}/cmdline" ; } >/dev/null 2>&1
249         then
250
251                 echo "ERROR: NFS Ganesha not running according to PID file"
252                 return 1
253         fi
254
255         return 0
256 }
257
258 #-------------------------------------------------
259
260 nfs_releaseip ()
261 {
262         if [ -x "/usr/bin/grace_period" ]; then
263                 /usr/bin/grace_period "2:${2}"
264         else
265                 dbus-send --print-reply --system --dest=org.ganesha.nfsd \
266                           /org/ganesha/nfsd/admin org.ganesha.nfsd.admin.grace \
267                           string:"2:${2}"
268         fi
269 }
270
271 nfs_takeip ()
272 {
273         case  $state_fs in
274         glusterfs)
275                 check_ln "$NODESTATEDIR" "${GANSTATEDIR}/${2}"
276                 ;;
277         esac
278         if [ -x "/usr/bin/grace_period" ]; then
279                 /usr/bin/grace_period "5:${2}"
280         else
281                 dbus-send --print-reply --system --dest=org.ganesha.nfsd \
282                           /org/ganesha/nfsd/admin org.ganesha.nfsd.admin.grace \
283                           string:"5:${2}"
284         fi
285 }
286
287 ##################################################
288 # service init startup and final shutdown
289
290 nfs_shutdown ()
291 {
292         basic_stop "nfs"
293 }
294
295 nfs_startup ()
296 {
297         basic_stop "nfs" || true
298
299         create_ganesha_recdirs
300
301         basic_start "nfs"
302         _f="${procfs}/sys/net/ipv4/tcp_tw_recycle"
303         if [ "$_f" ] ; then
304                 echo 1 >"$_f"
305         fi
306 }
307
308 ##################################################
309 # list share directories
310
311 nfs_monitor_list_shares ()
312 {
313         grep Path "$nfs_exports_file" |
314                 cut -f2 -d\" |
315                 sort -u
316 }
317
318 ##################################################
319
320 nfs_register ()
321 {
322         cat <<EOF
323 shutdown
324 startup
325 stop
326 start
327 monitor-pre
328 releaseip
329 takeip
330 monitor-list-shares
331 EOF
332 }
333
334 ##################################################
335
336 action="$1"
337 shift
338
339 case "$action" in
340 shutdown)            nfs_shutdown            ;;
341 startup)             nfs_startup             ;;
342 stop)                service_stop "$1"       ;;
343 start)               service_start "$1"      ;;
344 monitor-pre)         service_check "$1"      ;;
345 releaseip)           nfs_releaseip "$@"      ;;
346 takeip)              nfs_takeip "$@"         ;;
347 monitor-list-shares) nfs_monitor_list_shares ;;
348 register)            nfs_register            ;;
349 monitor-post|releaseip-pre|takeip-pre)
350         # Not required/implemented
351         :
352         ;;
353 *)
354         usage
355 esac