1# 2# CDDL HEADER START 3# 4# The contents of this file are subject to the terms of the 5# Common Development and Distribution License (the "License"). 6# You may not use this file except in compliance with the License. 7# 8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9# or http://www.opensolaris.org/os/licensing. 10# See the License for the specific language governing permissions 11# and limitations under the License. 12# 13# When distributing Covered Code, include this CDDL HEADER in each 14# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15# If applicable, add the following below this CDDL HEADER, with the 16# fields enclosed by brackets "[]" replaced with your own identifying 17# information: Portions Copyright [yyyy] [name of copyright owner] 18# 19# CDDL HEADER END 20# 21 22# 23# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24# Use is subject to license terms. 25# Copyright (c) 2012, 2017 by Delphix. All rights reserved. 26# Copyright (c) 2017 by Tim Chase. All rights reserved. 27# Copyright (c) 2017 by Nexenta Systems, Inc. All rights reserved. 28# Copyright (c) 2017 Datto Inc. 29# Copyright 2020 Joyent, Inc. 30# Copyright (c) 2025, Klara, Inc. 31# Copyright 2024 MNX Cloud, Inc. 32# Copyright 2026 Gordon W. Ross 33# 34 35UNAME=$(uname) 36 37. ${STF_TOOLS}/contrib/include/logapi.shlib 38. ${STF_SUITE}/include/math.shlib 39. ${STF_SUITE}/include/blkdev.shlib 40 41# Determine if this is a Linux test system 42# 43# Return 0 if platform Linux, 1 if otherwise 44 45function is_linux 46{ 47 [ "$UNAME" = "Linux" ] 48} 49 50# Determine if this is an illumos test system 51# 52# Return 0 if platform illumos, 1 if otherwise 53function is_illumos 54{ 55 [ "$UNAME" = "SunOS" ] 56} 57 58# Determine if this is a FreeBSD test system 59# 60# Return 0 if platform FreeBSD, 1 if otherwise 61 62function is_freebsd 63{ 64 [ "$UNAME" = "FreeBSD" ] 65} 66 67# Determine whether a dataset is mounted 68# 69# $1 dataset name 70# $2 filesystem type; optional - defaulted to zfs 71# 72# Return 0 if dataset is mounted; 1 if unmounted; 2 on error 73 74function ismounted 75{ 76 typeset fstype=$2 77 [[ -z $fstype ]] && fstype=zfs 78 typeset out dir name ret 79 80 case $fstype in 81 zfs) 82 if [[ "$1" == "/"* ]] ; then 83 for out in $(zfs mount | awk '{print $2}'); do 84 [[ $1 == $out ]] && return 0 85 done 86 else 87 for out in $(zfs mount | awk '{print $1}'); do 88 [[ $1 == $out ]] && return 0 89 done 90 fi 91 ;; 92 ufs|nfs) 93 out=$(df -F $fstype $1 2>/dev/null) 94 ret=$? 95 (($ret != 0)) && return $ret 96 97 dir=${out%%\(*} 98 dir=${dir%% *} 99 name=${out##*\(} 100 name=${name%%\)*} 101 name=${name%% *} 102 103 [[ "$1" == "$dir" || "$1" == "$name" ]] && return 0 104 ;; 105 esac 106 107 return 1 108} 109 110# Return 0 if a dataset is mounted; 1 otherwise 111# 112# $1 dataset name 113# $2 filesystem type; optional - defaulted to zfs 114 115function mounted 116{ 117 ismounted $1 $2 118 (($? == 0)) && return 0 119 return 1 120} 121 122# Return 0 if a dataset is unmounted; 1 otherwise 123# 124# $1 dataset name 125# $2 filesystem type; optional - defaulted to zfs 126 127function unmounted 128{ 129 ismounted $1 $2 130 (($? == 1)) && return 0 131 return 1 132} 133 134# split line on "," 135# 136# $1 - line to split 137 138function splitline 139{ 140 echo $1 | sed "s/,/ /g" 141} 142 143function default_setup 144{ 145 default_setup_noexit "$@" 146 147 log_pass 148} 149 150# 151# Given a list of disks, setup storage pools and datasets. 152# 153function default_setup_noexit 154{ 155 typeset disklist=$1 156 typeset container=$2 157 typeset volume=$3 158 159 if is_global_zone; then 160 if poolexists $TESTPOOL ; then 161 destroy_pool $TESTPOOL 162 fi 163 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 164 log_must zpool create -f $TESTPOOL $disklist 165 else 166 reexport_pool 167 fi 168 169 rm -rf $TESTDIR || log_unresolved Could not remove $TESTDIR 170 mkdir -p $TESTDIR || log_unresolved Could not create $TESTDIR 171 172 log_must zfs create $TESTPOOL/$TESTFS 173 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 174 175 if [[ -n $container ]]; then 176 rm -rf $TESTDIR1 || \ 177 log_unresolved Could not remove $TESTDIR1 178 mkdir -p $TESTDIR1 || \ 179 log_unresolved Could not create $TESTDIR1 180 181 log_must zfs create $TESTPOOL/$TESTCTR 182 log_must zfs set canmount=off $TESTPOOL/$TESTCTR 183 log_must zfs create $TESTPOOL/$TESTCTR/$TESTFS1 184 log_must zfs set mountpoint=$TESTDIR1 \ 185 $TESTPOOL/$TESTCTR/$TESTFS1 186 fi 187 188 if [[ -n $volume ]]; then 189 if is_global_zone ; then 190 log_must zfs create -V $VOLSIZE $TESTPOOL/$TESTVOL 191 else 192 log_must zfs create $TESTPOOL/$TESTVOL 193 fi 194 fi 195} 196 197# 198# Given a list of disks, setup a storage pool, file system and 199# a container. 200# 201function default_container_setup 202{ 203 typeset disklist=$1 204 205 default_setup "$disklist" "true" 206} 207 208# 209# Given a list of disks, setup a storage pool,file system 210# and a volume. 211# 212function default_volume_setup 213{ 214 typeset disklist=$1 215 216 default_setup "$disklist" "" "true" 217} 218 219# 220# Given a list of disks, setup a storage pool,file system, 221# a container and a volume. 222# 223function default_container_volume_setup 224{ 225 typeset disklist=$1 226 227 default_setup "$disklist" "true" "true" 228} 229 230# 231# Create a snapshot on a filesystem or volume. Defaultly create a snapshot on 232# filesystem 233# 234# $1 Existing filesystem or volume name. Default, $TESTPOOL/$TESTFS 235# $2 snapshot name. Default, $TESTSNAP 236# 237function create_snapshot 238{ 239 typeset fs_vol=${1:-$TESTPOOL/$TESTFS} 240 typeset snap=${2:-$TESTSNAP} 241 242 [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined." 243 [[ -z $snap ]] && log_fail "Snapshot's name is undefined." 244 245 if snapexists $fs_vol@$snap; then 246 log_fail "$fs_vol@$snap already exists." 247 fi 248 datasetexists $fs_vol || \ 249 log_fail "$fs_vol must exist." 250 251 log_must zfs snapshot $fs_vol@$snap 252} 253 254# 255# Create a clone from a snapshot, default clone name is $TESTCLONE. 256# 257# $1 Existing snapshot, $TESTPOOL/$TESTFS@$TESTSNAP is default. 258# $2 Clone name, $TESTPOOL/$TESTCLONE is default. 259# 260function create_clone # snapshot clone 261{ 262 typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 263 typeset clone=${2:-$TESTPOOL/$TESTCLONE} 264 265 [[ -z $snap ]] && \ 266 log_fail "Snapshot name is undefined." 267 [[ -z $clone ]] && \ 268 log_fail "Clone name is undefined." 269 270 log_must zfs clone $snap $clone 271} 272 273# 274# Create a bookmark of the given snapshot. Defaultly create a bookmark on 275# filesystem. 276# 277# $1 Existing filesystem or volume name. Default, $TESTFS 278# $2 Existing snapshot name. Default, $TESTSNAP 279# $3 bookmark name. Default, $TESTBKMARK 280# 281function create_bookmark 282{ 283 typeset fs_vol=${1:-$TESTFS} 284 typeset snap=${2:-$TESTSNAP} 285 typeset bkmark=${3:-$TESTBKMARK} 286 287 [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined." 288 [[ -z $snap ]] && log_fail "Snapshot's name is undefined." 289 [[ -z $bkmark ]] && log_fail "Bookmark's name is undefined." 290 291 if bkmarkexists $fs_vol#$bkmark; then 292 log_fail "$fs_vol#$bkmark already exists." 293 fi 294 datasetexists $fs_vol || \ 295 log_fail "$fs_vol must exist." 296 snapexists $fs_vol@$snap || \ 297 log_fail "$fs_vol@$snap must exist." 298 299 log_must zfs bookmark $fs_vol@$snap $fs_vol#$bkmark 300} 301 302# 303# Create a temporary clone result of an interrupted resumable 'zfs receive' 304# $1 Destination filesystem name. Must not exist, will be created as the result 305# of this function along with its %recv temporary clone 306# $2 Source filesystem name. Must not exist, will be created and destroyed 307# 308function create_recv_clone 309{ 310 typeset recvfs="$1" 311 typeset sendfs="${2:-$TESTPOOL/create_recv_clone}" 312 typeset snap="$sendfs@snap1" 313 typeset incr="$sendfs@snap2" 314 typeset mountpoint="$TESTDIR/create_recv_clone" 315 typeset sendfile="$TESTDIR/create_recv_clone.zsnap" 316 317 [[ -z $recvfs ]] && log_fail "Recv filesystem's name is undefined." 318 319 datasetexists $recvfs && log_fail "Recv filesystem must not exist." 320 datasetexists $sendfs && log_fail "Send filesystem must not exist." 321 322 log_must zfs create -o mountpoint="$mountpoint" $sendfs 323 log_must zfs snapshot $snap 324 log_must eval "zfs send $snap | zfs recv -u $recvfs" 325 log_must mkfile 1m "$mountpoint/data" 326 log_must zfs snapshot $incr 327 log_must eval "zfs send -i $snap $incr | dd bs=10k count=1 > $sendfile" 328 log_mustnot eval "zfs recv -su $recvfs < $sendfile" 329 destroy_dataset "$sendfs" "-r" 330 log_must rm -f "$sendfile" 331 332 if [[ $(get_prop 'inconsistent' "$recvfs/%recv") -ne 1 ]]; then 333 log_fail "Error creating temporary $recvfs/%recv clone" 334 fi 335} 336 337function default_mirror_setup 338{ 339 default_mirror_setup_noexit $1 $2 $3 340 341 log_pass 342} 343 344function default_mirror_2way_setup 345{ 346 default_mirror_setup_noexit $1 $2 347 348 log_pass 349} 350 351# 352# Given a pair of disks, set up a storage pool and dataset for the mirror 353# @parameters: $1 the primary side of the mirror 354# $2 the secondary side of the mirror 355# @uses: ZPOOL ZFS TESTPOOL TESTFS 356function default_mirror_setup_noexit 357{ 358 readonly func="default_mirror_setup_noexit" 359 typeset primary=$1 360 typeset secondary=$2 361 362 [[ -z $primary ]] && \ 363 log_fail "$func: No parameters passed" 364 [[ -z $secondary ]] && \ 365 log_fail "$func: No secondary partition passed" 366 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 367 log_must zpool create -f $TESTPOOL mirror $@ 368 log_must zfs create $TESTPOOL/$TESTFS 369 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 370} 371 372# 373# create a number of mirrors. 374# We create a number($1) of 2 way mirrors using the pairs of disks named 375# on the command line. These mirrors are *not* mounted 376# @parameters: $1 the number of mirrors to create 377# $... the devices to use to create the mirrors on 378# @uses: ZPOOL ZFS TESTPOOL 379function setup_mirrors 380{ 381 typeset -i nmirrors=$1 382 383 shift 384 while ((nmirrors > 0)); do 385 log_must test -n "$1" -a -n "$2" 386 [[ -d /$TESTPOOL$nmirrors ]] && rm -rf /$TESTPOOL$nmirrors 387 log_must zpool create -f $TESTPOOL$nmirrors mirror $1 $2 388 shift 2 389 ((nmirrors = nmirrors - 1)) 390 done 391} 392 393# 394# create a number of raidz pools. 395# We create a number($1) of 2 raidz pools using the pairs of disks named 396# on the command line. These pools are *not* mounted 397# @parameters: $1 the number of pools to create 398# $... the devices to use to create the pools on 399# @uses: ZPOOL ZFS TESTPOOL 400function setup_raidzs 401{ 402 typeset -i nraidzs=$1 403 404 shift 405 while ((nraidzs > 0)); do 406 log_must test -n "$1" -a -n "$2" 407 [[ -d /$TESTPOOL$nraidzs ]] && rm -rf /$TESTPOOL$nraidzs 408 log_must zpool create -f $TESTPOOL$nraidzs raidz $1 $2 409 shift 2 410 ((nraidzs = nraidzs - 1)) 411 done 412} 413 414# 415# Destroy the configured testpool mirrors. 416# the mirrors are of the form ${TESTPOOL}{number} 417# @uses: ZPOOL ZFS TESTPOOL 418function destroy_mirrors 419{ 420 default_cleanup_noexit 421 422 log_pass 423} 424 425# 426# Given a minimum of two disks, set up a storage pool and dataset for the raid-z 427# $1 the list of disks 428# 429function default_raidz_setup 430{ 431 typeset disklist="$*" 432 disks=(${disklist[*]}) 433 434 if [[ ${#disks[*]} -lt 2 ]]; then 435 log_fail "A raid-z requires a minimum of two disks." 436 fi 437 438 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 439 log_must zpool create -f $TESTPOOL raidz $disklist 440 log_must zfs create $TESTPOOL/$TESTFS 441 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 442 443 log_pass 444} 445 446# 447# Common function used to cleanup storage pools and datasets. 448# 449# Invoked at the start of the test suite to ensure the system 450# is in a known state, and also at the end of each set of 451# sub-tests to ensure errors from one set of tests doesn't 452# impact the execution of the next set. 453 454function default_cleanup 455{ 456 default_cleanup_noexit 457 458 log_pass 459} 460 461function default_cleanup_noexit 462{ 463 typeset exclude="" 464 typeset pool="" 465 # 466 # Destroying the pool will also destroy any 467 # filesystems it contains. 468 # 469 if is_global_zone; then 470 zfs unmount -a > /dev/null 2>&1 471 exclude=`eval echo \"'(${KEEP})'\"` 472 ALL_POOLS=$(zpool list -H -o name \ 473 | grep -v "$NO_POOLS" | egrep -v "$exclude") 474 # Here, we loop through the pools we're allowed to 475 # destroy, only destroying them if it's safe to do 476 # so. 477 while [ ! -z ${ALL_POOLS} ] 478 do 479 for pool in ${ALL_POOLS} 480 do 481 if safe_to_destroy_pool $pool ; 482 then 483 destroy_pool $pool 484 fi 485 ALL_POOLS=$(zpool list -H -o name \ 486 | grep -v "$NO_POOLS" \ 487 | egrep -v "$exclude") 488 done 489 done 490 491 zfs mount -a 492 else 493 typeset fs="" 494 for fs in $(zfs list -H -o name \ 495 | grep "^$ZONE_POOL/$ZONE_CTR[01234]/"); do 496 destroy_dataset "$fs" "-Rf" 497 done 498 499 # Need cleanup here to avoid garbage dir left. 500 for fs in $(zfs list -H -o name); do 501 [[ $fs == /$ZONE_POOL ]] && continue 502 [[ -d $fs ]] && log_must rm -rf $fs/* 503 done 504 505 # 506 # Reset the $ZONE_POOL/$ZONE_CTR[01234] file systems property to 507 # the default value 508 # 509 for fs in $(zfs list -H -o name); do 510 if [[ $fs == $ZONE_POOL/$ZONE_CTR[01234] ]]; then 511 log_must zfs set reservation=none $fs 512 log_must zfs set recordsize=128K $fs 513 log_must zfs set mountpoint=/$fs $fs 514 typeset enc="" 515 enc=$(get_prop encryption $fs) 516 if [[ $? -ne 0 ]] || [[ -z "$enc" ]] || \ 517 [[ "$enc" == "off" ]]; then 518 log_must zfs set checksum=on $fs 519 fi 520 log_must zfs set compression=off $fs 521 log_must zfs set atime=on $fs 522 log_must zfs set devices=off $fs 523 log_must zfs set exec=on $fs 524 log_must zfs set setuid=on $fs 525 log_must zfs set readonly=off $fs 526 log_must zfs set snapdir=hidden $fs 527 log_must zfs set aclmode=groupmask $fs 528 log_must zfs set aclinherit=secure $fs 529 fi 530 done 531 fi 532 533 [[ -d $TESTDIR ]] && \ 534 log_must rm -rf $TESTDIR 535} 536 537 538# 539# Common function used to cleanup storage pools, file systems 540# and containers. 541# 542function default_container_cleanup 543{ 544 if ! is_global_zone; then 545 reexport_pool 546 fi 547 548 ismounted $TESTPOOL/$TESTCTR/$TESTFS1 549 [[ $? -eq 0 ]] && \ 550 log_must zfs unmount $TESTPOOL/$TESTCTR/$TESTFS1 551 552 destroy_dataset "$TESTPOOL/$TESTCTR/$TESTFS1" "-R" 553 destroy_dataset "$TESTPOOL/$TESTCTR" "-Rf" 554 555 [[ -e $TESTDIR1 ]] && \ 556 log_must rm -rf $TESTDIR1 > /dev/null 2>&1 557 558 default_cleanup 559} 560 561# 562# Common function used to cleanup snapshot of file system or volume. Default to 563# delete the file system's snapshot 564# 565# $1 snapshot name 566# 567function destroy_snapshot 568{ 569 typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 570 571 if ! snapexists $snap; then 572 log_fail "'$snap' does not existed." 573 fi 574 575 # 576 # For the sake of the value which come from 'get_prop' is not equal 577 # to the really mountpoint when the snapshot is unmounted. So, firstly 578 # check and make sure this snapshot's been mounted in current system. 579 # 580 typeset mtpt="" 581 if ismounted $snap; then 582 mtpt=$(get_prop mountpoint $snap) 583 (($? != 0)) && \ 584 log_fail "get_prop mountpoint $snap failed." 585 fi 586 587 destroy_dataset $snap 588 [[ $mtpt != "" && -d $mtpt ]] && \ 589 log_must rm -rf $mtpt 590} 591 592# 593# Common function used to cleanup clone. 594# 595# $1 clone name 596# 597function destroy_clone 598{ 599 typeset clone=${1:-$TESTPOOL/$TESTCLONE} 600 601 if ! datasetexists $clone; then 602 log_fail "'$clone' does not existed." 603 fi 604 605 # With the same reason in destroy_snapshot 606 typeset mtpt="" 607 if ismounted $clone; then 608 mtpt=$(get_prop mountpoint $clone) 609 (($? != 0)) && \ 610 log_fail "get_prop mountpoint $clone failed." 611 fi 612 613 destroy_dataset $clone 614 [[ $mtpt != "" && -d $mtpt ]] && \ 615 log_must rm -rf $mtpt 616} 617 618# 619# Common function used to cleanup bookmark of file system or volume. Default 620# to delete the file system's bookmark. 621# 622# $1 bookmark name 623# 624function destroy_bookmark 625{ 626 typeset bkmark=${1:-$TESTPOOL/$TESTFS#$TESTBKMARK} 627 628 if ! bkmarkexists $bkmark; then 629 log_fail "'$bkmarkp' does not existed." 630 fi 631 632 destroy_dataset $bkmark 633} 634 635# Return 0 if a snapshot exists; $? otherwise 636# 637# $1 - snapshot name 638 639function snapexists 640{ 641 zfs list -H -t snapshot "$1" > /dev/null 2>&1 642 return $? 643} 644 645# 646# Return 0 if a bookmark exists; $? otherwise 647# 648# $1 - bookmark name 649# 650function bkmarkexists 651{ 652 zfs list -H -t bookmark "$1" > /dev/null 2>&1 653 return $? 654} 655 656# 657# Set a property to a certain value on a dataset. 658# Sets a property of the dataset to the value as passed in. 659# @param: 660# $1 dataset who's property is being set 661# $2 property to set 662# $3 value to set property to 663# @return: 664# 0 if the property could be set. 665# non-zero otherwise. 666# @use: ZFS 667# 668function dataset_setprop 669{ 670 typeset fn=dataset_setprop 671 672 if (($# < 3)); then 673 log_note "$fn: Insufficient parameters (need 3, had $#)" 674 return 1 675 fi 676 typeset output= 677 output=$(zfs set $2=$3 $1 2>&1) 678 typeset rv=$? 679 if ((rv != 0)); then 680 log_note "Setting property on $1 failed." 681 log_note "property $2=$3" 682 log_note "Return Code: $rv" 683 log_note "Output: $output" 684 return $rv 685 fi 686 return 0 687} 688 689# 690# Assign suite defined dataset properties. 691# This function is used to apply the suite's defined default set of 692# properties to a dataset. 693# @parameters: $1 dataset to use 694# @uses: ZFS COMPRESSION_PROP CHECKSUM_PROP 695# @returns: 696# 0 if the dataset has been altered. 697# 1 if no pool name was passed in. 698# 2 if the dataset could not be found. 699# 3 if the dataset could not have it's properties set. 700# 701function dataset_set_defaultproperties 702{ 703 typeset dataset="$1" 704 705 [[ -z $dataset ]] && return 1 706 707 typeset confset= 708 typeset -i found=0 709 for confset in $(zfs list); do 710 if [[ $dataset = $confset ]]; then 711 found=1 712 break 713 fi 714 done 715 [[ $found -eq 0 ]] && return 2 716 if [[ -n $COMPRESSION_PROP ]]; then 717 dataset_setprop $dataset compression $COMPRESSION_PROP || \ 718 return 3 719 log_note "Compression set to '$COMPRESSION_PROP' on $dataset" 720 fi 721 if [[ -n $CHECKSUM_PROP ]]; then 722 dataset_setprop $dataset checksum $CHECKSUM_PROP || \ 723 return 3 724 log_note "Checksum set to '$CHECKSUM_PROP' on $dataset" 725 fi 726 return 0 727} 728 729# 730# Check a numeric assertion 731# @parameter: $@ the assertion to check 732# @output: big loud notice if assertion failed 733# @use: log_fail 734# 735function assert 736{ 737 (($@)) || log_fail "$@" 738} 739 740# 741# Function to format partition size of a disk 742# Given a disk cxtxdx reduces all partitions 743# to 0 size 744# 745function zero_partitions #<whole_disk_name> 746{ 747 typeset diskname=$1 748 typeset i 749 750 for i in 0 1 3 4 5 6 7 751 do 752 set_partition $i "" 0mb $diskname 753 done 754} 755 756# 757# Given a slice, size and disk, this function 758# formats the slice to the specified size. 759# Size should be specified with units as per 760# the `format` command requirements eg. 100mb 3gb 761# 762function set_partition #<slice_num> <slice_start> <size_plus_units> <whole_disk_name> 763{ 764 typeset -i slicenum=$1 765 typeset start=$2 766 typeset size=$3 767 typeset disk=$4 768 [[ -z $slicenum || -z $size || -z $disk ]] && \ 769 log_fail "The slice, size or disk name is unspecified." 770 typeset format_file=/var/tmp/format_in.$$ 771 772 echo "partition" >$format_file 773 echo "$slicenum" >> $format_file 774 echo "" >> $format_file 775 echo "" >> $format_file 776 echo "$start" >> $format_file 777 echo "$size" >> $format_file 778 echo "label" >> $format_file 779 echo "" >> $format_file 780 echo "q" >> $format_file 781 echo "q" >> $format_file 782 783 format -e -s -d $disk -f $format_file 784 typeset ret_val=$? 785 rm -f $format_file 786 [[ $ret_val -ne 0 ]] && \ 787 log_fail "Unable to format $disk slice $slicenum to $size" 788 return 0 789} 790 791# 792# Get the end cyl of the given slice 793# 794function get_endslice #<disk> <slice> 795{ 796 typeset disk=$1 797 typeset slice=$2 798 if [[ -z $disk || -z $slice ]] ; then 799 log_fail "The disk name or slice number is unspecified." 800 fi 801 802 disk=${disk#/dev/dsk/} 803 disk=${disk#/dev/rdsk/} 804 disk=${disk%s*} 805 806 typeset -i ratio=0 807 ratio=$(prtvtoc /dev/rdsk/${disk}s2 | \ 808 grep "sectors\/cylinder" | \ 809 awk '{print $2}') 810 811 if ((ratio == 0)); then 812 return 813 fi 814 815 typeset -i endcyl=$(prtvtoc -h /dev/rdsk/${disk}s2 | 816 nawk -v token="$slice" '{if ($1==token) print $6}') 817 818 ((endcyl = (endcyl + 1) / ratio)) 819 echo $endcyl 820} 821 822 823# 824# Given a size,disk and total slice number, this function formats the 825# disk slices from 0 to the total slice number with the same specified 826# size. 827# 828function partition_disk #<slice_size> <whole_disk_name> <total_slices> 829{ 830 typeset -i i=0 831 typeset slice_size=$1 832 typeset disk_name=$2 833 typeset total_slices=$3 834 typeset cyl 835 836 zero_partitions $disk_name 837 while ((i < $total_slices)); do 838 if ((i == 2)); then 839 ((i = i + 1)) 840 continue 841 fi 842 set_partition $i "$cyl" $slice_size $disk_name 843 cyl=$(get_endslice $disk_name $i) 844 ((i = i+1)) 845 done 846} 847 848# 849# This function continues to write to a filenum number of files into dirnum 850# number of directories until either file_write returns an error or the 851# maximum number of files per directory have been written. 852# 853# Usage: 854# fill_fs [destdir] [dirnum] [filenum] [bytes] [num_writes] [data] 855# 856# Return value: 0 on success 857# non 0 on error 858# 859# Where : 860# destdir: is the directory where everything is to be created under 861# dirnum: the maximum number of subdirectories to use, -1 no limit 862# filenum: the maximum number of files per subdirectory 863# bytes: number of bytes to write 864# num_writes: numer of types to write out bytes 865# data: the data that will be writen 866# 867# E.g. 868# file_fs /testdir 20 25 1024 256 0 869# 870# Note: bytes * num_writes equals the size of the testfile 871# 872function fill_fs # destdir dirnum filenum bytes num_writes data 873{ 874 typeset destdir=${1:-$TESTDIR} 875 typeset -i dirnum=${2:-50} 876 typeset -i filenum=${3:-50} 877 typeset -i bytes=${4:-8192} 878 typeset -i num_writes=${5:-10240} 879 typeset data=${6:-0} 880 881 typeset -i odirnum=1 882 typeset -i idirnum=0 883 typeset -i fn=0 884 typeset -i retval=0 885 886 mkdir -p $destdir/$idirnum 887 while (($odirnum > 0)); do 888 if ((dirnum >= 0 && idirnum >= dirnum)); then 889 odirnum=0 890 break 891 fi 892 file_write -o create -f $destdir/$idirnum/$TESTFILE.$fn \ 893 -b $bytes -c $num_writes -d $data 894 retval=$? 895 if (($retval != 0)); then 896 odirnum=0 897 break 898 fi 899 if (($fn >= $filenum)); then 900 fn=0 901 ((idirnum = idirnum + 1)) 902 mkdir -p $destdir/$idirnum 903 else 904 ((fn = fn + 1)) 905 fi 906 done 907 return $retval 908} 909 910# 911# Simple function to get the specified property. If unable to 912# get the property then exits. 913# 914# Note property is in 'parsable' format (-p) 915# 916function get_prop # property dataset 917{ 918 typeset prop_val 919 typeset prop=$1 920 typeset dataset=$2 921 922 prop_val=$(zfs get -pH -o value $prop $dataset 2>/dev/null) 923 if [[ $? -ne 0 ]]; then 924 log_note "Unable to get $prop property for dataset " \ 925 "$dataset" 926 return 1 927 fi 928 929 echo "$prop_val" 930 return 0 931} 932 933# 934# Simple function to get the specified property of pool. If unable to 935# get the property then exits. 936# 937function get_pool_prop # property pool 938{ 939 typeset prop_val 940 typeset prop=$1 941 typeset pool=$2 942 943 if poolexists $pool ; then 944 prop_val=$(zpool get $prop $pool 2>/dev/null | tail -1 | \ 945 awk '{print $3}') 946 if [[ $? -ne 0 ]]; then 947 log_note "Unable to get $prop property for pool " \ 948 "$pool" 949 return 1 950 fi 951 else 952 log_note "Pool $pool not exists." 953 return 1 954 fi 955 956 echo $prop_val 957 return 0 958} 959 960# Return 0 if a pool exists; $? otherwise 961# 962# $1 - pool name 963 964function poolexists 965{ 966 typeset pool=$1 967 968 if [[ -z $pool ]]; then 969 log_note "No pool name given." 970 return 1 971 fi 972 973 zpool get name "$pool" > /dev/null 2>&1 974 return $? 975} 976 977# Return 0 if all the specified datasets exist; $? otherwise 978# 979# $1-n dataset name 980function datasetexists 981{ 982 if (($# == 0)); then 983 log_note "No dataset name given." 984 return 1 985 fi 986 987 while (($# > 0)); do 988 zfs get name $1 > /dev/null 2>&1 || \ 989 return $? 990 shift 991 done 992 993 return 0 994} 995 996# return 0 if none of the specified datasets exists, otherwise return 1. 997# 998# $1-n dataset name 999function datasetnonexists 1000{ 1001 if (($# == 0)); then 1002 log_note "No dataset name given." 1003 return 1 1004 fi 1005 1006 while (($# > 0)); do 1007 zfs list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 \ 1008 && return 1 1009 shift 1010 done 1011 1012 return 0 1013} 1014 1015# 1016# Given a mountpoint, or a dataset name, determine if it is shared. 1017# 1018# Returns 0 if shared, 1 otherwise. 1019# 1020function is_shared 1021{ 1022 typeset fs=$1 1023 typeset mtpt 1024 1025 if [[ $fs != "/"* ]] ; then 1026 if datasetnonexists "$fs" ; then 1027 return 1 1028 else 1029 mtpt=$(get_prop mountpoint "$fs") 1030 case $mtpt in 1031 none|legacy|-) return 1 1032 ;; 1033 *) fs=$mtpt 1034 ;; 1035 esac 1036 fi 1037 fi 1038 1039 for mtpt in `share | awk '{print $2}'` ; do 1040 if [[ $mtpt == $fs ]] ; then 1041 return 0 1042 fi 1043 done 1044 1045 typeset stat=$(svcs -H -o STA nfs/server:default) 1046 if [[ $stat != "ON" ]]; then 1047 log_note "Current nfs/server status: $stat" 1048 fi 1049 1050 return 1 1051} 1052 1053# 1054# Given a mountpoint, determine if it is not shared. 1055# 1056# Returns 0 if not shared, 1 otherwise. 1057# 1058function not_shared 1059{ 1060 typeset fs=$1 1061 1062 is_shared $fs 1063 if (($? == 0)); then 1064 return 1 1065 fi 1066 1067 return 0 1068} 1069 1070# 1071# Helper function to unshare a mountpoint. 1072# 1073function unshare_fs #fs 1074{ 1075 typeset fs=$1 1076 1077 is_shared $fs 1078 if (($? == 0)); then 1079 log_must zfs unshare $fs 1080 fi 1081 1082 return 0 1083} 1084 1085# 1086# Check NFS server status and trigger it online. 1087# 1088function setup_nfs_server 1089{ 1090 # Cannot share directory in non-global zone. 1091 # 1092 if ! is_global_zone; then 1093 log_note "Cannot trigger NFS server by sharing in LZ." 1094 return 1095 fi 1096 1097 typeset nfs_fmri="svc:/network/nfs/server:default" 1098 if [[ $(svcs -Ho STA $nfs_fmri) != "ON" ]]; then 1099 # 1100 # Only really sharing operation can enable NFS server 1101 # to online permanently. 1102 # 1103 typeset dummy=/tmp/dummy 1104 1105 if [[ -d $dummy ]]; then 1106 log_must rm -rf $dummy 1107 fi 1108 1109 log_must mkdir $dummy 1110 log_must share $dummy 1111 1112 # 1113 # Waiting for fmri's status to be the final status. 1114 # Otherwise, in transition, an asterisk (*) is appended for 1115 # instances, unshare will reverse status to 'DIS' again. 1116 # 1117 # Waiting for 1's at least. 1118 # 1119 log_must sleep 1 1120 timeout=10 1121 while [[ timeout -ne 0 && $(svcs -Ho STA $nfs_fmri) == *'*' ]] 1122 do 1123 log_must sleep 1 1124 1125 ((timeout -= 1)) 1126 done 1127 1128 log_must unshare $dummy 1129 log_must rm -rf $dummy 1130 fi 1131 1132 log_note "Current NFS status: '$(svcs -Ho STA,FMRI $nfs_fmri)'" 1133} 1134 1135# 1136# To verify whether calling process is in global zone 1137# 1138# Return 0 if in global zone, 1 in non-global zone 1139# 1140function is_global_zone 1141{ 1142 typeset cur_zone=$(zonename 2>/dev/null) 1143 if [[ $cur_zone != "global" ]]; then 1144 return 1 1145 fi 1146 return 0 1147} 1148 1149# 1150# Verify whether test is permitted to run from 1151# global zone, local zone, or both 1152# 1153# $1 zone limit, could be "global", "local", or "both"(no limit) 1154# 1155# Return 0 if permitted, otherwise exit with log_unsupported 1156# 1157function verify_runnable # zone limit 1158{ 1159 typeset limit=$1 1160 1161 [[ -z $limit ]] && return 0 1162 1163 if is_global_zone ; then 1164 case $limit in 1165 global|both) 1166 ;; 1167 local) log_unsupported "Test is unable to run from "\ 1168 "global zone." 1169 ;; 1170 *) log_note "Warning: unknown limit $limit - " \ 1171 "use both." 1172 ;; 1173 esac 1174 else 1175 case $limit in 1176 local|both) 1177 ;; 1178 global) log_unsupported "Test is unable to run from "\ 1179 "local zone." 1180 ;; 1181 *) log_note "Warning: unknown limit $limit - " \ 1182 "use both." 1183 ;; 1184 esac 1185 1186 reexport_pool 1187 fi 1188 1189 return 0 1190} 1191 1192# Return 0 if create successfully or the pool exists; $? otherwise 1193# Note: In local zones, this function should return 0 silently. 1194# 1195# $1 - pool name 1196# $2-n - [keyword] devs_list 1197 1198function create_pool #pool devs_list 1199{ 1200 typeset pool=${1%%/*} 1201 1202 shift 1203 1204 if [[ -z $pool ]]; then 1205 log_note "Missing pool name." 1206 return 1 1207 fi 1208 1209 if poolexists $pool ; then 1210 destroy_pool $pool 1211 fi 1212 1213 if is_global_zone ; then 1214 [[ -d /$pool ]] && rm -rf /$pool 1215 log_must zpool create -f $pool $@ 1216 fi 1217 1218 return 0 1219} 1220 1221# Return 0 if destroy successfully or the pool exists; $? otherwise 1222# Note: In local zones, this function should return 0 silently. 1223# 1224# $1 - pool name 1225# Destroy pool with the given parameters. 1226 1227function destroy_pool #pool 1228{ 1229 typeset pool=${1%%/*} 1230 typeset mtpt 1231 1232 if [[ -z $pool ]]; then 1233 log_note "No pool name given." 1234 return 1 1235 fi 1236 1237 if is_global_zone ; then 1238 if poolexists "$pool" ; then 1239 mtpt=$(get_prop mountpoint "$pool") 1240 1241 # Clear any zinject fault handlers before attempting 1242 # destroy. Active handlers can cause I/O to hang 1243 # indefinitely, blocking zpool destroy. 1244 zinject -c all 1245 1246 # If pool I/O is suspended (e.g. due to injected 1247 # errors), clear the error state so destroy can proceed. 1248 typeset health=$(zpool list -H -o health $pool \ 1249 2>/dev/null) 1250 if [[ "$health" == "SUSPENDED" ]]; then 1251 log_note "Pool $pool is suspended; clearing" \ 1252 "before destroy." 1253 zpool clear $pool 1254 fi 1255 1256 # At times, syseventd activity can cause attempts to 1257 # destroy a pool to fail with EBUSY. We retry a few 1258 # times allowing failures before requiring the destroy 1259 # to succeed. 1260 typeset -i wait_time=10 ret=1 count=0 1261 must="" 1262 while [[ $ret -ne 0 ]]; do 1263 $must zpool destroy -f $pool 1264 ret=$? 1265 [[ $ret -eq 0 ]] && break 1266 log_note "zpool destroy failed with $ret" 1267 [[ count++ -ge 7 ]] && must=log_must 1268 sleep $wait_time 1269 done 1270 1271 [[ -d $mtpt ]] && \ 1272 log_must rm -rf $mtpt 1273 1274 if poolexists "$pool"; then 1275 log_note "destroy_pool: pool '$pool' still" \ 1276 "exists after destroy" 1277 exit $STF_ABORTED 1278 fi 1279 else 1280 log_note "Pool does not exist. ($pool)" 1281 return 1 1282 fi 1283 fi 1284 1285 return 0 1286} 1287 1288# Return 0 if created successfully; $? otherwise 1289# 1290# $1 - dataset name 1291# $2-n - dataset options 1292 1293function create_dataset #dataset dataset_options 1294{ 1295 typeset dataset=$1 1296 1297 shift 1298 1299 if [[ -z $dataset ]]; then 1300 log_note "Missing dataset name." 1301 return 1 1302 fi 1303 1304 if datasetexists $dataset ; then 1305 destroy_dataset $dataset 1306 fi 1307 1308 log_must zfs create $@ $dataset 1309 1310 return 0 1311} 1312 1313# Return 0 if destroy successfully or the dataset exists; $? otherwise 1314# Note: In local zones, this function should return 0 silently. 1315# 1316# $1 - dataset name 1317# $2 - custom arguments for zfs destroy 1318# Destroy dataset with the given parameters. 1319 1320function destroy_dataset #dataset #args 1321{ 1322 typeset dataset=$1 1323 typeset mtpt 1324 typeset args=${2:-""} 1325 1326 if [[ -z $dataset ]]; then 1327 log_note "No dataset name given." 1328 return 1 1329 fi 1330 1331 if is_global_zone ; then 1332 if datasetexists "$dataset" ; then 1333 mtpt=$(get_prop mountpoint "$dataset") 1334 log_must zfs destroy $args $dataset 1335 1336 [[ -d $mtpt ]] && \ 1337 log_must rm -rf $mtpt 1338 else 1339 log_note "Dataset does not exist. ($dataset)" 1340 return 1 1341 fi 1342 fi 1343 1344 return 0 1345} 1346 1347# 1348# Firstly, create a pool with 5 datasets. Then, create a single zone and 1349# export the 5 datasets to it. In addition, we also add a ZFS filesystem 1350# and a zvol device to the zone. 1351# 1352# $1 zone name 1353# $2 zone root directory prefix 1354# $3 zone ip 1355# 1356function zfs_zones_setup #zone_name zone_root zone_ip 1357{ 1358 typeset zone_name=${1:-$(hostname)-z} 1359 typeset zone_root=${2:-"/zone_root"} 1360 typeset zone_ip=${3:-"10.1.1.10"} 1361 typeset prefix_ctr=$ZONE_CTR 1362 typeset pool_name=$ZONE_POOL 1363 typeset -i cntctr=5 1364 typeset -i i=0 1365 1366 # Create pool and 5 container within it 1367 # 1368 [[ -d /$pool_name ]] && rm -rf /$pool_name 1369 log_must zpool create -f $pool_name $DISKS 1370 while ((i < cntctr)); do 1371 log_must zfs create $pool_name/$prefix_ctr$i 1372 ((i += 1)) 1373 done 1374 1375 # create a zvol 1376 log_must zfs create -V 1g $pool_name/zone_zvol 1377 1378 # 1379 # If current system support slog, add slog device for pool 1380 # 1381 if verify_slog_support ; then 1382 typeset sdevs="/var/tmp/sdev1 /var/tmp/sdev2" 1383 log_must mkfile $MINVDEVSIZE $sdevs 1384 log_must zpool add $pool_name log mirror $sdevs 1385 fi 1386 1387 # this isn't supported just yet. 1388 # Create a filesystem. In order to add this to 1389 # the zone, it must have it's mountpoint set to 'legacy' 1390 # log_must zfs create $pool_name/zfs_filesystem 1391 # log_must zfs set mountpoint=legacy $pool_name/zfs_filesystem 1392 1393 [[ -d $zone_root ]] && \ 1394 log_must rm -rf $zone_root/$zone_name 1395 [[ ! -d $zone_root ]] && \ 1396 log_must mkdir -p -m 0700 $zone_root/$zone_name 1397 1398 # Create zone configure file and configure the zone 1399 # 1400 typeset zone_conf=/tmp/zone_conf.$$ 1401 echo "create" > $zone_conf 1402 echo "set zonepath=$zone_root/$zone_name" >> $zone_conf 1403 echo "set autoboot=true" >> $zone_conf 1404 i=0 1405 while ((i < cntctr)); do 1406 echo "add dataset" >> $zone_conf 1407 echo "set name=$pool_name/$prefix_ctr$i" >> \ 1408 $zone_conf 1409 echo "end" >> $zone_conf 1410 ((i += 1)) 1411 done 1412 1413 # add our zvol to the zone 1414 echo "add device" >> $zone_conf 1415 echo "set match=/dev/zvol/dsk/$pool_name/zone_zvol" >> $zone_conf 1416 echo "end" >> $zone_conf 1417 1418 # add a corresponding zvol rdsk to the zone 1419 echo "add device" >> $zone_conf 1420 echo "set match=/dev/zvol/rdsk/$pool_name/zone_zvol" >> $zone_conf 1421 echo "end" >> $zone_conf 1422 1423 # once it's supported, we'll add our filesystem to the zone 1424 # echo "add fs" >> $zone_conf 1425 # echo "set type=zfs" >> $zone_conf 1426 # echo "set special=$pool_name/zfs_filesystem" >> $zone_conf 1427 # echo "set dir=/export/zfs_filesystem" >> $zone_conf 1428 # echo "end" >> $zone_conf 1429 1430 echo "verify" >> $zone_conf 1431 echo "commit" >> $zone_conf 1432 log_must zonecfg -z $zone_name -f $zone_conf 1433 log_must rm -f $zone_conf 1434 1435 # Install the zone 1436 zoneadm -z $zone_name install 1437 if (($? == 0)); then 1438 log_note "SUCCESS: zoneadm -z $zone_name install" 1439 else 1440 log_fail "FAIL: zoneadm -z $zone_name install" 1441 fi 1442 1443 # Install sysidcfg file 1444 # 1445 typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg 1446 echo "system_locale=C" > $sysidcfg 1447 echo "terminal=dtterm" >> $sysidcfg 1448 echo "network_interface=primary {" >> $sysidcfg 1449 echo "hostname=$zone_name" >> $sysidcfg 1450 echo "}" >> $sysidcfg 1451 echo "name_service=NONE" >> $sysidcfg 1452 echo "root_password=mo791xfZ/SFiw" >> $sysidcfg 1453 echo "security_policy=NONE" >> $sysidcfg 1454 echo "timezone=US/Eastern" >> $sysidcfg 1455 1456 # Boot this zone 1457 log_must zoneadm -z $zone_name boot 1458} 1459 1460# 1461# Reexport TESTPOOL & TESTPOOL(1-4) 1462# 1463function reexport_pool 1464{ 1465 typeset -i cntctr=5 1466 typeset -i i=0 1467 1468 while ((i < cntctr)); do 1469 if ((i == 0)); then 1470 TESTPOOL=$ZONE_POOL/$ZONE_CTR$i 1471 if ! ismounted $TESTPOOL; then 1472 log_must zfs mount $TESTPOOL 1473 fi 1474 else 1475 eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i 1476 if eval ! ismounted \$TESTPOOL$i; then 1477 log_must eval zfs mount \$TESTPOOL$i 1478 fi 1479 fi 1480 ((i += 1)) 1481 done 1482} 1483 1484# 1485# Verify a given disk is online or offline 1486# 1487# Return 0 is pool/disk matches expected state, 1 otherwise 1488# 1489function check_state # pool disk state{online,offline} 1490{ 1491 typeset pool=$1 1492 typeset disk=${2#/dev/dsk/} 1493 typeset state=$3 1494 1495 zpool status -v $pool | grep "$disk" \ 1496 | grep -i "$state" > /dev/null 2>&1 1497 1498 return $? 1499} 1500 1501# 1502# Get the mountpoint of snapshot 1503# For the snapshot use <mp_filesystem>/.zfs/snapshot/<snap> 1504# as its mountpoint 1505# 1506function snapshot_mountpoint 1507{ 1508 typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 1509 1510 if [[ $dataset != *@* ]]; then 1511 log_fail "Error name of snapshot '$dataset'." 1512 fi 1513 1514 typeset fs=${dataset%@*} 1515 typeset snap=${dataset#*@} 1516 1517 if [[ -z $fs || -z $snap ]]; then 1518 log_fail "Error name of snapshot '$dataset'." 1519 fi 1520 1521 echo $(get_prop mountpoint $fs)/.zfs/snapshot/$snap 1522} 1523 1524# 1525# Given a device and 'ashift' value verify it's correctly set on every label 1526# 1527function verify_ashift # device ashift 1528{ 1529 typeset device="$1" 1530 typeset ashift="$2" 1531 1532 zdb -e -lll $device | nawk -v ashift=$ashift '/ashift: / { 1533 if (ashift != $2) 1534 exit 1; 1535 else 1536 count++; 1537 } END { 1538 if (count != 4) 1539 exit 1; 1540 else 1541 exit 0; 1542 }' 1543 1544 return $? 1545} 1546 1547# 1548# Given a pool and file system, this function will verify the file system 1549# using the zdb internal tool. Note that the pool is exported and imported 1550# to ensure it has consistent state. 1551# 1552function verify_filesys # pool filesystem dir 1553{ 1554 typeset pool="$1" 1555 typeset filesys="$2" 1556 typeset zdbout="/tmp/zdbout.$$" 1557 1558 shift 1559 shift 1560 typeset dirs=$@ 1561 typeset search_path="" 1562 1563 log_note "Calling zdb to verify filesystem '$filesys'" 1564 zfs unmount -a > /dev/null 2>&1 1565 log_must zpool export $pool 1566 1567 if [[ -n $dirs ]] ; then 1568 for dir in $dirs ; do 1569 search_path="$search_path -d $dir" 1570 done 1571 fi 1572 1573 log_must zpool import $search_path $pool 1574 1575 zdb -cudi $filesys > $zdbout 2>&1 1576 if [[ $? != 0 ]]; then 1577 log_note "Output: zdb -cudi $filesys" 1578 cat $zdbout 1579 log_fail "zdb detected errors with: '$filesys'" 1580 fi 1581 1582 log_must zfs mount -a 1583 log_must rm -rf $zdbout 1584} 1585 1586# 1587# Given a pool issue a scrub and verify that no checksum errors are reported. 1588# 1589function verify_pool 1590{ 1591 typeset pool=${1:-$TESTPOOL} 1592 1593 log_must zpool scrub $pool 1594 log_must wait_scrubbed $pool 1595 1596 cksum=$(zpool status $pool | \ 1597 awk '{if ($5 == "CKSUM"){L=1; next} if (L) {print $NF;L=0}}') 1598 if [[ $cksum != 0 ]]; then 1599 log_must zpool status -v 1600 log_fail "Unexpected CKSUM errors found on $pool ($cksum)" 1601 fi 1602} 1603 1604# 1605# Given a pool, and this function list all disks in the pool 1606# 1607function get_disklist # pool 1608{ 1609 typeset disklist="" 1610 1611 disklist=$(zpool iostat -v $1 | nawk '(NR >4) {print $1}' | \ 1612 grep -v "\-\-\-\-\-" | \ 1613 egrep -v -e "^(mirror|raidz[1-3]|spare|log|cache|special|dedup)$") 1614 1615 echo $disklist 1616} 1617 1618# /** 1619# This function kills a given list of processes after a time period. We use 1620# this in the stress tests instead of STF_TIMEOUT so that we can have processes 1621# run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT 1622# would be listed as FAIL, which we don't want : we're happy with stress tests 1623# running for a certain amount of time, then finishing. 1624# 1625# @param $1 the time in seconds after which we should terminate these processes 1626# @param $2..$n the processes we wish to terminate. 1627# */ 1628function stress_timeout 1629{ 1630 typeset -i TIMEOUT=$1 1631 shift 1632 typeset cpids="$@" 1633 1634 log_note "Waiting for child processes($cpids). " \ 1635 "It could last dozens of minutes, please be patient ..." 1636 log_must sleep $TIMEOUT 1637 1638 log_note "Killing child processes after ${TIMEOUT} stress timeout." 1639 typeset pid 1640 for pid in $cpids; do 1641 ps -p $pid > /dev/null 2>&1 1642 if (($? == 0)); then 1643 log_must kill -USR1 $pid 1644 fi 1645 done 1646} 1647 1648# 1649# Verify a given hotspare disk is inuse or avail 1650# 1651# Return 0 is pool/disk matches expected state, 1 otherwise 1652# 1653function check_hotspare_state # pool disk state{inuse,avail} 1654{ 1655 typeset pool=$1 1656 typeset disk=${2#/dev/dsk/} 1657 typeset state=$3 1658 1659 cur_state=$(get_device_state $pool $disk "spares") 1660 1661 if [[ $state != ${cur_state} ]]; then 1662 return 1 1663 fi 1664 return 0 1665} 1666 1667# 1668# Wait until a hotspare transitions to a given state or times out. 1669# 1670# Return 0 when pool/disk matches expected state, 1 on timeout. 1671# 1672function wait_hotspare_state # pool disk state timeout 1673{ 1674 typeset pool=$1 1675 typeset disk=${2#$/DEV_DSKDIR/} 1676 typeset state=$3 1677 typeset timeout=${4:-60} 1678 typeset -i i=0 1679 1680 while [[ $i -lt $timeout ]]; do 1681 if check_hotspare_state $pool $disk $state; then 1682 return 0 1683 fi 1684 1685 i=$((i+1)) 1686 sleep 1 1687 done 1688 1689 return 1 1690} 1691 1692# 1693# Verify a given slog disk is inuse or avail 1694# 1695# Return 0 is pool/disk matches expected state, 1 otherwise 1696# 1697function check_slog_state # pool disk state{online,offline,unavail} 1698{ 1699 typeset pool=$1 1700 typeset disk=${2#/dev/dsk/} 1701 typeset state=$3 1702 1703 cur_state=$(get_device_state $pool $disk "logs") 1704 1705 if [[ $state != ${cur_state} ]]; then 1706 return 1 1707 fi 1708 return 0 1709} 1710 1711# 1712# Verify a given vdev disk is inuse or avail 1713# 1714# Return 0 is pool/disk matches expected state, 1 otherwise 1715# 1716function check_vdev_state # pool disk state{online,offline,unavail} 1717{ 1718 typeset pool=$1 1719 typeset disk=${2#/dev/dsk/} 1720 typeset state=$3 1721 1722 cur_state=$(get_device_state $pool $disk) 1723 1724 if [[ $state != ${cur_state} ]]; then 1725 return 1 1726 fi 1727 return 0 1728} 1729 1730# 1731# Wait until a vdev transitions to a given state or times out. 1732# 1733# Return 0 when pool/disk matches expected state, 1 on timeout. 1734# 1735function wait_vdev_state # pool disk state timeout 1736{ 1737 typeset pool=$1 1738 typeset disk=${2#$/DEV_DSKDIR/} 1739 typeset state=$3 1740 typeset timeout=${4:-60} 1741 typeset -i i=0 1742 1743 while [[ $i -lt $timeout ]]; do 1744 if check_vdev_state $pool $disk $state; then 1745 return 0 1746 fi 1747 1748 i=$((i+1)) 1749 sleep 1 1750 done 1751 1752 return 1 1753} 1754 1755# 1756# Check the output of 'zpool status -v <pool>', 1757# and to see if the content of <token> contain the <keyword> specified. 1758# 1759# Return 0 is contain, 1 otherwise 1760# 1761function check_pool_status # pool token keyword <verbose> 1762{ 1763 typeset pool=$1 1764 typeset token=$2 1765 typeset keyword=$3 1766 typeset verbose=${4:-false} 1767 1768 scan=$(zpool status -v "$pool" 2>/dev/null | nawk -v token="$token:" ' 1769 ($1==token) {print $0}') 1770 if [[ $verbose == true ]]; then 1771 log_note $scan 1772 fi 1773 echo $scan | grep -i "$keyword" > /dev/null 2>&1 1774 1775 return $? 1776} 1777 1778# 1779# The following functions are instance of check_pool_status() 1780# is_pool_resilvering - to check if the pool is resilver in progress 1781# is_pool_resilvered - to check if the pool is resilver completed 1782# is_pool_scrubbing - to check if the pool is scrub in progress 1783# is_pool_scrubbed - to check if the pool is scrub completed 1784# is_pool_scrub_stopped - to check if the pool is scrub stopped 1785# is_pool_scrub_paused - to check if the pool has scrub paused 1786# is_pool_removing - to check if the pool is removing a vdev 1787# is_pool_removed - to check if the pool is remove completed 1788# is_pool_discarding - to check if the pool has checkpoint being discarded 1789# 1790function is_pool_resilvering #pool <verbose> 1791{ 1792 check_pool_status "$1" "scan" "resilver in progress since " $2 1793 return $? 1794} 1795 1796function is_pool_resilvered #pool <verbose> 1797{ 1798 check_pool_status "$1" "scan" "resilvered " $2 1799 return $? 1800} 1801 1802function is_pool_scrubbing #pool <verbose> 1803{ 1804 check_pool_status "$1" "scan" "scrub in progress since " $2 1805 return $? 1806} 1807 1808function is_pool_scrubbed #pool <verbose> 1809{ 1810 check_pool_status "$1" "scan" "scrub repaired" $2 1811 return $? 1812} 1813 1814function is_pool_scrub_stopped #pool <verbose> 1815{ 1816 check_pool_status "$1" "scan" "scrub canceled" $2 1817 return $? 1818} 1819 1820function is_pool_scrub_paused #pool <verbose> 1821{ 1822 check_pool_status "$1" "scan" "scrub paused since " $2 1823 return $? 1824} 1825 1826function is_pool_removing #pool 1827{ 1828 check_pool_status "$1" "remove" "in progress since " 1829 return $? 1830} 1831 1832function is_pool_removed #pool 1833{ 1834 check_pool_status "$1" "remove" "completed on" 1835 return $? 1836} 1837 1838function is_pool_discarding #pool 1839{ 1840 check_pool_status "$1" "checkpoint" "discarding" 1841 return $? 1842} 1843 1844function wait_for_degraded 1845{ 1846 typeset pool=$1 1847 typeset timeout=${2:-30} 1848 typeset t0=$SECONDS 1849 1850 while :; do 1851 [[ $(get_pool_prop health $pool) == "DEGRADED" ]] && break 1852 log_note "$pool is not yet degraded." 1853 sleep 1 1854 if ((SECONDS - t0 > $timeout)); then 1855 log_note "$pool not degraded after $timeout seconds." 1856 return 1 1857 fi 1858 done 1859 1860 return 0 1861} 1862 1863# 1864# Wait for a pool to be scrubbed 1865# 1866# $1 pool name 1867# $2 number of seconds to wait (optional) 1868# 1869# Returns true when pool has been scrubbed, or false if there's a timeout or if 1870# no scrub was done. 1871# 1872function wait_scrubbed 1873{ 1874 typeset pool=${1:-$TESTPOOL} 1875 while true ; do 1876 is_pool_scrubbed $pool && break 1877 log_must sleep 1 1878 done 1879} 1880 1881# 1882# Use create_pool()/destroy_pool() to clean up the infomation in 1883# in the given disk to avoid slice overlapping. 1884# 1885function cleanup_devices #vdevs 1886{ 1887 typeset pool="foopool$$" 1888 1889 if poolexists $pool ; then 1890 destroy_pool $pool 1891 fi 1892 1893 create_pool $pool $@ 1894 destroy_pool $pool 1895 1896 return 0 1897} 1898 1899#/** 1900# A function to find and locate free disks on a system or from given 1901# disks as the parameter. It works by locating disks that are in use 1902# as swap devices and dump devices, and also disks listed in /etc/vfstab 1903# 1904# $@ given disks to find which are free, default is all disks in 1905# the test system 1906# 1907# @return a string containing the list of available disks 1908#*/ 1909function find_disks 1910{ 1911 sfi=/tmp/swaplist.$$ 1912 dmpi=/tmp/dumpdev.$$ 1913 max_finddisksnum=${MAX_FINDDISKSNUM:-6} 1914 1915 swap -l > $sfi 1916 dumpadm > $dmpi 2>/dev/null 1917 1918# write an awk script that can process the output of format 1919# to produce a list of disks we know about. Note that we have 1920# to escape "$2" so that the shell doesn't interpret it while 1921# we're creating the awk script. 1922# ------------------- 1923 cat > /tmp/find_disks.awk <<EOF 1924#!/bin/nawk -f 1925 BEGIN { FS="."; } 1926 1927 /^Specify disk/{ 1928 searchdisks=0; 1929 } 1930 1931 { 1932 if (searchdisks && \$2 !~ "^$"){ 1933 split(\$2,arr," "); 1934 print arr[1]; 1935 } 1936 } 1937 1938 /^AVAILABLE DISK SELECTIONS:/{ 1939 searchdisks=1; 1940 } 1941EOF 1942#--------------------- 1943 1944 chmod 755 /tmp/find_disks.awk 1945 disks=${@:-$(echo "" | format -e 2>/dev/null | /tmp/find_disks.awk)} 1946 rm /tmp/find_disks.awk 1947 1948 unused="" 1949 for disk in $disks; do 1950 # Check for mounted 1951 grep "${disk}[sp]" /etc/mnttab >/dev/null 1952 (($? == 0)) && continue 1953 # Check for swap 1954 grep "${disk}[sp]" $sfi >/dev/null 1955 (($? == 0)) && continue 1956 # check for dump device 1957 grep "${disk}[sp]" $dmpi >/dev/null 1958 (($? == 0)) && continue 1959 # check to see if this disk hasn't been explicitly excluded 1960 # by a user-set environment variable 1961 echo "${ZFS_HOST_DEVICES_IGNORE}" | grep "${disk}" > /dev/null 1962 (($? == 0)) && continue 1963 unused_candidates="$unused_candidates $disk" 1964 done 1965 rm $sfi 1966 rm $dmpi 1967 1968# now just check to see if those disks do actually exist 1969# by looking for a device pointing to the first slice in 1970# each case. limit the number to max_finddisksnum 1971 count=0 1972 for disk in $unused_candidates; do 1973 if [ -b /dev/dsk/${disk}s0 ]; then 1974 if [ $count -lt $max_finddisksnum ]; then 1975 unused="$unused $disk" 1976 # do not impose limit if $@ is provided 1977 [[ -z $@ ]] && ((count = count + 1)) 1978 fi 1979 fi 1980 done 1981 1982# finally, return our disk list 1983 echo $unused 1984} 1985 1986# 1987# Add specified user to specified group 1988# 1989# $1 group name 1990# $2 user name 1991# $3 base of the homedir (optional) 1992# 1993function add_user #<group_name> <user_name> <basedir> 1994{ 1995 typeset gname=$1 1996 typeset uname=$2 1997 typeset basedir=${3:-"/var/tmp"} 1998 1999 if ((${#gname} == 0 || ${#uname} == 0)); then 2000 log_fail "group name or user name are not defined." 2001 fi 2002 2003 log_must useradd -g $gname -d $basedir/$uname -m $uname 2004 log_must passwd -N $uname 2005 2006 return 0 2007} 2008 2009# 2010# Delete the specified user. 2011# 2012# $1 login name 2013# $2 base of the homedir (optional) 2014# 2015function del_user #<logname> <basedir> 2016{ 2017 typeset user=$1 2018 typeset basedir=${2:-"/var/tmp"} 2019 2020 if ((${#user} == 0)); then 2021 log_fail "login name is necessary." 2022 fi 2023 2024 if id $user > /dev/null 2>&1; then 2025 log_must userdel $user 2026 fi 2027 2028 [[ -d $basedir/$user ]] && rm -fr $basedir/$user 2029 2030 return 0 2031} 2032 2033# 2034# Select valid gid and create specified group. 2035# 2036# $1 group name 2037# 2038function add_group #<group_name> 2039{ 2040 typeset group=$1 2041 2042 if ((${#group} == 0)); then 2043 log_fail "group name is necessary." 2044 fi 2045 2046 # Assign 100 as the base gid 2047 typeset -i gid=100 2048 while true; do 2049 groupadd -g $gid $group > /dev/null 2>&1 2050 typeset -i ret=$? 2051 case $ret in 2052 0) return 0 ;; 2053 # The gid is not unique 2054 4) ((gid += 1)) ;; 2055 *) return 1 ;; 2056 esac 2057 done 2058} 2059 2060# 2061# Delete the specified group. 2062# 2063# $1 group name 2064# 2065function del_group #<group_name> 2066{ 2067 typeset grp=$1 2068 if ((${#grp} == 0)); then 2069 log_fail "group name is necessary." 2070 fi 2071 2072 groupmod -n $grp $grp > /dev/null 2>&1 2073 typeset -i ret=$? 2074 case $ret in 2075 # Group does not exist. 2076 6) return 0 ;; 2077 # Name already exists as a group name 2078 9) log_must groupdel $grp ;; 2079 *) return 1 ;; 2080 esac 2081 2082 return 0 2083} 2084 2085# 2086# This function will return true if it's safe to destroy the pool passed 2087# as argument 1. It checks for pools based on zvols and files, and also 2088# files contained in a pool that may have a different mountpoint. 2089# 2090function safe_to_destroy_pool { # $1 the pool name 2091 2092 typeset pool="" 2093 typeset DONT_DESTROY="" 2094 2095 # We check that by deleting the $1 pool, we're not 2096 # going to pull the rug out from other pools. Do this 2097 # by looking at all other pools, ensuring that they 2098 # aren't built from files or zvols contained in this pool. 2099 2100 for pool in $(zpool list -H -o name) 2101 do 2102 ALTMOUNTPOOL="" 2103 2104 # this is a list of the top-level directories in each of the 2105 # files that make up the path to the files the pool is based on 2106 FILEPOOL=$(zpool status -v $pool | grep /$1/ | \ 2107 awk '{print $1}') 2108 2109 # this is a list of the zvols that make up the pool 2110 ZVOLPOOL=$(zpool status -v $pool | grep "/dev/zvol/dsk/$1$" \ 2111 | awk '{print $1}') 2112 2113 # also want to determine if it's a file-based pool using an 2114 # alternate mountpoint... 2115 POOL_FILE_DIRS=$(zpool status -v $pool | \ 2116 grep / | awk '{print $1}' | \ 2117 awk -F/ '{print $2}' | grep -v "dev") 2118 2119 for pooldir in $POOL_FILE_DIRS 2120 do 2121 OUTPUT=$(zfs list -H -r -o mountpoint $1 | \ 2122 grep "${pooldir}$" | awk '{print $1}') 2123 2124 ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}" 2125 done 2126 2127 2128 if [ ! -z "$ZVOLPOOL" ] 2129 then 2130 DONT_DESTROY="true" 2131 log_note "Pool $pool is built from $ZVOLPOOL on $1" 2132 fi 2133 2134 if [ ! -z "$FILEPOOL" ] 2135 then 2136 DONT_DESTROY="true" 2137 log_note "Pool $pool is built from $FILEPOOL on $1" 2138 fi 2139 2140 if [ ! -z "$ALTMOUNTPOOL" ] 2141 then 2142 DONT_DESTROY="true" 2143 log_note "Pool $pool is built from $ALTMOUNTPOOL on $1" 2144 fi 2145 done 2146 2147 if [ -z "${DONT_DESTROY}" ] 2148 then 2149 return 0 2150 else 2151 log_note "Warning: it is not safe to destroy $1!" 2152 return 1 2153 fi 2154} 2155 2156# 2157# Get the available ZFS compression options 2158# $1 option type zfs_set|zfs_compress 2159# 2160function get_compress_opts 2161{ 2162 typeset COMPRESS_OPTS 2163 typeset GZIP_OPTS="gzip gzip-1 gzip-2 gzip-3 gzip-4 gzip-5 \ 2164 gzip-6 gzip-7 gzip-8 gzip-9" 2165 2166 if [[ $1 == "zfs_compress" ]] ; then 2167 COMPRESS_OPTS="on lzjb" 2168 elif [[ $1 == "zfs_set" ]] ; then 2169 COMPRESS_OPTS="on off lzjb" 2170 fi 2171 typeset valid_opts="$COMPRESS_OPTS" 2172 zfs get 2>&1 | grep gzip >/dev/null 2>&1 2173 if [[ $? -eq 0 ]]; then 2174 valid_opts="$valid_opts $GZIP_OPTS" 2175 fi 2176 echo "$valid_opts" 2177} 2178 2179# 2180# Verify zfs operation with -p option work as expected 2181# $1 operation, value could be create, clone or rename 2182# $2 dataset type, value could be fs or vol 2183# $3 dataset name 2184# $4 new dataset name 2185# 2186function verify_opt_p_ops 2187{ 2188 typeset ops=$1 2189 typeset datatype=$2 2190 typeset dataset=$3 2191 typeset newdataset=$4 2192 2193 if [[ $datatype != "fs" && $datatype != "vol" ]]; then 2194 log_fail "$datatype is not supported." 2195 fi 2196 2197 # check parameters accordingly 2198 case $ops in 2199 create) 2200 newdataset=$dataset 2201 dataset="" 2202 if [[ $datatype == "vol" ]]; then 2203 ops="create -V $VOLSIZE" 2204 fi 2205 ;; 2206 clone) 2207 if [[ -z $newdataset ]]; then 2208 log_fail "newdataset should not be empty" \ 2209 "when ops is $ops." 2210 fi 2211 log_must datasetexists $dataset 2212 log_must snapexists $dataset 2213 ;; 2214 rename) 2215 if [[ -z $newdataset ]]; then 2216 log_fail "newdataset should not be empty" \ 2217 "when ops is $ops." 2218 fi 2219 log_must datasetexists $dataset 2220 log_mustnot snapexists $dataset 2221 ;; 2222 *) 2223 log_fail "$ops is not supported." 2224 ;; 2225 esac 2226 2227 # make sure the upper level filesystem does not exist 2228 destroy_dataset ${newdataset%/*} "-rRf" 2229 2230 # without -p option, operation will fail 2231 log_mustnot zfs $ops $dataset $newdataset 2232 log_mustnot datasetexists $newdataset ${newdataset%/*} 2233 2234 # with -p option, operation should succeed 2235 log_must zfs $ops -p $dataset $newdataset 2236 if ! datasetexists $newdataset ; then 2237 log_fail "-p option does not work for $ops" 2238 fi 2239 2240 # when $ops is create or clone, redo the operation still return zero 2241 if [[ $ops != "rename" ]]; then 2242 log_must zfs $ops -p $dataset $newdataset 2243 fi 2244 2245 return 0 2246} 2247 2248# 2249# Get configuration of pool 2250# $1 pool name 2251# $2 config name 2252# 2253function get_config 2254{ 2255 typeset pool=$1 2256 typeset config=$2 2257 typeset alt_root 2258 2259 if ! poolexists "$pool" ; then 2260 return 1 2261 fi 2262 alt_root=$(zpool list -H $pool | awk '{print $NF}') 2263 if [[ $alt_root == "-" ]]; then 2264 value=$(zdb -C $pool | grep "$config:" | awk -F: \ 2265 '{print $2}') 2266 else 2267 value=$(zdb -e $pool | grep "$config:" | awk -F: \ 2268 '{print $2}') 2269 fi 2270 if [[ -n $value ]] ; then 2271 value=${value#'} 2272 value=${value%'} 2273 fi 2274 echo $value 2275 2276 return 0 2277} 2278 2279# 2280# Privated function. Random select one of items from arguments. 2281# 2282# $1 count 2283# $2-n string 2284# 2285function _random_get 2286{ 2287 typeset cnt=$1 2288 shift 2289 2290 typeset str="$@" 2291 typeset -i ind 2292 ((ind = RANDOM % cnt + 1)) 2293 2294 typeset ret=$(echo "$str" | cut -f $ind -d ' ') 2295 echo $ret 2296} 2297 2298# 2299# Random select one of item from arguments which include NONE string 2300# 2301function random_get_with_non 2302{ 2303 typeset -i cnt=$# 2304 ((cnt =+ 1)) 2305 2306 _random_get "$cnt" "$@" 2307} 2308 2309# 2310# Random select one of item from arguments which doesn't include NONE string 2311# 2312function random_get 2313{ 2314 _random_get "$#" "$@" 2315} 2316 2317# 2318# Detect if the current system support slog 2319# 2320function verify_slog_support 2321{ 2322 typeset dir=/tmp/disk.$$ 2323 typeset pool=foo.$$ 2324 typeset vdev=$dir/a 2325 typeset sdev=$dir/b 2326 2327 mkdir -p $dir 2328 mkfile $MINVDEVSIZE $vdev $sdev 2329 2330 typeset -i ret=0 2331 if ! zpool create -n $pool $vdev log $sdev > /dev/null 2>&1; then 2332 ret=1 2333 fi 2334 rm -r $dir 2335 2336 return $ret 2337} 2338 2339# 2340# The function will generate a dataset name with specific length 2341# $1, the length of the name 2342# $2, the base string to construct the name 2343# 2344function gen_dataset_name 2345{ 2346 typeset -i len=$1 2347 typeset basestr="$2" 2348 typeset -i baselen=${#basestr} 2349 typeset -i iter=0 2350 typeset l_name="" 2351 2352 if ((len % baselen == 0)); then 2353 ((iter = len / baselen)) 2354 else 2355 ((iter = len / baselen + 1)) 2356 fi 2357 while ((iter > 0)); do 2358 l_name="${l_name}$basestr" 2359 2360 ((iter -= 1)) 2361 done 2362 2363 echo $l_name 2364} 2365 2366# 2367# Get cksum tuple of dataset 2368# $1 dataset name 2369# 2370# sample zdb output: 2371# Dataset data/test [ZPL], ID 355, cr_txg 2413856, 31.0K, 7 objects, rootbp 2372# DVA[0]=<0:803046400:200> DVA[1]=<0:81199000:200> [L0 DMU objset] fletcher4 2373# lzjb LE contiguous unique double size=800L/200P birth=2413856L/2413856P 2374# fill=7 cksum=11ce125712:643a9c18ee2:125e25238fca0:254a3f74b59744 2375function datasetcksum 2376{ 2377 typeset cksum 2378 sync 2379 sync_all_pools 2380 cksum=$(zdb -vvv $1 | grep "^Dataset $1 \[" | grep "cksum" \ 2381 | awk -F= '{print $7}') 2382 echo $cksum 2383} 2384 2385# 2386# Get cksum of file 2387# #1 file path 2388# 2389function checksum 2390{ 2391 typeset cksum 2392 cksum=$(cksum $1 | awk '{print $1}') 2393 echo $cksum 2394} 2395 2396# 2397# Get the given disk/slice state from the specific field of the pool 2398# 2399function get_device_state #pool disk field("", "spares","logs") 2400{ 2401 typeset pool=$1 2402 typeset disk=${2#/dev/dsk/} 2403 typeset field=${3:-$pool} 2404 2405 state=$(zpool status -v "$pool" 2>/dev/null | \ 2406 nawk -v device=$disk -v pool=$pool -v field=$field \ 2407 'BEGIN {startconfig=0; startfield=0; } 2408 /config:/ {startconfig=1} 2409 (startconfig==1) && ($1==field) {startfield=1; next;} 2410 (startfield==1) && ($1==device) {print $2; exit;} 2411 (startfield==1) && 2412 ($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}') 2413 echo $state 2414} 2415 2416 2417# 2418# print the given directory filesystem type 2419# 2420# $1 directory name 2421# 2422function get_fstype 2423{ 2424 typeset dir=$1 2425 2426 if [[ -z $dir ]]; then 2427 log_fail "Usage: get_fstype <directory>" 2428 fi 2429 2430 # 2431 # $ df -n / 2432 # / : ufs 2433 # 2434 df -n $dir | awk '{print $3}' 2435} 2436 2437# 2438# Given a disk, label it to VTOC regardless what label was on the disk 2439# $1 disk 2440# 2441function labelvtoc 2442{ 2443 typeset disk=$1 2444 typeset -i iter=120 2445 typeset -i ret_val=1 2446 2447 if [[ -z $disk ]]; then 2448 log_fail "The disk name is unspecified." 2449 fi 2450 typeset label_file=/var/tmp/labelvtoc.$$ 2451 typeset arch=$(uname -p) 2452 2453 if [[ $arch == "i386" ]]; then 2454 log_must fdisk -B ${disk}p0 2455 2456 echo "label" > $label_file 2457 echo "0" >> $label_file 2458 echo "" >> $label_file 2459 echo "q" >> $label_file 2460 echo "q" >> $label_file 2461 elif [[ $arch == "sparc" ]]; then 2462 echo "label" > $label_file 2463 echo "0" >> $label_file 2464 echo "" >> $label_file 2465 echo "" >> $label_file 2466 echo "" >> $label_file 2467 echo "q" >> $label_file 2468 else 2469 log_fail "unknown arch type" 2470 fi 2471 2472 # Disk update from fdisk -B may be delayed 2473 while ((iter > 0)); do 2474 if format -e -s -d $disk -f $label_file ; then 2475 iter=0 2476 ret_val=0 2477 else 2478 sleep 1 2479 ((iter -= 1)) 2480 fi 2481 done 2482 rm -f $label_file 2483 if ((ret_val != 0)); then 2484 log_fail "unable to label $disk as VTOC." 2485 fi 2486 2487 return 0 2488} 2489 2490# 2491# check if the system was installed as zfsroot or not 2492# return: 0 ture, otherwise false 2493# 2494function is_zfsroot 2495{ 2496 df -n / | grep zfs > /dev/null 2>&1 2497 return $? 2498} 2499 2500# 2501# get the root filesystem name if it's zfsroot system. 2502# 2503# return: root filesystem name 2504function get_rootfs 2505{ 2506 typeset rootfs="" 2507 rootfs=$(awk '{if ($2 == "/" && $3 == "zfs") print $1}' \ 2508 /etc/mnttab) 2509 if [[ -z "$rootfs" ]]; then 2510 log_fail "Can not get rootfs" 2511 fi 2512 zfs list $rootfs > /dev/null 2>&1 2513 if (($? == 0)); then 2514 echo $rootfs 2515 else 2516 log_fail "This is not a zfsroot system." 2517 fi 2518} 2519 2520# 2521# get the rootfs's pool name 2522# return: 2523# rootpool name 2524# 2525function get_rootpool 2526{ 2527 typeset rootfs="" 2528 typeset rootpool="" 2529 rootfs=$(awk '{if ($2 == "/" && $3 =="zfs") print $1}' \ 2530 /etc/mnttab) 2531 if [[ -z "$rootfs" ]]; then 2532 log_fail "Can not get rootpool" 2533 fi 2534 zfs list $rootfs > /dev/null 2>&1 2535 if (($? == 0)); then 2536 rootpool=`echo $rootfs | awk -F\/ '{print $1}'` 2537 echo $rootpool 2538 else 2539 log_fail "This is not a zfsroot system." 2540 fi 2541} 2542 2543# 2544# Check if the given device is physical device 2545# 2546function is_physical_device #device 2547{ 2548 typeset device=${1#/dev/dsk/} 2549 device=${device#/dev/rdsk/} 2550 2551 echo $device | egrep "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1 2552 return $? 2553} 2554 2555# 2556# Get the directory path of given device 2557# 2558function get_device_dir #device 2559{ 2560 typeset device=$1 2561 2562 if ! $(is_physical_device $device) ; then 2563 if [[ $device != "/" ]]; then 2564 device=${device%/*} 2565 fi 2566 echo $device 2567 else 2568 echo "/dev/dsk" 2569 fi 2570} 2571 2572# 2573# Get the package name 2574# 2575function get_package_name 2576{ 2577 typeset dirpath=${1:-$STC_NAME} 2578 2579 echo "SUNWstc-${dirpath}" | /usr/bin/sed -e "s/\//-/g" 2580} 2581 2582# 2583# Get the word numbers from a string separated by white space 2584# 2585function get_word_count 2586{ 2587 echo $1 | wc -w 2588} 2589 2590# 2591# To verify if the require numbers of disks is given 2592# 2593function verify_disk_count 2594{ 2595 typeset -i min=${2:-1} 2596 2597 typeset -i count=$(get_word_count "$1") 2598 2599 if ((count < min)); then 2600 log_untested "A minimum of $min disks is required to run." \ 2601 " You specified $count disk(s)" 2602 fi 2603} 2604 2605function ds_is_volume 2606{ 2607 typeset type=$(get_prop type $1) 2608 [[ $type = "volume" ]] && return 0 2609 return 1 2610} 2611 2612function ds_is_filesystem 2613{ 2614 typeset type=$(get_prop type $1) 2615 [[ $type = "filesystem" ]] && return 0 2616 return 1 2617} 2618 2619function ds_is_snapshot 2620{ 2621 typeset type=$(get_prop type $1) 2622 [[ $type = "snapshot" ]] && return 0 2623 return 1 2624} 2625 2626# 2627# Check if Trusted Extensions are installed and enabled 2628# 2629function is_te_enabled 2630{ 2631 svcs -H -o state labeld 2>/dev/null | grep "enabled" 2632 if (($? != 0)); then 2633 return 1 2634 else 2635 return 0 2636 fi 2637} 2638 2639# Utility function to determine if a system has multiple cpus. 2640function is_mp 2641{ 2642 (($(psrinfo | wc -l) > 1)) 2643} 2644 2645function get_cpu_freq 2646{ 2647 psrinfo -v 0 | awk '/processor operates at/ {print $6}' 2648} 2649 2650# Run the given command as the user provided. 2651function user_run 2652{ 2653 typeset user=$1 2654 shift 2655 2656 eval su \$user -c \"$@\" > /tmp/out 2>/tmp/err 2657 return $? 2658} 2659 2660# 2661# Check if the pool contains the specified vdevs 2662# 2663# $1 pool 2664# $2..n <vdev> ... 2665# 2666# Return 0 if the vdevs are contained in the pool, 1 if any of the specified 2667# vdevs is not in the pool, and 2 if pool name is missing. 2668# 2669function vdevs_in_pool 2670{ 2671 typeset pool=$1 2672 typeset vdev 2673 2674 if [[ -z $pool ]]; then 2675 log_note "Missing pool name." 2676 return 2 2677 fi 2678 2679 shift 2680 2681 typeset tmpfile=$(mktemp) 2682 zpool list -Hv "$pool" >$tmpfile 2683 for vdev in $@; do 2684 grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1 2685 [[ $? -ne 0 ]] && return 1 2686 done 2687 2688 rm -f $tmpfile 2689 2690 return 0; 2691} 2692 2693function get_max 2694{ 2695 typeset -l i max=$1 2696 shift 2697 2698 for i in "$@"; do 2699 max=$(echo $((max > i ? max : i))) 2700 done 2701 2702 echo $max 2703} 2704 2705function get_min 2706{ 2707 typeset -l i min=$1 2708 shift 2709 2710 for i in "$@"; do 2711 min=$(echo $((min < i ? min : i))) 2712 done 2713 2714 echo $min 2715} 2716 2717# 2718# Generate a random number between 1 and the argument. 2719# 2720function random 2721{ 2722 typeset max=$1 2723 echo $(( ($RANDOM % $max) + 1 )) 2724} 2725 2726# Write data that can be compressed into a directory 2727function write_compressible 2728{ 2729 typeset dir=$1 2730 typeset megs=$2 2731 typeset nfiles=${3:-1} 2732 typeset bs=${4:-1024k} 2733 typeset fname=${5:-file} 2734 2735 [[ -d $dir ]] || log_fail "No directory: $dir" 2736 2737 log_must eval "fio \ 2738 --name=job \ 2739 --fallocate=0 \ 2740 --minimal \ 2741 --randrepeat=0 \ 2742 --buffer_compress_percentage=66 \ 2743 --buffer_compress_chunk=4096 \ 2744 --directory=$dir \ 2745 --numjobs=$nfiles \ 2746 --rw=write \ 2747 --bs=$bs \ 2748 --filesize=$megs \ 2749 --filename_format='$fname.\$jobnum' >/dev/null" 2750} 2751 2752function get_objnum 2753{ 2754 typeset pathname=$1 2755 typeset objnum 2756 2757 [[ -e $pathname ]] || log_fail "No such file or directory: $pathname" 2758 objnum=$(stat -c %i $pathname) 2759 echo $objnum 2760} 2761 2762# 2763# Sync data to the pool 2764# 2765# $1 pool name 2766# $2 boolean to force uberblock (and config including zpool cache file) update 2767# 2768function sync_pool #pool <force> 2769{ 2770 typeset pool=${1:-$TESTPOOL} 2771 typeset force=${2:-false} 2772 2773 if [[ $force == true ]]; then 2774 log_must zpool sync -f $pool 2775 else 2776 log_must zpool sync $pool 2777 fi 2778 2779 return 0 2780} 2781 2782# 2783# Sync all pools 2784# 2785# $1 boolean to force uberblock (and config including zpool cache file) update 2786# 2787function sync_all_pools #<force> 2788{ 2789 typeset force=${1:-false} 2790 2791 if [[ $force == true ]]; then 2792 log_must zpool sync -f 2793 else 2794 log_must zpool sync 2795 fi 2796 2797 return 0 2798} 2799 2800# 2801# Wait for zpool 'freeing' property drops to zero. 2802# 2803# $1 pool name 2804# 2805function wait_freeing #pool 2806{ 2807 typeset pool=${1:-$TESTPOOL} 2808 while true; do 2809 [[ "0" == "$(zpool list -Ho freeing $pool)" ]] && break 2810 log_must sleep 1 2811 done 2812} 2813 2814# 2815# Prints the current time in seconds since UNIX Epoch. 2816# 2817function current_epoch 2818{ 2819 printf '%(%s)T' 2820} 2821 2822# 2823# Wait for every device replace operation to complete 2824# 2825# $1 pool name 2826# 2827function wait_replacing #pool 2828{ 2829 typeset pool=${1:-$TESTPOOL} 2830 while true; do 2831 [[ "" == "$(zpool status $pool | 2832 awk '/replacing-[0-9]+/ {print $1}')" ]] && break 2833 log_must sleep 1 2834 done 2835} 2836 2837# 2838# Set a global system tunable (64-bit value) 2839# 2840# $1 tunable name 2841# $2 tunable values 2842# 2843function set_tunable64 2844{ 2845 set_tunable_impl "$1" "$2" Z 2846} 2847 2848# 2849# Set a global system tunable (32-bit value) 2850# 2851# $1 tunable name 2852# $2 tunable values 2853# 2854function set_tunable32 2855{ 2856 set_tunable_impl "$1" "$2" W 2857} 2858 2859function set_tunable_impl 2860{ 2861 typeset tunable="$1" 2862 typeset value="$2" 2863 typeset mdb_cmd="$3" 2864 typeset module="${4:-zfs}" 2865 2866 [[ -z "$tunable" ]] && return 1 2867 [[ -z "$value" ]] && return 1 2868 [[ -z "$mdb_cmd" ]] && return 1 2869 2870 case "$(uname)" in 2871 Linux) 2872 typeset zfs_tunables="/sys/module/$module/parameters" 2873 [[ -w "$zfs_tunables/$tunable" ]] || return 1 2874 cat >"$zfs_tunables/$tunable" <<<"$value" 2875 return $? 2876 ;; 2877 SunOS) 2878 [[ "$module" -eq "zfs" ]] || return 1 2879 echo "${tunable}/${mdb_cmd}0t${value}" | mdb -kw 2880 return $? 2881 ;; 2882 esac 2883} 2884 2885# 2886# Get a global system tunable 2887# 2888# $1 tunable name 2889# 2890function get_tunable 2891{ 2892 get_tunable_impl "$1" 2893} 2894 2895function get_tunable_impl 2896{ 2897 typeset tunable="$1" 2898 typeset module="${2:-zfs}" 2899 2900 [[ -z "$tunable" ]] && return 1 2901 2902 case "$(uname)" in 2903 Linux) 2904 typeset zfs_tunables="/sys/module/$module/parameters" 2905 [[ -f "$zfs_tunables/$tunable" ]] || return 1 2906 cat $zfs_tunables/$tunable 2907 return $? 2908 ;; 2909 SunOS) 2910 typeset value=$(mdb -k -e "$tunable::print | ::eval .=E") 2911 if [[ $? -ne 0 ]]; then 2912 log_fail "Failed to get value of '$tunable' from mdb." 2913 return 1 2914 fi 2915 echo $value 2916 return 0 2917 ;; 2918 esac 2919 2920 return 1 2921} 2922 2923function new_fs #<args> 2924{ 2925 case "$(uname)" in 2926 FreeBSD) 2927 newfs "$@" 2928 ;; 2929 SunOS) 2930 echo y | newfs "$@" 2931 ;; 2932 *) 2933 echo y | newfs -v "$@" 2934 ;; 2935 esac 2936} 2937 2938# 2939# Wait for the specified arcstat to reach non-zero quiescence. 2940# If echo is 1 echo the value after reaching quiescence, otherwise 2941# if echo is 0 print the arcstat we are waiting on. 2942# 2943function arcstat_quiescence # stat echo 2944{ 2945 typeset stat=$1 2946 typeset echo=$2 2947 typeset do_once=true 2948 2949 if [[ $echo -eq 0 ]]; then 2950 echo "Waiting for arcstat $1 quiescence." 2951 fi 2952 2953 while $do_once || [ $stat1 -ne $stat2 ] || [ $stat2 -eq 0 ]; do 2954 typeset stat1=$(kstat arcstats.$stat) 2955 sleep 2 2956 typeset stat2=$(kstat arcstats.$stat) 2957 do_once=false 2958 done 2959 2960 if [[ $echo -eq 1 ]]; then 2961 echo $stat2 2962 fi 2963} 2964 2965function arcstat_quiescence_noecho # stat 2966{ 2967 typeset stat=$1 2968 arcstat_quiescence $stat 0 2969} 2970 2971function arcstat_quiescence_echo # stat 2972{ 2973 typeset stat=$1 2974 arcstat_quiescence $stat 1 2975} 2976 2977# 2978# Compute SHA256 digest for given file or stdin if no file given. 2979# Note: file path must not contain spaces 2980# 2981function sha256digest 2982{ 2983 typeset file=$1 2984 2985 if [ -x /usr/bin/digest ]; then 2986 /usr/bin/digest -a sha256 $file 2987 elif [ -x /usr/bin/sha256sum ]; then 2988 /usr/bin/sha256sum -b $file | awk '{ print $1 }' 2989 else 2990 echo "Cannot calculate SHA256 digest" 2991 return 1 2992 fi 2993 return 0 2994} 2995 2996. ${STF_SUITE}/include/kstat.shlib 2997