#!/usr/bin/perl # Copyright (C) 2008-2011 Florian Forster # Copyright (C) 2011 noris network AG # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; only version 2 of the License is applicable. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # Authors: # Florian "octo" Forster use strict; use warnings; use utf8; use vars (qw($BASE_DIR)); BEGIN { if (defined $ENV{'SCRIPT_FILENAME'}) { if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$}) { $::BASE_DIR = $1; unshift (@::INC, "$::BASE_DIR/lib"); } } } use Carp (qw(cluck confess)); use CGI (':cgi'); use CGI::Carp ('fatalsToBrowser'); use HTML::Entities ('encode_entities'); use Data::Dumper; use Collectd::Graph::Config (qw(gc_read_config gc_get_scalar)); use Collectd::Graph::TypeLoader (qw(tl_load_type)); use Collectd::Graph::Common (qw(get_files_from_directory get_all_hosts get_timespan_selection get_selected_files get_host_selection get_plugin_selection flush_files)); use Collectd::Graph::Type (); our $TimeSpans = { Hour => 3600, Day => 86400, Week => 7 * 86400, Month => 31 * 86400, Year => 366 * 86400 }; my %Actions = ( list_hosts => \&action_list_hosts, show_selection => \&action_show_selection ); sub base_dir { if (defined $::BASE_DIR) { return ($::BASE_DIR); } if (!defined ($ENV{'SCRIPT_FILENAME'})) { return; } if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$}) { $::BASE_DIR = $1; return ($::BASE_DIR); } return; } sub lib_dir { my $base = base_dir (); if ($base) { return "$base/lib"; } else { return "../lib"; } } sub sysconf_dir { my $base = base_dir (); if ($base) { return "$base/etc"; } else { return "../etc"; } } sub init { my $lib_dir = lib_dir (); my $sysconf_dir = sysconf_dir (); if (!grep { $lib_dir eq $_ } (@::INC)) { unshift (@::INC, $lib_dir); } gc_read_config ("$sysconf_dir/collection.conf"); } sub main { my $Debug = param ('debug') ? 1 : 0; my $action = param ('action') || 'list_hosts'; if (!exists ($Actions{$action})) { print STDERR "No such action: $action\n"; return (1); } init (); $Actions{$action}->(); return (1); } # sub main sub can_handle_xhtml { my %types = (); if (!defined $ENV{'HTTP_ACCEPT'}) { return; } for (split (',', $ENV{'HTTP_ACCEPT'})) { my $type = lc ($_); my $q = 1.0; if ($type =~ m#^([^;]+);q=([0-9\.]+)$#) { $type = $1; $q = 0.0 + $2; } $types{$type} = $q; } if (!defined ($types{'application/xhtml+xml'})) { return; } elsif (!defined ($types{'text/html'})) { return (1); } elsif ($types{'application/xhtml+xml'} < $types{'text/html'}) { return; } else { return (1); } } # can_handle_xhtml my $html_started; sub start_html { return if ($html_started); my $end; my $begin; my $timespan; $end = time (); $timespan = get_timespan_selection (); $begin = $end - $timespan; if (can_handle_xhtml ()) { print header (-Content_Type => 'application/xhtml+xml; charset=UTF-8'); print < HTML } else { print header (-Content_Type => 'text/html; charset=UTF-8'); print < HTML } print < collection.cgi, Version 3 HTML $html_started = 1; } sub end_html { print < HTML $html_started = 0; } sub contains_invalid_chars { my $str = shift; for (split (m//, $str)) { my $n = ord ($_); # Whitespace is allowed. if (($n >= 9) && ($n <= 13)) { next; } elsif ($n < 32) { return (1); } } return; } sub show_selector { my $timespan_selection = get_timespan_selection (); my $host_selection = get_host_selection (); my $plugin_selection = get_plugin_selection (); print <
Data selection HTML for (sort (keys %$plugin_selection)) { next if contains_invalid_chars ($_); my $plugin = encode_entities ($_); my $selected = $plugin_selection->{$_} ? ' selected="selected"' : ''; print qq# \n#; } print <
HTML } # show_selector sub action_list_hosts { start_html (); show_selector (); my @hosts = get_all_hosts (); print "
    \n"; for (sort @hosts) { my $url = encode_entities (script_name () . "?action=show_selection;hostname=$_"); next if contains_invalid_chars ($_); my $name = encode_entities ($_); print qq#
  • $name
  • \n#; } print "
\n"; end_html (); } # action_list_hosts =head1 MODULE LOADING This script makes use of the various B modules. If a file like C is encountered it tries to load the B module and, if that fails, falls back to the B base class. If you want to create a specialized graph for a certain type, you have to create a new module which inherits from the B base class. A description of provided (and used) methods can be found in the inline documentation of the B module. There are other, more specialized, "abstract" classes that possibly better fit your need. Unfortunately they are not yet documented. =over 4 =item B Specialized class that groups files by their plugin instance and stacks them on top of each other. Example types that inherit from this class are B and B. =item B Specialized class for input/output graphs. This class can only handle files with exactly two data sources, input and output. Example types that inherit from this class are B and B. =back =cut sub action_show_selection { start_html (); show_selector (); my $all_files; my $timespan; my $types = {}; my $id_counter = 0; $all_files = get_selected_files (); $timespan = get_timespan_selection (); if (param ('debug')) { print "
", Data::Dumper->Dump ([$all_files], ['all_files']), "
\n"; } # Send FLUSH command to the daemon if necessary and possible. flush_files ($all_files, begin => time () - $timespan, end => time (), addr => gc_get_scalar ('UnixSockAddr', undef), interval => gc_get_scalar ('Interval', 10)); for (@$all_files) { my $file = $_; my $type = ucfirst (lc ($file->{'type'})); $type =~ s/[^A-Za-z0-9_]//g; $type =~ s/_([A-Za-z0-9])/\U$1\E/g; if (!defined ($types->{$type})) { $types->{$type} = tl_load_type ($file->{'type'}); if (!$types->{$type}) { warn ("tl_load_type (" . $file->{'type'} . ") failed"); next; } } $types->{$type}->addFiles ($file); } #print STDOUT Data::Dumper->Dump ([$types], ['types']); print qq# \n#; for (sort (keys %$types)) { my $type = $_; if (!defined ($types->{$type})) { next; } my $graphs_num = $types->{$type}->getGraphsNum (); for (my $i = 0; $i < $graphs_num; $i++) { my $args = $types->{$type}->getGraphArgs ($i); my $url = encode_entities ("graph.cgi?$args;begin=-$timespan"); my $id = sprintf ("graph%04i", $id_counter++); print " \n"; print " \n" if ($i == 0); print <
A graph
-
+
H
D
W
M
Y
!
EOF # print qq#
\n#; print " \n"; } } print "
$type
\n"; end_html (); } main (); =head1 SEE ALSO L =head1 AUTHOR AND LICENSE Copyright (c) 2008 by Florian Forster EoctoEatEverplant.orgE. Licensed under the terms of the GNU General Public License, VersionE2 (GPLv2). =cut # vim: set shiftwidth=2 softtabstop=2 tabstop=8 :