#!/usr/bin/perl

use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
use Text::Diff;
use Term::Cap;
use Time::Local;

$| = 1;

my $THIS_FILE           =  ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory   =  ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
	$running_directory =~ s/^\./$ENV{PWD}/;
}

my $anvil = Anvil::Tools->new();

# Get a list of all interfaces with IP addresses.
$anvil->Get->switches({debug => 3, list => []});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});

$anvil->Database->connect;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"});
if (not $anvil->data->{sys}{database}{connections})
{
	# No databases, exit.
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, secure => 0, key => "error_0003"});
	$anvil->nice_exit({exit_code => 1});
}

my $t = Term::Cap->Tgetent;

print "Checking the state of all known machines. Please be patient.\n";

$anvil->Database->get_hosts();
foreach my $show_host_type ("striker", "node", "dr")
{
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { show_host_type => $show_host_type }});
	if ($show_host_type eq "striker")
	{
		print "-=] Striker Dashboards;\n";
	}
	elsif ($show_host_type eq "node")
	{
		print "\n-=] Anvil! sub-nodes;\n";
	}
	elsif ($show_host_type eq "dr")
	{
		print "\n-=] DR Hosts\n";
	}
	foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}})
	{
		my $host_uuid       = $anvil->data->{sys}{hosts}{by_name}{$host_name};
		my $host_type       = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type};
		my $short_host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{short_host_name};
		my $host_ipmi       = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_ipmi};
		my $access          = "";
		my $say_uptime      = "";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:host_name'       => $host_name, 
			's2:short_host_name' => $short_host_name, 
			's3:host_uuid'       => $host_uuid, 
			's4:host_type'       => $host_type, 
			's5:host_ipmi'       => $anvil->Log->is_secure($host_ipmi), 
		}});
		next if $host_type ne $show_host_type;
		next if $host_uuid eq $anvil->Get->host_uuid;
		
		if (not $host_ipmi)
		{
			# Sometimes the host_ipmi gets wiped. Can we find older entries? 
			my $tested = {};
			my $query  = "SELECT host_ipmi FROM history.hosts WHERE host_uuid = ".$anvil->Database->quote($host_uuid)." AND host_ipmi != '' ORDER BY modified_date DESC;";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
			my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
			my $count   = @{$results};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				results => $results, 
				count   => $count, 
			}});
			foreach my $row (@{$results})
			{
				my $this_host_ipmi = $row->[0];
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { this_host_ipmi => $this_host_ipmi }});
				
				if (exists $tested->{$this_host_ipmi})
				{
					next;
				}
				$tested->{$this_host_ipmi} = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { "tested->{".$this_host_ipmi."}" => $tested->{$this_host_ipmi} }});
				
				my $shell_call = $this_host_ipmi." -o status";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { this_host_ipmi => $this_host_ipmi }});
				
				my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, secure => 1});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					output      => $output, 
					return_code => $return_code,
				}});
				
				if (($return_code eq "0") or ($return_code eq "2"))
				{
					# Good IPMI, use it.
					$host_ipmi = $this_host_ipmi;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { host_ipmi => $host_ipmi }});
					
					# Update the database. 
					my $query = "
UPDATE 
    hosts 
SET 
    host_ipmi     = ".$anvil->Database->quote($host_ipmi).", 
    modified_date = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)." 
WHERE
    host_uuid     = ".$anvil->Database->quote($host_uuid)."
;";
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
					$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
				}
				last if $host_ipmi;
			}
		}
		
		my $matches = $anvil->Network->find_access({
			debug  => 2,
			target => $host_name, 
		});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { matches => $matches }});
		foreach my $network_name (sort {$a cmp $b} keys %{$anvil->data->{network_access}})
		{
			my $target_ip   = $anvil->data->{network_access}{$network_name}{target_ip_address};
			my $test_access = $anvil->Remote->test_access({target => $target_ip});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				's1:network_name' => $network_name, 
				's2:target_ip'    => $target_ip, 
				's3:test_access'  => $test_access, 
			}});
			
			if ($test_access)
			{
				# We're good.
				   $access     = 1;
				my $uptime     = $anvil->Get->uptime({debug => 2, target => $target_ip});
				   $say_uptime = $anvil->Convert->time({
					debug     => 2,
					'time'    => $uptime ? $uptime : 0,
					translate => 1,
					long      => 1,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					's1:access'     => $access, 
					's2:uptime'     => $uptime, 
					's3:say_uptime' => $say_uptime, 
				}});
				last;
			}
		}
		
		if ($access)
		{
			print $short_host_name." is up and has been running for: [".$say_uptime."]\n";
		}
		else
		{
			# Can we check the power using IPMI?
			if ($host_ipmi)
			{
				my $shell_call = $host_ipmi." -o status";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { shell_call => $shell_call }});
				
				my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, secure => 1});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					output      => $output, 
					return_code => $return_code,
				}});
				
				if ($return_code eq "0")
				{
					# The machine is already on
					print "The machine: [".$short_host_name."] appears to be powered ON, but we can't reach it. Is it booting?\n";
				}
				elsif ($return_code eq "1")
				{
					# Unable to connect to the fence device.
					print "The machine: [".$short_host_name."] can not be reached, and we can't check it's power status either. Has it been completely powered off?\n";
				}
				elsif ($return_code eq "2")
				{
					# The machine is off, try to start it.
					print "The machine: [".$short_host_name."] is confirmed to be powered OFF.\n";
				}
			}
			else
			{
				print "The machine: [".$short_host_name."] appears to be offline, and doesn't appear to have out-of-band management to check the power status.\n";
			}
		}
	}
}
print "\n Done!\n";

$anvil->nice_exit({exit_code => 0});
