#!/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.
# 8 = Duplicate NICs assigned to the same MAC address
# 9 = nmcli call failed.
# 
# 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 Anvil::Tools;
use Data::Dumper;
use Text::Diff;

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({list => [
	"file", 
	"job-uuid",
	"debug", 
], man => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});

if (($anvil->data->{switches}{debug}) && (not -e $anvil->data->{path}{configs}{'anvil.debug'}))
{
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "log_0843", variables => {
		file => $anvil->data->{path}{configs}{'anvil.debug'},
		host => $anvil->Get->short_host_name(),
	}});
	my $problem = $anvil->Storage->write_file({
		file      => $anvil->data->{path}{configs}{'anvil.debug'}, 
		body      => "",
		overwrite => 1,
		user      => "root", 
		group     => "root", 
		mode      => "0666",
	});
}

# 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});
}

# Make sure that we set the network->wait_on_network() timeout to 60 seconds.
$anvil->Storage->update_config({
	append   => 1,
	variable => "network::wait_on_network::timeout",
	value    => 60,
});

# Because we're working with the network config, we can't reliable use cached connections.
$anvil->data->{sys}{net}{always_reconnect} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { 
	"sys::net::always_reconnect" => $anvil->data->{sys}{net}{always_reconnect},
}});

# Connect
$anvil->Database->connect({check_for_resync => 1});
$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, 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);
overwrite_variables_with_switches($anvil);

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

# Reconfigure the network.
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);

# Update NTP, if needed.
my $ntp_updated = $anvil->System->check_ntp({debug => 2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ntp_updated => $ntp_updated }});

# If we're a DR host, grow the PV if needed.
my $host_type = $anvil->Get->host_type;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }});
if ($host_type eq "dr")
{
	$anvil->Storage->auto_grow_pv({debug => 2});
}

# Make sure that we set the network->wait_on_network() timeout back to 180.
$anvil->Storage->update_config({
	append   => 1,
	variable => "network::wait_on_network::timeout",
	value    => 180,
});

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

# We're done
$anvil->Job->update_progress({
	progress  => 100, 
	message   => "",
	job_uuid  => $anvil->data->{job}{uuid},
});

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


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

# This does a reboot.
sub do_reboot
{
	my ($anvil) = @_;

	# Tell the user why we're rebooting
	$anvil->Job->update_progress({
		progress  => $anvil->Job->bump_progress({steps => 5}), 
		message   => "message_0388", 
		job_uuid  => $anvil->data->{job}{uuid},
		'print'   => 1,
		log_level => 1,
		file      => $THIS_FILE, 
		line      => __LINE__,
	});
	my $time_left = 60;
	while ($time_left)
	{
		# Give them the countdown.
		$anvil->Job->update_progress({
			progress  => $anvil->Job->bump_progress({steps => 1}), 
			message   => "log_0626", 
			job_uuid  => $anvil->data->{job}{uuid},
			'print'   => 1,
			log_level => 1,
			file      => $THIS_FILE, 
			line      => __LINE__,
			variables => { seconds => $time_left },
		});
		sleep 10;
		$time_left -= 10;
	}
	
	# Last, log that we're going down now.
	$anvil->Job->update_progress({
		progress  => $anvil->Job->bump_progress({steps => 1}), 
		message   => "message_0389", 
		job_uuid  => $anvil->data->{job}{uuid},
		'print'   => 1,
		log_level => 1,
		file      => $THIS_FILE, 
		line      => __LINE__,
	});
	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 }});
	
	return(0);
}

# This updates the passwords on the root user, admin user, database and striker-ui-api user.
sub update_passwords
{
	my ($anvil) = @_;
	
	# Have we been asked to set a password?
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { 
		"config::striker_password" => $anvil->data->{config}{striker_password},
	}});
	if ($anvil->data->{config}{striker_password})
	{
		# Set the passwords
		my $password  = $anvil->data->{config}{striker_password};
		my $temp_file = "/tmp/anvil-pw.txt";
		$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->Job->update_progress({
				progress   => 100, 
				message    => "message_0030", 
				log_level  => 1,
				'print'    => 1, 
				job_uuid   => $anvil->data->{job}{uuid},
				job_status => "failed", 
				file       => $THIS_FILE, 
				line       => __LINE__,
				variables  => { file => $temp_file },
			});
			$anvil->nice_exit({exit_code => 5});
		}
		else
		{
			my $wait_until = time + 120;
			my $waiting    = 1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				waiting    => $waiting, 
				wait_until => $wait_until,
			}});
			while ($waiting)
			{
				my $shell_call = $anvil->data->{path}{exe}{'anvil-change-password'}.$anvil->Log->switches." --confirm --no-wait --password-file ".$temp_file;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
				
				my ($output, $return_code) = $anvil->System->call({
					debug      => 2, 
					timeout    => 30, 
					shell_call => $shell_call });
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					output      => $output, 
					return_code => $return_code,
				}});
				
				if ($return_code)
				{
					# Something went wrong
					if (time > $wait_until)
					{
						# Give up.
						$waiting = 0;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
					}
					else
					{
						$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0011", variables => { return_code => $return_code }});
					}
				}
				else
				{
					# Success
					$waiting = 0;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
				}
			}
			
			# Unlink the temp file.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0263", variables => { file => $temp_file }});
			unlink $temp_file;
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0283"});
		}
	}
	
	$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 $prefix        = $anvil->data->{config}{prefix};
	my $sequence      = $anvil->data->{config}{sequence};
	my $domain        = $anvil->data->{config}{domain};
	my $new_host_name = $anvil->data->{config}{host_name};
	my $organization  = $anvil->data->{config}{organization};
	my $bcn_count     = $anvil->data->{config}{bcn_count};
	my $ifn_count     = $anvil->data->{config}{ifn_count};
	my $sn_count      = $anvil->data->{config}{sn_count};
	my $mn_count      = $anvil->data->{config}{mn_count};
	my $type          = $anvil->Get->host_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		prefix        => $prefix, 
		sequence      => $sequence,
		domain        => $domain, 
		organization  => $organization, 
		bcn_count     => $bcn_count, 
		sn_count      => $sn_count,
		mn_count      => $mn_count,
		ifn_count     => $ifn_count,
		new_host_name => $new_host_name, 
		type          => $type, 
	}});
	
	# We'll set this to '1' if we reconfigure the network
	$anvil->data->{sys}{reboot} = 0;
	
	# 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, 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, 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, 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, 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, list => { pretty_host_name => $pretty_host_name }});
		}
		
		### NOTE: We check the current host 
		# Update the hostname, if needed.
		my $environment_hostname = $ENV{HOSTNAME} // ""; 
		my $system_hostname      = $anvil->Get->host_name({refresh => 1});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			"s1:new_host_name"        => $new_host_name, 
			"s2:environment_hostname" => $environment_hostname,
			"s3:system_hostname"      => $system_hostname, 
		}});
		
		if (($new_host_name ne $system_hostname) or (($environment_hostname) && ($new_host_name ne $environment_hostname)))
		{
			# Update the hostname and mark a reboot required. We must have all cached hostnames 
			# updated before joining pacemaker, and the safest option to ensure that is a reboot.
			my ($host_name, $descriptive_host_name) = $anvil->System->host_name({set => $new_host_name, pretty => $pretty_host_name});
			$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  => $anvil->Job->bump_progress({steps => 5}), 
					message   => "message_0016", 
					job_uuid  => $anvil->data->{job}{uuid},
					'print'   => 1,
					log_level => 1,
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { host_name => $new_host_name },
				});
			}
			else
			{
				# Failed
				$anvil->Job->update_progress({
					progress   => 100, 
					message    => "message_0017", 
					job_uuid   => $anvil->data->{job}{uuid},
					'print'    => 1,
					log_level  => 1,
					file       => $THIS_FILE, 
					line       => __LINE__,
					job_status => "failed", 
					variables  => {
						host_name     => $new_host_name,
						bad_host_name => $host_name,
					},
				});
				$anvil->nice_exit({exit_code => 4});
			}
			
			$anvil->data->{sys}{reboot} = 1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
				"sys::reboot" => $anvil->data->{sys}{reboot}, 
			}});
		}
	}
	
	# Now configure the network.
	my $dns = $anvil->data->{config}{dns} ? [split/,/, $anvil->data->{config}{dns}] : [];
	$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           = $anvil->data->{config}{gateway};
	my $gateway_interface = $anvil->data->{config}{gateway_interface};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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", "mn")
		{
			$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; }
			elsif ($network_type eq "mn")  { $count = $mn_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->{config}{$ip_key}) && ($anvil->data->{config}{$ip_key} eq "dhcp"))
				{
					$gateway_interface = $network_type.$network_count;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { gateway_interface => $gateway_interface }});
					last;
				}
			}
		}
	}

	# Before we continue, see if there's two interfaces pointing at the same NIC. If so, the node would 
	# endlessly reboot.
	my $macs  = {};
	my $query = "
SELECT 
    variable_uuid, 
    variable_name, 
    variable_value 
FROM 
    variables 
WHERE 
    variable_name LIKE '\%_mac_to_set::value' 
AND 
    variable_value != 'DELETED' 
AND 
    variable_source_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." 
ORDER BY 
    variable_name ASC;";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
	my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
	my $count   = @{$results};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		results => $results, 
		count   => $count,
	}});
	foreach my $row (@{$results})
	{
		my $variable_uuid  = $row->[0];
		my $variable_name  = $row->[1];
		my $variable_value = $row->[2];
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:variable_uuid'  => $variable_uuid, 
			's2:variable_name'  => $variable_name,
			's3:variable_value' => $variable_value, 
		}});

		# An undefined interface will have the MAC address value set to '1', ignore those.
		next if $variable_value eq "1";
		
		if ($variable_name =~ /form::config_step2::(.*?)_mac_to_set::value/)
		{
			my $device = lc($1);
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { device => $device}});
			
			if (not exists $macs->{$variable_value})
			{
				$macs->{$variable_value} = $device;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					"macs->${variable_value}" => $macs->{$variable_value},
				}});
			}
			else
			{
				# Fail out!
				$anvil->Job->update_progress({
					progress   => 100,
					message    => "error_0376", 
					job_status => "failed", 
					'print'    => 1, 
					log_level  => 1, 
					file       => $THIS_FILE, 
					line       => __LINE__,
					variables  => {
						mac_address => $variable_value, 
						iface1      => $macs->{$variable_value}, 
						iface2      => $device, 
					},
				});
				$anvil->nice_exit({exit_code => 8});
			}
		}
	}
	
	# Read the existing network data 
	$anvil->Network->collect_data({debug => 2, start => 1});
	
	# This will be set to '1' if we make a change.
	my $changes        = 0;
	my $new_interfaces = [];
	
	my $network_type = $anvil->System->check_network_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }});

	# Configure the network using Network Manager
	reconfigure_interfaces($anvil);
	
	### NOTE: If we're not a striker, then update the job to say we're disconnecting to 
	###       reconfigure the network.
	my $host_type = $anvil->Get->host_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }});
	if ($host_type ne "striker")
	{
		$anvil->Job->update_progress({
			progress  => $anvil->Job->bump_progress({set => 50}), 
			message   => "message_0415", 
			log_level => 1,
			'print'   => 1, 
			job_uuid  => $anvil->data->{job}{uuid},
			file      => $THIS_FILE, 
			line      => __LINE__,
		});
		$anvil->Database->disconnect({debug => 2});
	}
	
	# These can brake the connection.
	$anvil->data->{sys}{restart_nm} = 0;
	reconfigure_bonds($anvil);
	reconfigure_bridges($anvil);
	reconfigure_ip_addresses($anvil);
	
	# Sleep a few seconds, and then restart NetworkManager.service.
	my $nm_running = $anvil->System->check_daemon({debug => 2, daemon => "NetworkManager.service"});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		nm_running        => $nm_running,
		'sys::restart_nm' => $anvil->data->{sys}{restart_nm},
	}});
	if (($anvil->data->{sys}{restart_nm}) or (not $nm_running))
	{
		$anvil->System->stop_daemon({debug => 2, daemon => "NetworkManager.service"});
		$anvil->System->start_daemon({debug => 2, daemon => "NetworkManager.service"});
		sleep 2;
	}
	
	# Reconnect!
	if ($host_type ne "striker")
	{
		my $time_now        = time;
		my $restart_nm_time = $time_now + 30;
		my $wait_until      = $time_now + 60;
		my $nm_restarted    = 0;
		my $waiting         = 1;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:time_now'        => $time_now, 
			's2:restart_nm_time' => $restart_nm_time, 
			's3:wait_until'      => $wait_until,
		}});
		while ($waiting)
		{
			$anvil->refresh();
			$anvil->Database->connect();
			
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sys::database::connections" => $anvil->data->{sys}{database}{connections} }});
			if ($anvil->data->{sys}{database}{connections})
			{
				# We're back! 
				$waiting = 0;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
			}
			else
			{
				# Sleep for a bit.
				my $now_time  = time;
				my $time_left = $wait_until - $now_time;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					now_time  => $now_time,
					time_left => $time_left, 
				}});
				if ($time_left > 0)
				{
					if (($now_time > $restart_nm_time) && (not $nm_restarted))
					{
						# Kick the network
						$nm_restarted = 1;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { nm_restarted => $nm_restarted }});
						
						# Restart the daemon
						$anvil->System->stop_daemon({debug => 2, daemon => "NetworkManager.service"});
						$anvil->System->start_daemon({debug => 2, daemon => "NetworkManager.service"});
					}
					
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0829", variables => { time_left => $time_left }});
					sleep 2;
				}
				else
				{
					# Give up and reboot.
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "warning_0169"});
					
					# Don't use 'do_reboot()' as we've got no DB connection.
					my $shell_call = $anvil->data->{path}{exe}{systemctl}." reboot";
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { shell_call => $shell_call }});
					
					# We should be dead by now...
					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 }});
				}
			}
		}
		
		$anvil->Job->update_progress({
			progress  => $anvil->Job->bump_progress({set => 75}), 
			message   => "message_0416", 
			log_level => 1,
			'print'   => 1, 
			job_uuid  => $anvil->data->{job}{uuid},
			file      => $THIS_FILE, 
			line      => __LINE__,
		});
	}
	
	# If any virtio bridges exist, remove it/them.
	my $start      = 0;
	my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." net-list";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
	
	my ($bridges, $return_code) = $anvil->System->call({shell_call => $shell_call});
	$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}{setsid}." --wait ".$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}{setsid}." --wait ".$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}{setsid}." --wait ".$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 => $anvil->Job->bump_progress({steps => 1}), 
		job_uuid => $anvil->data->{job}{uuid},
	});

	return(0);
}

sub reconfigure_bridges
{
	my ($anvil) = @_;
	
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0853"});
	my $local_host = $anvil->Get->short_host_name();
	my $bcn_count  = $anvil->data->{config}{bcn_count};
	my $ifn_count  = $anvil->data->{config}{ifn_count};
	my $type       = $anvil->Get->host_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		local_host => $local_host,
		bcn_count  => $bcn_count,
		ifn_count  => $ifn_count, 
		type       => $type, 
	}});
	
	if ($type eq "striker")
	{
		# No bridges on strikers.
		return(0);
	}
	
	foreach my $network_type ("bcn", "ifn")
	{
		$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 "ifn") { $count = $ifn_count; }
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
		
		# This is the old type of network config
		foreach my $i (1..$count)
		{
			# This is almost always going to be on the bond, but bridges can be on interfaces on 
			# MicroAnvil! nodes and DR hosts.
			my $bridge_name = $network_type.$i."_bridge1";
			my $on_device   = $network_type.$i."_bond1";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				bridge_name => $bridge_name,
				on_device   => $on_device, 
			}});
			
			# If there's no bond, we'll using link1 if it's available, otherwise link2. If none 
			# are available, we're done.
			my $on_device_uuid = "";
			if ($anvil->data->{nmcli}{device}{$on_device}{uuid})
			{
				$on_device_uuid = $anvil->data->{nmcli}{device}{$on_device}{uuid};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { on_device_uuid => $on_device_uuid }});
			}
			else
			{
				# If there a link1 or link2 device?
				my $link1_key  = $network_type.$i."_link1";
				my $link1_uuid = "";
				my $link2_key  = $network_type.$i."_link2";
				my $link2_uuid = "";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					link1_key => $link1_key,
					link2_key => $link2_key,
				}});
				foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{nmcli}{uuid}})
				{
					my $match = $anvil->data->{nmcli}{uuid}{$uuid}{'match.interface-name'} // "";
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { match => $match }});
					next if not $match;
					foreach my $interface (split/,/, $match)
					{
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }});
						if ($interface eq $link1_key)
						{
							$link1_uuid = $uuid;
							$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link1_uuid => $link1_uuid }});
							last;
						}
						elsif ($interface eq $link2_key)
						{
							$link2_uuid = $uuid;
							$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link2_uuid => $link2_uuid }});
							last;
						}
					}
				}
				
				if ($link1_uuid)
				{
					# Use this interface
					$on_device      = $link1_key;
					$on_device_uuid = $link1_uuid;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
						on_device      => $on_device, 
						on_device_uuid => $on_device_uuid,
					}});
				}
				elsif ($link2_uuid)
				{
					# Use this interface
					$on_device      = $link2_key;
					$on_device_uuid = $link2_uuid;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
						on_device      => $on_device, 
						on_device_uuid => $on_device_uuid,
					}});
				}
				else
				{
					# No interfaces found for this interface, we can't proceed.
					$anvil->Job->update_progress({
						progress   => 100, 
						message    => "error_0004", 
						log_level  => 1,
						'print'    => 1, 
						job_uuid   => $anvil->data->{job}{uuid},
						job_status => "failed", 
						variables  => {
							bridge_name => $bridge_name, 
						},
					});
					$anvil->nice_exit({exit_code => 1});
				}
				
				# Mention that we're using link1
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0107", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { 
						on_device      => $on_device, 
						on_device_uuid => $on_device_uuid,
					},
				});
			}
			
			# Checking if the bridge exists and that it is on the requested device
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0400", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { 
					bridge_name => $bridge_name,
					on_device   => $on_device, 
				},
			});
			
			if (exists $anvil->data->{nmcli}{bridge}{$bridge_name})
			{
				# The bridge exists.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0401", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => {},
				});
			}
			else
			{
				# If there are ifcfg files for this bridge, move it.
				my $network_type = $anvil->System->check_network_type();
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }});
				if ($network_type eq "ifcfg")
				{
					my $ifcfg_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$bridge_name;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ifcfg_file => $ifcfg_file }});
					if (-f $ifcfg_file)
					{
						# It exists, move it.
						$anvil->Job->update_progress({
							progress  => $anvil->Job->bump_progress({steps => 1}), 
							message   => "message_0417", 
							log_level => 1,
							'print'   => 1, 
							file      => $THIS_FILE, 
							line      => __LINE__,
							job_uuid  => $anvil->data->{job}{uuid},
							variables => { file => $ifcfg_file },
						});
						$anvil->Storage->backup({file => $ifcfg_file});
						unlink $ifcfg_file;
					}
				}
				
				# Create the bridge.
				my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection add type bridge con-name ".$bridge_name." ifname ".$bridge_name;
				$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});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					output      => $output,
					return_code => $return_code, 
				}});
				
				# NM seems to have a race issue, so we sleep a second after nmcli calls.
				$anvil->data->{sys}{restart_nm} = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::restart_nm' => $anvil->data->{sys}{restart_nm} }});
				
				if ($return_code)
				{
					# Failed to add the bridge
					$anvil->Job->update_progress({
						progress   => 100, 
						message    => "error_0485", 
						log_level  => 1,
						'print'    => 1, 
						job_uuid   => $anvil->data->{job}{uuid},
						job_status => "failed", 
						variables  => {
							bridge_name => $bridge_name, 
							return_code => $return_code,
							output      => $output, 
						},
					});
					$anvil->nice_exit({exit_code => 1});
				}
				
				my $bridge_uuid = ($output =~ /\((.*?)\) successfully added/)[0];
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridge_uuid => $bridge_uuid }});
				
				if ($bridge_uuid)
				{
					# Disabling DHCP on the new bridge 
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0408", 
						log_level => 1,
						'print'   => 1, 
						job_uuid  => $anvil->data->{job}{uuid},
						file      => $THIS_FILE, 
						line      => __LINE__,
						variables => { 
							device      => $bridge_name,
							device_uuid => $bridge_uuid, 
						},
					});
					my ($output, $return_code) = $anvil->Network->modify_connection({uuid => $bridge_uuid, variable => "ipv4.method", value => "disabled"});
					   ($output, $return_code) = $anvil->Network->modify_connection({uuid => $bridge_uuid, variable => "ipv6.method", value => "disabled"});
					
					# Disable STP on the bridge (critical for Cisco BPDU Guard support, if STP is enabled, Cisco kills the port)
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0528", 
						log_level => 1,
						'print'   => 1, 
						job_uuid  => $anvil->data->{job}{uuid},
						file      => $THIS_FILE, 
						line      => __LINE__,
						variables => { 
							device      => $bridge_name,
							device_uuid => $bridge_uuid, 
						},
					});
			    ($output, $return_code) = $anvil->Network->modify_connection({uuid => $bridge_uuid, variable => "bridge.stp", value => "no"});
					
					my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection up ".$bridge_name;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
					($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
						output      => $output,
						return_code => $return_code, 
					}});
					
					# NM seems to have a race issue, so we sleep a second after nmcli calls.
					$anvil->data->{sys}{restart_nm} = 1;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::restart_nm' => $anvil->data->{sys}{restart_nm} }});
				}
				
				# Rescan.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0394", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { },
				});
				$anvil->Network->collect_data();
			}
			
			# Checking that the device is connected to this bridge
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0404", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { on_device => $on_device },
			});
			my $bridge_uuid          = $anvil->data->{nmcli}{bridge}{$bridge_name}{uuid};
			my $on_device_parent     = $anvil->data->{nmcli}{uuid}{$on_device_uuid}{'connection.master'}     // "";
			my $on_device_child_type = $anvil->data->{nmcli}{uuid}{$on_device_uuid}{'connection.slave-type'} // "";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				bridge_uuid          => $bridge_uuid,
				on_device_parent     => $on_device_parent, 
				on_device_child_type => $on_device_child_type, 
			}});
			
			if ($on_device_parent)
			{
				if ($on_device_parent eq $bridge_name)
				{
					# The device is connected to the bridge already.
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0405", 
						log_level => 1,
						'print'   => 1, 
						job_uuid  => $anvil->data->{job}{uuid},
						file      => $THIS_FILE, 
						line      => __LINE__,
						variables => { },
					});
					next;
				}
				else
				{
					# The device is on another bridge, moving it.
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0406", 
						log_level => 1,
						'print'   => 1, 
						job_uuid  => $anvil->data->{job}{uuid},
						file      => $THIS_FILE, 
						line      => __LINE__,
						variables => { on_device_parent => $on_device_parent },
					});
				}
			}
			else
			{
				# The device is not on this bridge, connecting it
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0407", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { on_device_parent => $on_device_parent },
				});
			}
			
			# Disabling DHCP on the device before connecting it.
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0408", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { 
					device      => $on_device,
					device_uuid => $on_device_uuid, 
				},
			});
			my ($output, $return_code) = $anvil->Network->modify_connection({uuid => $on_device_uuid, variable => "ipv4.method", value => "disabled"});
			   ($output, $return_code) = $anvil->Network->modify_connection({uuid => $on_device_uuid, variable => "ipv6.method", value => "disabled"});
			
			# Connect it now.
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0409", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { },
			});
			my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify ".$on_device_uuid." master ".$bridge_name;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
			($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				output      => $output,
				return_code => $return_code, 
			}});
			
			# NM seems to have a race issue, so we sleep a second after nmcli calls.
			$anvil->data->{sys}{restart_nm} = 1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::restart_nm' => $anvil->data->{sys}{restart_nm} }});
			
			if ($return_code)
			{
				# Failed to connect the device to the bridge.
				$anvil->Job->update_progress({
					progress   => 100, 
					message    => "error_0486", 
					log_level  => 1,
					'print'    => 1, 
					job_uuid   => $anvil->data->{job}{uuid},
					job_status => "failed", 
					variables  => {
						on_device   => $on_device, 
						bridge_name => $bridge_name, 
						return_code => $return_code,
						output      => $output, 
					},
				});
				$anvil->nice_exit({exit_code => 1});
			}
			
			# Reset the device.
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0403", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { 
					device      => $on_device,
					device_uuid => $on_device_uuid, 
				},
			});
			($output, $return_code) = $anvil->Network->reset_connection({uuid => $on_device_uuid});
			
			# Rescan.
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0394", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { },
			});
			$anvil->Network->collect_data();
		}
	}
	
	return(0);
}

sub reconfigure_bonds
{
	my ($anvil) = @_;
	
	my $local_host = $anvil->Get->short_host_name();
	my $prefix     = $anvil->data->{config}{prefix};
	my $sequence   = $anvil->data->{config}{sequence};
	my $domain     = $anvil->data->{config}{domain};
	my $bcn_count  = $anvil->data->{config}{bcn_count};
	my $ifn_count  = $anvil->data->{config}{ifn_count};
	my $sn_count   = $anvil->data->{config}{sn_count};
	my $mn_count   = $anvil->data->{config}{mn_count};
	my $type       = $anvil->Get->host_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		local_host    => $local_host,
		prefix        => $prefix, 
		sequence      => $sequence,
		domain        => $domain, 
		type          => $type, 
	}});
	foreach my $network_type ("bcn", "sn", "mn", "ifn")
	{
		$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; }
		elsif ($network_type eq "mn")  { $count = $mn_count;  }
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
		next if not $count;
		
		# This is the old type of network config
		foreach my $i (1..$count)
		{
			my $bond_name = $network_type.$i."_bond1";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bond_name => $bond_name }});
			
			# Skip if this isn't marked to become a bond.
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				"config::${bond_name}" => $anvil->data->{config}{$bond_name},
			}});
			if (not $anvil->data->{config}{$bond_name})
			{
				# No bond on this network.
				next;
			}
			
			my $link_nm_uuids = [];
			my $link1_name    = $network_type.$i."_link1";
			my $link1_mac_key = $network_type.$i."_link1_mac_to_set";
			my $link1_mac     = $anvil->data->{config}{$link1_mac_key};
			my $link1_nm_uuid = $anvil->data->{nmcli}{mac_address}{$link1_mac}{uuid};
			
			my $link2_name    = $network_type.$i."_link2";
			my $link2_mac_key = $network_type.$i."_link2_mac_to_set";
			my $link2_mac     = $anvil->data->{config}{$link2_mac_key};
			my $link2_nm_uuid = $anvil->data->{nmcli}{mac_address}{$link2_mac}{uuid};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				link1_name    => $link1_name, 
				link1_mac_key => $link1_mac_key,
				link1_mac     => $link1_mac, 
				link1_nm_uuid => $link1_nm_uuid, 
				link2_name    => $link2_name, 
				link2_mac_key => $link2_mac_key,
				link2_mac     => $link2_mac, 
				link2_nm_uuid => $link2_nm_uuid, 
			}});
			
			# Push the two nm_uuids into the array for later checking/adding to the bond.
			push @{$link_nm_uuids}, $link1_nm_uuid;
			push @{$link_nm_uuids}, $link2_nm_uuid;
			
			# Check if the bond exists or not.
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0390", 
				job_uuid  => $anvil->data->{job}{uuid},
				'print'   => 1,
				log_level => 1,
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { bond_name => $bond_name },
			});
			if (exists $anvil->data->{nmcli}{bond}{$bond_name})
			{
				# It does.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0391", 
					job_uuid  => $anvil->data->{job}{uuid},
					'print'   => 1,
					log_level => 1,
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { nm_uuid => $anvil->data->{nmcli}{bond}{$bond_name}{uuid} },
				});
			}
			else
			{
				# It doesn't, create it.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0392", 
					job_uuid  => $anvil->data->{job}{uuid},
					'print'   => 1,
					log_level => 1,
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { 
						bond_name     => $bond_name,
						link1_name    => $link1_name, 
						link1_mac     => $link1_mac, 
						link1_nm_uuid => $link1_nm_uuid, 
						link2_name    => $link2_name, 
						link2_mac     => $link2_mac, 
						link2_nm_uuid => $link2_nm_uuid, 
					},
				});
				
				# If there are ifcfg files for this bond, move it.
				my $network_type = $anvil->System->check_network_type();
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }});
				if ($network_type eq "ifcfg")
				{
					my $ifcfg_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$bond_name;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ifcfg_file => $ifcfg_file }});
					if (-f $ifcfg_file)
					{
						# It exists, move it.
						$anvil->Job->update_progress({
							progress  => $anvil->Job->bump_progress({steps => 1}), 
							message   => "message_0417", 
							log_level => 1,
							'print'   => 1, 
							file      => $THIS_FILE, 
							line      => __LINE__,
							job_uuid  => $anvil->data->{job}{uuid},
							variables => { file => $ifcfg_file },
						});
						$anvil->Storage->backup({file => $ifcfg_file});
						unlink $ifcfg_file;
					}
				}
				
				my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection add type bond con-name ".$bond_name." ifname ".$bond_name." bond.options \"mode=active-backup,miimon=100,downdelay=0,updelay=120000,primary=".$link1_name."\"";
				$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});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					output      => $output,
					return_code => $return_code, 
				}});
				
				if ($return_code)
				{
					# Adding the bond failed.
					$anvil->Job->update_progress({
						progress   => 100, 
						message    => "error_0483", 
						log_level  => 1,
						'print'    => 1, 
						job_uuid   => $anvil->data->{job}{uuid},
						job_status => "failed", 
						variables  => {
							bond_name   => $bond_name, 
							return_code => $return_code,
							output      => $output, 
						},
					});
					$anvil->nice_exit({exit_code => 1});
				}
				
				my $bond_uuid = ($output =~ /\((.*?)\) successfully added/)[0];
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bond_uuid => $bond_uuid }});
				
				# NM seems to have a race issue, so we sleep a second after nmcli calls.
				$anvil->data->{sys}{restart_nm} = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::restart_nm' => $anvil->data->{sys}{restart_nm} }});
				sleep 2;
				
				if ($bond_uuid)
				{
					# Disabling DHCP on the new bond device
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0408", 
						log_level => 1,
						'print'   => 1, 
						job_uuid  => $anvil->data->{job}{uuid},
						file      => $THIS_FILE, 
						line      => __LINE__,
						variables => { 
							device      => $bond_name,
							device_uuid => $bond_uuid, 
						},
					});
					my ($output, $return_code) = $anvil->Network->modify_connection({uuid => $bond_uuid, variable => "ipv4.method", value => "disabled"});
					   ($output, $return_code) = $anvil->Network->modify_connection({uuid => $bond_uuid, variable => "ipv6.method", value => "disabled"});
					
					my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection up ".$bond_name;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
					($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
						output      => $output,
						return_code => $return_code, 
					}});
					
					# NM seems to have a race issue, so we sleep a second after nmcli calls.
					$anvil->data->{sys}{restart_nm} = 1;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::restart_nm' => $anvil->data->{sys}{restart_nm} }});
					sleep 2;
				}
				
				# Done! Rescanning the network config
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0394", 
					job_uuid  => $anvil->data->{job}{uuid},
					'print'   => 1,
					log_level => 1,
					file      => $THIS_FILE, 
					line      => __LINE__,
				});
				$anvil->Network->collect_data();
			}
			
			# Now add the interfaces, disabling their ipv4.method first.
			my $uuid_count = @{$link_nm_uuids};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { uuid_count => $uuid_count }});
			foreach my $nm_uuid (@{$link_nm_uuids})
			{
				my $link_name        = $anvil->data->{nmcli}{uuid}{$nm_uuid}{name};
				my $link_device      = $anvil->data->{nmcli}{uuid}{$nm_uuid}{device};
				my $mac_address      = $anvil->data->{nmcli}{uuid}{$nm_uuid}{mac_address};
				my $parent_bond_name = $anvil->data->{nmcli}{uuid}{$nm_uuid}{'connection.master'};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					's1:nm_uuid'          => $nm_uuid,
					's2:link_name'        => $link_name, 
					's3:link_device'      => $link_device, 
					's4:mac_address'      => $mac_address, 
					's5:parent_bond_name' => $parent_bond_name, 
				}});

				if ($parent_bond_name eq "--")
				{
					$parent_bond_name = "";
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { parent_bond_name => $parent_bond_name }});
				}
				
				if ($parent_bond_name)
				{
					if ($parent_bond_name eq $bond_name)
					{
						# Already a member of the bond.
						$anvil->Job->update_progress({
							progress  => $anvil->Job->bump_progress({steps => 1}), 
							message   => "message_0395", 
							job_uuid  => $anvil->data->{job}{uuid},
							'print'   => 1,
							log_level => 1,
							file      => $THIS_FILE, 
							line      => __LINE__, 
							variables => {
								link_name => $link_device ? $link_device : $link_name, 
								nm_uuid   => $nm_uuid, 
							},
						});
						next;
					}
					else
					{
						# The interface is a member of another bond, switching it to this bond.\n";
						$anvil->Job->update_progress({
							progress  => $anvil->Job->bump_progress({steps => 1}), 
							message   => "message_0396", 
							job_uuid  => $anvil->data->{job}{uuid},
							'print'   => 1,
							log_level => 1,
							file      => $THIS_FILE, 
							line      => __LINE__, 
							variables => {
								link_name => $link_device ? $link_device : $link_name, 
								nm_uuid   => $nm_uuid, 
								old_bond  => $parent_bond_name,
							},
						});
					}
				}
				else
				{
					# The interface needs to be connected to the bond.
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0397", 
						job_uuid  => $anvil->data->{job}{uuid},
						'print'   => 1,
						log_level => 1,
						file      => $THIS_FILE, 
						line      => __LINE__, 
						variables => {
							link_name => $link_device ? $link_device : $link_name, 
							nm_uuid   => $nm_uuid, 
						},
					});
				}
				
				# Disabling DHCP on the interface
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0408", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { 
						device      => $link_device ? $link_device : $link_name,
						device_uuid => $nm_uuid, 
					},
				});
				my ($output, $return_code) = $anvil->Network->modify_connection({uuid => $nm_uuid, variable => "ipv4.method", value => "disabled"});
				   ($output, $return_code) = $anvil->Network->modify_connection({uuid => $nm_uuid, variable => "ipv6.method", value => "disabled"});
				
				# Connecting the interface to the bond
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0399", 
					job_uuid  => $anvil->data->{job}{uuid},
					'print'   => 1,
					log_level => 1,
					file      => $THIS_FILE, 
					line      => __LINE__, 
					variables => {
						link_name => $link_device ? $link_device : $link_name, 
						bond_name => $bond_name, 
					},
				});
				my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify ".$nm_uuid." connection.master ".$bond_name." connection.slave-type bond";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
				($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					output      => $output,
					return_code => $return_code, 
				}});
				
				# NM seems to have a race issue, so we sleep a second after nmcli calls.
				$anvil->data->{sys}{restart_nm} = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::restart_nm' => $anvil->data->{sys}{restart_nm} }});
				sleep 2;
				
				if ($return_code)
				{
					# Adding the link failed.
					$anvil->Job->update_progress({
						progress   => 100, 
						message    => "error_0484", 
						log_level  => 1,
						'print'    => 1, 
						job_uuid   => $anvil->data->{job}{uuid},
						job_status => "failed", 
						variables  => {
							link_name   => $link_device ? $link_device : $link_name,
							bond_name   => $bond_name, 
							return_code => $return_code,
							output      => $output, 
						},
					});
					$anvil->nice_exit({exit_code => 1});
				}
				
				# Reset the interface.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0403", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { 
						device      => $link_device ? $link_device : $link_name,
						device_uuid => $nm_uuid, 
					},
				});
				($output, $return_code) = $anvil->Network->reset_connection({uuid => $nm_uuid});
				
				# Rescan.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0394", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { },
				});
				$anvil->Network->collect_data({debug => 2});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { nm_uuid => $nm_uuid }});
			}
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { i => $i }});
		}
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }});
	}
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0852"});
	
	return(0);
}

sub wait_for_nm
{
	my ($anvil, $bond_name) = @_;
	
	my $found      = 0;
	my $waiting    = 1;
	my $wait_until = time + 30;
	while ($waiting)
	{
		my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection show | ".$anvil->data->{path}{exe}{'grep'}." -c ".$bond_name;
		$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});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			output      => $output,
			return_code => $return_code, 
		}});
		
		if ($output eq "0")
		{
			if (time > $wait_until)
			{
				# Give up.
				$waiting = 0;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
			}
			# Not found yet.
			sleep 2;
		}
		else
		{
			# Found it.
			$found   = 0;
			$waiting = 0;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				found   => $found, 
				waiting => $waiting,
			}});
		}
	}
	
	return($found);
}

sub reconfigure_ip_addresses
{
	my ($anvil) = @_;
	
	my $local_host   = $anvil->Get->short_host_name();
	my $bcn_count    = $anvil->data->{config}{bcn_count};
	my $ifn_count    = $anvil->data->{config}{ifn_count};
	my $sn_count     = $anvil->data->{config}{sn_count};
	my $mn_count     = $anvil->data->{config}{mn_count};
	my $dns          = $anvil->data->{config}{dns};
	my $gateway      = $anvil->data->{config}{gateway};
	my $gw_interface = $anvil->data->{config}{gateway_interface};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		local_host   => $local_host,
		bcn_count    => $bcn_count, 
		ifn_count    => $ifn_count, 
		sn_count     => $sn_count, 
		mn_count     => $mn_count, 
		dns          => $dns,
		gateway      => $gateway, 
		gw_interface => $gw_interface, 
	}});
	foreach my $network_type ("bcn", "sn", "mn", "ifn")
	{
		$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; }
		elsif ($network_type eq "mn")  { $count = $mn_count;  }
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
		
		# This is the old type of network config
		foreach my $i (1..$count)
		{
			my $ip_key      = $network_type.$i."_ip";
			my $subnet_key  = $network_type.$i."_subnet_mask";
			my $link1_name  = $network_type.$i."_link1";
			my $link2_name  = $network_type.$i."_link2";
			my $bond_name   = $network_type.$i."_bond1";
			my $bridge_name = $network_type.$i."_bridge1";
			my $is_gateway  = $gw_interface eq $network_type.$i ? 1 : 0;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				ip_key      => $ip_key, 
				subnet_key  => $subnet_key, 
				link1_name  => $link1_name, 
				link2_name  => $link2_name, 
				bond_name   => $bond_name, 
				bridge_name => $bridge_name, 
				is_gateway  => $is_gateway, 
			}});
			
			if ((not exists $anvil->data->{config}{$ip_key}) or (not $anvil->data->{config}{$ip_key}))
			{
				# No IP for this network
				next;
			}
			
			my $ip_address  = $anvil->data->{config}{$ip_key};
			my $subnet_mask = $anvil->data->{config}{$subnet_key};
			my $changes     = 0;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				ip_address  => $ip_address, 
				subnet_mask => $subnet_mask, 
				changes     => $changes, 
			}});
			
			if (($subnet_mask !~ /^\d+$/) or ($subnet_mask < 1) or ($subnet_mask > 32))
			{
				# Convert to CIDR
				my $cidr = $anvil->Convert->cidr({subnet_mask => $subnet_mask});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cidr => $cidr }});
				if (not $cidr)
				{
					# The subnet_mask is not valid.
					$anvil->Job->update_progress({
						progress   => 100, 
						message    => "error_0487", 
						log_level  => 1,
						'print'    => 1, 
						job_uuid   => $anvil->data->{job}{uuid},
						job_status => "failed", 
						file       => $THIS_FILE, 
						line       => __LINE__,
						variables  => { subnet_mask => $subnet_mask },
					});
					$anvil->nice_exit({exit_code => 1});
				}
				
				$subnet_mask = $cidr;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { subnet_mask => $subnet_mask }});
			}
			
			# What device are we assigning the IP address to?
			my $on_device      = "";
			my $on_device_uuid = "";
			if ((exists $anvil->data->{nmcli}{bridge}{$bridge_name}) && ($anvil->data->{nmcli}{bridge}{$bridge_name}{uuid}))
			{
				$on_device      = $bridge_name;
				$on_device_uuid = $anvil->data->{nmcli}{bridge}{$bridge_name}{uuid};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					on_device      => $on_device, 
					on_device_uuid => $on_device_uuid, 
				}});
			}
			elsif ((exists $anvil->data->{nmcli}{bond}{$bond_name}) && ($anvil->data->{nmcli}{bond}{$bond_name}{uuid}))
			{
				$on_device      = $bond_name;
				$on_device_uuid = $anvil->data->{nmcli}{bond}{$bond_name}{uuid};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					on_device      => $on_device, 
					on_device_uuid => $on_device_uuid, 
				}});
			}
			elsif ((exists $anvil->data->{nmcli}{interface}{$link1_name}) && ($anvil->data->{nmcli}{interface}{$link1_name}{uuid}))
			{
				$on_device      = $link1_name;
				$on_device_uuid = $anvil->data->{nmcli}{interface}{$link1_name}{uuid};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					on_device      => $on_device, 
					on_device_uuid => $on_device_uuid, 
				}});
			}
			else
			{
				# Failed to find a device to assign this IP address to.
				$anvil->Job->update_progress({
					progress   => 100, 
					message    => "error_0488", 
					log_level  => 1,
					'print'    => 1, 
					job_uuid   => $anvil->data->{job}{uuid},
					job_status => "failed", 
					file       => $THIS_FILE, 
					line       => __LINE__,
					variables  => { 
						network     => $network_type.$i,
						ip_address  => $ip_address, 
						subnet_mask => $subnet_mask, 
					},
				});
				$anvil->nice_exit({exit_code => 1});
			}
			
			# Check to see if the IP address is assigned yet.
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0393", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { 
					ip_address  => $ip_address, 
					subnet_mask => $subnet_mask, 
					device      => $on_device,
				},
			});
			my $clear_ip_from = "";
			if (exists $anvil->data->{nmcli}{ipv4}{$ip_address})
			{
				my $ip_uuid             =  $anvil->data->{nmcli}{ipv4}{$ip_address}{on_uuid};
				my $current_device      =  $anvil->data->{nmcli}{uuid}{$ip_uuid}{device};
				my $ip_sequence         =  $anvil->data->{nmcli}{ipv4}{$ip_address}{sequence};
				my $current_subnet_mask =  $anvil->data->{nmcli}{uuid}{$ip_uuid}{ipv4}{ip}{$ip_sequence}{subnet_mask};
				my $current_gateway     =  $anvil->data->{nmcli}{uuid}{$ip_uuid}{ipv4}{gateway};
				my $current_dns         =  $anvil->data->{nmcli}{uuid}{$ip_uuid}{ipv4}{dns};
				   $current_dns         =~ s/\s+//;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					's1:ip_uuid'             => $ip_uuid, 
					's2:current_device'      => $current_device, 
					's3:ip_sequence'         => $ip_sequence, 
					's4:current_subnet_mask' => $current_subnet_mask, 
					's5:current_gateway'     => $current_gateway, 
					's6:current_dns'         => $current_dns, 
				}});
				if (not $ip_uuid)
				{
					# The IP exists, but wasn't translate to a network manager UUID.
					$anvil->Job->update_progress({
						progress   => 100, 
						message    => "error_0489", 
						log_level  => 1,
						'print'    => 1, 
						job_uuid   => $anvil->data->{job}{uuid},
						job_status => "failed", 
						file       => $THIS_FILE, 
						line       => __LINE__,
						variables  => { ip_address => $ip_address },
					});
					$anvil->nice_exit({exit_code => 1});
				}
				
				# The IP exists, checking if it needs to be updated.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0398", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => {},
				});
				if ($on_device ne $current_device)
				{
					# The IP address is on another device, we'll move it
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0402", 
						log_level => 1,
						'print'   => 1, 
						job_uuid  => $anvil->data->{job}{uuid},
						file      => $THIS_FILE, 
						line      => __LINE__,
						variables => { device => $current_device },
					});
					$clear_ip_from = $current_device;
					$changes       = 1;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
						clear_ip_from => $clear_ip_from,
						changes       => $changes, 
					}});
				}
				elsif ($subnet_mask ne $current_subnet_mask)
				{
					# The current subnet mask is different, will update.
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0410", 
						log_level => 1,
						'print'   => 1, 
						job_uuid  => $anvil->data->{job}{uuid},
						file      => $THIS_FILE, 
						line      => __LINE__,
						variables => { current_subnet_mask => $current_subnet_mask },
					});
					$changes = 1;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }});
				}
				elsif ($gateway ne $current_gateway)
				{
					# Is this the gateway interface?
					if ((not $is_gateway) && ($current_gateway))
					{
						# No, remove it
						$anvil->Job->update_progress({
							progress  => $anvil->Job->bump_progress({steps => 1}), 
							message   => "message_0144", 
							log_level => 1,
							'print'   => 1, 
							job_uuid  => $anvil->data->{job}{uuid},
							file      => $THIS_FILE, 
							line      => __LINE__,
							variables => { current_gateway => $current_gateway },
						});
						$changes = 1;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }});
					}
					elsif (($is_gateway) && (not $current_gateway))
					{
						# Tell them we're adding the gateway
						$anvil->Job->update_progress({
							progress  => $anvil->Job->bump_progress({steps => 1}), 
							message   => "message_0145", 
							log_level => 1,
							'print'   => 1, 
							job_uuid  => $anvil->data->{job}{uuid},
							file      => $THIS_FILE, 
							line      => __LINE__,
							variables => { 
								new_gateway => $gateway,
							},
						});
						$changes = 1;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }});
					}
					elsif ($is_gateway)
					{
						# This is the default gateway interface but the gateway needs
						# to change.
						$anvil->Job->update_progress({
							progress  => $anvil->Job->bump_progress({steps => 1}), 
							message   => "message_0411", 
							log_level => 1,
							'print'   => 1, 
							job_uuid  => $anvil->data->{job}{uuid},
							file      => $THIS_FILE, 
							line      => __LINE__,
							variables => { 
								current_gateway => $current_gateway,
								new_gateway     => $gateway,
							},
						});
						$changes = 1;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }});
					}
				}
				elsif ($dns ne $current_dns)
				{
					# The current DNS is different, will update.
					$anvil->Job->update_progress({
						progress  => $anvil->Job->bump_progress({steps => 1}), 
						message   => "message_0412", 
						log_level => 1,
						'print'   => 1, 
						job_uuid  => $anvil->data->{job}{uuid},
						file      => $THIS_FILE, 
						line      => __LINE__,
						variables => { 
							current_dns => $current_dns,
							new_dns     => $dns, 
						},
					});
					$changes = 1;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }});
				}
			}
			else
			{
				# The IP address needs to be assigned.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0414", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { },
				});
				$changes = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }});
			}
			
			# End this iteration here if there were not changes to make.
			if (not $changes)
			{
				# No update is needed.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0413", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { },
				});
				next;
			}
			
			if ($clear_ip_from)
			{
				# Clear the IP off the old device
				my $old_uuid = $anvil->data->{nmcli}{device}{$clear_ip_from}{uuid};
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0408", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { 
						device      => $clear_ip_from,
						device_uuid => $old_uuid, 
					},
				});
				my ($output, $return_code) = $anvil->Network->modify_connection({uuid => $old_uuid, variable => "ipv4.method", value => "disabled"});
				   ($output, $return_code) = $anvil->Network->modify_connection({uuid => $old_uuid, variable => "ipv6.method", value => "disabled"});
				
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0403", 
					log_level => 1,
					'print'   => 1, 
					job_uuid  => $anvil->data->{job}{uuid},
					file      => $THIS_FILE, 
					line      => __LINE__,
					variables => { 
						device      => $clear_ip_from,
						device_uuid => $old_uuid, 
					},
				});
				($output, $return_code) = $anvil->Network->reset_connection({uuid => $old_uuid});
				
				# NM seems to have a race issue, so we sleep a second after nmcli calls.
				$anvil->data->{sys}{restart_nm} = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::restart_nm' => $anvil->data->{sys}{restart_nm} }});
				sleep 2;
			}
			
			# Now assign the IP.
			my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify ".$on_device_uuid." ipv4.method manual ipv4.addresses ".$ip_address."/".$subnet_mask;
			if ($is_gateway)
			{
				if ($gateway)
				{
					$shell_call .= " ipv4.gateway ".$gateway;
				}
				if ($dns)
				{
					# DNS needs to be quoted to be safe
					$shell_call .= " ipv4.dns \"".$dns."\"";
				}
			}
			$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});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				output      => $output,
				return_code => $return_code, 
			}});
			
			if ($return_code)
			{
				# Something went wrong, abort lest we leave the system inaccessible.
				$anvil->Job->update_progress({
					progress   => 100, 
					message    => "error_0316", 
					log_level  => 1,
					'print'    => 1, 
					job_uuid   => $anvil->data->{job}{uuid},
					job_status => "failed", 
					file       => $THIS_FILE, 
					line       => __LINE__,
					variables  => { 
						shell_call  => $shell_call,
						return_code => $return_code, 
						output      => $output, 
					},
				});
				$anvil->nice_exit({exit_code => 9});
			}
			
			# NM seems to have a race issue, so we sleep a second after nmcli calls.
			$anvil->data->{sys}{restart_nm} = 1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::restart_nm' => $anvil->data->{sys}{restart_nm} }});
			sleep 2;
			
			# Restart the interface
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0403", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { 
					device      => $on_device,
					device_uuid => $on_device_uuid, 
				},
			});
			($output, $return_code) = $anvil->Network->reset_connection({uuid => $on_device_uuid});
			
			# Rescan.
			$anvil->Job->update_progress({
				progress  => $anvil->Job->bump_progress({steps => 1}), 
				message   => "message_0394", 
				log_level => 1,
				'print'   => 1, 
				job_uuid  => $anvil->data->{job}{uuid},
				file      => $THIS_FILE, 
				line      => __LINE__,
				variables => { },
			});
			$anvil->Network->collect_data({debug => 2});
		}
	}
	
	return(0);
}

sub reconfigure_interfaces
{
	my ($anvil) = @_;
	
	# Before we start, we need to make sure all interfaces are up. Otherwise, there's no way to match a 
	# network manager device to the ip addr name or the biosdevname.
	foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{nmcli}{uuid}})
	{
		### TODO: Any devices that are down, set:
		###       nmcli connection modify <uuid> ipv4.method manual ipv4.addresses 169.0.0.x/8' (x == Wired connection x)
		###       For now, we do nothing here
		next;
		my $connection_id          = $anvil->data->{nmcli}{uuid}{$uuid}{'connection.id'}          // "";
		my $general_ip_iface       = $anvil->data->{nmcli}{uuid}{$uuid}{'GENERAL.IP-IFACE'}       // "";
		   $general_ip_iface       = "" if $general_ip_iface eq "--";
		my $device_type            = $anvil->data->{nmcli}{uuid}{$uuid}{'connection.type'}        // "";
		my $match_interface_name   = $anvil->data->{nmcli}{uuid}{$uuid}{'match.interface-name'}   // "";
		my $connection_autoconnect = $anvil->data->{nmcli}{uuid}{$uuid}{'connection.autoconnect'} // "";
		my $active                 = $anvil->data->{nmcli}{uuid}{$uuid}{active};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			's1:uuid'                   => $uuid,
			's2:connection_id'          => $connection_id, 
			's3:general_ip_iface'       => $general_ip_iface, 
			's4:device_type'            => $device_type, 
			's5:match_interface_name'   => $match_interface_name, 
			's6:connection_autoconnect' => $connection_autoconnect, 
			's7:active'                 => $active, 
		}});
	}
	
	my $local_host = $anvil->Get->short_host_name();
	my $prefix     = $anvil->data->{config}{prefix};
	my $sequence   = $anvil->data->{config}{sequence};
	my $domain     = $anvil->data->{config}{domain};
	my $bcn_count  = $anvil->data->{config}{bcn_count};
	my $ifn_count  = $anvil->data->{config}{ifn_count};
	my $sn_count   = $anvil->data->{config}{sn_count};
	my $mn_count   = $anvil->data->{config}{mn_count};
	my $type       = $anvil->Get->host_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		local_host    => $local_host,
		prefix        => $prefix, 
		sequence      => $sequence,
		domain        => $domain, 
		type          => $type, 
	}});
	foreach my $network_type ("bcn", "sn", "mn", "ifn")
	{
		$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; }
		elsif ($network_type eq "mn")  { $count = $mn_count;  }
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
		next if not $count;
		
		# This is the old type of network config
		foreach my $i (1..$count)
		{
			my $link1_mac_key = $network_type.$i."_link1_mac_to_set";
			my $link2_mac_key = $network_type.$i."_link2_mac_to_set";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				i             => $i,
				link1_mac_key => $link1_mac_key, 
				link2_mac_key => $link2_mac_key, 
			}});
			
			# This will get set to '1' if there's a link2
			my $bond_name                         = $network_type.$i."_bond1";
			   $anvil->data->{config}{$bond_name} = 0;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				"config::${bond_name}" => $anvil->data->{config}{$bond_name},
			}});
			
			# If the user had the option to create a network but didn't, there will be no link1 
			# mac to set.
			next if not exists $anvil->data->{config}{$link1_mac_key};
			next if not $anvil->data->{config}{$link1_mac_key};
			
			my $wanted_link1_name = $network_type.$i."_link1";
			my $wanted_link1_mac  = $anvil->data->{config}{$link1_mac_key};
			my $wanted_link2_name = $network_type.$i."_link2";
			my $wanted_link2_mac  = exists $anvil->data->{config}{$link2_mac_key} ? $anvil->data->{config}{$link2_mac_key} : "";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				wanted_link1_name => $wanted_link1_name,
				wanted_link1_mac  => $wanted_link1_mac, 
				wanted_link2_name => $wanted_link2_name, 
				wanted_link2_mac  => $wanted_link2_mac, 
			}});
			
			# Loop through our interfaces to see if we can find this interface.
			my $link1_nm_uuid = $anvil->data->{nmcli}{mac_address}{$wanted_link1_mac}{uuid};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link1_nm_uuid => $link1_nm_uuid }});
			
			# Are the link(s) already configured? 
			my $configure_link1 = 0;
			my $configure_link2 = 0;
			
			# Get the Network Manager UUIDs.
			if ($link1_nm_uuid)
			{
				my $found                = 0;
				my $match_interface_name = $anvil->data->{nmcli}{uuid}{$link1_nm_uuid}{'match.interface-name'} // "";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { match_interface_name => $match_interface_name }});
				foreach my $interface (split/,/, $match_interface_name)
				{
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }});
					
					# In case the GENERAL.IP-IFACE or GENERAL.NAME name didn't update, 
					# map this interface name to 'nmcli::interface::<name>::uuid'.
					if (not exists $anvil->data->{nmcli}{interface}{$interface})
					{
						$anvil->data->{nmcli}{interface}{$interface}{uuid} = $link1_nm_uuid;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
							"nmcli::interface::${interface}::uuid" => $anvil->data->{nmcli}{interface}{$interface}{uuid},
						}});
					}
					if ($interface eq $wanted_link1_name)
					{
						$found = 1;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
					}
				}
				if (not $found)
				{
					$configure_link1 = 1;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { configure_link1 => $configure_link1 }});
				}
				
				# Make sure that the autoconnect is enabled.
				if ($anvil->data->{nmcli}{uuid}{$link1_nm_uuid}{'connection.autoconnect'} ne "yes")
				{
					my ($output, $return_code) = $anvil->Network->modify_connection({uuid => $link1_nm_uuid, variable => "connection.autoconnect", value => "yes"});
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
						output      => $output,
						return_code => $return_code, 
					}});
				}
			}
			else
			{
				# We're dead.
				$anvil->Job->update_progress({
					progress   => 100, 
					message    => "error_0480", 
					log_level  => 1,
					'print'    => 1, 
					job_uuid   => $anvil->data->{job}{uuid},
					job_status => "failed", 
					variables  => {
						mac_address    => $wanted_link1_mac,
						interface_name => $wanted_link1_name, 
					},
				});
				$anvil->nice_exit({exit_code => 1});
			}
			
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { configure_link1 => $configure_link1 }});
			if ($configure_link1)
			{
				rename_interface($anvil, $wanted_link1_name, $link1_nm_uuid);
			}
			
			my $link2_nm_uuid = "";
			if ($wanted_link2_mac)
			{
				$link2_nm_uuid = $anvil->data->{nmcli}{mac_address}{$wanted_link2_mac}{uuid};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link2_nm_uuid => $link2_nm_uuid }});
				if ($link2_nm_uuid)
				{
					my $found                = 0;
					my $match_interface_name = $anvil->data->{nmcli}{uuid}{$link2_nm_uuid}{'match.interface-name'} ? $anvil->data->{nmcli}{uuid}{$link2_nm_uuid}{'match.interface-name'} : "";
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { match_interface_name => $match_interface_name }});
					foreach my $interface (split/,/, $match_interface_name)
					{
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }});
						if ($interface eq $wanted_link2_name)
						{
							$found = 1;
							$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
							last;
						}
					}
					if (not $found)
					{
						$configure_link2 = 1;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { configure_link1 => $configure_link1 }});
					}
				
					# Make sure that the autoconnect is enabled.
					if ($anvil->data->{nmcli}{uuid}{$link2_nm_uuid}{'connection.autoconnect'} ne "yes")
					{
						my ($output, $return_code) = $anvil->Network->modify_connection({uuid => $link2_nm_uuid, variable => "connection.autoconnect", value => "yes"});
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
							output      => $output,
							return_code => $return_code, 
						}});
					}
				}
				else
				{
					$anvil->Job->update_progress({
						progress   => 100, 
						message    => "error_0480", 
						log_level  => 1,
						'print'    => 1, 
						job_uuid   => $anvil->data->{job}{uuid},
						file       => $THIS_FILE, 
						line       => __LINE__,
						job_status => "failed", 
						variables  => {
							mac_address    => $wanted_link2_mac,
							interface_name => $wanted_link2_name, 
						},
					});
					$anvil->nice_exit({exit_code => 1});
				}
				
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { configure_link2 => $configure_link2 }});
				if ($configure_link2)
				{
					rename_interface($anvil, $wanted_link2_name, $link2_nm_uuid);
				}
				
				# There's a second interface, so create a bond.
				$anvil->data->{config}{$bond_name} = 1;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					"config::${bond_name}" => $anvil->data->{config}{$bond_name},
				}});
			}
		}
	}
	
	# There is no way (that we've found) to get the network interface names to be updated without a 
	# reboot. 
	if ($anvil->data->{sys}{reboot_needed})
	{
		do_reboot($anvil);
	}
	
	return(0);
}

sub rename_interface
{
	my ($anvil, $wanted_link_name, $nm_uuid) = @_;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		wanted_link_name => $wanted_link_name,
		nm_uuid          => $nm_uuid, 
	}});
	
	my $old_device  = $anvil->data->{nmcli}{uuid}{$nm_uuid}{device};
	my $name        = $anvil->data->{nmcli}{uuid}{$nm_uuid}{'connection.id'};
	my $mac_address = $anvil->data->{nmcli}{uuid}{$nm_uuid}{mac_address};
	my $type        = $anvil->data->{nmcli}{uuid}{$nm_uuid}{type_id} // "1";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		's1:wanted_link_name' => $wanted_link_name,
		's2:nm_uuid'          => $nm_uuid, 
		's3:old_device'       => $old_device, 
		's4:name'             => $name, 
		's5:mac_address'      => $mac_address, 
		's6:type'             => $type, 
	}});
	
	# Tell the user what we're about to rename
	$anvil->Job->update_progress({
		progress  => $anvil->Job->bump_progress({steps => 1}), 
		message   => "message_0383", 
		log_level => 1,
		'print'   => 1, 
		file      => $THIS_FILE, 
		line      => __LINE__,
		job_uuid  => $anvil->data->{job}{uuid},
		variables => {
			mac_address => $mac_address, 
			old_device  => $old_device, 
			old_name    => $name, 
			new_name    => $wanted_link_name, 
			nm_uuid     => $nm_uuid, 
		},
	});
	
	# If there are ifcfg files for this device, move them.
	my $network_type = $anvil->System->check_network_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }});
	if ($network_type eq "ifcfg")
	{
		my $old_ifcfg_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$old_device;
		my $new_ifcfg_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$wanted_link_name;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			old_ifcfg_file => $old_ifcfg_file,
			new_ifcfg_file => $new_ifcfg_file, 
		}});
		foreach my $file ($old_ifcfg_file, $new_ifcfg_file)
		{
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file => $file }});
			if (-f $file)
			{
				# It exists, move it.
				$anvil->Job->update_progress({
					progress  => $anvil->Job->bump_progress({steps => 1}), 
					message   => "message_0417", 
					log_level => 1,
					'print'   => 1, 
					file      => $THIS_FILE, 
					line      => __LINE__,
					job_uuid  => $anvil->data->{job}{uuid},
					variables => { file => $file },
				});
				$anvil->Storage->backup({file => $file});
				unlink $file;
				sleep 1;
				if (-f $file)
				{
					# Um, what?
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "error_0284", variables => { 
						file => $file,
						error => $@, 
					}});;
				}
				else
				{
					# It's gone.
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0616", variables => { file_path => $file }});;
				}
			}
		}
	}
	
	# Read persistent-net and see if it needs to be updated.
	my $new_persistent_net = "";
	my $old_persistent_net = "";
	if (-e $anvil->data->{path}{configs}{'persistent-net'})
	{
		$old_persistent_net = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'persistent-net'}});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_persistent_net => $old_persistent_net }});
	}
	foreach my $line (split/\n/, $old_persistent_net)
	{
		# If this MAC or device name exists already, delete the line.
		if (($line =~ /"$mac_address"/) or ($line =~ /"$wanted_link_name"/))
		{
			next;
		}
		$new_persistent_net .= $line."\n";
	}
	$new_persistent_net .= "SUBSYSTEM==\"net\",ACTION==\"add\",ATTR{address}==\"".$mac_address."\",ATTR{type}==\"".$type."\",NAME=\"".$wanted_link_name."\"\n";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_persistent_net => $new_persistent_net }});
	
	my $difference = diff \$old_persistent_net, \$new_persistent_net, { STYLE => 'Unified' };
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $difference }});
	
	# Write the new file.
	if ($difference)
	{
		# Updating the udev file
		$anvil->Job->update_progress({
			progress  => $anvil->Job->bump_progress({steps => 1}), 
			message   => "message_0384", 
			log_level => 1,
			'print'   => 1, 
			file      => $THIS_FILE, 
			line      => __LINE__,
			job_uuid  => $anvil->data->{job}{uuid},
			variables => { file => $anvil->data->{path}{configs}{'persistent-net'} },
		});
		my $problem = $anvil->Storage->write_file({
			file      => $anvil->data->{path}{configs}{'persistent-net'}, 
			body      => $new_persistent_net,
			overwrite => 1,
			user  => "admin", 
			group => "admin", 
			mode  => "0644",
			
		});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
		if ($problem)
		{
			# Failed to write the udev file
			$anvil->Job->update_progress({
				progress   => 100, 
				message    => "error_0043", 
				log_level  => 1,
				'print'    => 1, 
				file       => $THIS_FILE, 
				line       => __LINE__,
				job_uuid   => $anvil->data->{job}{uuid},
				job_status => "failed", 
				variables  => { file => $anvil->data->{path}{configs}{'persistent-net'} },
			});
			$anvil->nice_exit({exit_code => 1});
		}
	}
	
	# Update the connection.interface-name
	my $connection_interface_name = $anvil->data->{nmcli}{uuid}{$nm_uuid}{'connection.interface-name'} ? $anvil->data->{nmcli}{uuid}{$nm_uuid}{'connection.interface-name'} : "";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { connection_interface_name => $connection_interface_name }});
	if ($connection_interface_name)
	{
		# Removing the old 'connection.interface-name'
		$anvil->Job->update_progress({
			progress  => $anvil->Job->bump_progress({steps => 1}), 
			message   => "message_0385", 
			log_level => 1,
			'print'   => 1, 
			file      => $THIS_FILE, 
			line      => __LINE__,
			job_uuid  => $anvil->data->{job}{uuid},
			variables => { name => $connection_interface_name },
		});
		my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify ".$nm_uuid." connection.interface-name \"\"";
		$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});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			output      => $output, 
			return_code => $return_code, 
		}});
		# NM seems to have a race issue, so we sleep a second after nmcli calls.
		sleep 2;
		
		$shell_call = $anvil->data->{path}{exe}{nmcli}." --get-values connection.interface-name connection show ".$nm_uuid;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
		($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			output      => $output, 
			return_code => $return_code, 
		}});
		
		if ($output)
		{
			# Failed to delete the 'connection.interface-name', This should have been blank
			$anvil->Job->update_progress({
				progress   => 100, 
				message    => "error_0481", 
				log_level  => 1,
				'print'    => 1, 
				file       => $THIS_FILE, 
				line       => __LINE__,
				job_uuid   => $anvil->data->{job}{uuid},
				job_status => "failed", 
				variables  => { output => $output },
			});
			$anvil->nice_exit({exit_code => 1});
		}
	}
	
	# We'll log what it was, and change it anyway
	my $match_interface_name = $anvil->data->{nmcli}{uuid}{$nm_uuid}{'match.interface-name'} ? $anvil->data->{nmcli}{uuid}{$nm_uuid}{'match.interface-name'} : "";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { match_interface_name => $match_interface_name }});
	$anvil->Job->update_progress({
		progress  => $anvil->Job->bump_progress({steps => 1}), 
		message   => "message_0386", 
		log_level => 1,
		'print'   => 1, 
		job_uuid  => $anvil->data->{job}{uuid},
		file      => $THIS_FILE, 
		line      => __LINE__,
		variables => { 
			new_name   => $wanted_link_name,
			old_device => $old_device, 
		},
	});
	
	if ($old_device eq $wanted_link_name)
	{
		# Both the netbios name and the old device name are the same, match would break!
		$anvil->Job->update_progress({
			progress   => 100, 
			message    => "error_0490", 
			log_level  => 1,
			'print'    => 1, 
			file       => $THIS_FILE, 
			line       => __LINE__,
			job_uuid   => $anvil->data->{job}{uuid},
			job_status => "failed", 
			variables  => { 
				wanted_link_name => $wanted_link_name,
				old_device       => $old_device, 
			},
		});
		$anvil->nice_exit({exit_code => 1});
	}
	
	my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify ".$nm_uuid." match.interface-name \"".$wanted_link_name." ".$old_device."\"";
	$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});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		output      => $output, 
		return_code => $return_code, 
	}});
	# NM seems to have a race issue, so we sleep a second after nmcli calls.
	sleep 2;
	
	# Read it back
	$shell_call = $anvil->data->{path}{exe}{nmcli}." --get-values match.interface-name connection show ".$nm_uuid;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
	($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		output      => $output, 
		return_code => $return_code, 
	}});
	
	if (($output ne $wanted_link_name.",".$old_device) && ($output ne $old_device.",".$wanted_link_name))
	{
		# This should have been blank
		$anvil->Job->update_progress({
			progress   => 100, 
			message    => "error_0482", 
			log_level  => 1,
			'print'    => 1, 
			job_uuid   => $anvil->data->{job}{uuid},
			job_status => "failed", 
			file       => $THIS_FILE, 
			line       => __LINE__,
			variables  => { 
				new_name   => $wanted_link_name,
				old_device => $old_device, 
				output     => $output, 
			},
		});
		$anvil->nice_exit({exit_code => 1});
	}
	
	# Set the connection.id to the old name.
	$anvil->Job->update_progress({
		progress  => $anvil->Job->bump_progress({steps => 1}), 
		message   => "message_0387", 
		log_level => 1,
		'print'   => 1, 
		file      => $THIS_FILE, 
		line      => __LINE__,
		job_uuid  => $anvil->data->{job}{uuid},
		variables => { old_device => $old_device },
	});
	$shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify ".$nm_uuid." connection.id \"".$old_device."\"";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
	($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		output      => $output, 
		return_code => $return_code, 
	}});
	# NM seems to have a race issue, so we sleep a second after nmcli calls.
	sleep 2;
	
	# Read it back
	$shell_call = $anvil->data->{path}{exe}{nmcli}." --get-values connection.id connection show ".$nm_uuid;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
	($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		output      => $output, 
		return_code => $return_code, 
	}});
	
	# Re-read the updated data 
	$anvil->Job->update_progress({
		progress  => $anvil->Job->bump_progress({steps => 1}), 
		message   => "message_0394", 
		log_level => 1,
		'print'   => 1, 
		job_uuid  => $anvil->data->{job}{uuid},
		file      => $THIS_FILE, 
		line      => __LINE__,
		variables => { },
	});
	$anvil->Network->collect_data({debug => 2});
	
	# Set the reboot flag.
	$anvil->data->{sys}{reboot_needed} = 1;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sys::reboot_needed" => $anvil->data->{sys}{reboot_needed} }});
	
	return(0);
}

# This will pick up the job, or exit.
sub pickup_job_details
{
	my ($anvil) = @_;
	
	# If we're in a cluster, abort.
	if (-e $anvil->data->{path}{exe}{pcs})
	{
		# To make logs more sensible, we'll call 'problem' as 'out_of_cluster'.
		   $anvil->data->{cib}{parsed}{'local'}{ready} = "" if not defined $anvil->data->{cib}{parsed}{'local'}{ready};
		my ($out_of_cluster)                           = $anvil->Cluster->parse_cib();
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			out_of_cluster              => $out_of_cluster,
			"cib::parsed::local::ready" => defined $anvil->data->{cib}{parsed}{'local'}{ready} ? $anvil->data->{cib}{parsed}{'local'}{ready} : "",
		}});
		if ((not $out_of_cluster) && ($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},
				job_status => "failed", 
				'print'    => 1,
				log_level  => 1,
				file       => $THIS_FILE, 
				line       => __LINE__,
			});
			$anvil->nice_exit({exit_code => 7});
		}
	}
	
	### These are the new variables
	$anvil->data->{config}{striker_password}  = "";
	$anvil->data->{config}{prefix}            = "";
	$anvil->data->{config}{sequence}          = "";
	$anvil->data->{config}{domain}            = "";
	$anvil->data->{config}{host_name}         = "";
	$anvil->data->{config}{organization}      = "";
	$anvil->data->{config}{bcn_count}         = 1;
	$anvil->data->{config}{ifn_count}         = 1;
	$anvil->data->{config}{sn_count}          = 0;
	$anvil->data->{config}{mn_count}          = 0;
	$anvil->data->{config}{dns}               = "";
	$anvil->data->{config}{gateway}           = "";
	$anvil->data->{config}{gateway_interface} = "";
	
	### TODO: Remove this later
	# This will store the variables from the database
	$anvil->data->{variables} = {};
	
	# If we were passed a file, read it.
	if ($anvil->data->{switches}{file})
	{
		# If the file valid?
		if (not -f $anvil->data->{switches}{file})
		{
			# Needs to be a full path.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 0, priority => "err", key => "error_0002", variables => { file => $anvil->data->{switches}{file} }});;
			$anvil->nice_exit({exit_code => 1});
		}
		
		my $config_file = $anvil->Storage->read_file({file => $anvil->data->{switches}{file}});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { config_file => $config_file }});
		
		foreach my $line (split/\n/, $config_file)
		{
			$line = $anvil->Words->clean_spaces({string => $line});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
			next if $line =~ /^#/;
			if ($line =~ /^(\w+)\s*=\s*(.*)$/)
			{
				my $variable = $1;
				my $value    = $2;
				my $secure   = $variable =~ /passw/ ? 1 : 0;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => $secure, list => { 
					variable => $variable,
					value    => $value,
				}});
				
				$anvil->data->{config}{$variable} = $value;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					"config::${variable}" => $anvil->data->{config}{$variable}, 
				}});
			}
		}
		return(0);
	}
	
	# 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 => 2, 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 => 2, 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 => 2, 'print' => 1, key => "log_0147", variables => { 
			pid     => $job_picked_up_by,
			percent => $job_progress, 
		}});
	}
	
	### TODO: Remove this, it shouldn't be needed once we confirm everything is in the job_data.
	# 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_value != 'DELETED' 
AND 
    variable_source_table = 'hosts' 
AND 
    variable_source_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)."
;";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
	
	my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
	my $count   = @{$results};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		results => $results, 
		count   => $count,
	}});
	foreach my $row (@{$results})
	{
		my $this_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);
	}
	
	### TODO: This is the only way we should do it, but the variable names need to change to be more sensible.
	# Overwrite variables with job data.
	foreach my $line (split/\n/, $anvil->data->{jobs}{job_data})
	{
		if ($line =~ /^(form::config_step[^\s]+)=(.*?)$/)
		{
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
				'job_data::variable_name'  => $1,
				'job_data::variable_value' => $anvil->Log->is_secure($2),
			}});
			$anvil->_make_hash_reference($anvil->data->{variables}, $1, $2);
		}
		elsif ($line =~ /^(.*?)=(.*)$/)
		{
			my $variable                         = $1;
			my $value                            = $2;
			my $secure                           = $variable =~ /passw/ ? 1 : 0;
			   $anvil->data->{config}{$variable} = $value;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				's1:variable'            => $variable, 
				's2:value'               => $anvil->Log->is_secure($value), 
				"s3:config::${variable}" => $anvil->Log->is_secure($anvil->data->{config}{$variable}), 
			}});
		}
	}
	
	### TODO: Remove this when no longer needed.
	# Convert old variables to new ones.
	foreach my $config_step (sort {$a cmp $b} keys %{$anvil->data->{variables}{form}})
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { config_step => $config_step }});
		foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{variables}{form}{$config_step}})
		{
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }});
			
			if (exists $anvil->data->{variables}{form}{$config_step}{$variable}{value})
			{
				$anvil->data->{config}{$variable} = $anvil->data->{variables}{form}{$config_step}{$variable}{value};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					"config::${variable}" => $anvil->data->{config}{$variable},
				}});
			}
		}
	}
	
	# Make sure the DNS values are cleaned up. Otherwise a space can cause an unnecessary reconfigure.
	$anvil->data->{config}{dns} =~ s/\s+//g;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		"config::dns" => $anvil->data->{config}{dns}, 
	}});

	# 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  => $anvil->Job->bump_progress({steps => 1}), 
		message   => "message_0015", 
		job_uuid  => $anvil->data->{job}{uuid},
		'print'   => 1,
		log_level => 1,
		file      => $THIS_FILE, 
		line      => __LINE__,
	});
	
	return(0);
}

sub overwrite_variables_with_switches
{
	my ($anvil) = @_;

	if (not defined $anvil->data->{variables})
	{
		$anvil->data->{variables} = {};
	}

	foreach my $switch_name (keys %{$anvil->data->{switches}})
	{
		my $switch_value = $anvil->data->{switches}{$switch_name};

		if ($switch_name =~ /^form::config_step[^\s]+$/)
		{
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
				'switches::variable_name'  => $switch_name,
				'switches::variable_value' => $anvil->Log->is_secure($switch_value),
			}});
			$anvil->_make_hash_reference($anvil->data->{variables}, $switch_name, $switch_value);
		}
	}

	return(0);
}
