#! /usr/bin/perl # # Argus Client Software. Tools to read, analyze and manage Argus data. # Copyright (c) 2000-2003 QoSient, LLC # All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ragraph-histogram.pl # # use lib qw( /usr/local/rrdtool-1.0.40/lib/perl ../lib/perl ); use RRDs; use POSIX; use strict; my $tmpfile = tmpnam(); my $RRD = $tmpfile.".rrd"; my $GIF = "ragraph.gif"; my @arglist = (); my $title = ""; my $fill = 1; my $stack = 1; my $split = 1; my $log = 0; my $rigid = 0; my $upper = 0; my $lower = 0; my $height = 275; my $width = 800; my $xaxisstr; my $timeindex = -1; my $saddrindex = -1; my $daddrindex = -1; my $dportindex = -1; my $protoindex = -1; my $probeindex = -1; my $transindex = -1; my $avgdurindex = -1; my $ddurindex = -1; my $dstimeindex = -1; my $dltimeindex = -1; my $srcpktindex = -1; my $dstpktindex = -1; my $srcbyteindex = -1; my $dstbyteindex = -1; my $srclossindex = -1; my $dstlossindex = -1; my $srcjitterindex = -1; my $dstjitterindex = -1; my $srcdataindex = -1; my $dstdataindex = -1; my $minval = 'U'; my $maxval = 'U'; my @opts = (); my @columns = (); my @power = (); my @objects = (); my @objectlist = (); my $objectindex = -1; my $histo = 0; my $objects; my $num = 0; my ($START, $FRAC, $END, $STEP, $COLUMNS, $PROTOS, $PROBES, $RUNS, $ERROR); my @line_args; ARG: while (my $arg = shift(@ARGV)) { for ($arg) { s/^-log// && do { $log++; next ARG; }; s/^-fill// && do { $fill = 0; next ARG; }; s/^-stack// && do { $stack = 0; next ARG; }; s/^-split// && do { $split = 0; next ARG; }; s/^-rigid// && do { $rigid++; next ARG; }; s/^-height// && do { $height = shift (@ARGV); next ARG; }; s/^-width// && do { $width = shift (@ARGV); next ARG; }; s/^-upper// && do { $upper = shift (@ARGV); next ARG; }; s/^-lower// && do { $lower = shift (@ARGV); next ARG; }; s/^-title// && do { $title = shift (@ARGV); next ARG; }; s/^-w// && do { $GIF = shift (@ARGV); next ARG; }; s/^-N// && do { $num = shift (@ARGV); next ARG; }; /^-H/ && do { $histo++; }; s/\(/\\\(/ && do { ; }; s/\)/\\\)/ && do { ; }; } $arglist[@arglist + 0] = $arg; } RagraphProcessArgusData (); RagraphReadInitialValues ($tmpfile); RagraphGenerateRRDParameters ($tmpfile); RagraphGenerateRRD (); RagraphGenerateGIF (); RagraphCleanUp (); exit; sub RagraphCleanUp { `rm $tmpfile $RRD` } sub RagraphReadInitialValues { my $file = shift; my ($i, $k, $v); open(SESAME, $file); for (1 .. 7) { if (my $data = <SESAME>) { if ( ($k, $v) = $data =~ m/(\w+)=(.*)/ ) { for ($k) { /StartTime/ and do {(($START, $FRAC) = $v =~ m/(\d*)\.(.*)/) ;}; /StopTime/ and do {(($END, $FRAC) = $v =~ m/(\d*)\.(.*)/) ;}; /BinSize/ and do {(($STEP, $FRAC) = $v =~ m/(\d*)\.(.*)/) ;}; /Columns/ and do { $COLUMNS = $v;}; /Bins/ and do {$RUNS = $v;}; /Protos/ and do {$PROTOS = $v;}; /Probes/ and do {$PROBES = $v;}; } } } } close(SESAME); } sub RagraphProcessArgusData { my @args = ""; if ($histo) { @args = ("rahistogram -p6 -G -s ", @arglist, "> $tmpfile"); } else { @args = ("rahistogram -p6 -G -s lasttime ", @arglist, "> $tmpfile"); } my $input = `@args`; } my @dataobjects = (); my @typeobjects = (); my $objwidth = 0; sub RagraphGenerateRRDParameters { my $file = shift; my $x; @columns = split(/,/, $COLUMNS); for ($x = 0; $x < (@columns + 0); $x++) { for ($columns[$x]) { /Time/ and do {$timeindex = $x; }; /Host/ and do {push @typeobjects, $x; $saddrindex = $x; $objwidth = 15; }; /SrcAddr/ and do {push @typeobjects, $x; $saddrindex = $x; $objwidth = 15; }; /InAddr/ and do {push @typeobjects, $x; $saddrindex = $x; $objwidth = 15; }; /OutAddr/ and do {push @typeobjects, $x; $daddrindex = $x; $objwidth = 15; }; /DstAddr/ and do {push @typeobjects, $x; $daddrindex = $x; $objwidth = 15; }; /Type/ and do {push @typeobjects, $x; $protoindex = $x; $objwidth = 6; }; /ProbeId/ and do {push @typeobjects, $x; $probeindex = $x; $objwidth = 15; }; /Dport/ and do {push @typeobjects, $x; $dportindex = $x; $objwidth = 12; }; /SrcPkt/ and do {push @dataobjects, $x; $srcpktindex = $x; }; /InPkt/ and do {push @dataobjects, $x; $srcpktindex = $x; }; /DstPkt/ and do {push @dataobjects, $x; $dstpktindex = $x; }; /OutPkt/ and do {push @dataobjects, $x; $dstpktindex = $x; }; /InAppBytes/ and do {push @dataobjects, $x; $srcbyteindex = $x; }; /SAppBytes/ and do {push @dataobjects, $x; $srcbyteindex = $x; }; /SrcBytes/ and do {push @dataobjects, $x; $srcbyteindex = $x; }; /InBytes/ and do {push @dataobjects, $x; $srcbyteindex = $x; }; /OutAppBytes/ and do {push @dataobjects, $x; $dstbyteindex = $x; }; /DAppBytes/ and do {push @dataobjects, $x; $dstbyteindex = $x; }; /DstBytes/ and do {push @dataobjects, $x; $dstbyteindex = $x; }; /OutBytes/ and do {push @dataobjects, $x; $dstbyteindex = $x; }; /pSrc_Loss/ and do {push @dataobjects, $x; $srclossindex = $x; }; /pDst_Loss/ and do {push @dataobjects, $x; $dstlossindex = $x; }; /Src_Loss/ and do {push @dataobjects, $x; $srclossindex = $x; }; /Dst_Loss/ and do {push @dataobjects, $x; $dstlossindex = $x; }; /SrcJitter/ and do {push @dataobjects, $x; $srcbyteindex = $x; }; /DstJitter/ and do {push @dataobjects, $x; $dstbyteindex = $x; }; /Trans/ and do {push @dataobjects, $x; $transindex = $x; }; /AvgDur/ and do {push @dataobjects, $x; $avgdurindex = $x; }; /dDur/ and do {push @dataobjects, $x; $ddurindex = $x; }; /dsTime/ and do {push @dataobjects, $x; $dstimeindex = $x; }; /dlTime/ and do {push @dataobjects, $x; $dltimeindex = $x; }; } } if (($srcpktindex >= 0) || ($srcpktindex >= 0)) { if ($srcpktindex >= 0) { $srcdataindex = $srcpktindex; } if ($dstpktindex >= 0) { $dstdataindex = $dstpktindex; } } else { if (($srcbyteindex >= 0) || ($srcbyteindex >= 0)) { if ($srcbyteindex >= 0) { $srcdataindex = $srcbyteindex; } if ($dstbyteindex >= 0) { $dstdataindex = $dstbyteindex; } } else { if (($srcjitterindex >= 0) || ($dstjitterindex >= 0)) { if ($srcjitterindex >= 0) { $srcdataindex = $srcjitterindex; } if ($dstjitterindex >= 0) { $dstdataindex = $dstjitterindex; } } else { if (($srclossindex >= 0) || ($dstlossindex >= 0)) { if ($srclossindex >= 0) { $srcdataindex = $srclossindex; } if ($dstlossindex >= 0) { $dstdataindex = $dstlossindex; } } } } if ($avgdurindex >= 0) { $srcdataindex = -1; $dstdataindex = $avgdurindex; $minval = 0.000001; } if ($ddurindex >= 0) { $srcdataindex = -1; $dstdataindex = $ddurindex; } if ($transindex >= 0) { $srcdataindex = -1; $dstdataindex = $transindex; } if ($dstimeindex >= 0) { $srcdataindex = -1; $dstdataindex = $dstimeindex; } if ($dltimeindex >= 0) { $srcdataindex = -1; $dstdataindex = $dltimeindex; } } my $ind = 4; my $cnt = 1; my $lineind = 0; my $xaxislabel; @opts = ("--start", $START, "--step", $STEP); if (@typeobjects + 0) { my %objectlist = (); open(SESAME, $file); for (1 .. 7) { my $data = <SESAME>; } while (my $data = <SESAME>) { my @items = split(/,/, $data); for (my $i = 0; $i < (@typeobjects + 0); $i++) { chomp $items[$typeobjects[$i]]; $objectlist{$items[$typeobjects[$i]]}[0]++; for (my $x = 0; $x < (@dataobjects + 0); $x++) { chomp $items[$dataobjects[$x]]; $objectlist{$items[$typeobjects[$i]]}[1] += $items[$dataobjects[$x]]; } } } for my $objs (sort { $objectlist{$b}[0] <=> $objectlist{$a}[0] } keys %objectlist) { if (!($objs eq "0.0.0.0")) { push @objects, $objs; } } if ($num > 0) { splice @objects, $num; } seek(SESAME, 0, 0); for (1 .. 7) { my $data = <SESAME>; } if ($protoindex >= 0) { if ($daddrindex >= 0) { push @objects, "mcast"; } } my $ncnt = 0; my $ocnt = 0; for (my $i = 0; $i < (@objects + 0); $i++) { for (my $x = 0; $x < (@dataobjects + 0); $x++) { for ($columns[$dataobjects[$x]]) { /Host|Src|In|SApp/ and do { $opts[$ind++] = "DS:objectIn$ncnt:GAUGE:$STEP:U:$maxval"; $line_args[$lineind++] = "DEF:alphaIn$ncnt=$RRD:objectIn$ncnt:AVERAGE"; $line_args[$lineind++] = "CDEF:ralphaIn$ncnt=alphaIn$ncnt,$STEP,/"; $ncnt++; }; /Dst|Out|DApp|Trans|Dur|Time/ and do { $opts[$ind++] = "DS:objectOut$ocnt:GAUGE:$STEP:$minval:U"; $line_args[$lineind++] = "DEF:alphaOut$ocnt=$RRD:objectOut$ocnt:AVERAGE"; $line_args[$lineind++] = "CDEF:ralphaOut$ocnt=alphaOut$ocnt,$STEP,/"; $ocnt++; }; } } } } else { my $ncnt = 0; my $ocnt = 0; shift @columns; push @objects, @columns; @columns = split(/,/, $COLUMNS); for (my $i = 0; $i < (@objects + 0); $i++) { for ($objects[$i]) { /Host|Src|In|SApp/ and do { $opts[$ind++] = "DS:objectIn$ncnt:GAUGE:$STEP:U:$maxval"; $line_args[$lineind++] = "DEF:alphaIn$ncnt=$RRD:objectIn$ncnt:AVERAGE"; $line_args[$lineind++] = "CDEF:ralphaIn$ncnt=alphaIn$ncnt,$STEP,/"; $ncnt++; }; /Dst|Out|DApp|Trans|Dur|Time/ and do { $opts[$ind++] = "DS:objectOut$ocnt:GAUGE:$STEP:$minval:U"; $line_args[$lineind++] = "DEF:alphaOut$ocnt=$RRD:objectOut$ocnt:AVERAGE"; $line_args[$lineind++] = "CDEF:ralphaOut$ocnt=alphaOut$ocnt,$STEP,/"; $ocnt++; }; } } } my %inColors; my %outColors; my @incolors = ("#900000", "#008000", "#002275", "#ead00e", "#de2d1a", "#800080", "#008080", "#808080", "#808000"); my @outcolors = ("#f00000", "#00e000", "#0000f0", "#ffff66", "#f68d00", "#e000e0", "#00e0e0", "#e0e0e0", "#e0e000"); if ($protoindex >= 0) { %inColors = ( tcp => "#a00000", udp => "#008000", icmp => "#000080", igmp => "#008080", ospf => "#800080", pim => "#808080", mcast => "#808000", ip => "#707070" ); %outColors = ( tcp => "#f00000", udp => "#00f000", icmp => "#0000e0", igmp => "#00e0e0", ospf => "#e000e0", pim => "#e0e0e0", mcast => "#e0e000", ip => "#b0b0b0" ); } else { if (@typeobjects + 0) { for (my $i = 0; $i < (@objects + 0); $i++) { $inColors{$objects[$i]} = $incolors[$i]; $outColors{$objects[$i]} = $outcolors[$i]; } } else { $inColors{$objects[0]} = $incolors[0]; $outColors{$objects[0]} = $outcolors[0]; } } if ((@dataobjects + 0) == 0) { printf "usage: $0 metric (srcid | proto [daddr] | dport) [-title \"title\"] [ra-options]\n"; } if (($srcdataindex < 0) || ($dstdataindex < 0)) { $split = 0; } my $col; my $date; if (($END - $START) > (60 * 60 * 24)) { $date = strftime "%a %b %e %Y", localtime($START); $date .= " - "; $date .= strftime "%a %b %e %Y", localtime($END); } else { $date = strftime "%a %b %e %Y", localtime($START); } $line_args[$lineind++] = "COMMENT:$date\\c"; $line_args[$lineind++] = "COMMENT:\\s"; if ($fill && $split) { $line_args[$lineind++] = "HRULE:0#ffffff"; } else { $line_args[$lineind++] = "HRULE:0#888888"; } my $style; my $method; if ($fill) { $style = "AREA"; } else { $style = "LINE2"; } if ($stack) { $method = "STACK"; } else { $method = $style; } if (@typeobjects + 0) { my $ncnt = 0; my $ocnt = 0; my $object; for (my $i = 0; $i < (@objects + 0); $i++) { for (my $x = 0; $x < (@dataobjects + 0); $x++) { for ($columns[$dataobjects[$x]]) { /Dst|Out|DApp/ and do { if (!($col = $outColors{$objects[$i]})) { $col = "#707070"; } $object = sprintf "%-4.4s%-*.*s\\J", "out ", $objwidth, $objwidth, $objects[$i]; if ($ocnt == 0) { $line_args[$lineind++] = "$style:ralphaOut$ocnt$col:$object:"; } else { $line_args[$lineind++] = "$method:ralphaOut$ocnt$col:$object:"; } $ocnt++; }; /Trans|Dur|Time/ and do { if (!($col = $inColors{$objects[$i]})) { $col = "#707070"; } $object = sprintf "%-*.*s\\J", $objwidth, $objwidth, $objects[$i]; if ($ocnt == 0) { $line_args[$lineind++] = "$style:ralphaOut$ocnt$col:$object:"; } else { $line_args[$lineind++] = "$method:ralphaOut$ocnt$col:$object:"; } $ocnt++; }; } } } if ($ocnt > 0) { $line_args[$lineind++] = "COMMENT:\\s"; $line_args[$lineind++] = "COMMENT:\\s"; if ($ocnt > 7) { $line_args[$lineind++] = "COMMENT:\\s"; } } for (my $i = 0; $i < (@objects + 0); $i++) { for (my $x = 0; $x < (@dataobjects + 0); $x++) { for ($columns[$dataobjects[$x]]) { /Host|Src|In|SApp/ and do { if (!($col = $inColors{$objects[$i]})) { $col = "#b0b0b0"; } $object = sprintf "%-4.4s%-*.*s\\J", " in ", $objwidth, $objwidth, $objects[$i]; if ($ncnt == 0) { $line_args[$lineind++] = "$style:ralphaIn$ncnt$col:$object:"; } else { $line_args[$lineind++] = "$method:ralphaIn$ncnt$col:$object:"; } $ncnt++; }; } } } } else { my $ncnt = 0; my $ocnt = 0; for (my $x = 0; $x < (@objects + 0); $x++) { for (my $object = $objects[$x]) { /Host|Src|In|SApp/ and do { if (!($col = $incolors[$ncnt])) { $col = "#707070"; } if ($ncnt == 0) { $line_args[$lineind++] = "$style:ralphaIn$ncnt$col:$object"; } else { $line_args[$lineind++] = "$method:ralphaIn$ncnt$col:$object"; } $ncnt++; }; /Dst|Out|DApp|Trans|Dur|Time/ and do { if (!($col = $outcolors[$ocnt])) { $col = "#b0b0b0"; } if ($ocnt == 0) { $line_args[$lineind++] = "$style:ralphaOut$ocnt$col:$object"; } else { $line_args[$lineind++] = "$method:ralphaOut$ocnt$col:$object"; } $ocnt++; }; } } } my $xaxislabels = 0; for (my $x = 0; $x < (@columns + 0); $x++) { for ($columns[$x]) { /Pkt/ and do {$power[$x] = 1.0 ; $xaxislabels++; $xaxislabel = " pkts/sec" ;}; /Bytes/ and do {$power[$x] = 1.0 * 8.0; $xaxislabels++; $xaxislabel = "bits/sec" ;}; /Jitter/ and do {$power[$x] = $STEP; $xaxislabels++; $xaxislabel = "Jitter (uSecs)" ;}; /Loss/ and do {$power[$x] = 1.0 ; $xaxislabels++; $xaxislabel = "Loss (pkts/sec)" ;}; /Trans/ and do {$power[$x] = 1.0 ; $xaxislabels++; $xaxislabel = "Concurrent Transactions" ;}; /AvgDur/ and do {$power[$x] = $STEP ; $xaxislabels++; $xaxislabel = "Average Transaction Time (secs)" ;}; /dDur/ and do {$power[$x] = $STEP ; $xaxislabels++; $xaxislabel = "Network Transit Time (secs)" ;}; /dsTime/ and do {$power[$x] = $STEP ; $xaxislabels++; $xaxislabel = "Delta StartTime (secs)" ;}; /dlTime/ and do {$power[$x] = $STEP ; $xaxislabels++; $xaxislabel = "Delta LastTime (secs)" ;}; } } if ($xaxislabels > 1) { if (($ddurindex >= 0) && (($dstimeindex >= 0) || ($dltimeindex >= 0))) { $xaxislabel = "Delta Time (secs)" ; } } if ($split) { if ($objectindex >= 0) { $xaxisstr = "inbound(-).outbound(+) "; } else { $xaxisstr = "dest(-).src(+) "; } } else { $xaxisstr = ""; } $xaxisstr .= $xaxislabel; $opts[$ind] = "RRA:AVERAGE:0.5:1:$RUNS"; } sub RagraphGenerateRRD { my $data; open(SESAME, $tmpfile); for (1 .. 7) { $data = <SESAME>; } RRDs::create $RRD, @opts; $ERROR = RRDs::error; if ($ERROR) { die "$0: unable to create `$RRD': $ERROR\n"; } my $last = RRDs::last $RRD; if ($ERROR = RRDs::error) { die "$0: unable to get last `$RRD': $ERROR\n"; } @opts = (); my $startime = $START; my $lasttime = $START + $STEP; my %objectvals; my %bins; my @vals; for (my $i = 0; $i < (@objects + 0); $i++) { $objectvals{$objects[$i], 'in'} = 0; $objectvals{$objects[$i], 'out'} = 0; } while (my $data = <SESAME>) { @opts = split(/,/, $data); my $thistime = $opts[0]; if ($bins{$thistime}++ == 0) { } if (@typeobjects + 0) { for (my $i = 0; $i < (@typeobjects + 0); $i++) { my $thisobject = $opts[$typeobjects[$i]]; chomp $thisobject; if ($daddrindex >= 0) { my @addr = split(/./, $opts[2]); if (($addr[0] & 0xF0) == 0xE0) { $thisobject = "mcast"; } } for (my $x = 0; $x < (@dataobjects + 0); $x++) { my $dataindex = $dataobjects[$x]; for ($columns[$dataindex]) { /Host|Src|In|SApp/ and do { $objectvals{$thistime, $thisobject, 'in'} += $opts[$dataindex] * $power[$dataindex]; }; /Dst|Out|DApp|Trans|Dur|Time/ and do { $objectvals{$thistime, $thisobject,'out'} += $opts[$dataindex] * $power[$dataindex]; }; } } } } else { my $label; for (my $i = 0; $i < (@objects + 0); $i++) { for ($objects[$i]) { /Host|Src|In|SApp/ and do { $label = 'in'; }; /Dst|Out|DApp|Trans|Dur|Time/ and do { $label = 'out'; }; } my $value = $opts[$i+1]; my $power = $power[$i+1]; $objectvals{$thistime, $objects[$i], $label} += $opts[$i + 1] * $power[$i + 1]; } } } close(SESAME); for my $thisbin (sort keys %bins) { my $prime = $thisbin; my $nvalue = 0; my $ovalue = 0; for (my $i = 0; $i < (@objects + 0); $i++) { if (@typeobjects + 0) { for (my $x = 0; $x < (@dataobjects + 0); $x++) { my $dataindex = $dataobjects[$x]; for ($columns[$dataindex]) { /Host|Src|In|SApp/ and do { if (!($nvalue = $objectvals{$thisbin, $objects[$i], 'in'})) { $nvalue = 0; } if ($split) { if ($nvalue == 0) { $prime .= ":$nvalue"; } else { $prime .= ":-$nvalue"; } } else { $prime .= ":$nvalue"; } }; /Dst|Out|DApp|Trans|Dur|Time/ and do { if (!($ovalue = $objectvals{$thisbin, $objects[$i], 'out'})) { $ovalue = 0; } $prime .= ":$ovalue"; }; } } } else { my $label; for ($objects[$i]) { /Host|Src|In|SApp/ and do { $label = 'in'; }; /Dst|Out|DApp|Trans|Dur|Time/ and do { $label = 'out'; }; } $nvalue = $objectvals{$thisbin, $objects[$i], $label}; if ($split && ($label eq 'in')) { $prime .= ":-$nvalue"; } else { $prime .= ":$nvalue"; } } } RRDs::update $RRD, "$prime"; if ($ERROR = RRDs::error) { die "$0: unable to update `$RRD': $ERROR\n"; } } } sub RagraphGenerateGIF { my @rrd_gifs = ($RRD, $GIF); my @rrd_args = ( "--base", "1000", "--vertical-label", $xaxisstr, "--start", $START, "--end", $END, "--interlace", "--imgformat","GIF", ); if ($title) { push @rrd_args, ("--title", $title); } if ($rigid) { @rrd_args[++$#rrd_args] = "--rigid"; } if ($log) { @rrd_args[++$#rrd_args] = "--log"; } if ($width) { @rrd_args[++$#rrd_args] = "--width"; @rrd_args[++$#rrd_args] = $width; } if ($height) { @rrd_args[++$#rrd_args] = "--height"; @rrd_args[++$#rrd_args] = $height; } if (!($rigid) && ($upper || $lower)) { @rrd_args[++$#rrd_args] = "--rigid"; } if ($upper) { @rrd_args[++$#rrd_args] = "--upper-limit"; @rrd_args[++$#rrd_args] = $upper; } if ($lower) { @rrd_args[++$#rrd_args] = "--lower-limit"; @rrd_args[++$#rrd_args] = $lower; } push @rrd_args, @line_args; while (@rrd_gifs) { my $RRD = shift(@rrd_gifs); my $GIF = shift(@rrd_gifs); my ($graphret,$xs,$ys) = RRDs::graph $GIF, @rrd_args; if ($ERROR = RRDs::error) { print "ERROR: $ERROR\n"; } } }