1#!/usr/bin/env bash 2# 3# Copyright (C) 2013 eNovance SAS <licensing@enovance.com> 4# Author: Erwan Velu <erwan@enovance.com> 5# 6# The license below covers all files distributed with fio unless otherwise 7# noted in the file itself. 8# 9# This program is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License version 2 as 11# published by the Free Software Foundation. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program; if not, write to the Free Software 20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 22BLK_SIZE= 23BLOCK_SIZE=4k 24SEQ=-1 25TEMPLATE=/tmp/template.fio 26OUTFILE= 27DISKS= 28PRINTABLE_DISKS= 29RUNTIME=300 30ETA=0 31MODES="write,randwrite,read,randread" 32SHORT_HOSTNAME= 33CACHED_IO="FALSE" 34PREFIX="" 35PREFIX_FILENAME="" 36IODEPTH=1 37 38show_help() { 39 PROG=$(basename $0) 40 echo "usage of $PROG:" 41 cat << EOF 42-h : Show this help & exit 43-c : Enable cached-based IOs 44 Disabled by default 45-a : Run sequential test then parallel one 46 Disabled by default 47-s : Run sequential test (default value) 48 one test after another then one disk after another 49 Disabled by default 50-p : Run parallel test 51 one test after anoter but all disks at the same time 52 Enabled by default 53-D iodepth : Run with the specified iodepth 54 Default is $IODEPTH 55-d disk1[,disk2,disk3,..] : Run the tests on the selected disks 56 Separated each disk with a comma 57-z filesize : Specify the working file size, if you are passing filepaths to -d 58 Disabled by default 59-r seconds : Time in seconds per benchmark 60 0 means till the end of the device 61 Default is $RUNTIME seconds 62-b blocksize[,blocksize1, ...] : The blocksizes to test under fio format (4k, 1m, ...) 63 Separated each blocksize with a comma 64 Default is $BLOCK_SIZE 65-m mode1,[mode2,mode3, ...] : Define the fio IO profile to use like read, write, randread, randwrite 66 Default is "$MODES" 67-x prefix : Add a prefix to the fio filename 68 Useful to let a context associated with the file 69 If the prefix features a / (slash), prefix will be considered as a directory 70-A cmd_to_run : System command to run after each job (exec_postrun in fio) 71-B cmd_to_run : System command to run before each job (exec_prerun in fio) 72 73Example: 74 75$PROG -d /dev/sdb,/dev/sdc,/dev/sdd,/dev/sde -a -b 4k,128k,1m -r 100 -a -x dellr720-day2/ 76 77 Will generate an fio file that will run 78 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests 79 ETA ~ 4 tests * 4 disks * 100 seconds 80 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests 81 ETA ~ 4 tests * 4 disks * 100 seconds 82 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests 83 ETA ~ 4 tests * 4 disks * 100 seconds 84 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests 85 ETA ~ 4 tests * 100 seconds 86 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests 87 ETA ~ 4 tests * 100 seconds 88 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests 89 ETA ~ 4 tests * 100 seconds 90 91Generating dellr720-day2/localhost-4k,128k,1m-all-write,randwrite,read,randread-sdb,sdc,sdd,sde.fio 92Estimated Time = 6000 seconds : 1 hour 40 minutes 93EOF 94} 95 96finish_template() { 97echo "iodepth=$IODEPTH" >> $TEMPLATE 98 99if [ "$RUNTIME" != "0" ]; then 100 echo "runtime=$RUNTIME" >> $TEMPLATE 101 echo "time_based" >> $TEMPLATE 102fi 103 104if [ "$CACHED_IO" = "FALSE" ]; then 105 echo "direct=1" >> $TEMPLATE 106fi 107} 108 109 110diskname_to_printable() { 111COUNT=0 112for disk in $(echo $@ | tr "," " "); do 113 R=$(basename $disk | sed 's|/|_|g') 114 COUNT=$(($COUNT + 1)) 115 if [ $COUNT -eq 1 ]; then 116 P="$R" 117 else 118 P="$P,$R" 119 fi 120done 121echo $P 122} 123 124gen_template() { 125cat >$TEMPLATE << EOF 126[global] 127ioengine=libaio 128invalidate=1 129ramp_time=5 130EOF 131} 132 133gen_seq_suite() { 134TYPE=$1 135disk=$2 136PRINTABLE_DISK=$(diskname_to_printable $disk) 137cat >> $OUTFILE << EOF 138[$TYPE-$PRINTABLE_DISK-$BLK_SIZE-seq] 139stonewall 140bs=$BLK_SIZE 141filename=$disk 142rw=$TYPE 143write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results 144write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results 145EOF 146ETA=$(($ETA + $RUNTIME)) 147} 148 149gen_seq_fio() { 150for disk in $(echo $DISKS | tr "," " "); do 151 for mode in $(echo $MODES | tr "," " "); do 152 gen_seq_suite "$mode" "$disk" 153 done 154done 155} 156 157 158gen_para_suite() { 159TYPE=$1 160NEED_WALL=$2 161D=0 162for disk in $(echo $DISKS | tr "," " "); do 163 PRINTABLE_DISK=$(diskname_to_printable $disk) 164 cat >> $OUTFILE << EOF 165[$TYPE-$PRINTABLE_DISK-$BLK_SIZE-para] 166bs=$BLK_SIZE 167EOF 168 169if [ "$D" = 0 ]; then 170 echo "stonewall" >> $OUTFILE 171 D=1 172fi 173 174cat >> $OUTFILE << EOF 175filename=$disk 176rw=$TYPE 177write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results 178write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results 179EOF 180done 181 182ETA=$(($ETA + $RUNTIME)) 183echo >> $OUTFILE 184} 185 186gen_para_fio() { 187for mode in $(echo $MODES | tr "," " "); do 188 gen_para_suite "$mode" 189done 190} 191 192gen_fio() { 193case $SEQ in 194 2) 195 gen_seq_fio 196 gen_para_fio 197 ;; 198 1) 199 gen_seq_fio 200 ;; 201 0) 202 gen_para_fio 203 ;; 204esac 205} 206 207parse_cmdline() { 208while getopts "hacpsd:b:r:m:x:z:D:A:B:" opt; do 209 case $opt in 210 h) 211 show_help 212 exit 0 213 ;; 214 b) 215 BLOCK_SIZE=$OPTARG 216 ;; 217 c) 218 CACHED_IO="TRUE" 219 ;; 220 s) 221 if [ "$SEQ" = "-1" ]; then 222 SEQ=1 223 fi 224 ;; 225 x) 226 PREFIX=$OPTARG 227 echo "$PREFIX" | grep -q "/" 228 if [ "$?" -eq 0 ]; then 229 mkdir -p $PREFIX 230 # No need to keep the prefix for the log files 231 # we do have a directory for that 232 PREFIX_FILENAME="" 233 else 234 # We need to keep the prefix for the log files 235 PREFIX_FILENAME=$PREFIX 236 fi 237 ;; 238 r) 239 RUNTIME=$OPTARG 240 ;; 241 p) 242 if [ "$SEQ" = "-1" ]; then 243 SEQ=0 244 fi 245 ;; 246 m) 247 MODES=$OPTARG; 248 ;; 249 d) 250 DISKS=$OPTARG 251 PRINTABLE_DISKS=$(diskname_to_printable "$DISKS") 252 ;; 253 D) 254 IODEPTH=$OPTARG 255 ;; 256 a) 257 SEQ=2 258 ;; 259 B) 260 echo "exec_prerun=$OPTARG" >> $TEMPLATE 261 ;; 262 A) 263 echo "exec_postrun=$OPTARG" >> $TEMPLATE 264 ;; 265 z) 266 FSIZE=$OPTARG 267 echo "size=$FSIZE" >> $TEMPLATE 268 ;; 269 \?) 270 echo "Invalid option: -$OPTARG" >&2 271 ;; 272 esac 273done 274 275if [ "$SEQ" = "-1" ]; then 276 SEQ=0 277fi 278 279SHORT_HOSTNAME=$(hostname -s) 280case $SEQ in 281 2) 282 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-all-$MODES-$PRINTABLE_DISKS.fio 283 ;; 284 285 1) 286 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-sequential-$MODES-$PRINTABLE_DISKS.fio 287 ;; 288 0) 289 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-parallel-$MODES-$PRINTABLE_DISKS.fio 290 ;; 291esac 292 293if [ -z "$DISKS" ]; then 294 echo "Missing DISKS !" 295 echo "Please read the help !" 296 show_help 297 exit 1 298fi 299 300} 301 302check_mode_order() { 303FOUND_WRITE="NO" 304CAUSE="You are reading data before writing them " 305 306# If no write occurs, let's show a different message 307echo $MODES | grep -q "write" 308if [ "$?" -ne 0 ]; then 309 CAUSE="You are reading data while never wrote them before" 310fi 311 312for mode in $(echo $MODES | tr "," " "); do 313 echo $mode | grep -q write 314 if [ "$?" -eq 0 ]; then 315 FOUND_WRITE="YES" 316 fi 317 echo $mode | grep -q "read" 318 if [ "$?" -eq 0 ]; then 319 if [ "$FOUND_WRITE" = "NO" ]; then 320 echo "###############################################################" 321 echo "# Warning : $CAUSE#" 322 echo "# On some storage devices, this could lead to invalid results #" 323 echo "# #" 324 echo "# Press Ctrl-C to adjust pattern order if you have doubts #" 325 echo "# Or Wait 5 seconds before the file will be created #" 326 echo "###############################################################" 327 sleep 5 328 # No need to try showing the message more than one time 329 return 330 fi 331 fi 332done 333} 334 335 336########## MAIN 337gen_template 338parse_cmdline "$@" 339finish_template 340check_mode_order 341 342echo "Generating $OUTFILE" 343cp -f $TEMPLATE $OUTFILE 344echo >> $OUTFILE 345 346for BLK_SIZE in $(echo $BLOCK_SIZE | tr "," " "); do 347 gen_fio 348done 349ETA_H=$(($ETA / 3600)) 350ETA_M=$((($ETA - ($ETA_H*3600)) / 60)) 351if [ "$ETA" = "0" ]; then 352 echo "Cannot estimate ETA as RUNTIME=0" 353else 354 echo "Estimated Time = $ETA seconds : $ETA_H hour $ETA_M minutes" 355fi 356