Index: /noc/.bash_logout
===================================================================
--- /noc/.bash_logout	(revision 42)
+++ /noc/.bash_logout	(revision 42)
@@ -0,0 +1,3 @@
+# ~/.bash_logout
+
+clear
Index: /noc/.bash_profile
===================================================================
--- /noc/.bash_profile	(revision 42)
+++ /noc/.bash_profile	(revision 42)
@@ -0,0 +1,13 @@
+# .bash_profile
+
+# Get the aliases and functions
+if [ -f ~/.bashrc ]; then
+	. ~/.bashrc
+fi
+
+# User specific environment and startup programs
+
+PATH=$PATH:$HOME/bin
+
+export PATH
+unset USERNAME
Index: /noc/.bashrc
===================================================================
--- /noc/.bashrc	(revision 42)
+++ /noc/.bashrc	(revision 42)
@@ -0,0 +1,8 @@
+# .bashrc
+
+# Source global definitions
+if [ -f /etc/bashrc ]; then
+	. /etc/bashrc
+fi
+
+# User specific aliases and functions
Index: /noc/html/.attic/show.php
===================================================================
--- /noc/html/.attic/show.php	(revision 42)
+++ /noc/html/.attic/show.php	(revision 42)
@@ -0,0 +1,91 @@
+<?php
+/*
+(c)2006 Joe Presbrey <presbrey@mit.edu>
+*/
+
+include('rrdgraph.inc.php');
+require_once('rrdgraph.lib.php');
+
+if (isset($_GET['h'])&&isset($_GET['s'])&&isset($_GET['i'])) {
+	displayGraph($_GET['h'],$_GET['s'],$_GET['i']);
+	exit;
+}
+
+$skip_host[] = 'localhost';
+$skip_service[] = 'DISK_%2Fafs';
+$skip_service[] = 'DISK_%2Fboot';
+$skip_service[] = 'DISK_%2Fdev%2Fshm';
+$only_host = array();
+if (isset($_GET['host']))
+	$only_host[] = $_GET['host'];
+$only_service = array();
+if (isset($_GET['service']))
+	$only_service[] = $_GET['service'];
+
+function getServices($time=115200) {
+	$s = array();
+	foreach(glob("{$GLOBALS['RRD_PATH']}/*.rrd") as $f) {
+		if (time()-filemtime($f)<=$time) {
+			$e = explode('_', basename($f));
+//			//$s[$e[0]][] = $e[1];
+			if ($e[1] == 'DISK')
+				$s[array_shift($e)][] = substr(implode('_', $e),0,-4);
+			else {
+//				array_pop($e);
+				$s[$e[0]][] = $e[1];
+			}
+		}
+	}
+	return $s;
+}
+
+function displayGraph($host,$service,$time=null) {
+	$times = array(
+		'hour' => 19200,
+		'day' => 115200,
+		'week' => 691200,
+		'month' => 3024000,
+		'year' => 34560000);
+	$geom = array(
+		'hour' => '450x180',
+		'day' => '300x100',
+		'week' => '300x100',
+		'month' => '300x100',
+		'year' => '300x100');
+	$title = array(
+		'hour' => "$host: $service",
+		'day' => "$service today",
+		'week' => "$service this week",
+		'month' => "$service this month",
+		'year' => "$service this year");
+	if (is_null($time) || !isset($times[$time])) $time = 'day';
+	outputGraph($host, $service, $times[$time], array('legend'=>($time!='hour'?false:true),
+							'title'=>$title[$time],
+							'geom'=>explode('x',$geom[$time])));
+	//virtual('/ng/cgi-bin/show.cgi?host='.$host.'&service='.$service.'&graph='.$times[$time].'&geom='.$geom[$time].'&rrdopts='.str_replace(' ','_',$rrdopts[$time]));
+	//virtual('/ng/cgi-rin/show.cgi?host='.$host.'&service='.$service.'&graph=118800');
+	//virtual('/ng/cgi-bin/show.cgi?host=better-mousetrap&service=LOAD&db=load&graph=118800');
+	exit;
+}
+
+//displayGraph('better-mousetrap','LOAD');
+echo '<table border=0 cellspacing=0 cellpadding=0>';
+foreach(getServices() as $host=>$services) {
+	$host = urldecode($host);
+	if (in_array($host, $skip_host)) continue;
+	if (count($only_host) && !in_array($host, $only_host)) continue;
+	echo '<tr>';
+	foreach($services as $service) {
+		if (in_array($service, $skip_service)) continue;
+		if (count($only_service) && !in_array($service, $only_service)) continue;
+		echo '<td>';	
+		printf('<img src="show.php?h=%s&s=%s&i=%s">', $host, $service, 'hour');
+		echo '</td><td>';
+		printf('<img src="?h=%s&s=%s&i=%s">', $host, $service, 'day');
+		echo '<br />';
+		printf('<img src="?h=%s&s=%s&i=%s">', $host, $service, 'week');
+		echo '</td>';
+	}
+	echo '</tr>';
+}
+echo '</table>';
Index: /noc/html/.htaccess
===================================================================
--- /noc/html/.htaccess	(revision 42)
+++ /noc/html/.htaccess	(revision 42)
@@ -0,0 +1,6 @@
+
+RewriteEngine On
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_FILENAME}.php -f
+RewriteRule ^(.*) $1.php
Index: /noc/html/graph.php
===================================================================
--- /noc/html/graph.php	(revision 42)
+++ /noc/html/graph.php	(revision 42)
@@ -0,0 +1,20 @@
+<?php
+include('rrdgraph.inc.php');
+require_once('rrdgraph.lib.php');
+
+import_request_variables('g','i_');
+
+$host = isset($i_host)?$i_host:'better-mousetrap';
+$service = isset($i_service)?$i_service:'LOAD';
+$time = isset($i_time)?$i_time:'115200';
+$legend = isset($i_legend)&&$i_legend==0?0:1;
+//$title = isset($i_title)&&strlen($i_title)?($i_title):('%h: %s');
+$title = '%h: %s';
+if (isset($i_title) && $i_title==0) $title = null;
+$geom = isset($i_geom)&&strpos($i_geom,'x')?explode('x',$i_geom):array(403,146); /* (500x200 on output) */
+$width = isset($i_width)&&is_numeric($i_width)?floor($i_width):$geom[0];
+$height = isset($i_height)&&is_numeric($i_height)?floor($i_height):$geom[1];
+
+$time = rrd_time($time);
+
+outputGraph($host, $service, $time, array('legend'=>($legend==1?true:false), 'title'=>$title, 'geom'=>array($width,$height)));
Index: /noc/html/index.php
===================================================================
--- /noc/html/index.php	(revision 42)
+++ /noc/html/index.php	(revision 42)
@@ -0,0 +1,27 @@
+<?php
+
+
+?>
+<html>
+<head>
+<title>scripts-noc.mit.edu</title>
+<style>
+* {
+	margin: 0;
+}
+</style>
+</head>
+
+<body>
+
+<div style="display: block;">
+<img src="ping" /><img src="ping?t=604800" />
+</div>
+<div style="display: block;">
+<img src="load" /><img src="load?t=604800" />
+</div>
+<div style="display: block;">
+<img src="mysql" /><img src="mysql?t=604800" />
+</div>
+
+</body>
Index: /noc/html/load.php
===================================================================
--- /noc/html/load.php	(revision 42)
+++ /noc/html/load.php	(revision 42)
@@ -0,0 +1,18 @@
+<?php
+require_once('rrdgraph.inc.php');
+require_once('rrdgraph.lib.php');
+
+$RRD_IGNORE['load'][] = 'avg1min';
+$RRD_IGNORE['load'][] = 'avg5min';
+
+$time = isset($_GET['t'])?rrd_time($_GET['t']):100000;
+
+outputGraph(array('b-m',
+		  'o-f',
+		  'k-s',
+		  's-b',
+		  'n-f',
+		  'n-b'),
+		  'LOAD', $time, array('legend'=>1,
+					'title'=>'%s',
+					'geom'=>array(403,146)));
Index: /noc/html/mysql.php
===================================================================
--- /noc/html/mysql.php	(revision 42)
+++ /noc/html/mysql.php	(revision 42)
@@ -0,0 +1,14 @@
+<?php
+require_once('rrdgraph.inc.php');
+require_once('rrdgraph.lib.php');
+
+//$RRD_IGNORE['mysql'][] = 'avg15min';
+//$RRD_IGNORE['load'][] = 'avg5min';
+
+$time = isset($_GET['t'])?rrd_time($_GET['t']):100000;
+
+outputGraph(array('k-s',
+		  's-b'),
+		  'MYSQL', $time, array('legend'=>1,
+					'title'=>'%s',
+					'geom'=>array(403,146)));
Index: /noc/html/ping.php
===================================================================
--- /noc/html/ping.php	(revision 42)
+++ /noc/html/ping.php	(revision 42)
@@ -0,0 +1,17 @@
+<?php
+require_once('rrdgraph.inc.php');
+require_once('rrdgraph.lib.php');
+
+$RRD_IGNORE['ping'][] = 'losspct';
+
+$time = isset($_GET['t'])?rrd_time($_GET['t']):100000;
+
+outputGraph(array('b-m',
+		  'o-f',
+		  'k-s',
+		  's-b',
+		  'n-f',
+		  'n-b'),
+		  'PING', $time, array('legend'=>1,
+					'title'=>'%s',
+					'geom'=>array(403,146)));
Index: /noc/html/rrdgraph.inc.php
===================================================================
--- /noc/html/rrdgraph.inc.php	(revision 42)
+++ /noc/html/rrdgraph.inc.php	(revision 42)
@@ -0,0 +1,45 @@
+<?php
+/*
+(c)2006 Joe Presbrey <presbrey@mit.edu>
+*/
+
+$RRD_PATH = realpath(dirname(__FILE__).'/../ng/rrd');
+$RRD_IGNORE['tcp'][] = 'critical_time';
+$RRD_IGNORE['tcp'][] = 'warning_time';
+$RRD_IGNORE['tcp'][] = 'socket_timeout';
+$RRD_IGNORE['users'][] = 'uwarn';
+$RRD_IGNORE['users'][] = 'ucrit';
+$RRD_IGNORE['mysql'] = $RRD_IGNORE['tcp'];
+$RRD_IGNORE['https'] = $RRD_IGNORE['tcp'];
+$RRD_IGNORE['disk'][] = 'root';
+$RRD_IGNORE['disk'][] = 'user';
+$RRD_IGNORE['disk'][] = 'blockpct';
+$RRD_IGNORE['disk'][] = 'inodepct';
+$RRD_IGNORE['disk'][] = 'inodepct';
+$RRD_IGNORE['disk'][] = 'pctfree';
+$RRD_IGNORE['disk_/'] = $RRD_IGNORE['disk'];
+$RRD_IGNORE['disk_/afs'] = $RRD_IGNORE['disk'];
+$RRD_IGNORE['disk_/boot'] = $RRD_IGNORE['disk'];
+$RRD_IGNORE['disk_/dev/shm'] = $RRD_IGNORE['disk'];
+$RRD_IGNORE['disk_/srv'] = $RRD_IGNORE['disk'];
+
+$RRD_TIMES = array(
+	'hour' => 9000,
+	'day' => 115200,
+	'week' => 691200,
+	'month' => 3024000,
+	'year' => 34560000);
+
+function rrd_time($var) {
+	global $RRD_TIMES;
+	if (isset($RRD_TIMES[$var])) {
+		$time = $RRD_TIMES[$var];
+	} elseif (is_numeric($var)) {
+		$time = floor($var);
+	} else {
+		$time = 115200;
+	}
+	return $time;
+}
+
+?>
Index: /noc/html/rrdgraph.lib.php
===================================================================
--- /noc/html/rrdgraph.lib.php	(revision 42)
+++ /noc/html/rrdgraph.lib.php	(revision 42)
@@ -0,0 +1,148 @@
+<?php
+/*
+(c)2006 Joe Presbrey <presbrey@mit.edu>
+inspired by parts of nagiosgraph in Perl
+*/
+
+function hashcolor($x) {
+  $x .= 'x'; $c=1;
+  for($i = 0; $i < strlen($x); $i++) { $c=(51*$c+ord($x{$i}))%216; }
+  $h = array(51*floor($c/36), 51*floor($c/6%6), 51*($c%6));
+  $i = $n = $m = 0;
+  for($i = 0; $i <= 2; $i++) {
+    if ($h[$i] < $h[$m]) $m = $i;
+    if ($h[$i] > $h[$n]) $n = $i;
+  }
+  if ($h[$m]>102) $h[$m] = 102;
+  if ($h[$n]<153) $h[$n] = 153;
+  $n = ($h[2])+($h[1]*256)+$h[0]*256*256;
+  $c = sprintf("%06X", ($h[2])+($h[1]*256)+$h[0]*256*256);
+  return $c;
+}
+
+function findRRD($host, $service) {
+	if (isset($GLOBALS['RRD_PATH'])) {
+		$rrd = $GLOBALS['RRD_PATH'];
+	} else {
+		$rrd = dirname(__FILE__);
+	}
+	$f = glob("$rrd/{$host}_{$service}_*.rrd");
+	if (count($f)) {
+		$o = array_shift($f);
+	} else {
+		$host = str_replace('-','%2D',rawurlencode($host));
+		$service = str_replace('-','%2D',rawurlencode($service));
+		$f = glob("$rrd/{$host}_{$service}_*.rrd");
+		if (count($f)) {
+			$o = array_shift($f);
+		} else {
+			$f = glob("$rrd/{$host}_{$service}*.rrd");
+			if (count($f))
+				$o = array_shift($f);
+		}
+	}
+	$p = realpath($o);
+	if (strlen($p)>strlen($host)+strlen($service)) {
+		if (preg_match_all('/([^_]+)_([^_]+)_(.+).rrd/iU', basename($p), $m)) {
+			return array($p, $m[1][0], $m[2][0], $m[3][0]);
+		}
+	}
+}
+
+function graphInfo($file) {
+	$rrdinfo = `rrdtool info $file`;
+	preg_match_all('/ds\[([^\]]*)\]\./',$rrdinfo,$ds);
+	$lines = array_unique($ds[1]);
+	//sort($lines);
+	return $lines;
+}
+
+function makeDefs($file, $ignores=array(), $oneHost=true) {
+	$info = graphInfo($file[0]);
+	$defs = array();
+	$def = 'DEF:$dj=$file:$di:AVERAGE' .
+               ' LINE2:$dj#$c:$dj' .
+               ' GPRINT:$dj:MAX:Max\\\\:\\ %6.2lf%s' .
+               ' GPRINT:$dj:AVERAGE:Avg\\\\:\\ %6.2lf%s' .
+               ' GPRINT:$dj:MIN:Min\\\\:\\ %6.2lf%s' .
+               ' GPRINT:$dj:LAST:Cur\\\\:\\ %6.2lf%s\\\\n';
+	foreach($info as $sv) {
+		if (in_array(strtolower($sv), $ignores)) continue;
+		$d = str_replace('$di',$sv,$def);
+		if ($oneHost) {
+			$d = str_replace('$dj',$sv,$d);
+			$d = str_replace('$c',hashcolor($sv),$d);
+		} else {
+			$d = str_replace('$dj',urldecode($file[1]).'_'.$sv,$d);
+			$d = str_replace('$c',hashcolor(md5($file[0].$sv)),$d);
+		}
+		$d = str_replace('$file',$file[0],$d);
+		$defs[] = $d;
+	}
+	return implode(' ',$defs);
+}
+
+function outputGraph($hosts, $service, $time, $opts = array()) {
+	if (!is_array($hosts)) $hosts = array($hosts);
+	$oneHost = count($hosts)<=1;
+//	if (!is_array($services)) $services = array($services);
+	$defs = array();
+	$args = array();
+	$files = array();
+	foreach($hosts as $host) {
+		$file = findRRD($host, $service);
+		if (is_array($file) && strlen($file[0])) $files[] = $file;
+	}
+	foreach($files as $file) {
+		if (isset($GLOBALS['RRD_IGNORE'])
+		   && isset($GLOBALS['RRD_IGNORE'][strtolower($service)])) {
+			$def = makeDefs($file, $GLOBALS['RRD_IGNORE'][strtolower($service)], $oneHost);
+		} else {
+			$def = makeDefs($file, array(), $oneHost);
+		}
+		if (strlen($def)) $defs[] = $def;
+	}
+
+	if (count($opts))
+		extract($opts);
+	if (isset($geom)) {
+		if (isset($geom[0]))
+			$args[] = '-w '.$geom[0];
+		if (isset($geom[1]))
+			$args[] = '-h '.$geom[1];
+	}
+	if (isset($legend) && !$legend) {
+		$args[] = '-g';
+	}
+	if (isset($title)) {
+		if (count($files)) {
+			list($fhost, $fservice, $fdb) = array_slice(explode('_',basename($files[0][0])),0,3);
+			if ($oneHost) {
+				$title = str_replace('%h', urldecode($fhost), $title);
+				$title = str_replace('%s', urldecode($fservice), $title);
+			} else {
+				$title = str_replace('%h', implode(',',$hosts), $title);
+				$title = str_replace('%s', urldecode($service), $title);
+			}
+		}
+		$title = escapeshellarg($title);
+		if (strlen($title)) $args[] = "-v$title";
+	}
+
+	if (count($defs)) {
+		$defs = implode(' ', $defs);
+		if (count($args))
+			$argstr = implode(' ', $args);
+		$cmd = "rrdtool graph - -a PNG --start -$time $defs $argstr";
+		$data = `$cmd`;
+		if (strlen($data)>0) {
+			header('Content-Type: image/png');
+			echo $data;
+			exit;
+		} else {
+			echo "failed: $cmd";
+		}
+	}
+}
+
+//outputGraph('better-mousetrap', 'DISK: /', 192000);
Index: /noc/ng/CHANGELOG
===================================================================
--- /noc/ng/CHANGELOG	(revision 42)
+++ /noc/ng/CHANGELOG	(revision 42)
@@ -0,0 +1,51 @@
+0.8.2 2006-04-12
+
+* Fix to match rrd files. Patch from elfrinjo.
+
+0.8.1 2006-04-08
+
+* Db-file-has-a-number bug fixed. Patch from Ton Voon.
+
+0.8 2006-04-05
+
+* Use stylesheet. Contribution from Ton Voon.
+* Splitting graphs is now default. Contribution from Doug Farley.
+
+0.7 2005-10-27
+
+* Another significant performance increase by eval of rules only once
+* Header flush bug fixed
+
+0.6 2005-10-18
+
+* Only using RRD perl modules is supported. Removed binary rrdtool 
+  dependency.
+* Added support for perfdata log file for performance increase.  
+  Contribution from Alex.
+
+0.5 2005-06-22
+
+* Color bug fixed
+* Support for customized heartbeat
+
+0.4 2005-04-22
+
+* Better color handling
+* New webpage design
+* Added rrdopts feature
+* Several bug fixes
+
+0.3 2004-12-04
+
+* Added logging of system errors
+* Added customized graph sizes feature
+* Several bug fixes
+
+0.2 2004-10-14
+
+* Added documentation.
+* Delimiter bug fixed.
+
+0.1 2004-08-03
+
+* Initial release
Index: /noc/ng/INSTALL
===================================================================
--- /noc/ng/INSTALL	(revision 42)
+++ /noc/ng/INSTALL	(revision 42)
@@ -0,0 +1,124 @@
+nagiosgraph Installation
+------------------------
+
+File:    $Id: INSTALL,v 1.14 2006/04/05 12:37:11 sauber Exp $
+Author:  (c) Soren Dossing, 2005
+License: OSI Artistic License
+         http://www.opensource.org/licenses/artistic-license.php
+
+Follow instructions below to install and use nagiosgraph. The
+instructions are for Nagios 2.0b4, and might differ in other versions of
+Nagios.
+
+ - Check required packaged are installed: perl, CGI, nagios and rrdtool
+
+ - Install nagiosgraph.conf, map, insert.pl and show.cgi somewhere, for 
+   example in /usr/local/nagios/nagiosgraph/
+
+ - Edit paths, debug level etc. in nagiosgraph.conf.
+
+ - Check that nagios user can write to rrd dir, and www user can read.
+
+ - Check that nagios and www user can write to log file.
+
+ - In insert.pl and show.cgi edit path to nagiosgraph.conf file.
+
+ - In nagios.cfg set:
+
+     process_performance_data=1
+     service_perfdata_file=/var/spool/nagios/perfdata.log
+     service_perfdata_file_template=$LASTSERVICECHECK$||$HOSTNAME$||$SERVICEDESC$||$SERVICEOUTPUT$||$SERVICEPERFDATA$
+     service_perfdata_file_mode=a
+     service_perfdata_file_processing_interval=30
+     service_perfdata_file_processing_command=process-service-perfdata
+
+   Make sure that service_perfdata_command is not defined.
+
+   Make sure that location of perfdata_file matches definition in 
+   nagiosgraph.conf .
+
+ - In checkcommands.cfg or misccommands.cfg:
+
+     define command {
+       command_name  process-service-perfdata
+       command_line  /usr/local/nagios/nagiosgraph/insert.pl
+     }
+
+   Make sure there is only one definition for process-service-perfdata.
+
+ - Alternative to the two points above: The old style is still possible. 
+   It uses far more CPU but inserts data in rrd files immediately for 
+   every service check.
+
+   In nagios.cfg:
+
+     service_perfdata_command=process-service-perfdata
+
+   Make sure that service_perfdata_file_processing_command is not 
+   defined.
+
+   In misccommands.cfg:
+
+     define command{
+       command_name    process-service-perfdata
+       command_line  /usr/local/nagios/nagiosgraph/insert.pl "$LASTSERVICECHECK$||$HOSTNAME$||$SERVICEDESC$||$SERVICEOUTPUT$||$SERVICEPERFDATA$"
+     }
+
+ - Put an icon approx. 40x40 in .../share/images/logos/ for nagios to link
+   to graphs.
+
+ - Copy nagiosgraph.css to .../nagios/stylesheets/ .
+
+ - In cgi.cfg have:
+
+     xedtemplate_config_file=/usr/local/etc/nagios/serviceextinfo.cfg
+
+ - Edit serviceextinfo.cfg
+
+   Most services can be graphed with no particular configuration like this:
+
+     define serviceextinfo {
+       service_description  DNS
+       hostgroup       servers
+       notes_url       /nagiosgraph/show.cgi?host=$HOSTNAME$&service=$SERVICEDESC$
+       icon_image      graph.gif
+       icon_image_alt  View graphs
+     }
+
+   Instead of hostgroup line a host_name line with all hosts where this
+   type of data are being collected.
+
+   Some type of services have data values that have big differences in the
+   magnitude. In such cases it's good idea to split up into seperate
+   graphs. Here's an example for ntp:
+
+     define serviceextinfo {
+       service_description  NTP
+       host_name       server01,server02,server03,server04
+       notes_url       /nagiosgraph/show.cgi?host=$HOSTNAME$&service=$SERVICEDESC$&db=ntp,jitter,offset&db=ntp,stratum
+       icon_image      graph.gif
+       icon_image_alt  View graphs
+     }
+
+ - Add for example &geom=350x100 to notes_url line for custom sizes of 
+   graphs.
+
+ - Add for example &rrdopts=%2Dl%200%20%2Du%20100 (meaning: 
+   "-l 0 -u 100") to notes_url line for custom Y axis ranges. Any 
+   rrdgraph options can be specified, but has to be url encoded.
+
+ - Configure Apache to point to show.cgi. For example:
+
+     ScriptAlias /nagiosgraph/ /usr/local/nagios/nagiosgraph/
+
+ - To add new service types, edit the map file. This contains regular
+   expression to identify service types, and defines how to store data in 
+   rrd files. Use testentry.pl for testing before inserting in map file.
+
+ - Consider security.
+
+ - Start Nagios. Increase debug level in nagiosgraph.conf if things don't
+   work right away.
+
+ - Keep an eye on the log file. It can grow big. Perhaps rotate it, or
+   decrease log level when everything works fine.
Index: /noc/ng/README
===================================================================
--- /noc/ng/README	(revision 42)
+++ /noc/ng/README	(revision 42)
@@ -0,0 +1,95 @@
+Nagiosgraph
+-----------
+
+File:    $Id: README,v 1.10 2006/04/05 12:37:11 sauber Exp $
+Author:  (c) Soren Dossing, 2005
+License: OSI Artistic License
+         http://www.opensource.org/licenses/artistic-license.php
+
+
+Summary:
+
+Collects perfdata from Nagios check scripts and inserts data into rrd
+files. Data in the rrdfiles can be displayed in html pages with cgi
+script.
+
+
+Note:
+
+Nagios is a registered trademark of Ethan Galstad.
+
+
+Files:
+
+CHANGELOG         - History of changes
+INSTALL           - Dcoument for how to install and use nagiosgraph
+README            - This file
+README.map        - Document for how to create map file entries
+insert.pl         - Reads perfdata log from nagios and insert into rrd 
+                    files.
+show.cgi          - Generates a html page for the host/sevice specified, 
+                    and generates graphs on-the-fly.
+nagiosgraph.conf  - paths and other configuration
+nagiosgraph.css   - CSS stylesheet
+map               - Regular expression to identify services and 
+                    specification for how to create rrd files.
+testcolor.cgi     - Preview of colors for keywords in each color scheme
+testentry.pl      - A script for testing new map file entries.
+
+
+Usage:
+
+Follow the instructions in INSTALL for how to install and configure 
+nagiosgraph.
+
+
+Getting Help:
+
+Discussions related to nagiosgraph are located in a Sourceforge help
+forum; http://sourceforge.net/forum/forum.php?forum_id=394748 . A
+Sourceforge account is necessary for posting.
+
+
+Principles of Operation:
+
+nagiosgraph is basically a simple interface between Nagios and rrd data
+files. Simplicity comes from three factors; it doesn't do much, behavior
+is programmed rather than configurable, and automatically detects new
+data from Nagios.
+
+nagiosgraphs is operating in two modes. One is to collect performance
+data from servicechecks from nagios, and the other is to display graphs
+of the performance data collected.
+
+All the data collected are stored in rrd files by using rrdtools. A file
+called 'map' defines how identify the data from nagios and how to store
+it in the rrd files. Nagios passes all the service data collected to a
+nagiosgraph script called 'insert.pl'. This script will look up in
+'map', which rrd file to insert the data into, and how to name the data.
+
+In Nagios it's also possible to have extended service information pages.
+A nagiosgraph cgi script called 'show.cgi' can be used for such service
+information links. 'show.cgi' will look up in 'map' which performance
+data is stored in rrd files, and display graphs of this data. 
+
+nagiosgraph will automatically detect when new hosts or services has
+been added in Nagios, so generally no configuration of nagiosgraph is
+necessary when configuration of Nagios changes.
+
+nagiosgraph is designed to only require very little configuration.
+Integrating with Nagios is a complicated process nevertheless. The
+'README' file describes how to configure nagiosgraph and integrate with
+Nagios.
+
+The 'map' configuration file is actually perl code, that will be eval'ed
+by 'insert.pl' and 'show.cgi'. Several examples of servicechecks are
+included in the distributed 'map' file, but generally it's necessary to
+make modifications or add entries to match the output of the particular
+nagiosplugins in use. Knowing perl is helpful when making modifications,
+but the examples supplied should cover most types of performance data.
+
+By default all available data for a servicecheck will be displayed in
+the same graph. With extra configuration, embedded in the url, it's
+possible to display less data or to split values into multiple graphs.
+There is also a general method for specifying any rrd graph options.
+
Index: /noc/ng/README.map
===================================================================
--- /noc/ng/README.map	(revision 42)
+++ /noc/ng/README.map	(revision 42)
@@ -0,0 +1,100 @@
+map file
+--------
+
+File:    $Id: README.map,v 1.3 2005/10/08 05:55:08 sauber Exp $
+Author:  (c) Soren Dossing, 2005
+License: OSI Artistic License
+         http://www.opensource.org/licenses/artistic-license.php
+
+This describes how to work with the map file.
+
+The file called 'map' contains regular expressions to identify services 
+and define content in RRD databases. All entries are written in perl, so 
+editing, adding or deleting entries requires some perl programming 
+knowledge. Knowledge of RRD is also necessary.
+
+There has to be one entry for each type of service. The distributed map 
+file already have several examples for cpu, memory, disk, network etc.
+Most examples follow the same schema of identifying data from either 
+Nagios output or Nagios perfdata and defining a number of rrd data 
+sources.
+
+insert.pl is the script receiving data from Nagios. It format data for map 
+file by creating one string consisting of three lines of text. This string 
+might look like this:
+
+  servicedesc:ping
+  output:PING OK - Packet loss = 0%, RTA = 0.00 ms
+  perfdata:
+
+Or like this:
+
+  servicedescr:CPU Load 
+  output:OK - load average: 0.06, 0.12, 0.10
+  perfdata:load1=0;15;30;0 load5=0;10;25;0 load15=0;5;20;0 
+
+perfdata is not always set, so depending on type of service, the most 
+useful data can be in either the output or perfdata line.
+
+For the ping example above, data can be extracted from the output line 
+with a regular expression like this:
+
+  /output:PING.*?(\d+)%.+?([.\d]+)\sms/
+
+In this case, two values are extracted and available in $1 and $2. We can 
+then create a data structure describing the content of the database. The 
+general format is
+
+  [ db-name,
+    [ DS-name, TYPE, DS-value ],
+    [ DS-name, TYPE, DS-value ],
+    ...
+  ]
+
+Where DS-name is the name that will be assigned to a line showing on rrd 
+graphs. TYPE is either GAUGE or DERIVE. the DS value is the data 
+extracted in the regular expression. The DS value can be an expression, 
+for example to normalize to SI units.
+
+Each database definition must be added to the @s array.
+
+So the complete code to define and insert into and rrd database for the 
+PING example above, becomes:
+
+  /output:PING.*?(\d+)%.+?([.\d]+)\sms/
+  and push @s, [ ping,
+                [ losspct, GAUGE, $1      ],
+                [ rta,     GAUGE, $2/1000 ] ];
+
+In this case the database name is called 'ping' and the DS-names stored 
+are losspct and rta. The Nagios output reports round trip time in 
+milliseconds, so the value is multiplied by 1000 to convert to seconds. 
+Both DS type are GAUGE.
+
+Be careful about the database names and DS names. In the code example 
+above the names are barewords, which only works as long as the don't 
+conflict with perl functions or subroutines. For example the word 'sleep' 
+will not work without quoting.
+
+A safer version of the above example is
+
+  /output:PING.*?(\d+)%.+?([.\d]+)\sms/
+  and push @s, [ 'ping',
+                [ 'losspct', 'GAUGE', $1      ],
+                [ 'rta',     'GAUGE', $2/1000 ] ];
+
+After editing map file, the syntax can be checked with
+
+  perl -c map
+
+Again a word of caution. If map file has syntax errors, nothing will be 
+inserted into rrd files until the file is fixed. So don't edit production 
+map files. Instead do something like this:
+
+  cp map map.edit
+  vi map.edit
+  perl -c map.edit
+  mv map.edit map
+
+Share your work. If you have a good map file entry for standard Nagios 
+plugins, then please post it on the forum, or send it to me.
Index: /noc/ng/bin/insert.pl
===================================================================
--- /noc/ng/bin/insert.pl	(revision 42)
+++ /noc/ng/bin/insert.pl	(revision 42)
@@ -0,0 +1,210 @@
+#!/usr/bin/perl
+
+# File:    $Id: insert.pl,v 1.17 2005/10/26 14:42:57 sauber Exp $
+# Author:  (c) Soren Dossing, 2005
+# License: OSI Artistic License
+#          http://www.opensource.org/licenses/artistic-license.php
+
+use strict;
+use RRDs;
+
+# Configuration
+my $configfile = '/home/noc/ng/etc/nagiosgraph.conf';
+
+# Main program - change nothing below
+
+my %Config;
+
+# Read in config file
+#
+sub readconfig {
+  die "config file not found" unless -r $configfile;
+
+  # Read configuration data
+  open FH, $configfile;
+    while (<FH>) {
+      s/\s*#.*//;    # Strip comments
+      /^(\w+)\s*=\s*(.*?)\s*$/ and do {
+        $Config{$1} = $2;
+        debug(5, "INSERT Config $1:$2");
+      };
+    }
+  close FH;
+
+  # Make sure log file can be written to
+  die "Log file $Config{logfile} not writable" unless -w $Config{logfile};
+
+  # Make sure rrddir exist and is writable
+  unless ( -w $Config{rrddir} ) {
+    mkdir $Config{rrddir};
+    die "rrd dir $Config{rrddir} not writable" unless -w $Config{rrddir};
+  }
+}
+
+# Parse performance data from input
+#
+sub parseinput {
+  my $data = shift;
+  #debug(5, "INSERT perfdata: $data");
+  my @d = split( /\|\|/, $data);
+  return ( lastcheck    => $d[0],
+           hostname     => $d[1],
+           servicedescr => $d[2],
+           output       => $d[3],
+           perfdata     => $d[4],
+         );
+}
+
+# Write debug information to log file
+#
+sub debug { 
+  my($l, $text) = @_;
+  if ( $l <= $Config{debug} ) {
+    $l = qw(none critical error warn info debug)[$l];
+    $text =~ s/(\w+)/$1 $l:/;
+    open LOG, ">>$Config{logfile}";
+      print LOG scalar localtime;
+      print LOG " $text\n";
+    close LOG;
+  }
+}
+
+# Dump to log the files read from Nagios
+#
+sub dumpperfdata {
+  my %P = @_;
+  for ( keys %P ) {
+    debug(4, "INSERT Input $_:$P{$_}");
+  }
+}
+
+# URL encode a string
+#
+sub urlencode {
+  $_[0] =~ s/([\W])/"%" . uc(sprintf("%2.2x",ord($1)))/eg;
+  return $_[0];
+}
+
+# Create new rrd databases if necessary
+#
+sub createrrd {
+  my($host,$service,$start,$labels) = @_;
+  my($f,$v,$t,$ds,$db);
+
+  $db = shift @$labels;
+  $f = urlencode("${host}_${service}_${db}") . '.rrd';
+  debug(5, "INSERT Checking $Config{rrddir}/$f");
+  unless ( -e "$Config{rrddir}/$f" ) {
+    $ds = "$Config{rrddir}/$f --start $start";
+    for ( @$labels ) {
+      ($v,$t) = ($_->[0],$_->[1]);
+      my $u = $t eq 'DERIVE' ? '0' : 'U' ;
+      $ds .= " DS:$v:$t:$Config{heartbeat}:$u:U";
+    }
+    $ds .= " RRA:AVERAGE:0.5:1:600";
+    $ds .= " RRA:AVERAGE:0.5:6:700";
+    $ds .= " RRA:AVERAGE:0.5:24:775";
+    $ds .= " RRA:AVERAGE:0.5:288:797";
+
+    my @ds = split /\s+/, $ds;
+    debug(4, "INSERT RRDs::create $ds");
+    RRDs::create(@ds);
+    debug(2, "INSERT RRDs::create ERR " . RRDs::error) if RRDs::error;
+  }
+  return $f;
+}
+
+# Use RRDs to update rrd file
+#
+sub rrdupdate {
+  my($file,$time,$values) = @_;
+  my($ds,$c);
+
+  $ds = "$Config{rrddir}/$file $time";
+  for ( @$values ) {
+    $_->[2] ||= 0;
+    $ds .= ":$_->[2]";
+  }
+
+  my @ds = split /\s+/, $ds;
+  debug(4, "INSERT RRDs::update ". join ' ', @ds);
+  RRDs::update(@ds);
+  debug(2, "INSERT RRDs::update ERR " . RRDs::error) if RRDs::error;
+}
+
+# See if we can recognize any of the data we got
+#
+sub parseperfdata {
+  my %P = @_;
+
+  $_="servicedescr:$P{servicedescr}\noutput:$P{output}\nperfdata:$P{perfdata}";
+  evalrules($_);
+}
+
+# Check that we have some data to work on
+#
+sub inputdata {
+  my @inputlines;
+  if ( $ARGV[0] ) {
+    @inputlines = $ARGV[0];
+  } elsif ( defined $Config{perflog} ) {
+    open PERFLOG, $Config{perflog};
+      @inputlines = <PERFLOG>;
+    close PERFLOG
+  }
+
+  # Quit if there are no data to process
+  unless ( @inputlines ) {
+    debug(4, 'INSERT No inputdata. Exiting.');
+    exit 1;
+  }
+  return @inputlines;
+}
+
+# Process all input performance data
+#
+sub processdata {
+  my @perfdatalines = @_;
+  for my $l ( @perfdatalines ) {
+    debug(5, "INSERT processing perfdata: $l");
+    my %P = parseinput($l);
+    dumpperfdata(%P);
+    my $S = parseperfdata(%P);
+    for my $s ( @$S ) {
+      my $rrd = createrrd($P{hostname}, $P{servicedescr}, $P{lastcheck}-1, $s);
+      rrdupdate($rrd, $P{lastcheck}, $s);
+    }
+  }
+}
+
+### Main loop
+#  - Read config and input
+#  - Update rrd files
+#  - Create them first if necesary.
+
+readconfig();
+debug(5, 'INSERT nagiosgraph spawned');
+my @perfdata = inputdata();
+
+# Read the map file and define a subroutine that parses performance data
+my($rules);
+undef $/;
+open FH, $Config{mapfile};
+  $rules = <FH>;
+close FH;
+$rules = '
+sub evalrules {
+  $_=$_[0];
+  my @s;
+  no strict "subs";
+' . $rules . '
+  use strict "subs";
+  debug(3, "INSERT perfdata not recognized") unless @s;
+  return \@s;
+}';
+undef $@;
+eval $rules;
+debug(2, "INSERT Map file eval error: $@") if $@;
+
+processdata( @perfdata );
+debug(5, 'INSERT nagiosgraph exited');
Index: /noc/ng/cgi-bin/show.cgi
===================================================================
--- /noc/ng/cgi-bin/show.cgi	(revision 42)
+++ /noc/ng/cgi-bin/show.cgi	(revision 42)
@@ -0,0 +1,276 @@
+#!/usr/bin/perl
+
+# File:    $Id: show.cgi,v 1.22 2006/04/12 09:42:16 sauber Exp $
+# Author:  (c) Soren Dossing, 2005
+# License: OSI Artistic License
+#          http://www.opensource.org/licenses/artistic-license.php
+
+use strict;
+use RRDs;
+use CGI qw/:standard/;
+
+# Configuration
+my $configfile = '/home/nagios/ng/etc/nagiosgraph.conf';
+
+# Main program - change nothing below
+
+my %Config;
+
+# Read in configuration data
+#
+sub readconfig {
+  die "config file not found" unless -r $configfile;
+
+  # Read configuration data
+  open FH, $configfile;
+    while (<FH>) {
+      s/\s*#.*//;    # Strip comments
+      /^(\w+)\s*=\s*(.*?)\s*$/ and do {
+        $Config{$1} = $2;
+        debug(5, "CGI Config $1:$2");
+      };
+    }
+  close FH;
+
+  # Make sure log file can be written to
+  unless ( -w $Config{logfile} ) {
+    my $msg = "Log file $Config{logfile} not writable";
+    print header(-type => "text/html", -expires => 0);
+    print p($msg);
+    debug (2, "CGI Config $msg");
+    return undef;
+  }
+
+  # Make sure rrddir is readable
+  unless ( -r $Config{rrddir} ) {
+    my $msg = "rrd dir $Config{rrddir} not readable";
+    print header(-type => "text/html", -expires => 0);
+    print p($msg);
+    debug (2, "CGI Config $msg");
+    return undef;
+  }
+
+  return 1;
+}
+
+# Write debug information to log file
+#
+sub debug {
+  my($l, $text) = @_;
+  if ( $l <= $Config{debug} ) {
+    $l = qw(none critical error warn info debug)[$l];
+    $text =~ s/(\w+)/$1 $l:/;
+    open LOG, ">>$Config{logfile}";
+      print LOG scalar localtime;
+      print LOG " $text\n";
+    close LOG;
+  }
+}
+
+# URL encode a string
+#
+sub urlencode {
+  $_[0] =~ s/([\W])/"%" . uc(sprintf("%2.2x",ord($1)))/eg;
+  return $_[0];
+}
+
+# Get list of matching rrd files
+#
+sub dbfilelist {
+  my($host,$service) = @_;
+  my $hs = urlencode "${host}_${service}";
+  my @rrd;
+  opendir DH, $Config{rrddir};
+    @rrd = grep s/^${hs}_(.+)\.rrd$/$1/, readdir DH;
+  closedir DH;
+  return @rrd;
+}
+
+# Find graphs and values
+#
+sub graphinfo {
+  my($host,$service,@db) = @_;
+  my(@rrd,$ds,$f,$dsout,@values,$hs,%H,%R);
+
+  $hs = urlencode "${host}_${service}";
+
+  debug(5, 'CGI @db=' . join '&', @db);
+
+  # Determine which files to read lines from
+  if ( @db ) {
+    my $n = 0;
+    for my $d ( @db ) {
+      my($db,@lines) = split ',', $d;
+      $rrd[$n]{file} = $hs . urlencode("_$db") . '.rrd';
+      for my $l ( @lines ) {
+        my($line,$unit) = split '~', $l;
+        if ( $unit ) {
+          $rrd[$n]{line}{$line}{unit} = $unit if $unit;
+        } else {
+          $rrd[$n]{line}{$line} = 1;
+        }
+      }
+      $n++;
+    }
+    debug(4, "CGI Specified $hs db files in $Config{rrddir}: "
+           . join ', ', map { $_->{file} } @rrd);
+  } else {
+    @rrd = map {{ file=>$_ }}
+           map { "${hs}_${_}.rrd" }
+           dbfilelist($host,$service);
+    debug(4, "CGI Listing $hs db files in $Config{rrddir}: "
+           . join ', ', map { $_->{file} } @rrd);
+  }
+
+  for $f ( @rrd ) {
+    unless ( $f->{line} ) {
+      $ds = RRDs::info "$Config{rrddir}/$f->{file}";
+      debug(2, "CGI RRDs::info ERR " . RRDs::error) if RRDs::error;
+      map { $f->{line}{$_} = 1}
+      grep {!$H{$_}++}
+      map { /ds\[(.*)\]/; $1 }
+      grep /ds\[(.*)\]/,
+      keys %$ds;
+    }
+    debug(5, "CGI DS $f->{file} lines: "
+           . join ', ', keys %{ $f->{line} } );
+  }
+  return \@rrd;
+}
+
+# Choose a color for service
+#
+sub hashcolor {
+  my$c=$Config{colorscheme};
+  map{
+    $c=(51*$c+ord)%(216)
+  } split//,"$_[0]x";
+  my($i,$n,$m,@h);
+  @h=(51*int$c/36,
+      51*int$c/6%6,
+      51*($c%6));
+#debug(2, "hashcolor $_[0], $c, $h[0]");
+  for$i(0..2){
+	$m=$i if$h[$i]<$h[$m];
+	$n=$i if$h[$i]>$h[$n]
+  }
+  $h[$m]=102 if$h[$m]>102;
+  $h[$n]=153 if$h[$n]<153;
+#debug(2, "hashcolor $_[0]\t$c\t$h[0]\t$h[1]\t$h[2]");
+  #$c=sprintf"%06X",$h[2]+$h[1]*256+$h[0]*16**4;
+  $n = $h[2]+$h[1]*256+$h[0]*16**4;
+  $c=sprintf"%06X",$n;
+#debug(2, "hashcolor $_[0]\t$n\t$c");
+  return $c;
+}
+
+# Generate all the parameters for rrd to produce a graph
+#
+sub rrdline {
+  my($host,$service,$geom,$rrdopts,$G,$time) = @_;
+  my($g,$f,$v,$c,@ds);
+
+  @ds = ('-', '-a', 'PNG', '--start', "-$time");
+  # Identify where to pull data from and what to call it
+  for $g ( @$G ) {
+    $f = $g->{file};
+    debug(5, "CGI file=$f");
+    for $v ( sort keys %{ $g->{line} } ) {
+      $c = hashcolor($v);
+      debug(5, "CGI file=$f line=$v color=$c");
+      my $sv = "$v";
+      push @ds , "DEF:$sv=$Config{rrddir}/$f:$v:AVERAGE"
+               , "LINE2:${sv}#$c:$sv"
+               , "GPRINT:$sv:MAX:Max\\: %6.2lf%s"
+               , "GPRINT:$sv:AVERAGE:Avg\\: %6.2lf%s"
+               , "GPRINT:$sv:MIN:Min\\: %6.2lf%s"
+               , "GPRINT:$sv:LAST:Cur\\: %6.2lf%s\\n";
+    }
+  }
+
+  # Dimensions of graph if geom is specified
+  if ( $geom ) {
+    my($w,$h) = split 'x', $geom;
+    push @ds, '-w', $w, '-h', $h;
+  }
+  # Additional parameters to rrd graph, if specified
+  if ( $rrdopts ) {
+    push @ds, split /\s+/, $rrdopts;
+  }
+  return @ds;
+}
+
+# Write a pretty page with various graphs
+#
+sub page {
+  my($h,$s,$d,$o,@db) = @_;
+
+  # Reencode rrdopts
+  $o = urlencode $o;
+
+  # Detect available db files
+  @db = dbfilelist($h,$s) unless @db;
+  debug(5, "CGI dbfilelist @db");
+
+  # Define graph sizes
+  #   Daily   =  33h =   118800s
+  #   Weekly  =   9d =   777600s
+  #   Monthly =   5w =  3024000s
+  #   Yearly  = 400d = 34560000s
+  my @T=(['dai',118800], ['week',777600], ['month',3024000], ['year',34560000]);
+  print h1("Nagiosgraph");
+  print p("Performance data for ".strong("Host: ").tt($h).' &#183; '.strong("Service: ").tt($s));
+  for my $l ( @T ) {
+    my($p,$t) = ($l->[0],$l->[1]);
+    print h2(ucfirst $p . "ly");
+    if ( @db ) {
+      for my $g ( @db ) {
+        my $arg = join '&', "host=$h", "service=$s", "db=$g", "graph=$t",
+                            "geom=$d", "rrdopts=$o";
+        my @gl = split ',', $g;
+        my $ds = shift @gl;
+        print div({-class => "graphs"}, img( {-src => "?$arg", -alt => "Graph"} ) );
+        print div({-class => "graph_description"}, cite(strong($ds).br().small(join(", ", @gl))));
+      }
+    } else {
+      my $arg = join '&', "host=$h", "service=$s", "graph=$t",
+                          "geom=$d", "rrdopts=$o";
+      print div({-class => "graphs"}, img( {-src => "?$arg", -alt => "Graph"} ) );
+    }
+  }
+}
+
+exit unless readconfig();
+
+# Expect host, service and db input
+my $host = param('host') if param('host');
+my $service = param('service') if param('service');
+my @db = param('db') if param('db');
+my $graph = param('graph') if param('graph');
+my $geom = param('geom') if param('geom');
+my $rrdopts = param('rrdopts') if param('rrdopts');
+
+# Draw a graph or a page
+if ( $graph ) {
+  $| = 1; # Make sure headers arrive before image data
+  print header(-type => "image/png");
+  # Figure out db files and line labels
+  my $G = graphinfo($host,$service,@db);
+  my @ds = rrdline($host,$service,$geom,$rrdopts,$G,$graph);
+  debug(4, "CGI RRDs::graph ". join ' ', @ds);
+  RRDs::graph(@ds);
+  debug(2, "CGI RRDs::graph ERR " . RRDs::error) if RRDs::error;
+  exit;
+} else {
+  my @style;
+  if ($Config{stylesheet}) {
+    @style = ( -style => {-src => "$Config{stylesheet}"} );
+  }
+  print header, start_html(-id=>"nagiosgraph", -title => "nagiosgraph: $host-$service",
+    -meta => { -http_equiv => "Refresh", -content => "300" },
+    @style
+    );
+  page($host,$service,$geom,$rrdopts,@db);
+  print div({-id => "footer"}, hr(), small( "Created by ". a( {-href=>"http://nagiosgraph.sf.net/"}, "nagiosgraph"). "." ));
+  print end_html();
+}
Index: /noc/ng/cgi-bin/testcolor.cgi
===================================================================
--- /noc/ng/cgi-bin/testcolor.cgi	(revision 42)
+++ /noc/ng/cgi-bin/testcolor.cgi	(revision 42)
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+
+# File:    $Id: testcolor.cgi,v 1.2 2005/10/08 05:55:08 sauber Exp $
+# Author:  (c) Soren Dossing, 2005
+# License: OSI Artistic License
+#          http://www.opensource.org/licenses/artistic-license.php
+
+use strict;
+use CGI qw/:standard/;
+
+# Suggest some commonly used keywords
+my $w = param('words') ? join ' ', param('words') : 'response rta pctfree';
+
+# Start each page with an input field
+print <<EOF;
+Content-type: text/html
+
+<html><body>
+<form>
+Type some space seperated nagiosgraph line names here:<br>
+<input name=words size=80 value="$w">
+<input type=submit>
+</form><br>
+EOF
+
+# Render a table of colors of all schemes for each keyword
+if ( param('words') ) {
+  print "<table cellpadding=0><tr><td></td>";
+  print "<th>$_</th>" for 1..8;
+  print "</tr>\n";
+  for my $w ( split /\s+/, param('words') ) {
+    print "<tr><td>$w</td>";
+    for my $c ( 1..8 ) {
+      my $h = hashcolor($w, $c);
+      print "<td><table bgcolor=#000000><tr><td bgcolor=#$h>&nbsp;</td></tr></table></td>";
+    }
+    print "</tr>\n";
+  }
+  print "</table>\n";
+}
+
+# End of page
+print "</body></html>\n";
+
+# Calculate a color for a keyword
+#
+sub hashcolor {
+  my$c=$_[1];map{$c=1+(51*$c+ord)%(216)}split//,$_[0];
+  my($i,$n,$m,@h);@h=(51*int$c/36,51*int$c/6%6,51*($c%6));
+  for$i(0..2){$m=$i if$h[$i]<$h[$m];$n=$i if$h[$i]>$h[$n]}
+  $h[$m]=102if$h[$m]>102;$h[$n]=153if$h[$n]<153;
+  $c=sprintf"%06X",$h[2]+$h[1]*256+$h[0]*16**4;
+  return $c;
+}
+
Index: /noc/ng/cgi-bin/testentry.pl
===================================================================
--- /noc/ng/cgi-bin/testentry.pl	(revision 42)
+++ /noc/ng/cgi-bin/testentry.pl	(revision 42)
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+
+# File:    $Id: testentry.pl,v 1.4 2005/10/08 05:55:08 sauber Exp $
+# Author:  (c) Soren Dossing, 2005
+# License: OSI Artistic License
+#          http://www.opensource.org/licenses/artistic-license.php
+
+# Modify this script to test map entries before inserting into real
+# map file. Run the script and check if the output is as expected.
+
+use strict;
+no strict "subs";
+use Data::Dumper;
+my @s;
+
+# Insert servicesdescr, output and perfdata here as it appears in log file.
+#
+$_ = <<DATA;
+servicedescr:ping
+output:Total RX Bytes: 4058.14 MB, Total TX Bytes: 2697.28 MB<br>Average Traffic: 3.57 kB/s (0.0%) in, 4.92 kB/s (0.0%) out| inUsage=0.0,85,98 outUsage=0.0,85,98
+perfdata:
+DATA
+
+eval {
+
+# Insert here a map entry to parse the nagios plugin data above.
+#
+/output:.*Average Traffic.*?([.\d]+) kB.+?([.\d]+) kB/
+and push @s, [ rxbytes,
+               [ in,  GAUGE, $1 ],
+               [ out, GAUGE, $2 ] ];
+
+};
+
+print Data::Dumper->Dump([\@s], [qw(*s)]);
Index: /noc/ng/etc/httpd-ng.conf
===================================================================
--- /noc/ng/etc/httpd-ng.conf	(revision 42)
+++ /noc/ng/etc/httpd-ng.conf	(revision 42)
@@ -0,0 +1,13 @@
+#ScriptAlias /ng/cgi-bin/ /home/noc/ng/cgi-bin/
+#
+#<Directory /home/noc/ng/cgi-bin/>
+#   Options ExecCGI
+#   SSLRequireSSL
+#</Directory>
+#
+#Alias /ng/ /home/noc/ng/html/
+#
+#<Directory /home/noc/ng/html/>
+#   Options None
+#   SSLRequireSSL
+#</Directory>
Index: /noc/ng/etc/map
===================================================================
--- /noc/ng/etc/map	(revision 42)
+++ /noc/ng/etc/map	(revision 42)
@@ -0,0 +1,192 @@
+# File:    $Id: map,v 1.9 2005/10/08 05:55:08 sauber Exp $
+# Author:  (c) Soren Dossing, 2005
+# License: OSI Artistic License
+#          http://www.opensource.org/licenses/artistic-license.php
+
+########################################################################
+#
+# INSTRUCTIONS:
+#
+# This file contains several example of service types. Edit this file to 
+# add more service types. The data string from Nagios is in $_ . Use 
+# regular expressions to identify and extract data like the examples below
+# below.  Match on either output: or perfdata: . The code is pure perl, 
+# that will be run inside and eval{}. Results are expected in @s. The
+# general format is:
+# 
+# /output|perfdata:<servicetype> <key>=<value> <key2=value2> .../
+# and push @s, [ <databasename>,
+#                [ <key>,  GAUGE|DERIVE, <value>  ],
+#                [ <key2>, GAUGE|DERIVE, <value2> ],
+#                [ .       .              .        ],
+#                [ .       .              .        ] ];
+# 
+# But more advanced code is possible, as long as the resulting 
+# datastructure is correct.
+# 
+########################################################################
+
+# Service type: ping
+#   output:PING OK - Packet loss = 0%, RTA = 0.00 ms
+/output:PING.*?(\d+)%.+?([.\d]+)\sms/
+and push @s, [ "ping",
+               [ "losspct", GAUGE, $1      ],
+               [ "rta",     GAUGE, $2/1000 ] ];
+
+# Service type: single disk
+#  output:DISK OK - free space: /tmp 663 MB (90%):
+/output:DISK.*free space: (\S+) (\d+) MB \((\d+)\%\)/
+and push @s, [ $1,
+               [ "bytesfree", GAUGE, $2*1024**2 ],
+               [ "bytesmax", GAUGE, $3 ? $2*1024**2/$3*100 : 'U' ],
+               [ "pctfree", GAUGE, $3 ] ];
+
+# Service type: all unix-disk
+# Note: nagiosplugin requires the inode patch
+#   ouput:DISK OK - free space: / 12372 mB (77% inode=96%): /raid 882442 mB (88% inode=91%):
+#   perfdata: /=12372mB;14417;15698;96;16019 /raid=882441mB;999780;999780;91;999780
+/output:DISK.*inode=/ and do {
+  my @_pct = /: (\/.*?) .*?(\d+)% inode=(\d+)%/g;
+  while ( my($_d,$_b,$_i) = splice @_pct,0,3 ) {
+    my @_s;
+    /perfdata:.*$_d=(\d+)\w*?;(\d+);(\d+);(\d+);(\d+)/;
+    push @s, [ $_d,
+               [ "free",     GAUGE, $1*1024**2  ],
+               [ "user",     GAUGE, $2*1024**2  ],
+               [ "root",     GAUGE, $3*1024**2  ],
+               [ "max",      GAUGE, $5*1024**2  ],
+               [ "blockpct", GAUGE, $_b ],
+               [ "inodepct", GAUGE, $_i ] ];
+  }
+};
+
+# Service type: unix-dns
+#   output:DNS OK - 0.008 seconds response time (test.test.1M IN A192.169.0.47)
+#   perfdata:time=8260us;;;0
+/output:DNS.*?([.0-9]+) sec/
+and push @s, [ "dns",
+               [ "response",  GAUGE, $1 ] ];
+
+# Service type: unix-imap
+#   output:IMAP OK - 0.009 second response time on port 143
+/output:IMAP.*?([-.0-9]+) sec/
+and push @s, [ "imap",
+               [ "response", GAUGE, $1 ] ];
+
+# Service type: unix-ldap
+#   ouput:LDAP OK - 0.004 seconds response time
+#   perfdata:time=3657us;;;0
+/output:LDAP.*?([.0-9]+) sec/
+and push @s, [ "ldap",
+               [ "response", GAUGE, $1 ] ];
+
+# Service type: unix-load
+#   output: OK - load average: 0.66, 0.70, 0.73
+#   perfdata:load1=0;15;30;0 load5=0;10;25;0 load15=0;5;20;0
+/output:.*load average: ([.0-9]+), ([.0-9]+), ([.0-9]+)/
+and push @s, [ "load",
+               [ "avg1min",  GAUGE, $1 ],
+               [ "avg5min",  GAUGE, $2 ],
+               [ "avg15min", GAUGE, $3 ] ];
+
+# Service type: unix-mailq
+#   output:WARNING: mailq is 5717 (threshold w = 5000)
+#   perfdata:unsent=5717;5000;10000;0
+/perfdata:unsent=(\d+);(\d+);(\d+);(\d+)/
+and push @s, [ "mailq",
+               [ "qsize", GAUGE, $1 ],
+               [ "qwarn", GAUGE, $2 ],
+               [ "qcrit", GAUGE, $3 ] ];
+
+# Service type: unix-netstat
+#   output:OK
+#   perfdata:udpInDatagrams=46517147, udpOutDatagrams=46192507, udpInErrors=0, 
+#   tcpActiveOpens=1451583, tcpPassiveOpens=1076181, tcpAttemptFails=1909, 
+#   tcpEstabResets=5045, tcpCurrEstab=6, tcpOutDataBytes=3162434373, 
+#   tcpInDataBytes=1942718261, tcpRetransBytes=215439
+/perfdata:.*udpInDatagrams=(\d+), udpOutDatagrams=(\d+), udpInErrors=(\d+), tcpActiveOpens=(\d+), tcpPassiveOpens=(\d+), tcpAttemptFails=(\d+), tcpEstabResets=(\d+), tcpCurrEstab=(\d+), tcpOutDataBytes=(\d+), tcpInDataBytes=(\d+), tcpRetransBytes=(\d+)/
+and push @s, [ "udp",
+               [ "InPkts",  DERIVE, int $1/300 ],
+               [ "OutPkts", DERIVE, int $2/300 ],
+               [ "Errors",  DERIVE, int $3/300 ] ],
+             [ "tcp",
+               [ "ActOpens",    DERIVE, int $4/300    ],
+               [ "PsvOpens",    DERIVE, int $5/300    ],
+               [ "AttmptFails", DERIVE, int $6/300    ],
+               [ "OutBytes",    DERIVE, int $9/300*8  ],
+               [ "InBytes",     DERIVE, int $10/300*8 ] ];
+
+# Service type: unix-ntp
+#   output:NTP OK: Offset 0.001083 secs, jitter 14.84 msec, peer is stratum 1
+/output:NTP.*Offset ([-.0-9]+).*jitter ([-.0-9]+).*stratum (\d+)/
+and push @s, [ "ntp",
+               [ "offset",  GAUGE, $1      ],
+               [ "jitter",  GAUGE, $2/1000 ],
+               [ "stratum", GAUGE, $3+1    ] ];
+
+# Service type: unix-pop
+#   output:POP OK - 0.008 second response time on port 110
+/output:POP.*?([.0-9]+) second/
+and push @s, [ "pop3",
+               [ "response", GAUGE, $1 ] ];
+
+# Service type: unix-procs
+#   output:PROCS OK: 43 processes
+/output:PROCS.*?(\d+) processes\n/
+and push @s, [ "procs",
+               [ "procs", GAUGE, $1 ] ];
+
+# Service type: unix-smtp
+#   output:SMTP OK - 0.187 sec. response time
+/output:SMTP.*?([-.0-9]+) sec/
+and push @s, [ "smtp",
+               [ "response", GAUGE, $1 ] ];
+
+# Service type: unix-swap
+#   output:SWAP OK: 96% free (2616 MB out of 2744 MB)
+#   perfdata:swap=2616MB;274;54;0;2744
+/perfdata:swap=(\d+)MB;(\d+);(\d+);\d+;(\d+)/
+and push @s, [ "swap",
+               [ "swapfree", GAUGE, $1*1024**2 ],
+               [ "swapwarn", GAUGE, $2*1024**2 ],
+               [ "swapcrit", GAUGE, $3*1024**2 ],
+               [ "swapmax",  GAUGE, $4*1024**2 ] ];
+
+# Service type: unix-users
+#   output:USERS OK - 4 users currently logged in
+#   perfdata:users=4;5;10;0 
+/perfdata:users=(\d+);(\d+);(\d+)/
+and push @s, [ "procs",
+               [ "users", GAUGE, $1 ],
+               [ "uwarn",  GAUGE, $2 ],
+               [ "ucrit",  GAUGE, $3 ] ];
+
+# Service type: unix-zombies
+#   ouput:PROCS OK: 0 processes with STATE = Z
+/output:PROCS.*?(\d+) processes.*Z/
+and push @s, [ "zombie",
+               [ "zombies", GAUGE, $1 ] ];
+
+# Service type: unix-www
+#   ouput:HTTP OK HTTP/1.1 200 OK - 1456 bytes in 0.003 seconds
+/output:HTTP.*?(\d+) byte.*?([.0-9]+) sec/
+and push @s, [ "http",
+               [ "bps", GAUGE, $1/$2 ] ];
+
+# Service type: unix-tcp
+#   output:TCP OK - 0.061 second response time on port 22
+#   perfdata:time=0.060777s;0.000000;0.000000;0.000000;10.000000
+/output:TCP.*?on port (\d+)\s*perfdata:time=(\d+\.\d+).*(\d+\.\d+)\D*(\d+\.\d+)\D*(\d+\.\d+)\D*(\d+\.\d+)/
+and push @s, [ "tcp_$1",
+               [ 'connect_time',   GAUGE, $2 ],
+               [ 'warning_time',   GAUGE, $3 ],
+               [ 'critical_time',  GAUGE, $4 ],
+               [ 'socket_timeout', GAUGE, $6 ],
+             ];
+
+# Service type: mysql
+#   output: Uptime: 1659115  Threads: 1  Questions: 6424617  Slow queries: 0  Opens: 0  Flush tables: 1  Open tables: 512  Queries per second avg: 3.872 Slave IO: Yes Slave SQL: Yes Seconds Behind Master: 0
+/output:Uptime.*Questions: (\d+).*Queries per second avg: (\d+\.?\d+)/
+and push @s, [ "mysql",
+		[ 'qps', DERIVE, $1 ],
+		[ 'avgqps', GAUGE, $2 ]];
Index: /noc/ng/etc/nagios-ng.cfg
===================================================================
--- /noc/ng/etc/nagios-ng.cfg	(revision 42)
+++ /noc/ng/etc/nagios-ng.cfg	(revision 42)
@@ -0,0 +1,13 @@
+# /etc/nagios/nagios.cfg
+process_performance_data=1
+service_perfdata_file=/home/noc/ng/log/perfdata
+service_perfdata_file_template=$LASTSERVICECHECK$||$HOSTNAME$||$SERVICEDESC$||$SERVICEOUTPUT$||$SERVICEPERFDATA$
+service_perfdata_file_mode=a
+service_perfdata_file_processing_interval=60
+service_perfdata_file_processing_command=ng-service-perfdata
+
+# /etc/nagios/misccommands.cfg
+define command {
+  command_name  ng-service-perfdata
+  command_line  /home/noc/ng/bin/insert.pl
+}
Index: /noc/ng/etc/nagiosgraph.conf
===================================================================
--- /noc/ng/etc/nagiosgraph.conf	(revision 42)
+++ /noc/ng/etc/nagiosgraph.conf	(revision 42)
@@ -0,0 +1,35 @@
+# File:    $Id: nagiosgraph.conf,v 1.8 2006/04/06 10:00:06 sauber Exp $
+# Author:  (c) Soren Dossing, 2005
+# License: OSI Artistic License
+#          http://www.opensource.org/licenses/artistic-license.php
+
+# Debug levels
+# 0 = None
+# 1 = Critical
+# 2 = Error
+# 3 = Warn
+# 4 = Info
+# 5 = Debug
+debug = 2
+
+# Location of debug log file
+logfile = /home/noc/ng/log/debug
+
+# Directory to store rrd database files
+rrddir =  /home/noc/ng/rrd
+
+# File containing regular expressions to identify service and perf data
+mapfile = /home/noc/ng/etc/map
+
+# Color scheme for graphs. Choose a number between 1 and 8.
+colorscheme = 1
+
+# Heartbeat. In seconds, twice the size of servicecheck intervals
+#heartbeat = 600
+heartbeat = 60
+
+# Location of performance data log file. Comment out it not used.
+perflog = /home/noc/ng/log/perfdata
+
+# Stylesheet - added to head of show.cgi. Comment out if not used
+stylesheet = /ng/nagiosgraph.css
Index: /noc/ng/html/nagiosgraph.css
===================================================================
--- /noc/ng/html/nagiosgraph.css	(revision 42)
+++ /noc/ng/html/nagiosgraph.css	(revision 42)
@@ -0,0 +1,27 @@
+body#nagiosgraph {
+	color: #000000;
+	background-color: #BBBBFF;
+}
+h1 {
+	font-size: x-large;
+}
+div.graphs, div#footer {
+	clear: both;
+}
+h2 {
+	font-size: large;
+	padding-top: 1em;
+	margin-bottom: 0.5em;
+	clear: left;
+}
+img {
+	float: left;
+	margin-left: 15px;
+	margin-bottom: 1em;
+	padding-right: 10px;
+}
+div.graph_description {
+	/* to valign the text */
+	margin-top: 0;
+	padding-top: 70px;
+}
