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# $FreeBSD$ 2667f72211SAlan Somers 2767f72211SAlan Somers. $(atf_get_srcdir)/conf.sh 2867f72211SAlan Somers 2967f72211SAlan Somersatf_test_case add cleanup 3067f72211SAlan Somersadd_head() 3167f72211SAlan Somers{ 3267f72211SAlan Somers atf_set "descr" "Add a new path" 3367f72211SAlan Somers atf_set "require.user" "root" 3467f72211SAlan Somers} 3567f72211SAlan Somersadd_body() 3667f72211SAlan Somers{ 3767f72211SAlan Somers load_gmultipath 3867f72211SAlan Somers load_dtrace 3967f72211SAlan Somers 4067f72211SAlan Somers md0=$(alloc_md) 4167f72211SAlan Somers md1=$(alloc_md) 4267f72211SAlan Somers md2=$(alloc_md) 4367f72211SAlan Somers name=$(mkname) 4467f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} 4567f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" 4667f72211SAlan Somers 4767f72211SAlan Somers # Add a new path 4867f72211SAlan Somers atf_check -s exit:0 gmultipath add "$name" ${md2} 4967f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE" 5067f72211SAlan Somers} 5167f72211SAlan Somersadd_cleanup() 5267f72211SAlan Somers{ 5367f72211SAlan Somers common_cleanup 5467f72211SAlan Somers} 5567f72211SAlan Somers 5667f72211SAlan Somersatf_test_case create_A cleanup 5767f72211SAlan Somerscreate_A_head() 5867f72211SAlan Somers{ 5967f72211SAlan Somers atf_set "descr" "Create an Active/Active multipath device" 6067f72211SAlan Somers atf_set "require.user" "root" 6167f72211SAlan Somers} 6267f72211SAlan Somerscreate_A_body() 6367f72211SAlan Somers{ 6467f72211SAlan Somers load_gmultipath 6567f72211SAlan Somers load_dtrace 6667f72211SAlan Somers 6767f72211SAlan Somers md0=$(alloc_md) 6867f72211SAlan Somers md1=$(alloc_md) 6967f72211SAlan Somers name=$(mkname) 7067f72211SAlan Somers atf_check -s exit:0 gmultipath create -A "$name" ${md0} ${md1} 7167f72211SAlan Somers check_multipath_state "${md1} ${md0}" "OPTIMAL" "ACTIVE" "ACTIVE" 7267f72211SAlan Somers} 7367f72211SAlan Somerscreate_A_cleanup() 7467f72211SAlan Somers{ 7567f72211SAlan Somers common_cleanup 7667f72211SAlan Somers} 7767f72211SAlan Somers 7867f72211SAlan Somersatf_test_case create_R cleanup 7967f72211SAlan Somerscreate_R_head() 8067f72211SAlan Somers{ 8167f72211SAlan Somers atf_set "descr" "Create an Active/Read multipath device" 8267f72211SAlan Somers atf_set "require.user" "root" 8367f72211SAlan Somers} 8467f72211SAlan Somerscreate_R_body() 8567f72211SAlan Somers{ 8667f72211SAlan Somers load_gmultipath 8767f72211SAlan Somers load_dtrace 8867f72211SAlan Somers 8967f72211SAlan Somers md0=$(alloc_md) 9067f72211SAlan Somers md1=$(alloc_md) 9167f72211SAlan Somers name=$(mkname) 9267f72211SAlan Somers atf_check -s exit:0 gmultipath create -R "$name" ${md0} ${md1} 9367f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "READ" 9467f72211SAlan Somers} 9567f72211SAlan Somerscreate_R_cleanup() 9667f72211SAlan Somers{ 9767f72211SAlan Somers common_cleanup 9867f72211SAlan Somers} 9967f72211SAlan Somers 10067f72211SAlan Somersatf_test_case depart_and_arrive cleanup 10167f72211SAlan Somersdepart_and_arrive_head() 10267f72211SAlan Somers{ 10367f72211SAlan Somers atf_set "descr" "gmultipath should remove devices that disappear, and automatically reattach labeled providers that reappear" 10467f72211SAlan Somers atf_set "require.user" "root" 10567f72211SAlan Somers} 10667f72211SAlan Somersdepart_and_arrive_body() 10767f72211SAlan Somers{ 10867f72211SAlan Somers load_gnop 10967f72211SAlan Somers load_gmultipath 11067f72211SAlan Somers md0=$(alloc_md) 11167f72211SAlan Somers md1=$(alloc_md) 11267f72211SAlan Somers name=$(mkname) 11367f72211SAlan Somers # We need a non-zero offset to gmultipath won't see the label when it 11467f72211SAlan Somers # tastes the md device. We only want the label to be visible on the 11567f72211SAlan Somers # gnop device. 11667f72211SAlan Somers offset=131072 11767f72211SAlan Somers atf_check gnop create -o $offset /dev/${md0} 11867f72211SAlan Somers atf_check gnop create -o $offset /dev/${md1} 11967f72211SAlan Somers atf_check -s exit:0 gmultipath label "$name" ${md0}.nop 12067f72211SAlan Somers # gmultipath is too smart to let us create a gmultipath device by label 12167f72211SAlan Somers # when the two providers aren't actually related. So we create a 12267f72211SAlan Somers # device by label with one provider, and then manually add the second. 12367f72211SAlan Somers atf_check -s exit:0 gmultipath add "$name" ${md1}.nop 12467f72211SAlan Somers NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'` 12567f72211SAlan Somers atf_check_equal 2 $NDEVS 12667f72211SAlan Somers 12767f72211SAlan Somers # Now fail the labeled provider 12867f72211SAlan Somers atf_check -s exit:0 gnop destroy -f ${md0}.nop 12967f72211SAlan Somers # It should be automatically removed from the multipath device 13067f72211SAlan Somers NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'` 13167f72211SAlan Somers atf_check_equal 1 $NDEVS 13267f72211SAlan Somers 13367f72211SAlan Somers # Now return the labeled provider 13467f72211SAlan Somers atf_check gnop create -o $offset /dev/${md0} 13567f72211SAlan Somers # It should be automatically restored to the multipath device. We 13667f72211SAlan Somers # don't really care which path is active. 13767f72211SAlan Somers NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'` 13867f72211SAlan Somers atf_check_equal 2 $NDEVS 13967f72211SAlan Somers STATE=`gmultipath list "$name" | awk '/^State:/ {print $2}'` 14067f72211SAlan Somers atf_check_equal "OPTIMAL" $STATE 14167f72211SAlan Somers} 14267f72211SAlan Somersdepart_and_arrive_cleanup() 14367f72211SAlan Somers{ 14467f72211SAlan Somers common_cleanup 14567f72211SAlan Somers} 14667f72211SAlan Somers 14767f72211SAlan Somers 14867f72211SAlan Somersatf_test_case fail cleanup 14967f72211SAlan Somersfail_head() 15067f72211SAlan Somers{ 15167f72211SAlan Somers atf_set "descr" "Manually fail a path" 15267f72211SAlan Somers atf_set "require.user" "root" 15367f72211SAlan Somers} 15467f72211SAlan Somersfail_body() 15567f72211SAlan Somers{ 15667f72211SAlan Somers load_gmultipath 15767f72211SAlan Somers md0=$(alloc_md) 15867f72211SAlan Somers md1=$(alloc_md) 15967f72211SAlan Somers name=$(mkname) 16067f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} 16167f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" 16267f72211SAlan Somers # Manually fail the active path 16367f72211SAlan Somers atf_check -s exit:0 gmultipath fail "$name" ${md0} 16467f72211SAlan Somers check_multipath_state ${md1} "DEGRADED" "FAIL" "ACTIVE" 16567f72211SAlan Somers} 16667f72211SAlan Somersfail_cleanup() 16767f72211SAlan Somers{ 16867f72211SAlan Somers common_cleanup 16967f72211SAlan Somers} 17067f72211SAlan Somers 17167f72211SAlan Somersatf_test_case fail_on_error cleanup 17267f72211SAlan Somersfail_on_error_head() 17367f72211SAlan Somers{ 17467f72211SAlan Somers atf_set "descr" "An error in the provider will cause gmultipath to mark it as FAIL" 17567f72211SAlan Somers atf_set "require.user" "root" 17667f72211SAlan Somers} 17767f72211SAlan Somersfail_on_error_body() 17867f72211SAlan Somers{ 17967f72211SAlan Somers load_gnop 18067f72211SAlan Somers load_gmultipath 18167f72211SAlan Somers md0=$(alloc_md) 18267f72211SAlan Somers md1=$(alloc_md) 18367f72211SAlan Somers name=$(mkname) 18467f72211SAlan Somers atf_check gnop create /dev/${md0} 18567f72211SAlan Somers atf_check gnop create /dev/${md1} 18667f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop 18767f72211SAlan Somers # The first I/O to the first path should fail, causing gmultipath to 18867f72211SAlan Somers # fail over to the second path. 189*deed14f4SAlan Somers atf_check gnop configure -r 100 -w 100 ${md0}.nop 19067f72211SAlan Somers atf_check -s exit:0 -o ignore -e ignore dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1 19167f72211SAlan Somers check_multipath_state ${md1}.nop "DEGRADED" "FAIL" "ACTIVE" 19267f72211SAlan Somers} 19367f72211SAlan Somersfail_on_error_cleanup() 19467f72211SAlan Somers{ 19567f72211SAlan Somers common_cleanup 19667f72211SAlan Somers} 19767f72211SAlan Somers 19867f72211SAlan Somersatf_test_case physpath cleanup 19967f72211SAlan Somersphyspath_head() 20067f72211SAlan Somers{ 20167f72211SAlan Somers atf_set "descr" "gmultipath should pass through the underlying providers' physical path" 20267f72211SAlan Somers atf_set "require.user" "root" 20367f72211SAlan Somers} 20467f72211SAlan Somersphyspath_body() 20567f72211SAlan Somers{ 20667f72211SAlan Somers load_gnop 20767f72211SAlan Somers load_gmultipath 20867f72211SAlan Somers md0=$(alloc_md) 20967f72211SAlan Somers md1=$(alloc_md) 21067f72211SAlan Somers name=$(mkname) 21167f72211SAlan Somers physpath="some/physical/path" 21267f72211SAlan Somers # Create two providers with the same physical paths, mimicing how 21367f72211SAlan Somers # multipathed SAS drives appear. This is the normal way to use 21467f72211SAlan Somers # gmultipath. If the underlying providers' physical paths differ, 21567f72211SAlan Somers # then you're probably using gmultipath wrong. 21667f72211SAlan Somers atf_check gnop create -z $physpath /dev/${md0} 21767f72211SAlan Somers atf_check gnop create -z $physpath /dev/${md1} 21867f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop 21967f72211SAlan Somers gmultipath_physpath=$(diskinfo -p multipath/"$name") 22067f72211SAlan Somers atf_check_equal "$physpath" "$gmultipath_physpath" 22167f72211SAlan Somers} 22267f72211SAlan Somersphyspath_cleanup() 22367f72211SAlan Somers{ 22467f72211SAlan Somers common_cleanup 22567f72211SAlan Somers} 22667f72211SAlan Somers 22767f72211SAlan Somersatf_test_case prefer cleanup 22867f72211SAlan Somersprefer_head() 22967f72211SAlan Somers{ 23067f72211SAlan Somers atf_set "descr" "Manually select the preferred path" 23167f72211SAlan Somers atf_set "require.user" "root" 23267f72211SAlan Somers} 23367f72211SAlan Somersprefer_body() 23467f72211SAlan Somers{ 23567f72211SAlan Somers load_gmultipath 23667f72211SAlan Somers load_dtrace 23767f72211SAlan Somers 23867f72211SAlan Somers md0=$(alloc_md) 23967f72211SAlan Somers md1=$(alloc_md) 24067f72211SAlan Somers md2=$(alloc_md) 24167f72211SAlan Somers name=$(mkname) 24267f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} ${md2} 24367f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE" 24467f72211SAlan Somers 24567f72211SAlan Somers # Explicitly prefer the final path 24667f72211SAlan Somers atf_check -s exit:0 gmultipath prefer "$name" ${md2} 24767f72211SAlan Somers check_multipath_state ${md2} "OPTIMAL" "PASSIVE" "PASSIVE" "ACTIVE" 24867f72211SAlan Somers} 24967f72211SAlan Somersprefer_cleanup() 25067f72211SAlan Somers{ 25167f72211SAlan Somers common_cleanup 25267f72211SAlan Somers} 25367f72211SAlan Somers 25467f72211SAlan Somersatf_test_case restore cleanup 25567f72211SAlan Somersrestore_head() 25667f72211SAlan Somers{ 25767f72211SAlan Somers atf_set "descr" "Manually restore a failed path" 25867f72211SAlan Somers atf_set "require.user" "root" 25967f72211SAlan Somers} 26067f72211SAlan Somersrestore_body() 26167f72211SAlan Somers{ 26267f72211SAlan Somers load_gmultipath 26367f72211SAlan Somers load_dtrace 26467f72211SAlan Somers 26567f72211SAlan Somers md0=$(alloc_md) 26667f72211SAlan Somers md1=$(alloc_md) 26767f72211SAlan Somers name=$(mkname) 26867f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} 26967f72211SAlan Somers 27067f72211SAlan Somers # Explicitly fail the first path 27167f72211SAlan Somers atf_check -s exit:0 gmultipath fail "$name" ${md0} 27267f72211SAlan Somers check_multipath_state ${md1} "DEGRADED" "FAIL" "ACTIVE" 27367f72211SAlan Somers 27467f72211SAlan Somers # Explicitly restore it 27567f72211SAlan Somers atf_check -s exit:0 gmultipath restore "$name" ${md0} 27667f72211SAlan Somers check_multipath_state ${md1} "OPTIMAL" "PASSIVE" "ACTIVE" 27767f72211SAlan Somers} 27867f72211SAlan Somersrestore_cleanup() 27967f72211SAlan Somers{ 28067f72211SAlan Somers common_cleanup 28167f72211SAlan Somers} 28267f72211SAlan Somers 28367f72211SAlan Somersatf_test_case restore_on_error cleanup 28467f72211SAlan Somersrestore_on_error_head() 28567f72211SAlan Somers{ 28667f72211SAlan Somers atf_set "descr" "A failed path should be restored if an I/O error is encountered on all other active paths" 28767f72211SAlan Somers atf_set "require.user" "root" 28867f72211SAlan Somers} 28967f72211SAlan Somersrestore_on_error_body() 29067f72211SAlan Somers{ 29167f72211SAlan Somers load_gnop 29267f72211SAlan Somers load_gmultipath 29367f72211SAlan Somers load_dtrace 29467f72211SAlan Somers 29567f72211SAlan Somers md0=$(alloc_md) 29667f72211SAlan Somers md1=$(alloc_md) 29767f72211SAlan Somers name=$(mkname) 29867f72211SAlan Somers atf_check gnop create /dev/${md0} 29967f72211SAlan Somers atf_check gnop create /dev/${md1} 30067f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop 30167f72211SAlan Somers # Explicitly fail the first path 30267f72211SAlan Somers atf_check -s exit:0 gmultipath fail "$name" ${md0}.nop 30367f72211SAlan Somers 30467f72211SAlan Somers # Setup the second path to fail on the next I/O 30567f72211SAlan Somers atf_check gnop configure -r 100 -w 100 ${md1}.nop 30667f72211SAlan Somers atf_check -s exit:0 -o ignore -e ignore \ 30767f72211SAlan Somers dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1 30867f72211SAlan Somers 30967f72211SAlan Somers # Now the first path should be active, and the second should be failed 31067f72211SAlan Somers check_multipath_state ${md0}.nop "DEGRADED" "ACTIVE" "FAIL" 31167f72211SAlan Somers} 31267f72211SAlan Somersrestore_on_error_cleanup() 31367f72211SAlan Somers{ 31467f72211SAlan Somers common_cleanup 31567f72211SAlan Somers} 31667f72211SAlan Somers 31767f72211SAlan Somersatf_test_case rotate cleanup 31867f72211SAlan Somersrotate_head() 31967f72211SAlan Somers{ 32067f72211SAlan Somers atf_set "descr" "Manually rotate the active path" 32167f72211SAlan Somers atf_set "require.user" "root" 32267f72211SAlan Somers} 32367f72211SAlan Somersrotate_body() 32467f72211SAlan Somers{ 32567f72211SAlan Somers load_gmultipath 32667f72211SAlan Somers load_dtrace 32767f72211SAlan Somers 32867f72211SAlan Somers md0=$(alloc_md) 32967f72211SAlan Somers md1=$(alloc_md) 33067f72211SAlan Somers md2=$(alloc_md) 33167f72211SAlan Somers name=$(mkname) 33267f72211SAlan Somers atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} ${md2} 33367f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE" 33467f72211SAlan Somers 33567f72211SAlan Somers # Explicitly rotate the paths 33667f72211SAlan Somers atf_check -s exit:0 gmultipath rotate "$name" 33767f72211SAlan Somers check_multipath_state ${md2} "OPTIMAL" "PASSIVE" "PASSIVE" "ACTIVE" 33867f72211SAlan Somers # Again 33967f72211SAlan Somers atf_check -s exit:0 gmultipath rotate "$name" 34067f72211SAlan Somers check_multipath_state ${md1} "OPTIMAL" "PASSIVE" "ACTIVE" "PASSIVE" 34167f72211SAlan Somers # Final rotation should restore original configuration 34267f72211SAlan Somers atf_check -s exit:0 gmultipath rotate "$name" 34367f72211SAlan Somers check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE" 34467f72211SAlan Somers} 34567f72211SAlan Somersrotate_cleanup() 34667f72211SAlan Somers{ 34767f72211SAlan Somers common_cleanup 34867f72211SAlan Somers} 34967f72211SAlan Somers 35067f72211SAlan Somersatf_init_test_cases() 35167f72211SAlan Somers{ 35267f72211SAlan Somers atf_add_test_case add 35367f72211SAlan Somers atf_add_test_case create_A 35467f72211SAlan Somers atf_add_test_case create_R 35567f72211SAlan Somers atf_add_test_case depart_and_arrive 35667f72211SAlan Somers atf_add_test_case fail 35767f72211SAlan Somers atf_add_test_case fail_on_error 35867f72211SAlan Somers atf_add_test_case physpath 35967f72211SAlan Somers atf_add_test_case prefer 36067f72211SAlan Somers atf_add_test_case restore 36167f72211SAlan Somers atf_add_test_case restore_on_error 36267f72211SAlan Somers atf_add_test_case rotate 36367f72211SAlan Somers} 364