#! /usr/bin/env perl
##**************************************************************
##
## 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.
##
##**************************************************************
##
## This test check availability and usage contol of the new
## local, resources in both a static configuration and in psolts.
## It is model after what we did to test concurrency limits
## as we are checking the same sorts of things. bt
##
##**************************************************************

use CondorTest;
use CondorUtils;
use Check::SimpleJob;
use Check::CondorLog;
#use Time::ParseDate;
use Time::Local;
use Cwd;

my $test_name = "job_test_log_rotaion_multiwriters";
CondorTest::RunTest($test_name);

my $killconfig = $ARGV[0];
my $localconfig = "condor_config.local";
my $localconfigsave = "condor_config.local.save";
my $configlocation = "";
my $top = getcwd();

if(defined $killconfig) {
	print "Test running in found configuration.........\n";
	system("condor_config_val -config");
	SaveLocalConfig();
}

my $ClusterId = 0;
my $expect_return = 0;

$ENV{_CONDOR_MAX_WRITELOG_LOG} = "4 Kb";
$ENV{_CONDOR_WRITELOG_LOG} = "$(log)/WriteLogLog";
$ENV{_CONDOR_WRITELOG_DEBUG} = "D_ALWAYS";
$ENV{_CONDOR_MAX_NUM_WRITELOG_LOG} = 1;

if(!(defined $killconfig)) {
	my $firstappend_condor_config = '
		DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
		WANT_SUSPEND = FALSE
		#MAX_WRITELOG_LOG = 4 Kb
		#WRITELOG_LOG = $(log)/WriteLogLog
		#WRITELOG_DEBUG = D_ALWAYS
	';

	$configfile1 = CondorTest::CreateLocalConfig($firstappend_condor_config,"testlogrotation1");

	CondorTest::StartCondorWithParams(
		condor_name => "testlogrotation1",
		fresh_local => "TRUE",
		condorlocalsrc => "$configfile1",
	);
	print "First test basic job\n";
	#$result = SimpleJob::RunCheck(); # jobid 1
} else {
	my $test1 = '
		#MAX_WRITELOG_LOG = 4 Kb
		#WRITELOG_LOG = $(log)/WriteLogLog
		#WRITELOG_DEBUG = D_ALWAYS
	';
	#AppendLocalConfig($test1);

}

print "************ Size based test 4 kb, default log count: WriteLogLog\n";

my $return = "";
my $executecount = 0;
my $result = 1;

#Do a job before setting tesing exit codes

my @adarray = ();
my $LogLocation = "";

#system("condor_config_val -dump | grep MAX");
runCondorTool("condor_config_val log", \@adarray, 2, {emit_output=>0});

$LogLocation = $adarray[0];
chomp($LogLocation);

chdir("$LogLocation");

@adarray = ();
# 75 long
my $writers = "writerAAA,writerBBB";
LaunchWritersAndWait($writers,"1-4","-c 60  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
my $logsstring = GetLogsString($LogLocation,"WriteLogLog");
# pass logs oldest to newest

#check writers present all log files first
my $writershere = LogWritersCheck($logsstring, $writers);
RegisterResult($writershere, "test_name", $test_name);

print "Checking these logs: $logsstring for these writers: $writers: ";
if($writershere == 1) {
	print "OK\n";
} else {
	print "BAD\n";
}

my $result = ValidateLogs($logsstring,20480, 0, 60, $LogLocation,"writerAAA");
#RegisterResult($result, "test_name", $test_name);
$result = ValidateLogs($logsstring,20480, 0, 60, $LogLocation,"writerBBB");
RegisterResult($result, "test_name", $test_name);
chdir("$top");

# We only look at opne of the validate results since it is a size or time based
# check. But within we have a sequence validation per writer.

print "Test default log rotation with 4 Kb size constraint: ";
if($result == 1) {
	print "OK\n";
	if(defined $killconfig) {
		#CleanLogs($LogLocation);
	}
} else {
	print "BAD\n";
	if(defined $killconfig) {
		leave( "Stopping to allow log inspection here:$LogLocation\n");
	}
}


exit(0);
if(!(defined $killconfig)) {
	my $secondappend_condor_config = '
		DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
		WANT_SUSPEND = FALSE
		MAX_WRITELOG_LOG = 2 Mb
		WRITELOG_LOG = $(log)/WriteLogLog
		WRITELOG_DEBUG = D_ALWAYS
		MAX_NUM_WRITELOG_LOG = 4
	';

	$configfile2 = CondorTest::CreateLocalConfig($secondappend_condor_config,"testlogrotation2");

	CondorTest::StartCondorWithParams(
		condor_name => "testlogrotation2",
		fresh_local => "TRUE",
		condorlocalsrc => "$configfile2",
	);

	print "First test basic job\n";
	#$result = SimpleJob::RunCheck(); # jobid 1
} else {
	my $test2 = '
		MAX_NUM_WRITELOG_LOG = 4
	';

	AppendLocalConfig($test2);

}

print "****************** Sized based testing with more then two files\n";

runCondorTool("condor_config_val log", \@adarray, 2, {emit_output=>0});

$LogLocation = $adarray[0];
chomp($LogLocation);
chdir("$LogLocation");

@adarray = ();
$writers = "writerAAA,writerBBB";
LaunchWritersAndWait($writers,"1-3","-c 60  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
my $logsstring = GetLogsString($LogLocation,"WriteLogLog");
# pass logs oldest to newest

#check writers present all log files first
my $writershere = LogWritersCheck($logsstring, $writers);
RegisterResult($writershere, "test_name", $test_name);

print "Checking these logs: $logsstring for these writers: $writers: ";
if($writershere == 1) {
	print "OK\n";
} else {
	print "BAD\n";
}

my $result = ValidateLogs($logsstring,20480, 0, 60, $LogLocation,"writerAAA");
#RegisterResult($result, "test_name", $test_name);
$result = ValidateLogs($logsstring,20480, 0, 60, $LogLocation,"writerBBB");
RegisterResult($result, "test_name", $test_name);

chdir("$top");

print "Test multifile log rotation with 4 Kb size constraint: ";
if($result == 1) {
	print "OK\n";
	if(defined $killconfig) {
		CleanLogs($LogLocation);
	}
} else {
	print "Known Broken\n";
	if(defined $killconfig) {
		leave( "Stopping to allow log inspection here:$LogLocation\n");
	}
}

CondorTest::EndTest();
RestoreLocalConfig(); # retore original condor_config.local file
exit(0);

if(!(defined $killconfig)) {
	my $thirdappend_condor_config = '
		DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
		WANT_SUSPEND = FALSE
		MAX_WRITELOG_LOG = 60 Sec
		WRITELOG_LOG = $(log)/WriteLogLog
		WRITELOG_DEBUG = D_ALWAYS
	';

	$configfile3 = CondorTest::CreateLocalConfig($thirdappend_condor_config,"testlogrotation3");

	CondorTest::StartCondorWithParams(
		condor_name => "testlogrotation3",
		fresh_local => "TRUE",
		condorlocalsrc => "$configfile3",
	);

	print "First test basic job\n";
	#$result = SimpleJob::RunCheck(); # jobid 1
} else {
	my $test3 = '
		MAX_WRITELOG_LOG = 60 Sec
		MAX_NUM_WRITELOG_LOG = 1
	';

	AppendLocalConfig($test3);

	print "************ testing for 2 time based log files\n";
}

runCondorTool("condor_config_val log", \@adarray, 2, {emit_output=>0});

$LogLocation = $adarray[0];
chomp($LogLocation);
chdir("$LogLocation");

@adarray = ();
runCondorTool("condor_testwritelog -s 1 -c 300  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", \@adarray, 2 ,{emit_output=>0});
$logsstring = GetLogsString($LogLocation,"WriteLogLog");
# pass logs oldest to newest
$result = ValidateLogs($logsstring, 0, 60, 300, $LogLocation);
RegisterResult($result, "test_name", $test_name);
chdir("$top");

print "Test time based log rotation with 60 Sec time constraint: ";
if($result == 1) {
	print "OK\n";
	if(defined $killconfig) {
		CleanLogs($LogLocation);
	}
} else {
	print "BAD\n";
	if(defined $killconfig) {
		leave( "Stopping to allow log inspection here:$LogLocation\n");
	}
}

if(!(defined $killconfig)) {
	my $forthappend_condor_config = '
		DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
		WANT_SUSPEND = FALSE
		MAX_WRITELOG_LOG = 60 Sec
		WRITELOG_LOG = $(log)/WriteLogLog
		WRITELOG_DEBUG = D_ALWAYS
		MAX_NUM_WRITELOG_LOG = 2
	';

	$configfile4 = CondorTest::CreateLocalConfig($forthappend_condor_config,"testlogrotation4");

	CondorTest::StartCondorWithParams(
		condor_name => "testlogrotation4",
		fresh_local => "TRUE",
		condorlocalsrc => "$configfile4",
	);

	print "First test basic job\n";
	#$result = SimpleJob::RunCheck(); # jobid 1
} else {
	my $test4 = '
		MAX_NUM_WRITELOG_LOG = 2
	';

	AppendLocalConfig($test4);

	print "************ Testing for more then two time based log files\n";
}

runCondorTool("condor_config_val log", \@adarray, 2, {emit_output=>0});

$LogLocation = $adarray[0];
chomp($LogLocation);
chdir("$LogLocation");

@adarray = ();
runCondorTool("condor_testwritelog -s 1 -c 300  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", \@adarray, 2 ,{emit_output=>0});
# pass logs oldest to newest
my $logsstring = GetLogsString($LogLocation,"WriteLogLog");
$result = ValidateLogs($logsstring, 0, 60, 300, $LogLocation);
RegisterResult($result, "test_name", $test_name);
chdir("$top");

print "Test time based log rotation with 60 Sec time constraint and multiple files: ";
if($result == 1) {
	print "OK\n";
	if(defined $killconfig) {
		CleanLogs($LogLocation);
	}
} else {
	print "BAD\n";
	if(defined $killconfig) {
		leave( "Stopping to allow log inspection here:$LogLocation\n");
	}
}


CondorTest::EndTest();
RestoreLocalConfig(); # retore original condor_config.local file


#my $localconfig = "condor_config.local";
#my $localconfigsave = "condor_config.local.save";
#my $configlocation = "";
# $localconfig = SaveLocalConfig();

sub SaveLocalConfig
{
	my @adarray = ();
	runCondorTool("condor_config_val -config", \@adarray, 2, {emit_output=>1});

	my $locfigloc = $adarray[1];
	chomp($locfigloc);

	if($locfigloc =~ /^\s*(.*?)condor_config\.local$/) {
		$configlocation = $1;
	} else {
		print "Mismatch on $locfigloc\n";
	}
	chdir("$configlocation");
	runcmd("cp $localconfig $localconfigsave",{emit_output=>0});
	runcmd("ls",{emit_output=>0});

	#print "Leaving SaveLocalConfig configlocation: $configlocation, top:$top\n";
	#print "Configfile location is: $configlocation\n";
	chdir("$top");
}

sub AppendLocalConfig
{
	my $newtext = shift;
	my $tmpconfig = "condor_config.local.new";
	#print "entering AppendLocalConfig configlocation: $configlocation\n";
	chdir("$configlocation");
	#print "AppendLocalConfig src:$localconfig dest:$tmpconfig\n";
	open(CF, "<$localconfig") or leave( "Append Failed to $localconfig:$!\n");
	open(CFN, ">$tmpconfig") or leave("Append Failed to $tmpconfig:$!\n");
	# copy contents of current config file
	while (<CF>) {
		print CFN "$_";
	}
	close(CF);
	print CFN "$newtext";
	close(CFN);
	runcmd("mv $tmpconfig $localconfig",{emit_output=>0});
	chdir("$top");
}

sub RestoreLocalConfig
{
	#print "entering RestoreLocalConfig configlocation: $configlocation\n";
	chdir("$configlocation");
	runcmd("cp $localconfig condor_config.test",{emit_output=>0});
	runcmd("cp $localconfigsave $localconfig",{emit_output=>0});
	runcmd("ls",{emit_output=>1});

	print "Config restore complete\n";
	chdir("$top");
}

sub leave 
{
	my $diemessage = shift;
	RestoreLocalConfig();
	die $diemessage;
}

sub GetLogsString
{
	my $loglocation = shift;
	my $pattern = shift;
	my @patterns = ();
	my @sortedpatterns = ();
	my @reversepatterns =  ();
	my $sorteddstring = "";

	#print "Check pattern: $pattern from here: ";
	system("pwd;ls");
	@patterns = `ls $pattern.*`;
	@sortedpatterns = sort @patterns;
	print "Sorted logs:\n";
	foreach my $target (@sortedpatterns) {
		print "$target ";
	}
	print "\n";

	#@reversepatterns = reverse @sortedpatterns;

	foreach my $logfile (@sortedpatterns) {
		chomp($logfile);
		if($sorteddstring eq "") {
			$sorteddstring = $logfile;
		} elsif($logfile eq "WriteLogLog.old") {
			$sorteddstring = "$logfile," . $sorteddstring;
		} else {
			$sorteddstring = $sorteddstring . ",$logfile";
		}
	}

	print "New log ordering: $sorteddstring\n";
	return($sorteddstring);
}

sub CleanLogs
{
	my $loglocation = shift;
	chdir("$loglocation");
	#print "Cleaning, here now:\n";
	#runcmd("pwd;ls WriteLog*");
	runcmd("rm WriteLog*",{emit_output=>0});
	#print "After cleaning\n";
	#system("pwd;ls WriteLog*");
	chdir("$top");
}

sub ValidateLogs
{
	my $logs = shift;
	my $size = shift;
	my $times = shift;
	my $iters = shift;
	my $loglocation = shift;
	my $pattern = shift;
	my $result = 1; # defaults to good.
	my $sizechk = 0;
	my $timecheck = 0;
	my $logcount = 0;

	#print "ValidateLogs: Log Location: $loglocation\n";

	chdir("$loglocation");
	#print "here now:\n";
	#system("pwd;ls WriteLog*");
	my @logs = split /,/, $logs;
	$logcount = @logs;
	my $seqresult = 0;
	#print "Checking $logcount logs:$logs\n";

	# first check existence of files
	my $missingfile = 0;
	foreach my $logfile (@logs) {
		if(!(-f $logfile)) {
			print "Expected log file does not exist: $logfile: BAD\n";
			RegisterResult($missingfile, "test_name", $test_name);
			chdir($top);
		}
	}

	# next do sequence validation
	# next check even flow of numbers as we travel through ordered list of log files
	$seqresult = LogSequenceCheck($logs,$pattern);
	RegisterResult($seqresult, "test_name", $test_name);

	print "Sequence validation of: $logs and pattern: $pattern: ";
	if($seqresult == 0) {
		print "BAD\n";
	} else {
		print "OK\n";
	}

	# check approximate size or age of file
	if($size != 0) {
		$sizechk = 1;
		$result = LogSizeCheck($logs, $size, $logcount);
	}
	if($times != 0) {
		$timecheck = 1;
		$result = LogTimeCheck($logs, $times, $logcount);
	}
	if($sizechk or $timecheck) {
	} else {
		print "Either a size or time test needs to be requested\n";
		chdir($top);
		return($result);
	}
	if($result != 1) {
		chdir($top);
		return($result);
	}
	#$result = 1;
	chdir("$top");
	return($result);
}

sub LogWritersCheck
{
	my $logfiles = shift;
	my @logs = split /,/, $logfiles;
	my $logcount = @logs;
	my $writers = shift;
	my @testwriters = split /,/, $writers;
	my $writecount = @testwriters;
	my $expectedhits = $logcount * $writecount;
	print "$logcount logs and $writecount writers: expect $expectedhits positive hits\n";

	my $results = 0;

	foreach my $logfile (@logs) {
		foreach my $pat (@testwriters) {
			$results += FindPatternInFile($logfile,$pat);
		}
	}

	print "positive score:$results of $expectedhits\n";
	if($results != $expectedhits) {
		return(0);
	} else {
		return(1);
	}


}

sub FindPatternInFile
{
	my $file = shift;
	my $pattern = shift;
	my $foundit = 0;

	open(PP, "<$file") or die "Failed to open $file:$!\n";
	while(<PP>) {
		if($_ =~ /$pattern/) {
			$foundit++;
		}
	}
	close(PP);
	if($foundit == 0) {
		print "LOG: $file Missing: $pattern\n";
		return($foundit);
	}
	return(1);
}

sub LogSequenceCheck
{
	my $logs = shift;
	my $wpattern = shift;  # are we checking one writers sequences?
	my @logs = split /,/, $logs;
	my $res = 1;
	my $lastseq = 0;
	my $line = "";

	# if we are going this route with a pushed pattern, we expect multiple
	# writer patterns. We not only want to see sequences per writer be 
	# conserved, we expect to see more then one writer entries.
	my %writers = ();
	my @writerpatterns = ();
	my $writercount = 0;
	my $foundpattern = 0;
	print "Pattern: $wpattern\n";
	foreach my $logfile (@logs) {
		%writers = ();
		@writerpatterns = ();
		$writercount = 0;
		$foundpattern = 0;

		open(SC,"<$logfile") or leave( "Sequence check on validated log: $logfile failed:$!\n");
		while (<SC>) {
			chomp($_);
			$line = $_;
			if(defined $wpattern) {
				if($line =~ /^.*?$wpattern,(\d+),.*$/) {
					$foundpattern = 1;
					#print "$line\n";
					if($lastseq == 0) {
						$lastseq = $1;
					} else {
						if(($lastseq -1) != $1) {
							print "comparing ($lastseq -1) to $1 in $line\n";
							return(0);
						} else {
							$lastseq = $1;
						}
					}
				}
			} else {
				if($line =~ /^.*?(\d+),.*$/) {
					#print "$1\n";
					if($lastseq == 0) {
						$lastseq = $1;
					} else {
						if(($lastseq -1) != $1) {
							print "comparing ($lastseq -1) to $1 in $line\n";
							return(0);
						} else {
							$lastseq = $1;
						}
					}
				}
			}
		}
		close(SC);
		if(defined $wpattern) {
			if($foundpattern == 0) {
				RegisterResult(0, "test_name", $test_name);
				print "Failed to find $wpattern in $logfile: BAD\n";
			}
		}
	}
	return($res);
}

sub LogSizeCheck
{
	my $logs = shift;
	my $size = shift;
	my $logcount = shift;
	my @logs = split /,/, $logs;
	my $res = 1;
	my $count = 0;
	my $filesize = 0;
	while($count < ($logcount - 1)) {
		$filesize = -s $logs[$count];
		# lets bracket the size
		if($filesize < ($size + 1024)) {
		} else {
			print "Log: $logs[$count] size: $filesize > ($size + 1024)\n";
			return(0);
		}
		if($filesize > ($size - 1024)) {
		} else {
			print "Log: $logs[$count] size: $filesize < ($size - 1024)\n";
			return(0);
		}
		$count += 1;
	}
	print "Log size check is fine\n";
	return($res);
}

sub LogTimeCheck
{
	my $logs = shift;
	my $time = shift;
	my $logcount = shift;
	my @logs = split /,/, $logs;
	my $res = 1;
	my $count = 0;
	my $StartTime = 0;
	my $EndTime = 0;
	my $TimeDiff = 0;
	my $line = "";
	while($count < ($logcount - 1)) {
		$StartTime = 0;
		$EndTime = 0;
		open(LL,"<@logs[$count]") or leave( "Failed to open @logs[$count]:$!\n");
		while (<LL>) {
			chomp();
			$line = $_;
			#if($line =~ /^(\d+\/\d+\/\d+\s+\d+:\d+:\d+\s+).*$/) {
			if($line =~ /^(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+):(\d+)\s+.*$/) {
				#print "$1\n";
				#$StartTime = parsedate("$1");
				if($StartTime == 0) {
					$StartTime = timelocal($6,$5,$4,$2,$1,$3);
				} else {
					$EndTime = timelocal($6,$5,$4,$2,$1,$3);
				}
			}
		}
		close(LL);
		$TimeDiff = $EndTime - $StartTime;
		#print "---$StartTime---\n";
		#print "---$EndTime---\n";
		#print "Diff = $TimeDiff\n";
		if($TimeDiff < ($time + 4)) {
		} else {
			print "Log: $logs[$count] time: $TimeDiff > ($time + 4)\n";
			return(0);
		}
		if($TimeDiff > ($time - 4)) {
		} else {
			print "Log: $logs[$count] time: $TimeDiff < ($time - 4)\n";
			return(0);
		}
		$count += 1;
	}
	print "Log time check is fine\n";
	return($res);
}

# send writers requested as unique pattern they will enter
# send time range for randomized sleep between writer events
# and send the count and pattern
#sub LaunchWritersAndWait(writers,"1-5","-c 300  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
sub LaunchWritersAndWait
{
	my $writerstring = shift;
	my @writers = split /,/, $writerstring;
	my $rangestring = shift;
	my @rangeparts = split /\-/, $rangestring;
	my $rangecontrol = " -r $rangeparts[0] $rangeparts[1]";
	my $remaingargs = shift;
	my $program = "condor_testwritelog";
	my %writerpids = ();
	my $harvestpid = "";
	my @seeds = (
		41,
		87,
		3,
		29,
	);

	#submit the writers
	foreach my $wr (@writers) {
		my $seed = shift(@seeds);
		my $cmd = "$program -w $wr -S $seed $rangecontrol $remaingargs";
		print "$cmd\n";
		my $pid = fork();
		if($pid == -1) {
			die "Fork error:$!\n";
		} elsif($pid == 0) {
			exec $cmd or die "exec of $cmd failed: $!\n";
		} else {
			# parent gets pid of child fork
			$writepids{$pid} = $wr;
		}
	}
	# harvest writers then explore results
	while ( my $child = wait()) {
		if($child == -1) {
			last;
		} else {
			print "Writer: $writepids{$child} done\n";
		}
	}
	print "Leaviing LaunchWritersAndWait\n";
}

#my @a = (1, 2, 3);
#for my $p (@a) {
	#my $pid = fork();
	#if ($pid == -1) {
		#die;
	#} elsif ($pid == 0) {
		#exec '/bin/sleep', $p or die;
	#}
#}
#while (wait() != -1) {}
#print "Done\n";
