xref: /freebsd/tests/sys/geom/class/multipath/misc.sh (revision 62cfcf62f627e5093fb37026a6d8c98e4d2ef04c)
1#!/bin/sh
2# Copyright (c) 2019 Axcient
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions
6# are met:
7# 1. Redistributions of source code must retain the above copyright
8#    notice, this list of conditions and the following disclaimer.
9# 2. Redistributions in binary form must reproduce the above copyright
10#    notice, this list of conditions and the following disclaimer in the
11#    documentation and/or other materials provided with the distribution.
12#
13# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23# SUCH DAMAGE.
24#
25# $FreeBSD$
26
27. $(atf_get_srcdir)/conf.sh
28
29atf_test_case add cleanup
30add_head()
31{
32	atf_set "descr" "Add a new path"
33	atf_set "require.user" "root"
34}
35add_body()
36{
37	load_gmultipath
38	load_dtrace
39
40	md0=$(alloc_md)
41	md1=$(alloc_md)
42	md2=$(alloc_md)
43	name=$(mkname)
44	atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1}
45	check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE"
46
47	# Add a new path
48	atf_check -s exit:0 gmultipath add "$name" ${md2}
49	check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE"
50}
51add_cleanup()
52{
53	common_cleanup
54}
55
56atf_test_case create_A cleanup
57create_A_head()
58{
59	atf_set "descr" "Create an Active/Active multipath device"
60	atf_set "require.user" "root"
61}
62create_A_body()
63{
64	load_gmultipath
65	load_dtrace
66
67	md0=$(alloc_md)
68	md1=$(alloc_md)
69	name=$(mkname)
70	atf_check -s exit:0 gmultipath create -A "$name" ${md0} ${md1}
71	check_multipath_state "${md1} ${md0}" "OPTIMAL" "ACTIVE" "ACTIVE"
72}
73create_A_cleanup()
74{
75	common_cleanup
76}
77
78atf_test_case create_R cleanup
79create_R_head()
80{
81	atf_set "descr" "Create an Active/Read multipath device"
82	atf_set "require.user" "root"
83}
84create_R_body()
85{
86	load_gmultipath
87	load_dtrace
88
89	md0=$(alloc_md)
90	md1=$(alloc_md)
91	name=$(mkname)
92	atf_check -s exit:0 gmultipath create -R "$name" ${md0} ${md1}
93	check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "READ"
94}
95create_R_cleanup()
96{
97	common_cleanup
98}
99
100atf_test_case depart_and_arrive cleanup
101depart_and_arrive_head()
102{
103	atf_set "descr" "gmultipath should remove devices that disappear, and automatically reattach labeled providers that reappear"
104	atf_set "require.user" "root"
105}
106depart_and_arrive_body()
107{
108	load_gnop
109	load_gmultipath
110	md0=$(alloc_md)
111	md1=$(alloc_md)
112	name=$(mkname)
113	# We need a non-zero offset to gmultipath won't see the label when it
114	# tastes the md device.  We only want the label to be visible on the
115	# gnop device.
116	offset=131072
117	atf_check gnop create -o $offset /dev/${md0}
118	atf_check gnop create -o $offset /dev/${md1}
119	atf_check -s exit:0 gmultipath label "$name" ${md0}.nop
120	# gmultipath is too smart to let us create a gmultipath device by label
121	# when the two providers aren't actually related.  So we create a
122	# device by label with one provider, and then manually add the second.
123	atf_check -s exit:0 gmultipath add "$name" ${md1}.nop
124	NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'`
125	atf_check_equal 2 $NDEVS
126
127	# Now fail the labeled provider
128	atf_check -s exit:0 gnop destroy -f ${md0}.nop
129	# It should be automatically removed from the multipath device
130	NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'`
131	atf_check_equal 1 $NDEVS
132
133	# Now return the labeled provider
134	atf_check gnop create -o $offset /dev/${md0}
135	# It should be automatically restored to the multipath device.  We
136	# don't really care which path is active.
137	NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'`
138	atf_check_equal 2 $NDEVS
139	STATE=`gmultipath list "$name" | awk '/^State:/ {print $2}'`
140	atf_check_equal "OPTIMAL" $STATE
141}
142depart_and_arrive_cleanup()
143{
144	common_cleanup
145}
146
147
148atf_test_case fail cleanup
149fail_head()
150{
151	atf_set "descr" "Manually fail a path"
152	atf_set "require.user" "root"
153}
154fail_body()
155{
156	load_gmultipath
157	md0=$(alloc_md)
158	md1=$(alloc_md)
159	name=$(mkname)
160	atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1}
161	check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE"
162	# Manually fail the active path
163	atf_check -s exit:0 gmultipath fail "$name" ${md0}
164	check_multipath_state ${md1} "DEGRADED" "FAIL" "ACTIVE"
165}
166fail_cleanup()
167{
168	common_cleanup
169}
170
171atf_test_case fail_on_error cleanup
172fail_on_error_head()
173{
174	atf_set "descr" "An error in the provider will cause gmultipath to mark it as FAIL"
175	atf_set "require.user" "root"
176}
177fail_on_error_body()
178{
179	load_gnop
180	load_gmultipath
181	md0=$(alloc_md)
182	md1=$(alloc_md)
183	name=$(mkname)
184	atf_check gnop create /dev/${md0}
185	atf_check gnop create /dev/${md1}
186	atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop
187	# The first I/O to the first path should fail, causing gmultipath to
188	# fail over to the second path.
189	atf_check gnop configure -r 100 -w 100 ${md0}.nop
190	atf_check -s exit:0 -o ignore -e ignore dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1
191	check_multipath_state ${md1}.nop "DEGRADED" "FAIL" "ACTIVE"
192}
193fail_on_error_cleanup()
194{
195	common_cleanup
196}
197
198atf_test_case physpath cleanup
199physpath_head()
200{
201	atf_set "descr" "gmultipath should pass through the underlying providers' physical path"
202	atf_set "require.user" "root"
203}
204physpath_body()
205{
206	load_gnop
207	load_gmultipath
208	md0=$(alloc_md)
209	md1=$(alloc_md)
210	name=$(mkname)
211	physpath="some/physical/path"
212	# Create two providers with the same physical paths, mimicing how
213	# multipathed SAS drives appear.  This is the normal way to use
214	# gmultipath.  If the underlying providers' physical paths differ,
215	# then you're probably using gmultipath wrong.
216	atf_check gnop create -z $physpath /dev/${md0}
217	atf_check gnop create -z $physpath /dev/${md1}
218	atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop
219	gmultipath_physpath=$(diskinfo -p multipath/"$name")
220	atf_check_equal "$physpath" "$gmultipath_physpath"
221}
222physpath_cleanup()
223{
224	common_cleanup
225}
226
227atf_test_case prefer cleanup
228prefer_head()
229{
230	atf_set "descr" "Manually select the preferred path"
231	atf_set "require.user" "root"
232}
233prefer_body()
234{
235	load_gmultipath
236	load_dtrace
237
238	md0=$(alloc_md)
239	md1=$(alloc_md)
240	md2=$(alloc_md)
241	name=$(mkname)
242	atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} ${md2}
243	check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE"
244
245	# Explicitly prefer the final path
246	atf_check -s exit:0 gmultipath prefer "$name" ${md2}
247	check_multipath_state ${md2} "OPTIMAL" "PASSIVE" "PASSIVE" "ACTIVE"
248}
249prefer_cleanup()
250{
251	common_cleanup
252}
253
254atf_test_case restore cleanup
255restore_head()
256{
257	atf_set "descr" "Manually restore a failed path"
258	atf_set "require.user" "root"
259}
260restore_body()
261{
262	load_gmultipath
263	load_dtrace
264
265	md0=$(alloc_md)
266	md1=$(alloc_md)
267	name=$(mkname)
268	atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1}
269
270	# Explicitly fail the first path
271	atf_check -s exit:0 gmultipath fail "$name" ${md0}
272	check_multipath_state ${md1} "DEGRADED" "FAIL" "ACTIVE"
273
274	# Explicitly restore it
275	atf_check -s exit:0 gmultipath restore "$name" ${md0}
276	check_multipath_state ${md1} "OPTIMAL" "PASSIVE" "ACTIVE"
277}
278restore_cleanup()
279{
280	common_cleanup
281}
282
283atf_test_case restore_on_error cleanup
284restore_on_error_head()
285{
286	atf_set "descr" "A failed path should be restored if an I/O error is encountered on all other active paths"
287	atf_set "require.user" "root"
288}
289restore_on_error_body()
290{
291	load_gnop
292	load_gmultipath
293	load_dtrace
294
295	md0=$(alloc_md)
296	md1=$(alloc_md)
297	name=$(mkname)
298	atf_check gnop create /dev/${md0}
299	atf_check gnop create /dev/${md1}
300	atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop
301	# Explicitly fail the first path
302	atf_check -s exit:0 gmultipath fail "$name" ${md0}.nop
303
304	# Setup the second path to fail on the next I/O
305	atf_check gnop configure -r 100 -w 100  ${md1}.nop
306	atf_check -s exit:0 -o ignore -e ignore \
307	    dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1
308
309	# Now the first path should be active, and the second should be failed
310	check_multipath_state ${md0}.nop "DEGRADED" "ACTIVE" "FAIL"
311}
312restore_on_error_cleanup()
313{
314	common_cleanup
315}
316
317atf_test_case rotate cleanup
318rotate_head()
319{
320	atf_set "descr" "Manually rotate the active path"
321	atf_set "require.user" "root"
322}
323rotate_body()
324{
325	load_gmultipath
326	load_dtrace
327
328	md0=$(alloc_md)
329	md1=$(alloc_md)
330	md2=$(alloc_md)
331	name=$(mkname)
332	atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} ${md2}
333	check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE"
334
335	# Explicitly rotate the paths
336	atf_check -s exit:0 gmultipath rotate "$name"
337	check_multipath_state ${md2} "OPTIMAL" "PASSIVE" "PASSIVE" "ACTIVE"
338	# Again
339	atf_check -s exit:0 gmultipath rotate "$name"
340	check_multipath_state ${md1} "OPTIMAL" "PASSIVE" "ACTIVE" "PASSIVE"
341	# Final rotation should restore original configuration
342	atf_check -s exit:0 gmultipath rotate "$name"
343	check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE"
344}
345rotate_cleanup()
346{
347	common_cleanup
348}
349
350atf_init_test_cases()
351{
352	atf_add_test_case add
353	atf_add_test_case create_A
354	atf_add_test_case create_R
355	atf_add_test_case depart_and_arrive
356	atf_add_test_case fail
357	atf_add_test_case fail_on_error
358	atf_add_test_case physpath
359	atf_add_test_case prefer
360	atf_add_test_case restore
361	atf_add_test_case restore_on_error
362	atf_add_test_case rotate
363}
364