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