#!/bin/bash
#
# Script to retrieve data from TP-Link TD8816 ADSL modem for cacti
# 
# 20120611 MV: Fixed us/ds speed working for both interleaved and fast
# 20120322 MV: Started split out of statistics. Added line uptime, max
#              attainable downstream and upstream, added up- and downstream
#              bits per bin (32 groups per channel), added heap size and free.
#              Fixed bug in interface stats.
# 20120320 MV: Moved most of the parsing over to new structure.
# 20120319 MV: Initial changeover to different collection and parse strategy.
# 20120316 MV: Added initial stats for ethernet and atm interface. Split
#              between show and collect statistics, because cacti poller 
#              seems to run the script way too often. Fixed output bugs.
# 20120315 MV: Initial version

MODEM=HOSTNAME      # Hostname of the modem
PROMPT=SYSTEMPROMPT # Hostname the modem uses on the commandline
PASSWORD=PASSWORD   # Password to login to the modem

VERSION=0.3
DATAFILE=/tmp/tp-link.data
CONVERSE=/tmp/tp-link.converse

collect()
{
   expect <<EOF
	spawn /usr/bin/telnet $MODEM
	expect assword:
	send ${PASSWORD}\n
	expect ${PROMPT}>
	send "sys cpu display\n"
	expect ${PROMPT}>
	send "w ad perfdata\n"
	expect ${PROMPT}>
	send "wan adsl chandata far\n"
	expect ${PROMPT}>
	send "wan adsl linedata far\n"
	expect ${PROMPT}>
	send "wan adsl linedata near\n"
	expect ${PROMPT}>
	send "show channel disp 0\n"
	expect ${PROMPT}>
	send "show channel disp 1\n"
	expect ${PROMPT}>
	send "show channel disp 2\n"
	expect ${PROMPT}>
	send "show channel disp 3\n"
	expect ${PROMPT}>
	send "show channel disp 4\n"
	expect ${PROMPT}>
	send "show channel disp 5\n"
	expect ${PROMPT}>
	send "show channel disp 6\n"
	expect ${PROMPT}>
	send "show channel disp 7\n"
	expect ${PROMPT}>
	send "show channel disp 8\n"
	expect ${PROMPT}>
	send "wan dmt2 show rparams\n"
	expect ${PROMPT}>
	send "wan dmt2 show cparams\n"
	expect ${PROMPT}>
	send "sys mbufc disp\n"
	expect ${PROMPT}>
	send "sys ver\n"
	expect ${PROMPT}>
	send exit\r
	expect "by foreign host."
EOF
}

parse_system()
{
   cat $CONVERSE | gawk -v PROMPT=$PROMPT '
	BEGIN { state=0; ticks=0; count=0; }
	$0 ~ "^"PROMPT">" { if (state == 2) {
				printf("cpu:%02.2f"), 100 - (ticks/(baseticks*count)*100);}
			if (state == 10) {
				printf(" heapsize:%d heapfree:%d", heapsize,heapfree);}
			if (state == 11) {
				printf(" sysuptime:%d", sysuptime);}
			state = 0; 
	}
	/^CPU usage/ { state = 1; }
	/^ / { if (state == 2) {
			if ($2 != "") { ticks+=$2; count++;};
			if ($5 != "") { ticks+=$5; count++;};
			if ($8 != "") { ticks+=$8; count++;};
			if ($11 != "") {ticks+=$11; count++;}; }
		}
	/^  baseline/	{ baseticks=$2 }
	/^ sec /	{ if (state == 1)  state = 2; };

	/^heap size/	{ state=10; heapsize=$3 }
	/^Heap usage/	{ state=10; heapfree=$3 }

	/^ system up time/	{ state=11; split($4,a,":"); sysuptime=a[1]*3600 + a[2] * 60 + a[3]}

	END { printf("\n"); }'
}

parse_line()
{
   cat $CONVERSE | gawk -v PROMPT=$PROMPT '
	BEGIN { state=0; }
	$0 ~ "^"PROMPT">" { if (state == 3) {
				printf("nefec:%d fefec:%d necrc:%d fecrc:%d nehec:%d fehec:%d uptime:%d", nefec,fefec,necrc,fecrc,nehec,fehec,uptime); };
			 if (state == 4) {
				printf(" us:%d ds:%d", us*1000,ds*1000); };
			 if (state == 5) {
				printf(" ussnr:%.1f dspwr:%.1f usattn:%.1f", ussnr, dspwr, usattn);};
			 if (state == 6) {
				printf(" dssnr:%.1f uspwr:%.1f dsattn:%.1f", dssnr, uspwr, dsattn)}
			 if (state == 8) {
				printf(" dsmax:%d", dsmax*1000);
				for (i=0; i<32;i++)
					printf(" dsbin%d:%d", i, dsbin[i]);}
			 if (state == 9) {
				printf(" usmax:%d", usmax*1000);
				for (i=0; i<32;i++)
					printf(" usbin%d:%d", i, usbin[i]);}
			state = 0; 
	}

	/^near-end FEC/ { state = 3; nefec+=$5} 
	/^near-end CRC/ { necrc+=$5 }
	/^near-end HEC/ { nehec+=$5 }
	/^far-end FEC/  { fefec+=$5 }
	/^far-end CRC/  { fecrc+=$5 }
	/^far-end HEC/  { fehec+=$5 }
	/^ADSL uptime/  { split($3,a,":"); uptime=a[1]*3600 + a[2] * 60 + a[3]}

	/^near-end .* channel bit rate/ { state=4; if (ds==0) ds=$6}
	/^far-end .* channel bit rate/ {if (us==0) us=$6}

	/^ATTNDRds/ { state=8; dsmax=$3}
	/^DS Bit Assign/ { dsfound=1; }
	/^-------------/ { if (dsfound == 1) { dsfound=2 } }
        /^[0-9 ]+/ { if (dsfound > 1) {
                   	if (dsfound < 4) { dsfound++ }
                   	else { split($0,a);
				for (i=1;i<17;i++) {
					dsbin[dsfound-4]+=a[i];}
				dsfound++;} } }
	/^DS Gain Assign/ { dsfound=0; }

	/^ATTNDRus/ { state=9; usmax=$3}
	/^US Bit Assign/ { usfound=1; }
	/^-------------/ { if (usfound == 1) { usfound=2 } }
        /^[0-9 ]+/ { if (usfound > 1) {
			split($0,a," ");
			for (i=1;i<17;i++) {
                   		usbin[i-1+(usfound-2)*16] = a[i]} usfound++ } }
	/^US Gain Assign/ { usfound=0; }

	/^noise margin upstream/ { state=5; ussnr=$4 } 
	/^output power downstream/ { dspwr=$4 } 
	/^attenuation upstream/ { usattn=$3 }

	/^noise margin downstream/ { state=6; dssnr=$4 } 
	/^output power upstream/ { uspwr=$4 } 
	/^attenuation downstream/ { dsattn=$3 }

	END { printf("\n"); }'
}


parse_interfaces()
{
   cat $CONVERSE | gawk -v PROMPT=$PROMPT '
	BEGIN { state=0;}
	$0 ~ "^"PROMPT">" { if (state == 7) {
				printf("%sindisc:%d %sinerr:%d %sindrop:%d %sinoctet:%d %soutdisc:%d %souterr:%d %soutdrop:%d %soutoctet:%d ",chan,indisc,chan,inerr,chan,indrop,chan,inoctet,chan,outdisc,chan,outerr,chan,outdrop,chan,outoctet)}
			state = 0; 
	}
	/^---/ { state=7; chan=$3 } /^inPkt/ { gsub(/ /,""); split($0,a,","); indisc=substr(a[2],11); inerr=substr(a[3],9);} 
	/^inDrop/ { gsub(/ /,""); split($0,a,","); indrop=substr(a[1],8); inoctet=substr(a[2],9);} 
	/^outPtk/ { gsub(/ /,""); split($0,a,","); outdisc=substr(a[2],12); outerr=substr(a[3],10);} 
	/^outOctet/ { gsub(/ /,""); split($0,a,","); outoctet=substr(a[1],10) } 
	END { printf("\n"); }'
}

main()
{
   case "$1" in
   "collect")
      collect > $CONVERSE
      parse_system > $DATAFILE.system
      parse_line > $DATAFILE.line
      parse_interfaces > $DATAFILE.interfaces
      ;;
   "system")
      test -f $DATAFILE.system && cat $DATAFILE.system
      ;;
   "line")
      test -f $DATAFILE.line && cat $DATAFILE.line
      ;;
   "interfaces")
      test -f $DATAFILE.interfaces && cat $DATAFILE.interfaces
      ;;
   esac
}

main $*
