From 2290eae115e5f0b00632582498e2e8f0ff23f591 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Sat, 1 Sep 2007 20:59:27 +0200 Subject: [PATCH] contrib/collection.cgi: Quite some changes, but still incomplete and not very usable.. --- contrib/collection.cgi | 1153 ++++++++++++++++++++++++++++++---------- 1 file changed, 881 insertions(+), 272 deletions(-) diff --git a/contrib/collection.cgi b/contrib/collection.cgi index de42b786..159d9e6b 100755 --- a/contrib/collection.cgi +++ b/contrib/collection.cgi @@ -15,11 +15,21 @@ our $Config = "/etc/collection.conf"; our @DataDirs = (); our $LibDir; -our @RRDDefaultArgs = ('-w', '800'); +our $ValidTimespan = +{ + hour => 3600, + day => 86400, + week => 7 * 86400, + month => 31 * 86400, + year => 366 * 86400 +}; + +our @RRDDefaultArgs = ('-w', '400'); our $Args = {}; our $GraphDefs; +our $MetaGraphDefs = {}; load_graph_definitions (); for (qw(action host plugin plugin_instance type type_instance timespan)) @@ -76,128 +86,6 @@ sub read_config close ($fh); } # read_config -#sub validate_args -#{ -# if (!$Args->{'host'} || ($Args->{'host'} =~ m#/#) -# || !$Args->{'plugin'} || ($Args->{'plugin'} =~ m#/#) -# || (defined ($Args->{'plugin_instance'}) && ($Args->{'plugin_instance'} =~ m#/#)) -# || !$Args->{'type'} || ($Args->{'type'} =~ m#/#) -# || (defined ($Args->{'type_instance'}) && ($Args->{'type_instance'} =~ m#/#))) -# { -# delete ($Args->{'host'}); -# delete ($Args->{'plugin'}); -# delete ($Args->{'plugin_instance'}); -# delete ($Args->{'type'}); -# delete ($Args->{'type_instance'}); -# } -# elsif ($Args->{'type_instance'} eq '*') -# { -# my $subdir = $Args->{'host'} . '/' -# . $Args->{'plugin'} . (defined ($Args->{'plugin_instance'}) ? ('-' . $Args->{'plugin_instance'}) : ''); -# my %type_instances = (); -# my $regex_str = '^' . quotemeta ($Args->{'type'}) . '-(.*)\.rrd'; -# my $regex = qr/$regex_str/; -# my $mtime = 0; -# -# for (my $i = 0; $i < @DataDirs; $i++) -# { -# my $dh; -# my @files = (); -# opendir ($dh, $DataDirs[$i] . '/' . $subdir) or next; -# for (readdir ($dh)) -# { -# my $file = $_; -# if ($file =~ $regex) -# { -# my $tmp_mtime; -# my $type_instance = $1; -# my $filename = $DataDirs[$i] . '/' . $subdir . '/' . $Args->{'type'} . '-' . $type_instance . '.rrd'; -# -# $type_instances{$type_instance} = [] if (!exists ($type_instances{$type_instance})); -# $tmp_mtime = (stat ($filename))[9]; -# next if (!$tmp_mtime); -# -# push (@{$type_instances{$type_instance}}, $filename); -# $mtime = $tmp_mtime if ($mtime < $tmp_mtime); -# } -# } -# closedir ($dh); -# } -# -# if (!keys %type_instances) -# { -# print STDOUT header (-Status => '404 File not found', -Content_Type => 'text/plain'); -# print STDOUT <{'files'} = \%type_instances; -# $Args->{'mtime'} = $mtime; -# } -# else -# { -# my $filename = $Args->{'host'} . '/' -# . $Args->{'plugin'} . (defined ($Args->{'plugin_instance'}) ? ('-' . $Args->{'plugin_instance'}) : '') . '/' -# . $Args->{'type'} . (defined ($Args->{'type_instance'}) ? ('-' . $Args->{'type_instance'}) : '') . '.rrd'; -# my @files = (); -# my $mtime = 0; -# -# for (my $i = 0; $i < @DataDirs; $i++) -# { -# my $tmp_file; -# my $tmp_mtime; -# $tmp_file = $DataDirs[$i] . '/' . $filename; -# next if (!-e $tmp_file); -# $tmp_mtime = (stat ($tmp_file))[9]; -# next if (!$tmp_mtime); -# -# push (@files, $tmp_file); -# $mtime = $tmp_mtime if ($mtime < $tmp_mtime); -# } -# -# if (!@files) -# { -# print STDOUT header (-Status => '404 File not found', -Content_Type => 'text/plain'); -# print STDOUT <{'files'} = \@files; -# $Args->{'mtime'} = $mtime; -# } -# -# if ($Args->{'timespan'}) -# { -# if ($Args->{'timespan'} =~ m/^([0-9]+)$/) -# { -# $Args->{'timespan'} = (-1) * $1; -# } -# elsif ($Args->{'timespan'} =~ m/^(hour|day|week|month|year)$/) -# { -# my %map = -# ( -# hour => -3600, -# day => -86400, -# week => 7 * -86400, -# month => 31 * -86400, -# year => 366 * -86400 -# ); -# $Args->{'timespan'} = $map{$1}; -# } -# else -# { -# $Args->{'timespan'} = -86400; -# } -# } -#} # validate_args - sub validate_args { if ($Args->{'action'} && ($Args->{'action'} =~ m/^(overview|show_host|show_plugin|show_type|show_graph)$/)) @@ -247,24 +135,61 @@ sub validate_args } } # validate_args -sub _find_hosts { - my %hosts = (); + my $hosts; + sub _find_hosts + { + if (defined ($hosts)) + { + return (keys %$hosts); + } - for (my $i = 0; $i < @DataDirs; $i++) + $hosts = {}; + + for (my $i = 0; $i < @DataDirs; $i++) + { + my @tmp; + my $dh; + + opendir ($dh, $DataDirs[$i]) or next; + @tmp = grep { ($_ !~ m/^\./) && (-d $DataDirs[$i] . '/' . $_) } (readdir ($dh)); + closedir ($dh); + + $hosts->{$_} = 1 for (@tmp); + } # for (@DataDirs) + + return (keys %$hosts); + } # _find_hosts +} + +sub _get_param_host +{ + my %all_hosts = map { $_ => 1 } (_find_hosts ()); + my @selected_hosts = (); + for (param ('host')) { - my @tmp; - my $dh; + if (defined ($all_hosts{$_})) + { + push (@selected_hosts, "$_"); + } + } + return (@selected_hosts); +} # _get_param_host - opendir ($dh, $DataDirs[$i]) or next; - @tmp = grep { ($_ !~ m/^\./) && (-d $DataDirs[$i] . '/' . $_) } (readdir ($dh)); - closedir ($dh); +sub _get_param_timespan +{ + my $timespan = param ('timespan'); - $hosts{$_} = 1 for (@tmp); - } # for (@DataDirs) + $timespan ||= 'day'; + $timespan = lc ($timespan); - return (keys %hosts); -} # _find_hosts + if (!defined ($ValidTimespan->{$timespan})) + { + $timespan = 'day'; + } + + return ($timespan); +} # _get_param_timespan sub _find_plugins { @@ -290,7 +215,7 @@ sub _find_plugins } # for (@DataDirs) return (%plugins); -} # _find_hosts +} # _find_plugins sub _find_types { @@ -322,85 +247,117 @@ sub _find_types return (%types); } # _find_types -# sub _find_files_plugin -# { -# my $host = shift; -# my $plugin = shift; -# my $plugin_instance = shift; -# my $type = shift; -# my $type_instance = shift; -# -# my @plugins = (); -# my %files = (); -# -# if (!$plugin || ($plugin eq '*')) -# { -# } -# else -# { -# @plugins = ($plugin); -# } -# } # _find_files_plugin -# -# sub _find_files_host -# { -# my $host = shift; -# my $plugin = shift; -# my $plugin_instance = shift; -# my $type = shift; -# my $type_instance = shift; -# -# my @hosts = (); -# my %files = (); -# -# if (!$host || ($host eq '*')) -# { -# my %hosts; -# for (my $i = 0; $i < @DataDirs; $i++) -# { -# my @tmp; -# my $dh; -# -# opendir ($dh, $DataDirs[$i]) or next; -# @tmp = grep { ($_ !~ m/^\./) && (-d $_) } (readdir ($dh)); -# closedir ($dh); -# -# $hosts{$_} = 1 for (@tmp); -# } # for (@DataDirs) -# @hosts = keys %hosts; -# } -# else -# { -# @hosts = ($host); -# } -# -# for (my $i = 0; $i < @hosts; $i++) -# { -# my @files = _find_files_plugin ($hosts[$i], $plugin, -# $plugin_instance, $type, $type_instance); -# $files{$_} = 1 for (@files); -# } -# -# return (wantarray () ? keys %files : [keys %files]); -# } # _find_files_host -# -# sub _find_files -# { -# my $host = shift; -# my $plugin = shift; -# my $plugin_instance = shift; -# my $type = shift; -# my $type_instance = shift; -# -# if ($host eq '*') -# { -# my @hosts = _find_all_hosts (); -# -# for (my $i = 0; $i < @DataDirs; $i++) -# { -# -# } # for (i) -# } # _find_files +sub _find_files_for_host +{ + my $host = shift; + my $ret = {}; + + my %plugins = _find_plugins ($host); + for (keys %plugins) + { + my $plugin = $_; + my $plugin_instances = $plugins{$plugin}; + + if (!$plugin_instances || !@$plugin_instances) + { + $plugin_instances = ['-']; + } + + $ret->{$plugin} = {}; + + for (@$plugin_instances) + { + my $plugin_instance = $_; + my %types = _find_types ($host, $plugin, + ($plugin_instance ne '-') ? $plugin_instance : undef); + + $ret->{$plugin}{$plugin_instance} = {}; + + for (keys %types) + { + my $type = $_; + my $type_instances = $types{$type}; + + $ret->{$plugin}{$plugin_instance}{$type} = {}; + + for (@$type_instances) + { + $ret->{$plugin}{$plugin_instance}{$type}{$_} = 1; + } + + if (!@$type_instances) + { + $ret->{$plugin}{$plugin_instance}{$type}{'-'} = 1; + } + } # for (keys %types) + } # for (@$plugin_instances) + } # for (keys %plugins) + + return ($ret); +} # _find_files_for_host + +sub _find_files_for_hosts +{ + my @hosts = @_; + my $all_plugins = {}; + + for (my $i = 0; $i < @hosts; $i++) + { + my $tmp = _find_files_for_host ($hosts[$i]); + _files_union ($all_plugins, $tmp); + } + + return ($all_plugins); +} # _find_files_for_hosts + +sub _files_union +{ + my $dest = shift; + my $src = shift; + + for (keys %$src) + { + my $plugin = $_; + $dest->{$plugin} ||= {}; + + for (keys %{$src->{$plugin}}) + { + my $pinst = $_; + $dest->{$plugin}{$pinst} ||= {}; + + for (keys %{$src->{$plugin}{$pinst}}) + { + my $type = $_; + $dest->{$plugin}{$pinst}{$type} ||= {}; + + for (keys %{$src->{$plugin}{$pinst}{$type}}) + { + my $tinst = $_; + $dest->{$plugin}{$pinst}{$type}{$tinst} = 1; + } + } + } + } +} # _files_union + +sub _files_plugin_inst_count +{ + my $src = shift; + my $i = 0; + + for (keys %$src) + { + if (exists ($MetaGraphDefs->{$_})) + { + $i++; + } + else + { + $i = $i + keys %{$src->{$_}}; + } + } + return ($i); +} # _files_plugin_count sub list_hosts { @@ -418,81 +375,358 @@ sub list_hosts print "\n"; } # list_hosts +sub _string_to_color +{ + my $color = shift; + if ($color =~ m/([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])/) + { + return ([hex ($1) / 255.0, hex ($2) / 255.0, hex ($3) / 255.0]); + } + return; +} # _string_to_color + +sub _color_to_string +{ + confess ("Wrong number of arguments") if (@_ != 1); + return (sprintf ('%02hx%02hx%02hx', map { int (255.0 * $_) } @{$_[0]})); +} # _color_to_string + +sub _get_random_color +{ + my ($r, $g, $b) = (rand (), rand ()); + my $min = 0.0; + my $max = 1.0; + + if (($r + $g) < 1.0) + { + $min = 1.0 - ($r + $g); + } + else + { + $max = 2.0 - ($r + $g); + } + + $b = $min + (rand () * ($max - $min)); + + return ([$r, $g, $b]); +} # _get_random_color + +sub _get_faded_color +{ + my $fg = shift; + my $bg; + my %opts = @_; + my $ret = [undef, undef, undef]; + + $opts{'background'} ||= [1.0, 1.0, 1.0]; + $opts{'alpha'} ||= 0.25; + + if (!ref ($opts{'background'})) + { + $opts{'background'} = _string_to_color ($opts{'background'}) + or confess ("Cannot parse background color " . $opts{'background'}); + } + $bg = $opts{'background'}; + + for (my $i = 0; $i < 3; $i++) + { + $ret->[$i] = ($opts{'alpha'} * $fg->[$i]) + + ((1.0 - $opts{'alpha'}) * $bg->[$i]); + } + + return ($ret); +} # _get_faded_color + +sub _custom_sort_arrayref +{ + my $array_ref = shift; + my $array_sort = shift; + + my %elements = map { $_ => 1 } (@$array_ref); + splice (@$array_ref, 0); + + for (@$array_sort) + { + next if (!exists ($elements{$_})); + push (@$array_ref, $_); + delete ($elements{$_}); + } + push (@$array_ref, sort (keys %elements)); +} # _custom_sort_arrayref + sub action_show_host { - my $host = shift; - my $host_url = uri_escape ($host); - my %plugins = _find_plugins ($host); + my @hosts = _get_param_host (); + @hosts = sort (@hosts); + + my $all_plugins = {}; + my $plugins_per_host = {}; + + my $timespan = _get_param_timespan (); + + for (my $i = 0; $i < @hosts; $i++) + { + $plugins_per_host->{$hosts[$i]} = _find_files_for_host ($hosts[$i]); + _files_union ($all_plugins, $plugins_per_host->{$hosts[$i]}); + } + + my $param_host = join (";", map { "host=" . encode_entities ($_) } (@hosts)); + + print '\n"; + + my @plugins = sort (keys %$all_plugins); print qq(
Back to list of hosts
\n); - print "
    \n"; - for (sort (keys %plugins)) + print < + + Plugin +HTML + for (my $i = 0; $i < @hosts; $i++) { - my $plugin = $_; - my $plugin_html = encode_entities ($plugin); - my $plugin_url = uri_escape ($plugin); + print "\t", encode_entities ($hosts[$i]), "\n"; + } + print " \n"; + for (my $i = 0; $i < @plugins; $i++) + { + my $plugin = $plugins[$i]; + my $plugin_esc = encode_entities ($plugins[$i]); + + my @pinsts = sort (keys %{$all_plugins->{$plugin}}); - for (@{$plugins{$plugin}}) + for (my $j = 0; $j < @pinsts; $j++) { - my $instance = $_; - my $instance_html = encode_entities ($instance); - my $instance_url = uri_escape ($instance); + my $pinst = $pinsts[$j]; + my $pinst_esc = encode_entities ($pinst); + my $title = $plugin . ($pinst ne '-' ? " ($pinst)" : ''); + my $title_esc = encode_entities ($title); - print qq#
  • $plugin_html ($instance_html)
  • \n#; - } + my $param_plugin = "plugin=$plugin_esc"; + if ($pinst ne '-') + { + $param_plugin .= ";plugin_instance=$pinst_esc"; + } - if (!@{$plugins{$plugin}}) - { - print qq#
  • $plugin_html
  • \n#; - } - } # for (%plugins) - print "
\n"; + my $files_printed = 0; + my $files_num = _files_plugin_inst_count ($all_plugins->{$plugin}{$pinst}); + next if (!$files_num); + + my $rowspan = ($files_num == 1) ? '' : qq( rowspan="$files_num"); + + for (sort (keys %{$all_plugins->{$plugin}{$pinst}})) + { + my $type = $_; + my $type_esc = encode_entities ($type); + + if (exists ($MetaGraphDefs->{$type})) + { + my $param_type = "type=$type_esc"; + + print " \n"; + if ($files_printed == 0) + { + print "\t$title_esc\n"; + } + + for (my $k = 0; $k < @hosts; $k++) + { + my $host = $hosts[$k]; + my $host_esc = encode_entities ($host); + + print "\t"; + if (exists $plugins_per_host->{$host}{$plugin}{$pinst}{$type}) + { + #print qq(); + print encode_entities (qq()); + } + print "\n"; + } # for (my $k = 0; $k < @hosts; $k++) + + print " \n"; + + $files_printed++; + next; # pinst + } + + for (sort (keys %{$all_plugins->{$plugin}{$pinst}{$type}})) + { + my $tinst = $_; + my $tinst_esc = encode_entities ($tinst); + + my $param_type = "type=$type_esc"; + if ($tinst ne '-') + { + $param_type .= ";type_instance=$tinst_esc"; + } + + print " \n"; + if ($files_printed == 0) + { + print "\t$title_esc\n"; + } + + for (my $k = 0; $k < @hosts; $k++) + { + my $host = $hosts[$k]; + my $host_esc = encode_entities ($host); + + print "\t"; + if ($plugins_per_host->{$host}{$plugin}{$pinst}{$type}{$tinst}) + { + #print qq(); + print encode_entities (qq()); + } + print "\n"; + } # for (my $k = 0; $k < @hosts; $k++) + + print " \n"; + + $files_printed++; + } # for ($tinst) + } # for ($type) + } # for (my $j = 0; $j < @pinsts; $j++) + } # for (my $i = 0; $i < @plugins; $i++) + print " \n"; } # action_show_host sub action_show_plugin { - my $host = shift; + my @hosts = _get_param_host (); my $plugin = shift; my $plugin_instance = shift; + my $timespan = _get_param_timespan (); - my $host_url = uri_escape ($host); + my $hosts_url = join (';', map { 'host=' . uri_escape ($_) } (@hosts)); + my $plugin_esc = encode_entities ($plugin); my $plugin_url = uri_escape ($plugin); my $plugin_instance_url = defined ($plugin_instance) ? uri_escape ($plugin_instance) : undef; - my %types = _find_types ($host, $plugin, $plugin_instance); + my $all_plugins = {}; + my $plugins_per_host = {}; - my $url_prefix = script_name () . "?host=$host_url;plugin=$plugin_url"; + for (my $i = 0; $i < @hosts; $i++) + { + $plugins_per_host->{$hosts[$i]} = _find_files_for_host ($hosts[$i]); + _files_union ($all_plugins, $plugins_per_host->{$hosts[$i]}); + } + + my $url_prefix = script_name () . "?$hosts_url;plugin=$plugin_url"; $url_prefix .= ";plugin_instance=$plugin_instance_url" if (defined ($plugin_instance)); - print qq( \n); + print qq( \n); - for (sort (keys %types)) + if (!defined ($all_plugins->{$plugin})) { - my $type = $_; - my $type_html = encode_entities ($type); - my $type_url = uri_escape ($type); + print qq(
Plugin "${\encode_entities ($plugin)}" not found for host "${\encode_entities (@hosts)}".
\n); + return; + } - if (!defined ($GraphDefs->{$type})) + my @pinsts = sort (keys %{$all_plugins->{$plugin}}); + + print < + + Plugin +HTML + for (my $i = 0; $i < @hosts; $i++) + { + print "\t", encode_entities ($hosts[$i]), "\n"; + } + print " \n"; + + for (my $j = 0; $j < @pinsts; $j++) + { + my $pinst = $pinsts[$j]; + my $pinst_esc = encode_entities ($pinst); + my $title = $plugin . ($pinst ne '-' ? " ($pinst)" : ''); + my $title_esc = encode_entities ($title); + + my $param_plugin = "plugin=$plugin_esc"; + if ($pinst ne '-') { - print qq#
Unknown type "$type_html"
\n#; - next; + $param_plugin .= ";plugin_instance=$pinst_esc"; } - for (@{$types{$type}}) - { - my $instance = $_; - my $instance_html = encode_entities ($instance); - my $instance_url = uri_escape ($instance); + my $files_printed = 0; + my $files_num = _files_plugin_inst_count ($all_plugins->{$plugin}{$pinst}); + next if (!$files_num); - print qq#
\n#; - } + my $rowspan = ($files_num == 1) ? '' : qq( rowspan="$files_num"); - if (!@{$types{$type}}) + for (sort (keys %{$all_plugins->{$plugin}{$pinst}})) { - print qq#
\n#; - } - } + my $type = $_; + my $type_esc = encode_entities ($type); + + if (exists ($MetaGraphDefs->{$type})) + { + my $param_type = "type=$type_esc"; + + print " \n"; + if ($files_printed == 0) + { + print "\t$title_esc\n"; + } + + for (my $k = 0; $k < @hosts; $k++) + { + my $host = $hosts[$k]; + my $host_esc = encode_entities ($host); + + print "\t"; + if (exists $plugins_per_host->{$host}{$plugin}{$pinst}{$type}) + { + print qq(); + #print encode_entities (qq()); + } + print "\n"; + } # for (my $k = 0; $k < @hosts; $k++) + + print " \n"; + + $files_printed++; + next; # pinst + } + + for (sort (keys %{$all_plugins->{$plugin}{$pinst}{$type}})) + { + my $tinst = $_; + my $tinst_esc = encode_entities ($tinst); + + my $param_type = "type=$type_esc"; + if ($tinst ne '-') + { + $param_type .= ";type_instance=$tinst_esc"; + } + + print " \n"; + if ($files_printed == 0) + { + print "\t$title_esc\n"; + } + + for (my $k = 0; $k < @hosts; $k++) + { + my $host = $hosts[$k]; + my $host_esc = encode_entities ($host); + + print "\t"; + if ($plugins_per_host->{$host}{$plugin}{$pinst}{$type}{$tinst}) + { + print qq(); + #print encode_entities (qq()); + } + print "\n"; + } # for (my $k = 0; $k < @hosts; $k++) + + print " \n"; + + $files_printed++; + } # for ($tinst) + } # for ($type) + } # for (my $j = 0; $j < @pinsts; $j++) + print " \n"; } # action_show_plugin sub action_show_type @@ -543,6 +777,13 @@ sub action_show_graph #print STDERR Data::Dumper->Dump ([$Args], ['Args']); + # FIXME + if (exists ($MetaGraphDefs->{$type})) + { + my %types = _find_types ($host, $plugin, $plugin_instance); + return $MetaGraphDefs->{$type}->($host, $plugin, $plugin_instance, $type, $types{$type}); + } + return if (!defined ($GraphDefs->{$type})); @rrd_args = @{$GraphDefs->{$type}}; @@ -554,6 +795,7 @@ sub action_show_graph my $file = $DataDirs[$i] . "/$title.rrd"; next if (!-f $file); + $file =~ s/:/\\:/g; s/{file}/$file/ for (@rrd_args); RRDs::graph ('-', '-a', 'PNG', '-s', $start_time, '-t', $title, @RRDDefaultArgs, @rrd_args); @@ -564,6 +806,62 @@ sub action_show_graph } } # action_show_graph +sub print_selector +{ + my @hosts = _find_hosts (); + @hosts = sort (@hosts); + + my %selected_hosts = map { $_ => 1 } (_get_param_host ()); + my $timespan_selected = _get_param_timespan (); + + print < +
+ Selector + \n"; + + if (keys %selected_hosts) + { + my $all_plugins = _find_files_for_hosts (@hosts); + my %selected_plugins = map { $_ => 1 } (param ('plugin')); + + print qq(\t\n"; + } # if (keys %selected_hosts) + + print qq(\t +
+ +HTML +} + sub print_header { print < HEAD + print_selector (); } # print_header sub print_footer @@ -627,8 +936,7 @@ sub main } elsif (!$Args->{'type'}) { - action_show_plugin ($Args->{'host'}, - $Args->{'plugin'}, $Args->{'plugin_instance'}); + action_show_plugin ($Args->{'plugin'}, $Args->{'plugin_instance'}); } else { @@ -705,6 +1013,18 @@ sub load_graph_definitions 'GPRINT:max:MAX:%6.2lf Max,', 'GPRINT:avg:LAST:%6.2lf Last' ], + bitrate => ['-v', 'Bits/s', + 'DEF:avg={file}:value:AVERAGE', + 'DEF:min={file}:value:MIN', + 'DEF:max={file}:value:MAX', + "AREA:max#$HalfBlue", + "AREA:min#$Canvas", + "LINE1:avg#$FullBlue:Bits/s", + 'GPRINT:min:MIN:%5.1lf%s Min,', + 'GPRINT:avg:AVERAGE:%5.1lf%s Average,', + 'GPRINT:max:MAX:%5.1lf%s Max,', + 'GPRINT:avg:LAST:%5.1lf%s Last\l' + ], charge => [ 'DEF:avg={file}:charge:AVERAGE', 'DEF:min={file}:charge:MIN', @@ -753,7 +1073,7 @@ sub load_graph_definitions 'GPRINT:max:MAX:%5.1lf%sA Max,', 'GPRINT:avg:LAST:%5.1lf%sA Last\l' ], - df => ['-v', 'Percent', + df => ['-v', 'Percent', '-l', '0', 'DEF:free_avg={file}:free:AVERAGE', 'DEF:free_min={file}:free:MIN', 'DEF:free_max={file}:free:MAX', @@ -942,10 +1262,10 @@ sub load_graph_definitions 'GPRINT:qry_avg:LAST:%5.1lf%s Last', 'GPRINT:qry_avg_sum:LAST:(ca. %5.1lf%sB Total)\l' ], - email => [ - 'DEF:avg={file}:count:AVERAGE', - 'DEF:min={file}:count:MIN', - 'DEF:max={file}:count:MAX', + email_count => ['-v', 'Mails', + 'DEF:avg={file}:value:AVERAGE', + 'DEF:min={file}:value:MIN', + 'DEF:max={file}:value:MAX', "AREA:max#$HalfMagenta", "AREA:min#$Canvas", "LINE1:avg#$FullMagenta:Count ", @@ -954,10 +1274,10 @@ sub load_graph_definitions 'GPRINT:max:MAX:%4.1lf Max,', 'GPRINT:avg:LAST:%4.1lf Last\l' ], - email_size => [ - 'DEF:avg={file}:size:AVERAGE', - 'DEF:min={file}:size:MIN', - 'DEF:max={file}:size:MAX', + email_size => ['-v', 'Bytes', + 'DEF:avg={file}:value:AVERAGE', + 'DEF:min={file}:value:MIN', + 'DEF:max={file}:value:MAX', "AREA:max#$HalfMagenta", "AREA:min#$Canvas", "LINE1:avg#$FullMagenta:Count ", @@ -990,7 +1310,8 @@ sub load_graph_definitions 'GPRINT:max:MAX:%4.1lf Max,', 'GPRINT:avg:LAST:%4.1lf Last\l' ], - entropy => ['DEF:avg={file}:entropy:AVERAGE', + entropy => ['-v', 'Bits', + 'DEF:avg={file}:entropy:AVERAGE', 'DEF:min={file}:entropy:MIN', 'DEF:max={file}:entropy:MAX', "AREA:max#$HalfBlue", @@ -1091,6 +1412,51 @@ sub load_graph_definitions 'GPRINT:rx_avg:LAST:%5.1lf%s Last', 'GPRINT:rx_avg_sum:LAST:(ca. %4.0lf%s Total)\l' ], + if_collisions => ['-v', 'Collisions/s', + 'DEF:min_raw={file}:value:MIN', + 'DEF:avg_raw={file}:value:AVERAGE', + 'DEF:max_raw={file}:value:MAX', + 'CDEF:min=min_raw,8,*', + 'CDEF:avg=avg_raw,8,*', + 'CDEF:max=max_raw,8,*', + "AREA:max#$HalfBlue", + "AREA:min#$Canvas", + "LINE1:avg#$FullBlue:Collisions/s", + 'GPRINT:min:MIN:%5.1lf %s Min,', + 'GPRINT:avg:AVERAGE:%5.1lf%s Avg,', + 'GPRINT:max:MAX:%5.1lf%s Max,', + 'GPRINT:avg:LAST:%5.1lf%s Last\l' + ], + if_dropped => ['-v', 'Packets/s', + 'DEF:tx_min={file}:tx:MIN', + 'DEF:tx_avg={file}:tx:AVERAGE', + 'DEF:tx_max={file}:tx:MAX', + 'DEF:rx_min={file}:rx:MIN', + 'DEF:rx_avg={file}:rx:AVERAGE', + 'DEF:rx_max={file}:rx:MAX', + 'CDEF:overlap=tx_avg,rx_avg,GT,rx_avg,tx_avg,IF', + 'CDEF:mytime=tx_avg,TIME,TIME,IF', + 'CDEF:sample_len_raw=mytime,PREV(mytime),-', + 'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF', + 'CDEF:tx_avg_sample=tx_avg,UN,0,tx_avg,IF,sample_len,*', + 'CDEF:tx_avg_sum=PREV,UN,0,PREV,IF,tx_avg_sample,+', + 'CDEF:rx_avg_sample=rx_avg,UN,0,rx_avg,IF,sample_len,*', + 'CDEF:rx_avg_sum=PREV,UN,0,PREV,IF,rx_avg_sample,+', + "AREA:tx_avg#$HalfGreen", + "AREA:rx_avg#$HalfBlue", + "AREA:overlap#$HalfBlueGreen", + "LINE1:tx_avg#$FullGreen:TX", + 'GPRINT:tx_avg:AVERAGE:%5.1lf%s Avg,', + 'GPRINT:tx_max:MAX:%5.1lf%s Max,', + 'GPRINT:tx_avg:LAST:%5.1lf%s Last', + 'GPRINT:tx_avg_sum:LAST:(ca. %4.0lf%s Total)\l', + "LINE1:rx_avg#$FullBlue:RX", + #'GPRINT:rx_min:MIN:%5.1lf %s Min,', + 'GPRINT:rx_avg:AVERAGE:%5.1lf%s Avg,', + 'GPRINT:rx_max:MAX:%5.1lf%s Max,', + 'GPRINT:rx_avg:LAST:%5.1lf%s Last', + 'GPRINT:rx_avg_sum:LAST:(ca. %4.0lf%s Total)\l' + ], if_packets => ['-v', 'Packets/s', 'DEF:tx_min={file}:tx:MIN', 'DEF:tx_avg={file}:tx:AVERAGE', @@ -1121,6 +1487,22 @@ sub load_graph_definitions 'GPRINT:rx_avg:LAST:%5.1lf%s Last', 'GPRINT:rx_avg_sum:LAST:(ca. %4.0lf%s Total)\l' ], + if_rx_errors => ['-v', 'Errors/s', + 'DEF:min={file}:value:MIN', + 'DEF:avg={file}:value:AVERAGE', + 'DEF:max={file}:value:MAX', + 'CDEF:mytime=avg,TIME,TIME,IF', + 'CDEF:sample_len_raw=mytime,PREV(mytime),-', + 'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF', + 'CDEF:avg_sample=avg,UN,0,avg,IF,sample_len,*', + 'CDEF:avg_sum=PREV,UN,0,PREV,IF,avg_sample,+', + "AREA:avg#$HalfBlue", + "LINE1:avg#$FullBlue:Errors/s", + 'GPRINT:avg:AVERAGE:%3.1lf%s Avg,', + 'GPRINT:max:MAX:%3.1lf%s Max,', + 'GPRINT:avg:LAST:%3.1lf%s Last', + 'GPRINT:avg_sum:LAST:(ca. %2.0lf%s Total)\l' + ], ipt_bytes => ['-v', 'Bits/s', 'DEF:min_raw={file}:value:MIN', 'DEF:avg_raw={file}:value:AVERAGE', @@ -1169,7 +1551,8 @@ sub load_graph_definitions 'GPRINT:max:MAX:%6.2lf Max,', 'GPRINT:avg:LAST:%6.2lf Last\l' ], - load => ['DEF:s_avg={file}:shortterm:AVERAGE', + load => ['-v', 'System load', + 'DEF:s_avg={file}:shortterm:AVERAGE', 'DEF:s_min={file}:shortterm:MIN', 'DEF:s_max={file}:shortterm:MAX', 'DEF:m_avg={file}:midterm:AVERAGE', @@ -1822,8 +2205,15 @@ sub load_graph_definitions 'DEF:temp_avg={file}:value:AVERAGE', 'DEF:temp_min={file}:value:MIN', 'DEF:temp_max={file}:value:MAX', + 'CDEF:average=temp_avg,0.2,*,PREV,UN,temp_avg,PREV,IF,0.8,*,+', + 'CDEF:derivative=PREV(average),average,-', + 'CDEF:rising=derivative,-0.01,LT,INF,UNKN,IF', + 'CDEF:falling=derivative,0.01,GT,INF,UNKN,IF', + "AREA:rising#f0fff0", + "AREA:falling#fff0f0", "AREA:temp_max#$HalfRed", "AREA:temp_min#$Canvas", + #"LINE1:average#000000", "LINE1:temp_avg#$FullRed:Temperature", 'GPRINT:temp_min:MIN:%4.1lf Min,', 'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,', @@ -1854,7 +2244,7 @@ sub load_graph_definitions 'GPRINT:s_max:MAX:%7.3lf%s Max,', 'GPRINT:s_avg:LAST:%7.3lf%s Last' ], - if_octets => ['-v', 'Bits/s', + if_octets => ['-v', 'Bits/s', '-l', '0', 'DEF:out_min_raw={file}:tx:MIN', 'DEF:out_avg_raw={file}:tx:AVERAGE', 'DEF:out_max_raw={file}:tx:MAX', @@ -2019,5 +2409,224 @@ sub load_graph_definitions 'GPRINT:proc_avg:LAST:%4.1lf Last\l' ], }; + $GraphDefs->{'if_multicast'} = $GraphDefs->{'ipt_packets'}; + $GraphDefs->{'if_tx_errors'} = $GraphDefs->{'if_rx_errors'}; + + $MetaGraphDefs->{'cpu'} = \&meta_graph_cpu; + $MetaGraphDefs->{'memory'} = \&meta_graph_memory; } # load_graph_definitions + +sub meta_graph_generic_stack +{ + confess ("Wrong number of arguments") if (@_ != 2); + + my $opts = shift; + my $sources = shift; + my $i; + + my $timespan_str = _get_param_timespan (); + my $timespan_int = (-1) * $ValidTimespan->{$timespan_str}; + + $opts->{'title'} ||= 'Unknown title'; + $opts->{'rrd_opts'} ||= []; + $opts->{'colors'} ||= {}; + + my @cmd = ('-', '-a', 'PNG', '-s', $timespan_int, + '-t', $opts->{'title'} || 'Unknown title', + @RRDDefaultArgs, @{$opts->{'rrd_opts'}}); + + my $max_inst_name = 0; + + for ($i = 0; $i < @$sources; $i++) + { + my $inst_data = $sources->[$i]; + my $inst_name = $inst_data->{'name'} || confess; + my $file = $inst_data->{'file'} || confess; + + if (length ($inst_name) > $max_inst_name) + { + $max_inst_name = length ($inst_name); + } + + confess ("No such file: $file") if (!-e $file); + + push (@cmd, + qq#DEF:${inst_name}_min=$file:value:MIN#, + qq#DEF:${inst_name}_avg=$file:value:AVERAGE#, + qq#DEF:${inst_name}_max=$file:value:MAX#, + qq#CDEF:${inst_name}_nnl=${inst_name}_avg,UN,0,${inst_name}_avg,IF#); + } + + for (my $i = 0; $i < @$sources; $i++) + { + my $inst_data0 = $sources->[@$sources - (1 + $i)]; + my $inst_data1 = $sources->[@$sources - (($i == 0) ? 1 : $i)]; + + my $inst_name0 = $inst_data0->{'name'}; + my $inst_name1 = $inst_data1->{'name'}; + + my $cdef_name = ($i == 0) ? 'nnl' : 'stk'; + + push (@cmd, qq#CDEF:${inst_name0}_stk=${inst_name0}_nnl,${inst_name1}_${cdef_name},+#); + } + + for (my $i = 0; $i < @$sources; $i++) + { + my $inst_data = $sources->[$i]; + my $inst_name = $inst_data->{'name'}; + + my $legend = sprintf ('%-*s', $max_inst_name, $inst_name); + + my $line_color; + my $area_color; + + my $number_format = $opts->{'number_format'} || '%6.1lf'; + + if (exists ($opts->{'colors'}{$inst_name})) + { + $line_color = $opts->{'colors'}{$inst_name}; + $area_color = _string_to_color ($line_color); + } + else + { + $area_color = _get_random_color (); + $line_color = _color_to_string ($area_color); + } + $area_color = _color_to_string (_get_faded_color ($area_color)); + + push (@cmd, qq(AREA:${inst_name}_stk#$area_color), + qq(LINE1:${inst_name}_stk#$line_color:$legend), + qq(GPRINT:${inst_name}_min:MIN:$number_format Min,), + qq(GPRINT:${inst_name}_avg:AVERAGE:$number_format Avg,), + qq(GPRINT:${inst_name}_max:MAX:$number_format Max,), + qq(GPRINT:${inst_name}_avg:LAST:$number_format Last\l), + ); + } + + RRDs::graph (@cmd); + if (my $errmsg = RRDs::error ()) + { + confess ("RRDs::graph: $errmsg"); + } +} # meta_graph_generic_stack + +sub meta_graph_cpu +{ + confess ("Wrong number of arguments") if (@_ != 5); + + my $host = shift; + my $plugin = shift; + my $plugin_instance = shift; + my $type = shift; + my $type_instances = shift; + + my $opts = {}; + my $sources = []; + + $opts->{'title'} = "$host/$plugin" + . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type"; + + my @files = (); + + $opts->{'colors'} = + { + 'idle' => 'ffffff', + 'nice' => '00e000', + 'user' => '0000ff', + 'wait' => 'ffb000', + 'system' => 'ff0000', + 'softirq' => 'ff00ff', + 'interrupt' => 'a000a0', + 'steal' => '000000' + }; + + _custom_sort_arrayref ($type_instances, + [qw(idle nice user wait system softirq interrupt steal)]); + + for (@$type_instances) + { + my $inst = $_; + my $file = ''; + my $title = $opts->{'title'}; + + for (@DataDirs) + { + if (-e "$_/$title-$inst.rrd") + { + $file = "$_/$title-$inst.rrd"; + last; + } + } + confess ("No file found for $title") if ($file eq ''); + + push (@$sources, + { + name => $inst, + file => $file + } + ); + } # for (@$type_instances) + + return (meta_graph_generic_stack ($opts, $sources)); +} # meta_graph_cpu + +sub meta_graph_memory +{ + confess ("Wrong number of arguments") if (@_ != 5); + + my $host = shift; + my $plugin = shift; + my $plugin_instance = shift; + my $type = shift; + my $type_instances = shift; + + my $opts = {}; + my $sources = []; + + $opts->{'title'} = "$host/$plugin" + . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type"; + $opts->{'number_format'} = '%5.1lf%s'; + + $opts->{'rrd_opts'} = ['-b', '1024']; + + my @files = (); + + $opts->{'colors'} = + { + 'free' => '00e000', + 'cached' => '0000ff', + 'buffered' => 'ffb000', + 'used' => 'ff0000' + }; + + _custom_sort_arrayref ($type_instances, + [qw(free cached buffered used)]); + + for (@$type_instances) + { + my $inst = $_; + my $file = ''; + my $title = $opts->{'title'}; + + for (@DataDirs) + { + if (-e "$_/$title-$inst.rrd") + { + $file = "$_/$title-$inst.rrd"; + last; + } + } + confess ("No file found for $title") if ($file eq ''); + + push (@$sources, + { + name => $inst, + file => $file + } + ); + } # for (@$type_instances) + + return (meta_graph_generic_stack ($opts, $sources)); +} # meta_graph_cpu + # vim: shiftwidth=2:softtabstop=2:tabstop=8 -- 2.30.2