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 (c) 2009, Sun Microsystems Inc. All rights reserved. 24# Copyright (c) 2012, 2020, Delphix. All rights reserved. 25# Copyright (c) 2017, Tim Chase. All rights reserved. 26# Copyright (c) 2017, Nexenta Systems Inc. All rights reserved. 27# Copyright (c) 2017, Lawrence Livermore National Security LLC. 28# Copyright (c) 2017, Datto Inc. All rights reserved. 29# Copyright (c) 2017, Open-E Inc. All rights reserved. 30# Use is subject to license terms. 31# 32 33. ${STF_TOOLS}/include/logapi.shlib 34. ${STF_SUITE}/include/math.shlib 35. ${STF_SUITE}/include/blkdev.shlib 36 37. ${STF_SUITE}/include/tunables.cfg 38 39# 40# Apply constrained path when available. This is required since the 41# PATH may have been modified by sudo's secure_path behavior. 42# 43if [ -n "$STF_PATH" ]; then 44 export PATH="$STF_PATH" 45fi 46 47# 48# Generic dot version comparison function 49# 50# Returns success when version $1 is greater than or equal to $2. 51# 52function compare_version_gte 53{ 54 if [[ "$(printf "$1\n$2" | sort -V | tail -n1)" == "$1" ]]; then 55 return 0 56 else 57 return 1 58 fi 59} 60 61# Linux kernel version comparison function 62# 63# $1 Linux version ("4.10", "2.6.32") or blank for installed Linux version 64# 65# Used for comparison: if [ $(linux_version) -ge $(linux_version "2.6.32") ] 66# 67function linux_version 68{ 69 typeset ver="$1" 70 71 [[ -z "$ver" ]] && ver=$(uname -r | grep -Eo "^[0-9]+\.[0-9]+\.[0-9]+") 72 73 typeset version=$(echo $ver | cut -d '.' -f 1) 74 typeset major=$(echo $ver | cut -d '.' -f 2) 75 typeset minor=$(echo $ver | cut -d '.' -f 3) 76 77 [[ -z "$version" ]] && version=0 78 [[ -z "$major" ]] && major=0 79 [[ -z "$minor" ]] && minor=0 80 81 echo $((version * 10000 + major * 100 + minor)) 82} 83 84# Determine if this is a Linux test system 85# 86# Return 0 if platform Linux, 1 if otherwise 87 88function is_linux 89{ 90 if [[ $(uname -o) == "GNU/Linux" ]]; then 91 return 0 92 else 93 return 1 94 fi 95} 96 97# Determine if this is an illumos test system 98# 99# Return 0 if platform illumos, 1 if otherwise 100function is_illumos 101{ 102 if [[ $(uname -o) == "illumos" ]]; then 103 return 0 104 else 105 return 1 106 fi 107} 108 109# Determine if this is a FreeBSD test system 110# 111# Return 0 if platform FreeBSD, 1 if otherwise 112 113function is_freebsd 114{ 115 if [[ $(uname -o) == "FreeBSD" ]]; then 116 return 0 117 else 118 return 1 119 fi 120} 121 122# Determine if this is a DilOS test system 123# 124# Return 0 if platform DilOS, 1 if otherwise 125 126function is_dilos 127{ 128 typeset ID="" 129 [[ -f /etc/os-release ]] && . /etc/os-release 130 if [[ $ID == "dilos" ]]; then 131 return 0 132 else 133 return 1 134 fi 135} 136 137# Determine if this is a 32-bit system 138# 139# Return 0 if platform is 32-bit, 1 if otherwise 140 141function is_32bit 142{ 143 if [[ $(getconf LONG_BIT) == "32" ]]; then 144 return 0 145 else 146 return 1 147 fi 148} 149 150# Determine if kmemleak is enabled 151# 152# Return 0 if kmemleak is enabled, 1 if otherwise 153 154function is_kmemleak 155{ 156 if is_linux && [[ -e /sys/kernel/debug/kmemleak ]]; then 157 return 0 158 else 159 return 1 160 fi 161} 162 163# Determine whether a dataset is mounted 164# 165# $1 dataset name 166# $2 filesystem type; optional - defaulted to zfs 167# 168# Return 0 if dataset is mounted; 1 if unmounted; 2 on error 169 170function ismounted 171{ 172 typeset fstype=$2 173 [[ -z $fstype ]] && fstype=zfs 174 typeset out dir name ret 175 176 case $fstype in 177 zfs) 178 if [[ "$1" == "/"* ]] ; then 179 for out in $(zfs mount | awk '{print $2}'); do 180 [[ $1 == $out ]] && return 0 181 done 182 else 183 for out in $(zfs mount | awk '{print $1}'); do 184 [[ $1 == $out ]] && return 0 185 done 186 fi 187 ;; 188 ufs|nfs) 189 if is_freebsd; then 190 mount -pt $fstype | while read dev dir _t _flags; do 191 [[ "$1" == "$dev" || "$1" == "$dir" ]] && return 0 192 done 193 else 194 out=$(df -F $fstype $1 2>/dev/null) 195 ret=$? 196 (($ret != 0)) && return $ret 197 198 dir=${out%%\(*} 199 dir=${dir%% *} 200 name=${out##*\(} 201 name=${name%%\)*} 202 name=${name%% *} 203 204 [[ "$1" == "$dir" || "$1" == "$name" ]] && return 0 205 fi 206 ;; 207 ext*) 208 out=$(df -t $fstype $1 2>/dev/null) 209 return $? 210 ;; 211 zvol) 212 if [[ -L "$ZVOL_DEVDIR/$1" ]]; then 213 link=$(readlink -f $ZVOL_DEVDIR/$1) 214 [[ -n "$link" ]] && \ 215 mount | grep -q "^$link" && \ 216 return 0 217 fi 218 ;; 219 esac 220 221 return 1 222} 223 224# Return 0 if a dataset is mounted; 1 otherwise 225# 226# $1 dataset name 227# $2 filesystem type; optional - defaulted to zfs 228 229function mounted 230{ 231 ismounted $1 $2 232 (($? == 0)) && return 0 233 return 1 234} 235 236# Return 0 if a dataset is unmounted; 1 otherwise 237# 238# $1 dataset name 239# $2 filesystem type; optional - defaulted to zfs 240 241function unmounted 242{ 243 ismounted $1 $2 244 (($? == 1)) && return 0 245 return 1 246} 247 248# split line on "," 249# 250# $1 - line to split 251 252function splitline 253{ 254 echo $1 | sed "s/,/ /g" 255} 256 257function default_setup 258{ 259 default_setup_noexit "$@" 260 261 log_pass 262} 263 264function default_setup_no_mountpoint 265{ 266 default_setup_noexit "$1" "$2" "$3" "yes" 267 268 log_pass 269} 270 271# 272# Given a list of disks, setup storage pools and datasets. 273# 274function default_setup_noexit 275{ 276 typeset disklist=$1 277 typeset container=$2 278 typeset volume=$3 279 typeset no_mountpoint=$4 280 log_note begin default_setup_noexit 281 282 if is_global_zone; then 283 if poolexists $TESTPOOL ; then 284 destroy_pool $TESTPOOL 285 fi 286 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 287 log_must zpool create -f $TESTPOOL $disklist 288 else 289 reexport_pool 290 fi 291 292 rm -rf $TESTDIR || log_unresolved Could not remove $TESTDIR 293 mkdir -p $TESTDIR || log_unresolved Could not create $TESTDIR 294 295 log_must zfs create $TESTPOOL/$TESTFS 296 if [[ -z $no_mountpoint ]]; then 297 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 298 fi 299 300 if [[ -n $container ]]; then 301 rm -rf $TESTDIR1 || \ 302 log_unresolved Could not remove $TESTDIR1 303 mkdir -p $TESTDIR1 || \ 304 log_unresolved Could not create $TESTDIR1 305 306 log_must zfs create $TESTPOOL/$TESTCTR 307 log_must zfs set canmount=off $TESTPOOL/$TESTCTR 308 log_must zfs create $TESTPOOL/$TESTCTR/$TESTFS1 309 if [[ -z $no_mountpoint ]]; then 310 log_must zfs set mountpoint=$TESTDIR1 \ 311 $TESTPOOL/$TESTCTR/$TESTFS1 312 fi 313 fi 314 315 if [[ -n $volume ]]; then 316 if is_global_zone ; then 317 log_must zfs create -V $VOLSIZE $TESTPOOL/$TESTVOL 318 block_device_wait 319 else 320 log_must zfs create $TESTPOOL/$TESTVOL 321 fi 322 fi 323} 324 325# 326# Given a list of disks, setup a storage pool, file system and 327# a container. 328# 329function default_container_setup 330{ 331 typeset disklist=$1 332 333 default_setup "$disklist" "true" 334} 335 336# 337# Given a list of disks, setup a storage pool,file system 338# and a volume. 339# 340function default_volume_setup 341{ 342 typeset disklist=$1 343 344 default_setup "$disklist" "" "true" 345} 346 347# 348# Given a list of disks, setup a storage pool,file system, 349# a container and a volume. 350# 351function default_container_volume_setup 352{ 353 typeset disklist=$1 354 355 default_setup "$disklist" "true" "true" 356} 357 358# 359# Create a snapshot on a filesystem or volume. Defaultly create a snapshot on 360# filesystem 361# 362# $1 Existing filesystem or volume name. Default, $TESTPOOL/$TESTFS 363# $2 snapshot name. Default, $TESTSNAP 364# 365function create_snapshot 366{ 367 typeset fs_vol=${1:-$TESTPOOL/$TESTFS} 368 typeset snap=${2:-$TESTSNAP} 369 370 [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined." 371 [[ -z $snap ]] && log_fail "Snapshot's name is undefined." 372 373 if snapexists $fs_vol@$snap; then 374 log_fail "$fs_vol@$snap already exists." 375 fi 376 datasetexists $fs_vol || \ 377 log_fail "$fs_vol must exist." 378 379 log_must zfs snapshot $fs_vol@$snap 380} 381 382# 383# Create a clone from a snapshot, default clone name is $TESTCLONE. 384# 385# $1 Existing snapshot, $TESTPOOL/$TESTFS@$TESTSNAP is default. 386# $2 Clone name, $TESTPOOL/$TESTCLONE is default. 387# 388function create_clone # snapshot clone 389{ 390 typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 391 typeset clone=${2:-$TESTPOOL/$TESTCLONE} 392 393 [[ -z $snap ]] && \ 394 log_fail "Snapshot name is undefined." 395 [[ -z $clone ]] && \ 396 log_fail "Clone name is undefined." 397 398 log_must zfs clone $snap $clone 399} 400 401# 402# Create a bookmark of the given snapshot. Defaultly create a bookmark on 403# filesystem. 404# 405# $1 Existing filesystem or volume name. Default, $TESTFS 406# $2 Existing snapshot name. Default, $TESTSNAP 407# $3 bookmark name. Default, $TESTBKMARK 408# 409function create_bookmark 410{ 411 typeset fs_vol=${1:-$TESTFS} 412 typeset snap=${2:-$TESTSNAP} 413 typeset bkmark=${3:-$TESTBKMARK} 414 415 [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined." 416 [[ -z $snap ]] && log_fail "Snapshot's name is undefined." 417 [[ -z $bkmark ]] && log_fail "Bookmark's name is undefined." 418 419 if bkmarkexists $fs_vol#$bkmark; then 420 log_fail "$fs_vol#$bkmark already exists." 421 fi 422 datasetexists $fs_vol || \ 423 log_fail "$fs_vol must exist." 424 snapexists $fs_vol@$snap || \ 425 log_fail "$fs_vol@$snap must exist." 426 427 log_must zfs bookmark $fs_vol@$snap $fs_vol#$bkmark 428} 429 430# 431# Create a temporary clone result of an interrupted resumable 'zfs receive' 432# $1 Destination filesystem name. Must not exist, will be created as the result 433# of this function along with its %recv temporary clone 434# $2 Source filesystem name. Must not exist, will be created and destroyed 435# 436function create_recv_clone 437{ 438 typeset recvfs="$1" 439 typeset sendfs="${2:-$TESTPOOL/create_recv_clone}" 440 typeset snap="$sendfs@snap1" 441 typeset incr="$sendfs@snap2" 442 typeset mountpoint="$TESTDIR/create_recv_clone" 443 typeset sendfile="$TESTDIR/create_recv_clone.zsnap" 444 445 [[ -z $recvfs ]] && log_fail "Recv filesystem's name is undefined." 446 447 datasetexists $recvfs && log_fail "Recv filesystem must not exist." 448 datasetexists $sendfs && log_fail "Send filesystem must not exist." 449 450 log_must zfs create -o mountpoint="$mountpoint" $sendfs 451 log_must zfs snapshot $snap 452 log_must eval "zfs send $snap | zfs recv -u $recvfs" 453 log_must mkfile 1m "$mountpoint/data" 454 log_must zfs snapshot $incr 455 log_must eval "zfs send -i $snap $incr | dd bs=10K count=1 \ 456 iflag=fullblock > $sendfile" 457 log_mustnot eval "zfs recv -su $recvfs < $sendfile" 458 destroy_dataset "$sendfs" "-r" 459 log_must rm -f "$sendfile" 460 461 if [[ $(get_prop 'inconsistent' "$recvfs/%recv") -ne 1 ]]; then 462 log_fail "Error creating temporary $recvfs/%recv clone" 463 fi 464} 465 466function default_mirror_setup 467{ 468 default_mirror_setup_noexit $1 $2 $3 469 470 log_pass 471} 472 473# 474# Given a pair of disks, set up a storage pool and dataset for the mirror 475# @parameters: $1 the primary side of the mirror 476# $2 the secondary side of the mirror 477# @uses: ZPOOL ZFS TESTPOOL TESTFS 478function default_mirror_setup_noexit 479{ 480 readonly func="default_mirror_setup_noexit" 481 typeset primary=$1 482 typeset secondary=$2 483 484 [[ -z $primary ]] && \ 485 log_fail "$func: No parameters passed" 486 [[ -z $secondary ]] && \ 487 log_fail "$func: No secondary partition passed" 488 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 489 log_must zpool create -f $TESTPOOL mirror $@ 490 log_must zfs create $TESTPOOL/$TESTFS 491 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 492} 493 494# 495# create a number of mirrors. 496# We create a number($1) of 2 way mirrors using the pairs of disks named 497# on the command line. These mirrors are *not* mounted 498# @parameters: $1 the number of mirrors to create 499# $... the devices to use to create the mirrors on 500# @uses: ZPOOL ZFS TESTPOOL 501function setup_mirrors 502{ 503 typeset -i nmirrors=$1 504 505 shift 506 while ((nmirrors > 0)); do 507 log_must test -n "$1" -a -n "$2" 508 [[ -d /$TESTPOOL$nmirrors ]] && rm -rf /$TESTPOOL$nmirrors 509 log_must zpool create -f $TESTPOOL$nmirrors mirror $1 $2 510 shift 2 511 ((nmirrors = nmirrors - 1)) 512 done 513} 514 515# 516# create a number of raidz pools. 517# We create a number($1) of 2 raidz pools using the pairs of disks named 518# on the command line. These pools are *not* mounted 519# @parameters: $1 the number of pools to create 520# $... the devices to use to create the pools on 521# @uses: ZPOOL ZFS TESTPOOL 522function setup_raidzs 523{ 524 typeset -i nraidzs=$1 525 526 shift 527 while ((nraidzs > 0)); do 528 log_must test -n "$1" -a -n "$2" 529 [[ -d /$TESTPOOL$nraidzs ]] && rm -rf /$TESTPOOL$nraidzs 530 log_must zpool create -f $TESTPOOL$nraidzs raidz $1 $2 531 shift 2 532 ((nraidzs = nraidzs - 1)) 533 done 534} 535 536# 537# Destroy the configured testpool mirrors. 538# the mirrors are of the form ${TESTPOOL}{number} 539# @uses: ZPOOL ZFS TESTPOOL 540function destroy_mirrors 541{ 542 default_cleanup_noexit 543 544 log_pass 545} 546 547# 548# Given a minimum of two disks, set up a storage pool and dataset for the raid-z 549# $1 the list of disks 550# 551function default_raidz_setup 552{ 553 typeset disklist="$*" 554 disks=(${disklist[*]}) 555 556 if [[ ${#disks[*]} -lt 2 ]]; then 557 log_fail "A raid-z requires a minimum of two disks." 558 fi 559 560 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 561 log_must zpool create -f $TESTPOOL raidz $disklist 562 log_must zfs create $TESTPOOL/$TESTFS 563 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 564 565 log_pass 566} 567 568# 569# Common function used to cleanup storage pools and datasets. 570# 571# Invoked at the start of the test suite to ensure the system 572# is in a known state, and also at the end of each set of 573# sub-tests to ensure errors from one set of tests doesn't 574# impact the execution of the next set. 575 576function default_cleanup 577{ 578 default_cleanup_noexit 579 580 log_pass 581} 582 583# 584# Utility function used to list all available pool names. 585# 586# NOTE: $KEEP is a variable containing pool names, separated by a newline 587# character, that must be excluded from the returned list. 588# 589function get_all_pools 590{ 591 zpool list -H -o name | grep -Fvx "$KEEP" | grep -v "$NO_POOLS" 592} 593 594function default_cleanup_noexit 595{ 596 typeset pool="" 597 # 598 # Destroying the pool will also destroy any 599 # filesystems it contains. 600 # 601 if is_global_zone; then 602 zfs unmount -a > /dev/null 2>&1 603 ALL_POOLS=$(get_all_pools) 604 # Here, we loop through the pools we're allowed to 605 # destroy, only destroying them if it's safe to do 606 # so. 607 while [ ! -z ${ALL_POOLS} ] 608 do 609 for pool in ${ALL_POOLS} 610 do 611 if safe_to_destroy_pool $pool ; 612 then 613 destroy_pool $pool 614 fi 615 done 616 ALL_POOLS=$(get_all_pools) 617 done 618 619 zfs mount -a 620 else 621 typeset fs="" 622 for fs in $(zfs list -H -o name \ 623 | grep "^$ZONE_POOL/$ZONE_CTR[01234]/"); do 624 destroy_dataset "$fs" "-Rf" 625 done 626 627 # Need cleanup here to avoid garbage dir left. 628 for fs in $(zfs list -H -o name); do 629 [[ $fs == /$ZONE_POOL ]] && continue 630 [[ -d $fs ]] && log_must rm -rf $fs/* 631 done 632 633 # 634 # Reset the $ZONE_POOL/$ZONE_CTR[01234] file systems property to 635 # the default value 636 # 637 for fs in $(zfs list -H -o name); do 638 if [[ $fs == $ZONE_POOL/$ZONE_CTR[01234] ]]; then 639 log_must zfs set reservation=none $fs 640 log_must zfs set recordsize=128K $fs 641 log_must zfs set mountpoint=/$fs $fs 642 typeset enc="" 643 enc=$(get_prop encryption $fs) 644 if [[ $? -ne 0 ]] || [[ -z "$enc" ]] || \ 645 [[ "$enc" == "off" ]]; then 646 log_must zfs set checksum=on $fs 647 fi 648 log_must zfs set compression=off $fs 649 log_must zfs set atime=on $fs 650 log_must zfs set devices=off $fs 651 log_must zfs set exec=on $fs 652 log_must zfs set setuid=on $fs 653 log_must zfs set readonly=off $fs 654 log_must zfs set snapdir=hidden $fs 655 log_must zfs set aclmode=groupmask $fs 656 log_must zfs set aclinherit=secure $fs 657 fi 658 done 659 fi 660 661 [[ -d $TESTDIR ]] && \ 662 log_must rm -rf $TESTDIR 663 664 disk1=${DISKS%% *} 665 if is_mpath_device $disk1; then 666 delete_partitions 667 fi 668 669 rm -f $TEST_BASE_DIR/{err,out} 670} 671 672 673# 674# Common function used to cleanup storage pools, file systems 675# and containers. 676# 677function default_container_cleanup 678{ 679 if ! is_global_zone; then 680 reexport_pool 681 fi 682 683 ismounted $TESTPOOL/$TESTCTR/$TESTFS1 684 [[ $? -eq 0 ]] && \ 685 log_must zfs unmount $TESTPOOL/$TESTCTR/$TESTFS1 686 687 destroy_dataset "$TESTPOOL/$TESTCTR/$TESTFS1" "-R" 688 destroy_dataset "$TESTPOOL/$TESTCTR" "-Rf" 689 690 [[ -e $TESTDIR1 ]] && \ 691 log_must rm -rf $TESTDIR1 > /dev/null 2>&1 692 693 default_cleanup 694} 695 696# 697# Common function used to cleanup snapshot of file system or volume. Default to 698# delete the file system's snapshot 699# 700# $1 snapshot name 701# 702function destroy_snapshot 703{ 704 typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 705 706 if ! snapexists $snap; then 707 log_fail "'$snap' does not exist." 708 fi 709 710 # 711 # For the sake of the value which come from 'get_prop' is not equal 712 # to the really mountpoint when the snapshot is unmounted. So, firstly 713 # check and make sure this snapshot's been mounted in current system. 714 # 715 typeset mtpt="" 716 if ismounted $snap; then 717 mtpt=$(get_prop mountpoint $snap) 718 (($? != 0)) && \ 719 log_fail "get_prop mountpoint $snap failed." 720 fi 721 722 destroy_dataset "$snap" 723 [[ $mtpt != "" && -d $mtpt ]] && \ 724 log_must rm -rf $mtpt 725} 726 727# 728# Common function used to cleanup clone. 729# 730# $1 clone name 731# 732function destroy_clone 733{ 734 typeset clone=${1:-$TESTPOOL/$TESTCLONE} 735 736 if ! datasetexists $clone; then 737 log_fail "'$clone' does not existed." 738 fi 739 740 # With the same reason in destroy_snapshot 741 typeset mtpt="" 742 if ismounted $clone; then 743 mtpt=$(get_prop mountpoint $clone) 744 (($? != 0)) && \ 745 log_fail "get_prop mountpoint $clone failed." 746 fi 747 748 destroy_dataset "$clone" 749 [[ $mtpt != "" && -d $mtpt ]] && \ 750 log_must rm -rf $mtpt 751} 752 753# 754# Common function used to cleanup bookmark of file system or volume. Default 755# to delete the file system's bookmark. 756# 757# $1 bookmark name 758# 759function destroy_bookmark 760{ 761 typeset bkmark=${1:-$TESTPOOL/$TESTFS#$TESTBKMARK} 762 763 if ! bkmarkexists $bkmark; then 764 log_fail "'$bkmarkp' does not existed." 765 fi 766 767 destroy_dataset "$bkmark" 768} 769 770# Return 0 if a snapshot exists; $? otherwise 771# 772# $1 - snapshot name 773 774function snapexists 775{ 776 zfs list -H -t snapshot "$1" > /dev/null 2>&1 777 return $? 778} 779 780# 781# Return 0 if a bookmark exists; $? otherwise 782# 783# $1 - bookmark name 784# 785function bkmarkexists 786{ 787 zfs list -H -t bookmark "$1" > /dev/null 2>&1 788 return $? 789} 790 791# 792# Return 0 if a hold exists; $? otherwise 793# 794# $1 - hold tag 795# $2 - snapshot name 796# 797function holdexists 798{ 799 zfs holds "$2" | awk '{ print $2 }' | grep "$1" > /dev/null 2>&1 800 return $? 801} 802 803# 804# Set a property to a certain value on a dataset. 805# Sets a property of the dataset to the value as passed in. 806# @param: 807# $1 dataset who's property is being set 808# $2 property to set 809# $3 value to set property to 810# @return: 811# 0 if the property could be set. 812# non-zero otherwise. 813# @use: ZFS 814# 815function dataset_setprop 816{ 817 typeset fn=dataset_setprop 818 819 if (($# < 3)); then 820 log_note "$fn: Insufficient parameters (need 3, had $#)" 821 return 1 822 fi 823 typeset output= 824 output=$(zfs set $2=$3 $1 2>&1) 825 typeset rv=$? 826 if ((rv != 0)); then 827 log_note "Setting property on $1 failed." 828 log_note "property $2=$3" 829 log_note "Return Code: $rv" 830 log_note "Output: $output" 831 return $rv 832 fi 833 return 0 834} 835 836# 837# Assign suite defined dataset properties. 838# This function is used to apply the suite's defined default set of 839# properties to a dataset. 840# @parameters: $1 dataset to use 841# @uses: ZFS COMPRESSION_PROP CHECKSUM_PROP 842# @returns: 843# 0 if the dataset has been altered. 844# 1 if no pool name was passed in. 845# 2 if the dataset could not be found. 846# 3 if the dataset could not have it's properties set. 847# 848function dataset_set_defaultproperties 849{ 850 typeset dataset="$1" 851 852 [[ -z $dataset ]] && return 1 853 854 typeset confset= 855 typeset -i found=0 856 for confset in $(zfs list); do 857 if [[ $dataset = $confset ]]; then 858 found=1 859 break 860 fi 861 done 862 [[ $found -eq 0 ]] && return 2 863 if [[ -n $COMPRESSION_PROP ]]; then 864 dataset_setprop $dataset compression $COMPRESSION_PROP || \ 865 return 3 866 log_note "Compression set to '$COMPRESSION_PROP' on $dataset" 867 fi 868 if [[ -n $CHECKSUM_PROP ]]; then 869 dataset_setprop $dataset checksum $CHECKSUM_PROP || \ 870 return 3 871 log_note "Checksum set to '$CHECKSUM_PROP' on $dataset" 872 fi 873 return 0 874} 875 876# 877# Check a numeric assertion 878# @parameter: $@ the assertion to check 879# @output: big loud notice if assertion failed 880# @use: log_fail 881# 882function assert 883{ 884 (($@)) || log_fail "$@" 885} 886 887# 888# Function to format partition size of a disk 889# Given a disk cxtxdx reduces all partitions 890# to 0 size 891# 892function zero_partitions #<whole_disk_name> 893{ 894 typeset diskname=$1 895 typeset i 896 897 if is_freebsd; then 898 gpart destroy -F $diskname 899 elif is_linux; then 900 DSK=$DEV_DSKDIR/$diskname 901 DSK=$(echo $DSK | sed -e "s|//|/|g") 902 log_must parted $DSK -s -- mklabel gpt 903 blockdev --rereadpt $DSK 2>/dev/null 904 block_device_wait 905 else 906 for i in 0 1 3 4 5 6 7 907 do 908 log_must set_partition $i "" 0mb $diskname 909 done 910 fi 911 912 return 0 913} 914 915# 916# Given a slice, size and disk, this function 917# formats the slice to the specified size. 918# Size should be specified with units as per 919# the `format` command requirements eg. 100mb 3gb 920# 921# NOTE: This entire interface is problematic for the Linux parted utility 922# which requires the end of the partition to be specified. It would be 923# best to retire this interface and replace it with something more flexible. 924# At the moment a best effort is made. 925# 926# arguments: <slice_num> <slice_start> <size_plus_units> <whole_disk_name> 927function set_partition 928{ 929 typeset -i slicenum=$1 930 typeset start=$2 931 typeset size=$3 932 typeset disk=${4#$DEV_DSKDIR/} 933 disk=${disk#$DEV_RDSKDIR/} 934 935 case "$(uname)" in 936 Linux) 937 if [[ -z $size || -z $disk ]]; then 938 log_fail "The size or disk name is unspecified." 939 fi 940 disk=$DEV_DSKDIR/$disk 941 typeset size_mb=${size%%[mMgG]} 942 943 size_mb=${size_mb%%[mMgG][bB]} 944 if [[ ${size:1:1} == 'g' ]]; then 945 ((size_mb = size_mb * 1024)) 946 fi 947 948 # Create GPT partition table when setting slice 0 or 949 # when the device doesn't already contain a GPT label. 950 parted $disk -s -- print 1 >/dev/null 951 typeset ret_val=$? 952 if [[ $slicenum -eq 0 || $ret_val -ne 0 ]]; then 953 parted $disk -s -- mklabel gpt 954 if [[ $? -ne 0 ]]; then 955 log_note "Failed to create GPT partition table on $disk" 956 return 1 957 fi 958 fi 959 960 # When no start is given align on the first cylinder. 961 if [[ -z "$start" ]]; then 962 start=1 963 fi 964 965 # Determine the cylinder size for the device and using 966 # that calculate the end offset in cylinders. 967 typeset -i cly_size_kb=0 968 cly_size_kb=$(parted -m $disk -s -- \ 969 unit cyl print | head -3 | tail -1 | \ 970 awk -F '[:k.]' '{print $4}') 971 ((end = (size_mb * 1024 / cly_size_kb) + start)) 972 973 parted $disk -s -- \ 974 mkpart part$slicenum ${start}cyl ${end}cyl 975 typeset ret_val=$? 976 if [[ $ret_val -ne 0 ]]; then 977 log_note "Failed to create partition $slicenum on $disk" 978 return 1 979 fi 980 981 blockdev --rereadpt $disk 2>/dev/null 982 block_device_wait $disk 983 ;; 984 FreeBSD) 985 if [[ -z $size || -z $disk ]]; then 986 log_fail "The size or disk name is unspecified." 987 fi 988 disk=$DEV_DSKDIR/$disk 989 990 if [[ $slicenum -eq 0 ]] || ! gpart show $disk >/dev/null 2>&1; then 991 gpart destroy -F $disk >/dev/null 2>&1 992 gpart create -s GPT $disk 993 if [[ $? -ne 0 ]]; then 994 log_note "Failed to create GPT partition table on $disk" 995 return 1 996 fi 997 fi 998 999 typeset index=$((slicenum + 1)) 1000 1001 if [[ -n $start ]]; then 1002 start="-b $start" 1003 fi 1004 gpart add -t freebsd-zfs $start -s $size -i $index $disk 1005 if [[ $ret_val -ne 0 ]]; then 1006 log_note "Failed to create partition $slicenum on $disk" 1007 return 1 1008 fi 1009 1010 block_device_wait $disk 1011 ;; 1012 *) 1013 if [[ -z $slicenum || -z $size || -z $disk ]]; then 1014 log_fail "The slice, size or disk name is unspecified." 1015 fi 1016 1017 typeset format_file=/var/tmp/format_in.$$ 1018 1019 echo "partition" >$format_file 1020 echo "$slicenum" >> $format_file 1021 echo "" >> $format_file 1022 echo "" >> $format_file 1023 echo "$start" >> $format_file 1024 echo "$size" >> $format_file 1025 echo "label" >> $format_file 1026 echo "" >> $format_file 1027 echo "q" >> $format_file 1028 echo "q" >> $format_file 1029 1030 format -e -s -d $disk -f $format_file 1031 typeset ret_val=$? 1032 rm -f $format_file 1033 ;; 1034 esac 1035 1036 if [[ $ret_val -ne 0 ]]; then 1037 log_note "Unable to format $disk slice $slicenum to $size" 1038 return 1 1039 fi 1040 return 0 1041} 1042 1043# 1044# Delete all partitions on all disks - this is specifically for the use of multipath 1045# devices which currently can only be used in the test suite as raw/un-partitioned 1046# devices (ie a zpool cannot be created on a whole mpath device that has partitions) 1047# 1048function delete_partitions 1049{ 1050 typeset disk 1051 1052 if [[ -z $DISKSARRAY ]]; then 1053 DISKSARRAY=$DISKS 1054 fi 1055 1056 if is_linux; then 1057 typeset -i part 1058 for disk in $DISKSARRAY; do 1059 for (( part = 1; part < MAX_PARTITIONS; part++ )); do 1060 typeset partition=${disk}${SLICE_PREFIX}${part} 1061 parted $DEV_DSKDIR/$disk -s rm $part > /dev/null 2>&1 1062 if lsblk | grep -qF ${partition}; then 1063 log_fail "Partition ${partition} not deleted" 1064 else 1065 log_note "Partition ${partition} deleted" 1066 fi 1067 done 1068 done 1069 elif is_freebsd; then 1070 for disk in $DISKSARRAY; do 1071 if gpart destroy -F $disk; then 1072 log_note "Partitions for ${disk} deleted" 1073 else 1074 log_fail "Partitions for ${disk} not deleted" 1075 fi 1076 done 1077 fi 1078} 1079 1080# 1081# Get the end cyl of the given slice 1082# 1083function get_endslice #<disk> <slice> 1084{ 1085 typeset disk=$1 1086 typeset slice=$2 1087 if [[ -z $disk || -z $slice ]] ; then 1088 log_fail "The disk name or slice number is unspecified." 1089 fi 1090 1091 case "$(uname)" in 1092 Linux) 1093 endcyl=$(parted -s $DEV_DSKDIR/$disk -- unit cyl print | \ 1094 grep "part${slice}" | \ 1095 awk '{print $3}' | \ 1096 sed 's,cyl,,') 1097 ((endcyl = (endcyl + 1))) 1098 ;; 1099 FreeBSD) 1100 disk=${disk#/dev/zvol/} 1101 disk=${disk%p*} 1102 slice=$((slice + 1)) 1103 endcyl=$(gpart show $disk | \ 1104 awk -v slice=$slice '$3 == slice { print $1 + $2 }') 1105 ;; 1106 *) 1107 disk=${disk#/dev/dsk/} 1108 disk=${disk#/dev/rdsk/} 1109 disk=${disk%s*} 1110 1111 typeset -i ratio=0 1112 ratio=$(prtvtoc /dev/rdsk/${disk}s2 | \ 1113 grep "sectors\/cylinder" | \ 1114 awk '{print $2}') 1115 1116 if ((ratio == 0)); then 1117 return 1118 fi 1119 1120 typeset -i endcyl=$(prtvtoc -h /dev/rdsk/${disk}s2 | 1121 nawk -v token="$slice" '{if ($1==token) print $6}') 1122 1123 ((endcyl = (endcyl + 1) / ratio)) 1124 ;; 1125 esac 1126 1127 echo $endcyl 1128} 1129 1130 1131# 1132# Given a size,disk and total slice number, this function formats the 1133# disk slices from 0 to the total slice number with the same specified 1134# size. 1135# 1136function partition_disk #<slice_size> <whole_disk_name> <total_slices> 1137{ 1138 typeset -i i=0 1139 typeset slice_size=$1 1140 typeset disk_name=$2 1141 typeset total_slices=$3 1142 typeset cyl 1143 1144 zero_partitions $disk_name 1145 while ((i < $total_slices)); do 1146 if ! is_linux; then 1147 if ((i == 2)); then 1148 ((i = i + 1)) 1149 continue 1150 fi 1151 fi 1152 log_must set_partition $i "$cyl" $slice_size $disk_name 1153 cyl=$(get_endslice $disk_name $i) 1154 ((i = i+1)) 1155 done 1156} 1157 1158# 1159# This function continues to write to a filenum number of files into dirnum 1160# number of directories until either file_write returns an error or the 1161# maximum number of files per directory have been written. 1162# 1163# Usage: 1164# fill_fs [destdir] [dirnum] [filenum] [bytes] [num_writes] [data] 1165# 1166# Return value: 0 on success 1167# non 0 on error 1168# 1169# Where : 1170# destdir: is the directory where everything is to be created under 1171# dirnum: the maximum number of subdirectories to use, -1 no limit 1172# filenum: the maximum number of files per subdirectory 1173# bytes: number of bytes to write 1174# num_writes: number of types to write out bytes 1175# data: the data that will be written 1176# 1177# E.g. 1178# fill_fs /testdir 20 25 1024 256 0 1179# 1180# Note: bytes * num_writes equals the size of the testfile 1181# 1182function fill_fs # destdir dirnum filenum bytes num_writes data 1183{ 1184 typeset destdir=${1:-$TESTDIR} 1185 typeset -i dirnum=${2:-50} 1186 typeset -i filenum=${3:-50} 1187 typeset -i bytes=${4:-8192} 1188 typeset -i num_writes=${5:-10240} 1189 typeset data=${6:-0} 1190 1191 mkdir -p $destdir/{1..$dirnum} 1192 for f in $destdir/{1..$dirnum}/$TESTFILE{1..$filenum}; do 1193 file_write -o create -f $f -b $bytes -c $num_writes -d $data \ 1194 || return $? 1195 done 1196 return 0 1197} 1198 1199# 1200# Simple function to get the specified property. If unable to 1201# get the property then exits. 1202# 1203# Note property is in 'parsable' format (-p) 1204# 1205function get_prop # property dataset 1206{ 1207 typeset prop_val 1208 typeset prop=$1 1209 typeset dataset=$2 1210 1211 prop_val=$(zfs get -pH -o value $prop $dataset 2>/dev/null) 1212 if [[ $? -ne 0 ]]; then 1213 log_note "Unable to get $prop property for dataset " \ 1214 "$dataset" 1215 return 1 1216 fi 1217 1218 echo "$prop_val" 1219 return 0 1220} 1221 1222# 1223# Simple function to get the specified property of pool. If unable to 1224# get the property then exits. 1225# 1226# Note property is in 'parsable' format (-p) 1227# 1228function get_pool_prop # property pool 1229{ 1230 typeset prop_val 1231 typeset prop=$1 1232 typeset pool=$2 1233 1234 if poolexists $pool ; then 1235 prop_val=$(zpool get -pH $prop $pool 2>/dev/null | tail -1 | \ 1236 awk '{print $3}') 1237 if [[ $? -ne 0 ]]; then 1238 log_note "Unable to get $prop property for pool " \ 1239 "$pool" 1240 return 1 1241 fi 1242 else 1243 log_note "Pool $pool not exists." 1244 return 1 1245 fi 1246 1247 echo "$prop_val" 1248 return 0 1249} 1250 1251# Return 0 if a pool exists; $? otherwise 1252# 1253# $1 - pool name 1254 1255function poolexists 1256{ 1257 typeset pool=$1 1258 1259 if [[ -z $pool ]]; then 1260 log_note "No pool name given." 1261 return 1 1262 fi 1263 1264 zpool get name "$pool" > /dev/null 2>&1 1265 return $? 1266} 1267 1268# Return 0 if all the specified datasets exist; $? otherwise 1269# 1270# $1-n dataset name 1271function datasetexists 1272{ 1273 if (($# == 0)); then 1274 log_note "No dataset name given." 1275 return 1 1276 fi 1277 1278 while (($# > 0)); do 1279 zfs get name $1 > /dev/null 2>&1 || \ 1280 return $? 1281 shift 1282 done 1283 1284 return 0 1285} 1286 1287# return 0 if none of the specified datasets exists, otherwise return 1. 1288# 1289# $1-n dataset name 1290function datasetnonexists 1291{ 1292 if (($# == 0)); then 1293 log_note "No dataset name given." 1294 return 1 1295 fi 1296 1297 while (($# > 0)); do 1298 zfs list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 \ 1299 && return 1 1300 shift 1301 done 1302 1303 return 0 1304} 1305 1306function is_shared_freebsd 1307{ 1308 typeset fs=$1 1309 1310 pgrep -q mountd && showmount -E | grep -qx $fs 1311} 1312 1313function is_shared_illumos 1314{ 1315 typeset fs=$1 1316 typeset mtpt 1317 1318 for mtpt in `share | awk '{print $2}'` ; do 1319 if [[ $mtpt == $fs ]] ; then 1320 return 0 1321 fi 1322 done 1323 1324 typeset stat=$(svcs -H -o STA nfs/server:default) 1325 if [[ $stat != "ON" ]]; then 1326 log_note "Current nfs/server status: $stat" 1327 fi 1328 1329 return 1 1330} 1331 1332function is_shared_linux 1333{ 1334 typeset fs=$1 1335 typeset mtpt 1336 1337 for mtpt in `share | awk '{print $1}'` ; do 1338 if [[ $mtpt == $fs ]] ; then 1339 return 0 1340 fi 1341 done 1342 return 1 1343} 1344 1345# 1346# Given a mountpoint, or a dataset name, determine if it is shared via NFS. 1347# 1348# Returns 0 if shared, 1 otherwise. 1349# 1350function is_shared 1351{ 1352 typeset fs=$1 1353 typeset mtpt 1354 1355 if [[ $fs != "/"* ]] ; then 1356 if datasetnonexists "$fs" ; then 1357 return 1 1358 else 1359 mtpt=$(get_prop mountpoint "$fs") 1360 case $mtpt in 1361 none|legacy|-) return 1 1362 ;; 1363 *) fs=$mtpt 1364 ;; 1365 esac 1366 fi 1367 fi 1368 1369 case $(uname) in 1370 FreeBSD) is_shared_freebsd "$fs" ;; 1371 Linux) is_shared_linux "$fs" ;; 1372 *) is_shared_illumos "$fs" ;; 1373 esac 1374} 1375 1376function is_exported_illumos 1377{ 1378 typeset fs=$1 1379 typeset mtpt 1380 1381 for mtpt in `awk '{print $1}' /etc/dfs/sharetab` ; do 1382 if [[ $mtpt == $fs ]] ; then 1383 return 0 1384 fi 1385 done 1386 1387 return 1 1388} 1389 1390function is_exported_freebsd 1391{ 1392 typeset fs=$1 1393 typeset mtpt 1394 1395 for mtpt in `awk '{print $1}' /etc/zfs/exports` ; do 1396 if [[ $mtpt == $fs ]] ; then 1397 return 0 1398 fi 1399 done 1400 1401 return 1 1402} 1403 1404function is_exported_linux 1405{ 1406 typeset fs=$1 1407 typeset mtpt 1408 1409 for mtpt in `awk '{print $1}' /etc/exports.d/zfs.exports` ; do 1410 if [[ $mtpt == $fs ]] ; then 1411 return 0 1412 fi 1413 done 1414 1415 return 1 1416} 1417 1418# 1419# Given a mountpoint, or a dataset name, determine if it is exported via 1420# the os-specific NFS exports file. 1421# 1422# Returns 0 if exported, 1 otherwise. 1423# 1424function is_exported 1425{ 1426 typeset fs=$1 1427 typeset mtpt 1428 1429 if [[ $fs != "/"* ]] ; then 1430 if datasetnonexists "$fs" ; then 1431 return 1 1432 else 1433 mtpt=$(get_prop mountpoint "$fs") 1434 case $mtpt in 1435 none|legacy|-) return 1 1436 ;; 1437 *) fs=$mtpt 1438 ;; 1439 esac 1440 fi 1441 fi 1442 1443 case $(uname) in 1444 FreeBSD) is_exported_freebsd "$fs" ;; 1445 Linux) is_exported_linux "$fs" ;; 1446 *) is_exported_illumos "$fs" ;; 1447 esac 1448} 1449 1450# 1451# Given a dataset name determine if it is shared via SMB. 1452# 1453# Returns 0 if shared, 1 otherwise. 1454# 1455function is_shared_smb 1456{ 1457 typeset fs=$1 1458 typeset mtpt 1459 1460 if datasetnonexists "$fs" ; then 1461 return 1 1462 else 1463 fs=$(echo $fs | sed 's@/@_@g') 1464 fi 1465 1466 if is_linux; then 1467 for mtpt in `net usershare list | awk '{print $1}'` ; do 1468 if [[ $mtpt == $fs ]] ; then 1469 return 0 1470 fi 1471 done 1472 return 1 1473 else 1474 log_note "Currently unsupported by the test framework" 1475 return 1 1476 fi 1477} 1478 1479# 1480# Given a mountpoint, determine if it is not shared via NFS. 1481# 1482# Returns 0 if not shared, 1 otherwise. 1483# 1484function not_shared 1485{ 1486 typeset fs=$1 1487 1488 is_shared $fs 1489 if (($? == 0)); then 1490 return 1 1491 fi 1492 1493 return 0 1494} 1495 1496# 1497# Given a dataset determine if it is not shared via SMB. 1498# 1499# Returns 0 if not shared, 1 otherwise. 1500# 1501function not_shared_smb 1502{ 1503 typeset fs=$1 1504 1505 is_shared_smb $fs 1506 if (($? == 0)); then 1507 return 1 1508 fi 1509 1510 return 0 1511} 1512 1513# 1514# Helper function to unshare a mountpoint. 1515# 1516function unshare_fs #fs 1517{ 1518 typeset fs=$1 1519 1520 is_shared $fs || is_shared_smb $fs 1521 if (($? == 0)); then 1522 zfs unshare $fs || log_fail "zfs unshare $fs failed" 1523 fi 1524 1525 return 0 1526} 1527 1528# 1529# Helper function to share a NFS mountpoint. 1530# 1531function share_nfs #fs 1532{ 1533 typeset fs=$1 1534 1535 if is_linux; then 1536 is_shared $fs 1537 if (($? != 0)); then 1538 log_must share "*:$fs" 1539 fi 1540 else 1541 is_shared $fs 1542 if (($? != 0)); then 1543 log_must share -F nfs $fs 1544 fi 1545 fi 1546 1547 return 0 1548} 1549 1550# 1551# Helper function to unshare a NFS mountpoint. 1552# 1553function unshare_nfs #fs 1554{ 1555 typeset fs=$1 1556 1557 if is_linux; then 1558 is_shared $fs 1559 if (($? == 0)); then 1560 log_must unshare -u "*:$fs" 1561 fi 1562 else 1563 is_shared $fs 1564 if (($? == 0)); then 1565 log_must unshare -F nfs $fs 1566 fi 1567 fi 1568 1569 return 0 1570} 1571 1572# 1573# Helper function to show NFS shares. 1574# 1575function showshares_nfs 1576{ 1577 if is_linux; then 1578 share -v 1579 else 1580 share -F nfs 1581 fi 1582 1583 return 0 1584} 1585 1586# 1587# Helper function to show SMB shares. 1588# 1589function showshares_smb 1590{ 1591 if is_linux; then 1592 net usershare list 1593 else 1594 share -F smb 1595 fi 1596 1597 return 0 1598} 1599 1600function check_nfs 1601{ 1602 if is_linux; then 1603 share -s 1604 elif is_freebsd; then 1605 showmount -e 1606 else 1607 log_unsupported "Unknown platform" 1608 fi 1609 1610 if [[ $? -ne 0 ]]; then 1611 log_unsupported "The NFS utilities are not installed" 1612 fi 1613} 1614 1615# 1616# Check NFS server status and trigger it online. 1617# 1618function setup_nfs_server 1619{ 1620 # Cannot share directory in non-global zone. 1621 # 1622 if ! is_global_zone; then 1623 log_note "Cannot trigger NFS server by sharing in LZ." 1624 return 1625 fi 1626 1627 if is_linux; then 1628 # 1629 # Re-synchronize /var/lib/nfs/etab with /etc/exports and 1630 # /etc/exports.d./* to provide a clean test environment. 1631 # 1632 log_must share -r 1633 1634 log_note "NFS server must be started prior to running ZTS." 1635 return 1636 elif is_freebsd; then 1637 kill -s HUP $(cat /var/run/mountd.pid) 1638 1639 log_note "NFS server must be started prior to running ZTS." 1640 return 1641 fi 1642 1643 typeset nfs_fmri="svc:/network/nfs/server:default" 1644 if [[ $(svcs -Ho STA $nfs_fmri) != "ON" ]]; then 1645 # 1646 # Only really sharing operation can enable NFS server 1647 # to online permanently. 1648 # 1649 typeset dummy=/tmp/dummy 1650 1651 if [[ -d $dummy ]]; then 1652 log_must rm -rf $dummy 1653 fi 1654 1655 log_must mkdir $dummy 1656 log_must share $dummy 1657 1658 # 1659 # Waiting for fmri's status to be the final status. 1660 # Otherwise, in transition, an asterisk (*) is appended for 1661 # instances, unshare will reverse status to 'DIS' again. 1662 # 1663 # Waiting for 1's at least. 1664 # 1665 log_must sleep 1 1666 timeout=10 1667 while [[ timeout -ne 0 && $(svcs -Ho STA $nfs_fmri) == *'*' ]] 1668 do 1669 log_must sleep 1 1670 1671 ((timeout -= 1)) 1672 done 1673 1674 log_must unshare $dummy 1675 log_must rm -rf $dummy 1676 fi 1677 1678 log_note "Current NFS status: '$(svcs -Ho STA,FMRI $nfs_fmri)'" 1679} 1680 1681# 1682# To verify whether calling process is in global zone 1683# 1684# Return 0 if in global zone, 1 in non-global zone 1685# 1686function is_global_zone 1687{ 1688 if is_linux || is_freebsd; then 1689 return 0 1690 else 1691 typeset cur_zone=$(zonename 2>/dev/null) 1692 if [[ $cur_zone != "global" ]]; then 1693 return 1 1694 fi 1695 return 0 1696 fi 1697} 1698 1699# 1700# Verify whether test is permitted to run from 1701# global zone, local zone, or both 1702# 1703# $1 zone limit, could be "global", "local", or "both"(no limit) 1704# 1705# Return 0 if permitted, otherwise exit with log_unsupported 1706# 1707function verify_runnable # zone limit 1708{ 1709 typeset limit=$1 1710 1711 [[ -z $limit ]] && return 0 1712 1713 if is_global_zone ; then 1714 case $limit in 1715 global|both) 1716 ;; 1717 local) log_unsupported "Test is unable to run from "\ 1718 "global zone." 1719 ;; 1720 *) log_note "Warning: unknown limit $limit - " \ 1721 "use both." 1722 ;; 1723 esac 1724 else 1725 case $limit in 1726 local|both) 1727 ;; 1728 global) log_unsupported "Test is unable to run from "\ 1729 "local zone." 1730 ;; 1731 *) log_note "Warning: unknown limit $limit - " \ 1732 "use both." 1733 ;; 1734 esac 1735 1736 reexport_pool 1737 fi 1738 1739 return 0 1740} 1741 1742# Return 0 if create successfully or the pool exists; $? otherwise 1743# Note: In local zones, this function should return 0 silently. 1744# 1745# $1 - pool name 1746# $2-n - [keyword] devs_list 1747 1748function create_pool #pool devs_list 1749{ 1750 typeset pool=${1%%/*} 1751 1752 shift 1753 1754 if [[ -z $pool ]]; then 1755 log_note "Missing pool name." 1756 return 1 1757 fi 1758 1759 if poolexists $pool ; then 1760 destroy_pool $pool 1761 fi 1762 1763 if is_global_zone ; then 1764 [[ -d /$pool ]] && rm -rf /$pool 1765 log_must zpool create -f $pool $@ 1766 fi 1767 1768 return 0 1769} 1770 1771# Return 0 if destroy successfully or the pool exists; $? otherwise 1772# Note: In local zones, this function should return 0 silently. 1773# 1774# $1 - pool name 1775# Destroy pool with the given parameters. 1776 1777function destroy_pool #pool 1778{ 1779 typeset pool=${1%%/*} 1780 typeset mtpt 1781 1782 if [[ -z $pool ]]; then 1783 log_note "No pool name given." 1784 return 1 1785 fi 1786 1787 if is_global_zone ; then 1788 if poolexists "$pool" ; then 1789 mtpt=$(get_prop mountpoint "$pool") 1790 1791 # At times, syseventd/udev activity can cause attempts 1792 # to destroy a pool to fail with EBUSY. We retry a few 1793 # times allowing failures before requiring the destroy 1794 # to succeed. 1795 log_must_busy zpool destroy -f $pool 1796 1797 [[ -d $mtpt ]] && \ 1798 log_must rm -rf $mtpt 1799 else 1800 log_note "Pool does not exist. ($pool)" 1801 return 1 1802 fi 1803 fi 1804 1805 return 0 1806} 1807 1808# Return 0 if created successfully; $? otherwise 1809# 1810# $1 - dataset name 1811# $2-n - dataset options 1812 1813function create_dataset #dataset dataset_options 1814{ 1815 typeset dataset=$1 1816 1817 shift 1818 1819 if [[ -z $dataset ]]; then 1820 log_note "Missing dataset name." 1821 return 1 1822 fi 1823 1824 if datasetexists $dataset ; then 1825 destroy_dataset $dataset 1826 fi 1827 1828 log_must zfs create $@ $dataset 1829 1830 return 0 1831} 1832 1833# Return 0 if destroy successfully or the dataset exists; $? otherwise 1834# Note: In local zones, this function should return 0 silently. 1835# 1836# $1 - dataset name 1837# $2 - custom arguments for zfs destroy 1838# Destroy dataset with the given parameters. 1839 1840function destroy_dataset #dataset #args 1841{ 1842 typeset dataset=$1 1843 typeset mtpt 1844 typeset args=${2:-""} 1845 1846 if [[ -z $dataset ]]; then 1847 log_note "No dataset name given." 1848 return 1 1849 fi 1850 1851 if is_global_zone ; then 1852 if datasetexists "$dataset" ; then 1853 mtpt=$(get_prop mountpoint "$dataset") 1854 log_must_busy zfs destroy $args $dataset 1855 1856 [[ -d $mtpt ]] && \ 1857 log_must rm -rf $mtpt 1858 else 1859 log_note "Dataset does not exist. ($dataset)" 1860 return 1 1861 fi 1862 fi 1863 1864 return 0 1865} 1866 1867# 1868# Firstly, create a pool with 5 datasets. Then, create a single zone and 1869# export the 5 datasets to it. In addition, we also add a ZFS filesystem 1870# and a zvol device to the zone. 1871# 1872# $1 zone name 1873# $2 zone root directory prefix 1874# $3 zone ip 1875# 1876function zfs_zones_setup #zone_name zone_root zone_ip 1877{ 1878 typeset zone_name=${1:-$(hostname)-z} 1879 typeset zone_root=${2:-"/zone_root"} 1880 typeset zone_ip=${3:-"10.1.1.10"} 1881 typeset prefix_ctr=$ZONE_CTR 1882 typeset pool_name=$ZONE_POOL 1883 typeset -i cntctr=5 1884 typeset -i i=0 1885 1886 # Create pool and 5 container within it 1887 # 1888 [[ -d /$pool_name ]] && rm -rf /$pool_name 1889 log_must zpool create -f $pool_name $DISKS 1890 while ((i < cntctr)); do 1891 log_must zfs create $pool_name/$prefix_ctr$i 1892 ((i += 1)) 1893 done 1894 1895 # create a zvol 1896 log_must zfs create -V 1g $pool_name/zone_zvol 1897 block_device_wait 1898 1899 # 1900 # Add slog device for pool 1901 # 1902 typeset sdevs="$TEST_BASE_DIR/sdev1 $TEST_BASE_DIR/sdev2" 1903 log_must mkfile $MINVDEVSIZE $sdevs 1904 log_must zpool add $pool_name log mirror $sdevs 1905 1906 # this isn't supported just yet. 1907 # Create a filesystem. In order to add this to 1908 # the zone, it must have it's mountpoint set to 'legacy' 1909 # log_must zfs create $pool_name/zfs_filesystem 1910 # log_must zfs set mountpoint=legacy $pool_name/zfs_filesystem 1911 1912 [[ -d $zone_root ]] && \ 1913 log_must rm -rf $zone_root/$zone_name 1914 [[ ! -d $zone_root ]] && \ 1915 log_must mkdir -p -m 0700 $zone_root/$zone_name 1916 1917 # Create zone configure file and configure the zone 1918 # 1919 typeset zone_conf=/tmp/zone_conf.$$ 1920 echo "create" > $zone_conf 1921 echo "set zonepath=$zone_root/$zone_name" >> $zone_conf 1922 echo "set autoboot=true" >> $zone_conf 1923 i=0 1924 while ((i < cntctr)); do 1925 echo "add dataset" >> $zone_conf 1926 echo "set name=$pool_name/$prefix_ctr$i" >> \ 1927 $zone_conf 1928 echo "end" >> $zone_conf 1929 ((i += 1)) 1930 done 1931 1932 # add our zvol to the zone 1933 echo "add device" >> $zone_conf 1934 echo "set match=/dev/zvol/dsk/$pool_name/zone_zvol" >> $zone_conf 1935 echo "end" >> $zone_conf 1936 1937 # add a corresponding zvol rdsk to the zone 1938 echo "add device" >> $zone_conf 1939 echo "set match=$ZVOL_RDEVDIR/$pool_name/zone_zvol" >> $zone_conf 1940 echo "end" >> $zone_conf 1941 1942 # once it's supported, we'll add our filesystem to the zone 1943 # echo "add fs" >> $zone_conf 1944 # echo "set type=zfs" >> $zone_conf 1945 # echo "set special=$pool_name/zfs_filesystem" >> $zone_conf 1946 # echo "set dir=/export/zfs_filesystem" >> $zone_conf 1947 # echo "end" >> $zone_conf 1948 1949 echo "verify" >> $zone_conf 1950 echo "commit" >> $zone_conf 1951 log_must zonecfg -z $zone_name -f $zone_conf 1952 log_must rm -f $zone_conf 1953 1954 # Install the zone 1955 zoneadm -z $zone_name install 1956 if (($? == 0)); then 1957 log_note "SUCCESS: zoneadm -z $zone_name install" 1958 else 1959 log_fail "FAIL: zoneadm -z $zone_name install" 1960 fi 1961 1962 # Install sysidcfg file 1963 # 1964 typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg 1965 echo "system_locale=C" > $sysidcfg 1966 echo "terminal=dtterm" >> $sysidcfg 1967 echo "network_interface=primary {" >> $sysidcfg 1968 echo "hostname=$zone_name" >> $sysidcfg 1969 echo "}" >> $sysidcfg 1970 echo "name_service=NONE" >> $sysidcfg 1971 echo "root_password=mo791xfZ/SFiw" >> $sysidcfg 1972 echo "security_policy=NONE" >> $sysidcfg 1973 echo "timezone=US/Eastern" >> $sysidcfg 1974 1975 # Boot this zone 1976 log_must zoneadm -z $zone_name boot 1977} 1978 1979# 1980# Reexport TESTPOOL & TESTPOOL(1-4) 1981# 1982function reexport_pool 1983{ 1984 typeset -i cntctr=5 1985 typeset -i i=0 1986 1987 while ((i < cntctr)); do 1988 if ((i == 0)); then 1989 TESTPOOL=$ZONE_POOL/$ZONE_CTR$i 1990 if ! ismounted $TESTPOOL; then 1991 log_must zfs mount $TESTPOOL 1992 fi 1993 else 1994 eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i 1995 if eval ! ismounted \$TESTPOOL$i; then 1996 log_must eval zfs mount \$TESTPOOL$i 1997 fi 1998 fi 1999 ((i += 1)) 2000 done 2001} 2002 2003# 2004# Verify a given disk or pool state 2005# 2006# Return 0 is pool/disk matches expected state, 1 otherwise 2007# 2008function check_state # pool disk state{online,offline,degraded} 2009{ 2010 typeset pool=$1 2011 typeset disk=${2#$DEV_DSKDIR/} 2012 typeset state=$3 2013 2014 [[ -z $pool ]] || [[ -z $state ]] \ 2015 && log_fail "Arguments invalid or missing" 2016 2017 if [[ -z $disk ]]; then 2018 #check pool state only 2019 zpool get -H -o value health $pool \ 2020 | grep -i "$state" > /dev/null 2>&1 2021 else 2022 zpool status -v $pool | grep "$disk" \ 2023 | grep -i "$state" > /dev/null 2>&1 2024 fi 2025 2026 return $? 2027} 2028 2029# 2030# Get the mountpoint of snapshot 2031# For the snapshot use <mp_filesystem>/.zfs/snapshot/<snap> 2032# as its mountpoint 2033# 2034function snapshot_mountpoint 2035{ 2036 typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 2037 2038 if [[ $dataset != *@* ]]; then 2039 log_fail "Error name of snapshot '$dataset'." 2040 fi 2041 2042 typeset fs=${dataset%@*} 2043 typeset snap=${dataset#*@} 2044 2045 if [[ -z $fs || -z $snap ]]; then 2046 log_fail "Error name of snapshot '$dataset'." 2047 fi 2048 2049 echo $(get_prop mountpoint $fs)/.zfs/snapshot/$snap 2050} 2051 2052# 2053# Given a device and 'ashift' value verify it's correctly set on every label 2054# 2055function verify_ashift # device ashift 2056{ 2057 typeset device="$1" 2058 typeset ashift="$2" 2059 2060 zdb -e -lll $device | awk -v ashift=$ashift '/ashift: / { 2061 if (ashift != $2) 2062 exit 1; 2063 else 2064 count++; 2065 } END { 2066 if (count != 4) 2067 exit 1; 2068 else 2069 exit 0; 2070 }' 2071 2072 return $? 2073} 2074 2075# 2076# Given a pool and file system, this function will verify the file system 2077# using the zdb internal tool. Note that the pool is exported and imported 2078# to ensure it has consistent state. 2079# 2080function verify_filesys # pool filesystem dir 2081{ 2082 typeset pool="$1" 2083 typeset filesys="$2" 2084 typeset zdbout="/tmp/zdbout.$$" 2085 2086 shift 2087 shift 2088 typeset dirs=$@ 2089 typeset search_path="" 2090 2091 log_note "Calling zdb to verify filesystem '$filesys'" 2092 zfs unmount -a > /dev/null 2>&1 2093 log_must zpool export $pool 2094 2095 if [[ -n $dirs ]] ; then 2096 for dir in $dirs ; do 2097 search_path="$search_path -d $dir" 2098 done 2099 fi 2100 2101 log_must zpool import $search_path $pool 2102 2103 zdb -cudi $filesys > $zdbout 2>&1 2104 if [[ $? != 0 ]]; then 2105 log_note "Output: zdb -cudi $filesys" 2106 cat $zdbout 2107 log_fail "zdb detected errors with: '$filesys'" 2108 fi 2109 2110 log_must zfs mount -a 2111 log_must rm -rf $zdbout 2112} 2113 2114# 2115# Given a pool issue a scrub and verify that no checksum errors are reported. 2116# 2117function verify_pool 2118{ 2119 typeset pool=${1:-$TESTPOOL} 2120 2121 log_must zpool scrub $pool 2122 log_must wait_scrubbed $pool 2123 2124 typeset -i cksum=$(zpool status $pool | awk ' 2125 !NF { isvdev = 0 } 2126 isvdev { errors += $NF } 2127 /CKSUM$/ { isvdev = 1 } 2128 END { print errors } 2129 ') 2130 if [[ $cksum != 0 ]]; then 2131 log_must zpool status -v 2132 log_fail "Unexpected CKSUM errors found on $pool ($cksum)" 2133 fi 2134} 2135 2136# 2137# Given a pool, and this function list all disks in the pool 2138# 2139function get_disklist # pool 2140{ 2141 typeset disklist="" 2142 2143 disklist=$(zpool iostat -v $1 | nawk '(NR >4) {print $1}' | \ 2144 grep -v "\-\-\-\-\-" | \ 2145 egrep -v -e "^(mirror|raidz[1-3]|spare|log|cache|special|dedup)$") 2146 2147 echo $disklist 2148} 2149 2150# 2151# Given a pool, and this function list all disks in the pool with their full 2152# path (like "/dev/sda" instead of "sda"). 2153# 2154function get_disklist_fullpath # pool 2155{ 2156 args="-P $1" 2157 get_disklist $args 2158} 2159 2160 2161 2162# /** 2163# This function kills a given list of processes after a time period. We use 2164# this in the stress tests instead of STF_TIMEOUT so that we can have processes 2165# run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT 2166# would be listed as FAIL, which we don't want : we're happy with stress tests 2167# running for a certain amount of time, then finishing. 2168# 2169# @param $1 the time in seconds after which we should terminate these processes 2170# @param $2..$n the processes we wish to terminate. 2171# */ 2172function stress_timeout 2173{ 2174 typeset -i TIMEOUT=$1 2175 shift 2176 typeset cpids="$@" 2177 2178 log_note "Waiting for child processes($cpids). " \ 2179 "It could last dozens of minutes, please be patient ..." 2180 log_must sleep $TIMEOUT 2181 2182 log_note "Killing child processes after ${TIMEOUT} stress timeout." 2183 typeset pid 2184 for pid in $cpids; do 2185 ps -p $pid > /dev/null 2>&1 2186 if (($? == 0)); then 2187 log_must kill -USR1 $pid 2188 fi 2189 done 2190} 2191 2192# 2193# Verify a given hotspare disk is inuse or avail 2194# 2195# Return 0 is pool/disk matches expected state, 1 otherwise 2196# 2197function check_hotspare_state # pool disk state{inuse,avail} 2198{ 2199 typeset pool=$1 2200 typeset disk=${2#$DEV_DSKDIR/} 2201 typeset state=$3 2202 2203 cur_state=$(get_device_state $pool $disk "spares") 2204 2205 if [[ $state != ${cur_state} ]]; then 2206 return 1 2207 fi 2208 return 0 2209} 2210 2211# 2212# Wait until a hotspare transitions to a given state or times out. 2213# 2214# Return 0 when pool/disk matches expected state, 1 on timeout. 2215# 2216function wait_hotspare_state # pool disk state timeout 2217{ 2218 typeset pool=$1 2219 typeset disk=${2#*$DEV_DSKDIR/} 2220 typeset state=$3 2221 typeset timeout=${4:-60} 2222 typeset -i i=0 2223 2224 while [[ $i -lt $timeout ]]; do 2225 if check_hotspare_state $pool $disk $state; then 2226 return 0 2227 fi 2228 2229 i=$((i+1)) 2230 sleep 1 2231 done 2232 2233 return 1 2234} 2235 2236# 2237# Verify a given slog disk is inuse or avail 2238# 2239# Return 0 is pool/disk matches expected state, 1 otherwise 2240# 2241function check_slog_state # pool disk state{online,offline,unavail} 2242{ 2243 typeset pool=$1 2244 typeset disk=${2#$DEV_DSKDIR/} 2245 typeset state=$3 2246 2247 cur_state=$(get_device_state $pool $disk "logs") 2248 2249 if [[ $state != ${cur_state} ]]; then 2250 return 1 2251 fi 2252 return 0 2253} 2254 2255# 2256# Verify a given vdev disk is inuse or avail 2257# 2258# Return 0 is pool/disk matches expected state, 1 otherwise 2259# 2260function check_vdev_state # pool disk state{online,offline,unavail} 2261{ 2262 typeset pool=$1 2263 typeset disk=${2#*$DEV_DSKDIR/} 2264 typeset state=$3 2265 2266 cur_state=$(get_device_state $pool $disk) 2267 2268 if [[ $state != ${cur_state} ]]; then 2269 return 1 2270 fi 2271 return 0 2272} 2273 2274# 2275# Wait until a vdev transitions to a given state or times out. 2276# 2277# Return 0 when pool/disk matches expected state, 1 on timeout. 2278# 2279function wait_vdev_state # pool disk state timeout 2280{ 2281 typeset pool=$1 2282 typeset disk=${2#*$DEV_DSKDIR/} 2283 typeset state=$3 2284 typeset timeout=${4:-60} 2285 typeset -i i=0 2286 2287 while [[ $i -lt $timeout ]]; do 2288 if check_vdev_state $pool $disk $state; then 2289 return 0 2290 fi 2291 2292 i=$((i+1)) 2293 sleep 1 2294 done 2295 2296 return 1 2297} 2298 2299# 2300# Check the output of 'zpool status -v <pool>', 2301# and to see if the content of <token> contain the <keyword> specified. 2302# 2303# Return 0 is contain, 1 otherwise 2304# 2305function check_pool_status # pool token keyword <verbose> 2306{ 2307 typeset pool=$1 2308 typeset token=$2 2309 typeset keyword=$3 2310 typeset verbose=${4:-false} 2311 2312 scan=$(zpool status -v "$pool" 2>/dev/null | nawk -v token="$token:" ' 2313 ($1==token) {print $0}') 2314 if [[ $verbose == true ]]; then 2315 log_note $scan 2316 fi 2317 echo $scan | egrep -i "$keyword" > /dev/null 2>&1 2318 2319 return $? 2320} 2321 2322# 2323# The following functions are instance of check_pool_status() 2324# is_pool_resilvering - to check if the pool resilver is in progress 2325# is_pool_resilvered - to check if the pool resilver is completed 2326# is_pool_scrubbing - to check if the pool scrub is in progress 2327# is_pool_scrubbed - to check if the pool scrub is completed 2328# is_pool_scrub_stopped - to check if the pool scrub is stopped 2329# is_pool_scrub_paused - to check if the pool scrub has paused 2330# is_pool_removing - to check if the pool removing is a vdev 2331# is_pool_removed - to check if the pool remove is completed 2332# is_pool_discarding - to check if the pool checkpoint is being discarded 2333# 2334function is_pool_resilvering #pool <verbose> 2335{ 2336 check_pool_status "$1" "scan" \ 2337 "resilver[ ()0-9A-Za-z:_-]* in progress since" $2 2338 return $? 2339} 2340 2341function is_pool_resilvered #pool <verbose> 2342{ 2343 check_pool_status "$1" "scan" "resilvered " $2 2344 return $? 2345} 2346 2347function is_pool_scrubbing #pool <verbose> 2348{ 2349 check_pool_status "$1" "scan" "scrub in progress since " $2 2350 return $? 2351} 2352 2353function is_pool_scrubbed #pool <verbose> 2354{ 2355 check_pool_status "$1" "scan" "scrub repaired" $2 2356 return $? 2357} 2358 2359function is_pool_scrub_stopped #pool <verbose> 2360{ 2361 check_pool_status "$1" "scan" "scrub canceled" $2 2362 return $? 2363} 2364 2365function is_pool_scrub_paused #pool <verbose> 2366{ 2367 check_pool_status "$1" "scan" "scrub paused since " $2 2368 return $? 2369} 2370 2371function is_pool_removing #pool 2372{ 2373 check_pool_status "$1" "remove" "in progress since " 2374 return $? 2375} 2376 2377function is_pool_removed #pool 2378{ 2379 check_pool_status "$1" "remove" "completed on" 2380 return $? 2381} 2382 2383function is_pool_discarding #pool 2384{ 2385 check_pool_status "$1" "checkpoint" "discarding" 2386 return $? 2387} 2388 2389function wait_for_degraded 2390{ 2391 typeset pool=$1 2392 typeset timeout=${2:-30} 2393 typeset t0=$SECONDS 2394 2395 while :; do 2396 [[ $(get_pool_prop health $pool) == "DEGRADED" ]] && break 2397 log_note "$pool is not yet degraded." 2398 sleep 1 2399 if ((SECONDS - t0 > $timeout)); then 2400 log_note "$pool not degraded after $timeout seconds." 2401 return 1 2402 fi 2403 done 2404 2405 return 0 2406} 2407 2408# 2409# Use create_pool()/destroy_pool() to clean up the information in 2410# in the given disk to avoid slice overlapping. 2411# 2412function cleanup_devices #vdevs 2413{ 2414 typeset pool="foopool$$" 2415 2416 for vdev in $@; do 2417 zero_partitions $vdev 2418 done 2419 2420 poolexists $pool && destroy_pool $pool 2421 create_pool $pool $@ 2422 destroy_pool $pool 2423 2424 return 0 2425} 2426 2427#/** 2428# A function to find and locate free disks on a system or from given 2429# disks as the parameter. It works by locating disks that are in use 2430# as swap devices and dump devices, and also disks listed in /etc/vfstab 2431# 2432# $@ given disks to find which are free, default is all disks in 2433# the test system 2434# 2435# @return a string containing the list of available disks 2436#*/ 2437function find_disks 2438{ 2439 # Trust provided list, no attempt is made to locate unused devices. 2440 if is_linux || is_freebsd; then 2441 echo "$@" 2442 return 2443 fi 2444 2445 2446 sfi=/tmp/swaplist.$$ 2447 dmpi=/tmp/dumpdev.$$ 2448 max_finddisksnum=${MAX_FINDDISKSNUM:-6} 2449 2450 swap -l > $sfi 2451 dumpadm > $dmpi 2>/dev/null 2452 2453# write an awk script that can process the output of format 2454# to produce a list of disks we know about. Note that we have 2455# to escape "$2" so that the shell doesn't interpret it while 2456# we're creating the awk script. 2457# ------------------- 2458 cat > /tmp/find_disks.awk <<EOF 2459#!/bin/nawk -f 2460 BEGIN { FS="."; } 2461 2462 /^Specify disk/{ 2463 searchdisks=0; 2464 } 2465 2466 { 2467 if (searchdisks && \$2 !~ "^$"){ 2468 split(\$2,arr," "); 2469 print arr[1]; 2470 } 2471 } 2472 2473 /^AVAILABLE DISK SELECTIONS:/{ 2474 searchdisks=1; 2475 } 2476EOF 2477#--------------------- 2478 2479 chmod 755 /tmp/find_disks.awk 2480 disks=${@:-$(echo "" | format -e 2>/dev/null | /tmp/find_disks.awk)} 2481 rm /tmp/find_disks.awk 2482 2483 unused="" 2484 for disk in $disks; do 2485 # Check for mounted 2486 grep "${disk}[sp]" /etc/mnttab >/dev/null 2487 (($? == 0)) && continue 2488 # Check for swap 2489 grep "${disk}[sp]" $sfi >/dev/null 2490 (($? == 0)) && continue 2491 # check for dump device 2492 grep "${disk}[sp]" $dmpi >/dev/null 2493 (($? == 0)) && continue 2494 # check to see if this disk hasn't been explicitly excluded 2495 # by a user-set environment variable 2496 echo "${ZFS_HOST_DEVICES_IGNORE}" | grep "${disk}" > /dev/null 2497 (($? == 0)) && continue 2498 unused_candidates="$unused_candidates $disk" 2499 done 2500 rm $sfi 2501 rm $dmpi 2502 2503# now just check to see if those disks do actually exist 2504# by looking for a device pointing to the first slice in 2505# each case. limit the number to max_finddisksnum 2506 count=0 2507 for disk in $unused_candidates; do 2508 if is_disk_device $DEV_DSKDIR/${disk}s0 && \ 2509 [ $count -lt $max_finddisksnum ]; then 2510 unused="$unused $disk" 2511 # do not impose limit if $@ is provided 2512 [[ -z $@ ]] && ((count = count + 1)) 2513 fi 2514 done 2515 2516# finally, return our disk list 2517 echo $unused 2518} 2519 2520function add_user_freebsd #<group_name> <user_name> <basedir> 2521{ 2522 typeset group=$1 2523 typeset user=$2 2524 typeset basedir=$3 2525 2526 # Check to see if the user exists. 2527 if id $user > /dev/null 2>&1; then 2528 return 0 2529 fi 2530 2531 # Assign 1000 as the base uid 2532 typeset -i uid=1000 2533 while true; do 2534 typeset -i ret 2535 pw useradd -u $uid -g $group -d $basedir/$user -m -n $user 2536 ret=$? 2537 case $ret in 2538 0) break ;; 2539 # The uid is not unique 2540 65) ((uid += 1)) ;; 2541 *) return 1 ;; 2542 esac 2543 if [[ $uid == 65000 ]]; then 2544 log_fail "No user id available under 65000 for $user" 2545 fi 2546 done 2547 2548 # Silence MOTD 2549 touch $basedir/$user/.hushlogin 2550 2551 return 0 2552} 2553 2554# 2555# Delete the specified user. 2556# 2557# $1 login name 2558# 2559function del_user_freebsd #<logname> 2560{ 2561 typeset user=$1 2562 2563 if id $user > /dev/null 2>&1; then 2564 log_must pw userdel $user 2565 fi 2566 2567 return 0 2568} 2569 2570# 2571# Select valid gid and create specified group. 2572# 2573# $1 group name 2574# 2575function add_group_freebsd #<group_name> 2576{ 2577 typeset group=$1 2578 2579 # See if the group already exists. 2580 if pw groupshow $group >/dev/null 2>&1; then 2581 return 0 2582 fi 2583 2584 # Assign 1000 as the base gid 2585 typeset -i gid=1000 2586 while true; do 2587 pw groupadd -g $gid -n $group > /dev/null 2>&1 2588 typeset -i ret=$? 2589 case $ret in 2590 0) return 0 ;; 2591 # The gid is not unique 2592 65) ((gid += 1)) ;; 2593 *) return 1 ;; 2594 esac 2595 if [[ $gid == 65000 ]]; then 2596 log_fail "No user id available under 65000 for $group" 2597 fi 2598 done 2599} 2600 2601# 2602# Delete the specified group. 2603# 2604# $1 group name 2605# 2606function del_group_freebsd #<group_name> 2607{ 2608 typeset group=$1 2609 2610 pw groupdel -n $group > /dev/null 2>&1 2611 typeset -i ret=$? 2612 case $ret in 2613 # Group does not exist, or was deleted successfully. 2614 0|6|65) return 0 ;; 2615 # Name already exists as a group name 2616 9) log_must pw groupdel $group ;; 2617 *) return 1 ;; 2618 esac 2619 2620 return 0 2621} 2622 2623function add_user_illumos #<group_name> <user_name> <basedir> 2624{ 2625 typeset group=$1 2626 typeset user=$2 2627 typeset basedir=$3 2628 2629 log_must useradd -g $group -d $basedir/$user -m $user 2630 2631 return 0 2632} 2633 2634function del_user_illumos #<user_name> 2635{ 2636 typeset user=$1 2637 2638 if id $user > /dev/null 2>&1; then 2639 log_must_retry "currently used" 6 userdel $user 2640 fi 2641 2642 return 0 2643} 2644 2645function add_group_illumos #<group_name> 2646{ 2647 typeset group=$1 2648 2649 typeset -i gid=100 2650 while true; do 2651 groupadd -g $gid $group > /dev/null 2>&1 2652 typeset -i ret=$? 2653 case $ret in 2654 0) return 0 ;; 2655 # The gid is not unique 2656 4) ((gid += 1)) ;; 2657 *) return 1 ;; 2658 esac 2659 done 2660} 2661 2662function del_group_illumos #<group_name> 2663{ 2664 typeset group=$1 2665 2666 groupmod -n $grp $grp > /dev/null 2>&1 2667 typeset -i ret=$? 2668 case $ret in 2669 # Group does not exist. 2670 6) return 0 ;; 2671 # Name already exists as a group name 2672 9) log_must groupdel $grp ;; 2673 *) return 1 ;; 2674 esac 2675} 2676 2677function add_user_linux #<group_name> <user_name> <basedir> 2678{ 2679 typeset group=$1 2680 typeset user=$2 2681 typeset basedir=$3 2682 2683 log_must useradd -g $group -d $basedir/$user -m $user 2684 2685 # Add new users to the same group and the command line utils. 2686 # This allows them to be run out of the original users home 2687 # directory as long as it permissioned to be group readable. 2688 cmd_group=$(stat --format="%G" $(which zfs)) 2689 log_must usermod -a -G $cmd_group $user 2690 2691 return 0 2692} 2693 2694function del_user_linux #<user_name> 2695{ 2696 typeset user=$1 2697 2698 if id $user > /dev/null 2>&1; then 2699 log_must_retry "currently used" 6 userdel $user 2700 fi 2701 2702 return 0 2703} 2704 2705function add_group_linux #<group_name> 2706{ 2707 typeset group=$1 2708 2709 # Assign 100 as the base gid, a larger value is selected for 2710 # Linux because for many distributions 1000 and under are reserved. 2711 while true; do 2712 groupadd $group > /dev/null 2>&1 2713 typeset -i ret=$? 2714 case $ret in 2715 0) return 0 ;; 2716 *) return 1 ;; 2717 esac 2718 done 2719} 2720 2721function del_group_linux #<group_name> 2722{ 2723 typeset group=$1 2724 2725 getent group $group > /dev/null 2>&1 2726 typeset -i ret=$? 2727 case $ret in 2728 # Group does not exist. 2729 2) return 0 ;; 2730 # Name already exists as a group name 2731 0) log_must groupdel $group ;; 2732 *) return 1 ;; 2733 esac 2734 2735 return 0 2736} 2737 2738# 2739# Add specified user to specified group 2740# 2741# $1 group name 2742# $2 user name 2743# $3 base of the homedir (optional) 2744# 2745function add_user #<group_name> <user_name> <basedir> 2746{ 2747 typeset group=$1 2748 typeset user=$2 2749 typeset basedir=${3:-"/var/tmp"} 2750 2751 if ((${#group} == 0 || ${#user} == 0)); then 2752 log_fail "group name or user name are not defined." 2753 fi 2754 2755 case $(uname) in 2756 FreeBSD) 2757 add_user_freebsd "$group" "$user" "$basedir" 2758 ;; 2759 Linux) 2760 add_user_linux "$group" "$user" "$basedir" 2761 ;; 2762 *) 2763 add_user_illumos "$group" "$user" "$basedir" 2764 ;; 2765 esac 2766 2767 return 0 2768} 2769 2770# 2771# Delete the specified user. 2772# 2773# $1 login name 2774# $2 base of the homedir (optional) 2775# 2776function del_user #<logname> <basedir> 2777{ 2778 typeset user=$1 2779 typeset basedir=${2:-"/var/tmp"} 2780 2781 if ((${#user} == 0)); then 2782 log_fail "login name is necessary." 2783 fi 2784 2785 case $(uname) in 2786 FreeBSD) 2787 del_user_freebsd "$user" 2788 ;; 2789 Linux) 2790 del_user_linux "$user" 2791 ;; 2792 *) 2793 del_user_illumos "$user" 2794 ;; 2795 esac 2796 2797 [[ -d $basedir/$user ]] && rm -fr $basedir/$user 2798 2799 return 0 2800} 2801 2802# 2803# Select valid gid and create specified group. 2804# 2805# $1 group name 2806# 2807function add_group #<group_name> 2808{ 2809 typeset group=$1 2810 2811 if ((${#group} == 0)); then 2812 log_fail "group name is necessary." 2813 fi 2814 2815 case $(uname) in 2816 FreeBSD) 2817 add_group_freebsd "$group" 2818 ;; 2819 Linux) 2820 add_group_linux "$group" 2821 ;; 2822 *) 2823 add_group_illumos "$group" 2824 ;; 2825 esac 2826 2827 return 0 2828} 2829 2830# 2831# Delete the specified group. 2832# 2833# $1 group name 2834# 2835function del_group #<group_name> 2836{ 2837 typeset group=$1 2838 2839 if ((${#group} == 0)); then 2840 log_fail "group name is necessary." 2841 fi 2842 2843 case $(uname) in 2844 FreeBSD) 2845 del_group_freebsd "$group" 2846 ;; 2847 Linux) 2848 del_group_linux "$group" 2849 ;; 2850 *) 2851 del_group_illumos "$group" 2852 ;; 2853 esac 2854 2855 return 0 2856} 2857 2858# 2859# This function will return true if it's safe to destroy the pool passed 2860# as argument 1. It checks for pools based on zvols and files, and also 2861# files contained in a pool that may have a different mountpoint. 2862# 2863function safe_to_destroy_pool { # $1 the pool name 2864 2865 typeset pool="" 2866 typeset DONT_DESTROY="" 2867 2868 # We check that by deleting the $1 pool, we're not 2869 # going to pull the rug out from other pools. Do this 2870 # by looking at all other pools, ensuring that they 2871 # aren't built from files or zvols contained in this pool. 2872 2873 for pool in $(zpool list -H -o name) 2874 do 2875 ALTMOUNTPOOL="" 2876 2877 # this is a list of the top-level directories in each of the 2878 # files that make up the path to the files the pool is based on 2879 FILEPOOL=$(zpool status -v $pool | grep /$1/ | \ 2880 awk '{print $1}') 2881 2882 # this is a list of the zvols that make up the pool 2883 ZVOLPOOL=$(zpool status -v $pool | grep "$ZVOL_DEVDIR/$1$" \ 2884 | awk '{print $1}') 2885 2886 # also want to determine if it's a file-based pool using an 2887 # alternate mountpoint... 2888 POOL_FILE_DIRS=$(zpool status -v $pool | \ 2889 grep / | awk '{print $1}' | \ 2890 awk -F/ '{print $2}' | grep -v "dev") 2891 2892 for pooldir in $POOL_FILE_DIRS 2893 do 2894 OUTPUT=$(zfs list -H -r -o mountpoint $1 | \ 2895 grep "${pooldir}$" | awk '{print $1}') 2896 2897 ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}" 2898 done 2899 2900 2901 if [ ! -z "$ZVOLPOOL" ] 2902 then 2903 DONT_DESTROY="true" 2904 log_note "Pool $pool is built from $ZVOLPOOL on $1" 2905 fi 2906 2907 if [ ! -z "$FILEPOOL" ] 2908 then 2909 DONT_DESTROY="true" 2910 log_note "Pool $pool is built from $FILEPOOL on $1" 2911 fi 2912 2913 if [ ! -z "$ALTMOUNTPOOL" ] 2914 then 2915 DONT_DESTROY="true" 2916 log_note "Pool $pool is built from $ALTMOUNTPOOL on $1" 2917 fi 2918 done 2919 2920 if [ -z "${DONT_DESTROY}" ] 2921 then 2922 return 0 2923 else 2924 log_note "Warning: it is not safe to destroy $1!" 2925 return 1 2926 fi 2927} 2928 2929# 2930# Verify zfs operation with -p option work as expected 2931# $1 operation, value could be create, clone or rename 2932# $2 dataset type, value could be fs or vol 2933# $3 dataset name 2934# $4 new dataset name 2935# 2936function verify_opt_p_ops 2937{ 2938 typeset ops=$1 2939 typeset datatype=$2 2940 typeset dataset=$3 2941 typeset newdataset=$4 2942 2943 if [[ $datatype != "fs" && $datatype != "vol" ]]; then 2944 log_fail "$datatype is not supported." 2945 fi 2946 2947 # check parameters accordingly 2948 case $ops in 2949 create) 2950 newdataset=$dataset 2951 dataset="" 2952 if [[ $datatype == "vol" ]]; then 2953 ops="create -V $VOLSIZE" 2954 fi 2955 ;; 2956 clone) 2957 if [[ -z $newdataset ]]; then 2958 log_fail "newdataset should not be empty" \ 2959 "when ops is $ops." 2960 fi 2961 log_must datasetexists $dataset 2962 log_must snapexists $dataset 2963 ;; 2964 rename) 2965 if [[ -z $newdataset ]]; then 2966 log_fail "newdataset should not be empty" \ 2967 "when ops is $ops." 2968 fi 2969 log_must datasetexists $dataset 2970 ;; 2971 *) 2972 log_fail "$ops is not supported." 2973 ;; 2974 esac 2975 2976 # make sure the upper level filesystem does not exist 2977 destroy_dataset "${newdataset%/*}" "-rRf" 2978 2979 # without -p option, operation will fail 2980 log_mustnot zfs $ops $dataset $newdataset 2981 log_mustnot datasetexists $newdataset ${newdataset%/*} 2982 2983 # with -p option, operation should succeed 2984 log_must zfs $ops -p $dataset $newdataset 2985 block_device_wait 2986 2987 if ! datasetexists $newdataset ; then 2988 log_fail "-p option does not work for $ops" 2989 fi 2990 2991 # when $ops is create or clone, redo the operation still return zero 2992 if [[ $ops != "rename" ]]; then 2993 log_must zfs $ops -p $dataset $newdataset 2994 fi 2995 2996 return 0 2997} 2998 2999# 3000# Get configuration of pool 3001# $1 pool name 3002# $2 config name 3003# 3004function get_config 3005{ 3006 typeset pool=$1 3007 typeset config=$2 3008 typeset alt_root 3009 3010 if ! poolexists "$pool" ; then 3011 return 1 3012 fi 3013 alt_root=$(zpool list -H $pool | awk '{print $NF}') 3014 if [[ $alt_root == "-" ]]; then 3015 value=$(zdb -C $pool | grep "$config:" | awk -F: \ 3016 '{print $2}') 3017 else 3018 value=$(zdb -e $pool | grep "$config:" | awk -F: \ 3019 '{print $2}') 3020 fi 3021 if [[ -n $value ]] ; then 3022 value=${value#'} 3023 value=${value%'} 3024 fi 3025 echo $value 3026 3027 return 0 3028} 3029 3030# 3031# Privated function. Random select one of items from arguments. 3032# 3033# $1 count 3034# $2-n string 3035# 3036function _random_get 3037{ 3038 typeset cnt=$1 3039 shift 3040 3041 typeset str="$@" 3042 typeset -i ind 3043 ((ind = RANDOM % cnt + 1)) 3044 3045 typeset ret=$(echo "$str" | cut -f $ind -d ' ') 3046 echo $ret 3047} 3048 3049# 3050# Random select one of item from arguments which include NONE string 3051# 3052function random_get_with_non 3053{ 3054 typeset -i cnt=$# 3055 ((cnt =+ 1)) 3056 3057 _random_get "$cnt" "$@" 3058} 3059 3060# 3061# Random select one of item from arguments which doesn't include NONE string 3062# 3063function random_get 3064{ 3065 _random_get "$#" "$@" 3066} 3067 3068# 3069# The function will generate a dataset name with specific length 3070# $1, the length of the name 3071# $2, the base string to construct the name 3072# 3073function gen_dataset_name 3074{ 3075 typeset -i len=$1 3076 typeset basestr="$2" 3077 typeset -i baselen=${#basestr} 3078 typeset -i iter=0 3079 typeset l_name="" 3080 3081 if ((len % baselen == 0)); then 3082 ((iter = len / baselen)) 3083 else 3084 ((iter = len / baselen + 1)) 3085 fi 3086 while ((iter > 0)); do 3087 l_name="${l_name}$basestr" 3088 3089 ((iter -= 1)) 3090 done 3091 3092 echo $l_name 3093} 3094 3095# 3096# Get cksum tuple of dataset 3097# $1 dataset name 3098# 3099# sample zdb output: 3100# Dataset data/test [ZPL], ID 355, cr_txg 2413856, 31.0K, 7 objects, rootbp 3101# DVA[0]=<0:803046400:200> DVA[1]=<0:81199000:200> [L0 DMU objset] fletcher4 3102# lzjb LE contiguous unique double size=800L/200P birth=2413856L/2413856P 3103# fill=7 cksum=11ce125712:643a9c18ee2:125e25238fca0:254a3f74b59744 3104function datasetcksum 3105{ 3106 typeset cksum 3107 sync 3108 cksum=$(zdb -vvv $1 | grep "^Dataset $1 \[" | grep "cksum" \ 3109 | awk -F= '{print $7}') 3110 echo $cksum 3111} 3112 3113# 3114# Get cksum of file 3115# #1 file path 3116# 3117function checksum 3118{ 3119 typeset cksum 3120 cksum=$(cksum $1 | awk '{print $1}') 3121 echo $cksum 3122} 3123 3124# 3125# Get the given disk/slice state from the specific field of the pool 3126# 3127function get_device_state #pool disk field("", "spares","logs") 3128{ 3129 typeset pool=$1 3130 typeset disk=${2#$DEV_DSKDIR/} 3131 typeset field=${3:-$pool} 3132 3133 state=$(zpool status -v "$pool" 2>/dev/null | \ 3134 nawk -v device=$disk -v pool=$pool -v field=$field \ 3135 'BEGIN {startconfig=0; startfield=0; } 3136 /config:/ {startconfig=1} 3137 (startconfig==1) && ($1==field) {startfield=1; next;} 3138 (startfield==1) && ($1==device) {print $2; exit;} 3139 (startfield==1) && 3140 ($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}') 3141 echo $state 3142} 3143 3144 3145# 3146# print the given directory filesystem type 3147# 3148# $1 directory name 3149# 3150function get_fstype 3151{ 3152 typeset dir=$1 3153 3154 if [[ -z $dir ]]; then 3155 log_fail "Usage: get_fstype <directory>" 3156 fi 3157 3158 # 3159 # $ df -n / 3160 # / : ufs 3161 # 3162 df -n $dir | awk '{print $3}' 3163} 3164 3165# 3166# Given a disk, label it to VTOC regardless what label was on the disk 3167# $1 disk 3168# 3169function labelvtoc 3170{ 3171 typeset disk=$1 3172 if [[ -z $disk ]]; then 3173 log_fail "The disk name is unspecified." 3174 fi 3175 typeset label_file=/var/tmp/labelvtoc.$$ 3176 typeset arch=$(uname -p) 3177 3178 if is_linux || is_freebsd; then 3179 log_note "Currently unsupported by the test framework" 3180 return 1 3181 fi 3182 3183 if [[ $arch == "i386" ]]; then 3184 echo "label" > $label_file 3185 echo "0" >> $label_file 3186 echo "" >> $label_file 3187 echo "q" >> $label_file 3188 echo "q" >> $label_file 3189 3190 fdisk -B $disk >/dev/null 2>&1 3191 # wait a while for fdisk finishes 3192 sleep 60 3193 elif [[ $arch == "sparc" ]]; then 3194 echo "label" > $label_file 3195 echo "0" >> $label_file 3196 echo "" >> $label_file 3197 echo "" >> $label_file 3198 echo "" >> $label_file 3199 echo "q" >> $label_file 3200 else 3201 log_fail "unknown arch type" 3202 fi 3203 3204 format -e -s -d $disk -f $label_file 3205 typeset -i ret_val=$? 3206 rm -f $label_file 3207 # 3208 # wait the format to finish 3209 # 3210 sleep 60 3211 if ((ret_val != 0)); then 3212 log_fail "unable to label $disk as VTOC." 3213 fi 3214 3215 return 0 3216} 3217 3218# 3219# check if the system was installed as zfsroot or not 3220# return: 0 if zfsroot, non-zero if not 3221# 3222function is_zfsroot 3223{ 3224 df -n / | grep zfs > /dev/null 2>&1 3225 return $? 3226} 3227 3228# 3229# get the root filesystem name if it's zfsroot system. 3230# 3231# return: root filesystem name 3232function get_rootfs 3233{ 3234 typeset rootfs="" 3235 3236 if is_freebsd; then 3237 rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}') 3238 elif ! is_linux; then 3239 rootfs=$(awk '{if ($2 == "/" && $3 == "zfs") print $1}' \ 3240 /etc/mnttab) 3241 fi 3242 if [[ -z "$rootfs" ]]; then 3243 log_fail "Can not get rootfs" 3244 fi 3245 zfs list $rootfs > /dev/null 2>&1 3246 if (($? == 0)); then 3247 echo $rootfs 3248 else 3249 log_fail "This is not a zfsroot system." 3250 fi 3251} 3252 3253# 3254# get the rootfs's pool name 3255# return: 3256# rootpool name 3257# 3258function get_rootpool 3259{ 3260 typeset rootfs="" 3261 typeset rootpool="" 3262 3263 if is_freebsd; then 3264 rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}') 3265 elif ! is_linux; then 3266 rootfs=$(awk '{if ($2 == "/" && $3 =="zfs") print $1}' \ 3267 /etc/mnttab) 3268 fi 3269 if [[ -z "$rootfs" ]]; then 3270 log_fail "Can not get rootpool" 3271 fi 3272 zfs list $rootfs > /dev/null 2>&1 3273 if (($? == 0)); then 3274 echo ${rootfs%%/*} 3275 else 3276 log_fail "This is not a zfsroot system." 3277 fi 3278} 3279 3280# 3281# Get the word numbers from a string separated by white space 3282# 3283function get_word_count 3284{ 3285 echo $1 | wc -w 3286} 3287 3288# 3289# To verify if the require numbers of disks is given 3290# 3291function verify_disk_count 3292{ 3293 typeset -i min=${2:-1} 3294 3295 typeset -i count=$(get_word_count "$1") 3296 3297 if ((count < min)); then 3298 log_untested "A minimum of $min disks is required to run." \ 3299 " You specified $count disk(s)" 3300 fi 3301} 3302 3303function ds_is_volume 3304{ 3305 typeset type=$(get_prop type $1) 3306 [[ $type = "volume" ]] && return 0 3307 return 1 3308} 3309 3310function ds_is_filesystem 3311{ 3312 typeset type=$(get_prop type $1) 3313 [[ $type = "filesystem" ]] && return 0 3314 return 1 3315} 3316 3317function ds_is_snapshot 3318{ 3319 typeset type=$(get_prop type $1) 3320 [[ $type = "snapshot" ]] && return 0 3321 return 1 3322} 3323 3324# 3325# Check if Trusted Extensions are installed and enabled 3326# 3327function is_te_enabled 3328{ 3329 svcs -H -o state labeld 2>/dev/null | grep "enabled" 3330 if (($? != 0)); then 3331 return 1 3332 else 3333 return 0 3334 fi 3335} 3336 3337# Utility function to determine if a system has multiple cpus. 3338function is_mp 3339{ 3340 if is_linux; then 3341 (($(nproc) > 1)) 3342 elif is_freebsd; then 3343 sysctl -n kern.smp.cpus 3344 else 3345 (($(psrinfo | wc -l) > 1)) 3346 fi 3347 3348 return $? 3349} 3350 3351function get_cpu_freq 3352{ 3353 if is_linux; then 3354 lscpu | awk '/CPU MHz/ { print $3 }' 3355 elif is_freebsd; then 3356 sysctl -n hw.clockrate 3357 else 3358 psrinfo -v 0 | awk '/processor operates at/ {print $6}' 3359 fi 3360} 3361 3362# Run the given command as the user provided. 3363function user_run 3364{ 3365 typeset user=$1 3366 shift 3367 3368 log_note "user: $user" 3369 log_note "cmd: $*" 3370 3371 typeset out=$TEST_BASE_DIR/out 3372 typeset err=$TEST_BASE_DIR/err 3373 3374 sudo -Eu $user env PATH="$PATH" ksh <<<"$*" >$out 2>$err 3375 typeset res=$? 3376 log_note "out: $(<$out)" 3377 log_note "err: $(<$err)" 3378 return $res 3379} 3380 3381# 3382# Check if the pool contains the specified vdevs 3383# 3384# $1 pool 3385# $2..n <vdev> ... 3386# 3387# Return 0 if the vdevs are contained in the pool, 1 if any of the specified 3388# vdevs is not in the pool, and 2 if pool name is missing. 3389# 3390function vdevs_in_pool 3391{ 3392 typeset pool=$1 3393 typeset vdev 3394 3395 if [[ -z $pool ]]; then 3396 log_note "Missing pool name." 3397 return 2 3398 fi 3399 3400 shift 3401 3402 # We could use 'zpool list' to only get the vdevs of the pool but we 3403 # can't reference a mirror/raidz vdev using its ID (i.e mirror-0), 3404 # therefore we use the 'zpool status' output. 3405 typeset tmpfile=$(mktemp) 3406 zpool status -v "$pool" | grep -A 1000 "config:" >$tmpfile 3407 for vdev in $@; do 3408 grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1 3409 [[ $? -ne 0 ]] && return 1 3410 done 3411 3412 rm -f $tmpfile 3413 3414 return 0; 3415} 3416 3417function get_max 3418{ 3419 typeset -l i max=$1 3420 shift 3421 3422 for i in "$@"; do 3423 max=$((max > i ? max : i)) 3424 done 3425 3426 echo $max 3427} 3428 3429function get_min 3430{ 3431 typeset -l i min=$1 3432 shift 3433 3434 for i in "$@"; do 3435 min=$((min < i ? min : i)) 3436 done 3437 3438 echo $min 3439} 3440 3441# Write data that can be compressed into a directory 3442function write_compressible 3443{ 3444 typeset dir=$1 3445 typeset megs=$2 3446 typeset nfiles=${3:-1} 3447 typeset bs=${4:-1024k} 3448 typeset fname=${5:-file} 3449 3450 [[ -d $dir ]] || log_fail "No directory: $dir" 3451 3452 # Under Linux fio is not currently used since its behavior can 3453 # differ significantly across versions. This includes missing 3454 # command line options and cases where the --buffer_compress_* 3455 # options fail to behave as expected. 3456 if is_linux; then 3457 typeset file_bytes=$(to_bytes $megs) 3458 typeset bs_bytes=4096 3459 typeset blocks=$(($file_bytes / $bs_bytes)) 3460 3461 for (( i = 0; i < $nfiles; i++ )); do 3462 truncate -s $file_bytes $dir/$fname.$i 3463 3464 # Write every third block to get 66% compression. 3465 for (( j = 0; j < $blocks; j += 3 )); do 3466 dd if=/dev/urandom of=$dir/$fname.$i \ 3467 seek=$j bs=$bs_bytes count=1 \ 3468 conv=notrunc >/dev/null 2>&1 3469 done 3470 done 3471 else 3472 log_must eval "fio \ 3473 --name=job \ 3474 --fallocate=0 \ 3475 --minimal \ 3476 --randrepeat=0 \ 3477 --buffer_compress_percentage=66 \ 3478 --buffer_compress_chunk=4096 \ 3479 --directory=$dir \ 3480 --numjobs=$nfiles \ 3481 --nrfiles=$nfiles \ 3482 --rw=write \ 3483 --bs=$bs \ 3484 --filesize=$megs \ 3485 --filename_format='$fname.\$jobnum' >/dev/null" 3486 fi 3487} 3488 3489function get_objnum 3490{ 3491 typeset pathname=$1 3492 typeset objnum 3493 3494 [[ -e $pathname ]] || log_fail "No such file or directory: $pathname" 3495 if is_freebsd; then 3496 objnum=$(stat -f "%i" $pathname) 3497 else 3498 objnum=$(stat -c %i $pathname) 3499 fi 3500 echo $objnum 3501} 3502 3503# 3504# Sync data to the pool 3505# 3506# $1 pool name 3507# $2 boolean to force uberblock (and config including zpool cache file) update 3508# 3509function sync_pool #pool <force> 3510{ 3511 typeset pool=${1:-$TESTPOOL} 3512 typeset force=${2:-false} 3513 3514 if [[ $force == true ]]; then 3515 log_must zpool sync -f $pool 3516 else 3517 log_must zpool sync $pool 3518 fi 3519 3520 return 0 3521} 3522 3523# 3524# Wait for zpool 'freeing' property drops to zero. 3525# 3526# $1 pool name 3527# 3528function wait_freeing #pool 3529{ 3530 typeset pool=${1:-$TESTPOOL} 3531 while true; do 3532 [[ "0" == "$(zpool list -Ho freeing $pool)" ]] && break 3533 log_must sleep 1 3534 done 3535} 3536 3537# 3538# Wait for every device replace operation to complete 3539# 3540# $1 pool name 3541# 3542function wait_replacing #pool 3543{ 3544 typeset pool=${1:-$TESTPOOL} 3545 while true; do 3546 [[ "" == "$(zpool status $pool | 3547 awk '/replacing-[0-9]+/ {print $1}')" ]] && break 3548 log_must sleep 1 3549 done 3550} 3551 3552# 3553# Wait for a pool to be scrubbed 3554# 3555# $1 pool name 3556# 3557function wait_scrubbed 3558{ 3559 typeset pool=${1:-$TESTPOOL} 3560 while ! is_pool_scrubbed $pool ; do 3561 sleep 1 3562 done 3563} 3564 3565# Backup the zed.rc in our test directory so that we can edit it for our test. 3566# 3567# Returns: Backup file name. You will need to pass this to zed_rc_restore(). 3568function zed_rc_backup 3569{ 3570 zedrc_backup="$(mktemp)" 3571 cp $ZEDLET_DIR/zed.rc $zedrc_backup 3572 echo $zedrc_backup 3573} 3574 3575function zed_rc_restore 3576{ 3577 mv $1 $ZEDLET_DIR/zed.rc 3578} 3579 3580# 3581# Setup custom environment for the ZED. 3582# 3583# $@ Optional list of zedlets to run under zed. 3584function zed_setup 3585{ 3586 if ! is_linux; then 3587 log_unsupported "No zed on $(uname)" 3588 fi 3589 3590 if [[ ! -d $ZEDLET_DIR ]]; then 3591 log_must mkdir $ZEDLET_DIR 3592 fi 3593 3594 if [[ ! -e $VDEVID_CONF ]]; then 3595 log_must touch $VDEVID_CONF 3596 fi 3597 3598 if [[ -e $VDEVID_CONF_ETC ]]; then 3599 log_fail "Must not have $VDEVID_CONF_ETC file present on system" 3600 fi 3601 EXTRA_ZEDLETS=$@ 3602 3603 # Create a symlink for /etc/zfs/vdev_id.conf file. 3604 log_must ln -s $VDEVID_CONF $VDEVID_CONF_ETC 3605 3606 # Setup minimal ZED configuration. Individual test cases should 3607 # add additional ZEDLETs as needed for their specific test. 3608 log_must cp ${ZEDLET_ETC_DIR}/zed.rc $ZEDLET_DIR 3609 log_must cp ${ZEDLET_ETC_DIR}/zed-functions.sh $ZEDLET_DIR 3610 3611 # Scripts must only be user writable. 3612 if [[ -n "$EXTRA_ZEDLETS" ]] ; then 3613 saved_umask=$(umask) 3614 log_must umask 0022 3615 for i in $EXTRA_ZEDLETS ; do 3616 log_must cp ${ZEDLET_LIBEXEC_DIR}/$i $ZEDLET_DIR 3617 done 3618 log_must umask $saved_umask 3619 fi 3620 3621 # Customize the zed.rc file to enable the full debug log. 3622 log_must sed -i '/\#ZED_DEBUG_LOG=.*/d' $ZEDLET_DIR/zed.rc 3623 echo "ZED_DEBUG_LOG=$ZED_DEBUG_LOG" >>$ZEDLET_DIR/zed.rc 3624 3625} 3626 3627# 3628# Cleanup custom ZED environment. 3629# 3630# $@ Optional list of zedlets to remove from our test zed.d directory. 3631function zed_cleanup 3632{ 3633 if ! is_linux; then 3634 return 3635 fi 3636 EXTRA_ZEDLETS=$@ 3637 3638 log_must rm -f ${ZEDLET_DIR}/zed.rc 3639 log_must rm -f ${ZEDLET_DIR}/zed-functions.sh 3640 log_must rm -f ${ZEDLET_DIR}/all-syslog.sh 3641 log_must rm -f ${ZEDLET_DIR}/all-debug.sh 3642 log_must rm -f ${ZEDLET_DIR}/state 3643 3644 if [[ -n "$EXTRA_ZEDLETS" ]] ; then 3645 for i in $EXTRA_ZEDLETS ; do 3646 log_must rm -f ${ZEDLET_DIR}/$i 3647 done 3648 fi 3649 log_must rm -f $ZED_LOG 3650 log_must rm -f $ZED_DEBUG_LOG 3651 log_must rm -f $VDEVID_CONF_ETC 3652 log_must rm -f $VDEVID_CONF 3653 rmdir $ZEDLET_DIR 3654} 3655 3656# 3657# Check if ZED is currently running, if not start ZED. 3658# 3659function zed_start 3660{ 3661 if ! is_linux; then 3662 return 3663 fi 3664 3665 # ZEDLET_DIR=/var/tmp/zed 3666 if [[ ! -d $ZEDLET_DIR ]]; then 3667 log_must mkdir $ZEDLET_DIR 3668 fi 3669 3670 # Verify the ZED is not already running. 3671 pgrep -x zed > /dev/null 3672 if (($? == 0)); then 3673 log_note "ZED already running" 3674 else 3675 log_note "Starting ZED" 3676 # run ZED in the background and redirect foreground logging 3677 # output to $ZED_LOG. 3678 log_must truncate -s 0 $ZED_DEBUG_LOG 3679 log_must eval "zed -vF -d $ZEDLET_DIR -P $PATH" \ 3680 "-s $ZEDLET_DIR/state -j 1 2>$ZED_LOG &" 3681 fi 3682 3683 return 0 3684} 3685 3686# 3687# Kill ZED process 3688# 3689function zed_stop 3690{ 3691 if ! is_linux; then 3692 return 3693 fi 3694 3695 log_note "Stopping ZED" 3696 while true; do 3697 zedpids="$(pgrep -x zed)" 3698 [ "$?" -ne 0 ] && break 3699 3700 log_must kill $zedpids 3701 sleep 1 3702 done 3703 return 0 3704} 3705 3706# 3707# Drain all zevents 3708# 3709function zed_events_drain 3710{ 3711 while [ $(zpool events -H | wc -l) -ne 0 ]; do 3712 sleep 1 3713 zpool events -c >/dev/null 3714 done 3715} 3716 3717# Set a variable in zed.rc to something, un-commenting it in the process. 3718# 3719# $1 variable 3720# $2 value 3721function zed_rc_set 3722{ 3723 var="$1" 3724 val="$2" 3725 # Remove the line 3726 cmd="'/$var/d'" 3727 eval sed -i $cmd $ZEDLET_DIR/zed.rc 3728 3729 # Add it at the end 3730 echo "$var=$val" >> $ZEDLET_DIR/zed.rc 3731} 3732 3733 3734# 3735# Check is provided device is being active used as a swap device. 3736# 3737function is_swap_inuse 3738{ 3739 typeset device=$1 3740 3741 if [[ -z $device ]] ; then 3742 log_note "No device specified." 3743 return 1 3744 fi 3745 3746 if is_linux; then 3747 swapon -s | grep -w $(readlink -f $device) > /dev/null 2>&1 3748 elif is_freebsd; then 3749 swapctl -l | grep -w $device 3750 else 3751 swap -l | grep -w $device > /dev/null 2>&1 3752 fi 3753 3754 return $? 3755} 3756 3757# 3758# Setup a swap device using the provided device. 3759# 3760function swap_setup 3761{ 3762 typeset swapdev=$1 3763 3764 if is_linux; then 3765 log_must eval "mkswap $swapdev > /dev/null 2>&1" 3766 log_must swapon $swapdev 3767 elif is_freebsd; then 3768 log_must swapctl -a $swapdev 3769 else 3770 log_must swap -a $swapdev 3771 fi 3772 3773 return 0 3774} 3775 3776# 3777# Cleanup a swap device on the provided device. 3778# 3779function swap_cleanup 3780{ 3781 typeset swapdev=$1 3782 3783 if is_swap_inuse $swapdev; then 3784 if is_linux; then 3785 log_must swapoff $swapdev 3786 elif is_freebsd; then 3787 log_must swapoff $swapdev 3788 else 3789 log_must swap -d $swapdev 3790 fi 3791 fi 3792 3793 return 0 3794} 3795 3796# 3797# Set a global system tunable (64-bit value) 3798# 3799# $1 tunable name (use a NAME defined in tunables.cfg) 3800# $2 tunable values 3801# 3802function set_tunable64 3803{ 3804 set_tunable_impl "$1" "$2" Z 3805} 3806 3807# 3808# Set a global system tunable (32-bit value) 3809# 3810# $1 tunable name (use a NAME defined in tunables.cfg) 3811# $2 tunable values 3812# 3813function set_tunable32 3814{ 3815 set_tunable_impl "$1" "$2" W 3816} 3817 3818function set_tunable_impl 3819{ 3820 typeset name="$1" 3821 typeset value="$2" 3822 typeset mdb_cmd="$3" 3823 typeset module="${4:-zfs}" 3824 3825 eval "typeset tunable=\$$name" 3826 case "$tunable" in 3827 UNSUPPORTED) 3828 log_unsupported "Tunable '$name' is unsupported on $(uname)" 3829 ;; 3830 "") 3831 log_fail "Tunable '$name' must be added to tunables.cfg" 3832 ;; 3833 *) 3834 ;; 3835 esac 3836 3837 [[ -z "$value" ]] && return 1 3838 [[ -z "$mdb_cmd" ]] && return 1 3839 3840 case "$(uname)" in 3841 Linux) 3842 typeset zfs_tunables="/sys/module/$module/parameters" 3843 [[ -w "$zfs_tunables/$tunable" ]] || return 1 3844 cat >"$zfs_tunables/$tunable" <<<"$value" 3845 return $? 3846 ;; 3847 FreeBSD) 3848 sysctl vfs.zfs.$tunable=$value 3849 return "$?" 3850 ;; 3851 SunOS) 3852 [[ "$module" -eq "zfs" ]] || return 1 3853 echo "${tunable}/${mdb_cmd}0t${value}" | mdb -kw 3854 return $? 3855 ;; 3856 esac 3857} 3858 3859# 3860# Get a global system tunable 3861# 3862# $1 tunable name (use a NAME defined in tunables.cfg) 3863# 3864function get_tunable 3865{ 3866 get_tunable_impl "$1" 3867} 3868 3869function get_tunable_impl 3870{ 3871 typeset name="$1" 3872 typeset module="${2:-zfs}" 3873 3874 eval "typeset tunable=\$$name" 3875 case "$tunable" in 3876 UNSUPPORTED) 3877 log_unsupported "Tunable '$name' is unsupported on $(uname)" 3878 ;; 3879 "") 3880 log_fail "Tunable '$name' must be added to tunables.cfg" 3881 ;; 3882 *) 3883 ;; 3884 esac 3885 3886 case "$(uname)" in 3887 Linux) 3888 typeset zfs_tunables="/sys/module/$module/parameters" 3889 [[ -f "$zfs_tunables/$tunable" ]] || return 1 3890 cat $zfs_tunables/$tunable 3891 return $? 3892 ;; 3893 FreeBSD) 3894 sysctl -n vfs.zfs.$tunable 3895 ;; 3896 SunOS) 3897 [[ "$module" -eq "zfs" ]] || return 1 3898 ;; 3899 esac 3900 3901 return 1 3902} 3903 3904# 3905# Prints the current time in seconds since UNIX Epoch. 3906# 3907function current_epoch 3908{ 3909 printf '%(%s)T' 3910} 3911 3912# 3913# Get decimal value of global uint32_t variable using mdb. 3914# 3915function mdb_get_uint32 3916{ 3917 typeset variable=$1 3918 typeset value 3919 3920 value=$(mdb -k -e "$variable/X | ::eval .=U") 3921 if [[ $? -ne 0 ]]; then 3922 log_fail "Failed to get value of '$variable' from mdb." 3923 return 1 3924 fi 3925 3926 echo $value 3927 return 0 3928} 3929 3930# 3931# Set global uint32_t variable to a decimal value using mdb. 3932# 3933function mdb_set_uint32 3934{ 3935 typeset variable=$1 3936 typeset value=$2 3937 3938 mdb -kw -e "$variable/W 0t$value" > /dev/null 3939 if [[ $? -ne 0 ]]; then 3940 echo "Failed to set '$variable' to '$value' in mdb." 3941 return 1 3942 fi 3943 3944 return 0 3945} 3946 3947# 3948# Set global scalar integer variable to a hex value using mdb. 3949# Note: Target should have CTF data loaded. 3950# 3951function mdb_ctf_set_int 3952{ 3953 typeset variable=$1 3954 typeset value=$2 3955 3956 mdb -kw -e "$variable/z $value" > /dev/null 3957 if [[ $? -ne 0 ]]; then 3958 echo "Failed to set '$variable' to '$value' in mdb." 3959 return 1 3960 fi 3961 3962 return 0 3963} 3964 3965# 3966# Compute MD5 digest for given file or stdin if no file given. 3967# Note: file path must not contain spaces 3968# 3969function md5digest 3970{ 3971 typeset file=$1 3972 3973 case $(uname) in 3974 FreeBSD) 3975 md5 -q $file 3976 ;; 3977 *) 3978 md5sum -b $file | awk '{ print $1 }' 3979 ;; 3980 esac 3981} 3982 3983# 3984# Compute SHA256 digest for given file or stdin if no file given. 3985# Note: file path must not contain spaces 3986# 3987function sha256digest 3988{ 3989 typeset file=$1 3990 3991 case $(uname) in 3992 FreeBSD) 3993 sha256 -q $file 3994 ;; 3995 *) 3996 sha256sum -b $file | awk '{ print $1 }' 3997 ;; 3998 esac 3999} 4000 4001function new_fs #<args> 4002{ 4003 case $(uname) in 4004 FreeBSD) 4005 newfs "$@" 4006 ;; 4007 *) 4008 echo y | newfs -v "$@" 4009 ;; 4010 esac 4011} 4012 4013function stat_size #<path> 4014{ 4015 typeset path=$1 4016 4017 case $(uname) in 4018 FreeBSD) 4019 stat -f %z "$path" 4020 ;; 4021 *) 4022 stat -c %s "$path" 4023 ;; 4024 esac 4025} 4026 4027# Run a command as if it was being run in a TTY. 4028# 4029# Usage: 4030# 4031# faketty command 4032# 4033function faketty 4034{ 4035 if is_freebsd; then 4036 script -q /dev/null env "$@" 4037 else 4038 script --return --quiet -c "$*" /dev/null 4039 fi 4040} 4041 4042# 4043# Produce a random permutation of the integers in a given range (inclusive). 4044# 4045function range_shuffle # begin end 4046{ 4047 typeset -i begin=$1 4048 typeset -i end=$2 4049 4050 seq ${begin} ${end} | sort -R 4051} 4052 4053# 4054# Cross-platform xattr helpers 4055# 4056 4057function get_xattr # name path 4058{ 4059 typeset name=$1 4060 typeset path=$2 4061 4062 case $(uname) in 4063 FreeBSD) 4064 getextattr -qq user "${name}" "${path}" 4065 ;; 4066 *) 4067 attr -qg "${name}" "${path}" 4068 ;; 4069 esac 4070} 4071 4072function set_xattr # name value path 4073{ 4074 typeset name=$1 4075 typeset value=$2 4076 typeset path=$3 4077 4078 case $(uname) in 4079 FreeBSD) 4080 setextattr user "${name}" "${value}" "${path}" 4081 ;; 4082 *) 4083 attr -qs "${name}" -V "${value}" "${path}" 4084 ;; 4085 esac 4086} 4087 4088function set_xattr_stdin # name value 4089{ 4090 typeset name=$1 4091 typeset path=$2 4092 4093 case $(uname) in 4094 FreeBSD) 4095 setextattr -i user "${name}" "${path}" 4096 ;; 4097 *) 4098 attr -qs "${name}" "${path}" 4099 ;; 4100 esac 4101} 4102 4103function rm_xattr # name path 4104{ 4105 typeset name=$1 4106 typeset path=$2 4107 4108 case $(uname) in 4109 FreeBSD) 4110 rmextattr -q user "${name}" "${path}" 4111 ;; 4112 *) 4113 attr -qr "${name}" "${path}" 4114 ;; 4115 esac 4116} 4117 4118function ls_xattr # path 4119{ 4120 typeset path=$1 4121 4122 case $(uname) in 4123 FreeBSD) 4124 lsextattr -qq user "${path}" 4125 ;; 4126 *) 4127 attr -ql "${path}" 4128 ;; 4129 esac 4130} 4131 4132function kstat # stat flags? 4133{ 4134 typeset stat=$1 4135 typeset flags=${2-"-n"} 4136 4137 case $(uname) in 4138 FreeBSD) 4139 sysctl $flags kstat.zfs.misc.$stat 4140 ;; 4141 Linux) 4142 typeset zfs_kstat="/proc/spl/kstat/zfs/$stat" 4143 [[ -f "$zfs_kstat" ]] || return 1 4144 cat $zfs_kstat 4145 ;; 4146 *) 4147 false 4148 ;; 4149 esac 4150} 4151 4152function get_arcstat # stat 4153{ 4154 typeset stat=$1 4155 4156 case $(uname) in 4157 FreeBSD) 4158 kstat arcstats.$stat 4159 ;; 4160 Linux) 4161 kstat arcstats | awk "/$stat/ { print \$3 }" 4162 ;; 4163 *) 4164 false 4165 ;; 4166 esac 4167} 4168 4169# 4170# Wait for the specified arcstat to reach non-zero quiescence. 4171# If echo is 1 echo the value after reaching quiescence, otherwise 4172# if echo is 0 print the arcstat we are waiting on. 4173# 4174function arcstat_quiescence # stat echo 4175{ 4176 typeset stat=$1 4177 typeset echo=$2 4178 typeset do_once=true 4179 4180 if [[ $echo -eq 0 ]]; then 4181 echo "Waiting for arcstat $1 quiescence." 4182 fi 4183 4184 while $do_once || [ $stat1 -ne $stat2 ] || [ $stat2 -eq 0 ]; do 4185 typeset stat1=$(get_arcstat $stat) 4186 sleep 2 4187 typeset stat2=$(get_arcstat $stat) 4188 do_once=false 4189 done 4190 4191 if [[ $echo -eq 1 ]]; then 4192 echo $stat2 4193 fi 4194} 4195 4196function arcstat_quiescence_noecho # stat 4197{ 4198 typeset stat=$1 4199 arcstat_quiescence $stat 0 4200} 4201 4202function arcstat_quiescence_echo # stat 4203{ 4204 typeset stat=$1 4205 arcstat_quiescence $stat 1 4206} 4207 4208# 4209# Given an array of pids, wait until all processes 4210# have completed and check their return status. 4211# 4212function wait_for_children #children 4213{ 4214 rv=0 4215 children=("$@") 4216 for child in "${children[@]}" 4217 do 4218 child_exit=0 4219 wait ${child} || child_exit=$? 4220 if [ $child_exit -ne 0 ]; then 4221 echo "child ${child} failed with ${child_exit}" 4222 rv=1 4223 fi 4224 done 4225 return $rv 4226} 4227