#!/usr/bin/perl
# 
# This is called when striker, a node or a DR host needs to configure the local network and user accounts.
# 
# Exit codes;
# 0 = Normal exit.
# 1 = The program is not running as root.
# 2 = Failed to connect to database(s).
# 3 = Job was already picked up by another running instance.
# 4 = The host name did not update properly.
# 5 = Failed to write the temp file with the new password needed to call anvil-change-password.
# 6 = The job-uuid was not found.
# 7 = The host is an active cluster member.
# 
# TODO: 
# - Add MTU support
# - Check to see if this is a cluster node and/or running VMs, and if so, refuse to run.
# 

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

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
$anvil->Get->switches;
$anvil->Log->level({set => 2});
$anvil->Log->secure({set => 1});

# Make sure we're running as 'root'
# $< == real UID, $> == effective UID
if (($< != 0) && ($> != 0))
{
	# Not root
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0005"});
	$anvil->nice_exit({exit_code => 1});
}

# Connect
$anvil->Database->connect();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "message_0031"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, 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, key => "error_0003"});
	$anvil->nice_exit({exit_code => 2});
}

pickup_job_details($anvil);

# Set maintenance mode
$anvil->System->maintenance_mode({set => 1});

reconfigure_network($anvil);

# Record that we've configured this machine.
$anvil->Database->insert_or_update_variables({
	variable_name         => "system::configured", 
	variable_value        => 1, 
	variable_default      => "", 
	variable_description  => "striker_0048", 
	variable_section      => "system", 
	variable_source_uuid  => $anvil->data->{sys}{host_uuid}, 
	variable_source_table => "hosts", 
});

update_passwords($anvil);
$anvil->Job->update_progress({
	debug    => 3, 
	progress => 100, 
	message  => "",
	job_uuid => $anvil->data->{job}{uuid},
});

# Clear maintenance mode.
$anvil->System->maintenance_mode({set => 0});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, secure => 0, key => "log_0467"});

### TODO: As of now, the network doesn't come up reliably, so reboot.
my $shell_call = $anvil->data->{path}{exe}{systemctl}." reboot";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});

# Unlikely we're still alive, but 'poweroff' and 'reboot' do return once enqueued, so...
$anvil->nice_exit({exit_code => 0});


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

# This updates the passwords on the root user, admin user, database and apache user.
sub update_passwords
{
	my ($anvil) = @_;
	
	# Have we been asked to set a password?
	$anvil->data->{variables}{form}{config_step2}{striker_password}{value} = "" if not defined $anvil->data->{variables}{form}{config_step2}{striker_password}{value};
	if ($anvil->data->{variables}{form}{config_step2}{striker_password}{value})
	{
		# Set the passwords
		my $password  = $anvil->data->{variables}{form}{config_step2}{striker_password}{value};
		my $temp_file = "/tmp/anvil-".$anvil->Get->uuid;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { password => $password }});
		
		# Write the password into a temporary file.
		my $error = $anvil->Storage->write_file({
			body      => $password,
			file      => $temp_file,
			group     => "root", 
			mode      => "0600",
			overwrite => 1,
			secure    => 1,
			user      => "root",
		});
		
		# Call anvil-change-password
		if ($error)
		{
			# Couldn't write the temp file.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "message_0030", variables => { file => $temp_file }});
			$anvil->nice_exit({exit_code => 5});
		}
		else
		{
			my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{'anvil-change-password'}." -y --password-file ".$temp_file });
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { output => $output, return_code => $return_code }});
			foreach my $line (split/\n/, $output)
			{
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { line => $line }});
			}
			
			# Unlink the temp file.
			unlink $temp_file;
			
			if ($return_code)
			{
				# Something went wrong
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0011", variables => { return_code => $return_code }});
			}
		}
	}
	
	$anvil->Job->update_progress({
		progress => 95, 
		job_uuid => $anvil->data->{job}{uuid},
	});
	
	return(0);
}

# This does the work of reconfiguring the network
sub reconfigure_network
{
	my ($anvil) = @_;
	
	my $local_host       = $anvil->Get->short_host_name();
	my $reboot_needed    = 0;
	my $prefix           = exists $anvil->data->{variables}{form}{config_step1}{prefix}{value}       ? $anvil->data->{variables}{form}{config_step1}{prefix}{value}       : "";
	my $sequence         = exists $anvil->data->{variables}{form}{config_step1}{sequence}{value}     ? $anvil->data->{variables}{form}{config_step1}{sequence}{value}     : "";
	my $domain           = exists $anvil->data->{variables}{form}{config_step1}{domain}{value}       ? $anvil->data->{variables}{form}{config_step1}{domain}{value}       : "";
	my $organization     = exists $anvil->data->{variables}{form}{config_step1}{organization}{value} ? $anvil->data->{variables}{form}{config_step1}{organization}{value} : "";
	my $bcn_count        = exists $anvil->data->{variables}{form}{config_step1}{bcn_count}{value}    ? $anvil->data->{variables}{form}{config_step1}{bcn_count}{value}    : 1;
	my $sn_count         = exists $anvil->data->{variables}{form}{config_step1}{sn_count}{value}     ? $anvil->data->{variables}{form}{config_step1}{sn_count}{value}     : 0;
	my $ifn_count        = exists $anvil->data->{variables}{form}{config_step1}{ifn_count}{value}    ? $anvil->data->{variables}{form}{config_step1}{ifn_count}{value}    : 1;
	my $new_host_name    = exists $anvil->data->{variables}{form}{config_step2}{host_name}{value}    ? $anvil->data->{variables}{form}{config_step2}{host_name}{value}    : "";
	my $type             = $anvil->Get->host_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
		prefix        => $prefix, 
		sequence      => $sequence,
		domain        => $domain, 
		organization  => $organization, 
		bcn_count     => $bcn_count, 
		sn_count      => $sn_count, 
		ifn_count     => $ifn_count, 
		new_host_name => $new_host_name, 
		type          => $type, 
	}});
	
	# If we're configuring a dashboard and no host name was given, generate it.
	if (($type eq "striker") && (not $new_host_name))
	{
		$new_host_name = $prefix."-striker".sprintf("%02d", $sequence).".".$domain;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { new_host_name => $new_host_name }});
	}
	
	if ($new_host_name)
	{
		my $type_name = "";
		if ($type eq "striker")
		{
			$type_name = $anvil->Words->string({key => "brand_0003"});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, list => { type_name => $type_name }});
		}
		elsif ($type eq "node")
		{
			$type_name = $anvil->Words->string({key => "brand_0007"});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, list => { type_name => $type_name }});
		}
		elsif ($type eq "dr")
		{
			$type_name = $anvil->Words->string({key => "brand_0008"});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, list => { type_name => $type_name }});
		}
		my $pretty_host_name = $new_host_name;
		if (($organization) && ($sequence))
		{
			$pretty_host_name = $organization." - ".$type_name." ".sprintf("%02d", $sequence);
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, list => { pretty_host_name => $pretty_host_name }});
		}
		
		# Set the host_name
		my ($host_name, $descriptive_host_name) = $anvil->System->host_name({set => $new_host_name, pretty => $pretty_host_name, debug => 3});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
			host_name             => $host_name,
			descriptive_host_name => $descriptive_host_name, 
		}});
		if ($host_name eq $new_host_name)
		{
			# Success
			$anvil->Job->update_progress({
				progress => 10, 
				message  => "message_0016,!!host_name!$new_host_name!!", 
				job_uuid => $anvil->data->{job}{uuid},
			});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "message_0016", variables => { host_name => $new_host_name }});
		}
		else
		{
			# Failed
			$anvil->Job->update_progress({
				progress => 0, 
				message  => "message_0017,!!host_name!$new_host_name!!,!!bad_host_name!$host_name!!", 
				job_uuid => $anvil->data->{job}{uuid},
			});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "message_0017", variables => { 
				host_name     => $new_host_name,
				bad_host_name => $host_name,
			}});
			$anvil->nice_exit({exit_code => 4});
		}
	}
	
	# Read the local network manager data.
	$anvil->Network->read_nmcli({debug => 3});
	
	# Get the current list of IPs and MAC addresses.
	$anvil->Network->get_ips({debug => 3});
	
	# Now configure the network.
	my $dns = defined $anvil->data->{variables}{form}{config_step2}{dns}{value} ? [split/,/, $anvil->data->{variables}{form}{config_step2}{dns}{value}] : [];
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { dns => $dns }});
	for (my $i = 0; $i < @{$dns}; $i++)
	{
		$dns->[$i] = $anvil->Words->clean_spaces({ string => $dns->[$i] });
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "dns->[$i]" => $dns->[$i] }});
	}
	
	my $gateway           = defined $anvil->data->{variables}{form}{config_step2}{gateway}{value}           ? $anvil->data->{variables}{form}{config_step2}{gateway}{value}           : "";
	my $gateway_interface = defined $anvil->data->{variables}{form}{config_step2}{gateway_interface}{value} ? $anvil->data->{variables}{form}{config_step2}{gateway_interface}{value} : "";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
		gateway           => $gateway, 
		gateway_interface => $gateway_interface, 
	}});
	
	# If there is no default gateway device, and one of the interfaces are set to DHCP, use it.
	if (not $gateway_interface)
	{
		# IFN first, BCN second, SN last
		foreach my $network_type ("ifn", "bcn", "sn")
		{
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }});
			
			my $count = 0;
			if    ($network_type eq "bcn") { $count = $bcn_count; }
			elsif ($network_type eq "sn")  { $count = $sn_count;  }
			elsif ($network_type eq "ifn") { $count = $ifn_count; }
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
			
			next if not $count;
			foreach my $network_count (1..$count)
			{
				my $ip_key = $network_type.$network_count."_ip";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ip_key => $ip_key }});
				if ((exists $anvil->data->{variables}{form}{config_step2}{$ip_key}{value}) && ($anvil->data->{variables}{form}{config_step2}{$ip_key}{value} eq "dhcp"))
				{
					$gateway_interface = $network_type.$network_count;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { gateway_interface => $gateway_interface }});
					last;
				}
			}
		}
	}
	
	# Disconnect from the database, as we're about to tear down our connection.
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, secure => 0, key => "log_0466"});
	$anvil->Database->disconnect();
	
	# Close all open SSH connections
	foreach my $ssh_fh_key (sort {$a cmp $b} keys %{$anvil->data->{cache}{ssh_fh}})
	{
		my $ssh_fh = $anvil->data->{cache}{ssh_fh}{$ssh_fh_key};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ssh_fh => $ssh_fh }});
		
		next if $ssh_fh !~ /^Net::OpenSSH/;
		$ssh_fh->disconnect();
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "message_0009", variables => { target => $ssh_fh_key }});
		
		# For good measure, blank both variables.
		$anvil->data->{cache}{ssh_fh}{$ssh_fh_key} = "";
		$ssh_fh                                    = "";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cache::ssh_fh::${ssh_fh_key}" => $anvil->data->{cache}{ssh_fh}{$ssh_fh_key} }});
	}
	
	my $new_interfaces = [];
	foreach my $network_type ("bcn", "sn", "ifn")
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_type => $network_type }});
		
		my $count = 0;
		if    ($network_type eq "bcn") { $count = $bcn_count; }
		elsif ($network_type eq "sn")  { $count = $sn_count;  }
		elsif ($network_type eq "ifn") { $count = $ifn_count; }
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
		
		next if not $count;
		foreach my $network_count (1..$count)
		{
			my $this_network    =         $network_type.$network_count;
			my $link1_key       =         $this_network."_link1_mac_to_set";
			my $link2_key       =         $this_network."_link2_mac_to_set";
			my $subnet_mask_key =         $this_network."_subnet_mask";
			my $ip_key          =         $this_network."_ip";
			my $bridge_key      =         $this_network."_create_bridge";
			my $link1_mac       =         $anvil->data->{variables}{form}{config_step2}{$link1_key}{value};
			my $is_gateway      =         $this_network eq $gateway_interface                                  ? 1                                                                    : 0;
			my $link2_mac       = defined $anvil->data->{variables}{form}{config_step2}{$link2_key}{value}     ? $anvil->data->{variables}{form}{config_step2}{$link2_key}{value}     : "";
			my $old_link1_iface =         $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} ? $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} : "";
			my $old_link2_iface = defined $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface} ? $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface} : "";
			my $bridge          = defined $anvil->data->{variables}{form}{config_step2}{$bridge_key}{value}    ? $anvil->data->{variables}{form}{config_step2}{$bridge_key}{value}    : 0;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				ip_key          => $ip_key, 
				is_gateway      => $is_gateway, 
				link1_key       => $link1_key, 
				link1_mac       => $link1_mac, 
				link2_key       => $link2_key, 
				link2_mac       => $link2_mac, 
				bridge_key      => $bridge_key, 
				old_link1_iface => $old_link1_iface, 
				old_link2_iface => $old_link2_iface, 
				subnet_mask_key => $subnet_mask_key, 
				this_network    => $this_network, 
				bridge          => $bridge, 
			}});
			
			# Dig out the name that network manager knows the old interface(s) as. The 
			# 'old_link1_iface' is the name reported by 'ip', the 'DEVICE=xxx' value in the ifcfg-xxx file.
			my $old_link1_nm_name = $old_link1_iface;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link1_nm_name => $old_link1_nm_name }});
			if ((exists $anvil->data->{nmcli}{$local_host}{device_to_uuid}{$old_link1_iface}) && ($anvil->data->{nmcli}{$local_host}{device_to_uuid}{$old_link1_iface}))
			{
				$old_link1_nm_name = $anvil->data->{nmcli}{$local_host}{device_to_uuid}{$old_link1_iface};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link1_nm_name => $old_link1_nm_name }});
			}
			
			# Is there a link 2?
			my $old_link2_nm_name = "";
			if ($old_link2_iface)
			{
				$old_link2_nm_name = $old_link2_iface;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link2_nm_name => $old_link2_nm_name }});
				if ((exists $anvil->data->{nmcli}{$local_host}{device_to_uuid}{$old_link2_iface}) && ($anvil->data->{nmcli}{$local_host}{device_to_uuid}{$old_link2_iface}))
				{
					$old_link2_nm_name = $anvil->data->{nmcli}{$local_host}{device_to_uuid}{$old_link2_iface};
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link2_nm_name => $old_link2_nm_name }});
				}
			}
			
			# Skip if this doesn't exist or isn't a valid IPv4 address.
			if (not exists $anvil->data->{variables}{form}{config_step2}{$ip_key}{value})
			{
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0176", variables => { ip_key => $ip_key }});
				next;
			}
			else
			{
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "variables::form::config_step2::${ip_key}::value" => $anvil->data->{variables}{form}{config_step2}{$ip_key}{value} }});
			}
			if (($anvil->data->{variables}{form}{config_step2}{$ip_key}{value})           && 
			    ($anvil->data->{variables}{form}{config_step2}{$ip_key}{value} ne "dhcp") && 
			    (not $anvil->Validate->ipv4({ip => $anvil->data->{variables}{form}{config_step2}{$ip_key}{value}})))
			{
				# Something was set, but it isn't valid.
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0148", variables => { 
					network => $this_network,
					ip      => $anvil->data->{variables}{form}{config_step2}{$ip_key}{value},
				}});
				next;
			}
			
			# The IP could be 'dhcp', we'll handle that in a bit.
			my $ip_address  =         $anvil->data->{variables}{form}{config_step2}{$ip_key}{value};
			my $subnet_mask = defined $anvil->data->{variables}{form}{config_step2}{$subnet_mask_key}{value} ? $anvil->data->{variables}{form}{config_step2}{$subnet_mask_key}{value} : "";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				ip_address  => $ip_address, 
				subnet_mask => $subnet_mask, 
			}});
			
			# Are we building bonded interfaces?
			if ($anvil->Validate->mac({mac => $link2_mac}))
			{
				# Yup!
				my $say_network      = "";
				my $say_interface    = "";
				my $interface_prefix = "";
				my $say_defroute     = $is_gateway ? "yes" : "no";
				if ($network_type eq "bcn")
				{
					$say_network      = "Back-Channel Network ".$network_count;
					$say_interface    = "bcn".$network_count;
					$interface_prefix = "BCN";
				}
				elsif ($network_type eq "sn")
				{
					$say_network      = "Storage Network ".$network_count; 
					$say_interface    = "sn".$network_count;
					$interface_prefix = "SN";
				}
				elsif ($network_type eq "ifn")
				{
					$say_network      = "Internet-Facing Network ".$network_count; 
					$say_interface    = "ifn".$network_count;
					$interface_prefix = "IFN";
				}
				
				# Gather variables
				my $cidr             = $ip_address eq "dhcp" ? "" : $anvil->Convert->cidr({subnet_mask => $subnet_mask});
				my $bridge_file      = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Bridge_1";
				my $bond_file        = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Bond_1";
				my $new_link1_file   = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_1";
				my $new_link2_file   = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_2";
				my $old_link1_file   = $new_link1_file;
				my $old_link2_file   = $new_link2_file;
				my $new_bridge_iface = $say_interface."_bridge1";
				my $new_bond_iface   = $say_interface."_bond1";
				my $new_link1_iface  = $say_interface."_link1";
				my $new_link2_iface  = $say_interface."_link2";
				my $boot_proto       = $ip_address eq "dhcp" ? "dhcp" : "none";
				my $link1_uuid       = get_uuid_from_interface_file($anvil, $old_link1_file);
				my $link2_uuid       = get_uuid_from_interface_file($anvil, $old_link2_file);
				if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}))
				{
					$old_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface};
				}
				if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}))
				{
					$old_link2_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface};
				}
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					bond_file        => $bond_file, 
					boot_proto       => $boot_proto, 
					bridge_file      => $bridge_file, 
					cidr             => $cidr, 
					link1_uuid       => $link1_uuid, 
					link2_uuid       => $link2_uuid, 
					new_bond_iface   => $new_bond_iface, 
					new_bridge_iface => $new_bridge_iface, 
					new_link1_file   => $new_link1_file,
					new_link1_iface  => $new_link1_iface, 
					new_link2_file   => $new_link2_file, 
					new_link2_iface  => $new_link2_iface, 
					old_link1_file   => $old_link1_file, 
					old_link2_file   => $old_link2_file, 
					say_defroute     => $say_defroute, 
				}});
				
				### NOTE: Bridges and bonds take a UUID, but it can be temperamental. It 
				###       works more reliably without defining it, so we don't.
				# Are we building a bridge interface?
				my $bridge_config = "";
				if ($bridge)
				{
					my $new_bridge1_nm_name = $interface_prefix." ".$network_count." - Bridge 1";
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_bridge1_nm_name => $new_bridge1_nm_name }});
					
					# Record that we need to start this.
					push @{$new_interfaces}, $new_bridge1_nm_name;
					
					# Yup!
					$bridge_config =  "# $say_network - Bridge 1\n";
					$bridge_config .= "DEVICE=\"".$new_bridge_iface."\"\n";
					$bridge_config .= "NAME=\"".$new_bridge1_nm_name."\"\n";
					$bridge_config .= "TYPE=\"Bridge\"\n";
					$bridge_config .= "STP=\"yes\"\n";
					$bridge_config .= "BRIDGING_OPTS=\"priority=32768\"\n";
					$bridge_config .= "PROXY_METHOD=\"none\"\n";
					$bridge_config .= "BROWSER_ONLY=\"no\"\n";
					$bridge_config .= "IPV4_FAILURE_FATAL=\"no\"\n";
					$bridge_config .= "IPV6INIT=\"no\"\n";
					$bridge_config .= "BOOTPROTO=\"".$boot_proto."\"\n";
					
					# If the IP is NOT 'dhcp', set it. 
					if ($ip_address ne "dhcp")
					{
						$bridge_config .= "IPADDR=\"".$ip_address."\"\n";
						$bridge_config .= $cidr ? "PREFIX=\"".$cidr."\"\n" : "NETMASK=\"".$subnet_mask."\"\n";
						
						# If this is the default gateway, add that info.
						if ($is_gateway)
						{
							$bridge_config .= "GATEWAY=\"".$gateway."\"\n";
							for (my $i = 0; $i < @{$dns}; $i++)
							{
								$bridge_config .= "DNS".($i+1)."=\"".$dns->[$i]."\"\n";
							}
						}
					}
					$bridge_config .= "DEFROUTE=\"".$say_defroute."\"\n";
					$bridge_config .= "ONBOOT=\"yes\"\n";
					$bridge_config .= "ZONE=\"".uc($say_interface)."\"\n";
				}
				
				# If this is DHCP, but there is a bridge, the bond's boot proto in 'none'.
				$boot_proto = "none" if $bridge;
				
				# Make the rest of the network names.
				my $new_bond1_nm_name = $interface_prefix." ".$network_count." - Bond 1";
				my $new_link1_nm_name = $interface_prefix." ".$network_count." - Link 1";
				my $new_link2_nm_name = $interface_prefix." ".$network_count." - Link 2";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					new_bond1_nm_name => $new_bond1_nm_name, 
					new_link1_nm_name => $new_link1_nm_name, 
					new_link2_nm_name => $new_link2_nm_name, 
				}});
				
				# Record that we need to start this.
				push @{$new_interfaces}, $new_bond1_nm_name;
				push @{$new_interfaces}, $new_link1_nm_name;
				push @{$new_interfaces}, $new_link2_nm_name;
				
				# Build the Bond config.
				my $bond_config =  "# $say_network - Bond 1\n";
				   $bond_config .= "DEVICE=\"".$new_bond_iface."\"\n";
				   $bond_config .= "NAME=\"".$new_bond1_nm_name."\"\n";
				   $bond_config .= "TYPE=\"Bond\"\n";
				   $bond_config .= "BONDING_OPTS=\"downdelay=0 miimon=100 mode=active-backup primary=".$say_interface."_link1 updelay=120000\"\n";
				   $bond_config .= "BONDING_MASTER=\"yes\"\n";
				   $bond_config .= "ONBOOT=\"yes\"\n";
				
				# Is this connected to a bridge?
				if ($bridge)
				{
					$bond_config .= "BRIDGE=\"".$new_bridge_iface."\"\n";
				}
				else
				{
					# Does this bond have an IP?
					# If the IP is NOT 'dhcp', set it. 
					$bond_config .= "BOOTPROTO=\"".$boot_proto."\"\n";
					if ($ip_address ne "dhcp")
					{
						$bond_config .= "IPADDR=\"".$ip_address."\"\n";
						$bond_config .= $cidr ? "PREFIX=\"".$cidr."\"\n" : "NETMASK=\"".$subnet_mask."\"\n";
						
						# If this is the default gateway, add that info.
						if ($is_gateway)
						{
							$bond_config .= "GATEWAY=\"".$gateway."\"\n";
							for (my $i = 0; $i < @{$dns}; $i++)
							{
								$bond_config .= "DNS".($i+1)."=\"".$dns->[$i]."\"\n";
							}
						}
					}
					$bond_config .= "DEFROUTE=\"".$say_defroute."\"\n";
				}
				# Rest of the config
				$bond_config .= "ZONE=\"".uc($say_interface)."\"\n";
				   
				# Now build the links
				my $link1_config =  "# $say_network - Link 1\n";
				   $link1_config .= "HWADDR=\"".uc($link1_mac)."\"\n";
				   $link1_config .= "UUID=\"".$link1_uuid."\"\n";
				   $link1_config .= "NAME=\"".$new_link1_nm_name."\"\n";
				   $link1_config .= "DEVICE=\"".$new_link1_iface."\"\n";
				   $link1_config .= "TYPE=\"Ethernet\"\n";
				   $link1_config .= "BOOTPROTO=\"none\"\n";
				   $link1_config .= "IPV6INIT=\"no\"\n";
				   $link1_config .= "ONBOOT=\"yes\"\n";
				   $link1_config .= "USERCTL=\"no\"\n";
				   $link1_config .= "MTU=\"1500\"\n";	# TODO: Make the MTU user-adjustable
				   $link1_config .= "NM_CONTROLLED=\"yes\"\n";
				   $link1_config .= "SLAVE=\"yes\"\n";
				   $link1_config .= "MASTER=\"".$say_interface."_bond1\"\n";
				   $link1_config .= "ZONE=\"".uc($say_interface)."\"\n";
				   
				my $link2_config =  "# $say_network - Link 2\n";
				   $link2_config .= "HWADDR=\"".uc($link2_mac)."\"\n";
				   $link2_config .= "UUID=\"".$link2_uuid."\"\n";
				   $link2_config .= "NAME=\"".$new_link2_nm_name."\"\n";
				   $link2_config .= "DEVICE=\"".$new_link2_iface."\"\n";
				   $link2_config .= "TYPE=\"Ethernet\"\n";
				   $link2_config .= "BOOTPROTO=\"none\"\n";
				   $link2_config .= "IPV6INIT=\"no\"\n";
				   $link2_config .= "ONBOOT=\"yes\"\n";
				   $link2_config .= "USERCTL=\"no\"\n";
				   $link2_config .= "MTU=\"1500\"\n";	# TODO: Make the MTU user-adjustable
				   $link2_config .= "NM_CONTROLLED=\"yes\"\n";
				   $link2_config .= "SLAVE=\"yes\"\n";
				   $link2_config .= "MASTER=\"".$say_interface."_bond1\"\n";
				   $link2_config .= "ZONE=\"".uc($say_interface)."\"\n";
				   
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					bridge_config => $bridge_config, 
					bond_config   => $bond_config, 
					link1_config  => $link1_config, 
					link2_config  => $link2_config, 
				}});
				
				# Make backups of existing files
				if (-e $bridge_file)
				{
					$anvil->Storage->backup({debug => 2, file => $bridge_file});
				}
				if (-e $bond_file)
				{
					$anvil->Storage->backup({debug => 2, file => $bond_file});
				}
				if (-e $old_link1_file)
				{
					$anvil->Storage->backup({debug => 2, file => $old_link1_file});
				}
				if (-e $old_link2_file)
				{
					$anvil->Storage->backup({debug => 2, file => $old_link2_file});
				}
				if (($old_link1_file ne $new_link1_file) && (-e $new_link1_file))
				{
					$anvil->Storage->backup({debug => 2, file => $new_link1_file});
				}
				if (($old_link2_file ne $new_link2_file) && (-e $new_link2_file))
				{
					$anvil->Storage->backup({debug => 2, file => $new_link2_file});
				}
				
				# Remove the old link if it was different, of down and up it if the same.
				if ($old_link1_iface ne $new_link1_iface)
				{
					# Delete the old interface
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0461", variables => { interface => $old_link1_nm_name }});
					my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{nmcli}." connection delete ".$old_link1_nm_name});
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
						output      => $output,
						return_code => $return_code, 
					}});
					
					if (-e $old_link1_file)
					{
						$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0589", variables => { file => $old_link1_file }});
						unlink $old_link1_file;
					}
				}
				else
				{
					# Down the interface
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0462", variables => { interface => $old_link1_nm_name }});
					my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{nmcli}." connection down ".$old_link1_nm_name});
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
						output      => $output,
						return_code => $return_code, 
					}});
				}
				
				# Shut down (and rename) Link 2
				if ($old_link2_iface ne $new_link2_iface)
				{
					# Delete the old interface
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0461", variables => { interface => $old_link2_nm_name }});
					my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{nmcli}." connection delete ".$old_link2_nm_name});
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
						output      => $output,
						return_code => $return_code, 
					}});
					
					sleep 1;
					if (-e $old_link2_file)
					{
						$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0589", variables => { file => $old_link2_file }});
						unlink $old_link2_file;
					}
				}
				else
				{
					# Down the interface
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0462", variables => { interface => $old_link2_nm_name }});
					my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{nmcli}." connection down ".$old_link2_nm_name});
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
						output      => $output,
						return_code => $return_code, 
					}});
				}

				### Write out the new configs
				# Are we writing a bridge config?
				if ($bridge)
				{
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, list => { 
						output      => $bridge_file,
						return_code => $bridge_config, 
					}});
					$anvil->Storage->write_file({
						debug     => 3,
						file      => $bridge_file, 
						body      => $bridge_config, 
						user      => "root", 
						group     => "root", 
						mode      => "0644", 
						overwrite => 1
					});
				}
				
				# Bond, Link 1 and Link 2
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, list => { 
					output      => $bond_file,
					return_code => $bond_config, 
				}});
				$anvil->Storage->write_file({
					debug     => 3,
					file      => $bond_file,
					body      => $bond_config,   
					user      => "root", 
					group     => "root", 
					mode      => "0644", 
					overwrite => 1,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, list => { 
					output      => $new_link1_file,
					return_code => $link1_config, 
				}});
				$anvil->Storage->write_file({
					debug     => 3,
					file      => $new_link1_file, 
					body      => $link1_config, 
					user      => "root", 
					group     => "root", 
					mode      => "0644", 
					overwrite => 1,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, list => { 
					output      => $new_link2_file,
					return_code => $link2_config, 
				}});
				$anvil->Storage->write_file({
					debug     => 3,
					file      => $new_link2_file,
					body      => $link2_config, 
					user      => "root", 
					group     => "root", 
					mode      => "0644", 
					overwrite => 1,
				});
				
				# If the NICs names have changed, rename them now.
				if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && 
				    ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface})        && 
				    ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} ne $new_link1_iface))
				{
					rename_interface($anvil, $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}, $new_link1_iface);
				}
				if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}) && 
				    ($anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface})        && 
				    ($anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface} ne $new_link2_iface))
				{
					rename_interface($anvil, $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}, $new_link2_iface);
				}
			}
			elsif ((exists $anvil->data->{variables}{form}{config_step2}{$link1_key}{value}) && ($anvil->Validate->mac({mac => $anvil->data->{variables}{form}{config_step2}{$link1_key}{value}})))
			{
				### NOTE: This only applies when configuring Striker dashboards. They can't 
				###       be 'dhcp', either, so no checks are made for those cases. Likewise,
				###       bridges are not used.
				# Single interface, set it up
				my $link1_mac = $anvil->data->{variables}{form}{config_step2}{$link1_key}{value};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link1_mac => $link1_mac }});
				
				my $say_network      = "";
				my $say_interface    = "";
				my $interface_prefix = "";
				if ($network_type eq "bcn")
				{
					$say_network      = "Back-Channel Network ".$network_count;
					$say_interface    = "bcn".$network_count;
					$interface_prefix = "BCN";
				}
				elsif ($network_type eq "sn")
				{
					$say_network      = "Storage Network ".$network_count; 
					$say_interface    = "sn".$network_count;
					$interface_prefix = "SN";
				}
				elsif ($network_type eq "ifn")
				{
					$say_network      = "Internet-Facing Network ".$network_count; 
					$say_interface    = "ifn".$network_count;
					$interface_prefix = "IFN";
				}
				my $say_defroute    = $is_gateway ? "yes" : "no";
				my $cidr            = $anvil->Convert->cidr({subnet_mask => $subnet_mask});
				my $new_link1_file  = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_1";
				my $old_link1_file  = $new_link1_file;
				my $new_link1_iface = $say_interface."_link1";
				if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}))
				{
					$old_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface};
				}
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					cidr            => $cidr, 
					new_link1_file  => $new_link1_file,
					new_link1_iface => $new_link1_iface, 
					old_link1_file  => $old_link1_file, 
					say_defroute    => $say_defroute, 
				}});
				
				my $new_link1_nm_name = $interface_prefix." ".$network_count." - Link 1";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_link1_nm_name => $new_link1_nm_name }});
				push @{$new_interfaces}, $new_link1_nm_name;
				
				# Gather (or create) UUIDs
				my $link1_uuid = get_uuid_from_interface_file($anvil, $old_link1_file);
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link1_uuid => $link1_uuid }});
				
				my $link1_config =  "# $say_network - Link 1\n";
				   $link1_config .= "HWADDR=\"".uc($link1_mac)."\"\n";
				   $link1_config .= "UUID=\"".$link1_uuid."\"\n";
				   $link1_config .= "NAME=\"".$new_link1_nm_name."\"\n";
				   $link1_config .= "DEVICE=\"".$new_link1_iface."\"\n";
				   $link1_config .= "TYPE=\"Ethernet\"\n";
				   $link1_config .= "BOOTPROTO=\"none\"\n";
				   $link1_config .= "IPV6INIT=\"no\"\n";
				   $link1_config .= "ONBOOT=\"yes\"\n";
				   $link1_config .= "IPADDR=\"".$ip_address."\"\n";
				   $link1_config .= $cidr ? "PREFIX=\"".$cidr."\"\n" : "NETMASK=\"".$subnet_mask."\"\n";
				if ($is_gateway)
				{
					$link1_config .= "GATEWAY=\"".$gateway."\"\n";
					for (my $i = 0; $i < @{$dns}; $i++)
					{
						$link1_config .= "DNS".($i+1)."=\"".$dns->[$i]."\"\n";
					}
				}
				$link1_config .= "DEFROUTE=\"".$say_defroute."\"\n";
				$link1_config .= "USERCTL=\"no\"\n";
				$link1_config .= "MTU=\"1500\"\n";	# TODO: Make the MTU user-adjustable
				$link1_config .= "NM_CONTROLLED=\"yes\"\n";
				$link1_config .= "ZONE=\"".uc($say_interface)."\"";
				   
				# Backup the existing link1 file, if it exists.
				if (-e $old_link1_file)
				{
					$anvil->Storage->backup({debug => 2, file => $old_link1_file});
				}
				
				# Write out the link1 config file.
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, list => { 
					output      => $new_link1_file,
					return_code => $link1_config, 
				}});
				$anvil->Storage->write_file({
					debug     => 3, 
					file      => $new_link1_file, 
					body      => $link1_config, 
					user      => "root", 
					group     => "root", 
					mode      => "0644", 
					overwrite => 1,
				});
				
				# If the name differs from old, delete the old interface.
				if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && 
				    ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface})        && 
				    ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} ne $new_link1_iface))
				{
					# Delete the old interface
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0461", variables => { interface => $old_link1_nm_name }});
					$anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{nmcli}." connection delete ".$old_link1_nm_name});
					
					rename_interface($anvil, $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}, $new_link1_iface);
				}
				else
				{
					# Down the interface
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0462", variables => { interface => $old_link1_nm_name }});
					$anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{nmcli}." connection down ".$old_link1_nm_name});
				}
			}
			else
			{
				# Doesn't exist, skip.
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0149", variables => { network => $this_network }});
				next;
			}
		}
	}
	
	# Re-read the config
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0463"});
	my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{nmcli}." connection reload"});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
		output      => $output,
		return_code => $return_code, 
	}});
	
	# Wait for a DB connection. We'll wait up to 130 seconds (updelay is 120 seconds, plus a small buffer).
	my $wait_until = time + 130;
	until ($anvil->data->{sys}{database}{connections})
	{
		$anvil->refresh();
		$anvil->Database->connect();
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, secure => 0, key => "log_0132"});
		if (not $anvil->data->{sys}{database}{connections})
		{
			if (time > $wait_until)
			{
				# Failed to reconnect, exit. 
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0107"});
				$anvil->nice_exit({exit_code => 2});
			}
			
			# No databases, sleep and then try again.
			sleep 2;
		}
	}
	
	# We're half-way there.
	$anvil->Job->update_progress({
		progress => 50, 
		job_uuid => $anvil->data->{job}{uuid},
	});
	
	# If any virtio bridges exist, remove it/them.
	my $start                   = 0;
	(my $bridges, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{virsh}." net-list"});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridges => $bridges, return_code => $return_code }});
	if ($return_code)
	{
		### NOTE: We're doing a bunch of deletes, so to be safe we statically define the directory 
		###       here.
		# Libvirtd isn't running, check the directory directly.
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0478"});
		my $directory = "/etc/libvirt/qemu/networks/autostart";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { directory => $directory }});
		
		if (-d $directory)
		{
			# Delete all files.
			local(*DIRECTORY);
			opendir(DIRECTORY, $directory);
			while(my $file = readdir(DIRECTORY))
			{
				next if $file eq ".";
				next if $file eq "..";
				my $full_path = $directory."/".$file;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
					file      => $file,
					full_path => $full_path,
				}});
				if (-l $full_path)
				{
					# It's a symlink, remove it.
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0479", variables => { 'symlink' => $full_path }});
					unlink $full_path;
					if (-l $full_path)
					{
						# It didn't work...
						$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 0, priority => "err", key => "error_0132", variables => { 'symlink' => $full_path }});;
					}
				}
			}
			closedir(DIRECTORY);
		}
	}
	else
	{
		foreach my $line (split/\n/, $bridges)
		{
			$line = $anvil->Words->clean_spaces({string => $line});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
			if ($line =~ /^----------/)
			{
				$start = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { start => $start }});
				next;
			}
			next if not $start;
			my $bridge = ($line =~ /(.*?)\s/)[0];
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridge => $bridge }});
			
			$anvil->data->{virsh}{bridge}{$bridge} = 1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virsh::bridge::$bridge" => $anvil->data->{virsh}{bridge}{$bridge} }});
		}
		
		foreach my $bridge (sort {$a cmp $b} keys %{$anvil->data->{virsh}{bridge}})
		{
			# Destroy (stop) it.
			my ($destroy, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." net-destroy ".$bridge});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { destroy => $destroy, return_code => $return_code }});
			
			# Disable it from auto-start.
			(my $disable, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." net-autostart ".$bridge." --disable"});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disable => $disable, return_code => $return_code }});
			
			# Undefine (delete)
			(my $undefine, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." net-undefine ".$bridge});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { undefine => $undefine, return_code => $return_code }});
		}
	}
	
	$anvil->Job->update_progress({
		progress => 75, 
		job_uuid => $anvil->data->{job}{uuid},
	});

	return(0);
}

# This renames a network interface
sub rename_interface
{
	my ($anvil, $old_link_name, $new_link_name) = @_;
	
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0465", variables => { 
		old_interface => $old_link_name,
		new_interface => $new_link_name,
	}});
	
	# Take the old name down.
	my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{ip}." link set ".$old_link_name." down"});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
		output      => $output,
		return_code => $return_code, 
	}});
	
	# Rename
	($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{ip}." link set ".$old_link_name." name ".$new_link_name});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
		output      => $output,
		return_code => $return_code, 
	}});
	
	# Bring up the new interface
	($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{ip}." link set ".$new_link_name." up"});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 
		output      => $output,
		return_code => $return_code, 
	}});
	
	return(0);
}

# This will read a network interface file and return the UUID="x" value. If the file doesn't exist or the 
# UUID was not found, a new UUID is generated and returned.
sub get_uuid_from_interface_file
{
	my ($anvil, $file) = @_;
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 'print' => 1, key => "log_0131", variables => { function => "get_uuid_from_interface_file" }});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file }});
	
	my $uuid = "";
	if (-e $file)
	{
		my $body = $anvil->Storage->read_file({file => $file});
		foreach my $line (split/\n/, $body)
		{
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
			$line =~ s/#.*//;
			if ($line =~ /UUID=\"(.*?)\"/)
			{
				my $test_uuid = $1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { test_uuid => $test_uuid }});
				if ($anvil->Validate->uuid({uuid => $test_uuid}))
				{
					$uuid = $test_uuid;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uuid => $uuid }});
				}
				last;
			}
		}
	}
	if (not $uuid)
	{
		$uuid = $anvil->Get->uuid();
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uuid => $uuid }});
	}
	
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uuid => $uuid }});
	return($uuid);
}

# This will pick up the job, or exit.
sub pickup_job_details
{
	my ($anvil) = @_;
	
	# If this returns '1', the job-uuid was bad. If it returns '2', another process is running.
	my $return = $anvil->Job->get_job_details({
		check    => 1, 
		job_uuid => $anvil->data->{switches}{'job-uuid'},
	});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'return' => $return }});
	if ($return == 1)
	{
		# It's not a valid UUID.
		$anvil->nice_exit({exit_code => 6});
	}
	if ($return == 2)
	{
		# This job is being handled by another process that is still alive.
		$anvil->nice_exit({exit_code => 3});
	}
	
	# Still alive? Good.
	my $job_picked_up_by = $anvil->data->{jobs}{job_picked_up_by};
	my $job_progress     = $anvil->data->{jobs}{job_progress};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
		job_picked_up_by => $job_picked_up_by,
		job_progress     => $job_progress,
	}});
	
	# See if the job was picked up by another running instance.
	if ($job_picked_up_by)
	{
		# The previous job is gone if we're still alive, we'll take this over.
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 'print' => 1, key => "log_0147", variables => { 
			pid     => $job_picked_up_by,
			percent => $job_progress, 
		}});
	}
	
	# This will store the variables from the database
	$anvil->data->{variables} = {};
	
	# If we're still alive, pick up the details.
	my $query   = "
SELECT 
    variable_name, 
    variable_value 
FROM 
    variables 
WHERE 
    variable_name 
LIKE 
    'form::config_step%' 
AND 
    variable_source_table = 'hosts' 
AND 
    variable_source_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})."
;";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, 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 => 3, list => { 
		results => $results, 
		count   => $count,
	}});
	foreach my $row (@{$results})
	{
		my $this_variable = $row->[0];
		my $this_value    = $row->[1];
		my $secure        = $this_variable =~ /passw/ ? 1 : 0;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:this_variable' => $this_variable, 
			's2:this_value'    => $anvil->Log->is_secure($this_value), 
		}});
		
		$anvil->_make_hash_reference($anvil->data->{variables}, $this_variable, $this_value);
	}
	
	# Clear previous data
	$anvil->Job->update_progress({
		progress => 0, 
		message  => "clear", 
		job_uuid => $anvil->data->{job}{uuid},
	});
	
	# Record that we've picked up this job.
	$anvil->Job->update_progress({
		progress => 1, 
		message  => "message_0015", 
		job_uuid => $anvil->data->{job}{uuid},
	});
	
	# If we're in a cluster, abort.
	my ($problem) = $anvil->Cluster->parse_cib();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
	if ((not $problem) && ($anvil->data->{cib}{parsed}{'local'}{ready}))
	{
		# We're in a cluster, abort.
		$anvil->Job->update_progress({
			progress => 100, 
			message  => "error_0250", 
			job_uuid => $anvil->data->{job}{uuid},
		});
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0250"});
		$anvil->nice_exit({exit_code => 7});
	}
	
	return(0);
}
