From 978eb12a3e6725e4615e75ab090e456781b04703 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Fri, 7 Apr 2006 13:55:13 +0000 Subject: [PATCH] Merge from andun. Patches applied: * korbekk@start.no--2006--public/nms--kjetil--1.0--base-0 tag of sgunderson@bigfoot.com--2006/nms--mainline--1.0--patch-29 * korbekk@start.no--2006--public/nms--kjetil--1.0--patch-1 new graph generating code in C to make it faster (about 5x faster) --- web/ext/Makefile.PL | 10 ++ web/ext/Makefile.super | 12 ++ web/ext/glue.pl | 13 +++ web/ext/graph.c | 246 +++++++++++++++++++++++++++++++++++++++++ web/ext/graph.h | 43 +++++++ web/ext/graph.i | 29 +++++ web/ext/showswitch.pl | 237 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 590 insertions(+) create mode 100644 web/ext/Makefile.PL create mode 100644 web/ext/Makefile.super create mode 100644 web/ext/glue.pl create mode 100644 web/ext/graph.c create mode 100644 web/ext/graph.h create mode 100644 web/ext/graph.i create mode 100755 web/ext/showswitch.pl diff --git a/web/ext/Makefile.PL b/web/ext/Makefile.PL new file mode 100644 index 0000000..e12c8d6 --- /dev/null +++ b/web/ext/Makefile.PL @@ -0,0 +1,10 @@ +my $libs = `pkg-config --libs cairo`; +my $inc = `pkg-config --cflags cairo`; + +use ExtUtils::MakeMaker; +WriteMakefile( + 'NAME' => 'mygraph', # Name of module + 'LIBS' => $libs, + 'INC' => $inc, + 'OBJECT' => 'graph_wrap.o' # All object files +); diff --git a/web/ext/Makefile.super b/web/ext/Makefile.super new file mode 100644 index 0000000..f88fc9f --- /dev/null +++ b/web/ext/Makefile.super @@ -0,0 +1,12 @@ +all: + swig -perl5 graph.i + perl Makefile.PL + make + mv blib/arch/auto/mygraph/mygraph.so ./ + mv blib/lib/mygraph.pm ./ + rm -fr blib + rm pm_to_blib + +clean: + rm -f mygraph.pm mygraph.so graph_wrap.c graph_wrap.o \ + Makefile mygraph.bs diff --git a/web/ext/glue.pl b/web/ext/glue.pl new file mode 100644 index 0000000..c1f384c --- /dev/null +++ b/web/ext/glue.pl @@ -0,0 +1,13 @@ +#glue + +sub create_array { + my $len = scalar(@_); + my $ia = mygraph::int_array($len); + for (my $i = 0; $i < $len; $i++) { + my $val = shift; + mygraph::int_set($ia,$i,$val); + } + return $ia; +} + +1; diff --git a/web/ext/graph.c b/web/ext/graph.c new file mode 100644 index 0000000..ddd726b --- /dev/null +++ b/web/ext/graph.c @@ -0,0 +1,246 @@ +#include "graph.h" + + +void +mygraph_fill_background(graph *mygraph); + +void +mygraph_draw_graph (cairo_t *cr, + int x, + int y ); + +graph * +mygraph_new (int width, int height) +{ + graph *mygraph; + mygraph = malloc(sizeof(graph)); + mygraph->width = width; + mygraph->height = height; + + + int stride = width * 4; + unsigned char *image; + image = (unsigned char *) malloc (sizeof(unsigned char) * stride * height); + + mygraph->surface = cairo_image_surface_create_for_data (image, CAIRO_FORMAT_ARGB32, + width, height, stride); + mygraph->cr = cairo_create (mygraph->surface); + + cairo_set_source_rgb (mygraph->cr, 1.0, 1.0, 1.0); + mygraph_fill_background (mygraph); + + cairo_select_font_face (mygraph->cr, "Sans", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size (mygraph->cr, 10); + + cairo_set_antialias(mygraph->cr, CAIRO_ANTIALIAS_NONE); + cairo_set_line_width (mygraph->cr, 1.0); + + return mygraph; +} + +graph * +mygraph_make_graph (graph *mygraph, float min_x, float max_x, + float min_y, float max_y, int tickgran) +{ + int xoffset = 70; + cairo_text_extents_t extents; + + mygraph->xoffset = xoffset; + mygraph->min_x = min_x; + mygraph->max_x = max_x; + mygraph->min_y = min_y; + mygraph->max_y = max_y; + +/* cairo_t *cr; */ +/* cairo_surface_t *surface; */ + + float xs = ((float)mygraph->width - (float)(xoffset+2)) / + (float)(max_x - min_x); + + float ys = ((float)mygraph->height - 33.) / (float)(min_y - max_y); + + mygraph->xs = xs; + mygraph->ys = ys; + + float starthour = fmod((min_x + (float)tz_local_offset()) / 3600., 24.); + float diff, center, begin, end; + + char string[20]; + + int i; + for (i = 0; i<24; i++) + { // Hour marks and text + if ((i % 2) == 0) + cairo_set_source_rgb (mygraph->cr, 1.0, 1.0, 1.0); + else + cairo_set_source_rgb (mygraph->cr, 0.90, 0.90, 1.0); + + diff = fmod((float)i - starthour + 24., 24.); + begin = (diff * 3600.) * xs; + end = ((fmod(((float)i+1) - starthour + 24., 24.)) * 3600.) * xs; + center = (begin + end) / 2.; + + if (begin > end) + begin = 0.; + + // printf("i: %d, starthour: %f, diff: %f, begin: %f, end: %f\n", i, starthour, diff, begin, end); + + if (begin < 0.) + begin = 0.0; + if (begin > ((float)mygraph->width - ((float)xoffset))) + continue; + // printf("drawing\n"); + + cairo_rectangle (mygraph->cr, xoffset+begin, 0, + end - begin, mygraph->height); + + cairo_fill (mygraph->cr); + + if (begin <= 0.0 || end >= mygraph->width - (xoffset)) + continue; + + cairo_set_source_rgb (mygraph->cr, 0.0, 0.0, 0.0); + + sprintf(string, "%d", i); + // printf("showing string %s\n", string); + + cairo_text_extents (mygraph->cr, string, &extents); + cairo_move_to (mygraph->cr, xoffset + center - + (extents.width/2), + (mygraph->height - extents.height - 2)); + + cairo_show_text (mygraph->cr, string); + + } + + cairo_set_source_rgb (mygraph->cr, 0.4, 0.4, 0.4); + int ytick; + do + { + ytick = ((int)max_y - (int)min_y) / 11; + ytick = ceil (ytick / tickgran) * tickgran; + tickgran *= 0.1; + } while (((int)max_y - (int)min_y) / ytick < 4); + + int y, traf; + for (i = -11; i<12; i++) + { + y = (i * ytick - (int)max_y) * ys + 10; + if (y < 2 || y > mygraph->height - 18) + continue; + +/* printf("draw line at %d\n", y); */ + + cairo_move_to (mygraph->cr, xoffset, y); + cairo_line_to (mygraph->cr, mygraph->width-1, y); + + if (i == 0) + { + + cairo_set_source_rgb (mygraph->cr, 0.0, 0.0, 1.0); + cairo_stroke (mygraph->cr); + cairo_set_source_rgb (mygraph->cr, 0.6, 0.6, 0.6); + } + else + cairo_stroke (mygraph->cr); + + // draw text + traf = 8 * (i * ytick); +/* printf("traffic: %d\n", traf); */ + + if (traf >= 500000000) + sprintf (string, "%.1f Gbit", ((float)traf/1000000000)); + else if (traf >= 500000) + sprintf (string, "%.1f Mbit", ((float)traf/1000000)); + else + sprintf (string, "%.1f kbit", ((float)traf/1000)); + + cairo_text_extents (mygraph->cr, string, &extents); + + if (y - (extents.height/2) < 2 || + y + (extents.height/2) > mygraph->height - (extents.height + 2)) + continue; + + cairo_move_to (mygraph->cr, + xoffset - 4 - extents.width, + y + (extents.height/2)); + + cairo_show_text (mygraph->cr, string); + } + + cairo_rectangle (mygraph->cr, xoffset, 0, mygraph->width-xoffset-1, mygraph->height-1); + + cairo_set_source_rgb (mygraph->cr, 0.0, 0.0, 0.0); + cairo_stroke (mygraph->cr); + + return mygraph; +} + +void +mygraph_plot_series (graph *mygraph, int *xvals, int *yvals, int n_vals, + float r, float g, float b) +{ + int x, y, i; + x = xvals[0]; + y = yvals[0]; + + cairo_set_antialias(mygraph->cr, CAIRO_ANTIALIAS_DEFAULT); + cairo_set_source_rgb (mygraph->cr, r, g, b); + + cairo_move_to (mygraph->cr, + (int)((float)(x - (int)mygraph->min_x) * + mygraph->xs + (float)(mygraph->xoffset + 1)), + (int)((float)(y - (int)mygraph->max_y) * mygraph->ys + 10.)); + +/* printf("Plotting from:\n"); */ +/* printf("(%d, %d) %d (%d, %d)
\n", */ +/* x, y, (int)((float)(x - (int)mygraph->min_x) * mygraph->xs + (float)(mygraph->xoffset + 1)) , */ +/* (int)((float)(x - (int)mygraph->min_x) * mygraph->xs + (float)(mygraph->xoffset + 1)), */ +/* (int)((float)(y - (int)mygraph->max_y) * mygraph->ys + 10.)); */ + + for (i = 1; i < n_vals; i++) + { + + if (mygraph->xs * (xvals[i] - x) < 2 && + mygraph->ys * (yvals[i] - y) > -2) + continue; + + x = xvals[i]; + y = yvals[i]; + + cairo_line_to (mygraph->cr, + (int)((float)(x - (int)mygraph->min_x) * + mygraph->xs + (float)(mygraph->xoffset + 1)), + (int)((float)(y - (int)mygraph->max_y) * mygraph->ys + 10.)); + +/* printf("(%d, %d) = > (%d, %d)
\n", */ +/* x, y, */ +/* (int)((x - (int)x-mygraph->min_x) * (int)mygraph->xs + (int)mygraph->xoffset + 1), */ +/* (int)((y - (int)mygraph->max_y) * (int)mygraph->ys + 10)); */ + + } + + cairo_stroke(mygraph->cr); +} + +void +mygraph_to_file (graph *mygraph, char *filename) +{ + cairo_surface_write_to_png (mygraph->surface, filename); +} + +void +mygraph_cleanup (graph *self) +{ + cairo_destroy (self->cr); + cairo_surface_destroy (self->surface); +} + +void +mygraph_fill_background (graph *mygraph) +{ + cairo_rectangle (mygraph->cr, 0, 0, mygraph->width, mygraph->height); + cairo_fill (mygraph->cr); +} + diff --git a/web/ext/graph.h b/web/ext/graph.h new file mode 100644 index 0000000..2d39217 --- /dev/null +++ b/web/ext/graph.h @@ -0,0 +1,43 @@ +/* Copyright (C) 2006 Kjetil Ørbekk, Norway */ + +#include +#include +#include +#include + +typedef struct _graph { + cairo_t *cr; + cairo_surface_t *surface; + int width; + int height; + int xoffset; + int yoffset; + float min_x; + float max_x; + float min_y; + float max_y; + float xs; + float ys; +} graph; + +int tz_local_offset() { return 7200; } // riktig? + +void mygraph_fill_background(graph *mygraph); + +void mygraph_draw_graph (cairo_t *cr, int x, int y ); + +graph *mygraph_new (int width, int height); + +graph *mygraph_make_graph (graph *mygraph, float min_x, + float max_x, float min_y, float max_y, + int tickgran); + +void mygraph_plot_series (graph *mygraph, int *xvals, int *yvals, + int n_vals, float r, float g, float b); + +void mygraph_to_file (graph *mygraph, char *filename); + +void mygraph_cleanup (graph *self); + +void mygraph_fill_background (graph *mygraph); + diff --git a/web/ext/graph.i b/web/ext/graph.i new file mode 100644 index 0000000..b04056c --- /dev/null +++ b/web/ext/graph.i @@ -0,0 +1,29 @@ +%module mygraph +%{ +#include "graph.c" +%} + +%inline +%{ + // Add some helper functions for C arrays + int *int_array(int size) { + return (int *) malloc(sizeof(int)*size); + } + void int_destroy(int *a) { + free(a); + } + void int_set(int *a, int i, int val) { + a[i] = val; + } + int int_get(int *a, int i) { + return a[i]; + } +%} + +graph *mygraph_new (int width, int height); +graph *mygraph_make_graph (graph *mygraph, float min_x, + float max_x, float min_y, float max_y, int tickgran); +void mygraph_cleanup (graph *mygraph); +void mygraph_to_file (graph *mygraph, char *filename); +void mygraph_plot_series (graph *mygraph, int *xvals, int *yvals, int n_vals, + float r, float g, float b); diff --git a/web/ext/showswitch.pl b/web/ext/showswitch.pl new file mode 100755 index 0000000..8cfa390 --- /dev/null +++ b/web/ext/showswitch.pl @@ -0,0 +1,237 @@ +#!/usr/bin/perl +use CGI; +use DBI; +use Time::HiRes; +use POSIX ":sys_wait_h"; +use strict; +use warnings; + +use mygraph; +require 'glue.pl'; + +my $cgi = CGI->new; +my $switch = 9; #$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)); + +my $graph = mygraph::mygraph_new($width, $height); + +my $start = [Time::HiRes::gettimeofday]; +my $dbh = DBI->connect("dbi:Pg:dbname=tg", "tg", "tg06") + or die "Couldn't connect to database"; + +# Fetch the name +my $ref = $dbh->selectrow_hashref('SELECT sysname FROM switches WHERE switch=?', undef, $switch); + +print $cgi->header(-type=>'text/html; charset=utf-8'); +print <<"EOF"; + + + snmp + + +

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

+EOF + +my $q = $dbh->prepare('select port,coalesce(description, \'Port \' || port) as description,extract(epoch from time) as time,bytes_in,bytes_out from polls natural join switches natural left join portnames where time between \'2005-03-23 05:17:36+01\' and \'2005-03-24 05:17:36+01\' and switch=? order by switch,port,time;'); +$q->execute($switch); + +my (@totx, @toty1, @toty2) = (); + +my (@x, @y1, @y2) = (); +my $last_port = -1; +my $portname = ""; +my $min_x = 1111637856.70337; #time; +my $max_x = 1111551456; #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; +while (my $ref = $q->fetchrow_hashref()) { + my $time = $ref->{'time'}; + my $in = $ref->{'bytes_in'}; + my $out = $ref->{'bytes_out'}; + next if ($time == $prev_time); + + if ($ref->{'port'} != $last_port) { + if ($last_port != -1) { + my $filename = "$switch-$last_port-$width-$height.png"; + + # reap children + waitpid(-1, WNOHANG); + + my $numpids = 0; + + my $pid = fork(); + if ($pid == 0) { + # write out the graph + # print "$width, $height, $min_x, $max_x, $min_y, $max_y, 5
\n"; + + my $x_c = create_array(@x); + my $y1_c = create_array(@y1); + my $y2_c = create_array(@y2); + +# my $startthis = [Time::HiRes::gettimeofday]; + mygraph::mygraph_make_graph($graph, $min_x, $max_x, $min_y, $max_y, 5); + mygraph::mygraph_plot_series($graph, $x_c, $y1_c, $#x, 255, 0, 0); + mygraph::mygraph_plot_series($graph, $x_c, $y2_c, $#x, 0, 0, 255); + mygraph::mygraph_to_file($graph, "img/$filename"); +# mygraph::mygraph_cleanup ($graph); +# my $elapsedthis = Time::HiRes::tv_interval($startthis); +# printf "$elapsedthis seconds
\n"; + + 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->{'bytes_in'}; + $prev_out = $ref->{'bytes_out'}; + $last_port = $ref->{'port'}; + $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) { + # 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); + } + + # 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 = "$switch-$last_port-$width-$height.png"; + +my $pid = fork(); +if ($pid == 0) { + my $x_c = create_array(@x); + my $y1_c = create_array(@y1); + my $y2_c = create_array(@y2); + +# my $startthis = [Time::HiRes::gettimeofday]; + mygraph::mygraph_make_graph($graph, $min_x, $max_x, $min_y, $max_y, 5); + mygraph::mygraph_plot_series($graph, $x_c, $y1_c, $#x, 255, 0, 0); + mygraph::mygraph_plot_series($graph, $x_c, $y2_c, $#x, 0, 0, 255); + mygraph::mygraph_to_file($graph, "img/$filename"); +# mygraph::mygraph_cleanup ($graph); +# my $elapsedthis = Time::HiRes::tv_interval($startthis); +# printf "$elapsedthis seconds
\n"; + + exit; +} + +push @pids, $pid; + +$resthtml .= "

$portname

\n"; +$resthtml .= "

\n"; + +# total graph +$filename = "$switch-$width-$height.png"; +my $x_c = create_array(@totx); +my $y1_c = create_array(@toty1); +my $y2_c = create_array(@toty2); + +# my $startthis = [Time::HiRes::gettimeofday]; +mygraph::mygraph_make_graph($graph, $min_x, $max_x, $min_y, $max_y, 5); +mygraph::mygraph_plot_series($graph, $x_c, $y1_c, $#totx, 255, 0, 0); +mygraph::mygraph_plot_series($graph, $x_c, $y2_c, $#totx, 0, 0, 255); +mygraph::mygraph_to_file($graph, "img/$filename"); +#mygraph::mygraph_cleanup ($graph); +# my $elapsedthis = Time::HiRes::tv_interval($startthis); +# printf "$elapsedthis seconds
\n"; + +# 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"; -- 2.39.2