--- flow2rrd.pl 2004/12/23 10:24:02 1.1
+++ flow2rrd.pl 2004/12/23 21:08:32 1.2
@@ -1,4 +1,4 @@
-#!/usr/lpkg/bin/perl
+#!/usr/opkg/bin/perl
##
## flow2rrd -- store NetFlow data in Round-Robin Database (RRD)
## Copyright (c) 2004 Ralf S. Engelschall <rse@engelschall.com>
@@ -29,7 +29,8 @@
# fixed program information
my $my = {
- -progname => 'flow2rrd',
+ -progname => 'OSSP flow2rrd',
+ -proghome => 'http://www.ossp.org/pkg/tool/flow2rrd/',
-progvers => '0.1.0',
-progdate => '20-Dec-2004',
};
@@ -570,10 +571,10 @@
push(@args, '--base', 1024);
push(@args, '--x-grid', "HOUR:1:DAY:1:DAY:1:0:%d-%b-%Y");
push(@args, '--vertical-label', 'Bit/s');
- push(@args, '--color', 'CANVAS#ffffff');
- push(@args, '--color', 'BACK#e0e0e0');
- push(@args, '--color', 'SHADEA#f8f8f8');
- push(@args, '--color', 'SHADEB#999999');
+ push(@args, '--color', 'CANVAS#f0f0ff');
+ push(@args, '--color', 'BACK#e0e0f0');
+ push(@args, '--color', 'SHADEA#e5e5f5');
+ push(@args, '--color', 'SHADEB#d0d0e0');
push(@args, '--color', 'GRID#cccccc');
push(@args, '--color', 'MGRID#000000');
push(@args, '--color', 'FONT#000000');
@@ -583,7 +584,6 @@
push(@args, @def);
push(@args, @cdef);
push(@args, @draw);
- print STDERR join(" ", @args)."\n";
my ($rrd_averages, $rrd_xsize, $rrd_ysize) = RRDs::graph(@args);
my $err = RRDs::error();
die "failed to generate graph from RRD file: $err" if (defined($err));
@@ -650,10 +650,10 @@
push(@args, '--base', 1024);
push(@args, '--x-grid', "HOUR:1:DAY:1:DAY:1:0:%d-%b-%Y");
push(@args, '--vertical-label', 'Bit/s');
- push(@args, '--color', 'CANVAS#ffffff');
- push(@args, '--color', 'BACK#e0e0e0');
- push(@args, '--color', 'SHADEA#f8f8f8');
- push(@args, '--color', 'SHADEB#999999');
+ push(@args, '--color', 'CANVAS#f0f0ff');
+ push(@args, '--color', 'BACK#e0e0f0');
+ push(@args, '--color', 'SHADEA#e5e5f5');
+ push(@args, '--color', 'SHADEB#d0d0e0');
push(@args, '--color', 'GRID#cccccc');
push(@args, '--color', 'MGRID#000000');
push(@args, '--color', 'FONT#000000');
@@ -663,7 +663,6 @@
push(@args, @def);
push(@args, @cdef);
push(@args, @draw);
- print STDERR join(" ", @args)."\n";
my ($rrd_averages, $rrd_xsize, $rrd_ysize) = RRDs::graph(@args);
my $err = RRDs::error();
die "failed to generate graph from RRD file: $err" if (defined($err));
@@ -678,19 +677,117 @@
if ($opt->{-cgi}) {
my $cgi = new CGI;
- if (defined(my $graph = $cgi->param("graph"))) {
+ # error handler
+ $SIG{__DIE__} = sub {
+ my ($msg) = @_;
+ my $hint = '';
+ if ($msg =~ m|line\s+(\d+)|) {
+ my $line = $1;
+ my $io = new IO::File "<$0";
+ my @code = $io->getlines();
+ $io->close();
+ my $i = -1;
+ $hint = join("", map { s/^/sprintf("%d: ", $line+$i++)/se; $_; } @code[$line-2..$line]);
+ }
+ print STDOUT
+ "Content-Type: text/html; charset=ISO-8859-1\n" .
+ "\n" .
+ "<html>\n" .
+ " <head>\n" .
+ " <title>OSSP flow2rrd: ERROR</title>\n" .
+ " </head>\n" .
+ " <body>\n" .
+ " <h1>OSSP flow2rrd: ERROR</h1>\n" .
+ " <p>\n" .
+ " <tt>\n" .
+ " $msg<br>\n" .
+ " </tt>\n" .
+ " <pre>\n$hint</pre>\n" .
+ " </body>\n" .
+ "</html>\n";
+ exit(0);
+ };
+
+ if (defined($cgi->param("css"))) {
+ #
+ # output Cascading Style Sheet (CSS)
+ #
+
+ my $css = q{
+ body {
+ background: #c0c0c0;
+ color: #ffffff;
+ font-family: helvetica,arial,tahoma,verdana,sans-serif;
+ }
+ TABLE.flow2rrd {
+ background: #000000;
+ border: 2px solid #000000;
+ }
+ TABLE.flow2rrd TD.header {
+ color: #ffffff;
+ font-family: tahoma,helvetica,arial,tahoma,verdana,sans-serif;
+ font-weight: bold;
+ font-size: 200%;
+ padding: 4px;
+ text-align: center;
+ }
+ TABLE.flow2rrd TD.header A {
+ text-decoration: none;
+ color: #ffffff;
+ }
+ TABLE.flow2rrd TD.footer {
+ color: #ffffff;
+ font-family: tahoma,helvetica,arial,tahoma,verdana,sans-serif;
+ padding: 4px;
+ text-align: center;
+ }
+ TABLE.flow2rrd TD.footer A {
+ text-decoration: none;
+ font-weight: bold;
+ color: #ffffff;
+ }
+ TABLE.flow2rrd TABLE.explore TD.toolbar {
+ background: #333333;
+ padding: 10px;
+ }
+ TABLE.flow2rrd TABLE.explore TD.toolbar INPUT.textfield {
+ background: #333333;
+ color: #ffffff;
+ border: 0px;
+ border-bottom: 1px solid #999999;
+ }
+ TABLE.flow2rrd TABLE.explore TD.toolbar INPUT.submit {
+ background: #666666;
+ color: #ffffff;
+ margin-top: 10px;
+ border: 1px solid #999999;
+ font-weight: bold;
+ width: 100%;
+ }
+ };
+
+ # send out page
+ $css =~ s|^ ||mg;
+ print STDOUT $cgi->header(
+ -type => 'text/css',
+ -content_length => length($css),
+ -expires => '+5m'
+ ) . $css;
+ }
+ elsif (defined(my $graph = $cgi->param("graph"))) {
#
# output graph image
#
# prepare graph generation
- my (undef, $tmpfile) = mkstemp(($ENV{'TMPDIR'} || '/tmp') . "/flow2rrd.tmp");
- $graph =~ s|\@|\@$tmpfile|s;
+ my (undef, $tmpfile) = mkstemp(($ENV{'TMPDIR'} || '/tmp') . "/flow2rrd.XXXXXX");
+ $graph =~ s|\@|\@$tmpfile:|s;
# generate graph image
+ #die "$0 --graph $graph";
my $rc = system("$0 --graph $graph");
- if ($rc != 0 or not -s $tmpfile) {
- die "failed to generate graph image";
+ if ($rc != 0) { # or not -s $tmpfile) {
+ die "failed to generate graph image: $!";
}
# read graph image
@@ -708,30 +805,242 @@
# cleanup
unlink($tmpfile)
}
- else {
+ elsif (defined(my $explore = $cgi->param("explore"))) {
#
- # output HTML page
+ # output HTML page: EXPLORE A GRAPH
#
- # generate HTML page
+ # generate HTML page diversion
my $html = new String::Divert;
$html->overload(1);
- $html .= "<html>\n" .
- " <head>\n" .
- " " . $html->folder("head") .
- " </head>\n" .
- " <body>\n" .
- " " . $html->folder("body") .
- " </body>\n" .
- "</html>\n";
+
+ $html .=
+ "<html>\n" .
+ " <head>\n" .
+ " " . $html->folder("head") .
+ " </head>\n" .
+ " <body>\n" .
+ " " . $html->folder("body") .
+ " </body>\n" .
+ "</html>\n";
+
+ $html >> "head";
+ $html .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"".$cgi->url(-relative => 1)."?css=1\">\n";
+ $html << 1;
+
$html >> "body";
- $html .= "foo\n";
+ $html .= "<table class=\"flow2rrd\" border=0 cellpadding=0 cellspacing=0>\n";
+ $html .= " <tr>\n";
+ $html .= " <td class=\"header\">\n";
+ $html .= " " . $html->folder("header");
+ $html .= " </td>\n";
+ $html .= " <tr>\n";
+ $html .= " </tr>\n";
+ $html .= " <td class=\"canvas\">\n";
+ $html .= " " . $html->folder("canvas");
+ $html .= " </td>\n";
+ $html .= " <tr>\n";
+ $html .= " </tr>\n";
+ $html .= " <td class=\"footer\">\n";
+ $html .= " " . $html->folder("footer");
+ $html .= " </td>\n";
+ $html .= " </tr>\n";
+ $html .= "</table>\n";
+ $html << 1;
+
+ $html >> "header";
+ $html .= "<a href=\"".$cgi->url(-relative => 1)."\">Real-Time Network Statistics</a>";
+ $html << 1;
+ $html >> "footer";
+ $html .= "<a href=\"$my->{-proghome}\">$my->{-progname}</a> $my->{-progvers} ($my->{-progdate})";
$html << 1;
+
+ my $width = ($cgi->param("width") || "800");
+ my $height = ($cgi->param("height") || "200");
+ my $start = ($cgi->param("start") || "-48h");
+ my $end = ($cgi->param("end") || "now");
+ my $ulimit = ($cgi->param("ulimit") || "0");
+ my $llimit = ($cgi->param("llimit") || "0");
+
+ $html >> "canvas";
+ $html .= $cgi->start_form(
+ -method => "POST",
+ -action => $cgi->url(-relative => 1) . "?explore=$explore",
+ -enctype => "application/x-www-form-urlencoded"
+ );
+ $html .= $cgi->hidden(-name => "explore", -default => $cgi->url(-relative => 1) . "?explore=$explore")."\n";
+ $html .= "<table class=\"explore\" border=0 cellspacing=0 cellpadding=0>\n";
+ $html .= " <tr>\n";
+ $html .= " <td class=\"view\">\n";
+ $html .= " " . $html->folder("view");
+ $html .= " </td>\n";
+ $html .= " </tr>\n";
+ $html .= " <tr>\n";
+ $html .= " <td class=\"toolbar\">\n";
+ $html .= " " . $html->folder("toolbar");
+ $html .= " </td>\n";
+ $html .= " </tr>\n";
+ $html .= "</table>\n";
+ $html .= $cgi->end_form();
+ $html << 1;
+
+ my $img = $cgi->url(-relative => 1) . "?graph=$explore\@$width:$height:$start:$end:$ulimit:$llimit";
+ $html >> "view";
+ $html .= "<img src=\"$img\">\n";
+ $html << 1;
+
+ $html >> "toolbar";
+ $html .= "<table>\n";
+
+ $html .= "<tr><td>Graph Size:</td><td>" . $cgi->textfield(
+ -name => 'width',
+ -default => $width,
+ -size => 15,
+ -maxlength => 4,
+ -class => 'textfield',
+ ) . "</td><td>x</td><td>" . $cgi->textfield(
+ -name => 'height',
+ -default => $height,
+ -size => 15,
+ -maxlength => 4,
+ -class => 'textfield',
+ ) . "</td><td>(pixels)</td><td>Examples: '400 x 100', '800 x 200', ...</td></tr>";
+
+ $html .= "<tr><td>Data X-Range:</td><td>" . $cgi->textfield(
+ -name => 'start',
+ -default => $start,
+ -size => 15,
+ -maxlength => 20,
+ -class => 'textfield',
+ ) . "</td><td>-</td><td>" . $cgi->textfield(
+ -name => 'end',
+ -default => $end,
+ -size => 15,
+ -maxlength => 20,
+ -class => 'textfield',
+ ) . "</td><td>(time)</td><td>Examples: '-2d - now', '24-Dec-2004 - +48h', ...</td></tr>";
+
+ $html .= "<tr><td>Data Y-Range:</td><td>" . $cgi->textfield(
+ -name => 'ulimit',
+ -default => $ulimit,
+ -size => 15,
+ -maxlength => 10,
+ -class => 'textfield',
+ ) . "</td><td>-</td><td>" . $cgi->textfield(
+ -name => 'llimit',
+ -default => $llimit,
+ -size => 15,
+ -maxlength => 10,
+ -class => 'textfield',
+ ) . "</td><td>(Bit/s)</td><td>Examples: '2K - -1K', '4M - 2M', ...</td></tr>";
+
+ $html .= " <tr>\n";
+ $html .= " <td colspan=4>". $cgi->submit(-name => "Update Graph", -class => "submit") . "</td>\n";
+ $html .= " </tr>\n";
+
+ $html .= "</table>\n";
+ $html << 1;
+
+
+ # send out page
$html->undivert(0);
+ print STDOUT $cgi->header(
+ -type => 'text/html',
+ -content_length => length($html),
+ -expires => '+5m'
+ ) . $html;
+
+ # cleanup
+ undef $html;
+ }
+ else {
+ #
+ # output HTML page: TOP-LEVEL SUMMARY
+ #
+
+ # generate HTML page diversion
+ my $html = new String::Divert;
+ $html->overload(1);
+
+ $html .=
+ "<html>\n" .
+ " <head>\n" .
+ " " . $html->folder("head") .
+ " </head>\n" .
+ " <body>\n" .
+ " " . $html->folder("body") .
+ " </body>\n" .
+ "</html>\n";
- # FIXME
+ $html >> "head";
+ $html .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"".$cgi->url(-relative => 1)."?css=1\">\n";
+ $html << 1;
+
+ $html >> "body";
+ $html .= "<table class=\"flow2rrd\" border=0 cellpadding=0 cellspacing=0>\n";
+ $html .= " <tr>\n";
+ $html .= " <td class=\"header\">\n";
+ $html .= " " . $html->folder("header");
+ $html .= " </td>\n";
+ $html .= " <tr>\n";
+ $html .= " </tr>\n";
+ $html .= " <td class=\"canvas\">\n";
+ $html .= " " . $html->folder("canvas");
+ $html .= " </td>\n";
+ $html .= " <tr>\n";
+ $html .= " </tr>\n";
+ $html .= " <td class=\"footer\">\n";
+ $html .= " " . $html->folder("footer");
+ $html .= " </td>\n";
+ $html .= " </tr>\n";
+ $html .= "</table>\n";
+ $html << 1;
+
+ $html >> "header";
+ $html .= "<a href=\"".$cgi->url(-relative => 1)."\">Real-Time Network Statistics</a>";
+ $html << 1;
+ $html >> "footer";
+ $html .= "<a href=\"$my->{-proghome}\">$my->{-progname}</a> $my->{-progvers} ($my->{-progdate})";
+ $html << 1;
+
+ $html >> "canvas";
+ $html .= "<table border=0 cellpadding=0 cellspacing=0>\n";
+ $html .= " <tr>\n";
+ for (my $i = 0; $i < @{$cfg->{'Host'}}; $i++) {
+ $html .= " <td>\n";
+ $html .= " " . $html->folder("col$i");
+ $html .= " </td>\n";
+ }
+ $html .= " <tr>\n";
+ $html .= "</table>\n";
+ $html << 1;
+
+ for (my $i = 0; $i < @{$cfg->{'Host'}}; $i++) {
+ my $host = $cfg->{'Host'}->[$i];
+ $html >> "col$i";
+ $html .= "<table border=0 cellpadding=0 cellspacing=0>\n";
+ $html .= " <tr>\n";
+ $html .= " <td>\n";
+ my $url = $cgi->url(-relative => 1) . "?explore=$host->{-name}";
+ my $img = $cgi->url(-relative => 1) . "?graph=$host->{-name}\@400:100:-48h:now:0:0";
+ $html .= " <a href=\"$url\"><img src=\"$img\" border=0></a>\n";
+ $html .= " </td>\n";
+ $html .= " </tr>\n";
+ foreach my $target (@{$host->{-target}->{-order}}) {
+ $html .= " <tr>\n";
+ $html .= " <td>\n";
+ my $url = $cgi->url(-relative => 1) . "?explore=$host->{-name}:$target";
+ my $img = $cgi->url(-relative => 1) . "?graph=$host->{-name}:$target\@400:100:-48h:now:0:0";
+ $html .= " <a href=\"$url\"><img src=\"$img\" border=0></a>\n";
+ $html .= " </td>\n";
+ $html .= " </tr>\n";
+ }
+ $html .= "</table>\n";
+ $html << 1;
+ }
# send out page
+ $html->undivert(0);
print STDOUT $cgi->header(
-type => 'text/html',
-content_length => length($html),
|