3 # Copyright (C) 2008-2011 Florian Forster
4 # Copyright (C) 2011 noris network AG
6 # This program is free software; you can redistribute it and/or modify it under
7 # the terms of the GNU General Public License as published by the Free Software
8 # Foundation; only version 2 of the License is applicable.
10 # This program is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 # You should have received a copy of the GNU General Public License along with
16 # this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 # Florian "octo" Forster <octo at collectd.org>
25 use vars (qw($BASE_DIR));
29 if (defined $ENV{'SCRIPT_FILENAME'})
31 if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
34 unshift (@INC, "$BASE_DIR/lib");
39 use Carp (qw(cluck confess));
41 use CGI::Carp ('fatalsToBrowser');
42 use HTML::Entities ('encode_entities');
46 use Collectd::Graph::Config (qw(gc_read_config gc_get_scalar));
47 use Collectd::Graph::TypeLoader (qw(tl_load_type));
48 use Collectd::Graph::Common (qw(get_files_from_directory get_all_hosts
49 get_timespan_selection get_selected_files get_host_selection
50 get_plugin_selection flush_files));
51 use Collectd::Graph::Type ();
64 list_hosts => \&action_list_hosts,
65 show_selection => \&action_show_selection
76 print STDERR "INC = (" . join (', ', @INC) . ");\n";
78 gc_read_config ("$BASE_DIR/etc/collection.conf");
85 my $Debug = param ('debug') ? 1 : 0;
86 my $action = param ('action') || 'list_hosts';
88 if (!exists ($Actions{$action}))
90 print STDERR "No such action: $action\n";
96 $Actions{$action}->();
104 if (!defined $ENV{'HTTP_ACCEPT'})
109 for (split (',', $ENV{'HTTP_ACCEPT'}))
114 if ($type =~ m#^([^;]+);q=([0-9\.]+)$#)
122 if (!defined ($types{'application/xhtml+xml'}))
126 elsif (!defined ($types{'text/html'}))
130 elsif ($types{'application/xhtml+xml'} < $types{'text/html'})
143 return if ($html_started);
150 $timespan = get_timespan_selection ();
151 $begin = $end - $timespan;
153 if (can_handle_xhtml ())
155 print header (-Content_Type => 'application/xhtml+xml; charset=UTF-8');
157 <?xml version="1.0" encoding="UTF-8"?>
158 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
159 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
160 <html xmlns="http://www.w3.org/1999/xhtml"
161 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
162 xsi:schemaLocation="http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd"
168 print header (-Content_Type => 'text/html; charset=UTF-8');
170 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
171 "http://www.w3.org/TR/html4/strict.dtd">
177 <title>collection.cgi, Version 3</title>
178 <link rel="icon" href="../share/shortcut-icon.png" type="image/png" />
179 <link rel="stylesheet" href="../share/style.css" type="text/css" />
180 <script type="text/javascript" src="../share/navigate.js"></script>
182 <body onload="nav_init ($begin, $end);">
198 my $timespan_selection = get_timespan_selection ();
199 my $host_selection = get_host_selection ();
200 my $plugin_selection = get_plugin_selection ();
203 <form action="${\script_name ()}" method="get">
205 <legend>Data selection</legend>
206 <select name="hostname" multiple="multiple" size="15">
208 for (sort (keys %$host_selection))
210 my $host = encode_entities ($_);
211 my $selected = $host_selection->{$_}
212 ? ' selected="selected"'
214 print qq# <option value="$host"$selected>$host</option>\n#;
218 <select name="plugin" multiple="multiple" size="15">
220 for (sort (keys %$plugin_selection))
222 my $plugin = encode_entities ($_);
223 my $selected = $plugin_selection->{$_}
224 ? ' selected="selected"'
226 print qq# <option value="$plugin"$selected>$plugin</option>\n#;
230 <select name="timespan">
232 for (sort { $TimeSpans->{$a} <=> $TimeSpans->{$b} } (keys (%$TimeSpans)))
234 my $name = encode_entities ($_);
235 my $value = $TimeSpans->{$_};
236 my $selected = ($value == $timespan_selection)
237 ? ' selected="selected"'
239 print qq# <option value="$value"$selected>$name</option>\n#;
243 <input type="hidden" name="action" value="show_selection" />
244 <input type="submit" name="ok_button" value="OK" />
250 sub action_list_hosts
255 my @hosts = get_all_hosts ();
259 my $url = encode_entities (script_name () . "?action=show_selection;hostname=$_");
260 my $name = encode_entities ($_);
261 print qq# <li><a href="$url">$name</a></li>\n#;
266 } # action_list_hosts
268 =head1 MODULE LOADING
270 This script makes use of the various B<Collectd::Graph::Type::*> modules. If a
271 file like C<foo.rrd> is encountered it tries to load the
272 B<Collectd::Graph::Type::Foo> module and, if that fails, falls back to the
273 B<Collectd::Graph::Type> base class.
275 If you want to create a specialized graph for a certain type, you have to
276 create a new module which inherits from the B<Collectd::Graph::Type> base
277 class. A description of provided (and used) methods can be found in the inline
278 documentation of the B<Collectd::Graph::Type> module.
280 There are other, more specialized, "abstract" classes that possibly better fit
281 your need. Unfortunately they are not yet documented.
285 =item B<Collectd::Graph::Type::GenericStacked>
287 Specialized class that groups files by their plugin instance and stacks them on
288 top of each other. Example types that inherit from this class are
289 B<Collectd::Graph::Type::Cpu> and B<Collectd::Graph::Type::Memory>.
291 =item B<Collectd::Graph::Type::GenericIO>
293 Specialized class for input/output graphs. This class can only handle files
294 with exactly two data sources, input and output. Example types that inherit
295 from this class are B<Collectd::Graph::Type::DiskOctets> and
296 B<Collectd::Graph::Type::IfOctets>.
302 sub action_show_selection
314 $all_files = get_selected_files ();
315 $timespan = get_timespan_selection ();
319 print "<pre>", Data::Dumper->Dump ([$all_files], ['all_files']), "</pre>\n";
322 # Send FLUSH command to the daemon if necessary and possible.
323 flush_files ($all_files,
324 begin => time () - $timespan,
326 addr => gc_get_scalar ('UnixSockAddr', undef),
327 interval => gc_get_scalar ('Interval', 10));
332 my $type = ucfirst (lc ($file->{'type'}));
334 $type =~ s/[^A-Za-z0-9_]//g;
335 $type =~ s/_([A-Za-z0-9])/\U$1\E/g;
337 if (!defined ($types->{$type}))
339 $types->{$type} = tl_load_type ($file->{'type'});
340 if (!$types->{$type})
342 cluck ("tl_load_type (" . $file->{'type'} . ") failed");
347 $types->{$type}->addFiles ($file);
349 #print STDOUT Data::Dumper->Dump ([$types], ['types']);
351 print qq# <table>\n#;
352 for (sort (keys %$types))
356 if (!defined ($types->{$type}))
361 my $graphs_num = $types->{$type}->getGraphsNum ();
363 for (my $i = 0; $i < $graphs_num; $i++)
365 my $args = $types->{$type}->getGraphArgs ($i);
366 my $url = encode_entities ("graph.cgi?$args;begin=-$timespan");
367 my $id = sprintf ("graph%04i", $id_counter++);
370 print " <td rowspan=\"$graphs_num\">$type</td>\n" if ($i == 0);
373 <div class="graph_canvas">
374 <div class="graph_float">
375 <img id="${id}" class="graph_image"
378 <div class="controls zoom">
380 onclick="nav_move_earlier ('${id}');">←</div>
381 <div title="Zoom out"
382 onclick="nav_zoom_out ('${id}');">-</div>
384 onclick="nav_zoom_in ('${id}');">+</div>
386 onclick="nav_move_later ('${id}');">→</div>
388 <div class="controls preset">
389 <div title="Show current hour"
390 onclick="nav_time_reset ('${id}', 3600);">H</div>
391 <div title="Show current day"
392 onclick="nav_time_reset ('${id}', 86400);">D</div>
393 <div title="Show current week"
394 onclick="nav_time_reset ('${id}', 7 * 86400);">W</div>
395 <div title="Show current month"
396 onclick="nav_time_reset ('${id}', 31 * 86400);">M</div>
397 <div title="Show current year"
398 onclick="nav_time_reset ('${id}', 366 * 86400);">Y</div>
399 <div title="Set all images to this timespan"
400 onclick="nav_set_reference ('${id}');">!</div>
406 # print qq# <td><img src="$url" /></td>\n#;
419 L<Collectd::Graph::Type>
421 =head1 AUTHOR AND LICENSE
423 Copyright (c) 2008 by Florian Forster
424 E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>. Licensed under the terms of the GNU
425 General Public License, VersionE<nbsp>2 (GPLv2).
429 # vim: set shiftwidth=2 softtabstop=2 tabstop=8 :