#!/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(); $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); # Set maintenance mode $anvil->System->maintenance_mode({set => 1}); reconfigure_network($anvil); # Record that we've configured this machine. $anvil->Database->insert_or_update_variables({ variable_name => "system::configured", variable_value => 1, variable_default => "", variable_description => "striker_0048", variable_section => "system", variable_source_uuid => $anvil->data->{sys}{host_uuid}, variable_source_table => "hosts", }); update_passwords($anvil); $anvil->Job->update_progress({ debug => 3, progress => 100, message => "", job_uuid => $anvil->data->{job}{uuid}, }); # Clear maintenance mode. $anvil->System->maintenance_mode({set => 0}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, 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) = @_; # Mark that a reboot is needed, in case something kills us before we actually reboot. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0687", variables => { reason => "#!string!log_0693!#" }}); my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ file => $THIS_FILE, line => __LINE__, job_command => $anvil->data->{path}{exe}{'anvil-manage-power'}." --reboot -y".$anvil->Log->switches, job_data => "", job_name => "reboot::system", job_title => "job_0009", job_description => "job_0006", job_progress => 0, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); my $time_left = 60; while ($time_left) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "log_0626", variables => { seconds => $time_left }}); sleep 10; $time_left -= 10; } 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->data->{variables}{form}{config_step2}{striker_password}{value} = "" if not defined $anvil->data->{variables}{form}{config_step2}{striker_password}{value}; if ($anvil->data->{variables}{form}{config_step2}{striker_password}{value}) { # Set the passwords my $password = $anvil->data->{variables}{form}{config_step2}{striker_password}{value}; my $temp_file = "/tmp/anvil-".$anvil->Get->uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { password => $password }}); # Write the password into a temporary file. my $error = $anvil->Storage->write_file({ body => $password, file => $temp_file, group => "root", mode => "0600", overwrite => 1, secure => 1, user => "root", }); # Call anvil-change-password if ($error) { # Couldn't write the temp file. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "message_0030", variables => { file => $temp_file }}); $anvil->nice_exit({exit_code => 5}); } else { my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{'anvil-change-password'}." -y --password-file ".$temp_file.$anvil->Log->switches }); $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 }}); } # Unlink the temp file. unlink $temp_file; if ($return_code) { # Something went wrong $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0011", variables => { return_code => $return_code }}); } } } $anvil->Job->update_progress({ progress => 95, job_uuid => $anvil->data->{job}{uuid}, }); return(0); } # This does the work of reconfiguring the network sub reconfigure_network { my ($anvil) = @_; my $local_host = $anvil->Get->short_host_name(); my $reboot_needed = 0; my $prefix = exists $anvil->data->{variables}{form}{config_step1}{prefix}{value} ? $anvil->data->{variables}{form}{config_step1}{prefix}{value} : ""; my $sequence = exists $anvil->data->{variables}{form}{config_step1}{sequence}{value} ? $anvil->data->{variables}{form}{config_step1}{sequence}{value} : ""; my $domain = exists $anvil->data->{variables}{form}{config_step1}{domain}{value} ? $anvil->data->{variables}{form}{config_step1}{domain}{value} : ""; my $organization = exists $anvil->data->{variables}{form}{config_step1}{organization}{value} ? $anvil->data->{variables}{form}{config_step1}{organization}{value} : ""; my $bcn_count = exists $anvil->data->{variables}{form}{config_step1}{bcn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{bcn_count}{value} : 1; my $sn_count = exists $anvil->data->{variables}{form}{config_step1}{sn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{sn_count}{value} : 0; my $ifn_count = exists $anvil->data->{variables}{form}{config_step1}{ifn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{ifn_count}{value} : 1; my $new_host_name = exists $anvil->data->{variables}{form}{config_step2}{host_name}{value} ? $anvil->data->{variables}{form}{config_step2}{host_name}{value} : ""; my $type = $anvil->Get->host_type(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { prefix => $prefix, sequence => $sequence, domain => $domain, organization => $organization, bcn_count => $bcn_count, sn_count => $sn_count, ifn_count => $ifn_count, new_host_name => $new_host_name, type => $type, }}); # If we're configuring a dashboard and no host name was given, generate it. if (($type eq "striker") && (not $new_host_name)) { $new_host_name = $prefix."-striker".sprintf("%02d", $sequence).".".$domain; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 10, message => "message_0016,!!host_name!$new_host_name!!", job_uuid => $anvil->data->{job}{uuid}, }); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "message_0016", variables => { host_name => $new_host_name }}); } else { # Failed $anvil->Job->update_progress({ progress => 0, message => "message_0017,!!host_name!$new_host_name!!,!!bad_host_name!$host_name!!", job_uuid => $anvil->data->{job}{uuid}, }); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "message_0017", variables => { host_name => $new_host_name, bad_host_name => $host_name, }}); $anvil->nice_exit({exit_code => 4}); } } # Read the local network manager data. $anvil->Network->read_nmcli({debug => 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 = defined $anvil->data->{variables}{form}{config_step2}{dns}{value} ? [split/,/, $anvil->data->{variables}{form}{config_step2}{dns}{value}] : []; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { dns => $dns }}); for (my $i = 0; $i < @{$dns}; $i++) { $dns->[$i] = $anvil->Words->clean_spaces({ string => $dns->[$i] }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "dns->[$i]" => $dns->[$i] }}); } my $gateway = defined $anvil->data->{variables}{form}{config_step2}{gateway}{value} ? $anvil->data->{variables}{form}{config_step2}{gateway}{value} : ""; my $gateway_interface = defined $anvil->data->{variables}{form}{config_step2}{gateway_interface}{value} ? $anvil->data->{variables}{form}{config_step2}{gateway_interface}{value} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { gateway => $gateway, gateway_interface => $gateway_interface, }}); # If there is no default gateway device, and one of the interfaces are set to DHCP, use it. if (not $gateway_interface) { # IFN first, BCN second, SN last foreach my $network_type ("ifn", "bcn", "sn") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }}); my $count = 0; if ($network_type eq "bcn") { $count = $bcn_count; } elsif ($network_type eq "sn") { $count = $sn_count; } elsif ($network_type eq "ifn") { $count = $ifn_count; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); next if not $count; foreach my $network_count (1..$count) { my $ip_key = $network_type.$network_count."_ip"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ip_key => $ip_key }}); if ((exists $anvil->data->{variables}{form}{config_step2}{$ip_key}{value}) && ($anvil->data->{variables}{form}{config_step2}{$ip_key}{value} eq "dhcp")) { $gateway_interface = $network_type.$network_count; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { gateway_interface => $gateway_interface }}); last; } } } } ### 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, }}); 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! my $variables = { mac_address => $variable_value, iface1 => $macs->{$variable_value}, iface2 => $device, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "error_0376", variables => $variables}); $anvil->Job->update_progress({ progress => 100, message => "error_0376", variables => $variables, job_status => "failed", }); $anvil->nice_exit({exit_code => 8}); } } } # We'll set this to '1' if we reconfigure the network $anvil->data->{sys}{reboot} = 0; # This will be set to '1' if we make a change. my $changes = 0; my $new_interfaces = []; foreach my $network_type ("bcn", "sn", "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; } $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->{variables}{form}{config_step2}{$link1_key}; next if not $anvil->data->{variables}{form}{config_step2}{$link1_key}{value}; my $link2_key = $this_network."_link2_mac_to_set"; my $subnet_mask_key = $this_network."_subnet_mask"; my $ip_key = $this_network."_ip"; my $bridge_key = $this_network."_create_bridge"; my $link1_mac = $anvil->data->{variables}{form}{config_step2}{$link1_key}{value}; my $is_gateway = $this_network eq $gateway_interface ? 1 : 0; my $link2_mac = defined $anvil->data->{variables}{form}{config_step2}{$link2_key}{value} ? $anvil->data->{variables}{form}{config_step2}{$link2_key}{value} : ""; my $old_link1_iface = $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} ? $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface} : ""; my $old_link2_iface = defined $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface} ? $anvil->data->{network}{$local_host}{mac_address}{$link2_mac}{interface} : ""; my $bridge = defined $anvil->data->{variables}{form}{config_step2}{$bridge_key}{value} ? $anvil->data->{variables}{form}{config_step2}{$bridge_key}{value} : 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ip_key => $ip_key, is_gateway => $is_gateway, link1_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->{variables}{form}{config_step2}{$ip_key}{value}) { $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 => { "variables::form::config_step2::${ip_key}::value" => $anvil->data->{variables}{form}{config_step2}{$ip_key}{value} }}); } if (($anvil->data->{variables}{form}{config_step2}{$ip_key}{value}) && ($anvil->data->{variables}{form}{config_step2}{$ip_key}{value} ne "dhcp") && (not $anvil->Validate->ipv4({ip => $anvil->data->{variables}{form}{config_step2}{$ip_key}{value}}))) { # Something was set, but it isn't valid. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0148", variables => { network => $this_network, ip => $anvil->data->{variables}{form}{config_step2}{$ip_key}{value}, }}); next; } # The IP could be 'dhcp', we'll handle that in a bit. my $ip_address = $anvil->data->{variables}{form}{config_step2}{$ip_key}{value}; my $subnet_mask = defined $anvil->data->{variables}{form}{config_step2}{$subnet_mask_key}{value} ? $anvil->data->{variables}{form}{config_step2}{$subnet_mask_key}{value} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ip_address => $ip_address, subnet_mask => $subnet_mask, }}); # Are we building bonded interfaces? if ($anvil->Validate->mac({mac => $link2_mac})) { # Yup! my $say_network = ""; my $say_interface = ""; my $interface_prefix = ""; my $say_defroute = $is_gateway ? "yes" : "no"; if ($network_type eq "bcn") { $say_network = "Back-Channel Network ".$network_count; $say_interface = "bcn".$network_count; $interface_prefix = "BCN"; } elsif ($network_type eq "sn") { $say_network = "Storage Network ".$network_count; $say_interface = "sn".$network_count; $interface_prefix = "SN"; } elsif ($network_type eq "ifn") { $say_network = "Internet-Facing Network ".$network_count; $say_interface = "ifn".$network_count; $interface_prefix = "IFN"; } # Gather variables my $cidr = $ip_address eq "dhcp" ? "" : $anvil->Convert->cidr({subnet_mask => $subnet_mask}); my $bridge_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Bridge_1"; my $bond_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Bond_1"; my $new_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_1"; my $new_link2_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_2"; my $old_link1_file = $new_link1_file; my $old_link2_file = $new_link2_file; my $new_bridge_iface = $say_interface."_bridge1"; my $new_bond_iface = $say_interface."_bond1"; my $new_link1_iface = $say_interface."_link1"; my $new_link2_iface = $say_interface."_link2"; my $boot_proto = $ip_address eq "dhcp" ? "dhcp" : "none"; my $link1_uuid = get_uuid_from_interface_file($anvil, $old_link1_file); my $link2_uuid = get_uuid_from_interface_file($anvil, $old_link2_file); if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface})) { # 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->{variables}{form}{config_step2}{$link1_key}{value}) && ($anvil->Validate->mac({mac => $anvil->data->{variables}{form}{config_step2}{$link1_key}{value}}))) { ### NOTE: This only applies when configuring Striker dashboards. They can't ### be 'dhcp', either, so no checks are made for those cases. Likewise, ### bridges are not used. # Single interface, set it up my $link1_mac = $anvil->data->{variables}{form}{config_step2}{$link1_key}{value}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link1_mac => $link1_mac }}); my $say_network = ""; my $say_interface = ""; my $interface_prefix = ""; if ($network_type eq "bcn") { $say_network = "Back-Channel Network ".$network_count; $say_interface = "bcn".$network_count; $interface_prefix = "BCN"; } elsif ($network_type eq "sn") { $say_network = "Storage Network ".$network_count; $say_interface = "sn".$network_count; $interface_prefix = "SN"; } elsif ($network_type eq "ifn") { $say_network = "Internet-Facing Network ".$network_count; $say_interface = "ifn".$network_count; $interface_prefix = "IFN"; } my $say_defroute = $is_gateway ? "yes" : "no"; my $cidr = $anvil->Convert->cidr({subnet_mask => $subnet_mask}); my $new_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_1"; my $old_link1_file = $new_link1_file; my $new_link1_iface = $say_interface."_link1"; if ((exists $anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}) && ($anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface})) { $old_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$anvil->data->{network}{$local_host}{mac_address}{$link1_mac}{interface}; 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; } } } # 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 => 50, job_uuid => $anvil->data->{job}{uuid}, }); # If any virtio bridges exist, remove it/them. my $start = 0; my ($bridges, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{virsh}." net-list"}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridges => $bridges, return_code => $return_code }}); if ($return_code) { ### NOTE: We're doing a bunch of deletes, so to be safe we statically define the directory ### here. # Libvirtd isn't running, check the directory directly. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0478"}); my $directory = "/etc/libvirt/qemu/networks/autostart"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { directory => $directory }}); if (-d $directory) { # Delete all files. local(*DIRECTORY); opendir(DIRECTORY, $directory); while(my $file = readdir(DIRECTORY)) { next if $file eq "."; next if $file eq ".."; my $full_path = $directory."/".$file; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file, full_path => $full_path, }}); if (-l $full_path) { # It's a symlink, remove it. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0479", variables => { 'symlink' => $full_path }}); unlink $full_path; if (-l $full_path) { # It didn't work... $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 0, priority => "err", key => "error_0132", variables => { 'symlink' => $full_path }});; } } } closedir(DIRECTORY); } } else { foreach my $line (split/\n/, $bridges) { $line = $anvil->Words->clean_spaces({string => $line}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /^----------/) { $start = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { start => $start }}); next; } next if not $start; my $bridge = ($line =~ /(.*?)\s/)[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridge => $bridge }}); $anvil->data->{virsh}{bridge}{$bridge} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virsh::bridge::$bridge" => $anvil->data->{virsh}{bridge}{$bridge} }}); } foreach my $bridge (sort {$a cmp $b} keys %{$anvil->data->{virsh}{bridge}}) { # Destroy (stop) it. my ($destroy, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." net-destroy ".$bridge}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { destroy => $destroy, return_code => $return_code }}); # Disable it from auto-start. (my $disable, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." net-autostart ".$bridge." --disable"}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disable => $disable, return_code => $return_code }}); # Undefine (delete) (my $undefine, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." net-undefine ".$bridge}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { undefine => $undefine, return_code => $return_code }}); } } $anvil->Job->update_progress({ progress => 75, job_uuid => $anvil->data->{job}{uuid}, }); return(0); } # This renames a network interface sub rename_interface { my ($anvil, $old_link_name, $new_link_name) = @_; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0465", variables => { old_interface => $old_link_name, new_interface => $new_link_name, }}); # Take the old name down. my $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, }}); } # This will store the variables from the database $anvil->data->{variables} = {}; # If we're still alive, pick up the details. my $query = " SELECT variable_name, variable_value FROM variables WHERE variable_name LIKE 'form::config_step%' AND variable_source_table = 'hosts' AND variable_source_uuid = ".$anvil->Database->quote($anvil->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); } # Clear previous data $anvil->Job->update_progress({ progress => 0, message => "clear", job_uuid => $anvil->data->{job}{uuid}, }); # Record that we've picked up this job. $anvil->Job->update_progress({ progress => 1, message => "message_0015", job_uuid => $anvil->data->{job}{uuid}, }); # If we're in a cluster, abort. if (-e $anvil->data->{path}{exe}{pcs}) { # To make logs more sensible, we'll call 'problem' as 'out_of_cluster'. 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 }}); 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}, }); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0250"}); $anvil->nice_exit({exit_code => 7}); } } return(0); }