#!/usr/bin/perl
# 
# This program will manage servers; Changing RAM, CPU cores, Growing virtual disks, adding virtual disks, 
# inserting and ejecting ISO images into virtual optical media.
# 
# Exit codes;
# 0 = Normal exit.
# 1 = No database connection.
# 
# TODO: 
# 
# USAGE:
# - Show
#   - anvil-manage-server-storage --server srv01-fs37
# 

use strict;
use warnings;
use Anvil::Tools;
require POSIX;
use Term::Cap;
use Text::Diff;
use Data::Dumper;
use Sys::Virt;

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

# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;

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

# Read switches (target ([user@]host[:port]) and the file with the target's password.
$anvil->Get->switches({list => [
	"anvil", 
	"boot-menu", 
	"boot-order", 
	"confirm", 
	"cpu", 
	"job-uuid", 
	"ram", 
	"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 }});

# Connect to the database(s). If we have no connections, we'll proceed anyway as one of the 'run_once' tasks
# is to setup the database server.
$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, update the job, sleep for a bit and then exit. The daemon will pick it up and try 
	# again after we exit.
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0305"});
	sleep 10;
	$anvil->nice_exit({exit_code => 1});
}

if ($anvil->data->{switches}{'job-uuid'})
{
	load_job($anvil);
}

$anvil->Database->get_hosts();
$anvil->Database->get_anvils();
$anvil->Database->get_servers();

if ($anvil->data->{switches}{anvil})
{
	# Make sure they asked for a real anvil.
	$anvil->Get->anvil_from_switch({string => $anvil->data->{switches}{anvil}});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		"switches::anvil_name" => $anvil->data->{switches}{anvil_name},
		"switches::anvil_uuid" => $anvil->data->{switches}{anvil_uuid},
	}});
}

if (not $anvil->data->{switches}{server})
{
	# Show the list of servers.
	show_server_list($anvil);
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0456"});
	$anvil->Job->update_progress({
		progress => 100,
		message  => "error_0456", 
	}) if $anvil->data->{switches}{'job-uuid'};
	$anvil->nice_exit({exit_code => 0});
}

validate_server($anvil);

if ($anvil->data->{switches}{cpu})
{
	manage_cpu($anvil);
}
elsif ($anvil->data->{switches}{ram})
{
	manage_ram($anvil);
}
elsif ($anvil->data->{switches}{'boot-order'})
{
	manage_boot_order($anvil);
}
elsif ($anvil->data->{switches}{'boot-menu'})
{
	manage_boot_menu($anvil);
}
else
{
	show_server_details($anvil, 1);
}


$anvil->Job->update_progress({
	progress => 100,
	message  => "job_0281", 
}) if $anvil->data->{switches}{'job-uuid'};
$anvil->nice_exit({exit_code => 0});


#############################################################################################################
# Functions                                                                                                 #
#############################################################################################################

sub manage_cpu
{
	my ($anvil) = @_;
	
	my $short_host_name  = $anvil->Get->short_host_name;
	my $host_uuid        = $anvil->Get->host_uuid;
	my $server_name      = $anvil->data->{switches}{server_name};
	my $server_uuid      = $anvil->data->{switches}{server_uuid};
	my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
	my $anvil_uuid       = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid};
	my $anvil_name       = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name};
	my $server_host_name = "";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		's1:short_host_name'  => $short_host_name,
		's2:host_uuid'        => $host_uuid,
		's3:server_name'      => $server_name, 
		's4:server_uuid'      => $server_uuid, 
		's5:server_host_uuid' => $server_host_uuid,
		's6:anvil_uuid'       => $anvil_uuid, 
		's7:anvil_name'       => $anvil_name,
	}});
	
	my $from_source  = get_definition_source($anvil);
	my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		from_source   => $from_source,
		server_state  => $server_state, 
	}});
	if ($server_state eq "running")
	{
		$server_host_name = $anvil->Database->get_host_from_uuid({
			short     => 1,
			host_uuid => $server_host_uuid,
		});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_host_name => $server_host_name }});
	}
	
	# Get available resources.
	$anvil->Get->available_resources({
		debug      => 2,
		anvil_uuid => $anvil_uuid,
	});
	
	### TODO: Add support for changing the model, fallback, etc, and features 
	my $total_cores    = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{total_cores};
	my $sockets        = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{sockets};
	my $cores          = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{cores};
	my $threads        = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{threads};
	my $model_name     = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{model_name};
	my $model_fallback = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{model_fallback};
	my $match          = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{match};
	my $vendor         = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{vendor};
	my $mode           = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{mode};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		total_cores    => $total_cores, 
		sockets        => $sockets, 
		cores          => $cores, 
		threads        => $threads, 
		model_name     => $model_name, 
		model_fallback => $model_fallback, 
		match          => $match, 
		vendor         => $vendor, 
		mode           => $mode, 
	}});
	foreach my $name (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{feature}})
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
			's1:name'  => $name, 
			's2:value' => $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{feature}{$name},
		}});
	}
	
	# Max cores already accounts for 1 or 2 less than real cores.
	my $host_cores   = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores};
	my $host_threads = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads};
	my $max_cores    = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		's1:host_cores'   => $host_cores, 
		's2:host_threads' => $host_threads,
		's3:max_cores'    => $max_cores,
	}});
	
	my $cpu_buffer = 2;
	if ($host_cores <= 4)
	{
		$cpu_buffer = 1;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cpu_buffer => $cpu_buffer }});
	}
	
	my $recommended_max = $max_cores - $cpu_buffer;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { recommended_max => $recommended_max }});
	
	# Show the user or change?
	if ($anvil->data->{switches}{cpu} eq "#!SET!#")
	{
		# Show the CPU info
		my $variables = { 
			total_cores     => $total_cores,
			sockets         => $sockets, 
			cores           => $cores, 
			threads         => $threads,
			host_cores      => $host_cores, 
			host_threads    => $host_threads, 
			max_cores       => $max_cores, 
			recommended_max => $recommended_max, 
		};
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0380", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "message_0380", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		return(0);
	}
	
	my $new_sockets = 1;
	my $new_cores   = 1;
	my $new_threads = 1;
	if ($anvil->data->{switches}{cpu} =~ /^(\d+),(\d+),(\d+)$/)
	{
		$new_sockets = $1;
		$new_cores   = $2;
		$new_threads = $3;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:new_sockets' => $new_sockets, 
			's2:new_cores'   => $new_cores,
			's3:new_threads' => $new_threads, 
		}});
	}
	elsif ($anvil->data->{switches}{cpu} =~ /^(\d+),(\d+)$/)
	{
		$new_sockets = $1;
		$new_cores   = $2;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:new_sockets' => $new_sockets, 
			's2:new_cores'   => $new_cores,
		}});
	}
	elsif ($anvil->data->{switches}{cpu} =~ /^(\d+)$/)
	{
		$new_cores = $1;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_cores => $new_cores }});
	}
	else
	{
		# Bad formatting.
		my $variables = { requested_cpu => $anvil->data->{switches}{cpu} };
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0476", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "error_0476", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	
	# Is the requested number of cores sane?
	my $requested_cores = $new_sockets * $new_cores * $new_threads;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { requested_cores => $requested_cores }});
	
	if (($requested_cores < 1) or ($requested_cores > $max_cores))
	{
		my $variables = { 
			requested_cores => $requested_cores,
			new_sockets     => $new_sockets,
			new_cores       => $new_cores,
			new_threads     => $new_threads, 
			max_cores       => $max_cores, 
			recommended_max => $recommended_max, 
			host_cores      => $host_cores,
			host_threads    => $host_threads,  
			cpu_buffer      => $cpu_buffer, 
		};
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0477", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "error_0477", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	
	# Still alive?
	my $in_cpu                = 0;
	my $topology_seen         = 0;
	my $topology_line         = "<topology sockets=\"".$new_sockets."\" dies=\"1\" cores=\"".$new_cores."\" threads=\"".$new_threads."\"/>";
	my $new_server_definition = "";
	foreach my $line (split/\n/, $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml})
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
		if ($line =~ /<vcpu/)
		{
			$line =~ s/<vcpu (.*?)>\d+<\/vcpu>/<vcpu $1>$requested_cores<\/vcpu>/;
			$line =~ s/<vcpu>\d+<\/vcpu>/<vcpu>$requested_cores<\/vcpu>/;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
		}
		if ($in_cpu)
		{
			if ($line =~ /<\/cpu>/)
			{
				if (not $topology_seen)
				{
					$new_server_definition .= "    ".$topology_line."\n";
				}
				$in_cpu = 0;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_cpu => $in_cpu }});
			}
			
			if ($line =~ /<topology /)
			{
				$line          = "    ".$topology_line;
				$topology_seen = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { topology_seen => $topology_seen }});
			}
		}
		if ($line =~ /<cpu/)
		{
			# <cpu mode="host-passthrough" check="none" migratable="on"/>
			if ($line =~ /<cpu (.*?)\/>/)
			{
				# Adjust to add topology 
				my $arguments = $1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { arguments => $arguments }});
				
				$new_server_definition .= "  <cpu ".$arguments.">\n";
				$new_server_definition .= "    ".$topology_line."\n";
				$new_server_definition .= "  </cpu>\n";
				next;
			}
			
			$in_cpu = 1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_cpu => $in_cpu }});
		}
		$new_server_definition .= $line."\n";
	}
	
	# Confirm the new definition is valid
	my $problem = $anvil->Server->parse_definition({
		debug      => 2,
		host       => $short_host_name,
		server     => $server_name, 
		source     => "test_xml",
		definition => $new_server_definition, 
	});
	if ($problem)
	{
		# The new definition is bad
		my $variables = { new_server_definition => $new_server_definition };
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0470", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "error_0470", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	
	# Did the user confirm?
	if ($anvil->data->{switches}{'job-uuid'})
	{
		# Running as a job, don't prompt
		$anvil->Job->update_progress({
			progress => 75,
			message  => "job_0480", 
		});
	}
	elsif ($anvil->data->{switches}{confirm})
	{
		# User confirmed
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0150"});
	}
	else
	{
		# Ask the user to confirm.
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0381", variables => {
			old_total_cores => $total_cores, 
			old_sockets     => $sockets, 
			old_cores       => $cores, 
			old_threads     => $threads, 
			new_total_cores => $requested_cores,
			new_sockets     => $new_sockets, 
			new_cores       => $new_cores, 
			new_threads     => $new_threads, 
		}});
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0021"});
		my $answer = <STDIN>;
		chomp $answer;
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "log_0828", variables => { answer => $answer }});
		if ((lc($answer) eq "y") or (lc($answer) eq "yes"))
		{
			# Proceed
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0175"});
		}
		else
		{
			# Abort
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0022"});
			$anvil->nice_exit({exit_code => 0});
		}
	}
	
	# Record the new config. 
	$problem = $anvil->Server->update_definition({
		debug              => 2, 
		server             => $server_uuid,
		new_definition_xml => $new_server_definition, 
	});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
	if ($problem)
	{
		# Failed!
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0471"});
		$anvil->Job->update_progress({
			progress => 100,
			message  => "error_0471", 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	else
	{
		# Done!
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "log_0750"});
		$anvil->Job->update_progress({
			progress => 100,
			message  => "log_0750", 
		}) if $anvil->data->{switches}{'job-uuid'};
	}
	
	return(0);
}

sub manage_ram
{
	my ($anvil) = @_;
	
	my $short_host_name  = $anvil->Get->short_host_name;
	my $host_uuid        = $anvil->Get->host_uuid;
	my $server_name      = $anvil->data->{switches}{server_name};
	my $server_uuid      = $anvil->data->{switches}{server_uuid};
	my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
	my $anvil_uuid       = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid};
	my $anvil_name       = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name};
	my $server_host_name = "";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		's1:short_host_name'  => $short_host_name,
		's2:host_uuid'        => $host_uuid,
		's3:server_name'      => $server_name, 
		's4:server_uuid'      => $server_uuid, 
		's5:server_host_uuid' => $server_host_uuid,
		's6:anvil_uuid'       => $anvil_uuid, 
		's7:anvil_name'       => $anvil_name,
	}});
	
	my $from_source  = get_definition_source($anvil);
	my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		from_source   => $from_source,
		server_state  => $server_state, 
	}});
	if ($server_state eq "running")
	{
		$server_host_name = $anvil->Database->get_host_from_uuid({
			short     => 1,
			host_uuid => $server_host_uuid,
		});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_host_name => $server_host_name }});
	}
	
	# Get available resources.
	$anvil->Get->available_resources({
		debug      => 2,
		anvil_uuid => $anvil_uuid,
	});
	
	# The RAM In the database is stored in KiB
	my $server_ram_in_use     = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_ram_in_use};
	my $server_configured_ram = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_configured_ram};
	my $server_definition_ram = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{memory};
	my $available_ram         = $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available};
	my $max_ram               = $available_ram + $server_configured_ram;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		server_ram_in_use     => $server_ram_in_use." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $server_ram_in_use}).")",
		server_configured_ram => $server_configured_ram." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $server_configured_ram}).")",
		server_definition_ram => $server_definition_ram." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $server_definition_ram}).")",
		available_ram         => $available_ram." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $available_ram}).")",
		max_ram               => $max_ram." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_ram}).")",
	}});
	if ($anvil->data->{switches}{ram} eq "#!SET!#")
	{
		my $variables = { 
			ram_in_use     => $anvil->Convert->bytes_to_human_readable({'bytes' => $server_ram_in_use}),
			configured_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $server_configured_ram}), 
			max_ram        => $anvil->Convert->bytes_to_human_readable({'bytes' => $max_ram}), 
			available_ram  => $anvil->Convert->bytes_to_human_readable({'bytes' => $available_ram}), 
		};
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0378", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "message_0378", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		return(0);
	}
	
	# Is the requested RAM valid?
	my $requested_ram = $anvil->Convert->human_readable_to_bytes({
		debug => 2,
		size  => $anvil->data->{switches}{ram},
	});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { requested_ram => $requested_ram }});
	if ($requested_ram eq "!!error!!")
	{
		my $variables = { requested_ram => $anvil->data->{switches}{ram} };
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0473", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "error_0473", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	elsif ($requested_ram > $max_ram)
	{
		my $variables = { 
			requested_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $requested_ram}),
			max_ram       => $anvil->Convert->bytes_to_human_readable({'bytes' => $max_ram}), 
		};
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0474", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "error_0474", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	elsif ($requested_ram < 655360)
	{
		my $variables = { requested_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $requested_ram}) };
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0475", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "error_0475", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	
	# Convert the requested bytes to KiB
	my $new_requested_ram = $anvil->Convert->bytes_to_human_readable({
		debug   => 2,
		'bytes' => $requested_ram,
		base2   => 1,
		unit    => "k",
	});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ">>new_requested_ram" => $new_requested_ram }});
	$new_requested_ram =~ s/\s.*$//;
	$new_requested_ram =~ s/\..*$//;
	$new_requested_ram =~ s/,//g;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<<new_requested_ram" => $new_requested_ram }});
	
	# Still here? Update the definition!
	my $new_server_definition = "";
	foreach my $line (split/\n/, $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml})
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
		if ($line =~ /<memory unit='(.*?)'>(\d+)<\/memory>/)
		{
			my $old_unit = $1;
			my $old_size = $2;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				old_unit => $old_unit, 
				old_size => $old_size,
			}});
			
			$line =~ s/<memory unit='.*?'>\d+<\/memory>/<memory unit='KiB'>$new_requested_ram<\/memory>/;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
		}
		if ($line =~ /<currentMemory unit='(.*?)'>(\d+)<\/currentMemory>/)
		{
			my $old_unit = $1;
			my $old_size = $2;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				old_unit => $old_unit, 
				old_size => $old_size,
			}});
			
			$line =~ s/<currentMemory unit='.*?'>\d+<\/currentMemory>/<currentMemory unit='KiB'>$new_requested_ram<\/currentMemory>/;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
		}
		$new_server_definition .= $line."\n";
	}
	
	# Confirm the new definition is valid
	my $problem = $anvil->Server->parse_definition({
		debug      => 2,
		host       => $short_host_name,
		server     => $server_name, 
		source     => "test_xml",
		definition => $new_server_definition, 
	});
	if ($problem)
	{
		# The new definition is bad
		my $variables = { new_server_definition => $new_server_definition };
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0470", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "error_0470", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	
	# Did the user confirm?
	if ($anvil->data->{switches}{'job-uuid'})
	{
		# Running as a job, don't prompt
		$anvil->Job->update_progress({
			progress => 75,
			message  => "job_0480", 
		});
	}
	elsif ($anvil->data->{switches}{confirm})
	{
		# User confirmed
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0150"});
	}
	else
	{
		# Ask the user to confirm.
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0379", variables => {
			configured_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $server_configured_ram}), 
			requested_ram  => $anvil->Convert->bytes_to_human_readable({'bytes' => $requested_ram}),
		}});
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0021"});
		my $answer = <STDIN>;
		chomp $answer;
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "log_0828", variables => { answer => $answer }});
		if ((lc($answer) eq "y") or (lc($answer) eq "yes"))
		{
			# Proceed
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0175"});
		}
		else
		{
			# Abort
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0022"});
			$anvil->nice_exit({exit_code => 0});
		}
	}
	
	# Record the new config. 
	$problem = $anvil->Server->update_definition({
		debug              => 2, 
		server             => $server_uuid,
		new_definition_xml => $new_server_definition, 
	});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
	if ($problem)
	{
		# Failed!
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0471"});
		$anvil->Job->update_progress({
			progress => 100,
			message  => "error_0471", 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	else
	{
		# Update the server_configured_ram (making sure we've got the current DB data first)
		$anvil->Database->get_servers();
		$anvil->Database->insert_or_update_servers({
			debug                           => 2,
			server_uuid                     => $server_uuid, 
			server_name                     => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name}, 
			server_anvil_uuid               => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid},
			server_user_stop                => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_user_stop},
			server_start_after_server_uuid  => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_after_server_uuid},
			server_start_delay              => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_delay},
			server_host_uuid                => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid},
			server_state                    => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state},
			server_live_migration           => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration},
			server_pre_migration_file_uuid  => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_file_uuid},
			server_pre_migration_arguments  => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_arguments},
			server_post_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_file_uuid},
			server_post_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_arguments}, 
			server_ram_in_use               => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_ram_in_use},
			server_configured_ram           => $requested_ram,
			server_updated_by_user          => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user}, 
			server_boot_time                => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time}, 
		});
		
		# Done!
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "log_0750"});
		$anvil->Job->update_progress({
			progress => 100,
			message  => "log_0750", 
		}) if $anvil->data->{switches}{'job-uuid'};
	}
	
	return(0);
}

sub manage_boot_order
{
	my ($anvil) = @_;
	
	my $short_host_name  = $anvil->Get->short_host_name;
	my $host_uuid        = $anvil->Get->host_uuid;
	my $server_name      = $anvil->data->{switches}{server_name};
	my $server_uuid      = $anvil->data->{switches}{server_uuid};
	my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
	my $server_host_name = "";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		's1:short_host_name'  => $short_host_name,
		's2:host_uuid'        => $host_uuid,
		's3:server_name'      => $server_name, 
		's4:server_uuid'      => $server_uuid, 
		's5:server_host_uuid' => $server_host_uuid,
	}});
	
	my $from_source  = get_definition_source($anvil);
	my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		from_source   => $from_source,
		server_state  => $server_state, 
	}});
	if ($server_state eq "running")
	{
		$server_host_name = $anvil->Database->get_host_from_uuid({
			short     => 1,
			host_uuid => $server_host_uuid,
		});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_host_name => $server_host_name }});
	}
	
	if ($anvil->data->{switches}{'boot-order'} eq "#!SET!#")
	{
		# Show the existing boot devices and their boot order
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0372", variables => { server_name => $server_name }});
		foreach my $boot_order (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}})
		{
			my $device_target = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}{$boot_order}{device_target};
			my $device        = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}{$boot_order}{device_type};
			my $path          = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{path};
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0373", variables => { 
				boot_order    => $boot_order,
				device_target => $device_target, 
				device        => $device, 
				path          => $path, 
			}});
		}
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0374"});
		return(0);
	}
	else
	{
		# Updating boot devices.
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0375"});
		$anvil->Job->update_progress({
			progress => 25,
			message  => "message_0375", 
		}) if $anvil->data->{switches}{'job-uuid'};
		
		# Parse the user's input.
		my $boot_order = $anvil->data->{switches}{'boot-order'};
		my $boot_count = 0;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { boot_order => $boot_order }});
		foreach my $device_target (split/,/, $boot_order)
		{
			$boot_count++;
			$anvil->data->{new_boot_order}{$device_target}{order}       = $boot_count;
			$anvil->data->{new_boot_order}{$device_target}{device_type} = "";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				device_target                      => $device_target,
				boot_count                         => $boot_count, 
				"new_boot_order::${device_target}" => $anvil->data->{new_boot_order}{$device_target},
			}});
			
			if (not exists $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device_target}{$device_target}{type})
			{
				# The device was not found, exit.
				my $variables = { device_target => $device_target };
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0469", variables => $variables});
				$anvil->Job->update_progress({
					progress  => 100,
					message   => "error_0469", 
					variables => $variables, 
				}) if $anvil->data->{switches}{'job-uuid'};
				$anvil->nice_exit({exit_code => 1});
			}
			else
			{
				my $device                                                     = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device_target}{$device_target}{type};
				   $anvil->data->{new_boot_order}{$device_target}{device_type} = $device;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					"new_boot_order::${device_target}::device_type" => $anvil->data->{new_boot_order}{$device_target}{device_type},
				}});
			}
		}
		
		# Find any unlisted devices and bump up their boot order as needed.
		foreach my $boot_order (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}})
		{
			my $device_target = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}{$boot_order}{device_target};
			my $device        = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}{$boot_order}{device_type};
			
			if (not exists $anvil->data->{new_boot_order}{$device_target})
			{
				$anvil->data->{new_boot_order}{$device_target}{order}       = $boot_order + $boot_count;
				$anvil->data->{new_boot_order}{$device_target}{device_type} = $device;
			}
		}
		
		# Make sortable
		foreach my $device_target (sort {$a cmp $b} keys %{$anvil->data->{new_boot_order}})
		{
			my $boot_order = $anvil->data->{new_boot_order}{$device_target}{order};
			my $device     = $anvil->data->{new_boot_order}{$device_target}{device_type};

			$anvil->data->{new_boot_order}{$device_target}{order}  = $boot_order;
			$anvil->data->{new_boot_order}{$device_target}{target} = $device_target;
			$anvil->data->{new_boot_order}{$device_target}{device} = $device;
			$anvil->data->{new_boot_order}{$device_target}{path}   = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{path};
			$anvil->data->{new_order}{$boot_order}{target}         = $device_target;
		}
		
		# Show what the new boot order will be
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0376"});
		$anvil->data->{'say'}{cdrom} = $anvil->Words->string({key => "type_0001"});
		$anvil->data->{'say'}{disk}  = $anvil->Words->string({key => "type_0004"});
		foreach my $boot_order (sort {$a <=> $b} keys %{$anvil->data->{new_order}})
		{
			my $device_target = $anvil->data->{new_order}{$boot_order}{target};
			my $device        = $anvil->data->{new_boot_order}{$device_target}{device};
			my $path          = $anvil->data->{new_boot_order}{$device_target}{path};
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0377", variables => {
				boot_order    => $boot_order, 
				path          => $path, 
				device_target => $device_target, 
				device        => $anvil->data->{'say'}{$device},
			}});
		}
		
		# Tell the job we're about to create the new definition
		$anvil->Job->update_progress({
			progress  => 50,
			message   => "job_0478", 
		}) if $anvil->data->{switches}{'job-uuid'};
		
		# Update the definition.
		my $in_disk               = 0;
		my $device_target         = "";
		my $boot_order_seen       = 0;
		my $new_server_definition = "";
		foreach my $line (split/\n/, $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml})
		{
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
			if ($line =~ /<disk /)
			{
				$in_disk = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_disk => $in_disk }});
			}
			if ($in_disk)
			{
				if ($line =~ /<\/disk>/)
				{
					# If we didn't see the old boot order, or if it was too soon, add the
					# boot order now.
					if ((not $boot_order_seen) && ($device_target))
					{
						my $new_boot_order        =  $anvil->data->{new_boot_order}{$device_target}{order};
						   $new_server_definition .= "      <boot order='".$new_boot_order."'/>\n";
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_boot_order => $new_boot_order }});
					}
					$in_disk         = 0;
					$boot_order_seen = 0;
					$device_target   = "";
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
						in_disk         => $in_disk,
						boot_order_seen => $boot_order_seen, 
						device_target   => $device_target, 
					}});
				}
				elsif ($line =~ /<target dev='(.*?)'/)
				{
					$device_target = $1;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { device_target => $device_target }});
				}
				elsif ($line =~ /boot order='(\d+)'/)
				{
					# If we have a device_target, update the line. Otherwise, skip it and
					# we'll insert it later.
					my $old_boot_order = $1;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_boot_order => $old_boot_order }});
					if ($device_target)
					{
						   $boot_order_seen =  1;
						my $new_boot_order  =  $anvil->data->{new_boot_order}{$device_target}{order};
						   $line            =~ s/boot order='.*?'/boot order='$new_boot_order'/;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { boot_order_seen => $boot_order_seen }});
					}
				}
			}
			$new_server_definition .= $line."\n";
		}
		
		my $problem = $anvil->Server->parse_definition({
			debug      => 2,
			host       => $short_host_name,
			server     => $server_name, 
			source     => "test_xml",
			definition => $new_server_definition, 
		});
		if ($problem)
		{
			# The new definition is bad
			my $variables = { new_server_definition => $new_server_definition };
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0470", variables => $variables});
			$anvil->Job->update_progress({
				progress  => 100,
				message   => "error_0470", 
				variables => $variables, 
			}) if $anvil->data->{switches}{'job-uuid'};
			$anvil->nice_exit({exit_code => 1});
		}
		
		# Did the user confirm?
		if ($anvil->data->{switches}{'job-uuid'})
		{
			# Running as a job, don't prompt
			$anvil->Job->update_progress({
				progress => 75,
				message  => "job_0480", 
			});
		}
		elsif ($anvil->data->{switches}{confirm})
		{
			# User confirmed
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0150"});
		}
		else
		{
			# Ask the user to confirm.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0021"});
			my $answer = <STDIN>;
			chomp $answer;
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "log_0828", variables => { answer => $answer }});
			if ((lc($answer) eq "y") or (lc($answer) eq "yes"))
			{
				# Proceed
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0175"});
			}
			else
			{
				# Abort
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0022"});
				$anvil->nice_exit({exit_code => 0});
			}
		}
		
		# Record the new config. 
		$problem = $anvil->Server->update_definition({
			debug              => 2, 
			server             => $server_uuid,
			new_definition_xml => $new_server_definition, 
		});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
		if ($problem)
		{
			# Failed!
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0471"});
			$anvil->Job->update_progress({
				progress => 100,
				message  => "error_0471", 
			}) if $anvil->data->{switches}{'job-uuid'};
			$anvil->nice_exit({exit_code => 1});
		}
		else
		{
			# Done!
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "log_0750"});
			$anvil->Job->update_progress({
				progress => 100,
				message  => "log_0750", 
			}) if $anvil->data->{switches}{'job-uuid'};
		}
	}
	
	return(0);
}

sub manage_boot_menu
{
	my ($anvil) = @_;
	
	my $short_host_name  = $anvil->Get->short_host_name;
	my $host_uuid        = $anvil->Get->host_uuid;
	my $server_name      = $anvil->data->{switches}{server_name};
	my $server_uuid      = $anvil->data->{switches}{server_uuid};
	my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
	my $server_host_name = "";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		's1:short_host_name'  => $short_host_name,
		's2:host_uuid'        => $host_uuid,
		's3:server_name'      => $server_name, 
		's4:server_uuid'      => $server_uuid, 
		's5:server_host_uuid' => $server_host_uuid,
	}});
	
	my $from_source  = get_definition_source($anvil);
	my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		from_source   => $from_source,
		server_state  => $server_state, 
	}});
	if ($server_state eq "running")
	{
		$server_host_name = $anvil->Database->get_host_from_uuid({
			short     => 1,
			host_uuid => $server_host_uuid,
		});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_host_name => $server_host_name }});
	}
	
	my $say_yes           = $anvil->Words->string({key => 'unit_0001'});
	my $say_no            = $anvil->Words->string({key => 'unit_0002'});
	my $current_boot_menu = lc($anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{info}{boot_menu});
	my $say_old_boot_menu = $current_boot_menu eq "yes" ? $say_yes : $say_no;
	my $new_boot_menu     = $anvil->data->{switches}{'boot-menu'};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		current_boot_menu => $current_boot_menu,
		say_old_boot_menu => $say_old_boot_menu, 
		new_boot_menu     => $new_boot_menu,  
	}});
	
	if ((lc($anvil->data->{switches}{'boot-menu'}) eq "yes") or (lc($anvil->data->{switches}{'boot-menu'}) eq "no"))
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
			"servers::server_uuid::${server_uuid}::server_definition_xml" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml},
		}});
		
		# We need to rewrite the boot menu manually.
		my $new_definition = "";
		my $in_os          = 0;
		my $bootmenu_seen  = 0;
		foreach my $line (split/\n/, $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml})
		{
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
			if ($line =~ /<os>/)
			{
				$in_os = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_os => $in_os }});
			}
			if ($in_os)
			{
				if ($line =~ /<bootmenu enable='(.*?)'\/>/)
				{
					my $old_value     =  $1;
					   $bootmenu_seen =  1;
					   $line          =~ s/<bootmenu enable='.*?'\/>/<bootmenu enable='$new_boot_menu'\/>/;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
						old_value     => $old_value, 
						bootmenu_seen => $bootmenu_seen, 
						line          => $line,
					}});
				}
				if ($line =~ /<\/os>/)
				{
					$in_os = 0;
					if (not $bootmenu_seen)
					{
						# Insert it
						$new_definition .= "    <bootmenu enable='".$new_boot_menu."'\/>\n";
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_definition => $new_definition }});
					}
				}
			}
			$new_definition .= $line."\n";
		}
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_definition => $new_definition }});
		
		# Always call this, as a previous run may have only updated some definitions.
		my $problem = $anvil->Server->update_definition({
			debug              => 2, 
			server             => $server_uuid,
			new_definition_xml => $new_definition, 
		});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
		
		# Reload and parse from the DB to confirm things are updated.
		delete $anvil->data->{servers}{server_uuid}{$server_uuid};
		$anvil->Database->get_servers();
		my $server_definition_xml = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_xml => $server_definition_xml }});
		
		undef $anvil->data->{server}{$short_host_name}{$server_name}{from_db};
		$anvil->Server->parse_definition({
			debug      => 2,
			host       => $short_host_name,
			server     => $server_name, 
			source     => "from_db",
			definition => $server_definition_xml, 
		});
		
		my $new_boot_menu     = lc($anvil->data->{server}{$short_host_name}{$server_name}{from_db}{info}{boot_menu});
		my $say_new_boot_menu = $new_boot_menu eq "yes" ? $say_yes : $say_no;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			new_boot_menu     => $new_boot_menu,
			say_new_boot_menu => $say_new_boot_menu, 
		}});
		print "Boot Menu enabled now: [".$say_new_boot_menu."]\n";
		print "- The boot menu has been updated.\n";
	}
	else
	{
		print "Boot Menu enabled: [".$say_old_boot_menu."]\n";
		print "- You can change this using '--boot-menu yes' or '--boot-menu no'.\n";
	}
	
	return(0);
}

sub show_server_details
{
	my ($anvil, $show_nodes) = @_;
	
	my $short_host_name = $anvil->Get->short_host_name;
	my $host_uuid       = $anvil->Get->host_uuid;
	my $server_name     = $anvil->data->{switches}{server_name};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		's1:short_host_name' => $short_host_name,
		's2:host_uuid'       => $host_uuid,
		's3:server_name'     => $server_name, 
		's4:show_nodes'      => $show_nodes, 
	}});
	
	# Words we'll use.
	my $say_yes     = $anvil->Words->string({key => 'unit_0001'});
	my $say_no      = $anvil->Words->string({key => 'unit_0002'});
	my $say_disk    = $anvil->Words->string({key => 'header_0068'});
	my $say_optical = $anvil->Words->string({key => 'header_0111'});
	my $say_ejected = $anvil->Words->string({key => 'unit_0049'});
	
	my $from_source = get_definition_source($anvil);
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { from_source => $from_source }});
	
	# Boot stuff
	my $say_boot_menu   = lc($anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{info}{boot_menu})            eq "yes"   ? $say_yes     : $say_no;
	my $say_boot_device = lc($anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}{1}{device_type}) eq "cdrom" ? $say_optical : $say_disk;
	my $boot_device     = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}{1}{device_target};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		say_boot_menu   => $say_boot_menu,
		say_boot_device => $say_boot_device, 
		boot_device     => $boot_device, 
	}});
	my $boot_order = [];
	foreach my $sequence (sort {$a <=> $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}})
	{
		my $this_device_type     = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}{$sequence}{device_type};
		my $say_this_boot_device = lc($this_device_type) eq "cdrom" ? $say_optical : $say_disk;
		my $this_boot_device     = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{boot_order}{$sequence}{device_target};
		my $device_path          = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$this_device_type}{target}{$this_boot_device}{path} // "";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			sequence             => $sequence,
			say_this_boot_device => $say_this_boot_device, 
			this_boot_device     => $this_boot_device, 
			device_path          => $device_path, 
		}});
		if ((not $device_path) && (lc($this_device_type) eq "cdrom"))
		{
			$device_path = $say_ejected;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { device_path => $device_path }});
		}
		
		push @{$boot_order}, $sequence."; ".$this_boot_device.", type: [".$say_this_boot_device."], path: [".$device_path."]";
	}
	
	print "Boot Menu enabled: [".$say_boot_menu."]\n";
	print "Boot device:\n";
	foreach my $say_other_boot (@{$boot_order})
	{
		print "- ".$say_other_boot."\n";
	}
	
	# CPU 
	my $cpu_sockets     = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{sockets};
	my $cpu_cores       = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{cores};
	my $cpu_total_cores = $cpu_sockets * $cpu_cores;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		cpu_sockets     => $cpu_sockets, 
		cpu_cores       => $cpu_cores, 
		cpu_total_cores => $cpu_total_cores, 
	}});
	print "CPU Total: [".$cpu_total_cores."]; Sockets: [".$cpu_sockets."], Cores per Socket: [".$cpu_cores."]\n";
	
	# RAM
	my $ram = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{memory};
	print "RAM: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $ram})."] (".$anvil->Convert->add_commas({number => $ram})." Bytes)\n";
	
	
	return(0);
}

sub get_definition_source
{
	my ($anvil) = @_;
	
	my $short_host_name  = $anvil->Get->short_host_name;
	my $server_name      = $anvil->data->{switches}{server_name};
	my $server_uuid      = $anvil->data->{switches}{server_uuid};
	my $server_state     = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
	my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
	my $from_source      = "";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		short_host_name => $short_host_name,
		server_name     => $server_name, 
		server_uuid     => $server_uuid, 
		server_state    => $server_state, 
		from_source     => $from_source, 
	}});
	
	$anvil->data->{server}{$short_host_name}{$server_name}{from_virsh} = "" if not exists $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh};
	$anvil->data->{server}{$short_host_name}{$server_name}{from_disk}  = "" if not exists $anvil->data->{server}{$short_host_name}{$server_name}{from_disk};
	$anvil->data->{server}{$short_host_name}{$server_name}{from_db}    = "" if not exists $anvil->data->{server}{$short_host_name}{$server_name}{from_db};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		"server::${short_host_name}::${server_name}::from_virsh" => $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh},
		"server::${short_host_name}::${server_name}::from_disk"  => $anvil->data->{server}{$short_host_name}{$server_name}{from_disk},
		"server::${short_host_name}::${server_name}::from_db"    => $anvil->data->{server}{$short_host_name}{$server_name}{from_db},
	}});
	
	if (($server_state eq "running")                    && 
	    ($server_host_uuid eq $anvil->Get->host_uuid()) && 
	    (exists $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}))
	{
		$from_source = "from_virsh";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { from_source => $from_source }});
	}
	elsif (exists $anvil->data->{server}{$short_host_name}{$server_name}{from_disk})
	{
		$from_source = "from_disk";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { from_source => $from_source }});
	}
	else
	{
		$from_source = "from_db";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { from_source => $from_source }});
	}

	return($from_source);
}

sub show_server_list
{
	my ($anvil) = @_;
	
	# Loop through all Anvil! nodes, then all server in that Anvil!
	foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}})
	{
		my $anvil_uuid        = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid};
		my $anvil_description = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			anvil_name        => $anvil_name, 
			anvil_uuid        => $anvil_uuid,
			anvil_description => $anvil_description,
		}});
		if (($anvil->data->{switches}{anvil_uuid}) && ($anvil->data->{switches}{anvil_uuid} ne $anvil_uuid))
		{
			next;
		}
		print "\n".$anvil->Words->string({key => "message_0343", variables => {
			anvil_name        => $anvil_name, 
			anvil_uuid        => $anvil_uuid, 
			anvil_description => $anvil_description, 
		}})."\n";
		
		my $server_count = 0;
		if (exists $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name})
		{
			$server_count = keys %{$anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_count => $server_count }});
		}
		if (not $server_count)
		{
			print $anvil->Words->string({key => "message_0344"})."\n";
		}
		else
		{
			foreach my $server_name (sort {$a cmp $b} keys %{$anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}})
			{
				my $server_uuid  = $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}{$server_name}{server_uuid};
				my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					server_name  => $server_name, 
					server_uuid  => $server_uuid, 
					server_state => $server_state,
				}});
				next if $server_state eq "DELETED";
				print $anvil->Words->string({key => "message_0345", variables => {
					server_name => $server_name, 
					server_uuid => $server_uuid, 
				}})."\n";
			}
		}
	}
	
	return(0);
}

sub validate_server
{
	my ($anvil) = @_;
	
	$anvil->Get->server_from_switch({
		debug      => 2, 
		string     => $anvil->data->{switches}{server},
		anvil_uuid => $anvil->data->{switches}{anvil_uuid},
	});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		"switches::server_name" => $anvil->data->{switches}{server_name},
		"switches::server_uuid" => $anvil->data->{switches}{server_uuid},
	}});
	
	if (not $anvil->data->{switches}{server_uuid})
	{
		show_server_list($anvil);
		my $variables = {
			server => $anvil->data->{switches}{server}, 
			anvil  => $anvil->data->{switches}{anvil_name}, 
		};
		if ($anvil->data->{switches}{anvil_uuid})
		{
			# Not found on the requested Anvil! node.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0451", variables => $variables});
			$anvil->Job->update_progress({
				progress  => 100,
				message   => "error_0451", 
				variables => $variables, 
			}) if $anvil->data->{switches}{'job-uuid'};
		}
		else
		{
			# Not found at all.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0452", variables => $variables});
			$anvil->Job->update_progress({
				progress  => 100,
				message   => "error_0452", 
				variables => $variables, 
			}) if $anvil->data->{switches}{'job-uuid'};
		}
		$anvil->nice_exit({exit_code => 1});
	}
	
	my $variables = {
		server_name => $anvil->data->{switches}{server_name}, 
		server_uuid => $anvil->data->{switches}{server_uuid}, 
	};
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0802", variables => $variables});
	$anvil->Job->update_progress({
		progress  => 2,
		message   => "log_0802", 
		variables => $variables, 
	}) if $anvil->data->{switches}{'job-uuid'};
	my $short_host_name   = $anvil->Get->short_host_name;
	my $server_name       = $anvil->data->{switches}{server_name};
	my $server_uuid       = $anvil->data->{switches}{server_uuid};
	my $server_definition = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml};
	my $server_host_uuid  = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
	my $server_state      = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
	my $anvil_uuid        = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		's1:short_host_name'   => $short_host_name, 
		's2:server_name'       => $server_name, 
		's3:server_uuid'       => $server_uuid, 
		's4:server_definition' => $server_definition,
		's5:server_host_uuid'  => $server_host_uuid, 
		's6:server_state'      => $server_state, 
		's7:anvil_uuid'        => $anvil_uuid, 
	}});
	if (not $anvil->data->{switches}{anvil_uuid})
	{
		$anvil->data->{switches}{anvil_uuid} = $anvil_uuid;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:switches::anvil_uuid' => $anvil->data->{switches}{anvil_uuid}, 
		}});
	}
	
	if ($server_state eq "DELETED")
	{
		# The server has been deleted
		my $variables = { server => $server_name };
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0453", variables => $variables});
		$anvil->Job->update_progress({
			progress  => 100,
			message   => "error_0453", 
			variables => $variables, 
		}) if $anvil->data->{switches}{'job-uuid'};
		$anvil->nice_exit({exit_code => 1});
	}
	
	# Parse the definition.
	$anvil->Server->parse_definition({
		debug      => 3,
		host       => $short_host_name,
		server     => $server_name, 
		source     => "from_db",
		definition => $server_definition, 
	});
	
	# Can we read the XML definition?
	$anvil->Server->get_status({
		debug  => 2,
		server => $server_name,
		host   => $short_host_name,
	});
	
	if (not $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{xml})
	{
		# The server isn't actually running... Not here anyway.
		if ($server_state eq "running")
		{
			my $server_host_name = $anvil->Database->get_host_from_uuid({
				short     => 1,
				host_uuid => $server_host_uuid,
			});
			
			my $variables = {
				server    => $server_name, 
				host_name => $server_host_name,
			};
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0454", variables => $variables});
			$anvil->Job->update_progress({
				progress  => 100,
				message   => "error_0454", 
				variables => $variables, 
			}) if $anvil->data->{switches}{'job-uuid'};
			$anvil->nice_exit({exit_code => 1});
		}
	}
	
	return(0);
}

sub load_job
{
	my ($anvil) = @_;
	
	$anvil->Job->clear({
		debug    => 2,
		job_uuid => $anvil->data->{switches}{'job-uuid'}, 
	});
	$anvil->Job->get_job_details({
		debug    => 2,
		job_uuid => $anvil->data->{switches}{'job-uuid'}, 
	});
	
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		'jobs::job_data' => $anvil->data->{jobs}{job_data}, 
	}});
	foreach my $line (split/\n/, $anvil->data->{jobs}{job_data})
	{
		my ($variable, $value) =  ($line =~ /^(.*)=(.*)$/);
		   $value              =~ s/^"(.*)\"/$1/;
		   $value              =~ s/^'(.*)\'/$1/;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:line'     => $line, 
			's2:variable' => $variable, 
			's3:value'    => $value, 
		}});
		
		$anvil->data->{switches}{$variable} = $value;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			"switches::${variable}" => $anvil->data->{switches}{$variable}, 
		}});
	}
	
	$anvil->Job->update_progress({
		progress         => 1,
		job_picked_up_by => $$, 
		job_picked_up_at => time, 
		message          => "message_0353", 
	});
	
	return(0);
}
