#!/usr/bin/perl -w
# -*- perl -*-

#    Generate overview-[Daily|Monthly|Weekly|Yearly].html files
#    with links to latest graphs produced by cricket
#
#    Maintained by Dobrica Pavlinusic <dpavlin@rot13.org>
#
#    Based on generate-statics.franky from
#    Copyright (C) 1999 Noam Freedman <noam@noam.com> 
#
#
#
#    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; either version 2 of the License, or
#   (at your option) any later version.
#
#    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., 675 Mass Ave, Cambridge, MA 02139, USA.

# Target can be skipped in overview report with skip-overview

BEGIN {
#	$gInstallRoot = (($0 =~ m:^(.*/):)[0] || "./") . ".";
	require '/etc/cricket/cricket-conf.pl';

	# You need to update this to point to the URL
	# you use to access Cricket.
#	$gBaseURL = "http://localhost/~cricket/grapher.cgi";
	my $hostname = `hostname -f`;
	chomp($hostname);
	$gBaseURL = "/cgi-bin/cricket/grapher.cgi";

	# change this to destination directory
	$path = "/data/mon/";
}

#use lib "$gInstallRoot/../lib";
use lib "$Common::global::gInstallRoot/lib";

use File::Basename;
use LWP::UserAgent;
use HTTP::Request;
use HTTP::Response;

use ConfigTree::Cache;

use Common::HandleTarget;
use Common::Map;
use Common::Options;
use Common::Log;

#
# Set this option to 0 if you don't want JavaScript roll-up/roll-down
# of images to be created
#
my $dynamic_js = 1;

Common::Options::commonOptions( 'baseURL=s' => \$gBaseURL );
initConst();

$Common::global::gCT = new ConfigTree::Cache;
$gCT = $Common::global::gCT;
$gCT->Base($Common::global::gConfigRoot);
$gCT->Warn(\&Warn);

if (! $Common::global::gCT->init()) {
    Die("Failed to open compiled config tree from " .
		"$Common::global::gConfigRoot/config.db: $!");
}

# if they gave us no subtrees to focus on, use the root of the config tree
if ($#ARGV+1 == 0) {
    push @ARGV, '/';
}

my %html;	# this will store created html

my($subtree);
foreach $subtree (@ARGV) {
    if ($gCT->nodeExists($subtree)) {
        $gCT->visitLeafs($subtree, \&handleTarget,
                \&handleTargetInstance, \&localHandleTargetInstance);
    } else {
        Warn("Unknown subtree $subtree.");
    }
}

my %html_file;

foreach my $item (sort keys %html) {
	my ($range,$target) = split(/\t/,$item,2);
	my (undef,$service,$arg) = split(/\//,$target,3);

	$html_file{"$range/$service"} .= $html{$item};
	$html_file{"overview-$range"} .= $html{$item};
}

foreach my $key (keys %html_file) {

	my $filename = "$path/$key.html";
	my($dir) = dirname($filename);
	if (! -d $dir) {
	   Info("Making directory $dir to hold file $filename.");
	   Common::Util::MkDir($dir);
	}
	Info("Dumping HTML for $key to $filename.");
	open(OUT,"> $filename") || die "can't open output html '$filename': $!";
	print OUT "<html><head><title>$key</title>";
	print OUT '<meta content="no-cache" http-equiv="Pragma">
		<meta content="300" http-equiv="Refresh">' if ($filename =~ m/Daily/i);
	print OUT '</head><body>';
	my $html=<<'HTMLEND';

<script language="JavaScript">
var Selected = "";

function Switch(elm)
{
	var cookie = Get_Cookie("##cookie_name##");

	var elmref;

	// uncomment following lines for just 1 selected content
/*
	if (Selected != "")
	{
		elmref = eval("document.getElementById('" + Selected + "_h1')");
		if (elmref) elmref.style.display = 'none';
		elmref = eval("document.getElementById('" + Selected + "_h0')");
		// roll-up color
		if (elmref) elmref.bgColor = '#e0e0e0';
	}
	if (Selected != elm.name) 
	{
		Selected = elm.name;
		elmref = eval("document.getElementById('" + Selected + "_h1')");
		if (elmref) 
		{
			if (elmref.style.display=='none') elmref.style.display='';
			else elmref.style.display = 'none';
		}
		elmref = eval("document.getElementById('" + Selected + "_h0')");
		// roll-down color
		if (elmref) elmref.bgColor = '#ffffff';
	}
	else
		Selected="";
*/

	// or use this to unroll more than one line
	Selected = elm.name;
	elmref = eval("document.getElementById('" + Selected + "_h1')");
	elmref0 = eval("document.getElementById('" + Selected + "_h0')");
	if (elmref) 
	{
		if (elmref.style.display=='none') {
			elmref.style.display='';
			if (elmref0) elmref0.bgColor = '#eeeeee';
		} else {
			elmref.style.display = 'none';
			if (elmref0) elmref0.bgColor = '#e0e0e0';
		}
		cookie ^= 1 << Selected;
	}

	Set_Cookie("##cookie_name##",cookie);
	return false;
}

function SwitchThis()
{
	return Switch(this);
}


</script>

<table border=0 id=DynamicTable bgColor='#e0e0e0' cellpadding=2 cellspacing=1>

HTMLEND
	if ($dynamic_js) {
		my $cookie_name = $key;
		$cookie_name =~ s/[^a-zA-Z0-9]//g;
		$html =~ s/##cookie_name##/$cookie_name/g;
		print OUT $html;
	}

	print OUT $html_file{$key};
	$html = <<'HTMLEND';
</table>

<script language=javascript>

function Get_Cookie(name) {
    var start = document.cookie.indexOf(name+"=");
    var len = start+name.length+1;
    if ((!start) && (name != document.cookie.substring(0,name.length))) return null;
    if (start == -1) return null;
    var end = document.cookie.indexOf(";",len);
    if (end == -1) end = document.cookie.length;
    return unescape(document.cookie.substring(len,end));
}

function Set_Cookie(name,value,expires,path,domain,secure) {
    document.cookie = name + "=" +escape(value) +
        ( (expires) ? ";expires=" + expires.toGMTString() : "") +
        ( (path) ? ";path=" + path : "") + 
        ( (domain) ? ";domain=" + domain : "") +
        ( (secure) ? ";secure" : "");
}

function Delete_Cookie(name,path,domain) {
    if (Get_Cookie(name)) document.cookie = name + "=" +
       ( (path) ? ";path=" + path : "") +
       ( (domain) ? ";domain=" + domain : "") +
       ";expires=Thu, 01-Jan-70 00:00:01 GMT";
}

var table = document.getElementById("DynamicTable");
var links = table.getElementsByTagName("a");
var cookie = Get_Cookie("##cookie_name##");
var new_cookie = 0;
for (var i = 0; i < links.length; i++) {
    if (links[i].id == "DynMessLink") {
    	links[i].onclick = SwitchThis;
	elmref = eval("document.getElementById('" + links[i].name + "_h1')");
	elmref0 = eval("document.getElementById('" + links[i].name + "_h0')");
	if (elmref && cookie) {
		if (cookie & 1 << links[i].name) {
			elmref.style.display='';
			if (elmref0) elmref0.bgColor = '#eeeeee';
		} else {
			elmref.style.display = 'none';
			if (elmref0) elmref0.bgColor = '#e0e0e0';
		}
	} else if(elmref) {
		// default: show all
		new_cookie |= 1 << links[i].name;
		if (elmref.style.display=='none') {
			elmref.style.display='';
			if (elmref0) elmref0.bgColor = '#eeeeee';
		} else {
			// hide? no...
			//elmref.style.display = 'none';
			//if (elmref0) elmref0.bgColor = '#e0e0e0';
			if (elmref0) elmref0.bgColor = '#eeeeee';
		}
	}
    }
}
if (new_cookie) Set_Cookie("##cookie_name##",new_cookie);
</script>
HTMLEND

	if ($dynamic_js) {
		my $cookie_name = $key;
		$cookie_name =~ s/[^a-zA-Z0-9]//g;
		$html =~ s/##cookie_name##/$cookie_name/g;
		print OUT $html;
	}

	print OUT "</body></html>";
	close(OUT);

}

exit;

sub do_html {
	my ($desc,$url,$img) = @_;
	if ($dynamic_js) {
		my $html =<<'HTMLEND';
<tr bgcolor='#e0e0e0' id=##id##_h0>
<td><a name="##id##"><a href="###id##" id=DynMessLink name=##id## >##desc##</a></td>
</tr>
<tr id=##id##_h1 style='' bgcolor='#ffffff' >
<td><a href="##url##"><img src="##img##"</td>
</tr>
HTMLEND
		$html =~ s/##desc##/$desc/;
		$html =~ s/##url##/$url/;
		$html =~ s/##img##/$img/;
		$html =~ s/##id##/$dynamic_js/g;
		$dynamic_js++;			# increment usage counter
		return $html;
	} else {
		return "$desc<br><a href=\"$url\"><img src=\"$img\"></a><br>\n";
	}
}

sub localHandleTargetInstance {
	my($Name, $target) = @_;

	$targetpath = $target->{'auto-target-path'};
	$targetname = $target->{'auto-target-name'};

	if (! defined($target->{'skip-overview'}))
	{
		Info("Working on target $targetname.");
		my($reqRanges,@ranges);

		$reqRanges = $target->{'static-ranges'};

#		if (defined($target->{'static-path'}) &&
#		    defined($target->{'static-name'}))
#		{
#			$path = $target->{'static-path'};
#			$name = $target->{'static-name'};

		if (1) {

			my($range, @ranges);
			@ranges = getRanges($reqRanges);

			foreach $range (@ranges)
			{
				$rangeLabel = rangeToLabel($range);

				my($paramtarget) = "$targetpath/$targetname";
	
				my($paraminst);

				if (defined($target->{'inst'}))
				{
					$paraminst = $target->{'inst'};
				}

				my($paramrange) = $range;

				# DO DSLIST STUFF

				# find the ds names based on the target type
				my($ttype) = lc($target->{'target-type'});
				my($ttRef) = $main::gCT->configHash($Name, 'targettype', $ttype, $target);

				# If there are views defined, then we generate graphs
				# for each view.

				my($dslist);

				if (defined($ttRef->{'view'}))
				{
					my($v);
					foreach $v (split(/\s*,\s*/, $ttRef->{'view'}))
					{
						# views are like this: "cpu: cpu1load  cpu5load"
						my($vname, $dss) = split(/\s*:\s*/, $v, 2);

						$dslist = $dss;
						$dslist =~ s/\s*$//;
						$dslist =~ s/\s+/,/g;

						$URL = "$gBaseURL?type=png&target=$paramtarget";
						$URL .= "&dslist=$dslist&range=$paramrange";
						my $desc = "$paramtarget $vname";
						$desc .= " <b>".$target->{'short-desc'}."</b>" if (defined $target->{'short-desc'});
						if ($paraminst ne "") {
							$URL .= "&inst=$paraminst";
						}

						Info("Retrieving graph for $desc");
#						getURL($URL,"$path/$name-$vname-$rangeLabel.png");
						$tmp_URL = "$gBaseURL?target=$paramtarget&range=d:w:m:y&view=$vname";
						$html{"$rangeLabel\t$paramtarget"}.=do_html($desc,$tmp_URL,$URL);
					}
				} else {
					$dslist = $ttRef->{'ds'};
					# squeeze out any extra spaces
					$dslist = join(',', split(/\s*,\s*/, $dslist));

					$URL = "$gBaseURL?type=png&target=$paramtarget";
					$URL .= "&dslist=$dslist&range=$paramrange";
					my $desc ="$paraminst $rangeLabel";
					$desc .= " <b>".$target->{'short-desc'}."</b>" if (defined $target->{'short-desc'});
					if ($paraminst ne "") {
						$URL .= "&inst=$paraminst";
					}

					Info("Retrieving graph for $desc");

#					getURL($URL,"$path/$name-$rangeLabel.png");
					$tmp_URL = "$gBaseURL?target=$paramtarget&range=d:w:m:y";
					$html{"$rangeLabel\t$paramtarget"}.=do_html($desc,$tmp_URL,$URL);
				}
			}
		}
	}
		
	return;
}


sub getRanges {
	my($scales) = @_;
	$scales = "d:w:m:y" unless (defined($scales));

	# these definitions mirror how MRTG 2.5 sets up its graphs
	my(%scaleMap) = (       'd' => $main::kHour * 42,
					    'w' => $main::kDay * 10,
						'm' => $main::kWeek * 6,
					    'y' => $main::kMonth * 16);

	my($scale, @res);
	foreach $scale (split(/\s*:\s*/, $scales)) {
		# later, we might do more sophisticated scale specification
		$scale = $scaleMap{$scale};
		push @res, $scale;
	}
	return @res;
}


sub initConst {
	$main::kMinute = 60;	  #  60 seconds/min
	$main::kHour   = 60 * $main::kMinute;#  60 minutes/hr
	$main::kDay    = 24 * $main::kHour;  #  24 hrs/day
	$main::kWeek   = 7  * $main::kDay;   #   7 days/week
	$main::kMonth  = 30 * $main::kDay;   #  30 days/month
	$main::kYear   = 365 * $main::kDay;  # 365 days/year

	$main::kTypeUnknown     = 0;
	$main::kTypeUnknown     = 0;    # shut up, -w.
	$main::kTypeDaily       = 1;
	$main::kTypeWeekly      = 2;
	$main::kTypeMonthly     = 3;
	$main::kTypeYearly      = 4;

	@main::gRangeNameMap = ( undef, 'Daily', 'Weekly', 'Monthly', 'Yearly' );

}

sub rangeToLabel {
	my($range) = @_;
	return $main::gRangeNameMap[rangeType($range)];
}

sub rangeType {
	my($range) = @_;
	my($rangeHours) = $range / 3600;

	# question: when is kTypeUnknown appropriate?

	if ($range < $main::kWeek) {
		return $main::kTypeDaily;
	} elsif ($range < $main::kMonth) {
		return $main::kTypeWeekly;
	} elsif ($range < $main::kYear) {
		return $main::kTypeMonthly;
	} else {
		return $main::kTypeYearly;
	}
}


sub getURL
{
	my($url,$filename) = @_;

	Debug("Fetching url: $url");

	my $ua = new LWP::UserAgent;
	my $request = new HTTP::Request('GET', $url);
	my $response = $ua->request($request);

	if ($response->is_success) {
		my($dir) = dirname($filename);
		if (! -d $dir) {
			Info("Making directory $dir to hold file $filename.");
			Common::Util::MkDir($dir);
		}

		if (!open(URL,">$filename"))
		{
			Error("Error writing to $filename: $!");
			return;
		}
		print URL $response->content;
		close(URL);
	}
	else
	{
		Error("Error retrieving target graph: " . $response->message());
	}
}
