#! /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_rotation";
CondorTest::RunTest($test_name);

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

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

CondorTest::DebugLevel(2);

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

if(!(defined $killconfig)) {
	if(CondorUtils::is_windows()) {
		my $firstappend_condor_config = '
			DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
			WANT_SUSPEND = FALSE
			MAX_WRITELOG_LOG = 20 Kb
			WRITELOG_LOG = $(log)/WriteLogLog
			WRITELOG_DEBUG = D_ALWAYS
			LOG_OPEN_DEFAULT = FALSE
		';
	} else {
		my $firstappend_condor_config = '
			DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
			WANT_SUSPEND = FALSE
			MAX_WRITELOG_LOG = 20 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 = 20 Kb
		WRITELOG_LOG = $(log)/WriteLogLog
		WRITELOG_DEBUG = D_ALWAYS
	';
	AppendLocalConfig($test1);

	print "************ Size based test 20 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 = "";

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

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

chdir("$LogLocation");

@adarray = ();
# 75 long * 410 = 30 kb
runCondorTool("condor_testwritelog -c 410 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", \@adarray, 2 ,{emit_output=>0});
my $logsstring = GetLogsString($LogLocation,"WriteLogLog");
# pass logs oldest to newest

CondorTest::debug("About to call ValidateLogs:\n",$debuglevel);

my $result = ValidateLogs($logsstring,20480, 0, 2000, $LogLocation);
RegisterResult($result, "test_name", $test_name);
chdir("$top");

print "Test default log rotation with 20 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");
	}
}

if(!(defined $killconfig)) {
	if(CondorUtils::is_windows()) {
		my $secondappend_condor_config = '
			DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
			WANT_SUSPEND = FALSE
			MAX_WRITELOG_LOG = 20 Kb
			WRITELOG_LOG = $(log)/WriteLogLog
			WRITELOG_DEBUG = D_ALWAYS
			LOG_OPEN_DEFAULT = FALSE
		';
	} else {
		my $secondappend_condor_config = '
			DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
			WANT_SUSPEND = FALSE
			MAX_WRITELOG_LOG = 20 Kb
			WRITELOG_LOG = $(log)/WriteLogLog
			WRITELOG_DEBUG = D_ALWAYS
		';
	}

	$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 = ();
# 75 long * 955 = 70 kb
runCondorTool("condor_testwritelog -c 955 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", \@adarray, 2 ,{emit_output=>0});
$logsstring = GetLogsString($LogLocation,"WriteLogLog");
# pass logs oldest to newest
CondorTest::debug("About to call ValidateLogs:\n",$debuglevel);
$result = ValidateLogs($logsstring, 20480, 0, 112000, $LogLocation);
RegisterResult($result, "test_name", $test_name);
chdir("$top");

print "Test multifile log rotation with 20 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");
	}
}

if(!(defined $killconfig)) {
	if(CondorUtils::is_windows()) {
		my $thirdappend_condor_config = '
			DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
			WANT_SUSPEND = FALSE
			MAX_WRITELOG_LOG = 20 Kb
			WRITELOG_LOG = $(log)/WriteLogLog
			WRITELOG_DEBUG = D_ALWAYS
			LOG_OPEN_DEFAULT = FALSE
		';
	} else {
		my $thirdappend_condor_config = '
			DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
			WANT_SUSPEND = FALSE
			MAX_WRITELOG_LOG = 20 Kb
			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 = 20 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 80  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", \@adarray, 2 ,{emit_output=>0});
$logsstring = GetLogsString($LogLocation,"WriteLogLog");
# pass logs oldest to newest
CondorTest::debug("About to call ValidateLogs:\n",$debuglevel);
$result = ValidateLogs($logsstring, 0, 20, 300, $LogLocation);
RegisterResult($result, "test_name", $test_name);
chdir("$top");

print "Test time based log rotation with 20 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)) {
	if(CondorUtils::is_windows()) {
		my $forthappend_condor_config = '
			DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
			WANT_SUSPEND = FALSE
			MAX_WRITELOG_LOG = 20 Kb
			WRITELOG_LOG = $(log)/WriteLogLog
			WRITELOG_DEBUG = D_ALWAYS
			LOG_OPEN_DEFAULT = FALSE
		';
	} else {
		my $forthappend_condor_config = '
			DAEMON_LIST = MASTER,SCHEDD,COLLECTOR,NEGOTIATOR,STARTD
			WANT_SUSPEND = FALSE
			MAX_WRITELOG_LOG = 20 Kb
			WRITELOG_LOG = $(log)/WriteLogLog
			WRITELOG_DEBUG = D_ALWAYS
		';
	}

	$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 = ();
CondorTest::debug("calling condor_testwritelog -s 1 -c 80 xxxxx\n",$debuglevel);
runCondorTool("condor_testwritelog -s 1 -c 80  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", \@adarray, 2 ,{emit_output=>0});
CondorTest::debug("Back Froom condor_testwritelog -s 1 -c 80 xxxxx\n",$debuglevel);
# pass logs oldest to newest
my $logsstring = GetLogsString($LogLocation,"WriteLogLog");
CondorTest::debug("About to call ValidateLogs:\n",$debuglevel);
$result = ValidateLogs($logsstring, 0, 20, 300, $LogLocation);
RegisterResult($result, "test_name", $test_name);
chdir("$top");

print "Test time based log rotation with 20 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();

exit(0);
#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;
		print "Setting configlocation:$configlocation \n";
	} else {
		print "Mismatch on $locfigloc\n";
	}
	print "SET config location:$configlocation\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 GetFilesByPattern
{
	my $pattern = shift;
	my $resarrayref = shift;
	my $location = shift;
	my $cwd = getcwd();
	my $count = 0;
	if(defined $location) {
		chdir("$location");
	}

	print "GetFilesByPattern:$pattern\n";
	opendir(GD,".") or die "failed to open .:$!\n";
	foreach my $subfile (readdir GD) {
		next if $subfile =~ /^\.\.?$/;
		if($subfile =~/$pattern\..*/) {
			push @{$resarrayref}, $subfile;
			print "GetFilesByPattern:$subfile match pattern:$pattern\n";
			$count += 1;
		}
	}
	close(GD);
	if(defined $location) {
		chdir("cwd");
	}
	print "Files found:$count\n";
	return($count);
}

sub GetLogsString
{
	my $loglocation = shift;
	my $pattern = shift;
	my @patterns = ();
	my @sortedpatterns = ();
	my @reversepatterns =  ();
	my $sorteddstring = "";
	my $size = 0;
	my $count = 0;
	my $string = "";
	my $matches = 0;

	print "Check pattern: $pattern from here: ";
	#DirLs();
	#@patterns = `ls $pattern.*`;
	$matches = GetFilesByPattern($pattern,\@patterns);
	$size = @patterns;
	print "There are $size matching patterns\n";
	while($count < $size)
	{
		# remove possible white space
		$string = $patterns[$count];
		print "Consider:$string\n";

		#if($string =~ /\s*(.*?)\s*/) {
			#$patterns[$count] = $1;
			#print "Changed to :$1:\n";
		#}
		$count += 1;
	}

	@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 $result = 1; # defaults to good.
	my $sizechk = 0;
	my $timecheck = 0;
	my $logcount = 0;

	CondorTest::debug("ValidateLogs:$logs  Log Location: $loglocation\n",$debuglevel);
	CondorTest::debug("ValidateLogs:size:$size times:$times iters:$iters",$debuglevel);

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

	# first check existence of files
	foreach my $logfile (@logs) {
		if(!(-f $logfile)) {
			print "Expected log file does not exist: $logfile\n";
			chdir($top);
			return(0);
		}
	}
	# next check even flow of numbers as we travel through ordered list of log files
	
	CondorTest::debug("Calling:LogSequenceCheck\n",$debuglevel);
	$seqresult = LogSequenceCheck($logs);
	CondorTest::debug("BACK From Calling:LogSequenceCheck\n",$debuglevel);

	CondorTest::RegisterResult($seqresult, "test_name", $test_name);

	CondorTest::debug("Sequence validation of:$logs: ",$debuglevel);
	if($seqresult == 1) {
		print "OK\n";
	} else {
		print "BAD\n";
	}

	# check approximate size or age of file
	if($size != 0) {
		$sizechk = 1;
		CondorTest::debug("LogSizeCheck\n",$debuglevel);
		$result = LogSizeCheck($logs, $size, $logcount);
	}
	if($times != 0) {
		$timecheck = 1;
		CondorTest::debug("LogTimeCheck\n",$debuglevel);
		$result = LogTimeCheck($logs, $times, $logcount);
	}

	CondorTest::debug( "Back to validate\n",$debuglevel);
	if($sizechk or $timecheck) {
	} else {
		CondorTest::debug("Either a size or time test needs to be requested\n",$debuglevel);
		chdir($top);
		return($result);
	}
	if($result != 1) {
		chdir($top);
		return($result);
	}
	chdir("$top");
	return($result);
}

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 = "";
	CondorTest::debug("LogSequenceCheck: Start logs:$logs\n",$debuglevel);
	foreach my $logfile (@logs) {

		CondorTest::debug("LogSequenceCheck: open to check:$logfile\n",$debuglevel);
		open(SC,"<$logfile") or leave( "Sequence check on validated log: $logfile failed:$!\n");
		while (<SC>) {
			chomp($_);
			$line = $_;
			if($line =~ /hostname/) {
				next;
			}
			if(defined $wpattern) {
				if($line =~ /^.*?$wpattern,(\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;
						}
					}
				}
			} 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;
						}
					}
				}
			}
		}
	}
	CondorTest::debug("LogSequenceCheck: retun result:$res\n",$debuglevel);
	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;
	my $upper = ($size + 1024);
	my $lower = ($size + 1024);
	CondorTest::debug("LogSizeCheck: logs:$logs size:$size logcount:$logcount\n",$debuglevel);
	while($count < ($logcount - 1)) {
		$filesize = -s $logs[$count];
		CondorTest::debug("Looping in Size Check:$count of $logcount\n",$debuglevel);
		# lets bracket the size
		if($filesize < ($size + 1024)) {
			CondorTest::debug("<",$debuglevel);
			
		} else {
			print "Log: $logs[$count] size: $filesize > ($size + 1024)\n";
			return(0);
		}
		if($filesize > ($size - 1024)) {
			CondorTest::debug(">",$debuglevel);
		} else {
			print "Log: $logs[$count] size: $filesize < ($size - 1024)\n";
			return(0);
		}
		$count += 1;
	}
	CondorTest::debug("LogSizeCheck: leaving:$res\n",$debuglevel);
	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 = "";
	CondorTest::debug("LogSizeCheck: leaving:$res\n",$debuglevel);
	while($count < ($logcount - 1)) {
		CondorTest::debug("Looping in Size Check:$count of $logcount\n",$debuglevel);
		$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 - 1),$3);
				} else {
					$EndTime = timelocal($6,$5,$4,$2,($1 - 1),$3);
				}
			}
		}
		close(LL);
		$TimeDiff = $EndTime - $StartTime;
		#print "---$StartTime---\n";
		#print "---$EndTime---\n";
		#print "Diff = $TimeDiff\n";
		if($TimeDiff < ($time + 4)) {
			CondorTest::debug("<",$debuglevel);
		} else {
			print "Log: $logs[$count] time: $TimeDiff > ($time + 4)\n";
			return(0);
		}
		if($TimeDiff > ($time - 4)) {
			CondorTest::debug(">",$debuglevel);
		} else {
			print "Log: $logs[$count] time: $TimeDiff < ($time - 4)\n";
			return(0);
		}
		$count += 1;
	}
	return($res);
}
