#!/bin/bash
#
# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
#
# v0.1, 2008-10-15
# This script aims at combining tshark and awk to collect quick and useful
# statistics about NFSv3 calls from a capture file.
#
# Author: Fabio Olive Leite <fleite@redhat.com>
#
# v0.2, 2010-10-13 - Harshula Jayasuriya <harshula@redhat.com>
# Just added a Total time taken column.
#
# v0.3, 2013-06-08 - Niels de Vos <ndevos@redhat.com>
# Add support for NFSv4 opcodes, display nfs.main_opcode only.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License, version 2. A copy of the license
# can be obtained from http://www.gnu.org/.
#
# TODO: Supporting V2, V3 and V4 calls in separate sections of the output
# TODO: Calculate percentage of each call type relative to total call count

die() {
	echo $*
	exit 1
}

[ $# -eq 1 ] || die "Usage: $0 pcap_file"

[ -r $1 ] || die "Cannot read $1!"

[ -x /usr/bin/which ] || die 'Need /usr/bin/which to find my dependencies!'

TSHARK=$(/usr/bin/which tshark)
[ -n "$TSHARK" ] || die 'Need tshark, please install tshark (or maybe wireshark)'
TSHARK_VER=$(tshark -v | awk '/TShark/ {print $2}' | cut -d . -f 2)
if [ $TSHARK_VER -lt 8 ]; then
    TWO_PASS_FILTER=""
else
    TWO_PASS_FILTER="-2"
fi

AWK=$(/usr/bin/which awk)
[ -n "$AWK" ] || die 'Need AWK, please install it!'

PCAP_FILE=${1}

#echo tshark: $TSHARK
#echo awk: $AWK
#echo File: $PCAP_FILE

NFS_NAMES_V3='
	name[0] =  "NULL";
	name[1] =  "GETATTR";
	name[2] =  "SETATTR";
	name[3] =  "LOOKUP";
	name[4] =  "ACCESS";
	name[5] =  "READLINK";
	name[6] =  "READ";
	name[7] =  "WRITE";
	name[8] =  "CREATE";
	name[9] =  "MKDIR";
	name[10] = "SYMLINK";
	name[11] = "MKNOD";
	name[12] = "REMOVE";
	name[13] = "RMDIR";
	name[14] = "RENAME";
	name[15] = "LINK";
	name[16] = "READDIR";
	name[17] = "READDIRPLUS";
	name[18] = "FSSTAT";
	name[19] = "FSINFO";
	name[20] = "PATHCONF";
	name[21] = "COMMIT";
'

NFS_NAMES_V4='
	name[0] =  "NULL";
	name[1] =  "COMPOUND";
	name[2] =  "[RESERVED-2]";
	name[3] =  "ACCESS";
	name[4] =  "CLOSE";
	name[5] =  "COMMIT";
	name[6] =  "CREATE";
	name[7] =  "DELEPURGE";
	name[8] =  "DELEGRETURN";
	name[9] =  "GETATTR";
	name[10] = "GETFH";
	name[11] = "LINK";
	name[12] = "LOCK";
	name[13] = "LOCKT";
	name[14] = "LOCKU";
	name[15] = "LOOKUP";
	name[16] = "LOOKUPP";
	name[17] = "NVERIFY";
	name[18] = "OPEN";
	name[19] = "OPENATTR";
	name[20] = "OPEN_CONFIRM";
	name[21] = "OPEN_DOWNGRADE";
	name[22] = "PUTFH";
	name[23] = "PUTPUBFH";
	name[24] = "PUTROOTFH";
	name[25] = "READ";
	name[26] = "READDIR";
	name[27] = "READLINK";
	name[28] = "REMOVE";
	name[29] = "RENAME";
	name[30] = "RENEW";
	name[31] = "RESTOREFH";
	name[32] = "SAVEFH";
	name[33] = "SECINFO";
	name[34] = "SETATTR";
	name[35] = "SETCLIENTID";
	name[36] = "SETCLIENTID_CONFIRM";
	name[37] = "VERIFY";
	name[38] = "WRITE";
	name[39] = "RELEASE_LOCKOWNER";
	name[10044] = "ILLEGAL";
'

calculate_stats() {
	local NFS_VERSION=$1
	local NFS_NAMES="NFS_NAMES_V${NFS_VERSION}"

	local PROC_FIELD=''

	case ${NFS_VERSION} in
		3)
			PROC_FIELD='nfs.procedure_v3'
			;;
		4)
			PROC_FIELD='nfs.main_opcode'
			;;
		*)
			echo "NFS version ${NFS_VERSION} not suppored."
			return
			;;
	esac

	$TSHARK -r ${PCAP_FILE} $TWO_PASS_FILTER -R "nfs.procedure_v${NFS_VERSION} and rpc.time" \
		-e ${PROC_FIELD} -e rpc.time -T fields | $AWK "
	BEGIN {
		nfs_version = ${NFS_VERSION};

		${!NFS_NAMES}

		min_summary = -1;
		max_summary = 0;
	};"'

	{
		count[$1]++;
		count_summary++;
		sum[$1] += $2;
		sum_summary += $2
		if ($1 in min) {
			if ($2 < min[$1]) {
				min[$1] = $2;
			};
		} else {
			min[$1] = $2;
		}
		if ($2 < min_summary || min_summary == -1) { min_summary = $2 };
		if ($2 > max[$1]) { max[$1] = $2 };
		if ($2 > max_summary) { max_summary = $2 };
	};

	END {
		sep = "----------";
		printf "NFSv%d Call    Count     Min       Avg       Max       Total\n",
			nfs_version;
		for (op = 0; op < length(name); op++) {
			if (op in count) {
				printf "%-12s  %8d  %.6f  %.6f  %.6f  %.6f\n",
					name[op], count[op], min[op],
					sum[op]/count[op], max[op], sum[op];
			}
		}
		if (count_summary > 0) {
			printf "%-12s  %8d  %.6f  %.6f  %.6f  %.6f\n",
				"summary", count_summary, min_summary,
				sum_summary/count_summary, max_summary, sum_summary;
		}
	};
	'
}

for NFS_VERSION in $($TSHARK -r ${PCAP_FILE} -z rpc,programs -q | awk '/^NFS\(/ { print $2 }')
do
	calculate_stats ${NFS_VERSION}
done

