#! /usr/bin/perl


# This script figures out the state of the partitions by querying the
# mmcs and the collector for partitions backed by startds
# See the example output at the bottom of the script.

# XXX TODO emit classads for not generated partition, basically, they are just
# availble to be used as partition names only.

use warnings;
use strict;
use IPC::Open3; # stdin & stdout/err of a process

my $cst_cmd = "/home/condor/bin/condor_status";
my $mmcs_cmd = "/bgsys/drivers/ppcfloor/bin/mmcs_db_console";
my $mmcs_c_cmd = "/home/condor/libexec/htc_list_partitions";

# initial configuration of partitions, assume all are not generated, 
# then fill in the hashes with what I find via various sources. After the
# discovery phase, the hash entries will be in different states.
my @g_parts = 
(
	#{
		# "R00-M0-N00" =>
		# 	{
		# 		"State" => "GENERATED",
		# 		"Size" => 32
		# 	}
	#},
	{
		"R00-M0-N01" =>
			{
				# These are classad attribute names;
				"State" => "NOT_GENERATED",
			}
	},
	{
		"R00-M0-N02" =>
			{
				"State" => "NOT_GENERATED",
			}
	},
	{
		"R00-M0-N03" =>
			{
				"State" => "NOT_GENERATED",
			}
	},
	{
		"R00-M1-N00" =>
			{
				"State" => "NOT_GENERATED",
			}
	},

	{
		"R01-M0-N01" =>
			{
				"State" => "NOT_GENERATED",

			}
	},
	{
		"R01-M0-N02" =>
			{
				"State" => "NOT_GENERATED",
			}
	},
	{
		"R01-M0-N03" =>
			{
				"State" => "NOT_GENERATED",
			}
	},
	{
		"R01-M1-N00" =>
			{
				"State" => "NOT_GENERATED",
			}
	},

);

# grep out of the mmcs_partsref array to see if the specific pname is
# available to me, and if so, then grab out interesting data from it.
sub mmcs_status
{
	my ($pname, $pref, $mmcs_partsref) = @_;
	my @result;
	my @lines;
	my $kind;

	#print "Checking mmcs for status of $pname...\n";

	@lines = grep(/\Q$pname\E/, @{$mmcs_partsref});

	if (scalar(@lines) <= 0) {
		# if not labeled in the mmcs output, it isn't booted at all.
		#print "mmcs_status: $pname not found\n";
		return 0;
	}

	# normal behaviour
	if (scalar(@lines) == 1) {
		# check if ok
		if ($lines[0] =~ /[Ff]ail/) {
			# no good.
			#print "mmcs_status: $pname has failed\n";
			return 0;
		}
		
		# add to the "ad" describing this partition
		$pref->{$pname}{"State"} = "BOOTED";

		# get kind
		$kind = $lines[0];
		$kind =~ s/^(.*\shtc=)(\w+)(\s*)$/$2/;
		$kind = uc($kind);
		$pref->{$pname}{"Kind"} = $kind;

		# Since we are assuming 32 compute cards in a generated partition, 
		# depending upon what kind they are, multiply the size by a constant.
		# XXX The assumption needs to be fixed by using the bridge api.
		if ($kind eq "SMP") {
			$pref->{$pname}{"Size"} *= 1;
		} elsif ($kind eq "DUAL") {
			$pref->{$pname}{"Size"} *= 2;
		} elsif ($kind eq "VN") {
			$pref->{$pname}{"Size"} *= 4;
		}

		#print "mmcs_status: $pname BOOTED $kind\n";
	}

	return 1;
}

# This will override the state of the partition from BOOTED to BACKED 
# if it is found to be in the collector.
sub collector_status
{
	my ($pname, $pref) = @_;
	my @result;
	my @lines;
	my $kind;
	my $cmd;
	my ($junk, $ad_pname, $ad_num_cpus, $ad_kind, $ad_sname);

	#print "Looking in collector for $pname...\n";

	@result = `$cst_cmd -format "BACKER:\t%s\t" PARTITION_NAME -format "%d\t" BGP_BLOCK_SIZE -format "%s\t" BGP_BLOCK_TYPE -format "%s\n" Name 2>&1 | grep $pname`;

	@lines = grep(/BACKER:/, @result);
	if (scalar(@lines) <= 0) {
		return 0;
	}
	chomp $lines[0];

	# Testing line
	#$lines[0] = "BACKER:\tR00-M0-N00\t32\tSMP\tslot22\@R00-M0-N00\@fqdn";

	($junk, $ad_pname, $ad_num_cpus, $ad_kind, $ad_sname) = 
		split(/\t/, $lines[0]);
	
	# figure out the real backer startd name
	$ad_sname =~ s/^.*@(.*@.*)/$1/g;

	$pref->{$pname}{"State"} = "BACKED";
	$pref->{$pname}{"Backer"} = "$ad_sname";

	# kind and size already filled in by the mmcs function.
}

sub get_mmcs_partition_data
{
	my @parts;

	# call a specific C program to get me the info.
	# The info currently looks like this:
	# R00-M1-N00 htc=dual
	# R00-M0-N01 htc=smp
	# R00-M0-N00 htc=smp
	# ...

	@parts = `$mmcs_c_cmd`;

	map { chomp($_); } @parts;

	return @parts;
}

sub main
{
	my $partref;
	my $pname;
	my @result;
	my $attr;
	my @mmcs_parts;

	@mmcs_parts = get_mmcs_partition_data();

	foreach $partref (@g_parts) {
		# each hash table only has one key 
		($pname) = keys %{$partref};

		#print "Processing $pname\n";

		if (mmcs_status($pname, $partref, \@mmcs_parts) == 1) {
			# if mmcs knew about it, then maybe the collector 
			# knows too. Unfortunately, we query once per
			# partition to the collector. XXX FIXME for
			# speed.
			collector_status($pname, $partref);
		}
	}

	# emit the ads
	foreach $partref (@g_parts) {
		# each hash table only has one key 
		($pname) = keys %{$partref};
		print "Partition = \"$pname\"\n";
		foreach $attr (sort keys %{$partref->{$pname}}) {
			if ($attr eq "Size") {
				print "$attr = $partref->{$pname}{$attr}\n";
			} else {
				print "$attr = \"$partref->{$pname}{$attr}\"\n";
			}
		}
		print "---\n";
	}

	return 0;
}

exit main();


# example of style of classads emitted by this script after the status' of
# the various partition names have been determined.

sub examples
{
########################################
# generated, booted to smp, backed by startd
########################################
print qq|Partition = "R00-M0-N00"\n|;
print qq|State = "BACKED"\n|;
print qq|Size = 32\n|;
# if exists and booted
print qq|Kind = "SMP"\n|;
# if exists and booted and backed
print qq|Backer = "R00-M0-N00\@fqdn.com"\n|;

########################################
# generated, booted to dual, backed by startd
########################################
print qq|---\n|;
print qq|Partition = "R01-M0-N00"\n|;
print qq|State = "BACKED"\n|;
print qq|Size = 64\n|;
# if exists and booted
print qq|Kind = "DUAL"\n|;
# if exists and booted and backed
print qq|Backer = "R01-M0-N00\@fqdn.com"\n|;

########################################
# generated, booted to DUAL, not backed by startd
########################################
print qq|---\n|;
print qq|Partition = "R02-M0-N00"\n|;
print qq|State = "BOOTED"\n|;
print qq|Size = 128\n|;
# if exists and booted
print qq|Kind = "DUAL"\n|;

########################################
# generated, booted to VN, not backed by startd
########################################
print qq|---\n|;
print qq|Partition = "R03-M0-N00"\n|;
print qq|State = "BOOTED"\n|;
print qq|Size = 128\n|;
# if exists and booted
print qq|Kind = "VN"\n|;

########################################
# generated, not booted, not backed by startd
########################################
print qq|---\n|;
print qq|Partition = "R04-M0-N00"\n|;
print qq|State = "GENERATED"\n|;
print qq|Size = 256\n|;

########################################
# not generated, not booted, not backed by startd
########################################
print qq|Partition = "R01-M0-N01"\n|;
print qq|State = "NOT_GENERATED"\n|;

########################################
# not generated, not booted, not backed by startd
########################################
print qq|Partition = "R01-M0-N02"\n|;
print qq|State = "NOT_GENERATED"\n|;

########################################
# not generated, not booted, not backed by startd
########################################
print qq|Partition = "R01-M0-N03"\n|;
print qq|State = "NOT_GENERATED"\n|;

########################################
# not generated, not booted, not backed by startd
########################################
print qq|Partition = "R01-M1-N00"\n|;
print qq|State = "NOT_GENERATED"\n|;

}
