#! /usr/bin/perl -w

##**************************************************************
##
## Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
## University of Wisconsin-Madison, WI.
## 
## Licensed under the Apache License, Version 2.0 (the "License"); you
## may not use this file except in compliance with the License.  You may
## obtain a copy of the License at
## 
##    http://www.apache.org/licenses/LICENSE-2.0
## 
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
##**************************************************************


use strict;
$| = 1;

# Prototypes
sub Main( );
sub PromptUser( $$ );
sub CreateDir( $ );
sub DoConfig( $$$ );
sub DoPrefs( $ );
sub Lookup( $$ );

# Directories
my $Usage = "$0 tarball TargetDir";
my $TargetDir = "/home/hawkeye";
my $TarBall = "";
my $DataDir = ".";
my $HostallowWriteLine = 0;
my $HostallowWriteValue = "";

# Check command line..
$TarBall = shift( @ARGV ) if ( $#ARGV >= 0 );
$TargetDir = shift( @ARGV ) if ( $#ARGV >= 0 );
my %Daemons;

# Make sure we have a tarball..
die "$Usage" if ( $TarBall eq "" );

# Try to find the tarball
if ( ! -f $TarBall )
{
    my @Path = split( /\/+/, $0 );
    pop @Path if ( $#Path >= 0 );
    push( @Path, "." ) if ( $#Path < 0 );
    $DataDir = join( "/", @Path );
    my $TmpTarBall = $DataDir . "/" . $TarBall;
    die "Tarball '$TarBall' does not exist" if ( ! -f $TmpTarBall );
    $TarBall = $TmpTarBall;
}
print "Using tarball '$TarBall'\n";

$| = 1;
Main( );

# Main logic...
sub Main( )
{
    # Figure out where to install
    while ( 1 )
    {
	$TargetDir = PromptUser( "Directory to install Hawkeye", $TargetDir );
	last if ( -d $TargetDir );
	print "$TargetDir does not exist.  Create it? ";
	$_ = <>; chomp;
	next if ( ! /^y/i );
	next if CreateDir( $TargetDir );

	# Wow.  All ok.  Done here
	last;
    }

    # Get the absolute path to it
    $TargetDir = ( `cd $TargetDir; pwd` ); chomp $TargetDir;
    die "$TargetDir is not a valid directory!" if ( ! -d $TargetDir );

    # Get my hostname..
    print "\n";
    my $MyHostname = `/bin/hostname`; chomp $MyHostname;
    $MyHostname = Lookup( "this system", $MyHostname );

    # Ok.  We know where to install it now...
    print "Using my hostname = '$MyHostname'\n";

    # Get the Central Manager host
    print "\nEach Hawkeye pool needs ONE central manager.\n";
    print "Is this host the central manager <n>? "; $_ = <>; chomp $_;
    my $Collector = ( /^y/i ) ? $MyHostname : "";
    $Daemons{Collector} = Lookup( "Central Manager", $Collector );

    # Extract the tarballs
    print "\n\nExtracting tarballs into '$TargetDir'...\n";
    system( "tar -C $TargetDir -xvf $TarBall" );
    system( "tar --gzip -C $TargetDir -xvf hcav.tgz" ) if ( -f "hcav.tgz" );
    system( "cp $DataDir/README* $TargetDir" );

    # Create extra directories...
    die if CreateDir( "$TargetDir/etc" );
    die if CreateDir( "$TargetDir/spool" );
    die if CreateDir( "$TargetDir/execute" );
    die if CreateDir( "$TargetDir/log" );

    # Build the daemon list
    $Daemons{List} = "MASTER, STARTD";
    $Daemons{List} = "$Daemons{List}, COLLECTOR"
	if ( $Daemons{Collector} eq $MyHostname );

    # Build up the new config...
    my $ConfigIn = "$TargetDir/install/hawkeye_config.example";
    my $ConfigOut = "$TargetDir/etc/hawkeye_config";
    DoConfig( $ConfigIn, $ConfigOut, $MyHostname );

    # Build up the new config...
    my $LocalIn = "$TargetDir/install/hawkeye_config.local.example";
    my $LocalOut = "$TargetDir/etc/hawkeye_config.local";
    DoConfig( $LocalIn, $LocalOut, $MyHostname );

    # Fix up the ClassAd viewer 'prefs'
    my $Prefs = "$TargetDir/condor/classadView/prefs";
    DoPrefs( $Prefs ) if ( -f $Prefs );

    # Various paths
    my $PathBin = "$TargetDir/bin";
    my $PathSbin = "$TargetDir/sbin";

    # Create a simple script to run Hawkeye for the user
    {
	print "\nCreating setup / start scripts...\n";

	my $Script = "$TargetDir/StartHawkeye";
	if ( open( OUT, ">$Script" ) )
	{
	    print OUT "#! /bin/sh\n";
	    print OUT "HAWKEYE_CONFIG=\"$ConfigOut\"\n";
	    print OUT "export HAWKEYE_CONFIG\n";
	    print OUT "PATH=\"$PathBin:$PathSbin:\$PATH\"\n";
	    print OUT "export PATH\n";
	    print OUT "$TargetDir/sbin/hawkeye_master\n";
	    close( OUT );
	    chmod( 0750, $Script);
	    print "'$Script' has been created to start Hawkeye for you\n";
	}

	$Script = "$TargetDir/SetHawkeye.sh";
	if ( open( OUT, ">$Script" ) )
	{
	    print OUT "HAWKEYE_CONFIG=\"$ConfigOut\"\n";
	    print OUT "export HAWKEYE_CONFIG\n";
	    print OUT "PATH=\"$PathBin:$PathSbin:\$PATH\"\n";
	    print OUT "export PATH\n";
	    close( OUT );
	    chmod( 0750, $Script);
	    print "'$Script' has been created to set Hawkeye for you (sh version)\n";
	}

	$Script = "$TargetDir/SetHawkeye.csh";
	if ( open( OUT, ">$Script" ) )
	{
	    print OUT "setenv HAWKEYE_CONFIG \"$ConfigOut\"\n";
	    print OUT "setenv PATH \"$PathBin\":\"$PathSbin\":\"\$PATH\"\n";
	    close( OUT );
	    chmod( 0750, $Script);
	    print "'$Script' has been created to set Hawkeye for you (csh version)\n";
	}
    }

    # Create the "real" add module script
    my $InstallModulePerl = "$TargetDir/sbin/hawkeye_install_module.pl";
    my $InstallModule = "$TargetDir/sbin/hawkeye_install_module";
    if ( open( OUT, ">$InstallModule" ) )
    {
	print OUT "#! /bin/sh\n";
	print OUT "HAWKEYE_ROOT_DIR=\"$TargetDir\"\n";
	print OUT "export HAWKEYE_ROOT_DIR\n";
	print OUT "$InstallModulePerl \$*\n";
	close OUT;
	chmod( 0755, $InstallModule );
    }

    # Output to user
    print "\n";
    {
	my $Tmp = $PathBin;
	$Tmp =~ s/\//\\\//g;
	print "You should add '$PathBin' to your PATH\n"
	    if ( ! ( $ENV{PATH} =~ /$Tmp/ ) );
	$Tmp = $PathSbin;
	$Tmp =~ s/\//\\\//g;
	print "You should add '$PathSbin' to your PATH\n"
	    if ( ! ( $ENV{PATH} =~ /$Tmp/ ) );
	print "You should set HAWKEYE_CONFIG=$ConfigOut\n"
	    if (  ( ! exists $ENV{HAWKEYE_CONFIG} ) ||
		  ( $ENV{HAWKEYE_CONFIG} ne $ConfigOut )  );
	print "You should review the setting of the HOSTALLOW_WRITE" .
	    " parameter of your configuration\n".
	    " $ConfigOut line $HostallowWriteLine\n" .
	    " Current value is \"$HostallowWriteValue\"\n";
    }
}


# ###############################################
# Build a custom config file for the user
# ###############################################
sub DoConfig( $$$ )
{
    my $ConfigIn = shift;
    my $ConfigOut = shift;
    my $Hostname = shift;

    # Should we overwrite an existing config?
    if ( -f $ConfigOut )
    {
	print "Overwrite $ConfigOut <n>? ";
	$_ = <>;
	if ( ! /^y/i )
	{
	    return;
	}
    }

    # Open 'em both
    open( IN, $ConfigIn ) || die "Can't open '$ConfigIn' for reading\n";
    open( OUT, ">$ConfigOut" ) || die "Can't open '$ConfigOut' for writing\n";
    my @Modules;
  READ:
    while ( <IN> )
    {
	chomp;
	my $Line = $_;

	# Hawkeye base dir
	if ( /^HAWKEYE\s+=(.*) / )
	{
	    print OUT "HAWKEYE\t\t=$TargetDir\n";
	    next;
	}

	# Central manager
	elsif ( /^HAWKEYE_HOST\s+=/ )
	{
	    print OUT "HAWKEYE_HOST = $Daemons{Collector}\n";
	    next;
	}

	# Daemon list
	elsif ( /^DAEMON_LIST\s+=/ )
	{
	    print OUT "DAEMON_LIST = $Daemons{List}\n";
	    next;
	}

	# HOSTALLOW_WRITE
	elsif ( /^HOSTALLOW_WRITE\s+=/ )
	{
	    my $Domain = $Hostname;
	    $Domain =~ s/^[^\.]*/\*/;
	    print OUT "HOSTALLOW_WRITE = $Domain\n";
	    $HostallowWriteLine = $.;
	    $HostallowWriteValue = $Domain;
	    next;
	}

	# Actual cronjob list
	elsif ( /^HAWKEYE_JOBLIST\s+=/ )
	{
	    if ( $#Modules < 0 )
	    {
		print OUT "$Line\n";
	    }
	    else
	    {
		print OUT "$Line \\\n";
	    }

	    for my $i ( 0 .. $#Modules )
	    {
		my $Module = $Modules[$i];
		my $ModName = $Module->{Name};
		my $Prefix = $Module->{Prefix};
		my $Path = $Module->{Path};
		my $Period = $Module->{Period};
		my $Opt = $Module->{Options};
		my $Cont = ( $i == $#Modules ) ? "" : "\\";
		my $Start = "HAWKEYE_" . $ModName;
		print OUT "\t$ModName $Cont\n";
		print OUT $Start . "_PREFIX = " . $Module->{Prefix};
		print OUT $Start . "_EXECUTABLE = " . $Module->{Path};
		print OUT $Start . "_MODE = " . $Module->{Mode};
		print OUT $Start . "_RECONFIG = " . $Module->{Reconfig};
		print OUT $Start . "_PERIOD = " . $Module->{Period};
	    }
	    next;
	}

	# Module definition
	elsif ( /^\#.*MODULE\s+(\w+):(\w+):(\w+):(\w+)(.*)/ )
	{
	    print OUT "$Line\n";
	    my $ModName = $1;
	    my $ModPre = $2;
	    my $ModPath = $3;
	    my $ModTime = $4;
	    my $ModOpt = ( defined( $5 )  ?  $5  :  "");
	    my @ModDesc;
	    while( <IN> )
	    {
		chomp;
		$Line = $_;

		# Read the module description line(s)
		if ( /^[\#]+\+\s+(.*)/ )
		{
		    push @ModDesc, $1;
		    print OUT "$Line\n";
		    next;
		}

		# End this module def?
		print "Module: '$ModName'\n";
		foreach my $Desc ( @ModDesc )
		{
		    print "  $Desc\n";
		}
		while ( 1 )
		{
		    print "  Do you want to run $ModName (y/n)? ";
		    $_ = <>;
		    if ( /^n/i )
		    {
			$_ = $Line;
			redo READ;
		    }
		    last if ( /^y/i );
		    print "  Please answer yes or no\n";
		}

		# How often to run it?
		if ( ! ( $ModOpt =~ /continuous/i ) )
		{
		    while( 1 )
		    {
			print "  At what period should this module run " .
			    "(s/m/h modifiers ok) <$ModTime>? ";
			$_ = <>; chomp;
			last if ( $_ eq "" );
			if ( /^(\d+[sSmMhH]?)$/)
			{
			    $ModTime = $1;
			    last;
			}
			print "  '$_' is invalid.  Please enter a number ".
			    "optionally followed by an s, an m, or a h\n";
		    }
		}
		else
		{
		    print "   $ModName is runs in 'continuous' mode, ".
			"so no period is defined here.\n";
		}

		# Define the module...
		my $r = ();
		$r->{Name} = $ModName;
		$r->{Prefix} = $ModPre;
		$r->{Path} = "$TargetDir/modules/$ModPath";
		$r->{Period} = $ModTime;
		$r->{Options} = $ModOpt;
		push @Modules, $r;
		$_ = $Line;
		redo READ;
	    }
	}
	print OUT "$Line\n";
    }
    close( IN );
    close( OUT );
}

# ###################################
# Lookup a FQDN
# ###################################
sub Lookup( $$ )
{
    my $Title = shift;
    my $Hostname = shift;

    while( 1 )
    {
	my @FQDN = split( /\./, $Hostname );
	if ( $#FQDN == 0 )
	{
	    # Run nslookup on it..
	    my $Cmd = "nslookup $Hostname";
	    my $FQDN = "";
	    if ( open( NSLOOKUP, "$Cmd 2>&1 |" ) )
	    {
		while( <NSLOOKUP> )
		{
		    if ( /Name:\s*(\S+)/ )
		    {
			$FQDN = $1;
		    }
		}
		close( NSLOOKUP );
	    }

	    # Did we find it?
	    if ( $FQDN ne "" )
	    {
		print "$Title '$Hostname' resolves to '$FQDN'; is this correct <y>? ";
		$_ = <>; chomp;
		if ( /^n/i )
		{
		    $Hostname = "";
		    next;
		}
		return $FQDN;
	    }
	    else
	    {
		print "Unable to resolve '$Hostname'\n";
		print "Please enter FQDN of $Title: ";
		$Hostname = <>; chomp $Hostname;
	    }
	}

	# No hostname
	elsif ( $#FQDN < 0 )
	{
	    print "Please enter the host name of $Title: ";
	    $Hostname = <>; chomp $Hostname;
	}
	# FQDN looks valid
	else
	{
	    print "I've resolve the FQDN of $Title to '$Hostname'; is this correct <y>? ";
	    $_ = <>; chomp;
	    if ( /^n/i )
	    {
		$Hostname = "";
		next;
	    }
	    return $Hostname;
	}
    }
} # Lookup()


# ###################################
# Customize the ClassAd viewer prefs
# ###################################
sub DoPrefs( $ )
{
    my $File = shift;
    my @Prefs;
    open( IN, $File ) || die "Can't open view prefs '$File'\n";
    while ( <IN> )
    {
	chomp;
	if ( /(.*)\.collectorMachine\s+=/ )
	{
	    $_ = "$1.collectorMachine = $Daemons{Collector}";
	}
	push @Prefs, $_;
    }
    close( IN );

    open( OUT, ">$File" ) || die "Can't write to view prefs '$File'\n";
    foreach my $Pref ( @Prefs )
    {
	print OUT "$Pref\n";
    }
    close( OUT );
}


# ################################
# Prompt the user & get a reply
# ################################
sub PromptUser( $$ )
{
    my $Prompt = shift;
    my $Default = shift;
    print "$Prompt <$Default> ";
    my $Response = <>; chomp $Response;
    $Response = $Default if ( $Response eq "" );
    $Response = `echo $Response`; chomp $Response;
    return $Response;
}

# ############################################################
# Create a directory if it doesn't exist, check for errors
# ############################################################
sub CreateDir( $ )
{
    my $Dir = shift;
    return 0 if ( -d $Dir );
    if ( ! mkdir( $Dir, 0755 ) )
    {
	print STDERR "Can't create $Dir\n";
	return 1;
    }
    return 0;
}
