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 -q 100 -r 100 -w 100 -x 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