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