Project

General

Profile

Download (7.79 KB) Statistics
| Branch: | Revision:
#!/usr/bin/perl
#
# Generate a machine readable index from mrtg.cfg inputs
#

our $VERSION=0.5;

use strict;
use warnings;

use FileHandle;

use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;

use CGI ':all';
use CGI::Carp qw(fatalsToBrowser);
use Sys::Hostname;

use HTML::Entities();

# List of config files to search for
my @config_files = (
'/etc/mrtg.cfg',
'/usr/share/WWW/mrtg/mrtg.cfg',
);

# FIXME - globals
my $gifdone = 0; # Scan for newest graph and save info for later
my $warnings;
my $db = {}; # storage for all tests found

$db->{mrtg}{default}{post}{xsize} = 500;
$db->{mrtg}{default}{post}{ysize} = 135;

sub config_save_val($$$$) {
my ($filename,$target,$var,$val) = @_;

my $this_ysize;
my $this_xsize;
my $this_title;

# HACK!
if ( $var eq 'ysize' ) {
$val += 35;
} elsif ( $var eq 'xsize' ) {
$val += 100;
}

# Save any default initializers
if ($target eq '_') {
$db->{mrtg}{default}{post}{$var} = $val;
return;
}
if ($target eq '^') {
$db->{mrtg}{default}{pre}{$var} = $val;
return;
}

# Check for target name collision, and uniqueify
my $unique_target = $target;
my $unique_id = 1;
while (defined $db->{target}->{$unique_target}
&& $db->{target}->{$unique_target}->{_cfgfile} ne $filename) {
$unique_target = $target . '_' . $unique_id;
$unique_id++;
}

if (!defined $db->{target}->{$unique_target}) {
# initialize a new target

#TODO - instantiate new tests from all of the "_"/"^" defaults

push @{$db->{sequence}}, $unique_target;

$db->{target}->{$unique_target}->{_target} = $target;
$db->{target}->{$unique_target}->{_cfgfile} = $filename;
$db->{target}->{$unique_target}->{_sequence} = @{$db->{sequence}};

$db->{target}{$unique_target}{xsize} = $db->{mrtg}{default}{post}{xsize};
$db->{target}{$unique_target}{ysize} = $db->{mrtg}{default}{post}{ysize};

}
$db->{target}{$unique_target}{mrtg}{$var} = $val;
}

# Load one or more files
#
sub config_load_file(@) {
while (@_) {
my $filename = shift;

my $fh = new FileHandle $filename,"r";
if (!defined $fh) {
# FIXME globals
$warnings .= "Warning: Could not open $filename $!\n";
return;
}

#TODO - multi-line values

while(<$fh>){

# Is this a standard MRTG setting?
if ( $_ =~ /^([^#[][^[]+)\[(.*)\]:\s*(.+)$/ ) {
my $var=lc $1;
my $target=lc $2;
my $val=$3;

config_save_val($filename,$target,$var,$val);

}
# TODO - Handle extra statements:
# Include
# WorkDir
# LogDir
# ##HC Magic for baseurl
}
}
}

# Itterate through the loaded targets and read their mtime and
# any other filesystem details for them.
#
sub config_read_filesystem() {
# check and update details for all known tests;
for my $i (keys %{$db->{target}}) {
my $target = $db->{target}->{$i};
my $testname = $target->{_target};

# TODO - check workdir and validate it here

# Default is GIF, unless a PNG file exists
$target->{_imagetype} = 'gif';
if ( -e "$testname-day.png" ) {
$target->{_imagetype} = "png";
}

# TODO - change the filename based on the displayed period
my $filename = $testname . "-day." . $target->{_imagetype};
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
$atime,$mtime,$ctime,$blksize,$blocks)
= stat($filename);
if (!defined $mtime) {
# if we cannot stat the file, skip it
next;
}

$target->{_mtime} = $mtime;

# FIXME - global
# find the newest file
if ($mtime > $gifdone) {
$gifdone = $mtime;
}
}
}

sub extract_grouphosttest($) {
my ($name) = @_;

# Do not classify a test with no separators in it
if ($name !~ /,/) {
return undef;
}

my ($group,$host,$test) = split ',', $name;
# TODO cope with missing group/host/test parts.
if (!defined $group || !defined $host || !defined $test) {
return undef;
}
return ($group,$host,$test);
}

# Read through the list of targets and find common hostname prefixes,
# so that we can group these hosts together
# (a hosttype entry)
#
sub maketree_find_hosttypes() {

for my $name (keys %{$db->{target}}) {
my ($group,$host,$test) = extract_grouphosttest($name);
if (!defined($group)) {
next;
}
my $hosttype;
# TODO - this needs to have a better way...
# hosttype is the non digit hostname prefix - thus
# "proxy-1" and "proxy-2" would both end up in the
# "proxy-" hosttype
if ($host =~ m/^(.*[^0-9])(\d+)$/) {
$hosttype = $1.'*';
} else {
next;
}

$db->{target}->{$name}->{_hosttype} = $hosttype;
$db->{hosttype}->{$hosttype}->{$host}++;
}

for my $hosttype (keys %{$db->{hosttype}}) {
if ((keys %{$db->{hosttype}->{$hosttype}})<2) {
delete $db->{hosttype}->{$hosttype};
}
}
}

sub maketree_grouphosttest($$) {
my ($name,$sequence) = @_;

my ($group,$host,$test) = extract_grouphosttest($name);
if (!defined($group)) {
# TODO cope with missing group/host/test parts.
# for now, just assume it is not a group/host/test
$db->{tree}->{other}->{$name} = $sequence;
return;
}

# a valid hosttype both:
# defined($db->{target}->{$name}->{hosttype}) &&
# defined($db->{hosttype}->{$hosttype})
my $hosttype = $db->{target}->{$name}->{_hosttype};
if (defined($hosttype) && !defined($db->{hosttype}->{$hosttype})) {
$hosttype=undef;
}

# TODO - determine a better description for this tree leaf
my $testtreename = 'Graph Type';

if (defined $hosttype) {
$db->{tree}->{group}->{$group}->{$hosttype}->{$host}->{$test}->{$name} = $sequence;
$db->{tree}->{group}->{$group}->{$hosttype}->{$testtreename}->{$test}->{$name} = $sequence;
$db->{tree}->{group}->{ALL}->{$hosttype}->{$host}->{$test}->{$name} = $sequence;
$db->{tree}->{group}->{ALL}->{$testtreename}->{$test}->{$hosttype}->{$host}->{$name} = $sequence;
$db->{tree}->{group}->{$group}->{$testtreename}->{$test}->{$hosttype}->{$host}->{$name} = $sequence;
} else {
$db->{tree}->{group}->{$group}->{$host}->{$test}->{$name} = $sequence;
$db->{tree}->{group}->{ALL}->{$host}->{$test}->{$name} = $sequence;
$db->{tree}->{group}->{ALL}->{$testtreename}->{$test}->{$host}->{$name} = $sequence;
$db->{tree}->{group}->{$group}->{$testtreename}->{$test}->{$host}->{$name} = $sequence;
}
}

sub maketree_cfgfile($$) {
my ($name,$sequence) = @_;

# config files
my $cfgfile = $db->{target}->{$name}->{_cfgfile};

my @cfgpath = split /\//,$cfgfile;

shift @cfgpath; # remove initial slash

unshift @cfgpath,"config"; # ensure that the config node exists
my $node = $db->{tree};

while (@cfgpath ) {
if (!defined $node->{$cfgpath[0]}) {
$node->{$cfgpath[0]} = {};
}
$node = $node->{$cfgpath[0]};
shift @cfgpath;
}

# assign a target name and sequence to the final node
$node->{$name} = $sequence;
}

# Look through the target database and create tree entries for each cfgfile
#
sub maketree() {
maketree_find_hosttypes();

for my $name (keys %{$db->{target}}) {
my $sequence = $db->{target}->{$name}->{_sequence};
maketree_cfgfile($name,$sequence);
maketree_grouphosttest($name,$sequence);
# TODO - parse the Target setting and create trees
# TODO - parse magic comments and create trees here
}

# TODO - look for nodes that have only one child and that child is
# childless and prune the child
}

my $this_host = hostname;

print header,
start_html(-TITLE=>"$this_host indexmrtg (V$VERSION)"),
"\n";

config_load_file(@config_files);
config_read_filesystem();

# Time the next update to occur a little while after the next interval completes
my $interval = 300; # 5 min update interval
my $guardband = 15; # updates occur this many seconds after predicted gif completion
my $refresh = $interval + $guardband + $gifdone - time; # predict how long until next update
$refresh = $interval if $refresh <= $guardband;
my $expires = gmtime (time + $interval * 2 + $guardband);

print
"<meta http-equiv=\"expires\" content=\"$expires GMT\">\n",
"<meta http-equiv=\"refresh\" content=$refresh>\n";

print end_html;


if (param('debug')) {
print "\n",Dumper(\$db),"\n";
}


(4-4/6)