#!/usr/bin/perl # # Exit codes; # 0 == OK # 1 == Host UUID not available yet. # # TODO: # * Make the BCN count a thing, remove Striker user and make it statically 'admin'. # use strict; use warnings; use Anvil::Tools; use Data::Dumper; use NetAddr::IP; use CGI::Carp "fatalsToBrowser"; # Turn off buffering $| = 1; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) { $running_directory =~ s/^\./$ENV{PWD}/; } my $anvil = Anvil::Tools->new(); $anvil->Log->level({set => 2}); ### NOTE: We'll print the headers only when we need to. If we print them here, it will block cookies being set. # Setup some variables. $anvil->data->{skin}{url} = $anvil->data->{path}{urls}{skins}."/".$anvil->Template->skin; $anvil->data->{form}{body} = ""; $anvil->data->{form}{error_massage} = ""; $anvil->data->{form}{ok_message} = ""; $anvil->data->{form}{back_link} = ""; $anvil->data->{form}{refresh_link} = ""; # Read in any CGI variables, if needed. $anvil->Get->cgi({debug => 2}); # If we're being asked to get a file, do so now. if ($anvil->data->{cgi}{upload_file}{file_handle}) { # Save and exit. We've got ajax handling the UI so this invocation only saves the file. handle_upload($anvil); $anvil->nice_exit({exit_code => 0}); } # If the system hasn't initialized, there may be no host.uuid, and we'll need a better error to show the # user. if (not -e $anvil->data->{path}{data}{host_uuid}) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0002"}) }}); print_and_exit($anvil); } $anvil->Database->connect(); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); if (not $anvil->data->{sys}{database}{connections}) { # No databases, exit. print $anvil->Template->get({file => "shared.html", name => "http_headers"})."\n"; print $anvil->Template->get({file => "main.html", name => "header", variables => { language => $anvil->Words->language }}); print $anvil->Words->string({key => "error_0003"}); $anvil->nice_exit({exit_code => 2}); } # If any jobs are pending/running, show the "unavailable" option. my $configured = $anvil->System->check_if_configured; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { configured => $configured }}); ### TODO: Something is causing fairly frequent resyncs, which results in Striker going in and out of ### maintenance mode. my $available = $configured ? check_availability($anvil) : 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {available => $available }}); if (not $configured) { # If there is no user account yet, then the system is new and needs to be reconfigured. configure_striker($anvil); } elsif (not $available) { # Set the body to 'say::maintenance'. $anvil->data->{form}{body} = $anvil->data->{say}{maintenance}; } else { # Normal operation process_task($anvil); } # Build the page to display my $refresh_button = ""; if ($anvil->data->{form}{refresh_link}) { $refresh_button = $anvil->Template->get({file => "main.html", name => "refresh_button_on", variables => { url => $anvil->data->{form}{refresh_link} }}); } else { $refresh_button = $anvil->Template->get({file => "main.html", name => "refresh_button_off"}); } print_and_exit($anvil); ############################################################################################################# # Functions # ############################################################################################################# # This handles uploading (downloading by our perspective) sub handle_upload { my ($anvil) = @_; # Get the file handle. my $cgi_file_handle = $anvil->data->{cgi}{upload_file}{file_handle}; my $file_name = $anvil->data->{cgi}{upload_file}{file_name}; my $mime_type = $anvil->data->{cgi}{upload_file}{mime_type}; my $output_file = $anvil->data->{path}{directories}{shared}{incoming}."/".$file_name; $output_file =~ s/\/\//\//g; # TODO: Make sure characters like spaces and whatnot don't need to be escaped. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cgi_file_handle => $cgi_file_handle, file_name => $file_name, mime_type => $mime_type, output_file => $output_file, }}); open (my $file_handle, ">", $output_file) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0015", variables => { shell_call => $output_file, error => $! }}); binmode $file_handle; while(<$file_handle>) { print $cgi_file_handle $_; } close $file_handle; if (not -f $output_file) { # Something went wrong $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0257", variables => { file => $output_file }}); } else { # Looks like we got it. my $file_size = -s $output_file; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_size => $file_size }}); if (not $file_size) { # Looks like we didn't actually get the file... $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0258", variables => { file => $output_file }}); unlink $output_file; } else { my $say_size_in_bytes = $anvil->Convert->add_commas({number => $file_size}); my $say_human_size = $anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0256", variables => { file => $output_file, size_in_bytes => $say_size_in_bytes, human_readable_size => $say_human_size, }}); } } return(0); } sub print_and_exit { my ($anvil) = @_; # Time for the header my $header = $anvil->Template->get({file => "main.html", name => "header", variables => { language => $anvil->Words->language }}); my $back_button = ""; if ($anvil->data->{form}{back_link}) { my $url = $THIS_FILE; if ($anvil->data->{form}{back_link} ne "?") { # Turn on the back button, $url = $anvil->data->{form}{back_link}; } $back_button = $anvil->Template->get({file => "main.html", name => "back_button_on", variables => { url => $url }}); } else { # Back is disabled. $back_button = $anvil->Template->get({file => "main.html", name => "back_button_off"}); } my $left_buttons = $anvil->Template->get({file => "main.html", name => "button_bar_left", variables => { back_button => $back_button, refresh_button => $refresh_button, }}); # The jobs button is "on" when the user is logged in and there is one or more jobs on this system # under 100% complete. my $say_jobs_button = $anvil->Template->get({file => "main.html", name => "jobs_button_off"}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_jobs_button => $say_jobs_button }}); if (($anvil->data->{sys}{users}{user_name}) && ($anvil->Job->running({debug => 3}))) { $say_jobs_button = $anvil->Template->get({file => "main.html", name => "jobs_button_on"}); } my $right_buttons = $anvil->Template->get({file => "main.html", name => "button_bar_right", variables => { anvil_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "anvil_button_on"}) : $anvil->Template->get({file => "main.html", name => "anvil_button_off"}), configure_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "configure_button_on"}) : $anvil->Template->get({file => "main.html", name => "configure_button_off"}), files_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "files_button_on"}) : $anvil->Template->get({file => "main.html", name => "files_button_off"}), jobs_button => $say_jobs_button, striker_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "striker_button_on"}) : $anvil->Template->get({file => "main.html", name => "striker_button_off"}), user_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "user_button_on"}) : $anvil->Template->get({file => "main.html", name => "user_button_off"}), }}); my $footer = $anvil->Template->get({file => "main.html", name => "footer", variables => { user => $anvil->data->{sys}{users}{user_name} ? "#!string!message_0034!#" : " ", }}); # Display the page. my $say_center_top_bar = " "; if ($anvil->data->{form}{error_massage}) { $say_center_top_bar = $anvil->data->{form}{error_massage}; } elsif ($anvil->data->{form}{ok_message}) { $say_center_top_bar = $anvil->data->{form}{ok_message}; } my $body = $anvil->Template->get({file => "main.html", name => "master", variables => { header => $header, skin_url => $anvil->data->{path}{urls}{skins}."/".$anvil->Template->skin, center_top_bar => $say_center_top_bar, right_top_bar => $right_buttons, left_top_bar => $left_buttons, center_body => $anvil->data->{form}{body}, left_bottom_bar => " ", center_bottom_bar => " ", right_bottom_bar => " ", footer => $footer, }}); print $anvil->Template->get({file => "shared.html", name => "http_headers"})."\n"; print "$body"; $anvil->nice_exit({exit_code => 0}); return(0); } # This handles all the daily tasks of Striker. sub process_task { my ($anvil) = @_; # Is the user trying to log in? my $logged_in = 0; $anvil->data->{cgi}{login}{value} = "" if not defined $anvil->data->{cgi}{login}{value}; $anvil->data->{cgi}{logout}{value} = "" if not defined $anvil->data->{cgi}{logout}{value}; $anvil->data->{cgi}{save}{value} = "" if not defined $anvil->data->{cgi}{save}{value}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "cgi::login::value" => $anvil->data->{cgi}{login}{value}, "cgi::logout::value" => $anvil->data->{cgi}{logout}{value}, "cgi::save::value" => $anvil->data->{cgi}{save}{value}, }}); if ($anvil->data->{cgi}{login}{value}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::login::value" => $anvil->data->{cgi}{login}{value} }}); # Woot! my $failed = $anvil->Account->login({debug => 2}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }}); if (not $failed) { $logged_in = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { logged_in => $logged_in }}); } } elsif ($anvil->data->{cgi}{logout}{value}) { # Bye now! $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::logout::value" => $anvil->data->{cgi}{logout}{value} }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0215"}); $anvil->Account->logout({debug => 2}); } else { # Is the user logged in? # 0 - The cookies were read, the account was validated and the user's details were loaded. # 1 - No cookie was found or read. The user needs to log in # 2 - There was a problem reading the user's UUID (it wasn't found in the database), so the # cookies were deleted (via C<< Account->logout() >>. The user needs to log back in. # 3 - There user's hash is invalid, it is probably expired. The user has been logged out and # needs to log back in. my $cookie_problem = $anvil->Account->read_cookies(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { cookie_problem => $cookie_problem }}); if (not $cookie_problem) { $logged_in = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { logged_in => $logged_in }}); } } # Show the login screen, if the user isn't logged in. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { logged_in => $logged_in }}); if (not $logged_in) { $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "striker-login", variables => { user => $anvil->data->{cgi}{username}{value}, password => "", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }}); return(0); } # If we're here, the user is logged in! if ($anvil->data->{cgi}{striker}{value}) { process_striker_menu($anvil); } elsif ($anvil->data->{cgi}{anvil}{value}) { process_anvil_menu($anvil); } elsif ($anvil->data->{cgi}{files}{value}) { process_file_menu($anvil); } elsif ($anvil->data->{cgi}{jobs}{value}) { process_jobs_menu($anvil); } else { # Load the main page. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "striker-welcome"}); } # $anvil->data->{sys}{users}{user_name} = $user_name; # $anvil->data->{sys}{users}{user_uuid} = $user_uuid; # $anvil->data->{sys}{users}{user_password_hash} = $user_password_hash, # $anvil->data->{sys}{users}{user_salt} = $user_salt, # $anvil->data->{sys}{users}{user_algorithm} = $user_algorithm, # $anvil->data->{sys}{users}{user_hash_count} = $user_hash_count, # $anvil->data->{sys}{users}{user_language} = $user_language, # $anvil->data->{sys}{users}{user_is_admin} = $user_is_admin, # $anvil->data->{sys}{users}{user_is_experienced} = $user_is_experienced, # $anvil->data->{sys}{users}{user_is_trusted} = $user_is_trusted, return(0); } # This handles the "Striker" menu items. sub process_striker_menu { my ($anvil) = @_; $anvil->data->{form}{back_link} = "?striker=true"; $anvil->data->{cgi}{task}{value} = "" if not defined $anvil->data->{cgi}{task}{value}; $anvil->data->{cgi}{action}{value} = "" if not defined $anvil->data->{cgi}{action}{value}; $anvil->data->{cgi}{save}{value} = "" if not defined $anvil->data->{cgi}{save}{value}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::back_link" => $anvil->data->{form}{back_link}, "cgi::task::value" => $anvil->data->{cgi}{task}{value}, "cgi::action::value" => $anvil->data->{cgi}{action}{value}, "cgi::save::value" => $anvil->data->{cgi}{save}{value}, }}); if ($anvil->data->{cgi}{task}{value} eq "sync") { process_sync_page($anvil); } elsif ($anvil->data->{cgi}{task}{value} eq "reconfig") { process_reconfig_page($anvil); } elsif ($anvil->data->{cgi}{task}{value} eq "update") { process_update($anvil); } elsif ($anvil->data->{cgi}{task}{value} eq "install-target") { process_install_target($anvil); } elsif ($anvil->data->{cgi}{task}{value} eq "reboot") { process_power($anvil, "reboot"); } elsif ($anvil->data->{cgi}{task}{value} eq "poweroff") { process_power($anvil, "poweroff"); } else { # What we show for the reboot icon and text depends on if a reboot is pending. my $reboot_needed = $anvil->System->reboot_needed(); my $reboot_icon = $reboot_needed ? "reboot_needed_icon.png" : "reboot_icon.png"; my $reboot_message = $reboot_needed ? "#!string!striker_0093!#" : "#!string!striker_0092!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { reboot_needed => $reboot_needed, reboot_icon => $reboot_icon, reboot_message => $reboot_message, }}); # What we show for the install target icon and text depends on if it is enabled or not. my $install_target_title = "#!string!striker_0109!#"; my $install_target_icon = "install_target_disabled.png"; my $install_target_subtask = "unavailable"; my ($install_manifest_status, $variable_uuid, $modified_date) = $anvil->Database->read_variable({ variable_name => "install-target::enabled", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { install_manifest_status => $install_manifest_status, variable_uuid => $variable_uuid, modified_date => $modified_date, }}); if ($install_manifest_status eq "enabled") { # Offer the button to disable it. $install_target_title = "#!string!striker_0108!#"; $install_target_icon = "install_target_enabled.png"; $install_target_subtask = "disable"; } elsif ($install_manifest_status eq "disabled") { # Offer the button to enable it. $install_target_title = "#!string!striker_0107!#"; $install_target_subtask = "enable"; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { install_target_title => $install_target_title, install_target_icon => $install_target_icon, install_target_subtask => $install_target_subtask, }}); # The 'back' goes home $anvil->data->{form}{back_link} = "?"; $anvil->data->{form}{refresh_link} = "?striker=true"; $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "striker-setup", variables => { reboot_icon => $reboot_icon, reboot_message => $reboot_message, install_target_icon => $install_target_icon, install_target_title => $install_target_title, install_target_subtask => $install_target_subtask, }}); } return(0); } # This shows the user any running jobs. sub process_jobs_menu { my ($anvil) = @_; $anvil->data->{form}{refresh_link} = "?jobs=true"; $anvil->data->{form}{back_link} = "?striker=true"; $anvil->data->{cgi}{task}{value} = "" if not defined $anvil->data->{cgi}{task}{value}; $anvil->data->{cgi}{action}{value} = "" if not defined $anvil->data->{cgi}{action}{value}; $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "jobs", variables => { title_id => "", message_id => "", title => "#!string!striker_0096!#", description => "#!string!striker_0115!#", job_list => $anvil->Job->html_list({debug => 2, ended_within => 300}), }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'say::maintenance' => $anvil->data->{say}{maintenance} }}); return(0); } # This handles files. sub process_file_menu { my ($anvil) = @_; $anvil->data->{form}{refresh_link} = "striker?files=true"; #$anvil->data->{form}{back_link} = "?striker=true"; $anvil->data->{cgi}{task}{value} = "" if not defined $anvil->data->{cgi}{task}{value}; $anvil->data->{cgi}{action}{value} = "" if not defined $anvil->data->{cgi}{action}{value}; if ($anvil->data->{cgi}{task}{value} eq "upload") { #process_upload_page($anvil); } else { # The 'back' goes home $anvil->data->{form}{back_link} = "?"; $anvil->data->{form}{body} = $anvil->Template->get({file => "files.html", name => "main-menu", variables => { }}); } return(0); } # This handles the "Anvil" menu items. sub process_anvil_menu { my ($anvil) = @_; $anvil->data->{form}{refresh_link} = "striker?anvil=true"; $anvil->data->{form}{back_link} = "?striker=true"; $anvil->data->{cgi}{task}{value} = "" if not defined $anvil->data->{cgi}{task}{value}; $anvil->data->{cgi}{action}{value} = "" if not defined $anvil->data->{cgi}{action}{value}; if ($anvil->data->{cgi}{task}{value} eq "prep-host") { process_prep_host_page($anvil); } elsif ($anvil->data->{cgi}{task}{value} eq "manifest") { #process_manifest_page($anvil); } else { # The 'back' goes home $anvil->data->{form}{back_link} = "?"; $anvil->data->{form}{body} = $anvil->Template->get({file => "anvil.html", name => "main-menu", variables => { }}); } return(0); } # This handles installing our repo, installing the appropriate 'anvil-X' rpm, running OS updates if selected # and configuring the network on a machine destined to be an Anvil! node or DR host. sub process_prep_host_page { my ($anvil) = @_; $anvil->data->{form}{refresh_link} = "?jobs=true&task=prep-host"; $anvil->data->{form}{back_link} = "?jobs=true"; $anvil->data->{cgi}{task}{value} = "" if not defined $anvil->data->{cgi}{task}{value}; $anvil->data->{cgi}{action}{value} = "" if not defined $anvil->data->{cgi}{action}{value}; $anvil->data->{form}{body} = $anvil->Template->get({file => "anvil.html", name => "jobs", variables => { title_id => "", message_id => "", title => "#!string!striker_0096!#", description => "#!string!striker_0115!#", job_list => $anvil->Job->html_list({debug => 2, ended_within => 300}), }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'say::maintenance' => $anvil->data->{say}{maintenance} }}); return(0); } # This enables or disables the Install Target feature sub process_install_target { my ($anvil) = @_; # NOTE: We don't ask for confirmation, it's a low risk task. # What are we doing? $anvil->data->{cgi}{subtask}{value} = "" if not defined $anvil->data->{cgi}{subtask}{value}; if (($anvil->data->{cgi}{subtask}{value} eq "enable") or ($anvil->data->{cgi}{subtask}{value} eq "disable")) { my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ file => $THIS_FILE, line => __LINE__, job_command => $anvil->data->{path}{exe}{'striker-manage-install-target'}." --".$anvil->data->{cgi}{subtask}{value}, job_data => "", job_name => "install-target::".$anvil->data->{cgi}{task}{value}, job_title => "job_0015", job_description => "job_0016", job_progress => 0, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); # Show the use that the job has been saved. my $message = "striker_0111"; if ($anvil->data->{cgi}{subtask}{value} eq "enable") { $message = "striker_0112"; } $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "job recorded", variables => { title_id => "", message_id => "", reload_url => "/cgi-bin/".$THIS_FILE."?striker=true", title => "#!string!striker_0044!#", description => "#!string!".$message."!#", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }}); } else { # Just ignore it, someone's mucking with the subask value. $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0009"}) }}); } return(0); } # This handles powering off or rebooting this machine sub process_power { my ($anvil, $task) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { task => $task }}); ### NOTE: This doesn't update Striker (the Alteeve) stack yet, just the base OK. $anvil->data->{cgi}{confirm}{value} = "" if not defined $anvil->data->{cgi}{confirm}{value}; if ($anvil->data->{cgi}{confirm}{value}) { # Record the job! my $job_command = $anvil->data->{path}{exe}{'anvil-manage-power'}." --reboot -y"; my $job_title = "job_0009"; my $job_description = "job_0006"; my $say_title = "#!string!job_0005!#"; my $say_description = "#!string!job_0006!#"; if ($task eq "poweroff") { $job_command = $anvil->data->{path}{exe}{'anvil-manage-power'}." --poweroff -y"; $job_title = "job_0010"; $job_description = "job_0008"; $say_title = "#!string!job_0007!#"; $say_description = "#!string!job_0008!#"; } my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ file => $THIS_FILE, line => __LINE__, job_command => $job_command, job_data => "", job_name => "reboot::system", job_title => $job_title, job_description => $job_description, job_progress => 0, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); # We don't need to store anything as hidden variables, we'll read it back from the database # later. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "job recorded", variables => { title_id => "", message_id => "", reload_url => "/cgi-bin/".$THIS_FILE, title => $say_title, description => $say_description, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }}); # Log the user out, just to be safe. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0215"}); $anvil->Account->logout({debug => 2}); } else { # Show the screen the confirm the addition. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "confirm-action", variables => { title => $task eq "poweroff" ? "#!string!job_0007!#" : "#!string!job_0005!#", message => $task eq "poweroff" ? "#!string!striker_0101!#" : "#!string!striker_0100!#", hidden_fields => "", }}); } return(0); } # This handles updating this Striker. sub process_update { my ($anvil) = @_; ### NOTE: This doesn't update Striker (the Alteeve) stack yet, just the base OK. $anvil->data->{cgi}{confirm}{value} = "" if not defined $anvil->data->{cgi}{confirm}{value}; if ($anvil->data->{cgi}{confirm}{value}) { # Record the job! my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ debug => 1, file => $THIS_FILE, line => __LINE__, job_command => $anvil->data->{path}{exe}{'anvil-update-system'}, job_data => "", job_name => "update::system", job_title => "job_0003", job_description => "job_0004", job_progress => 0, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); # We don't need to store anything as hidden variables, we'll read it back from the database # later. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "job recorded", variables => { title_id => "", message_id => "", reload_url => "/cgi-bin/".$THIS_FILE, title => "#!string!striker_0044!#", description => "#!string!striker_0088!#", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }}); # Set maintenance mode. $anvil->System->maintenance_mode({set => 1}); } else { # Show the screen the confirm the addition. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "confirm-action", variables => { title => "#!string!striker_0078!#", message => "#!string!striker_0086!#", hidden_fields => "", }}); } return(0); } # This handles reconfiguring the Striker system. sub process_reconfig_page { my ($anvil) = @_; # Later, this will let each piece be recondigured. For now, it simple reset the "configured flag so # the next load will trigger the initial config again. $anvil->data->{cgi}{confirm}{value} = "" if not defined $anvil->data->{cgi}{confirm}{value}; if ($anvil->data->{cgi}{confirm}{value}) { # Update the configured variable $anvil->Database->insert_or_update_variables({ variable_name => "system::configured", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", variable_value => 0, update_value_only => 1, }); # Done. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "reconfig-done"}); } else { # Show the screen the confirm the addition. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "confirm-reconfig"}); } return(0); } # This handles tasks related to setting up peer Strikers. sub process_sync_page { my ($anvil) = @_; # Setup some CGI values we might use. $anvil->data->{cgi}{new_peer_access}{value} = "" if not defined $anvil->data->{cgi}{new_peer_access}{value}; $anvil->data->{cgi}{new_peer_password}{value} = "" if not defined $anvil->data->{cgi}{new_peer_password}{value}; $anvil->data->{cgi}{save}{value} = "" if not defined $anvil->data->{cgi}{save}{value}; $anvil->data->{cgi}{confirm}{value} = "" if not defined $anvil->data->{cgi}{confirm}{value}; # This handles checkboxes if (defined $anvil->data->{cgi}{new_peer_ping}{value}) { $anvil->data->{cgi}{new_peer_ping}{value} = "" if $anvil->data->{cgi}{new_peer_ping}{value} ne "on"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::new_peer_ping::value" => $anvil->data->{cgi}{new_peer_ping}{value} }}); } else { $anvil->data->{cgi}{new_peer_ping}{value} = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::new_peer_ping::value" => $anvil->data->{cgi}{new_peer_ping}{value} }}); } if (defined $anvil->data->{cgi}{new_peer_bidirection}{value}) { $anvil->data->{cgi}{new_peer_bidirection}{value} = "" if $anvil->data->{cgi}{new_peer_bidirection}{value} ne "on"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::new_peer_bidirection::value" => $anvil->data->{cgi}{new_peer_bidirection}{value} }}); } else { $anvil->data->{cgi}{new_peer_bidirection}{value} = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::new_peer_bidirection::value" => $anvil->data->{cgi}{new_peer_bidirection}{value} }}); } # Are we deleting or adding a new peer? if ($anvil->data->{cgi}{'delete'}{value}) { delete_sync_peer($anvil); } elsif (($anvil->data->{cgi}{new_peer_access}{value}) && ($anvil->data->{cgi}{new_peer_password}{value} ne "")) { add_sync_peer($anvil); } # If we've got a body now, return. if ($anvil->data->{form}{body}) { # We're done return(0); } my $host_uuid = $anvil->Get->host_uuid; $anvil->System->get_ips(); # We'll want to show the user way to access the local machine. For that, we'll loop through our own IPs. my $inbound_table = ""; foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{sys}{network}{interface}}) { next if (($interface !~ /^bcn/) && ($interface !~ /^ifn/)); next if not $anvil->Validate->is_ipv4({ip => $anvil->data->{sys}{network}{interface}{$interface}{ip}}); next if not $anvil->Validate->is_subnet({subnet => $anvil->data->{sys}{network}{interface}{$interface}{subnet}}); my ($network_type, $network_number) = ($interface =~ /^(.*?)(\d+)_/); my $database_user = $anvil->data->{database}{$host_uuid}{user} ? $anvil->data->{database}{$host_uuid}{user} : $anvil->data->{sys}{database}{user}; my $database_port = $anvil->data->{database}{$host_uuid}{port}; my $network_key = $network_type eq "bcn" ? "striker_0018" : "striker_0022"; my $say_network = $anvil->Words->string({key => $network_key, variables => { number => $network_number }}); # The user 'admin' and the port 5432 are default, so only show them if they're non-standard. my $access_string = $database_user."\@".$anvil->data->{sys}{network}{interface}{$interface}{ip}.":".$database_port; if (($database_port eq "5432") && ($database_user eq "admin")) { $access_string = $anvil->data->{sys}{network}{interface}{$interface}{ip}; } elsif ($database_port eq "5432") { $access_string = $database_user."\@".$anvil->data->{sys}{network}{interface}{$interface}{ip}; } elsif ($database_user eq "admin") { $access_string = $anvil->data->{sys}{network}{interface}{$interface}{ip}.":".$database_port; } $inbound_table .= $anvil->Template->get({file => "striker.html", name => "striker-sync-inbound", variables => { access => $access_string, note => $say_network, }}); } # This needs to be loaded into a hash by target, the sorted. We'll build the table on sort. my $peer_table = ""; foreach my $uuid (keys %{$anvil->data->{database}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { uuid => $uuid }}); next if not $anvil->Validate->is_uuid({uuid => $uuid}); next if $uuid eq $host_uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { uuid => $uuid }}); my $host = $anvil->data->{database}{$uuid}{host} ? $anvil->data->{database}{$uuid}{host} : ""; # This should fail my $port = $anvil->data->{database}{$uuid}{port} ? $anvil->data->{database}{$uuid}{port} : 5432; my $name = $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : $anvil->data->{sys}{database}{user}; my $ping = $anvil->data->{database}{$uuid}{ping} ? $anvil->data->{database}{$uuid}{ping} : 1; my $password = $anvil->data->{database}{$uuid}{password} ? $anvil->data->{database}{$uuid}{password} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host => $host, port => $port, name => $name, user => $user, ping => $ping, password => $anvil->Log->secure ? $password : $anvil->Words->string({key => "log_0186"}), }}); # Store it by name. $anvil->data->{peers}{$host}{port} = $port; $anvil->data->{peers}{$host}{name} = $name; $anvil->data->{peers}{$host}{user} = $user; $anvil->data->{peers}{$host}{ping} = $ping; $anvil->data->{peers}{$host}{uuid} = $uuid; $anvil->data->{peers}{$host}{password} = $password; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "peers::${host}::port" => $anvil->data->{peers}{$host}{port}, "peers::${host}::name" => $anvil->data->{peers}{$host}{name}, "peers::${host}::ping" => $anvil->data->{peers}{$host}{ping}, "peers::${host}::uuid" => $anvil->data->{peers}{$host}{uuid}, "peers::${host}::password" => $anvil->Log->secure ? $anvil->data->{peers}{$host}{password} : $anvil->Words->string({key => "log_0186"}), }}); } # Now peers are sortable by host name. foreach my $host (sort {$a cmp $b} keys %{$anvil->data->{peers}}) { my $port = $anvil->data->{peers}{$host}{port}; my $name = $anvil->data->{peers}{$host}{name}; my $user = $anvil->data->{peers}{$host}{user}; my $ping = $anvil->data->{peers}{$host}{ping}; my $uuid = $anvil->data->{peers}{$host}{uuid}; my $password = $anvil->data->{peers}{$host}{password}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host => $host, port => $port, name => $name, user => $user, ping => $ping, uuid => $uuid, password => $anvil->Log->secure ? $password : $anvil->Words->string({key => "log_0186"}), }}); $anvil->data->{cgi}{new_peer_password}{value} = "" if not defined $anvil->data->{cgi}{new_peer_password}{value}; $peer_table .= $anvil->Template->get({file => "striker.html", name => "striker-sync-entry", variables => { uuid => $uuid, access => $port eq 5432 ? $user."\@".$host : $user."\@".$host.":".$port, password => $anvil->data->{cgi}{new_peer_password}{value}, say_ping => $ping ? "#!string!unit_0001!#" : "#!string!unit_0002!#", }}); } # Build the menu. $anvil->data->{form}{refresh_link} = "?striker=true&task=sync"; $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "striker-sync", variables => { inbound_table => $inbound_table, peer_table => $peer_table, new_peer_access => defined $anvil->data->{cgi}{new_peer_access}{value} ? $anvil->data->{cgi}{new_peer_access}{value} : "", new_peer_password => defined $anvil->data->{cgi}{new_peer_password}{value} ? $anvil->data->{cgi}{new_peer_password}{value} : "", }}); return(0); } # This deletes a sync peer. sub delete_sync_peer { my ($anvil) = @_; my $uuid = $anvil->data->{cgi}{'delete'}{value}; my $host_name = $anvil->Get->host_name({host_uuid => $uuid}); my $host = $anvil->data->{database}{$uuid}{host} ? $anvil->data->{database}{$uuid}{host} : ""; # This should fail my $name = $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : $anvil->data->{sys}{database}{user}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { uuid => $uuid, host_name => $host_name, host => $host, name => $name, user => $user, }}); # Is the delete confirmed? if ($anvil->data->{cgi}{confirm}{value}) { # OK, save the job! my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ debug => 3, file => $THIS_FILE, line => __LINE__, job_command => $anvil->data->{path}{exe}{'striker-manage-peers'}." --remove --host-uuid ".$uuid, job_data => "", job_name => "striker-peer::remove", job_title => "job_0013", job_description => "job_0014", job_progress => 0, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); # Show the use that the job has been saved. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "job recorded", variables => { title_id => "", message_id => "", reload_url => "/cgi-bin/".$THIS_FILE."?striker=true&task=sync", title => "#!string!striker_0044!#", description => "#!string!striker_0104!#", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }}); } else { # Show the screen the confirm the addition. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "confirm-action", variables => { title => "#!string!job_0013!#", message => $anvil->Words->string({key => "striker_0105", variables => { peer => $user."\@".$host_name }}), hidden_fields => "", }}); } return(0); } # This adds a new peer to anvil.conf. sub add_sync_peer { my ($anvil) = @_; # Break up the user, host and port. If anything goes wrong, we'll set an error and send it back. my $user = $anvil->data->{sys}{database}{user}; my $host = $anvil->data->{cgi}{new_peer_access}{value}; my $password = $anvil->data->{cgi}{new_peer_password}{value}; my $name = $anvil->data->{sys}{database}{name}; my $ping = $anvil->data->{cgi}{new_peer_ping}{value} eq "on" ? 1 : 0; my $port = 5432; my $ssh_tcp = 22; my $peer_uuid = ""; my $peer_host = ""; my $use_ip = ""; # This will contain the local IP to use for the peer to setup comms with us if ($host =~ /,ssh=(\d+)$/) { $ssh_tcp = $1; $host =~ s/,ssh=\d+$//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ssh_tcp => $ssh_tcp, host => $host, }}); } if ($host =~ /^(.*?)\@(.*?):(\d+)$/) { $user = $1; $host = $2; $port = $3; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host => $host, port => $port, user => $user, }}); } elsif ($host =~ /^(.*?)\@(.*?)$/) { $user = $1; $host = $2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host => $host, user => $user, }}); } elsif ($host =~ /^(.*?):(\d+)$/) { $host = $1; $port = $2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host => $host, port => $port, }}); } # Is the host a domain or IP? # If so, and 'bi-directional' is set, verify we can ssh into the peer. my $is_domain = $anvil->Validate->is_domain_name({name => $host}); my $is_ipv4 = $anvil->Validate->is_ipv4({ip => $host}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { is_domain => $is_domain, is_ipv4 => $is_ipv4, port => $port, }}); if (((not $is_domain) && (not $is_ipv4)) or ($port < 1) or ($port > 65536)) { # Bad host. $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0002"}) }}); } else { # Can we connect to the peer? my $shell_call = $anvil->data->{path}{exe}{dmidecode}." --string system-uuid"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); my ($output, $error) = $anvil->Remote->call({ password => $password, target => $ssh_tcp != 22 ? $host.":".$ssh_tcp : $host, shell_call => $shell_call, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, error => $error, }}); if ($error) { # No access $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0003"}) }}); } else { # We got the peer's UUID. Get the hostname as well. $peer_uuid = lc($output); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_uuid => $peer_uuid }}); if (not $anvil->Validate->is_uuid({uuid => $peer_uuid})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0005", variables => { uuid => $peer_uuid }}) }}); } else { my ($error, $output) = $anvil->Remote->call({ password => $password, target => $ssh_tcp != 22 ? $host.":".$ssh_tcp : $host, shell_call => $anvil->data->{path}{exe}{hostnamectl}." --static", }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, error => $error, }}); $peer_host = $output; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_host => $peer_host }}); # Lastly, if bi-directional is set, make sure we have a way to talk to it. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::new_peer_bidirection::value" => $anvil->data->{cgi}{new_peer_bidirection}{value} }}); if ($anvil->data->{cgi}{new_peer_bidirection}{value} eq "on") { # See which of our IPs match theirs. If the peer is a hostname, first $use_ip = $anvil->System->find_matching_ip({host => $host}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_ip => $use_ip }}); if ((not $use_ip) or ($use_ip eq "!!error!!")) { # Can't do bi-directional $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0008", variables => { host => $host }}) }}); } } } } } # Now, verify we can access the peer database. This will involve writting out a .pgpass file, then making a local system call. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "form::error_massage" => $anvil->data->{form}{error_massage} }}); if (not $anvil->data->{form}{error_massage}) { my $pgpass_file = "/tmp/.pgpass"; $password =~ s/:/\:/g; my $body = $host.":".$port.":".$name.":".$user.":".$password; $anvil->Storage->write_file({ file => $pgpass_file, body => $body, mode => "0600", secure => 1, overwrite => 1, }); # This will return '1' only, if it works. my $db_access = $anvil->System->call({shell_call => "PGPASSFILE=\"".$pgpass_file."\" ".$anvil->data->{path}{exe}{psql}." --host ".$host." --port ".$port." --dbname ".$name." --username ".$user." --no-password --tuples-only --no-align --command \"SELECT 1\""}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { db_access => $db_access }}); if ($db_access ne "1") { # Failed to connect. $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0004"}) }}); } # Delete the pgpass file. unlink $pgpass_file; } # If an error was set, clear the 'save' and 'confirm' values. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "form::error_massage" => $anvil->data->{form}{error_massage} }}); if ($anvil->data->{form}{error_massage}) { $anvil->data->{cgi}{save}{value} = ""; $anvil->data->{cgi}{confirm}{value} = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::save::value" => $anvil->data->{cgi}{save}{value}, "cgi::confirm::value" => $anvil->data->{cgi}{confirm}{value}, }}); } else { # Things look good. Is the save confirmed? if ($anvil->data->{cgi}{confirm}{value}) { # OK, save the job! my $job_command = $anvil->data->{path}{exe}{'striker-manage-peers'}." --add --host-uuid ".$peer_uuid." --host ".$host." --port ".$port." --ping ".$ping; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_command => $job_command, password => $anvil->Log->secure ? $password : $anvil->Words->string({key => "log_0186"}), }}); # The job data will always contain the password for the peer, but also contain the # command for the peer to add us if 'bidirectional' was selected. This has to be # stored separately and not added directly as a job, because the peer is likely not # in 'hosts' yet, and thus we can't reference it in 'job_host_uuid' at this point. my $job_data = "password=".$password; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { job_data => $job_data }}); # Are we adding ourselves to the peer? $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_ip => $use_ip }}); if ($use_ip) { # See which of our IPs match theirs. If the peer is a hostname, first my $host_uuid = $anvil->Get->host_uuid; my $sql_port = $anvil->data->{database}{$host_uuid}{port}; my $job_command = $anvil->data->{path}{exe}{'striker-manage-peers'}." --add --host-uuid ".$host_uuid." --host ".$use_ip." --port ".$sql_port." --ping ".$ping; $job_data .= "\npeer_job_command=".$job_command; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { job_command => $job_command }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { job_data => $job_data }}); } # Store the job my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ debug => 3, file => $THIS_FILE, line => __LINE__, job_command => $job_command, job_data => $job_data, job_name => "striker-peer::add", job_title => "job_0011", job_description => "job_0012", job_progress => 0, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); # We don't need to store anything as hidden variables, we'll read it back from the # database later. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "job recorded", variables => { title_id => "", message_id => "", reload_url => "/cgi-bin/".$THIS_FILE."?striker=true&task=sync", title => "#!string!striker_0044!#", description => $ping ? "#!string!striker_0103!#" : "#!string!striker_0102!#", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }}); } else { # Show the screen the confirm the addition. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "confirm-new-peer", variables => { access => $user."@".$host.":".$port, ping => $anvil->data->{cgi}{new_peer_ping}{value} ? "#!string!unit_0001!#" : "#!string!unit_0002!#", bidirectional => $anvil->data->{cgi}{new_peer_bidirection}{value} ? "#!string!unit_0001!#" : "#!string!unit_0002!#", }}); } } return(0); } # This shows the menus for configuring Striker. sub configure_striker { my ($anvil) = @_; # This will be true when the dashboard is unconfigured. if (not $anvil->data->{cgi}{step}{value}) { $anvil->data->{form}{body} = config_step1($anvil); } elsif ($anvil->data->{cgi}{step}{value} eq "step1") { # Sanity check step1. my $sane = sanity_check_step1($anvil); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { sane => $sane }}); if ($sane) { # Step 1 was sane, show step 2. $anvil->data->{form}{body} = config_step2($anvil); } else { # No good $anvil->data->{form}{body} = config_step1($anvil); } } elsif ($anvil->data->{cgi}{step}{value} eq "step2") { # Sanity check step1. my $sane = sanity_check_step2($anvil); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { sane => $sane }}); if ($sane) { # Step 2 was sane, show step 3. $anvil->data->{form}{body} = config_step3($anvil); } else { # No good $anvil->data->{form}{body} = config_step2($anvil); } } elsif ($anvil->data->{cgi}{step}{value} eq "step3") { # User has confirmed, update the system! my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ debug => 3, file => $THIS_FILE, line => __LINE__, job_command => $anvil->data->{path}{exe}{'striker-configure-host'}, job_data => "form::config_step2", job_name => "configure::network", job_title => "job_0001", job_description => "job_0002", job_progress => 0, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { job_uuid => $job_uuid }}); # Set maintenance mode. $anvil->System->maintenance_mode({set => 1}); # We don't need to store anything as hidden variables, we'll read it back from the database later. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "network_job_recorded", variables => { title_id => "", message_id => "", reload_url => "/cgi-bin/".$THIS_FILE, title => "#!string!striker_0044!#", description => "#!string!striker_0045!#", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }}); } else { $anvil->data->{form}{body} = get_network_details_form($anvil); } return(0); } # This checks to see if anything is running that requires Striker being unavailable. If not, this returns # '1'. If there is a job pending/running, it returns '0' sub check_availability { my ($anvil) = @_; my $debug = 3; my $available = 1; # Check maintenance mode. my $maintenance_mode = $anvil->System->maintenance_mode({debug => $debug}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { maintenance_mode => $maintenance_mode }}); if ($maintenance_mode) { $available = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { available => $available }}); # If we have any running or recently finished jobs, we'll desplay them below. $anvil->data->{say}{maintenance} = $anvil->Template->get({file => "striker.html", name => "striker-offline", variables => { title_id => "", message_id => "", title => "#!string!striker_0046!#", description => $anvil->Words->string({key => "striker_0090", variables => {} }), job_list => $anvil->Job->html_list({debug => 2, ended_within => 300}), }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'say::maintenance' => $anvil->data->{say}{maintenance} }}); } # TODO: Phase this out if ($available) { my $query = " SELECT job_progress, modified_date, extract(epoch from modified_date) FROM jobs WHERE job_name = 'configure::network' AND job_progress != 100 AND job_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, 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 => $debug, list => { results => $results, count => $count, }}); if ($count) { # We're waiting for the network configuration my $percent = $results->[0]->[0]; my $timestamp = $results->[0]->[1]; my $unixtime = $results->[0]->[2]; my $seconds_ago = $anvil->Convert->add_commas({number => (time - $unixtime)}); $available = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { available => $available, percent => $percent, seconds_ago => $seconds_ago, timestamp => $timestamp, unixtime => $unixtime, }}); $anvil->data->{say}{maintenance} = $anvil->Template->get({file => "striker.html", name => "striker-offline", variables => { title_id => "", message_id => "", title => "#!string!striker_0046!#", description => $anvil->Words->string({key => "striker_0047", variables => { percent => $percent, timestamp => $timestamp, seconds_ago => $seconds_ago }}), }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'say::maintenance' => $anvil->data->{say}{maintenance} }}); } } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { available => $available }}); return($available); } # This shows the user what is about to be done and asks the user to confirm. sub config_step3 { my ($anvil) = @_; # Read in all the variables from the database. We don't care what was passed, the data in the # database has been tested and is more valid than, say, a manipulated URL. my $cgi = ""; my $nets = {}; my $query = " SELECT variable_name, variable_value FROM variables WHERE (variable_section = 'config_step1' OR variable_section = 'config_step2') AND variable_source_table = 'hosts' AND variable_source_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ORDER BY variable_name ASC;"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { 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_name = $row->[0]; my $variable_value = $row->[1]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable_name => $variable_name, variable_value => $variable_value, }}); if ($variable_name =~ /form::config_step2::(.*?)::value/) { my $variable = $1; $cgi .= $variable.","; $anvil->data->{cgi}{$variable}{value} = $variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::${variable}::value" => $anvil->data->{cgi}{$variable}{value} }}); if (($variable =~ /^(bcn\d+)_/) or ($variable =~ /^(ifn\d+)_/)) { my $this_net = $1; $nets->{$this_net} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "nets->this_net" => $nets->{$this_net} }}); } } } #foreach my $key (sort {$a cmp $b} keys %ENV) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "ENV{$key}" => $ENV{$key} }}); } # Get the list of current IPs so that we can warn the user if committing the changes will require # reconnecting. $anvil->System->get_ips; my $matched_ip = 0; my $server_ip = $ENV{SERVER_ADDR}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_ip => $server_ip }}); # We'll need ot show how networks will be configured. This depends on; # 1. Is it a bonded or single interface? # 2. Is it the interface with the gateway? # 3. If no gateway or DNS was specified, alert that there will be no outside access. get_network_details($anvil); my $networks = ""; foreach my $this_net (sort {$a cmp $b} keys %{$nets}) { my $ip_key = $this_net."_ip"; my $subnet_key = $this_net."_subnet"; my $iface1_key = $this_net."_link1_mac_to_set"; my $iface2_key = $this_net."_link2_mac_to_set"; my $ip = $anvil->data->{cgi}{$ip_key}{value}; my $subnet = $anvil->data->{cgi}{$subnet_key}{value}; my $iface1_mac = $anvil->data->{cgi}{$iface1_key}{value}; my $iface2_mac = ((defined $anvil->data->{cgi}{$iface2_key}{value}) && ($anvil->data->{cgi}{$iface2_key}{value})) ? $anvil->data->{cgi}{$iface2_key}{value} : ""; my $template = $iface2_mac ? "step3_bonded_interface" : "step3_single_interface"; my $gateway = $anvil->data->{cgi}{gateway_interface}{value} eq $this_net ? 1 : 0; my $link_number = ($this_net =~ /n(\d+)$/)[0]; my $column = $this_net =~ /^bcn/ ? "striker_0018" : "striker_0022"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "s1:nets->this_net" => $nets->{$this_net}, "s2:ip" => $ip, "s3:subnet" => $subnet, "s4:iface1_mac" => $iface1_mac, "s5:iface2_mac" => $iface2_mac, "s6:template" => $template, "s7:gateway" => $gateway, "s8:link_number" => $link_number, }}); # Add to the networks template. my $say_ip = $ip."/".$subnet; if (($gateway) && ($anvil->data->{cgi}{gateway}{value})) { $template .= "_with_gateway"; $anvil->data->{cgi}{dns}{value} = "--" if not $anvil->data->{cgi}{dns}{value}; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { template => $template }}); $networks .= $anvil->Template->get({file => "config.html", name => $template, variables => { column => $anvil->Words->string({key => $column, variables => { number => $link_number }}), ip_address => $say_ip, ip => $ip, subnet => $subnet, primary => $iface1_mac, backup => $iface2_mac, gateway => $anvil->data->{cgi}{gateway}{value}, dns => $anvil->data->{cgi}{dns}{value}, }})."\n"; # Does this interface's IP match the active one? if ($server_ip eq $ip) { # Yup! No need to warn the user $matched_ip = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { matched_ip => $matched_ip }}); } } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { networks => $networks }}); # If the IP is going to change, warn the user. if (not $matched_ip) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0001"}) }}); } # We don't need to store anything as hidden variables, we'll read it back from the database later. my $step3_body = $anvil->Template->get({file => "config.html", name => "config_step3", variables => { step1_welcome_title_id => "", step1_welcome_message_id => "", organization => $anvil->data->{cgi}{organization}{value}, prefix => $anvil->data->{cgi}{prefix}{value}, hostname => $anvil->data->{cgi}{hostname}{value}, striker_user => $anvil->data->{cgi}{striker_user}{value}, striker_password => $anvil->data->{cgi}{striker_password}{value}, networks => $networks, cgi_list => $cgi."step", show_name => 1, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { step3_body => $step3_body }}); return($step3_body); } # This is step2 where the user maps their network. sub config_step2 { my ($anvil) = @_; # If I am coming from stage 1, load any values from the database, if they exist. if ($anvil->data->{cgi}{step}{value} eq "step1") { # First load. See if we can read old data. We don't know for sure what variables will have # been set, so we'll load anything from the group 'config_step2'. my $query = " SELECT variable_name, variable_value FROM variables WHERE variable_section = 'config_step2' AND variable_source_table = 'hosts' AND variable_source_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ORDER BY variable_name ASC;"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0124", variables => { query => $query }}); my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); my $count = @{$results}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { results => $results, count => $count, }}); foreach my $row (@{$results}) { my $variable_name = $row->[0]; my $variable_value = $row->[1]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable_name => $variable_name, variable_value => $variable_value, }}); if ($variable_name =~ /form::config_step2::(.*?)::value/) { my $variable = $1; $anvil->data->{cgi}{$variable}{value} = $variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "cgi::${variable}::value" => $anvil->data->{cgi}{$variable}{value} }}); } } } # We need to decide how many IFNs there is, decide if we have enough NICs and then build the form # either complaining about insufficient NICs or a list of interfaces (single or bond, depending on # iface count), the IPs to assign and mapping MACs to ifaces. We'll also offer an option to # "blind-map" as we did in v2. get_network_details($anvil); my $required_interfaces_for_single = 1 + $anvil->data->{cgi}{ifn_count}{value}; my $required_interfaces_for_bonds = 2 * $required_interfaces_for_single; my $interface_count = keys %{$anvil->data->{interfaces}}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { required_interfaces_for_single => $required_interfaces_for_single, required_interfaces_for_bonds => $required_interfaces_for_bonds, interface_count => $interface_count, }}); my $interface_options = []; foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{interfaces}}) { my $this_mac = $anvil->data->{interfaces}{$interface}{mac}; push @{$interface_options}, $this_mac."#!#".$this_mac." (".$interface.")"; } my $problem = 0; my $interface_form = ""; my $cgi = ""; my $links = []; if ($interface_count >= $required_interfaces_for_bonds) { ### Show the bonded ifaces form. # BCN my $bcn_count = $anvil->data->{cgi}{bcn_count}{value} ? $anvil->data->{cgi}{bcn_count}{value} : 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bcn_count => $bcn_count }}); foreach my $bcn (1..$bcn_count) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bcn => $bcn }}); push @{$links}, "bcn_link".$bcn; my $this_ip_key = "bcn".$bcn."_ip"; my $this_subnet_key = "bcn".$bcn."_subnet"; my $this_iface1_key = "bcn".$bcn."_link1_mac_to_set"; my $this_iface2_key = "bcn".$bcn."_link2_mac_to_set"; $cgi .= $this_ip_key.",".$this_subnet_key.",".$this_iface1_key.",".$this_iface2_key.","; my $this_ip = generate_ip($anvil, "bcn", $bcn, $anvil->data->{cgi}{sequence}{value}); my $this_ip_class = $anvil->data->{cgi}{$this_ip_key}{alert} ? "input_alert" : "input_clear"; my $this_subnet_class = $anvil->data->{cgi}{$this_subnet_key}{alert} ? "input_alert" : "input_clear"; my $this_iface1_class = $anvil->data->{cgi}{$this_iface1_key}{alert} ? "input_alert" : "input_clear"; my $this_iface2_class = $anvil->data->{cgi}{$this_iface2_key}{alert} ? "input_alert" : "input_clear"; # Build the interface select boxes... my $this_iface1_form = $anvil->Template->select_form({ name => $this_iface1_key, options => $interface_options, blank => 1, selected => defined $anvil->data->{cgi}{$this_iface1_key}{value} ? $anvil->data->{cgi}{$this_iface1_key}{value} : "", class => $anvil->data->{cgi}{$this_iface1_key}{alert} ? "input_alert" : "input_clear", }); my $this_iface2_form = $anvil->Template->select_form({ name => $this_iface2_key, options => $interface_options, blank => 1, selected => defined $anvil->data->{cgi}{$this_iface2_key}{value} ? $anvil->data->{cgi}{$this_iface2_key}{value} : "", class => $anvil->data->{cgi}{$this_iface2_key}{alert} ? "input_alert" : "input_clear", }); # Assemble the form $interface_form .= $anvil->Template->get({file => "config.html", name => "bonded_interface_form", variables => { field => $anvil->Words->string({key => "striker_0018", variables => { number => $bcn }}), description => "#!string!striker_0019!#", ip_key => $this_ip_key, ip_value => defined $anvil->data->{cgi}{$this_ip_key}{value} ? $anvil->data->{cgi}{$this_ip_key}{value} : "", ip_value_default => $this_ip, ip_class => $this_ip_class, subnet_key => $this_subnet_key, subnet_value => defined $anvil->data->{cgi}{$this_subnet_key}{value} ? $anvil->data->{cgi}{$this_subnet_key}{value} : "", subnet_value_default => $anvil->data->{defaults}{network}{bcn}{netmask}, subnet_class => $this_subnet_class, iface1_select => $this_iface1_form, iface2_select => $this_iface2_form, }}); } # IFN my $ifn_count = $anvil->data->{cgi}{ifn_count}{value} ? $anvil->data->{cgi}{ifn_count}{value} : 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ifn_count => $ifn_count }}); foreach my $ifn (1..$ifn_count) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ifn => $ifn }}); push @{$links}, "ifn_link".$ifn; my $this_ip_key = "ifn".$ifn."_ip"; my $this_subnet_key = "ifn".$ifn."_subnet"; my $this_iface1_key = "ifn".$ifn."_link1_mac_to_set"; my $this_iface2_key = "ifn".$ifn."_link2_mac_to_set"; $cgi .= $this_ip_key.",".$this_subnet_key.",".$this_iface1_key.",".$this_iface2_key.","; my $this_ip = generate_ip($anvil, "ifn", $ifn, $anvil->data->{cgi}{sequence}{value}); my $this_ip_class = $anvil->data->{cgi}{$this_ip_key}{alert} ? "input_alert" : "input_clear"; my $this_subnet_class = $anvil->data->{cgi}{$this_subnet_key}{alert} ? "input_alert" : "input_clear"; my $this_iface1_class = $anvil->data->{cgi}{$this_iface1_key}{alert} ? "input_alert" : "input_clear"; my $this_iface2_class = $anvil->data->{cgi}{$this_iface2_key}{alert} ? "input_alert" : "input_clear"; # Build the interface select boxes... my $this_iface1_form = $anvil->Template->select_form({ name => $this_iface1_key, options => $interface_options, blank => 1, selected => defined $anvil->data->{cgi}{$this_iface1_key}{value} ? $anvil->data->{cgi}{$this_iface1_key}{value} : "", class => $anvil->data->{cgi}{$this_iface1_key}{alert} ? "input_alert" : "input_clear", }); my $this_iface2_form = $anvil->Template->select_form({ name => $this_iface2_key, options => $interface_options, blank => 1, selected => defined $anvil->data->{cgi}{$this_iface2_key}{value} ? $anvil->data->{cgi}{$this_iface2_key}{value} : "", class => $anvil->data->{cgi}{$this_iface2_key}{alert} ? "input_alert" : "input_clear", }); # Assemble the form $interface_form .= $anvil->Template->get({file => "config.html", name => "bonded_interface_form", variables => { field => $anvil->Words->string({key => "striker_0022", variables => { number => $ifn }}), description => "#!string!striker_0023!#", ip_key => $this_ip_key, ip_value => defined $anvil->data->{cgi}{$this_ip_key}{value} ? $anvil->data->{cgi}{$this_ip_key}{value} : "", ip_value_default => $this_ip, ip_class => $this_ip_class, subnet_key => $this_subnet_key, subnet_value => defined $anvil->data->{cgi}{$this_subnet_key}{value} ? $anvil->data->{cgi}{$this_subnet_key}{value} : "", subnet_value_default => $anvil->data->{defaults}{network}{ifn}{netmask}, subnet_class => $this_subnet_class, iface1_select => $this_iface1_form, iface2_select => $this_iface2_form, }}); } } else { ### Show the single iface per network form. # BCN my $bcn_count = $anvil->data->{cgi}{bcn_count}{value} ? $anvil->data->{cgi}{bcn_count}{value} : 1; foreach my $bcn (1..$bcn_count) { push @{$links}, "bcn_link".$bcn; my $this_ip_key = "bcn".$bcn."_ip"; my $this_subnet_key = "bcn".$bcn."_subnet"; my $this_iface1_key = "bcn".$bcn."_link1_mac_to_set"; $cgi .= $this_ip_key.",".$this_subnet_key.",".$this_iface1_key.","; my $this_ip = generate_ip($anvil, "bcn", $bcn, $anvil->data->{cgi}{sequence}{value}); my $this_ip_class = $anvil->data->{cgi}{$this_ip_key}{alert} ? "input_alert" : "input_clear"; my $this_subnet_class = $anvil->data->{cgi}{$this_subnet_key}{alert} ? "input_alert" : "input_clear"; my $this_iface1_class = $anvil->data->{cgi}{$this_iface1_key}{alert} ? "input_alert" : "input_clear"; # Build the interface select boxes... my $this_iface1_form = $anvil->Template->select_form({ name => $this_iface1_key, options => $interface_options, blank => 1, selected => defined $anvil->data->{cgi}{$this_iface1_key}{value} ? $anvil->data->{cgi}{$this_iface1_key}{value} : "", class => $anvil->data->{cgi}{$this_iface1_key}{alert} ? "input_alert" : "input_clear", }); # Assemble the form $interface_form .= $anvil->Template->get({file => "config.html", name => "single_interface_form", variables => { field => $anvil->Words->string({key => "striker_0018", variables => { number => $bcn }}), description => "#!string!striker_0019!#", ip_key => $this_ip_key, ip_value => defined $anvil->data->{cgi}{$this_ip_key}{value} ? $anvil->data->{cgi}{$this_ip_key}{value} : "", ip_value_default => $this_ip, ip_class => $this_ip_class, subnet_key => $this_subnet_key, subnet_value => defined $anvil->data->{cgi}{$this_subnet_key}{value} ? $anvil->data->{cgi}{$this_subnet_key}{value} : $anvil->data->{defaults}{network}{bcn}{netmask}, subnet_value_default => $anvil->data->{defaults}{network}{bcn}{netmask}, subnet_class => $this_subnet_class, iface1_select => $this_iface1_form, }}); } # IFN my $ifn_count = $anvil->data->{cgi}{ifn_count}{value} ? $anvil->data->{cgi}{ifn_count}{value} : 1; foreach my $ifn (1..$ifn_count) { push @{$links}, "ifn_link".$ifn; my $this_ip_key = "ifn".$ifn."_ip"; my $this_subnet_key = "ifn".$ifn."_subnet"; my $this_iface1_key = "ifn".$ifn."_link1_mac_to_set"; $cgi .= $this_ip_key.",".$this_subnet_key.",".$this_iface1_key.","; my $this_ip = generate_ip($anvil, "ifn", $ifn, $anvil->data->{cgi}{sequence}{value}); my $this_ip_class = $anvil->data->{cgi}{$this_ip_key}{alert} ? "input_alert" : "input_clear"; my $this_subnet_class = $anvil->data->{cgi}{$this_subnet_key}{alert} ? "input_alert" : "input_clear"; my $this_iface1_class = $anvil->data->{cgi}{$this_iface1_key}{alert} ? "input_alert" : "input_clear"; # Build the interface select boxes... my $this_iface1_form = $anvil->Template->select_form({ name => $this_iface1_key, options => $interface_options, blank => 1, selected => defined $anvil->data->{cgi}{$this_iface1_key}{value} ? $anvil->data->{cgi}{$this_iface1_key}{value} : "", class => $anvil->data->{cgi}{$this_iface1_key}{alert} ? "input_alert" : "input_clear", }); # Assemble the form $interface_form .= $anvil->Template->get({file => "config.html", name => "single_interface_form", variables => { field => $anvil->Words->string({key => "striker_0022", variables => { number => $ifn }}), description => "#!string!striker_0023!#", ip_key => $this_ip_key, ip_value => defined $anvil->data->{cgi}{$this_ip_key}{value} ? $anvil->data->{cgi}{$this_ip_key}{value} : "", ip_value_default => $this_ip, ip_class => $this_ip_class, subnet_key => $this_subnet_key, subnet_value => defined $anvil->data->{cgi}{$this_subnet_key}{value} ? $anvil->data->{cgi}{$this_subnet_key}{value} : $anvil->data->{defaults}{network}{ifn}{netmask}, subnet_value_default => $anvil->data->{defaults}{network}{ifn}{netmask}, subnet_class => $this_subnet_class, iface1_select => $this_iface1_form, }}); } } ### TODO: Add a form for Gateway, DNS and which interface to use # Gateway my $gateway_class = $anvil->data->{cgi}{gateway}{alert} ? "input_alert" : "input_clear"; my $say_gateway = $anvil->Template->get({file => "main.html", name => "input_text_form", variables => { name => "gateway", id => "gateway", field => "#!string!striker_0035!#", description => "#!string!striker_0036!#", value => defined $anvil->data->{cgi}{gateway}{value} ? $anvil->data->{cgi}{gateway}{value} : "", default_value => "", class => $gateway_class, extra => "", }}); # DNS my $dns_class = $anvil->data->{cgi}{dns}{alert} ? "input_alert" : "input_clear"; my $say_dns = $anvil->Template->get({file => "main.html", name => "input_text_form", variables => { name => "dns", id => "dns", field => "#!string!striker_0037!#", description => "#!string!striker_0038!#", value => defined $anvil->data->{cgi}{dns}{value} ? $anvil->data->{cgi}{dns}{value} : "", default_value => $anvil->data->{defaults}{network}{dns}, class => $dns_class, extra => "", }}); # Which interface gets the route? my $default_dg_iface = defined $anvil->data->{cgi}{dg_iface}{value} ? $anvil->data->{cgi}{dg_iface}{value} : "ifn_link1"; my $dg_iface_select = $anvil->Template->select_form({ name => "dg_iface", options => $links, blank => 0, selected => $default_dg_iface, class => $anvil->data->{cgi}{dg_iface}{alert} ? "input_alert" : "input_clear", }); ### NOTE: I'll likely want this for choosing which interface to use as the default gateway when 2+ ### IFNs are used. # my $dg_iface_class = $anvil->data->{cgi}{dg_iface}{alert} ? "input_alert" : "input_clear"; # my $say_dg_iface = $anvil->Template->get({file => "main.html", name => "input_select_form", variables => { # field => "#!string!striker_0039!#", # description => "#!string!striker_0040!#", # 'select' => "", # }}); # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { say_dg_iface => $say_dg_iface }}); # Hostname my $say_default_hostname = $anvil->data->{cgi}{prefix}{value}."-striker0".$anvil->data->{cgi}{sequence}{value}.".".$anvil->data->{cgi}{domain}{value}; my $hostname_class = $anvil->data->{cgi}{hostname}{alert} ? "input_alert" : "input_clear"; my $say_hostname = $anvil->Template->get({file => "main.html", name => "input_text_form", variables => { name => "hostname", id => "hostname", field => "#!string!striker_0016!#", description => "#!string!striker_0017!#", value => defined $anvil->data->{cgi}{hostname}{value} ? $anvil->data->{cgi}{hostname}{value} : $say_default_hostname, default_value => "", class => $hostname_class, extra => "", }}); # Admin user my $striker_user_class = $anvil->data->{cgi}{striker_user}{alert} ? "input_alert" : "input_clear"; my $say_striker_user = $anvil->Template->get({file => "main.html", name => "input_text_form", variables => { name => "striker_user", id => "striker_user", field => "#!string!striker_0031!#", description => "#!string!striker_0032!#", value => defined $anvil->data->{cgi}{striker_user}{value} ? $anvil->data->{cgi}{striker_user}{value} : $anvil->data->{sys}{user}{name}, default_value => "", class => $striker_user_class, extra => "", }}); # Password my $striker_password_class = $anvil->data->{cgi}{striker_password}{alert} ? "input_alert" : "input_clear"; my $say_striker_password = $anvil->Template->get({file => "main.html", name => "input_text_form", variables => { name => "striker_password", id => "striker_password", field => "#!string!striker_0033!#", description => "#!string!striker_0034!#", value => defined $anvil->data->{cgi}{striker_password}{value} ? $anvil->data->{cgi}{striker_password}{value} : "", default_value => "", class => $striker_password_class, extra => "", }}); # Get the table that shows the current interface states. my $interface_states = get_network_details_form($anvil); # Store the previous CGI variables and display the new fields. my $step2_body = $anvil->Template->get({file => "config.html", name => "config_step2", variables => { step1_welcome_title_id => "", step1_welcome_message_id => "", organization => $anvil->data->{cgi}{organization}{value}, prefix => $anvil->data->{cgi}{prefix}{value}, domain => $anvil->data->{cgi}{domain}{value}, sequence => $anvil->data->{cgi}{sequence}{value}, bcn_count => $anvil->data->{cgi}{bcn_count}{value}, ifn_count => $anvil->data->{cgi}{ifn_count}{value}, interface_form => $interface_form, interface_states => $interface_states, striker_user_form => $say_striker_user, striker_password_form => $say_striker_password, gateway_form => $say_gateway, dns_form => $say_dns, hostname_form => $say_hostname, cgi_list => $cgi."organization,prefix,domain,sequence,bcn_count,ifn_count,gateway,hostname,dns,striker_user,striker_password", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { step2_body => $step2_body }}); return($step2_body); } # This sanity-checks step 2 and returns '1' if there was a problem. sub sanity_check_step2 { my ($anvil) = @_; # This will flip if we run into a problem. my $sane = 1; # Do we have a host name, striker user and password? if (not $anvil->Validate->form_field({name => "hostname", type => "domain_name"})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0012"}) }}); $anvil->data->{cgi}{hostname}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane }}); } else { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::hostname::value", variable_value => $anvil->data->{cgi}{hostname}{value}, variable_default => "", variable_description => "striker_0017", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # The user name if ((not defined $anvil->data->{cgi}{striker_user}{value}) or (not $anvil->data->{cgi}{striker_user}{value})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0013"}) }}); $anvil->data->{cgi}{striker_user}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::striker_user::alert" => $anvil->data->{cgi}{striker_user}{alert} }}); } else { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::striker_user::value", variable_value => $anvil->data->{cgi}{striker_user}{value}, variable_default => "", variable_description => "striker_0032", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # The password if ((not defined $anvil->data->{cgi}{striker_password}{value}) or (not $anvil->data->{cgi}{striker_password}{value}) or (length($anvil->data->{cgi}{striker_password}{value}) < 6)) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0014"}) }}); $anvil->data->{cgi}{striker_password}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::striker_password::alert" => $anvil->data->{cgi}{striker_password}{alert} }}); } else { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::striker_password::value", variable_value => $anvil->data->{cgi}{striker_password}{value}, variable_default => "", variable_description => "striker_0034", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # DNS can be multiple entries, comma-separated. It can also be blank, if the user doesn't need Internet access. my $dns_ok = 1; if ((defined $anvil->data->{cgi}{dns}{value}) and ($anvil->data->{cgi}{dns}{value})) { foreach my $ip (split/,/, $anvil->data->{cgi}{dns}{value}) { $ip =~ s/^\s+//; $ip =~ s/\s+$//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ip => $ip }}); if (not $anvil->Validate->is_ipv4({ip => $ip})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0015"}) }}); $anvil->data->{cgi}{dns}{alert} = 1; $sane = 0; $dns_ok = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, dns_ok => $dns_ok, "cgi::dns::alert" => $anvil->data->{cgi}{dns}{alert} }}); } } } if ($dns_ok) { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::dns::value", variable_value => $anvil->data->{cgi}{dns}{value}, variable_default => "", variable_description => "striker_0038", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # Look for interfaces and sanity check them. my $networks = {}; foreach my $network ("bcn", "ifn") { my $count_key = $network."_count"; my $network_count = $anvil->data->{cgi}{$count_key}{value} ? $anvil->data->{cgi}{$count_key}{value} : 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network => $network, count_key => $count_key, network_count => $network_count, }}); foreach my $count (1..$network_count) { my $this_network = $network.$count; my $this_ip_key = $this_network."_ip"; my $this_subnet_key = $this_network."_subnet"; my $this_iface1_key = $this_network."_link1_mac_to_set"; my $this_iface2_key = $this_network."_link2_mac_to_set"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count, this_ip_key => $this_ip_key, this_subnet_key => $this_subnet_key, this_iface1_key => $this_iface1_key, this_iface2_key => $this_iface2_key, "cgi::${this_ip_key}::value" => $anvil->data->{cgi}{$this_ip_key}{value}, "cgi::${this_subnet_key}::value" => $anvil->data->{cgi}{$this_subnet_key}{value}, "cgi::${this_iface1_key}::value" => $anvil->data->{cgi}{$this_iface1_key}{value}, "cgi::${this_iface2_key}::value" => $anvil->data->{cgi}{$this_iface2_key}{value}, }}); # This will be used to tell the user which interface has a problem, if one exists. my $network_key = $network =~ /^bcn/ ? "striker_0018" : "striker_0022"; my $say_network = $anvil->Words->string({key => $network_key, variables => { number => $count }}); # Is the IP sane? my $ip_ok = 1; if (not $anvil->Validate->form_field({name => $this_ip_key, type => "ipv4"})) { # Nope $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0016", variables => { network => $say_network}}) }}); $anvil->data->{cgi}{$this_ip_key}{alert} = 1; $sane = 0; $ip_ok = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, ip_ok => $ip_ok }}); } else { # Save the IP $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::${this_ip_key}::value", variable_value => $anvil->data->{cgi}{$this_ip_key}{value}, variable_default => "", variable_description => "striker_0024", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # What about the subnet? if (not $anvil->Validate->form_field({name => $this_subnet_key, type => "subnet"})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0016", variables => { network => $say_network }}) }}); $anvil->data->{cgi}{$this_subnet_key}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane }}); } elsif ($ip_ok) { # We'll use the dotted-decimal subnet. If it already is, great. If not, convert it. my $say_subnet = $anvil->data->{cgi}{$this_subnet_key}{value} =~ /^\d{1,2}$/ ? $anvil->Convert->cide({cidr => $anvil->data->{cgi}{$this_subnet_key}{value}}) : $anvil->data->{cgi}{$this_subnet_key}{value}; my $full_ip = $anvil->data->{cgi}{$this_ip_key}{value}."/".$anvil->data->{cgi}{$this_subnet_key}{value}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_subnet => $say_subnet, full_ip => $full_ip }}); $networks->{$this_network} = $full_ip; # Save the subnet $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::${this_subnet_key}::value", variable_value => $anvil->data->{cgi}{$this_subnet_key}{value}, variable_default => "", variable_description => "striker_0025", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # Interface 1 must be set if (not $anvil->Validate->form_field({name => $this_iface1_key, type => "mac"})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0017", variables => { network => $say_network, 'link' => "1" }}) }}); $anvil->data->{cgi}{$this_iface1_key}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane }}); } else { # Valid MAC. Has it been assigned elsewhere? my $mac = $anvil->data->{cgi}{$this_iface1_key}{value}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { mac => $mac }}); if ((not exists $anvil->data->{network}{$mac}{set_as}) or (not $anvil->data->{network}{$mac}{set_as})) { $anvil->data->{network}{$mac}{set_as} = $this_iface1_key; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "network::${mac}::set_as" => $anvil->data->{network}{$mac}{set_as} }}); # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::${this_iface1_key}::value", variable_value => $anvil->data->{cgi}{$this_iface1_key}{value}, variable_default => "", variable_description => "striker_0029", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } else { # Conflict! Set the alert for this interface and the conflicting one. my $conflict_key = $anvil->data->{network}{$mac}{set_as}; $anvil->data->{cgi}{$conflict_key}{alert} = 1; $anvil->data->{cgi}{$this_iface1_key}{alert} = 1; $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0018"}) }}); $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::${conflict_key}::alert" => $anvil->data->{cgi}{$conflict_key}{alert}, "cgi::${this_iface1_key}::alert" => $anvil->data->{cgi}{$this_iface1_key}{alert}, }}); } } # Interface 2 doesn't have to be set. if ((defined $anvil->data->{cgi}{$this_iface2_key}{value}) && ($anvil->data->{cgi}{$this_iface2_key}{value})) { if (not $anvil->Validate->form_field({name => $this_iface2_key, type => "mac"})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0017", variables => { network => $say_network, 'link' => "2" }}) }}); $anvil->data->{cgi}{$this_iface2_key}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane }}); } else { # Valid MAC. Has it been assigned elsewhere? my $mac = $anvil->data->{cgi}{$this_iface2_key}{value}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { mac => $mac }}); if ((not exists $anvil->data->{network}{$mac}{set_as}) or (not $anvil->data->{network}{$mac}{set_as})) { $anvil->data->{network}{$mac}{set_as} = $this_iface2_key; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "network::${mac}::set_as" => $anvil->data->{network}{$mac}{set_as} }}); # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::${this_iface2_key}::value", variable_value => $anvil->data->{cgi}{$this_iface2_key}{value}, variable_default => "", variable_description => "striker_0030", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } else { # Conflict! Set the alert for this interface and the conflicting one. my $conflict_key = $anvil->data->{network}{$mac}{set_as}; $anvil->data->{cgi}{$conflict_key}{alert} = 1; $anvil->data->{cgi}{$this_iface2_key}{alert} = 1; $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0018"}) }}); $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::${conflict_key}::alert" => $anvil->data->{cgi}{$conflict_key}{alert}, "cgi::${this_iface2_key}::alert" => $anvil->data->{cgi}{$this_iface2_key}{alert}, }}); } } } } } # The gateway, this has to be after the interfaces so that we can match it to an interface (and error # if not) if (not $anvil->Validate->form_field({name => "gateway", type => "ipv4", empty_ok => 1})) { $anvil->data->{cgi}{gateway}{alert} = 1; $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0019"}) }}); $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane }}); } else { # Convert the gateway strings to binary. my $gateway = defined $anvil->data->{cgi}{gateway}{value} ? $anvil->data->{cgi}{gateway}{value} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { gateway => $gateway }}); # If there is no gateway, that's OK, there just won't be any Internet access. my $gateway_interface = ""; if ($gateway) { # Match this gateway to one of the interfaces. foreach my $this_network (sort {$a cmp $b} keys %{$networks}) { my ($this_ip, $this_subnet) = ($networks->{$this_network} =~ /^(.*?)\/(.*)$/); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "s1:this_network" => $this_network, "s2:networks->$this_network" => $networks->{$this_network}, "s3:this_ip" => $this_ip, "s4:this_subnet" => $this_subnet, }}); my $first = NetAddr::IP->new("$this_ip/$this_subnet"); my $second = NetAddr::IP->new("$gateway/$this_subnet"); if ($second->within($first)) { $gateway_interface = $this_network; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { gateway_interface => $gateway_interface }}); } } if (not $gateway_interface) { # Explain the problem $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0004"}) }}); $anvil->data->{cgi}{gateway}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::gateway::alert" => $anvil->data->{cgi}{gateway}{alert} }}); } } # If no alert was raised, record the values. if (not $anvil->data->{cgi}{gateway}{alert}) { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::gateway::value", variable_value => $anvil->data->{cgi}{gateway}{value}, variable_default => "", variable_description => "striker_0036", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); # Record the gateway interface $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step2::gateway_interface::value", variable_value => $gateway_interface, variable_default => "", variable_description => "", variable_section => "config_step2", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { sane => $sane }}); return($sane); } # This sanity-checks step 1 and returns '1' if there was a problem. sub sanity_check_step1 { my ($anvil) = @_; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0131", variables => { function => "sanity_check_step1()" }}); # This will flip if we run into a problem. my $sane = 1; # Organization just needs *something* if ((not defined $anvil->data->{cgi}{organization}{value}) or (not $anvil->data->{cgi}{organization}{value})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0020", variables => { field => "striker_0003" }}) }}); $anvil->data->{cgi}{organization}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::organization::alert" => $anvil->data->{cgi}{organization}{alert} }}); } else { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step1::organization::value", variable_value => $anvil->data->{cgi}{organization}{value}, variable_default => "", variable_description => "striker_0004", variable_section => "config_step1", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # The prefix needs to be alphanumeric and be between 1 ~ 5 chatacters. if ((not $anvil->Validate->is_alphanumeric({string => $anvil->data->{cgi}{prefix}{value}})) or (length($anvil->data->{cgi}{prefix}{value}) > 5)) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0021"}) }}); $anvil->data->{cgi}{prefix}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::prefix::alert" => $anvil->data->{cgi}{prefix}{alert} }}); } else { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step1::prefix::value", variable_value => $anvil->data->{cgi}{prefix}{value}, variable_default => "", variable_description => "striker_0006", variable_section => "config_step1", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # We can use Validate to check the domain. if (not $anvil->Validate->form_field({name => "domain", type => "domain_name"})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0020", variables => { field => "striker_0007" }}) }}); $anvil->data->{cgi}{domain}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane }}); } else { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step1::domain::value", variable_value => $anvil->data->{cgi}{domain}{value}, variable_default => "", variable_description => "striker_0008", variable_section => "config_step1", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } # The sequence and IFN count need to be integers. if (not $anvil->Validate->is_positive_integer({number => $anvil->data->{cgi}{sequence}{value}})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0022", variables => { field => "striker_0009" }}) }}); $anvil->data->{cgi}{sequence}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::sequence::alert" => $anvil->data->{cgi}{sequence}{alert} }}); } else { # Record the answer. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step1::sequence::value", variable_value => $anvil->data->{cgi}{sequence}{value}, variable_default => "", variable_description => "striker_0010", variable_section => "config_step1", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } if (not $anvil->Validate->is_positive_integer({number => $anvil->data->{cgi}{ifn_count}{value}})) { $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0022", variables => { field => "striker_0011" }}) }}); $anvil->data->{cgi}{ifn_count}{alert} = 1; $sane = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { sane => $sane, "cgi::ifn_count::alert" => $anvil->data->{cgi}{ifn_count}{alert} }}); } # Make sure we have enough interfaces. get_network_details($anvil); my $required_interfaces_for_single = 1 + $anvil->data->{cgi}{ifn_count}{value}; my $interface_count = keys %{$anvil->data->{interfaces}}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { required_interfaces_for_single => $required_interfaces_for_single, interface_count => $interface_count, }}); if ($interface_count < $required_interfaces_for_single) { # Build the error message. my $say_message = $anvil->Words->string({key => "error_0001", variables => { interface_count => $interface_count, required_interfaces_for_single => $required_interfaces_for_single, }}); # Not enough interfaces found. $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $say_message }}); $anvil->data->{cgi}{ifn_count}{alert} = 1; $sane = 0; } else { # Sane, Record the answers. $anvil->Database->insert_or_update_variables({ variable_name => "form::config_step1::ifn_count::value", variable_value => $anvil->data->{cgi}{ifn_count}{value}, variable_default => "", variable_description => "striker_0012", variable_section => "config_step1", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", update_value_only => 1, }); } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { sane => $sane }}); return($sane); } # This is step 1 in asking the user how to configure Striker. sub config_step1 { my ($anvil) = @_; if (not $anvil->data->{cgi}{'next'}{value}) { # First load. See if we can read old data. my ($organization) = $anvil->Database->read_variable({ variable_name => "form::config_step1::organization::value", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", }); my ($prefix) = $anvil->Database->read_variable({ variable_name => "form::config_step1::prefix::value", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", }); my ($domain) = $anvil->Database->read_variable({ variable_name => "form::config_step1::domain::value", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", }); my ($sequence) = $anvil->Database->read_variable({ variable_name => "form::config_step1::sequence::value", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", }); my ($ifn_count) = $anvil->Database->read_variable({ variable_name => "form::config_step1::ifn_count::value", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { organization => $organization, prefix => $prefix, domain => $domain, sequence => $sequence, ifn_count => $ifn_count, }}); $anvil->data->{cgi}{organization}{value} = $organization ne "" ? $organization : ""; $anvil->data->{cgi}{prefix}{value} = $prefix ne "" ? $prefix : ""; $anvil->data->{cgi}{domain}{value} = $domain ne "" ? $domain : ""; $anvil->data->{cgi}{sequence}{value} = $sequence ne "" ? $sequence : ""; $anvil->data->{cgi}{ifn_count}{value} = $ifn_count ne "" ? $ifn_count : 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'cgi::organization::value' => $anvil->data->{cgi}{organization}{value}, 'cgi::prefix::value' => $anvil->data->{cgi}{prefix}{value}, 'cgi::domain::value' => $anvil->data->{cgi}{domain}{value}, 'cgi::sequence::value' => $anvil->data->{cgi}{sequence}{value}, 'cgi::ifn_count::value' => $anvil->data->{cgi}{ifn_count}{value}, }}); # If we don't have an organization name, prefix, domain name or sequence number, try to parse it from # the current static and pretty hostnames. my ($traditional_hostname, $descriptive_hostname) = $anvil->System->hostname(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { traditional_hostname => $traditional_hostname, descriptive_hostname => $descriptive_hostname, }}); if ($descriptive_hostname =~ /^(.*?) - Striker (\d+)/) { my $organization = $1; my $sequence = $2; $sequence =~ s/^0+//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { organization => $organization, sequence => $sequence, }}); if (($organization) && ($anvil->data->{cgi}{organization}{value} eq "")) { $anvil->data->{cgi}{organization}{value} = $organization; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'cgi::organization::value' => $anvil->data->{cgi}{organization}{value}, }}); } if (($sequence =~ /^\d+$/) && ($anvil->data->{cgi}{sequence}{value} eq "")) { $anvil->data->{cgi}{sequence}{value} = $sequence; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'cgi::sequence::value' => $anvil->data->{cgi}{sequence}{value}, }}); } } if ($traditional_hostname =~ /^(.*?)-striker\d+\.(.*)$/) { my $prefix = $1; my $domain = $2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { prefix => $prefix, domain => $domain, }}); if (($prefix) && ($anvil->data->{cgi}{prefix}{value} eq "")) { $anvil->data->{cgi}{prefix}{value} = $prefix; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'cgi::prefix::value' => $anvil->data->{cgi}{prefix}{value}, }}); } if (($domain) && ($anvil->data->{cgi}{domain}{value} eq "")) { $anvil->data->{cgi}{domain}{value} = $domain; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'cgi::domain::value' => $anvil->data->{cgi}{domain}{value}, }}); } } # If I still don't have a sequence number, set '1'. if ($anvil->data->{cgi}{sequence}{value} eq "") { $anvil->data->{cgi}{sequence}{value} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'cgi::sequence::value' => $anvil->data->{cgi}{sequence}{value}, }}); } } my $organization_class = $anvil->data->{cgi}{organization}{alert} ? "input_alert" : "input_clear"; my $say_organization = $anvil->Template->get({file => "main.html", name => "input_text_form", variables => { name => "organization", id => "organization", field => "#!string!striker_0003!#", description => "#!string!striker_0004!#", value => defined $anvil->data->{cgi}{organization}{value} ? $anvil->data->{cgi}{organization}{value} : "", default_value => "", class => $organization_class, extra => "", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_organization => $say_organization }}); my $prefix_class = $anvil->data->{cgi}{prefix}{alert} ? "input_alert" : "input_clear"; my $say_prefix = $anvil->Template->get({file => "main.html", name => "input_text_form", variables => { name => "prefix", id => "prefix", field => "#!string!striker_0005!#", description => "#!string!striker_0006!#", value => defined $anvil->data->{cgi}{prefix}{value} ? $anvil->data->{cgi}{prefix}{value} : "", default_value => "", class => $prefix_class, extra => "maxlength=\"5\"", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_prefix => $say_prefix }}); my $domain_class = $anvil->data->{cgi}{domain}{alert} ? "input_alert" : "input_clear"; my $say_domain = $anvil->Template->get({file => "main.html", name => "input_text_form", variables => { name => "domain", id => "domain", field => "#!string!striker_0007!#", description => "#!string!striker_0008!#", value => defined $anvil->data->{cgi}{domain}{value} ? $anvil->data->{cgi}{domain}{value} : "", default_value => "", class => $domain_class, extra => "maxlength=\"255\"", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_domain => $say_domain }}); my $sequence_class = $anvil->data->{cgi}{sequence}{alert} ? "input_alert" : "input_clear"; my $say_sequence = $anvil->Template->get({file => "main.html", name => "input_number_form", variables => { name => "sequence", id => "sequence", field => "#!string!striker_0009!#", description => "#!string!striker_0010!#", value => defined $anvil->data->{cgi}{sequence}{value} ? $anvil->data->{cgi}{sequence}{value} : "", class => $sequence_class, extra => "min=\"1\" max=\"24\"", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_domain => $say_domain }}); my $ifn_count_class = $anvil->data->{cgi}{ifn_count}{alert} ? "input_alert" : "input_clear"; my $say_ifn_count = $anvil->Template->get({file => "main.html", name => "input_number_form", variables => { name => "ifn_count", id => "ifn_count", field => "#!string!striker_0011!#", description => "#!string!striker_0012!#", value => defined $anvil->data->{cgi}{ifn_count}{value} ? $anvil->data->{cgi}{ifn_count}{value} : "", class => $ifn_count_class, extra => "min=\"1\" max=\"24\"", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_ifn_count => $say_ifn_count }}); my $step1_body = $anvil->Template->get({file => "config.html", name => "config_step1", variables => { step1_welcome_title_id => "", step1_welcome_message_id => "", organization_form => $say_organization, prefix_form => $say_prefix, domain_form => $say_domain, sequence_form => $say_sequence, ifn_count_form => $say_ifn_count, cgi_list => "organization,prefix,domain,sequence,ifn_count", }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { step1_body => $step1_body }}); return($step1_body); } # This reads the network status XML file and loads the data into $anvil->data->{network}{{...}. sub get_network_details { my ($anvil) = @_; ### TODO: Daemonize this or solve selinux issues ### Refresh the network.xml #$anvil->System->call({shell_call => $anvil->data->{path}{exe}{'anvil-update-states'}}); # Now read the network.xml my $file = $anvil->data->{path}{directories}{html}."/status/network.xml"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file }}); # Parse... my $xml = XML::Simple->new(); my $data = ""; my $network = ""; eval { $data = $xml->XMLin($file, KeyAttr => { interface => 'name', key => 'name', ip => 'address' }, ForceArray => [ 'interface', 'key' ] ) }; if ($@) { chomp $@; my $error = "[ Error ] - The was a problem reading: [$file]. The error was:\n"; $error .= "===========================================================\n"; $error .= $@."\n"; $error .= "===========================================================\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => $error}); } else { foreach my $interface (sort {$a cmp $b} keys %{$data->{interface}}) { $anvil->data->{interfaces}{$interface} = { bond => $data->{interface}{$interface}{bond}, bridge => $data->{interface}{$interface}{bridge}, duplex => $data->{interface}{$interface}{duplex}, 'link' => $data->{interface}{$interface}{'link'}, mac => $data->{interface}{$interface}{mac}, media => $data->{interface}{$interface}{media}, mtu => $data->{interface}{$interface}{mtu}, order => $data->{interface}{$interface}{order}, speed => $data->{interface}{$interface}{speed}, 'state' => $data->{interface}{$interface}{'state'}, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "interfaces::${interface}::bond" => $anvil->data->{interfaces}{$interface}{bond}, "interfaces::${interface}::bridge" => $anvil->data->{interfaces}{$interface}{bridge}, "interfaces::${interface}::duplex" => $anvil->data->{interfaces}{$interface}{duplex}, "interfaces::${interface}::link" => $anvil->data->{interfaces}{$interface}{'link'}, "interfaces::${interface}::mac" => $anvil->data->{interfaces}{$interface}{mac}, "interfaces::${interface}::media" => $anvil->data->{interfaces}{$interface}{media}, "interfaces::${interface}::mtu" => $anvil->data->{interfaces}{$interface}{mtu}, "interfaces::${interface}::order" => $anvil->data->{interfaces}{$interface}{order}, "interfaces::${interface}::speed" => $anvil->data->{interfaces}{$interface}{speed}, "interfaces::${interface}::state" => $anvil->data->{interfaces}{$interface}{'state'}, }}); } ### TODO: Sort out how to read the XML using the proper KeyAttr to avoid this mess... # If there is only one IP, the details will be stored directly under the 'ip' key. If two or # more exist, each ip address will be the key after 'ip'. if (exists $data->{ip}{address}) { # Only one entry. Fix the hash. my $address = $data->{ip}{address}; my $on = $data->{ip}{on}; my $subnet = $data->{ip}{subnet}; my $gateway = $data->{ip}{gateway}; my $default_gateway = $data->{ip}{default_gateway}; my $dns = $data->{ip}{dns}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { address => $address, on => $on, subnet => $subnet, gateway => $gateway, default_gateway => $default_gateway, dns => $dns, }}); $anvil->data->{ip}{$address} = { on => $on, subnet => $subnet, gateway => $gateway, default_gateway => $default_gateway, dns => $dns, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "ip::${address}::on" => $anvil->data->{ip}{$address}{on}, "ip::${address}::subnet" => $anvil->data->{ip}{$address}{subnet}, "ip::${address}::gateway" => $anvil->data->{ip}{$address}{gateway}, "ip::${address}::default_gateway" => $anvil->data->{ip}{$address}{default_gateway}, "ip::${address}::dns" => $anvil->data->{ip}{$address}{dns}, }}); } else { foreach my $address (sort {$a cmp $b} keys %{$data->{ip}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { address => $address }}); $anvil->data->{ip}{$address} = { on => $data->{ip}{$address}{on}, subnet => $data->{ip}{$address}{subnet}, gateway => $data->{ip}{$address}{gateway}, default_gateway => $data->{ip}{$address}{default_gateway}, dns => $data->{ip}{$address}{dns}, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "ip::${address}::on" => $anvil->data->{ip}{$address}{on}, "ip::${address}::subnet" => $anvil->data->{ip}{$address}{subnet}, "ip::${address}::gateway" => $anvil->data->{ip}{$address}{gateway}, "ip::${address}::default_gateway" => $anvil->data->{ip}{$address}{default_gateway}, "ip::${address}::dns" => $anvil->data->{ip}{$address}{dns}, }}); } } } return(0); } sub get_network_details_form { my ($anvil) = @_; my $file = $anvil->data->{path}{directories}{html}."/status/network.xml"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file }}); my $xml = XML::Simple->new(); my $data = ""; my $network = ""; eval { $data = $xml->XMLin($file, KeyAttr => { interface => 'name', key => 'name' }, ForceArray => [ 'interface', 'key' ]) }; if ($@) { chomp $@; my $error = "[ Error ] - The was a problem reading: [$file]. The error was:\n"; $error .= "===========================================================\n"; $error .= $@."\n"; $error .= "===========================================================\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => $error}); } else { my $interface_list = ""; $network = $anvil->Template->get({file => "config.html", name => "network_header"}); foreach my $interface (sort {$a cmp $b} keys %{$data->{interface}}) { $interface_list .= "$interface,"; $network .= $anvil->Template->get({file => "config.html", name => "network_entry", variables => { mac => "", mac_id => $interface."_mac", name => $interface, name_id => $interface."_name", speed => "", speed_id => $interface."_speed", 'link' => "", link_id => $interface."_link", order => "", order_id => $interface."_order", }}); } $interface_list =~ s/,$//; $network .= $anvil->Template->get({file => "config.html", name => "network_footer", variables => { interface_list => $interface_list }}); } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network => $network }}); return($network); } =cut Network planning; 10.x.y.z / 255.255.0.0 10.x.y.z / 255.255.0.0 x = Network; - BCN = 200 + network ie: BCN1 = 10.201.y.z BCN2 = 10.202.y.z - SN = 100 + network ie: SN1 = 10.101.y.z SN2 = 10.102.y.z y = Device Type. Foudation Pack; 1. Switches 2. PDUs 3. UPSes 4. Switches 5. Strikers 6. Striker IPMI (BCN only) Anvil! systems; 1st - 10 = Node IP 11 = Node IPMI 2nd - 12 = Node IP 13 = Node IPMI 3rd - 14 = Node IP 15 = Node IPMI N... z = Device Sequence - Foundation pack devices are simple sequence - Anvils; .1 = node 1, .2 = node 2, .3 = DR host =cut # This is a rudimentary function for generating default Striker IPs. sub generate_ip { my ($anvil, $network, $network_sequence, $device_sequence) = @_; my $debug = 3; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { network => $network, network_sequence => $network_sequence, device_sequence => $device_sequence, }}); # An empty string is returned if we can't make a sane guess at what should be set. my $ip = ""; # The subnet's second octet will be '+X' where 'X' is the sequence. my $default_ip = $anvil->data->{defaults}{network}{$network}{subnet}; my $default_netmark = $anvil->data->{defaults}{network}{$network}{netmask}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { default_ip => $default_ip, default_netmark => $default_netmark, }}); if (($anvil->Validate->is_ipv4({ip => $default_ip})) && ($anvil->Validate->is_ipv4({ip => $default_netmark}))) { # Valid values. my ($ip_octet1, $ip_octet2, $ip_octet3, $ip_octet4) = (split/\./, $default_ip); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ip_octet1 => $ip_octet1, ip_octet2 => $ip_octet2, ip_octet3 => $ip_octet3, ip_octet4 => $ip_octet4, }}); if ($default_netmark eq "255.255.0.0") { # We can work with this. if ($network ne "ifn") { $ip_octet2 += $network_sequence; } $ip_octet3 = $anvil->data->{defaults}{network}{$network}{striker_octet3}; $ip_octet4 = $device_sequence; $ip = $ip_octet1.".".$ip_octet2.".".$ip_octet3.".".$ip_octet4; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "s1:ip_octet2" => $ip_octet2, "s2:ip_octet3" => $ip_octet3, "s3:ip_octet4" => $ip_octet4, "s4:ip" => $ip, }}); } } else { # Something wrong with our defaults. $ip = "#!error!#"; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ip => $ip }}); return($ip); }