#!/opt/star/bin/perl
###########
# ganglia_ip.pl: Connect to a GMOND or GMETAD and get their XML output.
# Author: Stratos Efstathiadis (stratos@bnl.gov)
###########
use IO::Socket;
use XML::Parser;
use Getopt::Long;
my $parser = new XML::Parser( Style => "Subs" );
&ProcessArgs;
if ( ! ($socket = IO::Socket::INET->new(Proto=>"tcp", PeerAddr=>"$host", PeerPort=>"$port"))) {
    $msg="Can't open socket to host=$host and port=$port: $! \n";
    print $msg;
    exit;
}
$parser->parse($socket);
sub ProcessArgs {
    GetOptions("hostname=s" => \$host, "port=i" => \$port, "output=s" => \$output);
    die  <<EOF
>>>>>> You must specify a HOST using the -host=<host> option.
>>>>>> Example: -host=ganglia01.bnl.gov\n
EOF
    unless defined $host;
    die <<EOF2
>>>>>> You must specify a PORT using the -port=<port> option, where <port> is an integer number.
>>>>>> Example: -port=8649\n
EOF2
    unless defined $port;
    die <<EOF3
>>>>>> You must specify an output using the -output=<output>, where <output> can be mds or debug.
>>>>>> Example: -output=mds
>>>>>> Example: -output=debug
EOF3
    unless defined $output;
    if ( lc ($output) =~ /mds/) {
#        print "Output will be in MDS format\n";
    } elsif ( lc ($output) =~ /debug/) {
#          print "Output will be in Debug format\n";
    } else {
        die " Currently the only available options for output are MDS or DEBUG.\n";
    }
}
sub GRID {
    my ($expat, $element, %attrs) = @_;
    my(%grid);
    if (%attrs) {
       $grid{NAME}= $attrs{NAME};
    }
# Create Reference ($rclusters) to clusters Array; This is where all clusters are stored.
# At this point the array @clusters is empty and thus $rclusters point to an empty array.
    $rclusters = \@clusters;
}
sub CLUSTER {
    my ($expat, $element, %attrs) = @_;
    my(%cluster);
    if (%attrs) {
      $cluster{OWNER} = $attrs{OWNER};
      $cluster{NAME} = $attrs{NAME};
    }
    $rcluster = \%cluster;
    my(@hosts);
    $rhosts = \@hosts;
}
sub HOST {
    my ($expat, $element, %attrs) = @_;
    my(%host);
    if (%attrs) {
        $host{NAME} = $attrs{NAME};
        $host{IP}   = $attrs{IP};
    }
# Create a Reference to hast %host.
# The hash %host will run out of scope at the end of the subroutine
# but the reference to %host ($rhost) will still exist.
    $rhost = \%host; # Reference to Hash host
}
sub HOST_ {
    push ( @$rhosts,$rhost );
}
sub METRIC {
    my ($expat, $element, %attrs) = @_;
    $$rhost{$attrs{NAME}}=$attrs{VAL};
}
sub CLUSTER_ {
    $$rcluster{HOSTS}=$rhosts;
    $l = $$rcluster{HOSTS};
# If reference $rclusters is defined, then we have a GRID Element in the XML
# and thus we are quering an GMETAD Server. We should push the info
# for each cluster in @$rclusters
    if (defined $rclusters) {
        push (@$rclusters, $rcluster);
    } elsif ( lc ($output) =~ /debug/) {
       &Debug_output($rcluster);
    } elsif ( lc ($output) =~ /mds/) {
       &Mds_Header_output;
       &Mds_output($rcluster);
    }
}
sub GRID_ {
    my ($i);
    if ( lc ($output) =~ /mds/) {
        &Mds_Header_output;
    }
    foreach $i (@$rclusters) {
       if( lc ($output) =~ /debug/) {
           &Debug_output($i);
       } elsif ( lc ($output) =~ /mds/) {
           &Mds_output($i);
       }
    }
}
sub Debug_output {
    my($ref) = shift @_;
    my($ref_array)=$$ref{HOSTS};
    my($hostname);
    foreach $i (@$ref_array) {
      $hostname = $i->{NAME};
      print "========== HOSTNAME: $hostname \n";
      while ( my( $key, $value) = each (%$i) ) {
         print "\t$key ==> $value \n";
      }
   }
}
sub Mds_Header_output {
 print <<Mds_Header;
dn: mds-vo-name=local, o=grid
objectclass: GlueTop
objectclass: GlueGeneralTop
GlueSchemaVersionMajor: 1
GlueSchemaVersionMinor: 0
Mds_Header
}
sub Mds_output {
    my($ref) = shift @_;
    my($ref_array)=$$ref{HOSTS};
    my($cluster_name) =$$ref{NAME};
    $cluster_name =~ s/ /_/g;
    my($cluster_owner) = $$ref{OWNER};
    $cluster_owner =~ s/ /_/g;
    my($Id)=join("-",$cluster_name,$cluster_owner);
    $os_name = "unspecified";
    $os_release = "unspecified";
    $os_version = "unspecified";
print <<EOF4;
dn: cl=$cluster_name, mds-vo-name=local, o=grid
objectclass: GlueClusterTop
objectclass: GlueCluster
GlueClusterName: $cluster_name
GlueClusterUniqueID: $Id
GlueClusterService: compute
dn: scl=sub1, cl=$cluster_name, mds-vo-name=local, o=grid
objectclass: GlueClusterTop
objectclass: GlueSubCluster
GlueSubClusterName: sub1
objectclass: GlueHostOperatingSystem
GlueHostOperatingSystemName: unspecified
GlueHostOperatingSystemRelease: $os_release
GlueHostOperatingSystemVersion: $os_version
EOF4
    my $i;
    foreach $i (@$ref_array) {
       $hostname = $i->{NAME};
       $UniqueId = join("-",$Id,$hostname);
       $load_one = $i->{load_one};
       $load_five = $i->{load_five};
       $load_fifteen = $i->{load_fifteen};
       $cpu_num = $i->{cpu_num};
       $cpu_speed = $i->{cpu_speed};
       $IP = $i->{IP};
       $mem_total = $i->{mem_total};
       $mem_free  = $i->{mem_free};
print <<EOF5;
dn: host=$hostname, cl=$cluster_name, mds-vo-name=local, o=grid
objectclass: GlueHost
GlueHostName: $hostname
GlueHostUniqueID: $UniqueId
objectclass: GlueHostProcessorLoad
GlueHostProcessorLoadLast1Min: $load_one
GlueHostProcessorLoadLast5Min: $load_five
GlueHostProcessorLoadLast15Min: $load_fifteen
objectclass: GlueHostSMPLoad
GlueHostSMPLoadLast1Min: $load_one
GlueHostSMPLoadLast5Min: $load_five
GlueHostSMPLoadLast15Min: $load_fifteen
objectclass: GlueHostArchitecture
GlueHostArchitectureSMPSize: $cpu_num
objectclass: GlueHostProcessor
GlueHostProcessorClockSpeed: $cpu_speed
objectclass: GlueHostNetworkAdapter
GlueHostNetworkAdapterName: $hostname
GlueHostNetworkAdapterIPAddress: $IP
objectclass: GlueHostMainMemory
GlueHostMainMemoryRAMSize: $mem_total
GlueHostMainMemoryRAMAvailable: $mem_free
EOF5
   }
}
=pod
=head1 NAME
ganglia_ip.pl --  A Ganglia MDS Information Provider in Perl.
=head1 DESCRIPTION
ganglia_ip.pl opens a socket to a GMETAD or GMOND (depending on the Port that you
specify in the command line). It uses XML::Parse to parse the XML input (from GMETAD
or GMOND) and produces Mds or Debug output.
=head1 SYNOPSIS
ganglia_ip.pl --host=<hostname> --port=<port> --out=<Debug or mds>
=head1 COMMAND LINE OPTIONS
The following command line options are available:
=over 4
=item HOST
Specify the Host you will connect to
=item PORT
Specify the Port you will connect to
=item OUTPUT
Specify the output format. Currently there are only two choices: debug or MDS
=back
=head1 EXAMPLES
Connect to Gmond (the output will be in MDS format)
=begin html
<font face="" size=+1 color=red>
ganglia_ip.pl --host=blc010.bnl.gov --port=8649 --output=mds
</font>
=end html
Connect to gmetad (the output will be in MDS format)
=begin html
<font face="" size=+1 color=red>
ganglia_ip.pl --host=blcsw1.bnl.gov --port=8651 --output=mds
</font>
=end html
=head1 REQUIRES
IO::Socket
GetOpt::Long
XML::Parser
=head1 AUTHOR
Stratos Efstathiadis <stratos@bnl.gov>
=pod OSNAMES
=cut