#!/usr/bin/perl -w
# vim:ts=4
# Set up all the appropriate files and copy in Nagios config data

use strict;
use Text::ParseWords;
use DBI;
use Getopt::Std;
use Nagios::Config;
use Nagios::Object;
use vars qw/$opt_c $opt_h $opt_d $opt_C $opt_q $opt_D $opt_U $opt_P $opt_H $opt_p $opt_S $opt_T $opt_X $opt_t $opt_V/;

my($DBHOST) = "nagios.auckland.ac.nz"; # database server
my($DBUSER) = "xxxxxxxxxx"; # database user 
                            # (must have ins/del/upd/create/drop privs)
my($DBPASS) = "xxxxxxxxxx"; # database password
my($DBPORT) = 3306;    # database port (mysql is normally 3306)
my($DBNAME) = "nagios"; # database name
my($PREFIX) = "nagios"; # table name prefix
my($dbh,$sth);
my($nagios);
my($CFGFILE) = "/u02/nagios/etc/nagios.cfg";
my($HOSTEXTINFO) = "/u02/nagios/etc/hostextinfo.cfg";
my($VERSION) = 2.0; # or 1.2 or 2.0.  Can usually be 2.0, even for nagios 1.2

# Database structure
my(%struct) = ( 
Timeperiod => { 
	timeperiod_name=>"varchar(32) not null default ''",
	alias=>"varchar(64) not null default ''",
	sunday=>"varchar(32) default null",
	monday=>"varchar(32) default null",
	tuesday=>"varchar(32) default null",
	wednesday=>"varchar(32) default null",
	thursday=>"varchar(32) default null",
	friday=>"varchar(32) default null",
	saturday=>"varchar(32) default null"
}, Host => { # incorporates hostextinfo
	host_name=>"varchar(32) not null default ''",
	alias=>"varchar(64) not null default ''",
	address=>"varchar(32) not null default ''",
	parents=>"varchar(64) default null",
	check_command=>"varchar(32) default null",
	max_check_attempts=>"tinyint(2) default 1",
	checks_enabled=>"enum('0','1') default null",
	event_handler=>"varchar(32) default null",
	event_handler_enabled=>"enum('0','1') default null",
	high_flap_threshold=>"int(8) default null",
	low_flap_threshold=>"int(8) default null",
	flap_detection_enabled=>"enum('0','1') default null",
	process_perf_data=>"enum('0','1') default null",
	retain_status_information=>"enum('0','1') default null",
	retain_nonstatus_information=>"enum('0','1') default null",
	notification_interval=>"int(4) default 240",
	notification_period=>"varchar(32) default null",
	notification_options=>"varchar(16) default null",
	notifications_enabled=>"enum('0','1') default null",
	stalking_options=>"varchar(16) default null",
	notes_url=>"varchar(64) default null",
	icon_image=>"varchar(32) default null",
	icon_image_alt=>"varchar(32) default null",
	vrml_image=>"varchar(32) default null",
	statusmap_image=>"varchar(32) default null",
	'2d_coords'=>"varchar(16) default null",
	'3d_coords'=>"varchar(32) default null",
	x2d=>"mediumint(9) default null",  # separated out for use by other apps
	y2d=>"mediumint(9) default null",
	x3d=>"mediumint(9) default null",
	y3d=>"mediumint(9) default null",
	z3d=>"mediumint(9) default null",
	hide=>"enum('0','1') default null"
}, HostGroup => { 
	hostgroup_name=>"varchar(32) not null default ''",
	alias=>"varchar(64) not null default ''",
	contact_groups=>"varchar(128) not null default ''", # should b separate tbl
	members=>"text(2048) not null default ''" # should be separate table
}, Service => { 
	host_name=>"varchar(32) not null default ''",
	service_description=>"varchar(64) not null default ''",
	is_volatile=>"enum('0','1') default null",
	check_command=>"varchar(32) default null",
	max_check_attempts=>"tinyint(2) default 1",
	normal_check_interval=>"int(4) default 5",
	retry_check_interval=>"int(4) default 5",
	active_checks_enabled=>"enum('0','1') default null",
	passive_checks_enabled=>"enum('0','1') default null",
	check_period=>"varchar(32)  default null",
	parallelize_check=>"enum('0','1') default null",
	obsess_over_service=>"enum('0','1') default null",
	check_freshness=>"enum('0','1') default null",
	freshness_threshold=>"int(4)  default null",
	event_handler=>"varchar(32) default null",
	event_handler_enabled=>"enum('0','1') default null",
	high_flap_threshold=>"int(8) default null",
	low_flap_threshold=>"int(8) default null",
	flap_detection_enabled=>"enum('0','1') default null",
	process_perf_data=>"enum('0','1') default null",
	retain_status_information=>"enum('0','1') default null",
	retain_nonstatus_information=>"enum('0','1') default null",
	notification_interval=>"int(4) default 240",
	notification_period=>"varchar(32) default null",
	notification_options=>"varchar(16) default null",
	notifications_enabled=>"enum('0','1') default null",
	stalking_options=>"varchar(16) null default null",
	contact_groups=>"varchar(128) default null" # shd b separate tbl
}, Contact => { 
	contact_name=>"varchar(32) not null default ''",
	alias=>"varchar(64) not null default ''",
	host_notification_period=>"varchar(32)  default null",
	service_notification_period=>"varchar(32)  default null",
	host_notification_options=>"varchar(16) default null",
	service_notification_options=>"varchar(16) default null",
	host_notification_commands=>"varchar(64) default null",
	service_notification_commands=>"varchar(64) default null",
	email=>"varchar(64) default null",
	pager=>"varchar(64) default null"
}, ContactGroup => { 
	contactgroup_name=>"varchar(32) not null default ''",
	alias=>"varchar(64) not null default ''",
	members=>"text(1024) not null default ''" # should be separate table
});
my(%primarykey) = (
Timeperiod => [ 'timeperiod_name' 
], Host => [ 'host_name'
], HostGroup => [  'hostgroup_name'
], Service => [  'host_name', 'service_description'
], Contact => [  'contact_name'
], ContactGroup => [ 'contactgroup_name'
]);

my($table,$i,$t);
my(%sh);
my($usth);
#############################################################################
# param might be a string, or an array, or an array of objects, or an object
sub clean($) {
	my($o) = $_[0];
	my($rv) = "";

	return $o if(!ref $o); # straight string
	if( ref $o eq 'ARRAY' ) {
		foreach ( @$o ) {
			$rv .= ',' if($rv);
			$rv .= &clean($_);
		}
		return $rv;
	}
	return $o->name;
}
sub parse($) {
	my($f) = $_[0];
	my($o) = undef;
	open HXI,"<$f";
	while( <HXI> ) {
		chomp;
		if( /^\s*define\s+hostextinfo\s*{/ ) { $o = undef; next; }
		if( /^\s*}/ ) { $o = undef; next; }
		if( /^\s*host_name\s+(\S+)/ ) {
			$o = $nagios->find_object($1,"Nagios::Host"); next; 
		}
		next if(!$o);
		if( /^\s*(\S+)\s+(\S.*)/ ) { $o->{$1} = $2; next; }
	}
	close HXI;
}
sub create_tables {
	my($sql,$table,$fld);

	if($opt_X) {
		print "Dropping any existing tables\n";
		foreach $table ( keys %struct ) {
			$sql = "drop table $PREFIX".(lc $table);
			$dbh->do($sql);
		}
	}

	print "Creating Nagios database tables ".localtime()."\n";

	foreach my $table ( keys %struct ) {
		print "$table ";
		$sql = "CREATE TABLE `$PREFIX".(lc $table)."` ( \n";
		foreach $fld ( keys %{$struct{$table}} ) {
			$sql .= " `$fld` ".$struct{$table}{$fld}.", \n";
		}
		$sql .= "PRIMARY KEY (`".(join "`,`",@{$primarykey{$table}})."`)\n";
		$sql .= " )\n";
		$dbh->do($sql);
		if($dbh->err) {
			print "\n".$dbh->errstr()."\n";
		}
	}
	print "DONE\n";
	
}
#############################################################################
sub do_help {
	print "Usage: loadconfig [-h][-d][-q][-T [-X]]\n                [-D dbname][-U username -P password][-H dbhost -p dbport]\n                -c nagios.cfg [-V version]\n";
	print " -X: Drop tables first(DANGER!)\n -T: create tables in database\n -d: debug mode\n -q: quiet mode - no progress counters\n -V: Give nagios version (1.2, 2.0)\n";
}

####################################################################
# These functions write a Nagios object to the database tables.
sub write_object($$) {
	my($t,$o) = @_;
	my($sql,$sth,@f,@a);

	return if(!$o->register);
	foreach ( @{$primarykey{$t}} ) { return if(!$o->{$_}); }

	@f = keys %{$struct{$t}};
	if(!$sh{$t}) {
		$sql = "insert into ".$PREFIX.(lc $t)."( ".(join ",",@f)
			." ) values ( ".('?,'x$#f)."?)";
		$sh{$t} = $dbh->prepare($sql);
	}
	$sth = $sh{$t};
	foreach ( @f ) {
		if(! $o->has_attribute($_) ) {
			push @a, undef;
#		} elsif( $o->attribute_is_list($_) and (ref $o->$_())) {
#			push @a, ( join ",", @{$o->$_()} );
		} else {
			push @a,clean($o->$_());
		}
	}
	$sth->execute(@a);
	if($sth->err) {
		print "\r".$sth->errstr()."\n";
	}
}
sub update_host($) {
	my($o) = $_[0];
	my($hostname, $xx, $yy, $xxx, $yyy, $zzz) 
		= ('',undef,undef,undef,undef,undef);
	my($sql);
	
	if(!$usth) {
		$usth = $dbh->prepare("update `".$PREFIX."hosts` set `notes_url` = ?, `icon_image` = ? , `icon_image_alt` = ?, `vrml_image` = ?, `statusmap_image` = ? , `2d_coords` = ?, `3d_coords` = ?, `x2d` = ?, `y2d` = ?, `x3d` = ?, `y3d` = ?, `z3d` = ? where host_name = ?");
	}
	
	$hostname = $o->host_name;
	return if(!$hostname);
	if( $o->has_attribute('3d_coords')  and $o->{'3d_coords'}
		and $o->{'3d_coords'} =~ /(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/ ) {
		($xxx,$yyy,$zzz) = ($1,$2,$3);
	}
	if( $o->has_attribute('2d_coords')  and $o->{'2d_coords'}
		and $o->{'2d_coords'} =~ /(\d+)\s*,\s*(\d+)/ ) {
		($xx,$yy) = ($1,$2);
	}
	$usth->execute( 
		($o->has_attribute('notes_url')?$o->notes_url:undef), 
		($o->has_attribute('icon_image')?$o->icon_image:undef), 
		($o->has_attribute('icon_image_alt')?$o->icon_image_alt:undef), 
		($o->has_attribute('vrml_image')?$o->vrml_image:undef), 
		($o->has_attribute('statusmap_image')?$o->statusmap_image:undef), 
		($o->has_attribute('2d_coords')?$o->{'2d_coords'}:undef), 
		($o->has_attribute('3d_coords')?$o->{'3d_coords'}:undef), 	
		$xx,$yy,$xxx,$yyy,$zzz, $hostname );
	
}
####################################################################
# MAIN CODE
$|=1;
getopts('c:dqhTH:U:P:p:D:XV:');

if($opt_h) { do_help(); exit 0; }
$DBHOST = $opt_H if($opt_H);
$DBUSER = $opt_U if($opt_U);
$DBPASS = $opt_P if($opt_P);
$DBPORT = $opt_p if($opt_p);
$DBNAME = $opt_D if($opt_D);
$VERSION = $opt_V if($opt_V);

print "Starting processing ".localtime()."\n";

print "Connecting to database ".localtime()."\n";
$dbh = DBI->connect("DBI:mysql:database=$DBNAME;host=$DBHOST;port=$DBPORT", $DBUSER, $DBPASS, { PrintError=>0, RaiseError=>0 }) or do {
	print "Error connecting to database: $!\n";
	exit 1;
};


print "Parsing Nagios configuration files ".localtime()."...\n";
$nagios = Nagios::Config->new( Filename=>($opt_c?$opt_c:$CFGFILE), Version=>$VERSION );
if(!$nagios) {
	print "Unable to parse Nagion configuration files.\n";
	exit 1;
}
print "Parsing Nagios hostextinfo ".localtime()."...\n";
if($VERSION < 2) {
	parse($HOSTEXTINFO); # special function for nagios 1.2
} else {
	$nagios->parse($HOSTEXTINFO);
}
print "... read in configuration at ".localtime()."\n";
print "Starting template processing ".localtime()."\n";
$nagios->resolve_objects(); # do it twice because of multiple inheritance
$nagios->resolve_objects();
$nagios->register_objects();
print "Templates done ".localtime()."\n";

create_tables() if($opt_T);

foreach $table ( keys %struct ) {
	print "Loading $table entries\n" if($opt_q);
	print "Writing $table entries (     0)" if(!$opt_q); $t = time; $i = 0;
#	foreach ( $nagios->all_objects_for_type("Nagios::$table") ){ 
	foreach ( $nagios->_list(lc $table) ){ 
		if( (time - $t)>5 and !$opt_q) { 
			printf "\b\b\b\b\b\b\b%6d)",$i; $t = time; }
		$nagios->resolve($_) if($_->{use});
		next if(!$_->register);
		write_object($table,$_);
		$i++;
	}
	printf "\b\b\b\b\b\b\b%6d) COMPLETE\n",$i if(!$opt_q); 
}
print "Adding hostextinfo details to hosts\n" if($opt_q);
print "Adding hostextinfo details to hosts... (     0)" if(!$opt_q);
$t = time; $i = 0;
foreach ( $nagios->_list('hostextinfo') ) {
	if( (time - $t)>5 and !$opt_q) { 
		printf "\b\b\b\b\b\b\b%6d)",$i; $t = time; }
	$i++;
	update_host($_);
}
printf "\b\b\b\b\b\b\b%6d) COMPLETE\n",$i if(!$opt_q); 

print "Actions complete ".localtime()."\n";
exit 0;
