Local modifications to ClusterLabs/Anvil by Alteeve
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2867 lines
122 KiB

#!/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!#" : "&nbsp;",
}});
# Display the page.
my $say_center_top_bar = "&nbsp;";
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 => "&nbsp;",
center_bottom_bar => "&nbsp;",
right_bottom_bar => "&nbsp;",
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->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},
}});
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};
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 => 3,
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 => "<input type=\"hidden\" name=\"delete\" id=\"delete\" value=\"".$uuid."\" />",
}});
}
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 ($error, $output) = $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 => { 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->[0]);
$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",
});
$peer_host = $output->[0];
$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->data->{sys}{database}{use_handle}->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->data->{sys}{database}{use_handle}->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->data->{sys}{database}{use_handle}->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);
}