#! /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;

# Update the module include path
BEGIN
{
    my $Dir = $0;
    if ( $Dir =~ /(.*)\/.*/ )
    {
	push @INC, "$1";
    }
}
use HawkeyePublish;
use HawkeyeLib;

# Prototypes
sub Init( );
sub RunIt( );
sub CheckCollectors( $ );
sub RunAbsent( );
sub RunTotal( $$ );
sub RunSubmit(  );
sub RunNegotiator( $ );


# Setup the hawkeye stuff
my $Hawkeye;
my %Config = (
	      PoolProg => "condor_pool",
	      PoolName => "",
	      Cluster => "",
	      Reserved => "",
	      NegotiatorList => "",
	      DoAbsent => 1,
	      DoSummary => 1,
	      DoRun => 1,
	      DoCollectors => 1,
	      DoNegotiator => 1,
	      DoSubmit => 1,
	      DoSubmitMunge => 0,
	      DoCheckpoint => 1,
	      DoSubmitDetails => 1,
	      DoArchDetail => 1,
	      PoolDescription => "",
	      FpingExe => 0,
	     );
my $Hash;

# Do it
Init();
RunIt();

sub Init()
{
    HawkeyeLib::DoConfig( );

    $Hawkeye = HawkeyePublish->new;
    $Hawkeye->Quiet( 1 );
    $Hawkeye->AutoIndexSet( 0 );

    # Read the config info..
    my $Tmp;

    $Tmp = HawkeyeLib::ReadConfig( "_pathadd", "" );
    $ENV{PATH} = "$Tmp:$ENV{PATH}" if ( $Tmp ne "" );

    $Tmp = HawkeyeLib::ReadConfig( "_executable", "" );
    $Config{PoolProg} = $Tmp if ( $Tmp ne "" );

    $Tmp = HawkeyeLib::ReadConfig( "_dir", "" );
    push( @{$Config{AbsentOptions}}, "--dir=$Tmp" ) if ( $Tmp ne "" );

    $Tmp = HawkeyeLib::ReadConfig( "_name", "" );
    $Config{PoolName} = $Tmp if ( $Tmp ne "" );

    $Tmp = HawkeyeLib::ReadConfig( "_description", "" );
    $Config{Description} = $Tmp if ( $Tmp ne "" );

    $Tmp = HawkeyeLib::ReadConfig( "_cluster", "true" );
    if ( $Tmp =~ /true/i )
    {
	push( @{$Config{AbsentOptions}}, "--down=cluster" );
	push( @{$Config{AbsentOptions}}, "--downlist=cluster" );
    }

    $Tmp = HawkeyeLib::ReadConfig( "_reserved", "false" );
    push( @{$Config{AbsentOptions}}, "--reserved" );

    $Tmp = HawkeyeLib::ReadConfig( "_absent", "true" );
    $Config{DoAbsent} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_summary", "true" );
    $Config{DoSummary} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_run", "true" );
    $Config{DoRun} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_submit", "true" );
    $Config{DoSubmit} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_collector", "true" );
    $Config{DoCollectors} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_negotiator", "true" );
    $Config{DoNegotiator} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_submit_munge", "true" );
    $Config{DoSubmitMunge} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_submit_detail", "true" );
    $Config{DoSubmitDetail} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_arch_detail", "true" );
    $Config{DoArchDetail} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_ckpt", "true" );
    $Config{DoCheckpoint} = ( $Tmp =~ /true/i ) ? 1 : 0;

    $Tmp = HawkeyeLib::ReadConfig( "_fping_exe", "" );
    $Config{FpingExe} = $Tmp if ( $Tmp ne "" );

    $Tmp = HawkeyeLib::ReadConfig( "_negotiator_list", "" );
    $Config{NegotiatorList} = $Tmp if ( $Tmp ne "" );

}

# Do the real work here...
sub RunIt()
{
    # Start things off
    $Hash = HawkeyeHash->new( \$Hawkeye, "" );

    # Description?
    if ( $Config{PoolDescription} ne "" )
    {
	$Hash->Add( "Description",
		    HawkeyePublish::TypeString,
		    $Config{PoolDescription} );
	$Hawkeye->StoreIndex( "Description" );
    }

    my $cc = "";
    $cc = $ENV{CONDOR_CONFIG} if ( exists $ENV{CONDOR_CONFIG} );
    print STDERR "CONDOR_CONFIG=$cc\n";
    print STDERR "PATH=" . $ENV{PATH} . "\n";

    my %Collectors;
    CheckCollectors( \%Collectors );
    RunAbsent() if ( $Config{DoAbsent} );
    RunTotal( "", "Sum" ) if ( $Config{DoSummary} );
    RunTotal( "-run", "Run" ) if ( $Config{DoRun} );
    RunSubmit( ) if ( $Config{DoSubmit} );
    RunCheckPoint( ) if ( $Config{DoCheckpoint} );
    RunNegotiator( \%Collectors ) if ( $Config{DoNegotiator} );

    # Ok, summary is done...
    $Hash->Store( );
    $Hawkeye->Publish( );
}

# Read a list from the condor_config
sub GetConfigList( $$ )
{
    my $Param = shift;
    my $PrintErrors = shift;
    my @List;

    # Get the collector list
    my $Cmd = "condor_config_val $Param";
    if ( ! open( CCV, "$Cmd 2>/dev/null |" ) )
    {
	print STDERR "Can't get run '$Cmd'\n" if ( $PrintErrors );
	return @List;
    }

    # Only one line
    $_ = <CCV>;
    close( CCV );

    # Parse it
    if ( ! defined $_ )
    {
	print STDERR "No $Param value\n" if ( $PrintErrors );
	return @List;
    }
    chomp;
    s/^\s+//g;
    if ( length == 0 )
    {
	print STDERR "No $Param list\n" if ( $PrintErrors );
	return @List;
    }

    # Parse it
    @List = split( /[,\s]+/, $_ );
    foreach my $Off ( 0 .. $#List )
    {
	$List[$Off] =~ s/:\d+//;
    }

    # Done
    return @List;
}

# Gather collector info
sub CheckCollectors( $ )
{
    my $Collectors = shift;

    my $Publish = $Config{DoCollectors};

    my @ConfigList = GetConfigList( "collector_host", 1 );
    if ( $Publish )
    {
	$Hash->Add( "Collectors_Count", HawkeyePublish::TypeNumber,
		    scalar( @ConfigList ) );
    }

    my @ConfigNames;
    foreach my $Host ( @ConfigList )
    {
	my $Name = $Host;
	$Name =~ s/\..*$//;
	my $r = ();
	$r->{ConfigName} = $Host;
	$Collectors->{$Name} = $r;
	if ( $Publish )
	{
	    $Hash->Add( "Collectors__".$Name."_ConfigName",
			HawkeyePublish::TypeString,
			$Host )
	}
    }
    if ( $Publish )
    {
	$Hash->Add( "Collectors_Index", HawkeyePublish::TypeString,
		    join( ",", sort keys %{$Collectors} ) )
    }

    # Query each collector
    foreach my $Name( keys %{$Collectors} )
    {
	my $Collector = $Collectors->{$Name};
	my $ConfigHost = $Collector->{ConfigName};
	my $Cmd = "condor_status -pool $ConfigHost -any";
	if ( ! open( STATUS, "$Cmd|" ) )
	{
	    print STDERR "Can't query collector '$ConfigHost'\n";
	    return;
	}
	my %AdTypes;
	my $NumAds = 0;
	while( <STATUS> )
	{
	    chomp;
	    s/^\s+//g;
	    if ( /(\w+)\s+(\w+)\s+(\S+)/ )
	    {
		my $Type = $1;
		next if ( $Type eq "MyType" );
		$AdTypes{$Type} = 0 if ( ! exists $AdTypes{$Type} );
		$AdTypes{$Type}++;
		$NumAds++;
	    }
	}
	close( STATUS );
	if ( $Publish )
	{
	    $Hash->Add( "Collectors__".$Name."_NumAds",
			HawkeyePublish::TypeNumber,
			$NumAds )
	}
	$Collector->{NumAds} = $NumAds;
	$Collector->{AdTypes} = %AdTypes;
	next if ( ! $NumAds );

	if ( $Publish )
	{
	    $Hash->Add( "Collectors__".$Name."_Ads_Index",
			HawkeyePublish::TypeString,
			join( ",", sort(keys %AdTypes))  );
	    foreach my $Type ( keys %AdTypes )
	    {
		$Hash->Add( "Collectors__".$Name."_Ads_$Type",
			    HawkeyePublish::TypeNumber,
			    $AdTypes{$Type} );
	    }
	}
    }
}

# Do the real work here...
sub RunAbsent()
{

    # Lists
    my @ClusterAbsent;
    my @ClusterDown;
    my @ClusterKnownDown;
    my @Absent;
    my @NotDown;

    # Now, run condor_status and gather some more info...
    push( @{$Config{AbsentOptions}}, "--absent" );
    push( @{$Config{AbsentOptions}}, "--no-rcfile" );
    push( @{$Config{AbsentOptions}}, "--script" );
    push( @{$Config{AbsentOptions}}, "--ping" );
    push( @{$Config{AbsentOptions}}, "--pingok" );
    push( @{$Config{AbsentOptions}}, "--bygroup" );

    # Configure it to use fping
    if ( $Config{FpingExe} ne "" )
    {
	push( @{$Config{AbsentOptions}}, "--fping=$Config{FpingExe}" )
    }
    else
    {
	push( @{$Config{AbsentOptions}}, "--fping" )
    }

    # Now, form the final command & run it...
    my $Cmd = "$Config{PoolProg} $Config{PoolName} ".
	  join( " ", @{$Config{AbsentOptions}} );
    print STDERR "Running '$Cmd'\n";
    if ( ! open( ABSENT, "$Cmd|" ) )
    {
	print STDERR "Can't get absent info\n";
	return;
    }

    # Parse the output...
    my $Section;
    my $Cluster;
    my $Count;
    while ( <ABSENT> )
    {
	chomp;

	my ( $Attr, @Values );
	if ( /^(\w+)=(.*)/ )
	{
	    $Attr = $1;
	    @Values = split( /\,/, $2 );
	}
	else
	{
	    next;
	}

	# What have we got?
	if ( $Attr eq "Section" )
	{
	    $Section = shift @Values;
	    $Cluster = shift @Values;
	    $Count = shift @Values;
	    next;
	}
	# Listed node
	elsif ( $Attr eq "Node" )
	{
	    my $Host = shift @Values;
	    if ( $Section eq "Absent" )
	    {
		if ( $Cluster eq "<NONE>" )
		{
		    push( @Absent, $Host );
		}
		else
		{
		    push( @ClusterAbsent, "$Host" );
		}
	    }
	    elsif ( $Section eq "MarkedDown" )
	    {
		if ( $Cluster ne "<NONE>" && $Cluster ne "<ALL>" )
		{
		    push @ClusterKnownDown, $Host;
		}
	    }
	    elsif ( $Section eq "DownPingOk" )
	    {
		push @NotDown, $Host;
	    }
	    elsif ( $Section eq "Down" )
	    {
		if ( $Cluster ne "<NONE>" && $Cluster ne "<ALL>" )
		{
		    push( @ClusterDown, $1 );
		}
	    }
	}
	elsif ( $Attr eq "Roster" )
	{
	    $Hash->Add( "Count", HawkeyePublish::TypeNumber, shift @Values );
	    $Hawkeye->StoreIndex( "Count" );
	}
	elsif ( $Attr =~ /^Cluster(\w+)$/ )
	{
	    my $What = $1;
	    $Hash->Add( "Cluster_$What", HawkeyePublish::TypeAuto,
			shift @Values );
	    $Hawkeye->StoreIndex( "Cluster_$What" );
	}
	elsif ( $Attr eq /NodesAbsent/ )
	{
	    $Hash->Add( "Absent_Count", HawkeyePublish::TypeNumber,
			shift @Values );
	    $Hawkeye->StoreIndex( "Absent_Count" );
	}
	elsif ( $Attr eq "Description" )
	{
	    $Hash->Add( "Description", HawkeyePublish::TypeString,
			shift @Values );
	    $Hawkeye->StoreIndex( "Description" );
	}
    }
    close( ABSENT );

    # Cluster Down list
    $Hash->Add( "Cluster_AbsentList",
		HawkeyePublish::TypeString,
		join( " ", sort @ClusterAbsent ) );
    $Hawkeye->StoreIndex( "Cluster_AbsentList" );

    # Cluster Down list
    $Hash->Add( "Cluster_DownList",
		HawkeyePublish::TypeString,
		join( " ", sort @ClusterDown ) );
    $Hawkeye->StoreIndex( "Cluster_DownList" );

    # Cluster Down list
    $Hash->Add( "Cluster_KnownDownList",
		HawkeyePublish::TypeString,
		join( " ", sort @ClusterKnownDown ) );
    $Hawkeye->StoreIndex( "Cluster_KnownDownList" );
    $Hash->Add( "Cluster_KnownDownCount",
		HawkeyePublish::TypeNumber,
		scalar @ClusterKnownDown );
    $Hawkeye->StoreIndex( "Cluster_KnownDownCount" );

    # Absent list
    $Hash->Add( "Absent_List",
		HawkeyePublish::TypeString,
		join( " ", sort @Absent ) );
    $Hawkeye->StoreIndex( "Absent_List" );

    # Number of nodes listed as down, but ping ok
    $Hash->Add( "Down_PingOkCount",
		HawkeyePublish::TypeNumber,
		scalar @NotDown );
    $Hawkeye->StoreIndex( "Down_PingOkCount" );
    $Hash->Add( "Down_PingOkList",
		HawkeyePublish::TypeString,
		join( " ", sort @NotDown ) );
    $Hawkeye->StoreIndex( "Down_PingOkList" );
}


# Run condor_status -total -run & parse it's output..
sub RunTotal( $$ )
{
    my $Parms = shift;
    my $Prefix = shift;

    # Run it..
    my $Cmd = "condor_status -total $Parms";
    if ( ! open( TOTAL, "$Cmd|" ) )
    {
	print STDERR "Can't get total info\n";
	return;
    }

    # Add it to the prefix list
    $Hawkeye->StoreIndex( $Prefix );

    # Add an _ to it..
    $Prefix .= "_";

    # "Global" info..
    my @FieldNames;

    # Parse the output...
    my @ArchList;
    while ( <TOTAL> )
    {
	chomp;
	s/^\s+//g;
	next if ( $_ eq "" );

	# Header line?
	if ( /Machines/ )
	{
	    @FieldNames = split( /\s+/, $_ );
	}
	elsif ( ! /omit/i )
	{
	    my ( $Arch, @Values ) = split;

	    next if (  (! $Config{DoArchDetail}) && ( $Arch ne "Total" )  );
	    push @ArchList, $Arch;
	    foreach my $Field ( 0 .. $#Values )
	    {
		my $Attr = $Prefix . $Arch . "_" . $FieldNames[$Field];
		$Hash->Add( $Attr, HawkeyePublish::TypeAuto, $Values[$Field] );
	    }
	}
    }
    $Hash->Add( $Prefix . "ArchList",
		HawkeyePublish::TypeString,
		join( " ", @ArchList ) )
	if ( $#ArchList >= 0 );

    # Done
    close( TOTAL );
}

# Run condor_status -sub & parse it's output..
sub RunSubmit(  )
{
    # Run it..
    my @Fields =
	(
	 { Format => "%s",
	   Type => HawkeyePublish::TypeString,
	   Name => "Name",
	   Munge => 1 },
	 { Format => "%s",
	   Type => HawkeyePublish::TypeString,
	   Name => "Machine",
	   Munge => 0 },
	 { Format => "%d",
	   Type => HawkeyePublish::TypeNumber,
	   Name => "RunningJobs",
	   Munge => 0 },
	 { Format => "%d",
	   Type => HawkeyePublish::TypeNumber,
	   Name => "IdleJobs",
	   Munge => 0 },
	 { Format => "%d",
	   Type => HawkeyePublish::TypeNumber,
	   Name => "HeldJobs",
	   Munge => 0 },
	);

    my $Cmd = "condor_status -submit";
    foreach my $Field ( @Fields )
    {
	$Cmd = $Cmd .
	    " -format \"" . $Field->{Format} . " \" " . $Field->{Name};
    }
    $Cmd = $Cmd . " -format \"\\n\" Name";
    if ( ! open( SUB, "$Cmd|" ) )
    {
	print STDERR "Can't get submit info\n";
	return;
    }

    # Parse the output...
    my $SubmitNo = 0;
    my %Totals;
    while ( <SUB> )
    {
	chomp;
	next if ( $_ eq "" );
	$SubmitNo++;

	# Split it up, crunch it out
	my @Values = split;
	warn "Wrong data size ($_) : " if ( $#Values != $#Fields );
	for my $FieldNo ( 0 .. $#Fields )
	{
	    my $Name = $Fields[$FieldNo]->{Name};
	    my $Attr = sprintf "Submit_%02d_%s", $SubmitNo, $Name;

	    # Munge the email address to deter spambots
	    if (  ( $Fields[$FieldNo]->{Munge} ) &&
		  ( $Config{DoSubmitMunge} )  )
	    {
		$Values[$FieldNo] =~ s/@/ at /;
	    }

	    # Finally, publish it
	    $Hash->Add( $Attr, $Fields[$FieldNo]->{Type}, $Values[$FieldNo] )
		if ( $Config{DoSubmitDetail} );

	    # If it's a number, sum it up
	    if ( $Fields[$FieldNo]->{Type} eq HawkeyePublish::TypeNumber )
	    {
		$Totals{$Name} = 0 if ( ! exists $Totals{$Name} );
		$Totals{$Name} += $Values[$FieldNo];
	    }
	}
    }
    $Hash->Add( "Submit_Count", HawkeyePublish::TypeNumber, $SubmitNo );
    foreach my $Name ( keys %Totals )
    {
	$Hash->Add( "Submit_Total_$Name",
		    HawkeyePublish::TypeNumber,
		    $Totals{$Name} );
    }

    # Done
    close( SUB );
}

# Run condor_status -ckpt & parse it's output..
sub RunCheckPoint(  )
{
    my $Cmd = "condor_status -ckpt";
    if ( ! open( CKPT, "$Cmd|" ) )
    {
	print STDERR "Can't get checkpoint info\n";
	return;
    }

    # Checkpoint server hash
    my %Servers;

    # Parse the output...
    while ( <CKPT> )
    {
	s/^\s+//g;
	chomp;
	next if ( $_ eq "" );

	# Split it up, crunch it out
	my @Values = split;

	# Total line
	if ( $Values[0] eq "Total" )
	{
	    $Hash->Add( "Ckpt_Total_Servers", HawkeyePublish::TypeNumber,
			$Values[1] );
	    $Hash->Add( "Ckpt_Total_AvailDisk", HawkeyePublish::TypeNumber,
			$Values[2] );
	}
	# Server line
	elsif ( $Values[1] =~ /^\d+$/ )
	{
	    my $Fqdn = $Values[0];
	    my $Name = $Fqdn;

	    # Build the logical name, check for conflicts..
	    $Name =~ s/^(\w+).*/$1/;
	    $Name = $Fqdn if ( exists ( $Servers{$Name} ) );
	    while ( exists ( $Servers{$Name} ) )
	    {
		$Name .= "_";
	    }

	    # Stuff it in the hash...
	    $Servers{$Name}{FullName} = $Fqdn;
	    $Servers{$Name}{AvailDisk} = $Values[1];
	    $Servers{$Name}{Subnet} = $Values[2];
	}
    }

    # Publish it all...
    my %Types = ( FullName => HawkeyePublish::TypeString,
		  AvailDisk => HawkeyePublish::TypeNumber,
		  Subnet => HawkeyePublish::TypeString, );
    foreach my $Server ( keys %Servers )
    {
	foreach my $Field ( keys %{$Servers{$Server}} )
	{
	    my $Type = HawkeyePublish::TypeAuto;
	    $Type = $Types{$Field} if ( exists $Types{$Field} );
	    $Hash->Add( "Ckpt_" . $Server . "_" . $Field, $Type,
			$Servers{$Server}{$Field} );
	}
    }

    # And, the server list
    $Hash->Add( "Ckpt_Servers", HawkeyePublish::TypeString,
		join( " ", keys %Servers  ) );

    # Done
    close( CKPT );
}

sub GetCanonicalList( @ )
{
    my @List;
    foreach my $Off ( 0 .. $#_ )
    {
	my $Host = $_[$Off];
	my $Cmd = "host $Host";
	if ( !open( HOST, "$Cmd|" ) )
	{
	    print STDERR "Can't run '$Cmd'\n";
	}
	else
	{
	    while( <HOST> )
	    {
		chomp;
		if ( /is an alias for ([\w\.]+)/ )
		{
		    $Host = $1;
		    $Host =~ s/\.$//;
		}
	    }
	    close( HOST );
	}
	push( @List, $Host );
    }
    return @List;
}

# Run condor_status -negotiator & parse it's output..
sub RunNegotiator( $ )
{
    my $Collectors = shift;

    # Significant attributes
    my %SigAttrs = ( Name => HawkeyePublish::TypeString,
		     NegotiatorIpAddr => HawkeyePublish::TypeString,
		     CondorVersion => HawkeyePublish::TypeString,
		     DaemonStartTime => HawkeyePublish::TypeNumber,,
		   );

    # Get the negotiator list
    my @NegoList;
    if ( $Config{NegotiatorList} eq "" )
    {
	@NegoList = split( /,\s/, $Config{NegotiatorList} );
    }
    if ( ! scalar @NegoList )
    {
	@NegoList = GetConfigList( "negotiator_host", 0 );
    }
    if ( ! scalar @NegoList )
    {
	@NegoList = GetConfigList( "had_list", 0 );
    }
    if ( ! scalar @NegoList )
    {
	@NegoList = GetConfigList( "collector_host", 0 );
    }
    my @CanonList = GetCanonicalList( @NegoList );

    $Hash->Add( "Negotiators_ConfigList",
		HawkeyePublish::TypeString,
		join( ",", @NegoList ) );
    $Hash->Add( "Negotiators_CanonicalList",
		HawkeyePublish::TypeString,
		join( ",", @CanonList ) );
    $Hash->Add( "Negotiators_Primary",
		HawkeyePublish::TypeString,
		$CanonList[0] );

    my %Negotiators;
    my %NegoNames;
    foreach my $Collector ( "", keys %{$Collectors} )
    {
	# Run it..
	my $Cmd = "condor_status -negotiator -l";
	$Cmd .= " -pool $Collector" if ( $Collector ne "" );
	if ( ! open( STATUS, "$Cmd|" ) )
	{
	    print STDERR "Can't get negotiator info\n";
	    return;
	}

	# Parse the output...
	my @CollectorNegotiators;
	my %Attrs;
	my $Name = "";
	while ( <STATUS> )
	{
	    chomp;
	    s/^\s+//g;
	    if ( /(\S+)\s*=\s*(.*)$/ )
	    {
		my $Attr = $1;
		my $Value = $2;
		if ( $Value =~ /^\"(.*)\"$/ )
		{
		    $Value = $1;
		}
		if ( exists $SigAttrs{$Attr} )
		{
		    $Attrs{$Attr} = $Value;
		    $Name = $Value if ( $Attr eq "Name" );
		}
		next;
	    }

	    # Not an "attr = value" line

	    # No name; clear it all & start over
	    if ( $Name eq "" )
	    {
		foreach my $Attr ( keys %Attrs )
		{
		    delete( $Attrs{$Attr} );
		}
		next;
	    }

	    # Got a real name
	    my $FullName = $Name;
	    $Name =~ s/\..*$//;
	    $NegoNames{$Name} = $FullName;

	    # Handle the default collector case
	    if ( $Collector eq "" )
	    {
		foreach my $Attr ( keys %Attrs )
		{
		    $Hash->Add( "Negotiators__".$Name."_".$Attr,
				$SigAttrs{$Attr},
				$Attrs{$Attr} );
		}
	    }
	    else
	    {
		push( @{$Negotiators{$Name}}, $Collector );
		push( @CollectorNegotiators, $Name );
		if ( $Config{DoCollectors} )
		{
		    my $BaseAttr = 
			"Collectors__".$Collector."_Negotiators_".$Name."_";
		    foreach my $Attr ( keys %Attrs )
		    {
			$Hash->Add( $BaseAttr . $Attr,
				    $SigAttrs{$Attr},
				    $Attrs{$Attr} );
		    }
		}
	    }

	    # Clear the attr hash
	    foreach my $Attr ( keys %Attrs )
	    {
		delete( $Attrs{$Attr} );
	    }

	    if ( ( $Collector ne "" ) && ( $Config{DoCollectors} ) )
	    {
		$Hash->Add( "Collectors__".$Collector."_Negotiators_Index",
			    HawkeyePublish::TypeString,
			    join( ",", @CollectorNegotiators ) );
		$Hash->Add( "Collectors__".$Collector."_Negotiators_Count",
			    HawkeyePublish::TypeNumber,
			    scalar @CollectorNegotiators );
	    }
	    $Name = "";
	}
	close( STATUS );
    }

    my $Count = 0;
    foreach my $Nego ( keys %Negotiators )
    {
	$Count++;
	$Hash->Add( "Negotiators__".$Nego."_Collectors",
		    HawkeyePublish::TypeString,
		    join( ",", sort @{$Negotiators{$Nego}} ) );
    }

    $Hash->Add( "Negotiators_Index",
		HawkeyePublish::TypeString,
		join( ",", sort keys %Negotiators ) );
    $Hash->Add( "Negotiators_Count",
		HawkeyePublish::TypeNumber,
		$Count );

    # If there is more than one negotiator, publish "UNKNOWN", otherwise,
    # publish the name of the one we have.
    my @List = keys %NegoNames;
    my $Current = "UNKNOWN";
    $Current = $NegoNames{$List[0]} if ( scalar @List == 1 );
    $Hash->Add( "Negotiators_Current",
		HawkeyePublish::TypeString,
		$Current );
}
