1#!@BASH_SHELL@
2
3#
4# NFS/CIFS file system mount/umount/etc. agent
5#
6#
7# Copyright (C) 1997-2003 Sistina Software, Inc.  All rights reserved.
8# Copyright (C) 2004-2011 Red Hat, Inc.  All rights reserved.
9#
10# This program is free software; you can redistribute it and/or
11# modify it under the terms of the GNU General Public License
12# as published by the Free Software Foundation; either version 2
13# of the License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23#
24
25. $(dirname $0)/utils/fs-lib.sh
26
27do_metadata()
28{
29	cat <<EOT
30<?xml version="1.0" ?>
31<!DOCTYPE resource-agent SYSTEM "ra-api-1-modified.dtd">
32<resource-agent name="netfs" version="rgmanager 2.0">
33    <version>1.0</version>
34
35    <longdesc lang="en">
36        This defines an NFS/CIFS mount for use by cluster services.
37    </longdesc>
38    <shortdesc lang="en">
39        Defines an NFS/CIFS file system mount.
40    </shortdesc>
41
42    <parameters>
43        <parameter name="name" primary="1">
44	    <longdesc lang="en">
45	        Symbolic name for this file system.
46	    </longdesc>
47            <shortdesc lang="en">
48                File System Name
49            </shortdesc>
50	    <content type="string"/>
51        </parameter>
52
53        <parameter name="mountpoint" unique="1" required="1">
54	    <longdesc lang="en">
55	        Path in file system heirarchy to mount this file system.
56	    </longdesc>
57            <shortdesc lang="en">
58                Mount Point
59            </shortdesc>
60	    <content type="string"/>
61        </parameter>
62
63        <parameter name="host" required="1">
64	    <longdesc lang="en">
65	    	Server IP address or hostname
66	    </longdesc>
67            <shortdesc lang="en">
68	    	IP or Host
69            </shortdesc>
70	    <content type="string"/>
71        </parameter>
72
73        <parameter name="export" required="1">
74	    <longdesc lang="en">
75	    	NFS Export directory name or CIFS share
76	    </longdesc>
77            <shortdesc lang="en">
78	    	Export
79            </shortdesc>
80	    <content type="string"/>
81        </parameter>
82
83        <parameter name="fstype">
84	    <longdesc lang="en">
85	    	File System type (nfs, nfs4 or cifs)
86	    </longdesc>
87            <shortdesc lang="en">
88	    	File System Type
89            </shortdesc>
90	    <content type="string" default="nfs"/>
91        </parameter>
92
93        <parameter name="no_unmount" required="0">
94	    <longdesc lang="en">
95	    	Do not unmount the filesystem during a stop or relocation operation
96	    </longdesc>
97            <shortdesc lang="en">
98	    	Skip unmount opration
99            </shortdesc>
100	    <content type="boolean"/>
101        </parameter>
102
103        <parameter name="force_unmount">
104            <longdesc lang="en">
105                If set, the cluster will kill all processes using 
106                this file system when the resource group is 
107                stopped.  Otherwise, the unmount will fail, and
108                the resource group will be restarted.
109            </longdesc>
110            <shortdesc lang="en">
111                Force Unmount
112            </shortdesc>
113	    <content type="boolean"/>
114        </parameter>
115
116        <parameter name="self_fence">
117            <longdesc lang="en">
118                If set and unmounting the file system fails, the node will
119                immediately reboot.  Generally, this is used in conjunction
120                with force_unmount support, but it is not required.
121            </longdesc>
122            <shortdesc lang="en">
123                Seppuku Unmount
124            </shortdesc>
125            <content type="boolean"/>
126        </parameter>
127
128        <parameter name="options">
129            <longdesc lang="en">
130	    	Provides a list of mount options.  If none are specified,
131		the NFS file system is mounted -o sync.
132            </longdesc>
133            <shortdesc lang="en">
134                Mount Options
135            </shortdesc>
136	    <content type="string"/>
137        </parameter>
138
139        <parameter name="use_findmnt">
140            <longdesc lang="en">
141        Use findmnt to determine if and where a filesystem is mounted.
142        Disabling this uses the failback method (should be used if autofs
143        maps are located on network storage (ie. nfs, iscsi, etc).
144            </longdesc>
145            <shortdesc lang="en">
146        Utilize findmnt to detect if and where filesystems are mounted
147            </shortdesc>
148            <content type="boolean"/>
149        </parameter>
150
151    </parameters>
152
153    <actions>
154        <action name="start" timeout="900"/>
155	<action name="stop" timeout="30"/>
156	<!-- Recovery isn't possible; we don't know if resources are using
157	     the file system. -->
158
159	<!-- Checks to see if it's mounted in the right place -->
160	<action name="status" interval="1m" timeout="10"/>
161	<action name="monitor" interval="1m" timeout="10"/>
162
163	<!-- Checks to see if we can read from the mountpoint -->
164	<action name="status" depth="10" timeout="30" interval="5m"/>
165	<action name="monitor" depth="10" timeout="30" interval="5m"/>
166
167	<!-- Checks to see if we can write to the mountpoint (if !ROFS) -->
168	<action name="status" depth="20" timeout="30" interval="10m"/>
169	<action name="monitor" depth="20" timeout="30" interval="10m"/>
170
171	<action name="meta-data" timeout="5"/>
172	<action name="validate-all" timeout="5"/>
173    </actions>
174
175    <special tag="rgmanager">
176        <child type="nfsexport" forbid="1"/>
177        <child type="nfsclient" forbid="1"/>
178    </special>
179</resource-agent>
180EOT
181}
182
183
184verify_host()
185{
186	if [ -z "$OCF_RESKEY_host" ]; then
187	       ocf_log err "No server hostname or IP address specified."
188	       return 1
189	fi
190
191	host $OCF_RESKEY_host 2>&1 | grep -vq "not found"
192	if [ $? -eq 0 ]; then
193		return 0
194	fi
195
196	ocf_log err "Hostname or IP address \"$OCF_RESKEY_host\" not valid"
197
198	return $OCF_ERR_ARGS
199}
200
201
202verify_fstype()
203{
204	# Auto detect?
205	[ -z "$OCF_RESKEY_fstype" ] && return 0
206
207	case $OCF_RESKEY_fstype in
208	nfs|nfs4|cifs)
209		return 0
210		;;
211	*)
212		ocf_log err "File system type $OCF_RESKEY_fstype not supported"
213		return $OCF_ERR_ARGS
214		;;
215	esac
216}
217
218
219verify_options()
220{
221	declare -i ret=0
222
223	#
224	# From mount(1)
225	#
226	for o in `echo $OCF_RESKEY_options | sed -e s/,/\ /g`; do
227		case $o in
228		async|atime|auto|defaults|dev|exec|_netdev|noatime)
229			continue
230			;;
231		noauto|nodev|noexec|nosuid|nouser|ro|rw|suid|sync)
232			continue
233			;;
234		dirsync|user|users)
235			continue
236			;;
237		esac
238
239		case $OCF_RESKEY_fstype in
240		cifs)
241			continue
242			;;
243		nfs|nfs4)
244			case $o in
245			#
246			# NFS / NFS4 common
247			#
248			rsize=*|wsize=*|timeo=*|retrans=*|acregmin=*)
249				continue
250				;;
251			acregmax=*|acdirmin=*|acdirmax=*|actimeo=*)
252				continue
253				;;
254			retry=*|port=*|bg|fg|soft|hard|intr|cto|ac|noac)
255				continue
256				;;
257			esac
258
259			#
260			# NFS v2/v3 only
261			#
262			if [ "$OCF_RESKEY_fstype" = "nfs" ]; then
263				case $o in
264				mountport=*|mounthost=*)
265					continue
266					;;
267				mountprog=*|mountvers=*|nfsprog=*|nfsvers=*)
268					continue
269					;;
270				namelen=*)
271					continue
272					;;
273				tcp|udp|lock|nolock)
274					continue
275					;;
276				esac
277			fi
278
279			#
280			# NFS4 only
281			#
282			if [ "$OCF_RESKEY_fstype" = "nfs4" ]; then
283				case $o in
284				proto=*|clientaddr=*|sec=*)
285					continue
286					;;
287				esac
288			fi
289
290			;;
291		esac
292
293		ocf_log err "Option $o not supported for $OCF_RESKEY_fstype"
294		ret=$OCF_ERR_ARGS
295	done
296
297	return $ret
298}
299
300
301do_validate()
302{
303	verify_name || return $OCF_ERR_ARGS
304	verify_fstype|| return $OCF_ERR_ARGS
305	verify_host || return $OCF_ERR_ARGS
306	verify_mountpoint || return $OCF_ERR_ARGS
307	verify_options || return $OCF_ERR_ARGS
308	# verify_target || return $OCF_ERR_ARGS
309}
310
311
312#
313# Override real_device to use fs-lib's functions for start/stop_filesystem
314#
315real_device() {
316	export REAL_DEVICE="$1"
317}
318
319
320#
321# do_mount - nfs / cifs are mounted differently than blockdevs
322#
323do_mount() {
324	declare opts=""
325	declare mount_options=""
326	declare ret_val
327	declare mp="$OCF_RESKEY_mountpoint"
328
329	#
330	# Get the filesystem type, if specified.
331	#
332	fstype_option=""
333	fstype=${OCF_RESKEY_fstype}
334		case "$fstype" in
335	""|"[ 	]*")
336		fstype=""
337		;;
338	*)	# found it
339		fstype_option="-t $fstype"
340		;;
341	esac
342
343	#
344	# Get the mount options, if they exist.
345	#
346	mount_options=""
347	opts=${OCF_RESKEY_options}
348	case "$opts" in
349	""|"[ 	]*")
350		opts=""
351		;;
352	*)	# found it
353		mount_options="-o $opts"
354		;;
355	esac
356
357        case $OCF_RESKEY_fstype in
358	nfs|nfs4)
359		mount -t $OCF_RESKEY_fstype $mount_options $OCF_RESKEY_host:"$OCF_RESKEY_export" "$mp"
360		;;
361	cifs)
362		mount -t $OCF_RESKEY_fstype $mount_options //$OCF_RESKEY_host/"$OCF_RESKEY_export" "$mp"
363		;;
364	esac
365
366	ret_val=$?
367	if [ $ret_val -ne 0 ]; then
368		ocf_log err "\
369'mount $fstype_option $mount_options $OCF_RESKEY_host:$OCF_RESKEY_export $mp' failed, error=$ret_val"
370		return 1
371	fi
372
373	return 0
374}
375
376do_nfs_rpc_check() {
377	# see man nfs TRANSPORT PROTOCOL section for defaults
378	local nfsproto=tcp
379	local nfsmountproto=udp
380
381	# follow the same logic as mount.nfs option parser.
382	# the rightmost option wins over previous ones, so don't break when
383	# we find something.
384
385	for o in $(echo ${OCF_RESKEY_options} | sed -e s/,/\ /g); do
386		if echo $o | grep -q "^proto=" ; then
387			nfsproto="$(echo $o | cut -d "=" -f 2)"
388		fi
389		if echo $o | grep -q "^mountproto=" ; then
390			nfsmountproto="$(echo $o | cut -d "=" -f 2)"
391		fi
392		case $o in
393		tcp)	nfsproto=tcp;;
394		udp)	nfsproto=udp;;
395		rdma)	nfsproto=rdma;;
396		esac
397	done
398
399	ocf_log debug "Testing generic rpc access on server ${OCF_RESKEY_host} with protocol $nfsproto"
400	if ! rpcinfo -T $nfsproto ${OCF_RESKEY_host} > /dev/null 2>&1; then
401		ocf_log alert "RPC server on ${OCF_RESKEY_host} with $nfsproto is not responding"
402		return 1
403	fi
404
405	ocf_log debug "Testing nfs rcp access on server ${OCF_RESKEY_host} with protocol $nfsproto"
406	if ! rpcinfo -T $nfsproto ${OCF_RESKEY_host} nfs > /dev/null 2>&1; then
407		ocf_log alert "NFS server on ${OCF_RESKEY_host} with $nfsproto is not responding"
408		return 1
409	fi
410
411	if [ "$OCF_RESKEY_fstype" = nfs ]; then
412		ocf_log debug "Testing mountd rpc access on server ${OCF_RESKEY_host} with protocol $nfsmountproto"
413		if ! rpcinfo -T $nfsmountproto ${OCF_RESKEY_host} mountd; then
414			ocf_log alert "MOUNTD server on ${OCF_RESKEY_host} with $nfsmountproto is not responding"
415			return 1
416		fi
417	fi
418
419	return 0
420}
421
422do_pre_unmount() {
423	case $OCF_RESKEY_fstype in
424	nfs|nfs4)
425		if [ "$self_fence" != $YES ]; then
426			ocf_log debug "Skipping pre unmount checks: self_fence is disabled"
427			return 0
428		fi
429
430		is_mounted "$dev" "$mp"
431		case $? in
432		$NO)
433			ocf_log debug "Skipping pre unmount checks: device is not mounted"
434			return 0
435			;;
436		esac
437
438		ocf_log info "pre unmount: checking if nfs server ${OCF_RESKEY_host} is alive"
439		if ! do_nfs_rpc_check; then
440			ocf_log alert "NFS server not responding - REBOOTING"
441			sleep 2
442			reboot -fn
443		fi
444		;;
445	esac
446
447	return 0
448}
449
450do_force_unmount() {
451        case $OCF_RESKEY_fstype in
452	nfs|nfs4)
453		ocf_log warning "Calling 'umount -f $mp'"
454		umount -f "$OCF_RESKEY_mountpoint"
455		return $?
456		;;
457	*)
458		;;
459	esac
460
461	return 1	# Returning 1 lets stop_filesystem do add'l checks
462}
463
464
465populate_defaults()
466{
467        case $OCF_RESKEY_fstype in
468	nfs|nfs4)
469		export OCF_RESKEY_device="$OCF_RESKEY_host:$OCF_RESKEY_export"
470		if [ -z "$OCF_RESKEY_options" ]; then
471			export OCF_RESKEY_options=sync,soft,noac
472		fi
473		;;
474	cifs)
475		export OCF_RESKEY_device="//$OCF_RESKEY_host/$OCF_RESKEY_export"
476		if [ -z "$OCF_RESKEY_options" ]; then
477			export OCF_RESKEY_options=guest
478		fi
479		;;
480	esac
481}
482
483
484#
485# Main...
486#
487populate_defaults
488main $*
489