#! /usr/bin/perl use CGI; use DBI; use Time::HiRes; use POSIX ":sys_wait_h"; use strict; use warnings; use lib '../../include'; use nms; use File::Basename; my $cgi = CGI->new; my $cwd = dirname($0); my $switch = $cgi->param('id'); my $width = $cgi->param('width'); my $height = $cgi->param('height'); my @pids = (); my $resthtml = ""; $width = 500 unless (defined($width)); $height = 250 unless (defined($height)); require "$cwd/mygraph.pl"; my $start = [Time::HiRes::gettimeofday]; my $dbh = nms::db_connect(); sub filename($$$$) { my ($switch, $last_port, $width, $height) = @_; $last_port =~ y,/,_,; return "$switch-$last_port-$width-$height.png"; } # Fetch the name my $ref = $dbh->selectrow_hashref('SELECT sysname,ip FROM switches WHERE switch=?', undef, $switch); print $cgi->header(-type=>'text/html; charset=utf-8'); print <<"EOF"; snmp

Switch $switch ($ref->{'sysname'} - $ref->{'ip'})

EOF my $q = $dbh->prepare('select ifname,\'Port \' || ifname as description,extract(epoch from time) as time,ifhcinoctets,ifhcoutoctets from switches natural join polls where time between now() - \'1 day\'::interval and now() and switch=? order by switch,ifname,time;') or die $dbh->errstr; $q->execute($switch) or die $dbh->errstr; my (@totx, @toty1, @toty2) = (); my (@x, @y1, @y2) = (); my $last_port; my $portname = ""; my $min_x = time; my $max_x = time - 86400; my ($min_y, $max_y, $prev_time, $prev_in, $prev_out); my ($if,$of,$ifv,$ofv); my $idx; my ($min_ty,$max_ty) = (0, 10_000_000/8); $prev_time = -1; my $last_totx; # Buffer all the rows so we can fancy-sort them my @rows; while (my $ref = $q->fetchrow_hashref()) { push @rows, $ref; } sub cmp_ports { my @a = split(/(\d+)/, $_[0]); my @b = split(/(\d+)/, $_[1]); for (my $i = 0; $i < scalar @a; ++$i) { last if $i >= scalar @b; my $a = $a[$i]; my $b = $b[$i]; if ($a =~ /^\d+$/ && $a =~ /^\d+$/) { return $a <=> $b if ($a != $b); } else { return $a cmp $b if ($a ne $b); } } # None of the N first parts differed, this means the shortest one is first return scalar @a <=> scalar @b; } sort { cmp_ports($a->{'ifname'}, $b->{'ifname'}) } @rows; foreach my $ref (@rows) { my $time = $ref->{'time'}; my $in = $ref->{'ifhcinoctets'}; my $out = $ref->{'ifhcoutoctets'}; next if ($time == $prev_time); if ($ref->{'ifname'} ne $last_port) { if (defined $last_port) { my $filename = filename($switch, $last_port, $width, $height); # reap children waitpid(-1, WNOHANG); my $pid = fork(); if ($pid == 0) { # write out the graph my $graph = makegraph($width, $height, $min_x, $max_x, $min_y, $max_y, 5); plotseries($graph, \@x, \@y1, 255, 0, 0, $min_x, $max_y); plotseries($graph, \@x, \@y2, 0, 0, 255, $min_x, $max_y); open GRAPH, ">$cwd/img/$filename" or die "$cwd/img/$filename: $!"; print GRAPH $graph->png; close GRAPH; exit; } push @pids, $pid; $resthtml .= "

$portname

\n"; $resthtml .= "

\n"; } # Reset all the variables @x = (); @y1 = (); @y2 = (); ($min_y,$max_y) = (0, 10_000_000/8); $prev_time = $ref->{'time'}; $prev_in = $ref->{'ifhcinoctets'}; $prev_out = $ref->{'ifhcoutoctets'}; $last_port = $ref->{'ifname'}; $portname = $ref->{'description'}; ($if,$of,$ifv,$ofv) = (0,0,0,0); ($prev_time,$prev_in,$prev_out) = ($time,$in,$out); $idx = 0; $last_totx = undef; next; } # Assume overflow (unless the switch has been down for >10 minutes) my ($calc_in, $calc_out) = ($in, $out); if ($in < $prev_in || $out < $prev_out) { if ($prev_in < 4294967296 && $prev_out < 4294967296) { # ick, heuristics if ($prev_time - $time > 600 || ($in + 4294967296 - $prev_in) > 2147483648 || ($out + 4294967296 - $prev_out) > 2147483648) { ($prev_time,$prev_in,$prev_out) = ($time,$in,$out); next; } $calc_in += 4294967296 if ($in < $prev_in); $calc_out += 4294967296 if ($out < $prev_out); } else { $prev_in = 0; $prev_out = 0; } } # Remove dupes if ($in == $prev_in && $out == $prev_out) { ($prev_time,$prev_in,$prev_out) = ($time,$in,$out); next; } # Find the current flow my $if = ($calc_in - $prev_in) / ($time - $prev_time); my $of = ($calc_out - $prev_out) / ($time - $prev_time); # Summarize (we don't care about the summed variance for now) $min_x = $time if (!defined($min_x) || $time < $min_x); $max_x = $time if (!defined($max_x) || $time > $max_x); $min_y = $if if (!defined($min_y) || $if < $min_y); $min_y = $of if ($of < $min_y); $max_y = $if if (!defined($max_y) || $if > $max_y); $max_y = $of if ($of > $max_y); my $pt = 0.5 * ($time + $prev_time); push @x, $pt; push @y1, $if; push @y2, $of; while ($idx < $#totx && $pt > $totx[$idx]) { ++$idx; } if ($idx >= $#totx) { push @totx, $pt; push @toty1, $if; push @toty2, $of; ++$idx; $min_ty = $if if (!defined($min_ty) || $if < $min_ty); $min_ty = $of if ($of < $min_ty); $max_ty = $if if (!defined($max_ty) || $if > $max_ty); $max_ty = $of if ($of > $max_ty); } else { if (!defined($last_totx) || $last_totx != $idx) { $toty1[$idx] += $if; $toty2[$idx] += $of; } $last_totx = $idx; $min_ty = $toty1[$idx] if (!defined($min_ty) || $toty1[$idx] < $min_ty); $min_ty = $toty2[$idx] if ($toty2[$idx] < $min_ty); $max_ty = $toty1[$idx] if (!defined($max_ty) || $toty1[$idx] > $max_ty); $max_ty = $toty2[$idx] if ($toty2[$idx] > $max_ty); } ($prev_time,$prev_in,$prev_out) = ($time,$in,$out); } $dbh->disconnect; # last graph my $filename = filename($switch, $last_port, $width, $height); my $pid = fork(); if ($pid == 0) { my $graph = makegraph($width, $height, $min_x, $max_x, $min_y, $max_y, 5); plotseries($graph, \@x, \@y1, 255, 0, 0, $min_x, $max_y); plotseries($graph, \@x, \@y2, 0, 0, 255, $min_x, $max_y); open GRAPH, ">$cwd/img/$filename" or die "img/$filename: $!"; print GRAPH $graph->png; close GRAPH; exit; } push @pids, $pid; $resthtml .= "

$portname

\n"; $resthtml .= "

\n"; # total graph my $graph = makegraph($width, $height, $min_x, $max_x, $min_ty, $max_ty, 5); plotseries($graph, \@totx, \@toty1, 255, 0, 0, $min_x, $max_ty); plotseries($graph, \@totx, \@toty2, 0, 0, 255, $min_x, $max_ty); $filename = "$switch-$width-$height.png"; open GRAPH, ">$cwd/img/$filename" or die "img/$filename: $!"; print GRAPH $graph->png; close GRAPH; # Wait for all the other graphs to be done while (waitpid(-1, 0) != -1) { 1; } print $resthtml; print "

Total

\n"; print "

\n"; my $elapsed = Time::HiRes::tv_interval($start); printf "

Page and all graphs generated in %.2f seconds.

\n", $elapsed; print "\n\n";