#! /bin/sh
# PCP QA Test No. 635
# Check Linux network.interface metrics
#
# Copyright (c) 1995-2002 Silicon Graphics, Inc.  All Rights Reserved.
#

seq=`basename $0`
echo "QA output created by $seq"

# get standard environment, filters and checks
. ./common.product
. ./common.filter
. ./common.check

[ $PCP_PLATFORM = linux ] || _notrun "Depends on Linux ip -s link (or netstat -ai) output format"

status=0	# success is the default!
trap "rm -f $tmp.*; exit \$status" 0 1 2 3 15

# sample output
#kenj-pc 68% netstat -ai
#Kernel Interface table
#Iface   MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
#eth0   1500   0     5212      0      0      0     3080      2      0      0 BRU
#lo     3924   0   164293      0      0      0   164293      0      0      0 LRU
#
# vm24 i686 openSUSE 13.1
#Kernel Interface table
#Iface   MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
#ens3   1500   0     5174      0      0      1     2731      0      0      0 BMRU
#lo    65536   0     2524      0      0      0     2524      0      0      0 LRU
#
#
# Grab the netstat statistics.  Get name and mtu:
#
_netstat_i_1()
{
    netstat -ai \
    | tee -a $seq_full \
    | sed -e 's/\*/ /' \
    | $PCP_AWK_PROG '
BEGIN	{ iface = -1; mtu = -1; hdr = 0 }
/Kernel Interface table/	{ next }
hdr==0	{ for (i = 0; i <= NF; i++) {
		if ($i == "Iface") iface = i
		if ($i == "MTU") mtu = i
	  }
	  if (iface == -1 || mtu == -1) {
	    print "Botch: bad iface/mtu header line:",iface,mtu
	    exit(0);
	  }
	  hdr = 1
	  next
	}
hdr==1	{ print $iface, $mtu }'
}

# Then recv packets, recv errors, recv drops, xmit packets, xmit errors
# and xmit drops.
#
#Iface     MTU Met   RX-OK RX-ERR RX-DRP RX-OVR   TX-OK TX-ERR TX-DRP TX-OVR Flg
#eth0       1500   0 12263928      0      0      0 3273749      0      0      0 BMRU
#lo        16436   0 3489788      0      0      0 3489788      0      0      0 LRU
#
# Note1: fifo metric from /proc/net/dev is ?-OVR visible in netstat
# Note2: "Met" column may not be there!
#
_netstat_i_2()
{
    netstat -ai \
    tee -a $seq_full \
    | sed \
	-e 's/\*/ /' \
    | $PCP_AWK_PROG '
BEGIN	{ rx = -1; rx_err = -1; rx_drp = -1; rx_ovr = -1
	  tx = -1; tx_err = -1; tx_drp = -1; tx_ovr = -1
	  hdr = 0
	}
/Kernel Interface table/	{ next }
hdr==0	{ for (i = 0; i <= NF; i++) {
		if ($i == "RX-OK") rx = i
		if ($i == "RX-ERR") rx_err = i
		if ($i == "RX-DRP") rx_drp = i
		if ($i == "RX-OVR") rx_ovr = i
		if ($i == "TX-OK") tx = i
		if ($i == "TX-ERR") tx_err = i
		if ($i == "TX-DRP") tx_drp = i
		if ($i == "TX-OVR") tx_ovr = i
	  }
	  if (rx == -1 || rx_err == -1 || rx_drp == -1 || rx_ovr == -1 ||
	      tx == -1 || tx_err == -1 || tx_drp == -1 || tx_ovr == -1) {
	    print "Botch: bad rx/txheader line:",rx,rx_err,rx_drp,rx_ovr,tx,tx_err,tx_drp,tx_ovr
	    exit(0);
	  }
	  hdr = 1
	  next
	}
hdr==1	{ print $rx, $rx_err, $rx_drp, $rx_ovr, $tx, $tx_err, $tx_drp, $tx_ovr }'
}

# Alternate (preferred) ip -s link output
# 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
#     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
#     RX:  bytes packets errors dropped  missed   mcast           
#         421068    6123      0       0       0       0 
#     TX:  bytes packets errors dropped carrier collsns           
#         421068    6123      0       0       0       0 
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
#     link/ether 52:54:00:8f:c9:5e brd ff:ff:ff:ff:ff:ff
#     RX:  bytes packets errors dropped  missed   mcast           
#       55981432  211764      0   10129       0       0 
#     TX:  bytes packets errors dropped carrier collsns           
#        5719767   56028      0       0       0       0 
#
_ip_s()
{
    ip -s link \
    | tee -a $seq_full \
    | $PCP_AWK_PROG '
$1 ~ /^[0-9][0-9]*:/	{ sub(/:/,"",$2)
			  iface = $2
			  want = 0
			  mtu = 1500
			  for (i = 3; i <= NF; i++) {
			    if (want == 1) {
				mtu = $i
				break
			    }
			    if ($i == "mtu")
				want = 1
			  }
			  end_entry = NR + 5
			  data = 0
			  next
			}
$1 == "RX:"		{ data = 1	# receive data
			  next
			}
$1 == "TX:"		{ data = 2	# transmit data
			  next
			}
data == 1		{ printf "%s %d %d %d %d %d",iface,mtu,$2,$3,$4,$5
			  data = 0
			  next
			}
data == 2		{ printf " %d %d %d %d\n",$2,$3,$4,$5
			  data = 0
			  next
			}
'
}

# real QA test starts here
echo >$seq_full

# Get the list of network interfaces from ifconfig or ip.
# Need uniq because may have one interface appearing more than once
# (e.g. br1 and br1:avahi which become 2 br1 lines otherwise).
#
if which ifconfig >/dev/null 2>&1
then
    # ifconfig lines of interest are like
    # eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
    #
    ifconfig -a \
	| sed -n -e '/^[^ 	]/{
s/[: 	].*//
p
}' \
    | LC_COLLATE=POSIX sort \
    | uniq >$tmp.net.indom
elif which ip >/dev/null 2>&1
then
    # ip lines of interest are like
    # 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc ...
    #
    ip address \
	| sed -n -e '/^[^ 	]/{
s/^[0-9][0-9]*: //
s/[: 	].*//
p
}' \
    | LC_COLLATE=POSIX sort \
    | uniq >$tmp.net.indom
else
    _notrun "Neither ifconfig(1) nor ip(1), how do I get a list of network interfaces?"
fi

pminfo -f network.interface.total.drops \
    | $PCP_AWK_PROG '
	/ inst /	{ print $4 }
			{ next } ' \
    | tr -d '"]' \
    | LC_COLLATE=POSIX sort >$tmp.pcp.indom

if diff $tmp.pcp.indom $tmp.net.indom >$tmp.indom.diff
then
    :
else
    echo "Instance domains for network.interface.total.drops are different:"
    diff -u $tmp.pcp.indom $tmp.net.indom
    echo "PCP instance domain ..." >>$seq_full
    cat $tmp.pcp.indom >>$seq_full
    echo "netstat instance domain ..." >>$seq_full
    cat $tmp.net.indom >>$seq_full
    status=1
fi

# Metrics to test.  All of these should be counters.
#	out.qlength, out.qmax
# are excluded because they're instantaneous values.
#
cat >$tmp.metrics <<END
in.errors
in.packets
in.drops
in.fifo
out.errors
out.packets
out.drops
out.fifo
END

use_ip=false
which ip >/dev/null 2>&1 && use_ip=true

# first sample of network data
#
if $use_ip
then
    echo >>$seq_full
    echo "=== ip -s link for first counts ===" >>$seq_full
    _ip_s | LC_COLLATE=POSIX sort >$tmp.net.1
    echo "===" >>$seq_full
    cat $tmp.net.1 >>$seq_full
else
    echo >>$seq_full
    echo "=== first netstat for i/f name and mtu ===" >>$seq_full
    _netstat_i_1 >$tmp.net.1a
    echo "===" >>$seq_full
    cat $tmp.net.1a >>$seq_full

    echo >>$seq_full
    echo "=== netstat for first counts ===" >>$seq_full
    _netstat_i_2 >$tmp.net.1b
    echo "===" >>$seq_full
    cat $tmp.net.1b >>$seq_full

    # and stick 'em together (just lick and press)
    #
    paste $tmp.net.1a $tmp.net.1b \
	| LC_COLLATE=POSIX sort >$tmp.net.1
fi

# Now get the metrics
#
echo >>$seq_full
echo "=== pminfo ==" >>$seq_full
for m in `cat $tmp.metrics`
do
    pminfo -f network.interface.$m \
    | tee -a $seq_full \
    | $PCP_AWK_PROG '
	/ inst /	{ print $4, $6 }
			{ next } ' \
    | tr -d '"]' \
    | LC_COLLATE=POSIX sort >$tmp.vals.$m.1
done

otherhost=`./getpmcdhosts -L -n 1 2>$tmp.out`
[ -z "$otherhost" ] && _notrun `cat $tmp.out`

# Generate a bit of network traffic
#
$sudo ping -f -c 100 localhost >/dev/null 2>&1 &
$sudo ping -f -c 100 $otherhost >/dev/null 2>&1 &
sleep 3

for m in `cat $tmp.metrics`
do
    pminfo -f network.interface.$m \
	| $PCP_AWK_PROG '
	    / inst /	{ print $4, $6 }
			{ next } ' \
	| tr -d '"]' \
	| LC_COLLATE=POSIX sort >$tmp.vals.$m.2

    LC_COLLATE=POSIX join -1 1 -2 1 -o 1.2,2.2,1.1 -t' ' $tmp.vals.$m.1 $tmp.vals.$m.2 >$tmp.vals
    while read v1 v2 inst
    do
	if [ $v2 -lt $v1 ]
	then
	    echo "$m[\"$inst\"] decreased from $v1 to $v2!"
	    status=1
	fi
    done <$tmp.vals

done

# second sample of network data
#
if $use_ip
then
    echo >>$seq_full
    echo "=== ip -s link for second counts ===" >>$seq_full
    _ip_s | LC_COLLATE=POSIX sort >$tmp.net.2
    echo "===" >>$seq_full
    cat $tmp.net.2 >>$seq_full
else
    echo >>$seq_full
    echo "=== second netstat for i/f name and mtu ===" >>$seq_full
    _netstat_i_1 >$tmp.net.2a
    echo "===" >>$seq_full
    cat $tmp.net.2a >>$seq_full

    echo >>$seq_full
    echo "=== netstat for first counts ===" >>$seq_full
    _netstat_i_2 >$tmp.net.2b
    echo "===" >>$seq_full
    cat $tmp.net.2b >>$seq_full

    # and stick 'em together (just lick and press)
    #
    paste $tmp.net.2a $tmp.net.2b \
	| LC_COLLATE=POSIX sort >$tmp.net.2
fi

$PCP_AWK_PROG < $tmp.net.1 '
	{
	    print $1, $3 >"'$tmp.net.in.packets.1'"
	    print $1, $4 >"'$tmp.net.in.errors.1'"
	    print $1, $5 >"'$tmp.net.in.drops.1'"
	    print $1, $6 >"'$tmp.net.in.fifo.1'"
	    print $1, $7 >"'$tmp.net.out.packets.1'"
	    print $1, $8 >"'$tmp.net.out.errors.1'"
	    print $1, $9 >"'$tmp.net.out.drops.1'"
	    print $1, $10 >"'$tmp.net.out.fifo.1'"
	}'

$PCP_AWK_PROG < $tmp.net.2 '
	{
	    print $3 >"'$tmp.net.in.packets.2'"
	    print $4 >"'$tmp.net.in.errors.2'"
	    print $5 >"'$tmp.net.in.drops.2'"
	    print $6 >"'$tmp.net.in.fifo.2'"
	    print $7 >"'$tmp.net.out.packets.2'"
	    print $8 >"'$tmp.net.out.errors.2'"
	    print $9 >"'$tmp.net.out.drops.2'"
	    print $10 >"'$tmp.net.out.fifo.2'"
	}'

# Bring the two netstat snaps together and check the pminfo data falls
# within the netstat data.
#
for m in `cat $tmp.metrics`
do
    [ ! -f $tmp.net.$m.1 ] && continue

    echo "--- $tmp.net.$m.1 ---" >>$seq_full
    cat $tmp.net.$m.1 >>$seq_full
    echo "--- $tmp.net.$m.2 ---" >>$seq_full
    cat $tmp.net.$m.2 >>$seq_full
    echo "---" >>$seq_full
    paste $tmp.net.$m.1 $tmp.net.$m.2 >$tmp.net

    echo '' >>$seq_full
    echo "=== $m pminfo ===" >>$seq_full
    cat $tmp.vals.$m.2 >>$seq_full
    echo "--- $m netstat ---" >>$seq_full
    cat $tmp.net >>$seq_full
    LC_COLLATE=POSIX join -1 1 -2 1 -o 1.1,2.2,1.2,2.3 $tmp.vals.$m.2 $tmp.net >$tmp.all

    while read inst nv1 pcp nv2
    do
	if src/compare $nv1 $pcp $nv2
	then
	    :
	else
	    echo "network.interface.$m[\"$inst\"] value $pcp not in range $nv1..$nv2"
	    status=1
	fi
    done <$tmp.all
done

exit
