diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..d68d852 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,24 @@ +$Id: ChangeLog,v 1.2 2002/01/04 17:13:26 kas Exp $ + +Fri Jan 4 18:00:39 CET 2002 + +Directory mode almost fully implemented. +Bumped the version number to 0.3 + + +Wed Jan 2 17:57:31 CET 2002 + +The Directory[] directive is now visible in the URL. + + +Tue Dec 18 14:06:29 CET 2001 + +Implemented the following mrtg.cfg directives: + Unscaled, WithPeak, XZoom, YZoom +Fixed the wrong behaviour of XScale, YScale. +Added $version as the separate variable. +Fixed the global options so that the graph still starts at y_min=0, even + if there is no time when the variable is near 0. +Fixed the [^], [_], and [$] behaviour for multiple configuration files + (the value should not remain defined from the previous cfg. file). + diff --git a/FAQ b/FAQ index 0a9aa76..cd32725 100644 --- a/FAQ +++ b/FAQ @@ -1,4 +1,4 @@ -$Id: FAQ,v 1.3 2001/12/17 17:30:36 kas Exp $ +$Id: FAQ,v 1.4 2002/01/04 17:18:17 kas Exp $ Q: What is this? A: The mrtg-rrd.cgi scripts is a replacement for the 14all.cgi script @@ -52,8 +52,7 @@ A: No, but it understands a very large subset of MRTG directives. See the Q: Why to develop another 14all.cgi? A: This script tries to produce its output as much similar to the original MRTG output as possible. It also implements (I believe) much bigger - subset of MRTG configuration directives than 14all.cgi. On the other - hand, it doesn't do the directory index files (yet). + subset of MRTG configuration directives than 14all.cgi. Q: Where can I find the newest version? A: Look at http://www.fi.muni.cz/~kas/mrtg-rrd/ - this is the project home diff --git a/Makefile b/Makefile index dbca3b3..373fd26 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ -# $Id: Makefile,v 1.1 2001/12/17 15:38:07 kas Exp $ +# $Id: Makefile,v 1.3 2002/01/04 17:16:05 kas Exp $ -FILES = COPYING FAQ TODO Makefile mrtg-rrd.cgi +FILES = COPYING FAQ TODO Makefile mrtg-rrd.cgi ChangeLog TARGETS = mrtg-rrd.fcgi -VERSION = 0.1 +VERSION = 0.3 all: $(TARGETS) diff --git a/TODO b/TODO index 32e41e4..5a244af 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -$Id: TODO,v 1.1 2001/12/17 15:06:37 kas Exp $ +$Id: TODO,v 1.3 2002/01/04 17:13:27 kas Exp $ Unimplemented mrtg.cfg directives ================================= @@ -9,17 +9,12 @@ NoMib2: (and displaying system uptime) I don't want to query routers from Language: TODO Libadd: (handled separately) Don't want to mess with more BEGIN{} statements. RouterUptime: I don't want to query the routers from the CGI. -Unscaled: don't know how to tell this to rrdgraph -WithPeak: don't know how to tell this to rrdgraph Extension: (has no meaning) -Directory: (partly implemented - reads the directive, but it is not visible in URL) -XZoom: don't know how to tell this to rrdgraph -YZoom: don't know how to tell this to rrdgraph YTics: don't know how to tell this to rrdgraph YTicsFactor: don't know how to tell this to rrdgraph Step: don't know what this should do Option: - growright: it is the default (don't know how to make growleft grapsh) + growright: it is the default (don't know how to make growleft graphs) noi: TODO noo: TODO SetEnv: Makes sense only for Target[]. Not implemented. @@ -31,10 +26,20 @@ Other TODO/missing items Option for setting the image format (PNG/GIF). -Directory/all graphs listing. +More customizable directory listing. Exact time of the previous VRULE (for month and year graphs). The MRTG-like legend on the X axis of the daily graph (every 2nd hour instead of every 6th our and ":00" appended). + +Known bugs +========== + +* It should complain if the RRD::graph() fails. +* Directory mode does not honour the "Suppress[]: d" and refers to the daily + graph unconditionally. +* Directory mode does not generate the "WIDHT=" and "HEIGHT=" parameters + to the tag. + diff --git a/mrtg-rrd.cgi b/mrtg-rrd.cgi index e8b199a..0134f5f 100755 --- a/mrtg-rrd.cgi +++ b/mrtg-rrd.cgi @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# $Id: mrtg-rrd.cgi,v 1.3 2001/12/17 17:17:42 kas Exp $ +# $Id: mrtg-rrd.cgi,v 1.6 2002/01/04 17:13:27 kas Exp $ # # mrtg-rrd.cgi: The script for generating graphs for MRTG statistics. # @@ -31,10 +31,13 @@ use POSIX qw(strftime); use lib '/usr/lib/perl5/5.00503/i386-linux'; use RRDs; -use vars qw(@config_files @all_config_files %targets %config $config_time); +use vars qw(@config_files @all_config_files %targets %config $config_time + %directories $version); # EDIT THIS to reflect all your MRTG config files -BEGIN { @config_files = qw(/home/fadmin/mrtg/mrtg.cfg); } +BEGIN { @config_files = qw(/home/fadmin/mrtg/new/mrtg.cfg); } + +$version = '0.3'; sub handler ($) { @@ -42,15 +45,29 @@ sub handler ($) try_read_config(); - my ($stat, $ext) = ($q->path_info() =~ - /^\/(.+)(\.html|-(day|week|month|year)\.png)$/); + my $path = $q->path_info(); + $path =~ s/^\///; + $path =~ s/\/$//; + if (defined $directories{$path}) { + if ($q->path_info() =~ /\/$/) { + print_dir($path); + } else { + print "Location: ", $q->url(-path_info=>1), "/\n\n"; + } + return; + } + + my ($dir, $stat, $ext) = ($q->path_info() =~ + /^(.*)\/([^\/]+)(\.html|-(day|week|month|year)\.png)$/); -# $stat ||= 'ares.fi.muni.cz.eth2'; -# $ext ||= '.html'; + $dir =~ s/^\///; print_error("Undefined statistics") unless defined $targets{$stat}; + print_error("Incorrect directory") + unless defined $targets{$stat}{direcory} || $targets{$stat}{directory} eq $dir; + my $tgt = $targets{$stat}; common_args($stat, $tgt, $q); @@ -89,7 +106,7 @@ sub do_html($) my @month = do_image($tgt, 'month'); my @year = do_image($tgt, 'year'); - http_headers('text/html', $tgt); + http_headers('text/html'); print <<'EOF'; @@ -122,55 +139,38 @@ EOF html_graph($tgt, 'month', 'Monthly', '2 Hour', \@month); html_graph($tgt, 'year', 'Yearly', '1 Day', \@year); - print <<"EOF" unless defined $tgt->{options}{nolegend}; -
- - -
-$tgt->{colname1} ###$tgt->{legend1}
-$tgt->{colname2} ###$tgt->{legend2}
-EOF - print <<"EOF" unless defined $tgt->{options}{nobanner}; - + unless (defined $tgt->{options}{nolegend}) { + print < - - - - - - -
- - - - - - - - - - - - -
-version 2.9.17 -Tobias Oetiker -<oetiker\@ee.ethz.ch> -
-Dave Rand <dlr\@bungi.com> -
-mrtg-rrd.cgi version 0.1 - -Jan "Yenya" Kasprzak -<kas\@fi.muni.cz> -
+ + + + + + + +EOF + if ($tgt->{withpeak} ne '') { + print < + + + + + +EOF + } + print <
+ $tgt->{colname1} ###$tgt->{legend1}
+ $tgt->{colname2} ###$tgt->{legend2}
+ $tgt->{colname3} ###$tgt->{legend3}
+ $tgt->{colname4} ###$tgt->{legend4}
EOF + } + + print_banner() unless defined $tgt->{options}{nobanner}; + print $tgt->{pagefoot} if defined $tgt->{pagefoot}; print "\n", <<'EOF'; @@ -204,8 +204,8 @@ sub html_graph($$$$$) my $x = $params->[1]; my $y = $params->[2]; - $x *= $tgt->{xscale} if defined $tgt->{xscale}; - $y *= $tgt->{yscale} if defined $tgt->{yscale}; + $x *= $tgt->{xzoom} if defined $tgt->{xzoom}; + $y *= $tgt->{yzoom} if defined $tgt->{yzoom}; my $kilo = $tgt->{kilo}; my @kmg = split(',', $tgt->{kmg}); @@ -309,9 +309,9 @@ sub do_percent($$) @percent; } -sub http_headers($$) +sub http_headers($) { - my ($content_type, $target) = @_; + my ($content_type) = @_; print <<"EOF"; Content-Type: $content_type @@ -325,7 +325,7 @@ EOF print "\n"; } -sub do_image($$$) +sub do_image($$) { my ($target, $ext) = @_; @@ -341,39 +341,65 @@ sub do_image($$$) my $oldsec; my $back; + my $unscaled; + my $withpeak; + if ($ext eq 'day') { $seconds = strftime("%s", @t); $back = 30*3600; # 30 hours $oldsec = $seconds - 86400; + $unscaled = 1 if $target->{unscaled} =~ /d/; + $withpeak = 1 if $target->{withpeak} =~ /d/; } elsif ($ext eq 'week') { $seconds = strftime("%s", @t); $t[6] = ($t[6]+6) % 7; $seconds -= $t[6]*86400; $back = 8*86400; # 8 days $oldsec = $seconds - 7*86400; + $unscaled = 1 if $target->{unscaled} =~ /w/; + $withpeak = 1 if $target->{withpeak} =~ /w/; } elsif ($ext eq 'month') { $t[3] = 1; $seconds = strftime("%s", @t); $back = 36*86400; # 36 days $oldsec = $seconds - 30*86400; # FIXME (the right # of days!!) + $unscaled = 1 if $target->{unscaled} =~ /m/; + $withpeak = 1 if $target->{withpeak} =~ /m/; } elsif ($ext eq 'year') { $t[3] = 1; $t[4] = 0; $seconds = strftime("%s", @t); $back = 396*86400; # 365 + 31 days $oldsec = $seconds - 365*86400; # FIXME (the right # of days!!) + $unscaled = 1 if $target->{unscaled} =~ /y/; + $withpeak = 1 if $target->{withpeak} =~ /y/; } else { print_error("Unknown file extension: $ext"); } - my @rv = RRDs::graph($file, '-s', "-$back", @{$target->{args}}, - "VRULE:$oldsec#ff0000", "VRULE:$seconds#ff0000"); + my @local_args; + + if ($unscaled) { + @local_args = ('-u', $target->{maxbytes1}); + push @local_args, '--rigid' unless defined $target->{absmax}; + } + + my @local_args_end; + + if ($withpeak) { + push @local_args_end, 'LINE1:maxin'.$target->{col3}.':MaxIn', + 'LINE1:maxout'.$target->{col4}.':MaxOut'; + } + + my @rv = RRDs::graph($file, '-s', "-$back", @local_args, + @{$target->{args}}, "VRULE:$oldsec#ff0000", + "VRULE:$seconds#ff0000", @local_args_end); # In array context just return the values return @rv if wantarray; # Not in array context ==> print out the PNG file. - http_headers('image/png', $target); + http_headers('image/png'); open PNG, "<$file" or print_error("Can't open $file: $!"); my $buf; @@ -392,20 +418,20 @@ sub common_args($$$) $target->{name} = $name; - if (defined $target->{directory}) { - $target->{directory} .= '/' - unless $target->{directory} =~ /\/$/; - } else { - $target->{directory} = ''; - } + $target->{directory} = '' + unless defined $target->{directory}; + + my $tdir = $target->{directory}; + $tdir .= '/' + unless $tdir eq '' || $tdir =~ /\/$/; - $target->{url} = $q->url . '/' . $name; + $target->{url} = $q->url . '/' . $tdir . $name; my $dir = $config{workdir}; $dir = $config{logdir} if defined $config{logdir}; - $target->{rrd} = $dir . '/' . $target->{directory} . $name . '.rrd'; + $target->{rrd} = $dir . '/' . $tdir . $name . '.rrd'; %{$target->{options}} = () unless defined %{$target->{options}}; @@ -416,13 +442,13 @@ sub common_args($$$) $target->{suppress} ||= ''; - $target->{day} = $dir . '/' . $target->{directory} . $name - . '-day.png' unless $target->{suppress} =~ /d/; - $target->{week} = $dir . '/' . $target->{directory} . $name + $target->{day} = $dir . '/' . $tdir . $name + . '-day.png' unless $tdir =~ /d/; + $target->{week} = $dir . '/' . $tdir . $name . '-week.png' unless $target->{suppress} =~ /w/; - $target->{month} = $dir . '/' . $target->{directory} . $name + $target->{month} = $dir . '/' . $tdir . $name . '-month.png' unless $target->{suppress} =~ /m/; - $target->{year} = $dir . '/' . $target->{directory} . $name + $target->{year} = $dir . '/' . $tdir . $name . '-year.png' unless $target->{suppress} =~ /y/; $target->{maxbytes1} = $target->{maxbytes} @@ -435,7 +461,7 @@ sub common_args($$$) push @args, '--lazy', '-c', 'FONT#000000', '-c', 'MGRID#000000', '-c', 'FRAME#000000', - '-g'; + '-g', '-l', '0'; $target->{background} = '#f5f5f5' unless defined $target->{background}; @@ -455,11 +481,21 @@ sub common_args($$$) push @args, '-b', $target->{kilo} if defined $target->{kilo}; - push @args, '-w', $target->{xsize} - if defined $target->{xsize}; + if (defined $target->{xsize}) { + if (defined $target->{xscale}) { + push @args, '-w', $target->{xsize}*$target->{xscale}; + } else { + push @args, '-w', $target->{xsize}; + } + } - push @args, '-h', $target->{ysize} - if defined $target->{ysize}; + if (defined $target->{ysize}) { + if (defined $target->{yscale}) { + push @args, '-h', $target->{ysize}*$target->{yscale}; + } else { + push @args, '-h', $target->{ysize}; + } + } my $scale = 1; @@ -477,6 +513,10 @@ sub common_args($$$) unless defined $target->{legend1}; $target->{legend2} = 'Outgoing Traffic in Bits per Second' unless defined $target->{legend2}; + $target->{legend3} = 'Peak Incoming Traffic in Bits per Second' + unless defined $target->{legend3}; + $target->{legend4} = 'Peak Outgoing Traffic in Bits per Second' + unless defined $target->{legend4}; $target->{shortlegend} = 'b/s' unless defined $target->{shortlegend}; } else { @@ -486,6 +526,10 @@ sub common_args($$$) unless defined $target->{legend1}; $target->{legend2} = 'Outgoing Traffic in Bytes per Second' unless defined $target->{legend2}; + $target->{legend3} = 'Peak Incoming Traffic in Bytes per Second' + unless defined $target->{legend3}; + $target->{legend4} = 'Peak Outgoing Traffic in Bytes per Second' + unless defined $target->{legend4}; $target->{shortlegend} = 'B/s' unless defined $target->{shortlegend}; } @@ -494,10 +538,16 @@ sub common_args($$$) push @args, "DEF:in0=$target->{rrd}:ds0:AVERAGE", "CDEF:in=in0,$scale,*", "DEF:out0=$target->{rrd}:ds1:AVERAGE", - "CDEF:out=out0,$scale,*"; + "CDEF:out=out0,$scale,*", + "DEF:maxin0=$target->{rrd}:ds0:MAX", + "CDEF:maxin=maxin0,$scale,*", + "DEF:maxout0=$target->{rrd}:ds1:MAX", + "CDEF:maxout=maxout0,$scale,*"; } else { push @args, "DEF:in=$target->{rrd}:ds0:AVERAGE", - "DEF:out=$target->{rrd}:ds1:AVERAGE"; + "DEF:out=$target->{rrd}:ds1:AVERAGE", + "DEF:maxin=$target->{rrd}:ds0:MAX", + "DEF:maxout=$target->{rrd}:ds1:MAX"; } my $i=1; @@ -559,6 +609,9 @@ sub try_read_config() colours => 'GREEN#00cc00,BLUE#0000ff,DARK GREEN#006600,MAGENTA#ff00ff,AMBER#ef9f4f', legendi => ' In:', legendo => ' Out:', + unscaled => '', + withpeak => '', + directory => '', ); %config = ( @@ -569,16 +622,21 @@ sub try_read_config() %targets = (); - %{$targets{_}} = %defaults; - %{$targets{'^'}} = (); - %{$targets{'$'}} = (); - @all_config_files = @config_files; + my $order = 0; for my $cfgfile (@config_files) { - read_mrtg_config($cfgfile, \%defaults); + %{$targets{_}} = %defaults; + %{$targets{'^'}} = (); + %{$targets{'$'}} = (); + + read_mrtg_config($cfgfile, \%defaults, \$order); } + delete $targets{'^'}; + delete $targets{_}; + delete $targets{'$'}; + if (defined $config{pathadd}) { $ENV{PATH} .= ':'.$config{pathadd}; } @@ -587,14 +645,16 @@ sub try_read_config() # use lib $config{libadd} # } + parse_directories(); + $config_time = time; } -sub read_mrtg_config($$); +sub read_mrtg_config($$$); -sub read_mrtg_config($$) +sub read_mrtg_config($$$) { - my ($file, $def) = @_; + my ($file, $def, $order) = @_; my %defaults = %$def; @@ -620,6 +680,7 @@ sub read_mrtg_config($$) my ($tgt, $opt, $val) = (lc($2), lc($1), $3); unless (exists $targets{$tgt}) { %{$targets{$tgt}} = %{$targets{_}}; + $targets{$tgt}{order} = ++$$order; } if ($tgt eq '_' && $val eq '') { if ($defaults{$opt}) { @@ -643,7 +704,7 @@ sub read_mrtg_config($$) next; } elsif (/^Include *: *(\S*)$/) { push @all_config_files, $1; - read_mrtg_config($1, $def); + read_mrtg_config($1, $def, $order); next; } elsif (/^([\w\d]+) *: *(\S*)$/) { my ($opt, $val) = (lc($1), lc($2)); @@ -654,8 +715,118 @@ sub read_mrtg_config($$) } } -sub dump_targets() { +sub parse_directories { + %directories = (); + # FIXME: the sort is expensive + for my $name (sort { $targets{$a}{order} <=> $targets{$b}{order} } keys %targets) { + my $dir = $targets{$name}{directory} + if defined $targets{$name}{directory}; + $dir = '' unless defined $dir; + + my $prefix = ''; + for my $component (split /\/+/, $dir) { + add_dir($prefix, $component); + $prefix .= $component . '/'; + } + + add_dir($dir, $name); + + } +} + +sub add_dir($$) { + my ($dir, $name) = @_; + + @{$directories{$dir}} = () unless defined $directories{$dir}; + push (@{$directories{$dir}}, $name) + unless defined $directories{$dir.$name}; +} + +sub print_dir($) { + my ($dir) = @_; + + my $dir1 = $dir . '/'; + + http_headers('text/html'); + + print < + +MRTG: Directory $dir1 + +

MRTG graphs and subdirectories in directory $dir1

+ +
    +EOF + for my $item (@{$directories{$dir}}) { + if (defined $targets{$item}) { + my $itemname = $item; + $itemname = $targets{$item}{title} + if defined $targets{$item}{title}; + print <$itemname
    + $item +

    +EOF + } else { + print <$item/ +EOF + } + } + + print "
\n"; + print_banner(); + print "\n\n"; +} + +sub print_banner() { + print < + + + + + + +
+ + + + + + + + + + + + +
+version 2.9.17 +Tobias Oetiker +<oetiker\@ee.ethz.ch> +
+Dave Rand <dlr\@bungi.com> +
+mrtg-rrd.cgi version $version + +Jan "Yenya" Kasprzak +<kas\@fi.muni.cz> +
+EOF + +} + +sub dump_targets() { for my $tgt (keys %targets) { print "Target $tgt:\n"; for my $opt (keys %{$targets{$tgt}}) { @@ -673,13 +844,23 @@ sub dump_targets() { } sub dump_config() { - print "Config:\n"; for my $opt (keys %config) { print $opt, ": ", $config{$opt}, "\n"; } } +sub dump_directories { + print "Directories:\n"; + + for my $dir (keys %directories) { + print "Directory $dir:\n"; + for my $item (@{$directories{$dir}}) { + print "\t$item\n"; + } + } +} + sub print_error(@) { print "Content-Type: text/plain\n\nError: ", join(' ', @_), "\n";