From 6a0c9f27dfcf1e82e0abe69122f0e962e057893a Mon Sep 17 00:00:00 2001 From: Madison Kelly Date: Sun, 9 Jun 2024 21:49:22 -0400 Subject: [PATCH] Updated anvil-join-anvil to auto-create the anvil-configure-host job * This will, when all NICs can be accounted for, allow the reconfiguring of the network from stored database data after a node has been reinstalled during an 'anvil-join-anvil --rejoin' run. Signed-off-by: Madison Kelly --- tools/anvil-join-anvil | 201 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 196 insertions(+), 5 deletions(-) diff --git a/tools/anvil-join-anvil b/tools/anvil-join-anvil index a0f1e6e7..574d33a6 100755 --- a/tools/anvil-join-anvil +++ b/tools/anvil-join-anvil @@ -204,8 +204,10 @@ sub process_rejoin } # Does this host need to be reconfigured? If so, register a job, if one doesn't exist yet, and exit. - my $configured = check_if_configured({debug => 2, thorough => 1}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { configured => $configured }}); + my $configured = $anvil->System->check_if_configured({ + debug => 2, + thorough => 1, + }); if ($configured) { print "The host appears to already be configured, good.\n"; @@ -243,6 +245,7 @@ sub process_rejoin $anvil->nice_exit({exit_code => 1}); } } + die; if ($manifest) { @@ -267,7 +270,7 @@ sub process_rejoin } else { - print "[ Error ] - The manifest: [".$manifest."] was not found in the database.\n"; < + print "[ Error ] - The manifest: [".$manifest."] was not found in the database.\n"; print "[ Error ] - Try again without this switch to see the available manifests.\n"; $anvil->nice_exit({exit_code => 1}); } @@ -446,9 +449,197 @@ sub process_rejoin sub create_anvil_configure_host_job { my ($anvil, $host_uuid) = @_; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }}); + + # This is just to make the job_data cleaner. + if (exists $anvil->data->{job_vars}) + { + delete $anvil->data->{job_vars}; + } + if (exists $anvil->data->{iface_map}) + { + delete $anvil->data->{iface_map}; + } + + # Load the network interface data to validate/update the MACs to interface names. If the subnode was + # rebuilt, the device name could have reverted. So we walk through the history to see if the same MAC + # used to have a name we recognize. + my $query = " +SELECT + network_interface_uuid, + network_interface_mac_address, + network_interface_name, + network_interface_device, + round(extract(epoch from modified_date)) +FROM + history.network_interfaces +WHERE + network_interface_operational != 'DELETED' +AND + network_interface_host_uuid = ".$anvil->Database->quote($host_uuid)." +ORDER BY + modified_date DESC +;"; + $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 $network_interface_uuid = $row->[0]; + my $mac_address = $row->[1]; + my $name = $row->[2]; + my $device = $row->[3]; + my $modified_date_unix = $row->[4]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + "s1:network_interface_uuid" => $network_interface_uuid, + "s2:mac_address" => $mac_address, + "s3:name" => $name, + "s4:device" => $device, + "s5:modified_date_unix" => $modified_date_unix, + }}); + + $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{mac_address} = $mac_address; + $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{name} = $name; + $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{device} = $device; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "s1:iface_map::${network_interface_uuid}::timestamp::${modified_date_unix}::mac_address" => $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{mac_address}, + "s2:iface_map::${network_interface_uuid}::timestamp::${modified_date_unix}::name" => $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{name}, + "s3:iface_map::${network_interface_uuid}::timestamp::${modified_date_unix}::device" => $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{device}, + }}); + } + + # Load the variables, if not already done so. + if (not exists $anvil->data->{variables}{source_table}{hosts}{source_uuid}{$host_uuid}) + { + my $short_host_name = $anvil->Get->short_host_name(); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { short_host_name => $short_host_name }}); + + $anvil->Network->get_ips({debug => 3, target => $short_host_name}); + $anvil->Network->collect_data({debug => 3}); + $anvil->Database->get_variables({debug => 2}); + } + + # The variables were already read in, so it's easy to recreate the job. + my $form_data_found = 0; + foreach my $variable_uuid (sort {$a cmp $b} keys %{$anvil->data->{variables}{source_table}{hosts}{source_uuid}{$host_uuid}{variable_uuid}}) + { + my $variable_name = $anvil->data->{variables}{source_table}{hosts}{source_uuid}{$host_uuid}{variable_uuid}{$variable_uuid}{variable_name}; + my $variable_value = $anvil->data->{variables}{source_table}{hosts}{source_uuid}{$host_uuid}{variable_uuid}{$variable_uuid}{variable_value}; + $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, + }}); + next if $variable_name !~ /^form::config_step\d::/; + + if (not $form_data_found) + { + $form_data_found = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { form_data_found => $form_data_found }}); + } + + # If the config value is the MAC address, check to see if there was a change since the + # original variables were recorded. + if ($variable_name =~ /form::config_step2::(.*?)_mac_to_set::value/) + { + my $interface = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }}); + + my $iface_found = ""; + my $mac_found = ""; + foreach my $network_interface_uuid (sort {$a cmp $b} keys %{$anvil->data->{iface_map}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_interface_uuid => $network_interface_uuid }}); + foreach my $modified_date_unix (sort {$b <=> $a} keys %{$anvil->data->{iface_map}{$network_interface_uuid}{timestamp}}) + { + my $mac_address = $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{mac_address}; + my $name = $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{name}; + my $device = $anvil->data->{iface_map}{$network_interface_uuid}{timestamp}{$modified_date_unix}{device}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + #"s1:modified_date_unix" => $modified_date_unix, + "s2:mac_address" => $mac_address, + "s3:name" => $name, + "s4:device" => $device, + }}); + + if ((not $iface_found) && ($device eq $interface)) + { + $iface_found = $mac_address; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { iface_found => $iface_found }}); + } + if ((not $iface_found) && ($name eq $interface)) + { + $iface_found = $mac_address; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { iface_found => $iface_found }}); + } + if ((not $mac_found) && ($mac_found eq $variable_value)) + { + $mac_found = $interface; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_found => $mac_found }}); + } + } + } + + if ((not $iface_found) && (not $mac_found)) + { + # Abort, we can't find this interface by name or MAC address. + print "[ Error ] - The interface: [".$interface."] which was originally assigned to the device with the MAC\n"; + print "[ Error ] address: [".$variable_value."] could not be found. We checked for the interface name in the\n"; + print "[ Error ] 'network_interfaces' historical schema, in case the interface was moved to a new NIC in the\n"; + print "[ Error ] past. You will need to use the Striker UI (Striker logo -> Anvil -> Prepare Network) to \n"; + print "[ Error ] initialize the network for this node before it can rejoin the cluster.\n"; + return(""); + } + # If I found the mac address, we're done. If not, but we found the interface, update + # the variable_value to the new MAC address. + if (not $iface_found) + { + $variable_value = $mac_found; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable_value => $variable_value }}); + } + } + + $anvil->data->{job_vars}{$variable_name} = $variable_value; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "job_vars::${variable_name}" => $anvil->data->{job_vars}{$variable_name}, + }}); + } + + if (not $form_data_found) + { + print "[ Error ] - It appears that the data used to configure this node is no longer stored in the database.\n"; + print "[ Error ] You will need to use the Striker UI (Striker logo -> Anvil -> Prepare Network) to initialize\n"; + print "[ Error ] the network for this node before it can rejoin the cluster.\n"; + return(""); + } - my $job_uuid = ""; + my $job_lines = 0; + my $job_data = ""; + foreach my $variable_name (sort {$a cmp $b} keys %{$anvil->data->{job_vars}}) + { + $job_data .= $variable_name."=".$anvil->data->{job_vars}{$variable_name}."\n"; + $job_lines++; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + "s1:job_lines" => $job_lines, + "s2:job_data" => $job_data, + }}); + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_data => $job_data }}); + my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ + job_command => $anvil->data->{path}{exe}{'anvil-configure-host'}.$anvil->Log->switches, + job_data => $job_data, + job_name => "configure::network", + job_title => "job_0001", + job_description => "job_0071", + job_progress => 0, + job_host_uuid => $host_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); return($job_uuid) }