167f72211SAlan Somers#!/bin/sh 267f72211SAlan Somers# Copyright (c) 2019 Axcient 367f72211SAlan Somers# 467f72211SAlan Somers# Redistribution and use in source and binary forms, with or without 567f72211SAlan Somers# modification, are permitted provided that the following conditions 667f72211SAlan Somers# are met: 767f72211SAlan Somers# 1. Redistributions of source code must retain the above copyright 867f72211SAlan Somers# notice, this list of conditions and the following disclaimer. 967f72211SAlan Somers# 2. Redistributions in binary form must reproduce the above copyright 1067f72211SAlan Somers# notice, this list of conditions and the following disclaimer in the 1167f72211SAlan Somers# documentation and/or other materials provided with the distribution. 1267f72211SAlan Somers# 1367f72211SAlan Somers# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1467f72211SAlan Somers# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1567f72211SAlan Somers# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1667f72211SAlan Somers# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1767f72211SAlan Somers# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1867f72211SAlan Somers# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1967f72211SAlan Somers# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2067f72211SAlan Somers# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2167f72211SAlan Somers# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2267f72211SAlan Somers# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2367f72211SAlan Somers# SUCH DAMAGE. 2467f72211SAlan Somers# 2567f72211SAlan Somers 2667f72211SAlan Somers. $(atf_get_srcdir)/conf.sh 2767f72211SAlan Somers 2867f72211SAlan Somersatf_test_case add cleanup 2967f72211SAlan Somersadd_head() 3067f72211SAlan Somers{ 3167f72211SAlan Somers atf_set "descr" "Add a new path" 3267f72211SAlan Somers atf_set "require.user" "root" 3367f72211SAlan Somers} 3467f72211SAlan Somersadd_body() 3567f72211SAlan Somers{ 3667f72211SAlan Somers load_gmultipath 3767f72211SAlan Somers load_dtrace 3867f72211SAlan Somers 3967f72211SAlan Somers md0=$(alloc_md) 4067f72211SAlan Somers md1=$(alloc_md) 4167f72211SAlan Somers md2=$(alloc_md) 4267f72211SAlan Somers name=$(mkname) 4367f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} 4467f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" 4567f72211SAlan Somers 4667f72211SAlan Somers # Add a new path 4767f72211SAlan Somers atf_check -s exit:0 gmultipath add "$name" ${md2} 4867f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE" 4967f72211SAlan Somers} 5067f72211SAlan Somersadd_cleanup() 5167f72211SAlan Somers{ 5267f72211SAlan Somers common_cleanup 5367f72211SAlan Somers} 5467f72211SAlan Somers 5567f72211SAlan Somersatf_test_case create_A cleanup 5667f72211SAlan Somerscreate_A_head() 5767f72211SAlan Somers{ 5867f72211SAlan Somers atf_set "descr" "Create an Active/Active multipath device" 5967f72211SAlan Somers atf_set "require.user" "root" 6067f72211SAlan Somers} 6167f72211SAlan Somerscreate_A_body() 6267f72211SAlan Somers{ 6367f72211SAlan Somers load_gmultipath 6467f72211SAlan Somers load_dtrace 6567f72211SAlan Somers 6667f72211SAlan Somers md0=$(alloc_md) 6767f72211SAlan Somers md1=$(alloc_md) 6867f72211SAlan Somers name=$(mkname) 6967f72211SAlan Somers atf_check -s exit:0 gmultipath create -A "$name" ${md0} ${md1} 7067f72211SAlan Somers check_multipath_state "${md1} ${md0}" "OPTIMAL" "ACTIVE" "ACTIVE" 7167f72211SAlan Somers} 7267f72211SAlan Somerscreate_A_cleanup() 7367f72211SAlan Somers{ 7467f72211SAlan Somers common_cleanup 7567f72211SAlan Somers} 7667f72211SAlan Somers 7767f72211SAlan Somersatf_test_case create_R cleanup 7867f72211SAlan Somerscreate_R_head() 7967f72211SAlan Somers{ 8067f72211SAlan Somers atf_set "descr" "Create an Active/Read multipath device" 8167f72211SAlan Somers atf_set "require.user" "root" 8267f72211SAlan Somers} 8367f72211SAlan Somerscreate_R_body() 8467f72211SAlan Somers{ 8567f72211SAlan Somers load_gmultipath 8667f72211SAlan Somers load_dtrace 8767f72211SAlan Somers 8867f72211SAlan Somers md0=$(alloc_md) 8967f72211SAlan Somers md1=$(alloc_md) 9067f72211SAlan Somers name=$(mkname) 9167f72211SAlan Somers atf_check -s exit:0 gmultipath create -R "$name" ${md0} ${md1} 9267f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "READ" 9367f72211SAlan Somers} 9467f72211SAlan Somerscreate_R_cleanup() 9567f72211SAlan Somers{ 9667f72211SAlan Somers common_cleanup 9767f72211SAlan Somers} 9867f72211SAlan Somers 9967f72211SAlan Somersatf_test_case depart_and_arrive cleanup 10067f72211SAlan Somersdepart_and_arrive_head() 10167f72211SAlan Somers{ 10267f72211SAlan Somers atf_set "descr" "gmultipath should remove devices that disappear, and automatically reattach labeled providers that reappear" 10367f72211SAlan Somers atf_set "require.user" "root" 10467f72211SAlan Somers} 10567f72211SAlan Somersdepart_and_arrive_body() 10667f72211SAlan Somers{ 10767f72211SAlan Somers load_gnop 10867f72211SAlan Somers load_gmultipath 10967f72211SAlan Somers md0=$(alloc_md) 11067f72211SAlan Somers md1=$(alloc_md) 11167f72211SAlan Somers name=$(mkname) 11267f72211SAlan Somers # We need a non-zero offset to gmultipath won't see the label when it 11367f72211SAlan Somers # tastes the md device. We only want the label to be visible on the 11467f72211SAlan Somers # gnop device. 11567f72211SAlan Somers offset=131072 11667f72211SAlan Somers atf_check gnop create -o $offset /dev/${md0} 11767f72211SAlan Somers atf_check gnop create -o $offset /dev/${md1} 11867f72211SAlan Somers atf_check -s exit:0 gmultipath label "$name" ${md0}.nop 11967f72211SAlan Somers # gmultipath is too smart to let us create a gmultipath device by label 12067f72211SAlan Somers # when the two providers aren't actually related. So we create a 12167f72211SAlan Somers # device by label with one provider, and then manually add the second. 12267f72211SAlan Somers atf_check -s exit:0 gmultipath add "$name" ${md1}.nop 12367f72211SAlan Somers NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'` 12467f72211SAlan Somers atf_check_equal 2 $NDEVS 12567f72211SAlan Somers 12667f72211SAlan Somers # Now fail the labeled provider 12767f72211SAlan Somers atf_check -s exit:0 gnop destroy -f ${md0}.nop 12867f72211SAlan Somers # It should be automatically removed from the multipath device 12967f72211SAlan Somers NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'` 13067f72211SAlan Somers atf_check_equal 1 $NDEVS 13167f72211SAlan Somers 13267f72211SAlan Somers # Now return the labeled provider 13367f72211SAlan Somers atf_check gnop create -o $offset /dev/${md0} 13467f72211SAlan Somers # It should be automatically restored to the multipath device. We 13567f72211SAlan Somers # don't really care which path is active. 13667f72211SAlan Somers NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'` 13767f72211SAlan Somers atf_check_equal 2 $NDEVS 13867f72211SAlan Somers STATE=`gmultipath list "$name" | awk '/^State:/ {print $2}'` 13967f72211SAlan Somers atf_check_equal "OPTIMAL" $STATE 14067f72211SAlan Somers} 14167f72211SAlan Somersdepart_and_arrive_cleanup() 14267f72211SAlan Somers{ 14367f72211SAlan Somers common_cleanup 14467f72211SAlan Somers} 14567f72211SAlan Somers 14667f72211SAlan Somers 14767f72211SAlan Somersatf_test_case fail cleanup 14867f72211SAlan Somersfail_head() 14967f72211SAlan Somers{ 15067f72211SAlan Somers atf_set "descr" "Manually fail a path" 15167f72211SAlan Somers atf_set "require.user" "root" 15267f72211SAlan Somers} 15367f72211SAlan Somersfail_body() 15467f72211SAlan Somers{ 15567f72211SAlan Somers load_gmultipath 15667f72211SAlan Somers md0=$(alloc_md) 15767f72211SAlan Somers md1=$(alloc_md) 15867f72211SAlan Somers name=$(mkname) 15967f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} 16067f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" 16167f72211SAlan Somers # Manually fail the active path 16267f72211SAlan Somers atf_check -s exit:0 gmultipath fail "$name" ${md0} 16367f72211SAlan Somers check_multipath_state ${md1} "DEGRADED" "FAIL" "ACTIVE" 16467f72211SAlan Somers} 16567f72211SAlan Somersfail_cleanup() 16667f72211SAlan Somers{ 16767f72211SAlan Somers common_cleanup 16867f72211SAlan Somers} 16967f72211SAlan Somers 17067f72211SAlan Somersatf_test_case fail_on_error cleanup 17167f72211SAlan Somersfail_on_error_head() 17267f72211SAlan Somers{ 17367f72211SAlan Somers atf_set "descr" "An error in the provider will cause gmultipath to mark it as FAIL" 17467f72211SAlan Somers atf_set "require.user" "root" 17567f72211SAlan Somers} 17667f72211SAlan Somersfail_on_error_body() 17767f72211SAlan Somers{ 17867f72211SAlan Somers load_gnop 17967f72211SAlan Somers load_gmultipath 18067f72211SAlan Somers md0=$(alloc_md) 18167f72211SAlan Somers md1=$(alloc_md) 18267f72211SAlan Somers name=$(mkname) 18367f72211SAlan Somers atf_check gnop create /dev/${md0} 18467f72211SAlan Somers atf_check gnop create /dev/${md1} 18567f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop 18667f72211SAlan Somers # The first I/O to the first path should fail, causing gmultipath to 18767f72211SAlan Somers # fail over to the second path. 188deed14f4SAlan Somers atf_check gnop configure -r 100 -w 100 ${md0}.nop 18967f72211SAlan Somers atf_check -s exit:0 -o ignore -e ignore dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1 19067f72211SAlan Somers check_multipath_state ${md1}.nop "DEGRADED" "FAIL" "ACTIVE" 19167f72211SAlan Somers} 19267f72211SAlan Somersfail_on_error_cleanup() 19367f72211SAlan Somers{ 19467f72211SAlan Somers common_cleanup 19567f72211SAlan Somers} 19667f72211SAlan Somers 19767f72211SAlan Somersatf_test_case physpath cleanup 19867f72211SAlan Somersphyspath_head() 19967f72211SAlan Somers{ 200*420dbe76SAlan Somers atf_set "descr" "gmultipath should append /mp to the underlying providers' physical path" 20167f72211SAlan Somers atf_set "require.user" "root" 20267f72211SAlan Somers} 20367f72211SAlan Somersphyspath_body() 20467f72211SAlan Somers{ 20567f72211SAlan Somers load_gnop 20667f72211SAlan Somers load_gmultipath 20767f72211SAlan Somers md0=$(alloc_md) 20867f72211SAlan Somers md1=$(alloc_md) 20967f72211SAlan Somers name=$(mkname) 21067f72211SAlan Somers physpath="some/physical/path" 21167f72211SAlan Somers # Create two providers with the same physical paths, mimicing how 21267f72211SAlan Somers # multipathed SAS drives appear. This is the normal way to use 21367f72211SAlan Somers # gmultipath. If the underlying providers' physical paths differ, 21467f72211SAlan Somers # then you're probably using gmultipath wrong. 21567f72211SAlan Somers atf_check gnop create -z $physpath /dev/${md0} 21667f72211SAlan Somers atf_check gnop create -z $physpath /dev/${md1} 21767f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop 21867f72211SAlan Somers gmultipath_physpath=$(diskinfo -p multipath/"$name") 219*420dbe76SAlan Somers atf_check_equal "$physpath/mp" "$gmultipath_physpath" 22067f72211SAlan Somers} 22167f72211SAlan Somersphyspath_cleanup() 22267f72211SAlan Somers{ 22367f72211SAlan Somers common_cleanup 22467f72211SAlan Somers} 22567f72211SAlan Somers 22667f72211SAlan Somersatf_test_case prefer cleanup 22767f72211SAlan Somersprefer_head() 22867f72211SAlan Somers{ 22967f72211SAlan Somers atf_set "descr" "Manually select the preferred path" 23067f72211SAlan Somers atf_set "require.user" "root" 23167f72211SAlan Somers} 23267f72211SAlan Somersprefer_body() 23367f72211SAlan Somers{ 23467f72211SAlan Somers load_gmultipath 23567f72211SAlan Somers load_dtrace 23667f72211SAlan Somers 23767f72211SAlan Somers md0=$(alloc_md) 23867f72211SAlan Somers md1=$(alloc_md) 23967f72211SAlan Somers md2=$(alloc_md) 24067f72211SAlan Somers name=$(mkname) 24167f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} ${md2} 24267f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE" 24367f72211SAlan Somers 24467f72211SAlan Somers # Explicitly prefer the final path 24567f72211SAlan Somers atf_check -s exit:0 gmultipath prefer "$name" ${md2} 24667f72211SAlan Somers check_multipath_state ${md2} "OPTIMAL" "PASSIVE" "PASSIVE" "ACTIVE" 24767f72211SAlan Somers} 24867f72211SAlan Somersprefer_cleanup() 24967f72211SAlan Somers{ 25067f72211SAlan Somers common_cleanup 25167f72211SAlan Somers} 25267f72211SAlan Somers 25367f72211SAlan Somersatf_test_case restore cleanup 25467f72211SAlan Somersrestore_head() 25567f72211SAlan Somers{ 25667f72211SAlan Somers atf_set "descr" "Manually restore a failed path" 25767f72211SAlan Somers atf_set "require.user" "root" 25867f72211SAlan Somers} 25967f72211SAlan Somersrestore_body() 26067f72211SAlan Somers{ 26167f72211SAlan Somers load_gmultipath 26267f72211SAlan Somers load_dtrace 26367f72211SAlan Somers 26467f72211SAlan Somers md0=$(alloc_md) 26567f72211SAlan Somers md1=$(alloc_md) 26667f72211SAlan Somers name=$(mkname) 26767f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} 26867f72211SAlan Somers 26967f72211SAlan Somers # Explicitly fail the first path 27067f72211SAlan Somers atf_check -s exit:0 gmultipath fail "$name" ${md0} 27167f72211SAlan Somers check_multipath_state ${md1} "DEGRADED" "FAIL" "ACTIVE" 27267f72211SAlan Somers 27367f72211SAlan Somers # Explicitly restore it 27467f72211SAlan Somers atf_check -s exit:0 gmultipath restore "$name" ${md0} 27567f72211SAlan Somers check_multipath_state ${md1} "OPTIMAL" "PASSIVE" "ACTIVE" 27667f72211SAlan Somers} 27767f72211SAlan Somersrestore_cleanup() 27867f72211SAlan Somers{ 27967f72211SAlan Somers common_cleanup 28067f72211SAlan Somers} 28167f72211SAlan Somers 28267f72211SAlan Somersatf_test_case restore_on_error cleanup 28367f72211SAlan Somersrestore_on_error_head() 28467f72211SAlan Somers{ 28567f72211SAlan Somers atf_set "descr" "A failed path should be restored if an I/O error is encountered on all other active paths" 28667f72211SAlan Somers atf_set "require.user" "root" 28767f72211SAlan Somers} 28867f72211SAlan Somersrestore_on_error_body() 28967f72211SAlan Somers{ 29067f72211SAlan Somers load_gnop 29167f72211SAlan Somers load_gmultipath 29267f72211SAlan Somers load_dtrace 29367f72211SAlan Somers 29467f72211SAlan Somers md0=$(alloc_md) 29567f72211SAlan Somers md1=$(alloc_md) 29667f72211SAlan Somers name=$(mkname) 29767f72211SAlan Somers atf_check gnop create /dev/${md0} 29867f72211SAlan Somers atf_check gnop create /dev/${md1} 29967f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop 30067f72211SAlan Somers # Explicitly fail the first path 30167f72211SAlan Somers atf_check -s exit:0 gmultipath fail "$name" ${md0}.nop 30267f72211SAlan Somers 30367f72211SAlan Somers # Setup the second path to fail on the next I/O 30467f72211SAlan Somers atf_check gnop configure -r 100 -w 100 ${md1}.nop 30567f72211SAlan Somers atf_check -s exit:0 -o ignore -e ignore \ 30667f72211SAlan Somers dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1 30767f72211SAlan Somers 30867f72211SAlan Somers # Now the first path should be active, and the second should be failed 30967f72211SAlan Somers check_multipath_state ${md0}.nop "DEGRADED" "ACTIVE" "FAIL" 31067f72211SAlan Somers} 31167f72211SAlan Somersrestore_on_error_cleanup() 31267f72211SAlan Somers{ 31367f72211SAlan Somers common_cleanup 31467f72211SAlan Somers} 31567f72211SAlan Somers 31667f72211SAlan Somersatf_test_case rotate cleanup 31767f72211SAlan Somersrotate_head() 31867f72211SAlan Somers{ 31967f72211SAlan Somers atf_set "descr" "Manually rotate the active path" 32067f72211SAlan Somers atf_set "require.user" "root" 32167f72211SAlan Somers} 32267f72211SAlan Somersrotate_body() 32367f72211SAlan Somers{ 32467f72211SAlan Somers load_gmultipath 32567f72211SAlan Somers load_dtrace 32667f72211SAlan Somers 32767f72211SAlan Somers md0=$(alloc_md) 32867f72211SAlan Somers md1=$(alloc_md) 32967f72211SAlan Somers md2=$(alloc_md) 33067f72211SAlan Somers name=$(mkname) 33167f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} ${md2} 33267f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE" 33367f72211SAlan Somers 33467f72211SAlan Somers # Explicitly rotate the paths 33567f72211SAlan Somers atf_check -s exit:0 gmultipath rotate "$name" 33667f72211SAlan Somers check_multipath_state ${md2} "OPTIMAL" "PASSIVE" "PASSIVE" "ACTIVE" 33767f72211SAlan Somers # Again 33867f72211SAlan Somers atf_check -s exit:0 gmultipath rotate "$name" 33967f72211SAlan Somers check_multipath_state ${md1} "OPTIMAL" "PASSIVE" "ACTIVE" "PASSIVE" 34067f72211SAlan Somers # Final rotation should restore original configuration 34167f72211SAlan Somers atf_check -s exit:0 gmultipath rotate "$name" 34267f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE" 34367f72211SAlan Somers} 34467f72211SAlan Somersrotate_cleanup() 34567f72211SAlan Somers{ 34667f72211SAlan Somers common_cleanup 34767f72211SAlan Somers} 34867f72211SAlan Somers 34967f72211SAlan Somersatf_init_test_cases() 35067f72211SAlan Somers{ 35167f72211SAlan Somers atf_add_test_case add 35267f72211SAlan Somers atf_add_test_case create_A 35367f72211SAlan Somers atf_add_test_case create_R 35467f72211SAlan Somers atf_add_test_case depart_and_arrive 35567f72211SAlan Somers atf_add_test_case fail 35667f72211SAlan Somers atf_add_test_case fail_on_error 35767f72211SAlan Somers atf_add_test_case physpath 35867f72211SAlan Somers atf_add_test_case prefer 35967f72211SAlan Somers atf_add_test_case restore 36067f72211SAlan Somers atf_add_test_case restore_on_error 36167f72211SAlan Somers atf_add_test_case rotate 36267f72211SAlan Somers} 363