I've decided to make some charts from the output of the iostat
command. Just for fun =)
The idea is simple: a script named `report.sh` runs periodically in background(via `cron`) collecting `iostat` data into $REPORT_DIR/$param/$device
files, where
$REPORT_DIR
is a directory with the reports;$param
is the `iostat` parameter name;$device
is a device name from the same `iostat` output.
When we need to update the charts, we call `plot.sh`.
Required components
- Bash
iostat
utility- Gnuplot
Scripts
util.sh#!/bin/bash - # $1 - Optional error message. function die { [ $# -gt 0 ] && echo >&2 $1 exit 1 } function warning { echo >&2 "Warning: $1" } function getSafeFilename { r=${1//\//_} echo ${r/\%/} } #vim: ts=2 sts=2 sw=2 etreport.sh
#!/bin/bash - # Generates iostat data files for processing with the GNU plot utility. HEADER= TIME_FORMAT="%Y/%m/%d/%H:%M" TIME=$(date "+$TIME_FORMAT") DIR=$(cd $(dirname "$0"); pwd) REPORT_DIR="${DIR}/r" ##################################################################### # Functions source $DIR/util.sh # $1 - exit code function usage { echo " Generates iostat data files for processing with the GNU plot utility(plot.sh). Usage: $0 OPTIONS OPTIONS: -h, --help Display help message -t, --time-format Time format. Default: ${TIME_FORMAT}. -i, --report-dir Directory with reports. Default: ${REPORT_DIR}. " exit $1 } ##################################################################### # Parsing CLI options OPTS=$(getopt -o t:i:h -l time-format:,report-dir:,help -- "$@") [ $? -eq 0 ] || usage 1 eval set -- "$OPTS" while true do case "$1" in -t|--time-format) TIME_FORMAT="$2" shift 2;; -i|--report-dir) REPORT_DIR="$2" shift 2;; -h|--help) usage 0 shift;; --) shift break;; ?) usage 1;; *) die "Internal error: failed to parse CLI args" esac done echo TIME_FORMAT: $TIME_FORMAT REPORT_DIR: $REPORT_DIR ##################################################################### mkdir -p $REPORT_DIR [ $? -eq 0 ] || die "Report directory '$REPORT_DIR' is inaccessible" iostat -dpxk | while read -r line do if [[ -z $HEADER && $line == Device* ]]; then HEADER=( $line ) elif [[ -n $HEADER ]]; then [[ -z $line ]] && continue columns=( $line ) [[ ${#HEADER[@]} != ${#columns[@]} ]] && continue i=1 for h in ${HEADER[@]:1} do param_report_dir="${REPORT_DIR}/"$(getSafeFilename $h) mkdir -p $param_report_dir [ $? -eq 0 ] || die "Parameter directory '$param_report_dir' is inaccessible" # $REPORT_DIR/param/device echo $TIME$'\t'${columns[$i]} >> $param_report_dir'/'${columns[0]} (( ++i )) done fi done echo 'Done'plot.sh
#!/bin/bash - # Creates visual representation of data files generated by report.sh TIME_FORMAT="%Y/%m/%d/%H:%M" # Report files location $REPORT_DIR/param/device DIR=$(cd $(dirname "$0"); pwd) REPORT_DIR="${DIR}/r" OUTPUT_DIR="${DIR}/o" XRANGE= PLOT_TERMINAL="png" ##################################################################### # Functions source $DIR/util.sh function getPlots { param_path="$1" find $param_path/ -maxdepth 1 -mindepth 1 -type f | while read -r device_path; do # $device_path = /*/*/.../param/device device=$(basename $device_path) printf "'%s' using 1:2 t '%s' with lp pt 5, " \ "$device_path" $device printf "'%s' using 1:2 notitle with impulses, " \ "$device_path" done } # Returns default value for GNU plot xrange function getXRange { y=$(date +%Y) m=$(date +%m) (( m -= 1 )) if [ $m -lt 1 ]; then m=1 (( y -= 1 )) fi d=$(date "+$TIME_FORMAT") echo "[ '$y/$m/1/00:00' : '$d' ]" } # $1 - exit code function usage { echo " Creates charts from files generated by report.sh Usage: $0 OPTIONS OPTIONS: -h, --help Display help message -x, --xrange GNU Plot xrange value. Default: $(getXRange). -t, --time-format Time format. Default: ${TIME_FORMAT}. -i, --report-dir Directory with reports. Default: ${REPORT_DIR}. -o, --output-dir Output directory. Default: ${OUTPUT_DIR}. -f, --term GNU Plot terminal. Default: ${PLOT_TERMINAL}. See 'gnuplot set terminal'. " exit $1 } ##################################################################### # Parsing CLI options OPTS=$(getopt -o x:t:i:o:f:h -l xrange:,time-format:,report-dir:,output-dir:,term:,help -- "$@") [ $? -eq 0 ] || usage 1 eval set -- "$OPTS" while true do case "$1" in -x|--xrange) XRANGE="$2" shift 2;; -t|--time-format) TIME_FORMAT="$2" shift 2;; -i|--report-dir) REPORT_DIR="$2" shift 2;; -o|--output-dir) OUTPUT_DIR="$2" shift 2;; -f|--term) PLOT_TERMINAL="$2" shift 2;; -h|--help) usage 0 shift;; --) shift break;; ?) usage 1;; *) die "Internal error: failed to parse CLI args" esac done echo XRANGE: $XRANGE TIME_FORMAT: $TIME_FORMAT OUTPUT_DIR: $OUTPUT_DIR REPORT_DIR: $REPORT_DIR ##################################################################### [ -d $REPORT_DIR ] || die "$REPORT_DIR doesn't exist" find $REPORT_DIR/ -maxdepth 1 -mindepth 1 -type d | while read -r param_path do # $param_path = /*/*/.../param param=$(basename $param_path) plots=$(getPlots "$param_path") plots=${plots/%, /} mkdir -p $OUTPUT_DIR [ $? -eq 0 ] || die "Output directory '$OUTPUT_DIR' is inaccessible" outfile="${OUTPUT_DIR}/${param}.${PLOT_TERMINAL}" [[ -z $XRANGE ]] && XRANGE="$(getXRange)" gnuplot <<EOS TIME_FORMAT = "$TIME_FORMAT" set terminal $PLOT_TERMINAL set grid nopolar set xtics rotate set xlabel "Date/Time" set ylabel "Value" set yrange [ 0 : ] set format x TIME_FORMAT timedate set timefmt TIME_FORMAT set xdata time set xrange $XRANGE set output "$outfile" plot $plots EOS done echo 'Done'