#!/usr/bin/env bash # # Copyright (C) 2013 eNovance SAS <licensing@enovance.com> # Author: Erwan Velu <erwan@enovance.com> # # The license below covers all files distributed with fio unless otherwise # noted in the file itself. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA BLK_SIZE= BLOCK_SIZE=4k SEQ=-1 TEMPLATE=/tmp/template.fio OUTFILE= DISKS= PRINTABLE_DISKS= RUNTIME=300 ETA=0 MODES="write,randwrite,read,randread" SHORT_HOSTNAME= CACHED_IO="FALSE" PREFIX="" PREFIX_FILENAME="" IODEPTH=1 show_help() { PROG=$(basename $0) echo "usage of $PROG:" cat << EOF -h : Show this help & exit -c : Enable cached-based IOs Disabled by default -a : Run sequential test then parallel one Disabled by default -s : Run sequential test (default value) one test after another then one disk after another Disabled by default -p : Run parallel test one test after anoter but all disks at the same time Enabled by default -D iodepth : Run with the specified iodepth Default is $IODEPTH -d disk1[,disk2,disk3,..] : Run the tests on the selected disks Separated each disk with a comma -z filesize : Specify the working file size, if you are passing filepaths to -d Disabled by default -r seconds : Time in seconds per benchmark 0 means till the end of the device Default is $RUNTIME seconds -b blocksize[,blocksize1, ...] : The blocksizes to test under fio format (4k, 1m, ...) Separated each blocksize with a comma Default is $BLOCK_SIZE -m mode1,[mode2,mode3, ...] : Define the fio IO profile to use like read, write, randread, randwrite Default is "$MODES" -x prefix : Add a prefix to the fio filename Useful to let a context associated with the file If the prefix features a / (slash), prefix will be considered as a directory -A cmd_to_run : System command to run after each job (exec_postrun in fio) -B cmd_to_run : System command to run before each job (exec_prerun in fio) Example: $PROG -d /dev/sdb,/dev/sdc,/dev/sdd,/dev/sde -a -b 4k,128k,1m -r 100 -a -x dellr720-day2/ Will generate an fio file that will run - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests ETA ~ 4 tests * 4 disks * 100 seconds - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests ETA ~ 4 tests * 4 disks * 100 seconds - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests ETA ~ 4 tests * 4 disks * 100 seconds - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests ETA ~ 4 tests * 100 seconds - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests ETA ~ 4 tests * 100 seconds - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests ETA ~ 4 tests * 100 seconds Generating dellr720-day2/localhost-4k,128k,1m-all-write,randwrite,read,randread-sdb,sdc,sdd,sde.fio Estimated Time = 6000 seconds : 1 hour 40 minutes EOF } finish_template() { echo "iodepth=$IODEPTH" >> $TEMPLATE if [ "$RUNTIME" != "0" ]; then echo "runtime=$RUNTIME" >> $TEMPLATE echo "time_based" >> $TEMPLATE fi if [ "$CACHED_IO" = "FALSE" ]; then echo "direct=1" >> $TEMPLATE fi } diskname_to_printable() { COUNT=0 for disk in $(echo $@ | tr "," " "); do R=$(basename $disk | sed 's|/|_|g') COUNT=$(($COUNT + 1)) if [ $COUNT -eq 1 ]; then P="$R" else P="$P,$R" fi done echo $P } gen_template() { cat >$TEMPLATE << EOF [global] ioengine=libaio invalidate=1 ramp_time=5 EOF } gen_seq_suite() { TYPE=$1 disk=$2 PRINTABLE_DISK=$(diskname_to_printable $disk) cat >> $OUTFILE << EOF [$TYPE-$PRINTABLE_DISK-$BLK_SIZE-seq] stonewall bs=$BLK_SIZE filename=$disk rw=$TYPE write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results EOF ETA=$(($ETA + $RUNTIME)) } gen_seq_fio() { for disk in $(echo $DISKS | tr "," " "); do for mode in $(echo $MODES | tr "," " "); do gen_seq_suite "$mode" "$disk" done done } gen_para_suite() { TYPE=$1 NEED_WALL=$2 D=0 for disk in $(echo $DISKS | tr "," " "); do PRINTABLE_DISK=$(diskname_to_printable $disk) cat >> $OUTFILE << EOF [$TYPE-$PRINTABLE_DISK-$BLK_SIZE-para] bs=$BLK_SIZE EOF if [ "$D" = 0 ]; then echo "stonewall" >> $OUTFILE D=1 fi cat >> $OUTFILE << EOF filename=$disk rw=$TYPE write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results EOF done ETA=$(($ETA + $RUNTIME)) echo >> $OUTFILE } gen_para_fio() { for mode in $(echo $MODES | tr "," " "); do gen_para_suite "$mode" done } gen_fio() { case $SEQ in 2) gen_seq_fio gen_para_fio ;; 1) gen_seq_fio ;; 0) gen_para_fio ;; esac } parse_cmdline() { while getopts "hacpsd:b:r:m:x:z:D:A:B:" opt; do case $opt in h) show_help exit 0 ;; b) BLOCK_SIZE=$OPTARG ;; c) CACHED_IO="TRUE" ;; s) if [ "$SEQ" = "-1" ]; then SEQ=1 fi ;; x) PREFIX=$OPTARG echo "$PREFIX" | grep -q "/" if [ "$?" -eq 0 ]; then mkdir -p $PREFIX # No need to keep the prefix for the log files # we do have a directory for that PREFIX_FILENAME="" else # We need to keep the prefix for the log files PREFIX_FILENAME=$PREFIX fi ;; r) RUNTIME=$OPTARG ;; p) if [ "$SEQ" = "-1" ]; then SEQ=0 fi ;; m) MODES=$OPTARG; ;; d) DISKS=$OPTARG PRINTABLE_DISKS=$(diskname_to_printable "$DISKS") ;; D) IODEPTH=$OPTARG ;; a) SEQ=2 ;; B) echo "exec_prerun=$OPTARG" >> $TEMPLATE ;; A) echo "exec_postrun=$OPTARG" >> $TEMPLATE ;; z) FSIZE=$OPTARG echo "size=$FSIZE" >> $TEMPLATE ;; \?) echo "Invalid option: -$OPTARG" >&2 ;; esac done if [ "$SEQ" = "-1" ]; then SEQ=0 fi SHORT_HOSTNAME=$(hostname -s) case $SEQ in 2) OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-all-$MODES-$PRINTABLE_DISKS.fio ;; 1) OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-sequential-$MODES-$PRINTABLE_DISKS.fio ;; 0) OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-parallel-$MODES-$PRINTABLE_DISKS.fio ;; esac if [ -z "$DISKS" ]; then echo "Missing DISKS !" echo "Please read the help !" show_help exit 1 fi } check_mode_order() { FOUND_WRITE="NO" CAUSE="You are reading data before writing them " # If no write occurs, let's show a different message echo $MODES | grep -q "write" if [ "$?" -ne 0 ]; then CAUSE="You are reading data while never wrote them before" fi for mode in $(echo $MODES | tr "," " "); do echo $mode | grep -q write if [ "$?" -eq 0 ]; then FOUND_WRITE="YES" fi echo $mode | grep -q "read" if [ "$?" -eq 0 ]; then if [ "$FOUND_WRITE" = "NO" ]; then echo "###############################################################" echo "# Warning : $CAUSE#" echo "# On some storage devices, this could lead to invalid results #" echo "# #" echo "# Press Ctrl-C to adjust pattern order if you have doubts #" echo "# Or Wait 5 seconds before the file will be created #" echo "###############################################################" sleep 5 # No need to try showing the message more than one time return fi fi done } ########## MAIN gen_template parse_cmdline "$@" finish_template check_mode_order echo "Generating $OUTFILE" cp -f $TEMPLATE $OUTFILE echo >> $OUTFILE for BLK_SIZE in $(echo $BLOCK_SIZE | tr "," " "); do gen_fio done ETA_H=$(($ETA / 3600)) ETA_M=$((($ETA - ($ETA_H*3600)) / 60)) if [ "$ETA" = "0" ]; then echo "Cannot estimate ETA as RUNTIME=0" else echo "Estimated Time = $ETA seconds : $ETA_H hour $ETA_M minutes" fi