#!/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 # # 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 => ["job-uuid"], man => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}}); # Make sure we're running as 'root' # $< == real UID, $> == effective UID if (($< != 0) && ($> != 0)) { # Not root $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0005"}); $anvil->nice_exit({exit_code => 1}); } # Connect $anvil->Database->connect({debug => 2, 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({ debug => 2, variable_name => "system::configured", variable_value => 1, variable_default => "", variable_description => "striker_0048", variable_section => "system", variable_source_uuid => $anvil->data->{sys}{host_uuid}, variable_source_table => "hosts", }); update_passwords($anvil); $anvil->Job->update_progress({ progress => 100, message => "", job_uuid => $anvil->data->{job}{uuid}, }); # Clear maintenance mode. $anvil->System->maintenance_mode({set => 0, debug => 2}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "log_0467"}); ### TODO: As of now, the network doesn't come up reliably, so reboot. We add a 60 second delay to make it ### easier to log in and disable anvil-daemon in case of reboot loops caused by this program thinking ### it needs to reconfigure the system every run. # if ($anvil->data->{sys}{reboot}) # { # do_reboot($anvil); # } $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({set => 10}), message => "message_0388", job_uuid => $anvil->data->{job}{uuid}, 'print' => 1, log_level => 1, }); my $time_left = 60; while ($time_left) { # Give them the countdown. $anvil->Job->update_progress({ progress => $anvil->Job->bump_progress({set => 10}), message => "log_0626", job_uuid => $anvil->data->{job}{uuid}, 'print' => 1, log_level => 1, 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({set => 10}), message => "message_0389", job_uuid => $anvil->data->{job}{uuid}, 'print' => 1, log_level => 1, }); 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 apache 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-".$anvil->Get->uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { password => $password }}); # Write the password into a temporary file. my $error = $anvil->Storage->write_file({ body => $password, file => $temp_file, group => "root", mode => "0600", overwrite => 1, secure => 1, user => "root", }); # Call anvil-change-password if ($error) { # Couldn't write the temp file. $anvil->Job->update_progress({ progress => 100, message => "message_0030", log_leve => 1, 'print' => 1, job_uuid => $anvil->data->{job}{uuid}, job_status => "failed", 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'}." -y --password-file ".$temp_file.$anvil->Log->switches; $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 => 15, shell_call => $shell_call }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }}); foreach my $line (split/\n/, $output) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); } 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. unlink $temp_file; } } $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, }}); # 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 }}); } # Set the host_name my ($host_name, $descriptive_host_name) = $anvil->System->host_name({set => $new_host_name, pretty => $pretty_host_name, debug => 3}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { host_name => $host_name, descriptive_host_name => $descriptive_host_name, }}); if ($host_name eq $new_host_name) { # Success $anvil->Job->update_progress({ progress => $anvil->Job->bump_progress({set => 10}), message => "message_0016", job_uuid => $anvil->data->{job}{uuid}, 'print' => 1, log_level => 1, 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, job_status => "failed", variables => { host_name => $new_host_name, bad_host_name => $host_name, }, }); $anvil->nice_exit({exit_code => 4}); } } # Read the local network manager data. #$anvil->Network->read_nmcli({debug => 2}); # Get the current list of IPs and MAC addresses. #$anvil->Network->get_ips({debug => 2}); # If we're a striker, check apache's config. if ($type eq "striker") { $anvil->Striker->check_httpd_conf({debug => 2}); } # 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 => 3, list => { gateway => $gateway, gateway_interface => $gateway_interface, }}); # If there is no default gateway device, and one of the interfaces are set to DHCP, use it. if (not $gateway_interface) { # IFN first, BCN second, SN last foreach my $network_type ("ifn", "bcn", "sn", "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; } } } } ### NOTE: Not disconnecting anymore, we'll reboot on changes. # Disconnect from the database, as we're about to tear down our connection. # $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "log_0466"}); # $anvil->Database->disconnect(); # # Close all open SSH connections # foreach my $ssh_fh_key (sort {$a cmp $b} keys %{$anvil->data->{cache}{ssh_fh}}) # { # my $ssh_fh = $anvil->data->{cache}{ssh_fh}{$ssh_fh_key}; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ssh_fh => $ssh_fh }}); # # next if $ssh_fh !~ /^Net::OpenSSH/; # $ssh_fh->disconnect(); # $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "message_0009", variables => { target => $ssh_fh_key }}); # # # For good measure, blank both variables. # $anvil->data->{cache}{ssh_fh}{$ssh_fh_key} = ""; # $ssh_fh = ""; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cache::ssh_fh::${ssh_fh_key}" => $anvil->data->{cache}{ssh_fh}{$ssh_fh_key} }}); # } # 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_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, variables => { mac_address => $variable_value, iface1 => $macs->{$variable_value}, iface2 => $device, }, }); $anvil->nice_exit({exit_code => 8}); } } } # We'll set this to '1' if we reconfigure the network $anvil->data->{sys}{reboot} = 0; # Read the existing network data $anvil->Network->collect_data({debug => 2}); # 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 }}); if ($network_type eq "ifcfg") { foreach my $network_type ("bcn", "sn", "mn", "ifn") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }}); # This is the old type of network config #configure_ifcfg_network($anvil, $network_type); } } else { # Configure the network using Network Manager reconfigure_interfaces($anvil); reconfigure_bonds($anvil); reconfigure_bridges($anvil); reconfigure_ip_addresses($anvil); } die; # If we should reset, do so now. if ($anvil->data->{sys}{reboot}) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0687", variables => { reason => "#!string!log_0631!#" }}); do_reboot($anvil); } if ($changes) { # In an attempt to make network changes more reliable, we'll just reboot. This shouldn't # actually be hit anymore as any change should have triggered the reboot above. $anvil->data->{sys}{reboot} = 1; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0687", variables => { reason => "#!string!log_0631!#" }}); do_reboot($anvil); # Re-read the config $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0463"}); my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection reload"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); # Give a couple seconds for the reload sleep 2; # Now check the bonds my $repaired = $anvil->Network->check_network({heal => "all"}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { repaired => $repaired }}); if ($repaired) { # It can take a bit for the bonds to allow traffic, so sleep for a bit. sleep 30; } } # Wait for a DB connection. We'll wait up to 130 seconds, as sometimes it takes a while for the network # to start routing traffic. my $wait_until = time + 130; until ($anvil->data->{sys}{database}{connections}) { $anvil->refresh(); $anvil->Database->connect(); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "log_0132"}); if (not $anvil->data->{sys}{database}{connections}) { if (time > $wait_until) { # Failed to reconnect, reboot. Hopefully the network comes up cleanly # next time.. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0107"}); do_reboot($anvil); } # No databases, sleep and then try again. sleep 10; } } # We're half-way there. $anvil->Job->update_progress({ progress => $anvil->Job->bump_progress({set => 50}), job_uuid => $anvil->data->{job}{uuid}, }); # If any virtio bridges exist, remove it/them. my $start = 0; my ($bridges, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." net-list"}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridges => $bridges, return_code => $return_code }}); if ($return_code) { ### NOTE: We're doing a bunch of deletes, so to be safe we statically define the directory ### here. # Libvirtd isn't running, check the directory directly. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0478"}); my $directory = "/etc/libvirt/qemu/networks/autostart"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { directory => $directory }}); if (-d $directory) { # Delete all files. local(*DIRECTORY); opendir(DIRECTORY, $directory); while(my $file = readdir(DIRECTORY)) { next if $file eq "."; next if $file eq ".."; my $full_path = $directory."/".$file; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file, full_path => $full_path, }}); if (-l $full_path) { # It's a symlink, remove it. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0479", variables => { 'symlink' => $full_path }}); unlink $full_path; if (-l $full_path) { # It didn't work... $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 0, priority => "err", key => "error_0132", variables => { 'symlink' => $full_path }});; } } } closedir(DIRECTORY); } } else { foreach my $line (split/\n/, $bridges) { $line = $anvil->Words->clean_spaces({string => $line}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /^----------/) { $start = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { start => $start }}); next; } next if not $start; my $bridge = ($line =~ /(.*?)\s/)[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridge => $bridge }}); $anvil->data->{virsh}{bridge}{$bridge} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virsh::bridge::$bridge" => $anvil->data->{virsh}{bridge}{$bridge} }}); } foreach my $bridge (sort {$a cmp $b} keys %{$anvil->data->{virsh}{bridge}}) { # Destroy (stop) it. my ($destroy, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{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({set => 75}), job_uuid => $anvil->data->{job}{uuid}, }); return(0); } sub reconfigure_bridges { my ($anvil) = @_; return(0); } sub reconfigure_bonds { 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 $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 }}); # 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. next if not exists $anvil->data->{config}{$bond_name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "config::${bond_name}" => $anvil->data->{config}{$bond_name}, }}); # Check if the bond exists or not. $anvil->Job->update_progress({ progress => $anvil->Job->bump_progress({set => 10}), message => "message_0390", job_uuid => $anvil->data->{job}{uuid}, 'print' => 1, log_level => 1, variables => { bond_name => $bond_name }, }); if (exists $anvil->data->{nmcli}{bond}{$bond_name}) { # It does. $anvil->Job->update_progress({ progress => $anvil->Job->bump_progress({set => 10}), message => "message_0391", job_uuid => $anvil->data->{job}{uuid}, 'print' => 1, log_level => 1, variables => { nm_uuid => $anvil->data->{nmcli}{bond}{$bond_name}{uuid} }, }); } else { # It doesn't, create it. 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, }}); $anvil->Job->update_progress({ progress => $anvil->Job->bump_progress({set => 10}), message => "message_0392", job_uuid => $anvil->data->{job}{uuid}, 'print' => 1, log_level => 1, 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, }, }); die; # my $primary_interface = $anvil->data->{network_manager}{want}{bond}{$bond_name}{interfaces}->[0]; # if (not $primary_interface) # { # print "[ Error ] - There appears to be no primary interface specified for this bond!\n"; # $anvil->nice_exit({exit_code => 1}); # } # print "- It does not, creating it with the primary interface: [".$primary_interface."] now.\n"; # 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=".$primary_interface."\""; # $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) # { # print "[ Error ] - The attempt to add the bond failed! The return code was: [".$return_code."]. The output, if any, was:\n"; # print "========\n"; # print $output."\n"; # print "========\n"; # $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 }}); # # if ($bond_uuid) # { # print " - Disabling DHCP on the new bond device: [".$bond_uuid."].\n"; # my ($output, $return_code) = modify_connection($anvil, $bond_uuid, "ipv4.method", "disabled"); # ($output, $return_code) = modify_connection($anvil, $bond_uuid, "ipv6.method", "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, # }}); # } # # # Rescan. # print " - Done! Rescanning the network config.\n"; # collect_data($anvil); } # Now add the interfaces, disabling their ipv4.method first. # foreach my $interface_name (@{$anvil->data->{network_manager}{want}{bond}{$bond_name}{interfaces}}) # { # # What is the interface UUID? # my $interface_uuid = $anvil->data->{interface}{device}{$interface_name}{uuid}; # my $parent_bond_name = $anvil->data->{interface}{uuid}{$interface_uuid}{'connection.master'}; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # interface_name => $interface_name, # interface_uuid => $interface_uuid, # 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) # { # print "- The interface: [".$interface_name."] (".$interface_uuid.") is already a member of the bond.\n"; # next; # } # else # { # print "- The interface: [".$interface_name."] (".$interface_uuid.") is a member of the bond: [".$parent_bond_name."], switching it to this bond.\n"; # } # } # else # { # print "- The interface: [".$interface_name."] (".$interface_uuid.") needs to be connected to the bond.\n"; # } # # print " - Disabling DHCP on the interface\n"; # my ($output, $return_code) = modify_connection($anvil, $interface_uuid, "ipv4.method", "disabled"); # ($output, $return_code) = modify_connection($anvil, $interface_uuid, "ipv6.method", "disabled"); # # print " - Connecting the interface to the bond.\n"; # my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify ".$interface_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, # }}); # # if ($return_code) # { # print "[ Error ] - The attempt to add the bond failed! The return code was: [".$return_code."]. The output, if any, was:\n"; # print "========\n"; # print $output."\n"; # print "========\n"; # $anvil->nice_exit({exit_code => 1}); # } # # # Rescan. # print " - Done! Rescanning the network config.\n"; # ($output, $return_code) = reset_connection($anvil, $interface_uuid); # # # Rescan. # collect_data($anvil); # } } } =cut * nmcli::uuid::::device = 'connection.interface-name', or 'GENERAL.DEVICES'. See note below * nmcli::uuid::::type = interface, bond, bridge, etc * nmcli::uuid::::active = 1,0 * nmcli::uuid::::state = activated,activating,etc * nmcli::uuid:::: = all 'variable: value' pairs returned by 'nmcli connection show ' * nmcli::uuid::::mac_address = MAC address (in lower case) * nmcli::uuid::::connected = 0 is down, unix timestamp (seconds since epoch) of when it connected if up. * nmcli::uuid::::mtu = This is the MTU (maximum transimssion unit in bytes) of the interface. To make it easier to map a device by name or MAC address to a UUID, this lookup hash is provided. Note that 'device' is 'connection.interface-name' when available, falling back to 'GENERAL.DEVICES' otherwise. B<< NOTE >>: An inactive interface will not report the 'connection.interface-name', and the bios device name will be returned (which is what is stored in 'GENERAL.DEVICES'. If you're trying to find a device, and the expected name doesn't exist, look up the device by MAC address. If that's not found, then the old GENERAL.DEVICES name can help you identify a replaced interface. * nmcli::device::::uuid = interface name (or device name) * nmcli::mac_address::::uuid = MAC address (lower case) Given that a single interface can have multiple IP addresses and routes, the IPs on a given interface are stored using a sequence number <1, 2, 3 ... n>. To make it easier to find what device has an IP, the IPs are stored with a quick access hash. * nmcli::ipv4::::on_uuid = interface UUID * nmcli::ipv4::::sequence = sequence number * nmcli::uuid::::ipv{4,6}::ip::::ip_address = IP address * nmcli::uuid::::ipv{4,6}::ip::::subnet_mask = subnet mask (CIDR notation) * nmcli::uuid::::ipv{4,6}::dns = comma-separated list of DNS IP addresses * nmcli::uuid::::ipv{4,6}::gateway = comma-separated list of DNS IP addresses * nmcli::uuid::::ipv{4,6}::route:: = Route info (ie: 'dst = 0.0.0.0/0, nh = 192.168.255.254, mt = 428', or 'dst = 192.168.0.0/16, nh = 0.0.0.0, mt = 428'.) Bond data is stored in these hashes; * nmcli::bond::::uuid = The UUID on the bond * nmcli::bond::::carrier = 1,0 - indicates if the bond has a connection or not. * nmcli::bond::::operstate = 1,0 - indicates if the bond is operational or not. * nmcli::bond::::up = 1,0 - indicates if the bond up up or not. * nmcli::bond::::interface::::up = 1,0 - indicates if the child interface is up or not. Bridge data is simple, but also made easy to find. The only real data is the hash references for the interfaces connected to the bridge. * nmcli::bridge::::uuid = The UUID of the bridge * nmcli::bridge::::interface::::status = This is the link data for the connected interface (ie: 'BROADCAST,MULTICAST,MASTER,UP,LOWER_UP'). To make it easier to find interfaces, the following look up hash is available. * nmcli::interface::::uuid = The UUID of the interface * nmcli::mac_address::::uuid = $anvil->data->{nmcli}{mac_address}{$mac_address}{uuid}, =cut foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{config}}) { print $variable.": [".$anvil->data->{config}{$variable}."]\n"; } return(0); } sub reconfigure_ip_addresses { my ($anvil) = @_; return(0); } sub reconfigure_interfaces { 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 }}); # 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, }}); # 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 }}); # Get the Network Manager UUIDs. if (not $link1_nm_uuid) { $anvil->Job->update_progress({ progress => 100, message => "error_0480", log_leve => 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}); } rename_interface($anvil, $wanted_link1_name, $link1_nm_uuid); my $link2_nm_uuid = ""; if ($link2_nm_uuid) { $link2_nm_uuid = $anvil->data->{nmcli}{mac_address}{$wanted_link1_mac}{uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link2_nm_uuid => $link2_nm_uuid }}); if (not $link2_nm_uuid) { $anvil->Job->update_progress({ progress => 100, message => "error_0480", log_leve => 1, 'print' => 1, job_uuid => $anvil->data->{job}{uuid}, job_status => "failed", variables => { mac_address => $wanted_link2_mac, interface_name => $wanted_link2_name, }, }); $anvil->nice_exit({exit_code => 1}); } rename_interface($anvil, $wanted_link2_name, $link2_nm_uuid); # There's a second interface, so create a bond. my $bond_name = $network_type.$i."_bond1"; $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 ames 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}; $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 => 2}), message => "message_0383", log_leve => 1, 'print' => 1, 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, }, }); # 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({debug => 2, 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 => 2}), message => "message_0384", log_leve => 1, 'print' => 1, 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_leve => 1, 'print' => 1, 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 => 2}), message => "message_0385", log_leve => 1, 'print' => 1, 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, }}); $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_leve => 1, 'print' => 1, 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 => 2}), message => "message_0386", log_leve => 1, 'print' => 1, job_uuid => $anvil->data->{job}{uuid}, variables => { new_name => $wanted_link_name, old_device => $old_device, }, }); 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, }}); # 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_leve => 1, 'print' => 1, job_uuid => $anvil->data->{job}{uuid}, job_status => "failed", 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 => 2}), message => "message_0387", log_leve => 1, 'print' => 1, 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, }}); # 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->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); } sub configure_ifcfg_network { my ($anvil, $network_type) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }}); =cut 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; } my $localhost = $anvil->Get->short_host_name(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); next if not $count; foreach my $network_count (1..$count) { # If the user had the option to create a network but didn't, there will be no link1 # mac to set. my $this_network = $network_type.$network_count; my $link1_key = $this_network."_link1_mac_to_set"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { this_network => $this_network, link1_key => $link1_key, }}); next if not exists $anvil->data->{config}{$link1_key}; next if not $anvil->data->{config}{$link1_key}; my $link2_key = $this_network."_link2_mac_to_set"; my $subnet_mask_key = $this_network."_subnet_mask"; my $ip_key = $this_network."_ip"; my $bridge_key = $this_network."_create_bridge"; my $link1_mac = $anvil->data->{config}{$link1_key}; my $is_gateway = $this_network eq $gateway_interface ? 1 : 0; my $link2_mac = defined $anvil->data->{config}{$link2_key} ? $anvil->data->{config}{$link2_key} : ""; my $old_link1_iface = $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} ? $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} : ""; my $old_link2_iface = defined $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface} ? $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface} : ""; my $bridge = defined $anvil->data->{config}{$bridge_key} ? $anvil->data->{config}{$bridge_key} : 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ip_key => $ip_key, is_gateway => $is_gateway, link1_mac => $link1_mac, link2_key => $link2_key, link2_mac => $link2_mac, bridge_key => $bridge_key, old_link1_iface => $old_link1_iface, old_link2_iface => $old_link2_iface, subnet_mask_key => $subnet_mask_key, bridge => $bridge, }}); # Dig out the name that network manager knows the old interface(s) as. The # 'old_link1_iface' is the name reported by 'ip', the 'DEVICE=xxx' value in the # ifcfg-xxx file. my $old_link1_nm_name = $old_link1_iface; my $link1_nm_uuid = $anvil->data->{nmcli}{$local_host}{device_to_uuid}{$old_link1_iface}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link1_nm_name => $old_link1_nm_name, link1_nm_uuid => $link1_nm_uuid, }}); if ((exists $anvil->data->{nmcli}{$local_host}{uuid}{$link1_nm_uuid}) && ($anvil->data->{nmcli}{$local_host}{uuid}{$link1_nm_uuid}{name})) { $old_link1_nm_name = $anvil->data->{nmcli}{$local_host}{uuid}{$link1_nm_uuid}{name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link1_nm_name => $old_link1_nm_name }}); } # Is there a link 2? my $old_link2_nm_name = ""; if ($old_link2_iface) { $old_link2_nm_name = $old_link2_iface; my $link2_nm_uuid = $anvil->data->{nmcli}{$local_host}{device_to_uuid}{$old_link2_nm_name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link2_nm_name => $old_link2_nm_name, link2_nm_uuid => $link2_nm_uuid, }}); if ((exists $anvil->data->{nmcli}{$local_host}{uuid}{$link2_nm_uuid}) && ($anvil->data->{nmcli}{$local_host}{uuid}{$link2_nm_uuid}{name})) { $old_link2_nm_name = $anvil->data->{nmcli}{$local_host}{uuid}{$link2_nm_uuid}{name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link2_nm_name => $old_link2_nm_name }}); } } # Skip if this doesn't exist or isn't a valid IPv4 address. if (not exists $anvil->data->{config}{$ip_key}) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0176", variables => { ip_key => $ip_key }}); next; } else { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "config::${ip_key}" => $anvil->data->{config}{$ip_key} }}); } if (($anvil->data->{config}{$ip_key}) && ($anvil->data->{config}{$ip_key} ne "dhcp") && (not $anvil->Validate->ipv4({ip => $anvil->data->{config}{$ip_key}}))) { # Something was set, but it isn't valid. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0148", variables => { network => $this_network, ip => $anvil->data->{config}{$ip_key}, }}); next; } # The IP could be 'dhcp', we'll handle that in a bit. my $ip_address = $anvil->data->{config}{$ip_key}; my $subnet_mask = defined $anvil->data->{config}{$subnet_mask_key} ? $anvil->data->{config}{$subnet_mask_key} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ip_address => $ip_address, subnet_mask => $subnet_mask, }}); # Are we building bonded interfaces? if ($anvil->Validate->mac({mac => $link2_mac})) { # Yup! my $say_network = ""; my $say_interface = ""; my $interface_prefix = ""; my $say_defroute = $is_gateway ? "yes" : "no"; if ($network_type eq "bcn") { $say_network = "Back-Channel Network ".$network_count; $say_interface = "bcn".$network_count; $interface_prefix = "BCN"; } elsif ($network_type eq "sn") { $say_network = "Storage Network ".$network_count; $say_interface = "sn".$network_count; $interface_prefix = "SN"; } elsif ($network_type eq "mn") { $say_network = "Migration Network ".$network_count; $say_interface = "mn".$network_count; $interface_prefix = "MN"; } elsif ($network_type eq "ifn") { $say_network = "Internet-Facing Network ".$network_count; $say_interface = "ifn".$network_count; $interface_prefix = "IFN"; } # Gather variables my $cidr = $ip_address eq "dhcp" ? "" : $anvil->Convert->cidr({subnet_mask => $subnet_mask}); my $bridge_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Bridge_1"; my $bond_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Bond_1"; my $new_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_1"; my $new_link2_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_2"; my $old_link1_file = $new_link1_file; my $old_link2_file = $new_link2_file; my $new_bridge_iface = $say_interface."_bridge1"; my $new_bond_iface = $say_interface."_bond1"; my $new_link1_iface = $say_interface."_link1"; my $new_link2_iface = $say_interface."_link2"; my $boot_proto = $ip_address eq "dhcp" ? "dhcp" : "none"; my $link1_uuid = get_uuid_from_interface_file($anvil, $old_link1_file); my $link2_uuid = get_uuid_from_interface_file($anvil, $old_link2_file); if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface})) { # This is the device name (ie: bcn1_link1, ens0, etc). If it doesn't # exist, see if the new name exists already. $old_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link1_file => $old_link1_file }}); if (not -f $old_link1_file) { # Does the new file already exist? if (-f $new_link1_file) { # Set the old file to the new one. $old_link1_file = $new_link1_file; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link1_file => $old_link1_file }}); } } } if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface})) { $old_link2_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link2_file => $old_link2_file }}); if (not -f $old_link2_file) { # Does the new file already exist? if (-f $new_link2_file) { # Set the old file to the new one. $old_link2_file = $new_link2_file; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link2_file => $old_link2_file }}); } } } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bond_file => $bond_file, boot_proto => $boot_proto, bridge_file => $bridge_file, cidr => $cidr, link1_uuid => $link1_uuid, link2_uuid => $link2_uuid, new_bond_iface => $new_bond_iface, new_bridge_iface => $new_bridge_iface, new_link1_file => $new_link1_file, new_link1_iface => $new_link1_iface, new_link2_file => $new_link2_file, new_link2_iface => $new_link2_iface, old_link1_file => $old_link1_file, old_link2_file => $old_link2_file, say_defroute => $say_defroute, }}); ### NOTE: Bridges and bonds take a UUID, but it can be temperamental. It ### works more reliably without defining it, so we don't. # Are we building a bridge interface? my $bridge_config = ""; if ($bridge) { my $new_bridge1_nm_name = $interface_prefix." ".$network_count." - Bridge 1"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_bridge1_nm_name => $new_bridge1_nm_name }}); # Record that we need to start this. push @{$new_interfaces}, $new_bridge1_nm_name; # Yup! $bridge_config = "# $say_network - Bridge 1\n"; $bridge_config .= "DEVICE=\"".$new_bridge_iface."\"\n"; $bridge_config .= "NAME=\"".$new_bridge1_nm_name."\"\n"; $bridge_config .= "TYPE=\"Bridge\"\n"; $bridge_config .= "STP=\"yes\"\n"; $bridge_config .= "BRIDGING_OPTS=\"priority=32768\"\n"; $bridge_config .= "PROXY_METHOD=\"none\"\n"; $bridge_config .= "BROWSER_ONLY=\"no\"\n"; $bridge_config .= "IPV4_FAILURE_FATAL=\"no\"\n"; $bridge_config .= "IPV6INIT=\"no\"\n"; $bridge_config .= "BOOTPROTO=\"".$boot_proto."\"\n"; # If the IP is NOT 'dhcp', set it. if ($ip_address ne "dhcp") { $bridge_config .= "IPADDR=\"".$ip_address."\"\n"; $bridge_config .= $cidr ? "PREFIX=\"".$cidr."\"\n" : "NETMASK=\"".$subnet_mask."\"\n"; # If this is the default gateway, add that info. if ($is_gateway) { $bridge_config .= "GATEWAY=\"".$gateway."\"\n"; for (my $i = 0; $i < @{$dns}; $i++) { $bridge_config .= "DNS".($i+1)."=\"".$dns->[$i]."\"\n"; } } } $bridge_config .= "DEFROUTE=\"".$say_defroute."\"\n"; $bridge_config .= "ONBOOT=\"yes\"\n"; $bridge_config .= "ZONE=\"".uc($say_interface)."\"\n"; } # If this is DHCP, but there is a bridge, the bond's boot proto in 'none'. $boot_proto = "none" if $bridge; # Make the rest of the network names. my $new_bond1_nm_name = $interface_prefix." ".$network_count." - Bond 1"; my $new_link1_nm_name = $interface_prefix." ".$network_count." - Link 1"; my $new_link2_nm_name = $interface_prefix." ".$network_count." - Link 2"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_bond1_nm_name => $new_bond1_nm_name, new_link1_nm_name => $new_link1_nm_name, new_link2_nm_name => $new_link2_nm_name, }}); # Record that we need to start this. push @{$new_interfaces}, $new_bond1_nm_name; push @{$new_interfaces}, $new_link1_nm_name; push @{$new_interfaces}, $new_link2_nm_name; # Build the Bond config. my $bond_config = "# $say_network - Bond 1\n"; $bond_config .= "DEVICE=\"".$new_bond_iface."\"\n"; $bond_config .= "NAME=\"".$new_bond1_nm_name."\"\n"; $bond_config .= "TYPE=\"Bond\"\n"; $bond_config .= "BONDING_OPTS=\"downdelay=0 miimon=100 mode=active-backup primary=".$say_interface."_link1 updelay=120000\"\n"; $bond_config .= "BONDING_MASTER=\"yes\"\n"; $bond_config .= "ONBOOT=\"yes\"\n"; # Is this connected to a bridge? if ($bridge) { $bond_config .= "BRIDGE=\"".$new_bridge_iface."\"\n"; } else { # Does this bond have an IP? # If the IP is NOT 'dhcp', set it. $bond_config .= "BOOTPROTO=\"".$boot_proto."\"\n"; if ($ip_address ne "dhcp") { $bond_config .= "IPADDR=\"".$ip_address."\"\n"; $bond_config .= $cidr ? "PREFIX=\"".$cidr."\"\n" : "NETMASK=\"".$subnet_mask."\"\n"; # If this is the default gateway, add that info. if ($is_gateway) { $bond_config .= "GATEWAY=\"".$gateway."\"\n"; for (my $i = 0; $i < @{$dns}; $i++) { $bond_config .= "DNS".($i+1)."=\"".$dns->[$i]."\"\n"; } } } $bond_config .= "DEFROUTE=\"".$say_defroute."\"\n"; } # Rest of the config $bond_config .= "ZONE=\"".uc($say_interface)."\"\n"; # Now build the links my $link1_config = "# $say_network - Link 1\n"; $link1_config .= "HWADDR=\"".uc($link1_mac)."\"\n"; $link1_config .= "UUID=\"".$link1_uuid."\"\n"; $link1_config .= "NAME=\"".$new_link1_nm_name."\"\n"; $link1_config .= "DEVICE=\"".$new_link1_iface."\"\n"; $link1_config .= "TYPE=\"Ethernet\"\n"; $link1_config .= "BOOTPROTO=\"none\"\n"; $link1_config .= "IPV6INIT=\"no\"\n"; $link1_config .= "ONBOOT=\"yes\"\n"; $link1_config .= "USERCTL=\"no\"\n"; # $link1_config .= "MTU=\"1500\"\n"; # TODO: Make the MTU user-adjustable $link1_config .= "NM_CONTROLLED=\"yes\"\n"; $link1_config .= "SLAVE=\"yes\"\n"; $link1_config .= "MASTER=\"".$say_interface."_bond1\"\n"; $link1_config .= "ZONE=\"".uc($say_interface)."\"\n"; my $link2_config = "# $say_network - Link 2\n"; $link2_config .= "HWADDR=\"".uc($link2_mac)."\"\n"; $link2_config .= "UUID=\"".$link2_uuid."\"\n"; $link2_config .= "NAME=\"".$new_link2_nm_name."\"\n"; $link2_config .= "DEVICE=\"".$new_link2_iface."\"\n"; $link2_config .= "TYPE=\"Ethernet\"\n"; $link2_config .= "BOOTPROTO=\"none\"\n"; $link2_config .= "IPV6INIT=\"no\"\n"; $link2_config .= "ONBOOT=\"yes\"\n"; $link2_config .= "USERCTL=\"no\"\n"; # $link2_config .= "MTU=\"1500\"\n"; # TODO: Make the MTU user-adjustable $link2_config .= "NM_CONTROLLED=\"yes\"\n"; $link2_config .= "SLAVE=\"yes\"\n"; $link2_config .= "MASTER=\"".$say_interface."_bond1\"\n"; $link2_config .= "ZONE=\"".uc($say_interface)."\"\n"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridge_config => $bridge_config, bond_config => $bond_config, link1_config => $link1_config, link2_config => $link2_config, }}); # Decide if we need to reboot. if (($old_link1_file ne $new_link1_file) && (-e $new_link1_file)) { $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } if (($old_link2_file ne $new_link2_file) && (-e $new_link2_file)) { $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } ### Write out the new configs, if needed # Are we writing a bridge config? if ($bridge) { # If the file already exists, see if it changed. my $update = 1; if (-e $bridge_file) { # Read it in to see if there is a difference. my $old_body = $anvil->Storage->read_file({file => $bridge_file}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_body => $old_body }}); my $difference = diff \$old_body, \$bridge_config, { STYLE => 'Unified' }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $difference }}); if ($difference) { # Backup the old file. $anvil->Storage->backup({debug => 2, file => $bridge_file}); $changes = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); } else { # No need to update $update = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); } } if ($update) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { bridge_file => $bridge_file, bridge_config => $bridge_config, }}); $anvil->Storage->write_file({ file => $bridge_file, body => $bridge_config, user => "root", group => "root", mode => "0644", overwrite => 1 }); } } # Bond, Link 1 and Link 2 my $update_bond = 1; if (-f $bond_file) { # Read it in to see if there is a difference. my $old_body = $anvil->Storage->read_file({file => $bond_file}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_body => $old_body }}); my $difference = diff \$old_body, \$bond_config, { STYLE => 'Unified' }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $difference }}); if ($difference) { # Backup the old file. $anvil->Storage->backup({debug => 2, file => $bond_file}); $changes = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); } else { # No need to update $update_bond = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_bond => $update_bond }}); } } if ($update_bond) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { bond_file => $bond_file, bond_config => $bond_config, }}); $anvil->Storage->write_file({ file => $bond_file, body => $bond_config, user => "root", group => "root", mode => "0644", overwrite => 1 }); } # Link 1 my $update_link1 = 1; if (-f $new_link1_file) { # Read it in to see if there is a difference. my $old_body = $anvil->Storage->read_file({file => $new_link1_file}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_body => $old_body }}); my $difference = diff \$old_body, \$link1_config, { STYLE => 'Unified' }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $difference }}); if ($difference) { # Backup the old file. $anvil->Storage->backup({debug => 2, file => $new_link1_file}); $changes = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); } else { # No need to update $update_link1 = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_link1 => $update_link1 }}); } } if ($update_link1) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { link1_file => $new_link1_file, link1_config => $link1_config, }}); $anvil->Storage->write_file({ file => $new_link1_file, body => $link1_config, user => "root", group => "root", mode => "0644", overwrite => 1 }); } # Link 2 my $update_link2 = 1; if (-f $new_link2_file) { # Read it in to see if there is a difference. my $old_body = $anvil->Storage->read_file({file => $new_link2_file}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_body => $old_body }}); my $difference = diff \$old_body, \$link2_config, { STYLE => 'Unified' }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $difference }}); if ($difference) { # Backup the old file. $anvil->Storage->backup({debug => 2, file => $new_link2_file}); $changes = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); } else { # No need to update $update_link2 = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_link2 => $update_link2 }}); } } if ($update_link2) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { link2_file => $new_link2_file, link2_config => $link2_config, }}); $anvil->Storage->write_file({ file => $new_link2_file, body => $link2_config, user => "root", group => "root", mode => "0644", overwrite => 1 }); } # Backup the old config files, if needed. if (-e $old_link1_file) { $anvil->Storage->backup({file => $old_link1_file}); } if (-e $old_link2_file) { $anvil->Storage->backup({file => $old_link2_file}); } # If the NICs names have changed, rename them now. if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} ne $new_link1_iface)) { rename_interface($anvil, $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}, $new_link1_iface); $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface} ne $new_link2_iface)) { rename_interface($anvil, $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface}, $new_link2_iface); $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } # Remove the old link if it was different, of down and up it if the same. if ($old_link1_iface ne $new_link1_iface) { # Delete the old interface $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0461", variables => { interface => $old_link1_nm_name }}); # my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection delete ".$old_link1_nm_name; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # output => $output, # return_code => $return_code, # }}); if (-e $old_link1_file) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0589", variables => { file => $old_link1_file }}); unlink $old_link1_file; } $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } elsif ($update_link1) { # Down the interface $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0462", variables => { interface => $old_link1_nm_name }}); # my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection down ".$old_link1_nm_name; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # output => $output, # return_code => $return_code, # }}); # # $changes = 1; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } # Shut down (and rename) Link 2 if ($old_link2_iface ne $new_link2_iface) { # Delete the old interface $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0461", variables => { interface => $old_link2_nm_name }}); # my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection delete ".$old_link2_nm_name; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # output => $output, # return_code => $return_code, # }}); if (-e $old_link2_file) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0589", variables => { file => $old_link2_file }}); unlink $old_link2_file; } $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } elsif ($update_link1) { # Down the interface $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0462", variables => { interface => $old_link2_nm_name }}); # my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection down ".$old_link2_nm_name; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # output => $output, # return_code => $return_code, # }}); # # $changes = 1; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } } elsif ((exists $anvil->data->{config}{$link1_key}) && ($anvil->Validate->mac({mac => $anvil->data->{config}{$link1_key}}))) { ### NOTE: This only applies when configuring Striker dashboards. They can't ### be 'dhcp', either, so no checks are made for those cases. Likewise, ### bridges are not used. # Single interface, set it up my $link1_mac = $anvil->data->{config}{$link1_key}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link1_mac => $link1_mac }}); my $say_network = ""; my $say_interface = ""; my $interface_prefix = ""; if ($network_type eq "bcn") { $say_network = "Back-Channel Network ".$network_count; $say_interface = "bcn".$network_count; $interface_prefix = "BCN"; } elsif ($network_type eq "sn") { $say_network = "Storage Network ".$network_count; $say_interface = "sn".$network_count; $interface_prefix = "SN"; } elsif ($network_type eq "ifn") { $say_network = "Internet-Facing Network ".$network_count; $say_interface = "ifn".$network_count; $interface_prefix = "IFN"; } elsif ($network_type eq "mn") { $say_network = "Migration Network ".$network_count; $say_interface = "mn".$network_count; $interface_prefix = "MN"; } my $say_defroute = $is_gateway ? "yes" : "no"; my $cidr = $anvil->Convert->cidr({subnet_mask => $subnet_mask}); my $new_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_1"; my $old_link1_file = $new_link1_file; my $new_link1_iface = $say_interface."_link1"; if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface})) { $old_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}; if (not -f $old_link1_file) { # Does the new file already exist? if (-f $new_link1_file) { # Set the old file to the new one. $old_link1_file = $new_link1_file; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_link1_file => $old_link1_file }}); } } } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cidr => $cidr, new_link1_file => $new_link1_file, new_link1_iface => $new_link1_iface, old_link1_file => $old_link1_file, say_defroute => $say_defroute, }}); my $new_link1_nm_name = $interface_prefix." ".$network_count." - Link 1"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_link1_nm_name => $new_link1_nm_name }}); push @{$new_interfaces}, $new_link1_nm_name; # Gather (or create) UUIDs my $link1_uuid = get_uuid_from_interface_file($anvil, $old_link1_file); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link1_uuid => $link1_uuid }}); my $link1_config = "# $say_network - Link 1\n"; $link1_config .= "HWADDR=\"".uc($link1_mac)."\"\n"; $link1_config .= "UUID=\"".$link1_uuid."\"\n"; $link1_config .= "NAME=\"".$new_link1_nm_name."\"\n"; $link1_config .= "DEVICE=\"".$new_link1_iface."\"\n"; $link1_config .= "TYPE=\"Ethernet\"\n"; $link1_config .= "BOOTPROTO=\"none\"\n"; $link1_config .= "IPV6INIT=\"no\"\n"; $link1_config .= "ONBOOT=\"yes\"\n"; $link1_config .= "IPADDR=\"".$ip_address."\"\n"; $link1_config .= $cidr ? "PREFIX=\"".$cidr."\"\n" : "NETMASK=\"".$subnet_mask."\"\n"; if ($is_gateway) { $link1_config .= "GATEWAY=\"".$gateway."\"\n"; for (my $i = 0; $i < @{$dns}; $i++) { $link1_config .= "DNS".($i+1)."=\"".$dns->[$i]."\"\n"; } } $link1_config .= "DEFROUTE=\"".$say_defroute."\"\n"; $link1_config .= "USERCTL=\"no\"\n"; # $link1_config .= "MTU=\"1500\"\n"; # TODO: Make the MTU user-adjustable $link1_config .= "NM_CONTROLLED=\"yes\"\n"; $link1_config .= "ZONE=\"".uc($say_interface)."\""; # Link 1 my $update_link1 = 1; if (-f $new_link1_file) { # Read it in to see if there is a difference. my $old_body = $anvil->Storage->read_file({file => $new_link1_file}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_body => $old_body }}); my $difference = diff \$old_body, \$link1_config, { STYLE => 'Unified' }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $difference }}); if ($difference) { # Backup the old file. $anvil->Storage->backup({debug => 2, file => $new_link1_file}); $changes = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); } else { # No need to update $update_link1 = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_link1 => $update_link1 }}); } } if ($update_link1) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { link1_file => $new_link1_file, link1_config => $link1_config, }}); $anvil->Storage->write_file({ file => $new_link1_file, body => $link1_config, user => "root", group => "root", mode => "0644", overwrite => 1 }); } # Backup the existing link1 file, if it exists and is different. if (-e $old_link1_file) { $anvil->Storage->backup({file => $old_link1_file}); } # If the name differs from old, delete the old interface. if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} ne $new_link1_iface)) { # Delete the old interface $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0461", variables => { interface => $old_link1_nm_name }}); # my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection delete ".$old_link1_nm_name; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # $anvil->System->call({debug => 2, shell_call => $shell_call}); # # rename_interface($anvil, $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}, $new_link1_iface); $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } elsif ($update_link1) { # Down the interface $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0462", variables => { interface => $old_link1_nm_name }}); # my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection down ".$old_link1_nm_name; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # $anvil->System->call({debug => 2, shell_call => $shell_call}); # # $changes = 1; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); $changes = 1; $anvil->data->{sys}{reboot} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes, "sys::reboot" => $anvil->data->{sys}{reboot}, }}); } } else { # Doesn't exist, skip. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0149", variables => { network => $this_network }}); next; } } =cut return(0); } # This renames a network interface # sub rename_interface # { # my ($anvil, $old_link_name, $new_link_name) = @_; # # $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0465", variables => { # old_interface => $old_link_name, # new_interface => $new_link_name, # }}); # # # Take the old name down. # my $shell_call = $anvil->data->{path}{exe}{ip}." link set ".$old_link_name." down"; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # output => $output, # return_code => $return_code, # }}); # # # Rename # $shell_call = $anvil->data->{path}{exe}{ip}." link set ".$old_link_name." name ".$new_link_name; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # output => $output, # return_code => $return_code, # }}); # # # Bring up the new interface # $shell_call = $anvil->data->{path}{exe}{ip}." link set ".$new_link_name." up"; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # output => $output, # return_code => $return_code, # }}); # # return(0); # } # This will read a network interface file and return the UUID="x" value. If the file doesn't exist or the # UUID was not found, a new UUID is generated and returned. # sub get_uuid_from_interface_file # { # my ($anvil, $file) = @_; # $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 'print' => 1, key => "log_0131", variables => { function => "get_uuid_from_interface_file" }}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file }}); # # my $uuid = ""; # if (-e $file) # { # my $body = $anvil->Storage->read_file({file => $file}); # foreach my $line (split/\n/, $body) # { # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }}); # $line =~ s/#.*//; # if ($line =~ /UUID=\"(.*?)\"/) # { # my $test_uuid = $1; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { test_uuid => $test_uuid }}); # if ($anvil->Validate->uuid({uuid => $test_uuid})) # { # $uuid = $test_uuid; # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uuid => $uuid }}); # } # last; # } # } # } # if (not $uuid) # { # $uuid = $anvil->Get->uuid(); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uuid => $uuid }}); # } # # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uuid => $uuid }}); # return($uuid); # } # This will pick up the job, or exit. sub pickup_job_details { my ($anvil) = @_; # If this returns '1', the job-uuid was bad. If it returns '2', another process is running. my $return = $anvil->Job->get_job_details({ check => 1, job_uuid => $anvil->data->{switches}{'job-uuid'}, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 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, }}); } ### 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} = {}; ### 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_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}, }}); } } } # 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, }); # 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" => $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, }); $anvil->nice_exit({exit_code => 7}); } } 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); }