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