Saturday, March 25, 2006

Solaris "netstat" summary by service name

#! /bin/sh
#
# summarise network connection on Solaris
# discard ephemeral ports
#
# Sample output:
# PASSIVE:
# Protocol            No of Connections
# --------            -----------------
# ssh                 1 =  ESTABLISHED(1)
# sge_qmaster         17 =  ESTABLISHED(17)
#
# ACTIVE:
# Protocol            No of Connections
# --------            -----------------
# sge_qmaster         1 =  ESTABLISHED(1)
# 80                  1 =  TIME_WAIT(1)
#



# shell function
# create the awk's BEGIN section, mapping service number to service name
serviceN2S()
{
        awk '
        BEGIN {
                printf("BEGIN{")
        }
        /^[a-zA-Z]/ && $2 ~ /tcp$/ {
                split($2,a,"/")
                printf("service[\"%d\"]=\"%s\";", a[1], $1)
        }
        END {
                printf("}")
        }' /etc/services
}
b=`serviceN2S`


# ignore high port
ephemeral=`/usr/sbin/ndd /dev/tcp tcp_smallest_anon_port`
netstat -n -P tcp | nawk -v ephemeral=$ephemeral ''$b'

# awk function
# summarise the states of all connections
function details(List, arr1, arr2, i, sum, str) {
        split(List, arr1, " ")
        sum = 0
        for ( i in arr1 ) {
                if ( arr1[i] != "" ) {
                        ++arr2[arr1[i]]
                        ++sum
                }
        }
        str = sprintf("%d = ", sum)
        for ( i in arr2 ) {
                str = sprintf("%s %s(%s)", str, i, arr2[i])
        }
        return str
}

/^[1-9][0-9]*\./ {
        # passive
        split($1, arr_p, ".")
        if( int(arr_p[5]) < ephemeral ) {
                passive[arr_p[5]] = sprintf("%s %s", passive[arr_p[5]], $NF)
        }

        # active
        split($2, arr_a, ".")
        if( int(arr_a[5]) < ephemeral ) {
                active[arr_a[5]] = sprintf("%s %s", active[arr_a[5]], $NF)
        }
}
END {
        print "PASSIVE:"
        printf("%-18s  %s\n","Protocol","No of Connections")
        printf("%-18s  %s\n","--------","-----------------")
        for ( i in passive ) {
                if ( int(i) > ephemeral ) continue
                sname = service[i]
                if ( sname == "" ) {
                        sname = i
                }
                printf("%-18s  %s\n", sname, details(passive[i]))
        }
        print ""
        print "ACTIVE:"
        printf("%-18s  %s\n","Protocol","No of Connections")
        printf("%-18s  %s\n","--------","-----------------")
        for ( i in active ) {
                if ( int(i) > ephemeral ) continue
                sname = service[i]
                if ( sname == "" ) {
                        sname = i
                }
                printf("%-18s  %s\n", sname, details(active[i]))
        }
}'

Friday, March 24, 2006

(date;df -k) >> log conversion

#! /bin/sh
#
# Input:
# Thu Mar 23 18:05:05 SGT 2006
# Filesystem           1K-blocks      Used Available Use% Mounted on
# /dev/sda2              8064304   6240288   1414360  82% /
# /dev/sda1               101089     14928     80942  16% /boot
# none                   1030804         0   1030804   0% /dev/shm
# /dev/sdd1            1200501556 541543720 597975824  48% /san
#
# Output:
# 2006-03-23 18:06:05 /boot 16 / 82 /dev/shm 0 /san 48 
#


if [ $# -ne 1 ]; then
 echo "Usage: $0 "
 exit 1
fi
logfile=$1


awk '

function printSample()
{
 printf("%s ",timestamp)
 for(mp in mount) {
  printf("%s %d ",mp,mount[mp])
 }
 printf("\n")
}

# setup mapping
BEGIN {
 month2num["Jan"]="01"
 month2num["Feb"]="02"
 month2num["Mar"]="03"
 month2num["Apr"]="04"
 month2num["May"]="05"
 month2num["Jun"]="06"
 month2num["Jul"]="07"
 month2num["Aug"]="08"
 month2num["Sep"]="09"
 month2num["Oct"]="10"
 month2num["Nov"]="11"
 month2num["Dec"]="12"
}

# timestamp (good enough pattern matching)
/ [1-9][0-9][0-9][0-9]$/ && length($0)==28 {
 if ( timestamp != "" ) {
  printSample()
 }
 split($0,date)
 y=date[6]
 m=month2num[date[2]]
 d=date[3]
 hms=date[4]
 timestamp=sprintf("%s-%s-%s %s",y,m,d,hms)
}

# mount point and percentage of used
$NF ~ /^\// {
 mountpoint=$NF
 last2=NF-1
 used=substr($last2,1,length($last2)-1)
 mount[mountpoint]=used
}

# print last sample
END {
 printSample()
}
' $logfile

Thursday, March 23, 2006

AWK, the fun way

#! /bin/sh
#
# the longest word
# in redhat 9, the longest word is 28 alphabets "antidisestablishmentarianism"

gawk '{print length($0),$0}' /usr/share/dict/linux.words | sort -n -k 1 | tail -1




#! /bin/sh
#
# non-repeatable alphabets in word
# in redhat 9,
# 14 alphabets - "ambidextrously"
# 13 alphabets - "consumptively", "copyrightable", "unpredictably"

if [ $# -ne 1 ]; then
 echo "Usage: $0 "
 exit
fi

gawk -v n=$1 '
{
        if ( n!=length($1) ) {
                next;
        }
        delete s;
        split(tolower($1),a,"");
        for(i in a) {
                ind=a[i];
                ++s[ind];
                if (s[ind]>1) next
        }
        print $1
}' /usr/share/dict/linux.words




# /bin/sh
#
# how to use AWK to calculate the min/avg/max/sum
#
# sample output from redhat 9
#     Minimum    = 6
#     Average    = 8.01017
#     Maximum    = 28
#     Sum        = 363878
#     Word Count = 45427

# generate some arbitrary numbers
tmpfile=/tmp/funwithawk.$$
awk '{print length($0)}' /usr/share/dict/linux.words > $tmpfile

# shell functions
sum()
{
        awk '{s+=$1} END {print s}' $1
}
avg()
{
        awk '{s+=$1} END {print s/NR}' $1
}
min()
{
        awk 'BEGIN {min=9999999} {if ($1max) {max=$1}} END {print max}' $1
}
wc()
{
        awk 'END {print NR}' $1
}

echo "Minimum    = `min $tmpfile`"
echo "Average    = `avg $tmpfile`"
echo "Maximum    = `max $tmpfile`"
echo "Sum        = `sum $tmpfile`"
echo "Word Count = `wc $tmpfile`"

rm -f $tmpfile

Wednesday, March 22, 2006

Linux SAR to RRD update

#! /bin/sh
 
if [ $# -ne 1 ]; then
        echo "Usage: $0 "
        exit 1
fi
sarfile=$1
if [ ! -f $sarfile ]; then
        echo "Error. $sarfile does not exist"
        exit 2
fi
 
 
tmp=/tmp/.tmp-`basename $0`
 
# CPU
# user sys wio idle
sar -f $sarfile |         awk '                 /^[0-9].*[0-9]$/ && length($0)>0 && $NF !~ /[a-z]/ {print $4,$5,6,$7}                 /Average/ {exit}         ' > $tmp-cpu
 
# NETWORK
# rxbyt/s wxbyt/s
sar -f $sarfile -n FULL |         awk '                 /^[0-9].*[0-9]$/ && length($0)>0 && $NF !~ /[a-z]/ && /eth0/ {print $6,$7}                 /Average/ {exit}         ' > $tmp-net
 
# MEMORY
# memused% swpused%
sar -f $sarfile -r |         awk '                 /^[0-9].*[0-9]$/ && length($0)>0 && $NF !~ /[a-z]/ {print $5,$11}                 /Average/ {exit}         ' > $tmp-mem
 
# RUN QUEUE
# runq-sz plist-sz ldavg1 ldavg5
sar -f $sarfile -q |         awk '                 /^[0-9].*[0-9]$/ && length($0)>0 && $NF !~ /[a-z]/ {print $3,$4,$5,$6}                 /Average/ {exit}         ' > $tmp-run
 
 
#
# print time (epoch)
sar -f $sarfile -h -r | awk '{print $3}' | uniq > $tmp-time
 
 
#
# put them all in one file
paste $tmp-time $tmp-cpu $tmp-mem $tmp-net $tmp-run | sed -e 's/\t/ /g' > $tmp-all
 
 
host=`hostname`
ymd=`date +'%Y%m%d'`
logdir=/var/log/sa/SAR-RRD
[ ! -d $logdir ] && mkdir -p $logdir
rrdupdate="$logdir/$ymd.rrdupdate"
 
 
 
exec 1> $rrdupdate
 
 
echo "HOST=$host"
echo "DS=usr:GAUGE"
echo "DS=sys:GAUGE"
echo "DS=wio:GAUGE"
echo "DS=idl:GAUGE"
echo "DS=memused:GAUGE"
echo "DS=swpused:GAUGE"
echo "DS=rxbyte:GAUGE"
echo "DS=txbyte:GAUGE"
echo "DS=runq:GAUGE"
echo "DS=plist:GAUGE"
echo "DS=ldavg1:GAUGE"
echo "DS=ldavg5:GAUGE"
echo "GRAPH=$ydm-cpu.png:0:100:CPU Utilisation ($host):Percent:1.0:usr#AREA#ff0000,sys#STACK#00ff00,wio#STACK#0000ff,idl#STACK#ffffff"
echo "GRAPH=$ydm-net.png:0:U:Network Utilisation ($host):Kbps:0.0078125:rxbyte#AREA#00ff00,txbyte#LINE2#0000ff"
echo "GRAPH=$ydm-mem.png:0:100:Memory Utilisation ($host):Percent:1.0:memused#LINE2#00ff00,swpused#LINE2#0000ff"
echo "GRAPH=$ydm-run.png:0:U:Run Queue / Average Load ($host):Queue/AveLoad:1.0:runq#LINE2#00ff00,ldavg5#LINE2#0000ff"
cat $tmp-all
 
 
rm -f $tmp-cpu $tmp-net $tmp-mem $tmp-run $tmp-all $tmp-time

Friday, March 17, 2006

Self-Descriptive Plot and Data using Tcl and Rrdtool

#! /usr/local/bin/tclsh
#
# Input file definition:
# 
# HEADER
# HOST=
# DS=:                          (allow multiple DS)
# GRAPH=                      (allow multiple GRAPH)
# (colon separated)
# - image filename
# - y-axis lower limit
# - y-axis upper limit
# - Title
# - y-axis label
# - scale factor
# - data source to be graphed (comma separated, # separated for each data source details)
#   - data source name
#   - graph type (LINE2, AREA, ...)
#   - RGB colour code
#