#!/usr/bin/perl
# 
# This keeps an eye on the network configuration and ensures the firewall is configured appropriately. What
# exactly that means depends on why kind of machine the local host is.
# 
# Exit codes;
# 0 = Normal exit.
# 1 = Failed to unlink an unneeded file.
# 2 = Failed to write or update a file.
# 
# TODO:
#   
#   # Allow routing/masq'ing through the IFN1 (provide net access to the BCN)
#   firewall-cmd --zone=IFN1 --add-masquerade
#   # Check
#   firewall-cmd --zone=IFN1 --query-masquerade
#   #[yes|no]
#   # Disable 
#   # NOTE: Doesn't break existing connections
#   firewall-cmd --zone=IFN1 --remove-masquerade
#   

use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
use Text::Diff;


# Disable buffering
$| = 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();

### NOTE: This is called by ocf:alteeve:server, so we only connect to the database if we've got a job-uuid.

# Read switches
$anvil->Get->switches({list => [
	'job-uuid',
	"server"], man => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0115", variables => { program => $THIS_FILE }});

# Log our start.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0134"});

# If we've been passed a job UUID, pick up the details.
if ($anvil->data->{switches}{'job-uuid'})
{
	$anvil->Database->connect({sensitive => 2});
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132" });
	if (not $anvil->data->{sys}{database}{connections})
	{
		# No databases, exit.
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003"});
		$anvil->nice_exit({exit_code => 1});
	}

	$anvil->Job->clear();
	$anvil->Job->get_job_details();
	$anvil->Job->update_progress({
		progress         => 1,
		job_picked_up_by => $$, 
		job_picked_up_at => time, 
		message          => "message_0134", 
	});
	
	if ($anvil->data->{jobs}{job_data} =~ /server=(.*)$/)
	{
		$anvil->data->{switches}{server} = $1 if not $anvil->data->{switches}{server};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			'switches::server' => $anvil->data->{switches}{server},
		}});
	}

}

# If the user has disabled auto-management of the firewall, exit.
if (not $anvil->data->{sys}{manage}{firewall})
{
	# Do nothing.
	$anvil->Job->update_progress({
		progress => 100,
		level    => 2,
		'print'  => 1,
		message  => "log_0670", 
	});
	$anvil->nice_exit({exit_code => 0});
}

### NOTE: Without the DB, we don't use Get->server_from_switch().
if ($anvil->data->{switches}{server})
{
	wait_for_server($anvil);
}

# Check that the firewall ports are open.
$anvil->Network->manage_firewall({
	debug => 2,
	task  => "check", 
});
if ($anvil->data->{switches}{'job-uuid'})
{
	$anvil->Job->update_progress({
		progress => 100,
		message  => "job_0281", 
	});
}

# We're done
$anvil->nice_exit({exit_code => 0});


#############################################################################################################
# Private functions.                                                                                        #
#############################################################################################################

# This simple watches 'virsh list' until the named server appears.
sub wait_for_server
{
	($anvil) = @_;
	
	$anvil->Job->update_progress({
		progress => 25,
		'print'  => 1,
		level    => 2,
		message  => "job_0401,!!server!".$anvil->data->{switches}{server}."!!", 
	});
	my $wait_until  = time + 60;
	my $waiting     = 1;
	my $server_name = $anvil->data->{switches}{server};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		wait_until  => $wait_until,
		server_name => $server_name, 
	}});
	while($waiting)
	{
		# See if the server is running locally, and that both the VNC and websockify ports are 
		# returned.
		$anvil->Server->get_server_ports({debug => 2});
		
		if (exists $anvil->data->{server_ports}{$server_name})
		{
			my $server_state     = $anvil->data->{server_ports}{$server_name}{'state'};
			my $is_running       = $anvil->data->{server_ports}{$server_name}{running};
			my $graphics_type    = $anvil->data->{server_ports}{$server_name}{graphics}{type};
			my $graphics_port    = $anvil->data->{server_ports}{$server_name}{graphics}{port};
			my $websockify_proxy = $anvil->data->{server_ports}{$server_name}{graphics}{ws_proxy};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				server_state     => $server_state,
				is_running       => $is_running, 
				graphics_type    => $graphics_type, 
				graphics_port    => $graphics_port, 
				websockify_proxy => $websockify_proxy, 
			}});
			if (($is_running)    && 
			    ($graphics_port) && 
			    ($websockify_proxy))
			{
				# The server is ready.
				$waiting = 0;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
			}
		}
		
		if ($waiting)
		{
			if (time > $wait_until)
			{
				# timed out 
				$anvil->Job->update_progress({
					progress  => 75,
					'print'   => 1, 
					level     => 2, 
					message   => "job_0402",
					variables => {
						server => $anvil->data->{switches}{server}, 
					},
				});
			}
			else
			{
				sleep 3;
				my $time_left = $wait_until - time;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0403", variables => { 
					server    => $anvil->data->{switches}{server},
					time_left => $time_left, 
				}});
				$anvil->Job->update_progress({
					progress  => 50,
					'print'   => 1, 
					level     => 2, 
					message   => "job_0403", 
					variables => {
						server    => $anvil->data->{switches}{server},
						time_left => $time_left, 
					},
				});
			}
		}
	}
	
	return(0);
}
