#!/bin/bash # # Copyright (C) 2013 eNovance SAS # Author: Erwan Velu # # 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= 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 32 -d disk1[,disk2,disk3,..] : Run the tests on the selected disks Separated each disk with a comma Disk name shall be "sdxx", /dev/ shall NOT be used here -r seconds : Time in seconds per benchmark 0 means till the end of the device Default is 300 seconds -b blocksize[,blocksize1, ...] : The blocksizes to test under fio format (4k, 1m, ...) Separated each blocksize with a comma Default is 4k -m mode1,[mode2,mode3, ...] : Define the fio IO profile to use like read, write, randread, randwrite Default is "write,randwrite,read,randread" -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 Example: $PROG -d sdb,sdc,sdd,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 } gen_template() { cat >$TEMPLATE << EOF [global] ioengine=libaio iodepth=$IODEPTH invalidate=1 ramp_time=5 EOF if [ "$RUNTIME" != "0" ]; then cat >>$TEMPLATE << EOF runtime=$RUNTIME time_based EOF fi if [ "$CACHED_IO" = "FALSE" ]; then cat >>$TEMPLATE << EOF direct=1 EOF fi } gen_seq_suite() { TYPE=$1 cat >> $OUTFILE << EOF [$TYPE-$disk-$BLK_SIZE-seq] stonewall bs=$BLK_SIZE filename=/dev/$disk rw=$TYPE write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$disk-$TYPE-seq.results write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$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" done done } gen_para_suite() { TYPE=$1 NEED_WALL=$2 D=0 for disk in $(echo $DISKS | tr "," " "); do cat >> $OUTFILE << EOF [$TYPE-$disk-$BLK_SIZE-para] bs=$BLK_SIZE EOF if [ "$D" = 0 ]; then echo "stonewall" >> $OUTFILE D=1 fi cat >> $OUTFILE << EOF filename=/dev/$disk rw=$TYPE write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$disk-$TYPE-para.results write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$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:D:" 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 ;; D) IODEPTH=$OPTARG ;; a) SEQ=2 ;; \?) 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-$DISKS.fio ;; 1) OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-sequential-$MODES-$DISKS.fio ;; 0) OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-parallel-$MODES-$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 parse_cmdline $@ check_mode_order gen_template 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