* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair.

* Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered.
* Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database.
* Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host.
* Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper.
* In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead.
* Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table.
* Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 5 years ago
parent 6f74ca376b
commit badaa39b7a
  1. 18
      Anvil/Tools.pm
  2. 23
      Anvil/Tools/Database.pm
  3. 93
      Anvil/Tools/Get.pm
  4. 2
      Anvil/Tools/Storage.pm
  5. 210
      Anvil/Tools/Striker.pm
  6. 255
      cgi-bin/striker
  7. 129
      html/skins/alteeve/anvil.html
  8. BIN
      html/skins/alteeve/images/configure-network.png
  9. 2
      html/skins/alteeve/images/sources.txt
  10. 1
      html/skins/alteeve/pxe.txt
  11. 5
      html/skins/alteeve/striker.html
  12. 1
      rpm/SPECS/anvil.spec
  13. 1
      share/anvil.sql
  14. 18
      share/words.xml
  15. 2
      tools/anvil-daemon
  16. 141
      tools/striker-get-peer-data
  17. 51
      tools/striker-initialize-host

@ -48,6 +48,7 @@ use Anvil::Tools::Job;
use Anvil::Tools::Log;
use Anvil::Tools::Remote;
use Anvil::Tools::Server;
use Anvil::Tools::Striker;
use Anvil::Tools::Storage;
use Anvil::Tools::System;
use Anvil::Tools::Template;
@ -127,6 +128,7 @@ sub new
JOB => Anvil::Tools::Job->new(),
REMOTE => Anvil::Tools::Remote->new(),
SERVER => Anvil::Tools::Server->new(),
STRIKER => Anvil::Tools::Striker->new(),
STORAGE => Anvil::Tools::Storage->new(),
SYSTEM => Anvil::Tools::System->new(),
TEMPLATE => Anvil::Tools::Template->new(),
@ -166,6 +168,7 @@ sub new
$anvil->Job->parent($anvil);
$anvil->Remote->parent($anvil);
$anvil->Server->parent($anvil);
$anvil->Striker->parent($anvil);
$anvil->Storage->parent($anvil);
$anvil->System->parent($anvil);
$anvil->Template->parent($anvil);
@ -535,6 +538,18 @@ sub Server
return ($self->{HANDLE}{SERVER});
}
=head2 Striker
Access the C<Striker.pm> methods via 'C<< $anvil->Striker->method >>'.
=cut
sub Striker
{
my $self = shift;
return ($self->{HANDLE}{STRIKER});
}
=head2 Storage
Access the C<Storage.pm> methods via 'C<< $anvil->Storage->method >>'.
@ -975,6 +990,7 @@ sub _set_paths
'redhat-release' => "/etc/redhat-release",
},
directories => {
anvil => "/etc/anvil",
backups => "/root/anvil-backups",
'cgi-bin' => "/var/www/cgi-bin",
firewalld_services => "/usr/lib/firewalld/services",
@ -1014,6 +1030,7 @@ sub _set_paths
'anvil-update-system' => "/usr/sbin/anvil-update-system",
bridge => "/usr/sbin/bridge",
'call_striker-get-peer-data' => "/usr/sbin/call_striker-get-peer-data",
cat => "/usr/bin/cat",
'chmod' => "/usr/bin/chmod",
'chown' => "/usr/bin/chown",
cibadmin => "/usr/sbin/cibadmin",
@ -1076,6 +1093,7 @@ sub _set_paths
'striker-prep-database' => "/usr/sbin/striker-prep-database",
stty => "/usr/bin/stty",
su => "/usr/bin/su",
'subscription-manager' => "/usr/sbin/subscription-manager",
systemctl => "/usr/bin/systemctl",
timeout => "/usr/bin/timeout",
touch => "/usr/bin/touch",

@ -867,8 +867,9 @@ sub connect
password => $password,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
remote_version => $remote_version,
local_version => $local_version,
's1:host' => $host,
's2:remote_version' => $remote_version,
's3:local_version' => $local_version,
}});
# TODO: Periodically, we fail to get the remote version. For now, we proceed if
# everything else is OK. Might be better to pause a re-try... To be determined.
@ -5925,6 +5926,9 @@ sub resync_databases
# 'sys::database::check_tables'
foreach my $table (@{$anvil->data->{sys}{database}{check_tables}})
{
# We don't sync 'states' as it's transient and sometimes per-DB.
next if $table eq "states";
# If the 'schema' is 'public', there is no table in the history schema. If there is a host
# column, the resync will be restricted to entries from this host uuid.
my $schema = $anvil->data->{sys}{database}{table}{$table}{schema};
@ -6095,12 +6099,12 @@ sub resync_databases
die $THIS_FILE." ".__LINE__."; This row's modified_date wasn't the first column returned in query: [$query]\n" if not $modified_date;
die $THIS_FILE." ".__LINE__."; This row's UUID column: [$uuid_column] wasn't the second column returned in query: [$query]\n" if not $row_uuid;
# Record this in the unified and local hashes. # This table isn't restricted to given hosts.
# Record this in the unified and local hashes.
$anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name} = $column_value;
$anvil->data->{db_data}{$uuid}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name} = $column_value;
$anvil->data->{db_data}{$uuid}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name} = $column_value;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"db_data::unified::${table}::modified_date::${modified_date}::${uuid_column}::${row_uuid}::${column_name}" => $anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name},
"db_data::${uuid}::${table}::modified_date::${modified_date}::${uuid_column}::${row_uuid}::${column_name}" => $anvil->data->{db_data}{$uuid}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name},
"db_data::${uuid}::${table}::modified_date::${modified_date}::${uuid_column}::${row_uuid}::${column_name}" => $anvil->data->{db_data}{$uuid}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name},
}});
}
}
@ -6110,7 +6114,6 @@ sub resync_databases
foreach my $modified_date (sort {$b cmp $a} keys %{$anvil->data->{db_data}{unified}{$table}{modified_date}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { modified_date => $modified_date }});
foreach my $row_uuid (sort {$a cmp $b} keys %{$anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { row_uuid => $row_uuid }});
@ -6281,7 +6284,7 @@ sub resync_databases
to_write => $to_write_count,
host_name => $anvil->Get->host_name({host_uuid => $uuid}),
}});
$anvil->Database->write({uuid => $uuid, query => $merged, source => $THIS_FILE, line => __LINE__});
$anvil->Database->write({debug => $debug, uuid => $uuid, query => $merged, source => $THIS_FILE, line => __LINE__});
undef $merged;
}
}
@ -6714,6 +6717,9 @@ sub _find_behind_databases
# behind and a resync will be needed.
foreach my $table (@{$anvil->data->{sys}{database}{check_tables}})
{
# We don't sync 'states' as it's transient and sometimes per-DB.
next if $table eq "states";
# Does this table exist yet?
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
@ -6811,6 +6817,9 @@ ORDER BY
# databases. If it has, trigger a resync.
foreach my $table (sort {$a cmp $b} keys %{$anvil->data->{sys}{database}{table}})
{
# We don't sync 'states' as it's transient and sometimes per-DB.
next if $table eq "states";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"sys::database::table::${table}::last_updated" => $anvil->data->{sys}{database}{table}{$table}{last_updated},
"sys::database::table::${table}::row_count" => $anvil->data->{sys}{database}{table}{$table}{row_count},

@ -138,8 +138,27 @@ sub anvil_version
# Is this a local call or a remote call?
if (($target) && ($target ne "local") && ($target ne $anvil->_hostname) && ($target ne $anvil->_short_hostname))
{
# Remote call.
my $shell_call = "
# Remote call. If we're running as the apache user, we need to read the cached version for
# the peer. otherwise, after we read the version, will write the cached version.
my $user = getpwuid($<);
my $cache_file = $anvil->data->{path}{directories}{anvil}."/anvil.".$target.".version";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
cache_file => $cache_file,
user => $user,
}});
if ($user eq "apache")
{
# Try to read the local cached version.
if (-e $cache_file)
{
# Read it in.
$version = $anvil->Storage->read_file({file => $cache_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { version => $version }});
}
}
else
{
my $shell_call = "
if [ -e ".$anvil->data->{path}{configs}{'anvil.version'}." ];
then
cat ".$anvil->data->{path}{configs}{'anvil.version'}.";
@ -147,22 +166,60 @@ else
echo 0;
fi;
";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
my ($output, $error, $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
error => $error,
output => $output,
}});
$version = defined $output ? $output : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { version => $version }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
my ($output, $error, $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
error => $error,
output => $output,
}});
$version = defined $output ? $output : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { version => $version }});
# Create/Update the cache file.
if ($version)
{
my $update_cache = 1;
my $old_version = "";
if (-e $cache_file)
{
$old_version = $anvil->Storage->read_file({file => $cache_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_version => $old_version }});
if ($old_version eq $version)
{
# No need to update
$update_cache = 0;
}
else
{
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { update_cache => $update_cache }});
if ($update_cache)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0437", variables => {
target => $target,
file => $cache_file,
}});
$anvil->Storage->write_file({
debug => $debug,
file => $cache_file,
body => $version,
mode => "0666",
overwrite => 1,
});
}
}
}
}
else
{

@ -1638,7 +1638,7 @@ sub read_file
{
# Something went wrong...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0162", variables => {
remote_file => $remote_user."\@".$target.$file,
remote_file => $remote_user."\@".$target.":".$file,
local_file => $temp_file,
}});
return("!!error!!");

@ -0,0 +1,210 @@
package Anvil::Tools::Striker;
#
# This module contains methods used to handle common Striker (webUI) tasks.
#
use strict;
use warnings;
use Data::Dumper;
use Scalar::Util qw(weaken isweak);
our $VERSION = "3.0.0";
my $THIS_FILE = "Striker.pm";
### Methods;
#
=pod
=encoding utf8
=head1 NAME
Anvil::Tools::Striker
Provides all methods related to the Striker WebUI.
=head1 SYNOPSIS
use Anvil::Tools;
# Get a common object handle on all Anvil::Tools modules.
my $anvil = Anvil::Tools->new();
# Access to methods using '$anvil->Striker->X'.
#
# Example using 'system_call()';
=head1 METHODS
Methods in this module;
=cut
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
return ($self);
}
# Get a handle on the Anvil::Tools object. I know that technically that is a sibling module, but it makes more
# sense in this case to think of it as a parent.
sub parent
{
my $self = shift;
my $parent = shift;
$self->{HANDLE}{TOOLS} = $parent if $parent;
# Defend against memory leads. See Scalar::Util'.
if (not isweak($self->{HANDLE}{TOOLS}))
{
weaken($self->{HANDLE}{TOOLS});
}
return ($self->{HANDLE}{TOOLS});
}
#############################################################################################################
# Public methods #
#############################################################################################################
=head2 get_peer_data
This calls the C<< call_striker-get-peer-data >> program to try to connect to the target (as C<< root >>). If successful, it will return the target's host UUID (either by reading C<< /etc/anvil/host.uuid >> if it exists, or using C<< dmidecode >> if not).
This method will return a string variable with C<< 1 >> if the peer was reached, or C<< 0 >> if it was not. It will also return a hash reference containing the collected data.
my ($connected, $data) = $anvil->Striker->get_peer_data({target => 10.255.1.218, password => "Initial1"});
if ($connected)
{
print "Hostname: [".$data->{host_name}."], host UUID: [".$data->{host_uuid}."]\n";
}
Parameters;
=head3 password (required)
This is the target machine's C<< root >> password.
=head3 port (optional, default 22)
This is the TCP port to use when connecting to the target
=head3 target (required, IPv4 address)
This is the current IP address of the target machine.
=cut
sub get_peer_data
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Striker->get_peer_data()" }});
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
target => $target,
password => $anvil->Log->is_secure($password),
port => $port,
}});
# Store the password.
my $connected = 0;
my $data = {
host_uuid => "",
host_name => "",
host_os => "",
os_registered => "",
};
if (not $target)
{
# No target...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Striker->get_peer_data()", parameter => "target" }});
return($connected, $data);
}
my $state_uuid = $anvil->Database->insert_or_update_states({
debug => $debug,
file => $THIS_FILE,
line => __LINE__,
state_name => "peer::".$target."::password",
state_note => $password,
uuid => $anvil->data->{sys}{host_uuid}, # Only write to our DB, no reason to store elsewhere
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { state_uuid => $state_uuid }});
my ($output, $error, $return_code) = $anvil->System->call({
debug => $debug,
shell_call => $anvil->data->{path}{exe}{'call_striker-get-peer-data'}." --target root\@".$target.":".$port." --state-uuid ".$state_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
error => $error,
return_code => $return_code,
}});
# Pull out the details
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($line =~ /connected=(.*)$/)
{
# We collect this, but apparently not for any real reason...
$connected = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { connected => $connected }});
}
if ($line =~ /host_name=(.*)$/)
{
# We collect this, but apparently not for any real reason...
$data->{host_name} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'data->{host_name}' => $data->{host_name} }});
}
if ($line =~ /host_uuid=(.*)$/)
{
$data->{host_uuid} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'data->{host_uuid}' => $data->{host_uuid} }});
}
if ($line =~ /host_os=(.*)$/)
{
$data->{host_os} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'data->{host_os}' => $data->{host_os} }});
}
if ($line =~ /os_registered=(.*)$/)
{
$data->{os_registered} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'data->{os_registered}' => $data->{os_registered} }});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
connected => $connected,
'data->{host_name}' => $data->{host_name},
'data->{host_uuid}' => $data->{host_uuid},
'data->{host_os}' => $data->{host_os},
'data->{os_registered}' => $data->{os_registered},
}});
# Make sure the database entry is gone (striker-get-peer-data should have removed it, but lets be safe).
my $query = "DELETE FROM states WHERE state_name = ".$anvil->Database->quote("peer::".$target."::password").";";
$anvil->Database->write({uuid => $anvil->data->{sys}{host_uuid}, debug => 3, query => $query, source => $THIS_FILE, line => __LINE__});
if (not $anvil->Validate->is_uuid({uuid => $data->{host_uuid}}))
{
$data->{host_uuid} = "";
}
return($connected, $data);
}
1;

@ -57,8 +57,8 @@ if (not -e $anvil->data->{path}{data}{host_uuid})
print_and_exit($anvil);
}
$anvil->Database->connect();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"});
$anvil->Database->connect({debug => 3});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"});
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, exit.
@ -257,7 +257,7 @@ sub process_task
$anvil->data->{cgi}{login}{value} = "" if not defined $anvil->data->{cgi}{login}{value};
$anvil->data->{cgi}{logout}{value} = "" if not defined $anvil->data->{cgi}{logout}{value};
$anvil->data->{cgi}{save}{value} = "" if not defined $anvil->data->{cgi}{save}{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"cgi::login::value" => $anvil->data->{cgi}{login}{value},
"cgi::logout::value" => $anvil->data->{cgi}{logout}{value},
"cgi::save::value" => $anvil->data->{cgi}{save}{value},
@ -503,11 +503,19 @@ sub process_anvil_menu
$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->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'cgi::task::value' => $anvil->data->{cgi}{task}{value},
'cgi::action::value' => $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 "configure-network")
{
#process_configure_network($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "manifest")
{
#process_manifest_page($anvil);
@ -529,25 +537,181 @@ sub process_prep_host_page
{
my ($anvil) = @_;
my $ip_address = defined $anvil->data->{cgi}{ip_address}{value} ? $anvil->data->{cgi}{ip_address}{value} : "";
my $rh_user = defined $anvil->data->{cgi}{rh_user}{value} ? $anvil->data->{cgi}{rh_user}{value} : "";
my $rh_password = defined $anvil->data->{cgi}{rh_password}{value} ? $anvil->data->{cgi}{rh_password}{value} : "";
my $node_checked = "checked";
my $dr_checked = "";
my $form_body = $anvil->Template->get({file => "anvil.html", name => "host-setup-menu1", variables => {
ip_address => $ip_address,
node_checked => $node_checked,
dr_checked => $dr_checked,
my $host_ip_address = defined $anvil->data->{cgi}{host_ip_address}{value} ? $anvil->data->{cgi}{host_ip_address}{value} : "";
my $host_password = defined $anvil->data->{cgi}{host_password}{value} ? $anvil->data->{cgi}{host_password}{value} : "";
my $rh_user = defined $anvil->data->{cgi}{rh_user}{value} ? $anvil->data->{cgi}{rh_user}{value} : "";
my $rh_password = defined $anvil->data->{cgi}{rh_password}{value} ? $anvil->data->{cgi}{rh_password}{value} : "";
my $type = defined $anvil->data->{cgi}{type}{value} ? $anvil->data->{cgi}{type}{value} : "";
my $connect = defined $anvil->data->{cgi}{'connect'}{value} ? $anvil->data->{cgi}{'connect'}{value} : "";
my $confirm = defined $anvil->data->{cgi}{confirm}{value} ? $anvil->data->{cgi}{confirm}{value} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_ip_address => $host_ip_address,
host_password => $anvil->Log->is_secure($host_password),
rh_user => $rh_user,
rh_password => $anvil->Log->is_secure($rh_password),
type => $type,
'connect' => $connect,
confirm => $confirm,
}});
$anvil->data->{form}{refresh_link} = "?anvil=true&task=prep-host";
$anvil->data->{form}{back_link} = "?anvil=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 => "prep-host-main", variables => {
form => $form_body,
# Is the IP or hostname valid?
my $ssh_port = 22;
if ($host_ip_address =~ /:(\d+)$/)
{
$ssh_port = $1;
$host_ip_address =~ s/:(\d+)$//;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
ssh_port => $ssh_port,
host_ip_address => $host_ip_address,
}});
# If we've been passed 'connect', verify we can connect.
if (($connect) && ($confirm))
{
# Save the job
my $job_command = $anvil->data->{path}{exe}{'striker-initialize-host'}." --target root\@".$host_ip_address.":".$ssh_port." --type".$type;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
job_command => $job_command,
password => $anvil->Log->is_secure($host_password),
}});
# Store the peer's password as the job data
my $job_data = "password=".$host_password;
$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."?anvil=true&task=prep-host",
title => "#!string!striker_0044!#",
description => "#!string!striker_0129!#",
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }});
}
elsif ($connect)
{
if (not $anvil->Validate->is_ipv4({ip => $host_ip_address}))
{
# Bad IP
$anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0010"}) }});
}
elsif (($ssh_port < 1) or ($ssh_port > 65535))
{
# Bad port
$anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0011"}) }});
}
else
{
# Can we connect?
my $target_host_name = "";
my $target_host_uuid = "";
my ($connected, $data) = $anvil->Striker->get_peer_data({
debug => 3,
target => $host_ip_address,
password => $host_password,
port => $ssh_port,
});
foreach my $key (sort {$a cmp $b} keys %{$data})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "data->{$key}" => $data->{$key} }});
}
if ($data->{host_name})
{
# We collect this, but apparently not for any real reason...
$target_host_name = $data->{host_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { target_host_name => $target_host_name }});
}
if ($data->{host_uuid})
{
$target_host_uuid = $data->{host_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { target_host_uuid => $target_host_uuid }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
target_host_name => $target_host_name,
target_host_uuid => $target_host_uuid,
}});
# If the target is RHEL and it is not registered, offer the user to provide the RH user and password.
my $rh_template = "";
if (($data->{host_os} =~ /^rhel/) && ($data->{os_registered} ne "yes"))
{
$rh_template = $anvil->Template->get({file => "anvil.html", name => "host-setup-redhat", variables => {
rh_user => $rh_user,
rh_password => $rh_password,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { rh_template => $rh_template }});
}
if (not $connected)
{
$anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0012"}) }});
}
elsif (not $target_host_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 => $target_host_uuid }}) }});
}
else
{
# Connected! Ask th euser to confirm.
$anvil->data->{form}{body} = $anvil->Template->get({file => "anvil.html", name => "confirm-initialize-host", variables => {
'package' => $type eq "dr" ? "anvil-dr" : "anvil-node",
redhat => $rh_template,
access => "root\@".$host_ip_address.":".$ssh_port,
host_name => $target_host_name,
host_uuid => $target_host_uuid,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }});
}
}
}
if ((not $anvil->data->{form}{body}) or ($anvil->data->{form}{error_massage}))
{
my $node_checked = "checked";
my $dr_checked = "";
if ($type eq "dr")
{
$node_checked = "";
$dr_checked = "checked";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
node_checked => $node_checked,
dr_checked => $dr_checked,
}});
my $form_body = $anvil->Template->get({file => "anvil.html", name => "host-setup-menu1", variables => {
host_ip_address => $host_ip_address,
node_checked => $node_checked,
dr_checked => $dr_checked,
}});
$anvil->data->{form}{refresh_link} = "?anvil=true&task=prep-host";
$anvil->data->{form}{back_link} = "?anvil=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 => "prep-host-main", variables => {
form => $form_body,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'form::body' => $anvil->data->{form}{body} }});
}
return(0);
}
@ -1057,52 +1221,29 @@ sub add_sync_peer
}
else
{
my $state_uuid = $anvil->Database->insert_or_update_states({
debug => 3,
file => $THIS_FILE,
line => __LINE__,
state_name => "peer::".$host."::password",
state_note => $password,
uuid => $anvil->data->{sys}{host_uuid}, # Only write to our DB, no reason to store elsewhere
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { state_uuid => $state_uuid }});
my ($output, $error, $return_code) = $anvil->System->call({
debug => 2,
shell_call => $anvil->data->{path}{exe}{'call_striker-get-peer-data'}." --target root\@".$host.":".$ssh_port." --state-uuid ".$state_uuid,
my ($connected, $data) = $anvil->Striker->get_peer_data({
debug => 2,
target => $host,
password => $password,
port => $ssh_port,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
error => $error,
return_code => $return_code,
}});
# Pull out the details
foreach my $line (split/\n/, $output)
if ($data->{host_name})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($line =~ /host_name=(.*)$/)
{
# We collect this, but apparently not for any real reason...
$peer_host_name = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_host_name => $peer_host_name }});
}
if ($line =~ /host_uuid=(.*)$/)
{
$peer_host_uuid = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_host_uuid => $peer_host_uuid }});
}
# We collect this, but apparently not for any real reason...
$peer_host_name = $data->{host_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_host_name => $peer_host_name }});
}
if ($data->{host_uuid})
{
$peer_host_uuid = $data->{host_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_host_uuid => $peer_host_uuid }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
peer_host_name => $peer_host_name,
peer_host_uuid => $peer_host_uuid,
}});
# Make sure the database entry is gone (striker-get-peer-data should have removed it, but lets be safe).
my $query = "DELETE FROM states WHERE state_name = ".$anvil->Database->quote("peer::".$host."::password").";";
$anvil->Database->write({uuid => $anvil->data->{sys}{host_uuid}, debug => 2, query => $query, source => $THIS_FILE, line => __LINE__});
if (not $anvil->Validate->is_uuid({uuid => $peer_host_uuid}))
if ((not $connected) or (not $peer_host_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_host_uuid }}) }});
}
@ -1111,7 +1252,7 @@ sub add_sync_peer
$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
# See which of our IPs match theirs. If the peer is a hostname, first, find the IP.
$use_ip = $anvil->System->find_matching_ip({host => $host});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_ip => $use_ip }});

@ -20,7 +20,15 @@
<a href="?anvil=true&task=prep-host"><img src="#!data!skin::url!#/images/prep-host_icon.png" class="top_icon" ></a>
</td>
<td class="main_option">
<a href="?anvil=true&task=sync">#!string!striker_0116!#</a>
<a href="?anvil=true&task=prep-host">#!string!striker_0116!#</a>
</td>
</tr>
<tr>
<td class="main_option_icon">
<a href="?anvil=true&task=prep-network"><img src="#!data!skin::url!#/images/configure-network.png" class="top_icon" ></a>
</td>
<td class="main_option">
<a href="?anvil=true&task=prep-network">#!string!striker_0130!#</a>
</td>
</tr>
<tr>
@ -66,7 +74,7 @@
#!variable!form!#
</tr>
<input type="hidden" name="anvil" id="anvil" value="1">
<input type="hidden" name="task" id="prep-host" value="1">
<input type="hidden" name="task" id="task" value="prep-host">
</form>
</table>
<!-- end prep-host-main -->
@ -76,13 +84,13 @@
<div id="host-setup">
<tr>
<td colspan="3">
#!string!message_0146!#"
#!string!message_0146!#
</td>
</tr>
<tr>
<td colspan="3">
<input type="radio" name="type" value="node" id="node" class="radio" #!variable!node_checked!#> #!string!message_0116!#" <br />
<input type="radio" name="type" value="dr" id="dr" class="radio" #!variable!dr_checked!#> #!string!message_0117!#"
<input type="radio" name="type" value="node" id="node" class="radio" #!variable!node_checked!#> #!string!message_0116!# <br />
<input type="radio" name="type" value="dr" id="dr" class="radio" #!variable!dr_checked!#> #!string!message_0117!#
</td>
</tr>
<tr>
@ -92,12 +100,12 @@
</tr>
<tr>
<td colspan="3">
#!string!message_0147!#"
#!string!message_0147!#
</td>
</tr>
<tr>
<td>
<input type="text" name="host_ip_address" id="host_ip_address" value="#!variable!ip_address!#" placeholder="#!string!striker_0024!#" />
<input type="text" name="host_ip_address" id="host_ip_address" value="#!variable!host_ip_address!#" placeholder="#!string!striker_0024!#" />
</td>
<td>
&nbsp;
@ -106,7 +114,7 @@
<!--
This combination of input type text, disk characters and autocomplete=off prevents browsers from pre-filling password and offering to save passwords.
-->
<input type="text" name="host_password" id="host_password" value="#!variable!password!#" style="text-security:disc; -webkit-text-security:disc;" autocomplete="off" placeholder="#!string!striker_0051!#" />
<input type="text" name="host_password" id="host_password" value="#!variable!password!#" style="text-security:disc; -webkit-text-security:disc;" autocomplete="off" placeholder="#!string!striker_0123!#" />
</td>
</tr>
<tr>
@ -116,30 +124,113 @@
</tr>
<tr>
<td colspan="3">
#!string!message_0148!#"
<input type="submit" name="connect" id="connect" class="button" value="#!string!striker_0049!#">
</td>
</tr>
</div>
</table>
<!-- end host-setup-menu1 -->
<!-- start confirm-initialize-host -->
<table align="center">
<form name="confirm-new-peer" action="" method="post">
<tr>
<td class="menu_title">
#!string!striker_0078!#
</td>
</tr>
<tr>
<td>
<input type="text" name="rh_user" id="rh_user" value="#!variable!rh_user!#" placeholder="#!string!message_0144!#" />
<td class="menu_details">
#!string!striker_0124!#
</td>
</tr>
<tr>
<td>
&nbsp;
</td>
</tr>
#!variable!redhat!#
<tr>
<td>
<input type="text" name="rh_password" id="rh_password" value="#!variable!rh_password!#" style="text-security:disc; -webkit-text-security:disc;" autocomplete="off" placeholder="#!string!message_0145!#" />
<table align="center" class="data_table">
<div id="confirm-new-peer">
<tr>
<td class="top_padded_cell">
#!string!striker_0125!#:
</td>
<td class="top_padded_cell" class="fixed_width">
#!variable!access!#
</td>
</tr>
<tr>
<td class="padded_cell">
#!string!striker_0126!#:
</td>
<td class="padded_cell" class="fixed_width">
#!variable!host_name!#
</td>
</tr>
<tr>
<td class="padded_cell">
#!string!striker_0127!#:
</td>
<td class="padded_cell" class="fixed_width">
#!variable!host_uuid!#
</td>
</tr>
<tr>
<td class="button_cell" style="text-align: left;">
<a href="/cgi-bin/striker?anvil=true&task=prep-host&host_ip_address=#!data!cgi::host_ip_address::value!#" class="button">#!string!striker_0098!#</a>
</td>
<td class="button_cell" style="text-align: right;">
<input type="submit" name="confirm" id="confirm" class="button" value="#!string!striker_0128!#">
</td>
</tr>
<input type="hidden" name="anvil" id="anvil" value="#!data!cgi::anvil::value!#">
<input type="hidden" name="connect" id="connect" value="#!data!cgi::connect::value!#">
<input type="hidden" name="host_ip_address" id="host_ip_address" value="#!data!cgi::host_ip_address::value!#">
<input type="hidden" name="host_password" id="host_password" value="#!data!cgi::host_password::value!#">
<input type="hidden" name="type" id="type" value="#!data!cgi::striker::value!#">
<input type="hidden" name="task" id="task" value="#!data!cgi::task::value!#">
</div>
</table>
</td>
</tr>
</form>
</table>
<!-- end confirm-initialize-host -->
<!-- start host-setup-redhat -->
<tr>
<td colspan="3">
&nbsp;
<td>
<table align="center" width="90%">
<tr>
<td colspan="3">
#!string!message_0148!#
</td>
</tr>
<tr>
<td>
<input type="text" name="rh_user" id="rh_user" value="#!variable!rh_user!#" placeholder="#!string!message_0144!#" />
</td>
<td>
&nbsp;
</td>
<td>
<input type="text" name="rh_password" id="rh_password" value="#!variable!rh_password!#" style="text-security:disc; -webkit-text-security:disc;" autocomplete="off" placeholder="#!string!message_0145!#" />
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td colspan="3">
<input type="submit" name="connect" id="connect" class="button" value="#!string!striker_0049!#">
<td>
&nbsp;
</td>
</tr>
</div>
</table>
<!-- end host-setup-menu1 -->
<!-- end host-setup-redhat -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@ -30,7 +30,7 @@ Refresh by Denis Klyuchnikov from the Noun Project (https://thenounproject.com/t
- refresh.png
configure by Gregor Cresnar from the Noun Project (https://thenounproject.com/term/configure/485294/)
- striker_configure.png
- striker_configure.png / configure-network.png
customize by Trusted Icons from the Noun Project (https://thenounproject.com/term/customize/538026/)
- configure_icon.png

@ -73,6 +73,7 @@ selinux --permissive
%packages
@^minimal-environment
alteeve-el8-repo
rsync
%end

@ -198,10 +198,7 @@
&nbsp;
</td>
<td>
<!--
This combination of input type text, disk characters and autocomplete=off prevents browsers from pre-filling password and offering to save passwords.
-->
<input type="text" name="password" id="password" value="#!variable!password!#" style="text-security:disc; -webkit-text-security:disc;" autocomplete="off" placeholder="#!string!striker_0051!#" />
<input type="password" name="password" id="password" value="#!variable!password!#" placeholder="#!string!striker_0051!#" />
</td>
<td>
&nbsp;

@ -84,6 +84,7 @@ Requires: dhcp-server
Requires: firefox
Requires: gcc
Requires: gdm
Requires: gnome-terminal
Requires: httpd
Requires: kernel-core
Requires: nmap

@ -1410,6 +1410,7 @@ CREATE TABLE alert_sent (
ALTER TABLE updated OWNER TO admin;
-- NOTE: We don't resync this table! It's meant to be a transient data store, sometimes on a per-DB basis
-- This stores state information, like the whether migrations are happening and so on.
CREATE TABLE states (
state_uuid uuid not null primary key,

@ -218,7 +218,7 @@ NOTE: Please be patient!
<!-- Above here are strings used in the kickstart scripts. Be sure to test kickstart installation after changing / translation between 'message_0103' and 'message_0114'. -->
<key name="message_0115">Striker Dashboard</key>
<key name="message_0116">Anvil! Node</key>
<key name="message_0117">Dsaster Recovery (DR) Host</key>
<key name="message_0117">Disaster Recovery (DR) Host</key>
<key name="message_0118">Regenerating the source repository metadata.</key>
<key name="message_0119">[ Error ] - The comps.xml file: [#!variable!comps_xml!#] was not found. This provides package group information required for Install Target guests. Is the 'anvil-striker-extra' package installed?</key>
<key name="message_0120">
@ -252,7 +252,7 @@ About to try to download aproximately: [#!variable!packages!#] packages needed t
<key name="message_0145">Red Hat password</key>
<key name="message_0146">What kind of machine will this host be?</key>
<key name="message_0147"><![CDATA[What is the host's <b>current</b> IP address and password?]]></key>
<key name="message_0148"><![CDATA[(Optional); If this is a RHEL 8 host and has not yet been subscribed,<br />you can enter your Red Hat subscription credentials below.<br />If needed, the host will be subscribed during setup.]]></key>
<key name="message_0148"><![CDATA[This is a RHEL host and has not yet been subscribed.<br />You can enter your Red Hat subscription credentials below.<br />The host will be subscribed during setup.]]></key>
<!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key>
@ -759,6 +759,7 @@ Failed to promote the DRBD resource: [#!variable!resource!#] primary. Expected a
<key name="log_0434">- The DRBD resource: [#!variable!resource!#] is in the role: [#!variable!role!#] already, no need to bring it up.</key>
<key name="log_0435">Program: [#!variable!program!#] running as the real user: [#!variable!real_user!# (#!variable!real_uid!#)] and effective user: [#!variable!effective_user!# (#!variable!effective_uid!#)].</key>
<key name="log_0436">The setuid c-wrapper: [#!variable!wrapper!#] already exists, no need to create it.</key>
<key name="log_0437">The anvil version cache file: [#!variable!file!#] for: [#!variable!target!#] needs to be created/updated.</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key>
@ -913,13 +914,21 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="striker_0113">Anvil! Configuration and Management.</key>
<key name="striker_0114">Create a new Anvil! system.</key>
<key name="striker_0115">Any running jobs, or jobs that have ended recently, are displayed below.</key>
<key name="striker_0116">Prepare a host for use as an Anvil! node or disaster recovery target.</key>
<key name="striker_0116">Initialize an Anvil! node or disaster recovery target.</key>
<key name="striker_0117">Initial host configuration.</key>
<key name="striker_0118">Prepare a new machine for use as an Anvil! node or DR (disaster recovery) host. This process will setup the repository, install the appropriate anvil packages and link it to the Anvil! databases on the Strikers you choose.</key>
<key name="striker_0119">Anvil! File Manager.</key>
<key name="striker_0120">Saving File...</key>
<key name="striker_0121">Prepare Node or DR Host</key>
<key name="striker_0122">Please enter the IP address and root password of the target machine you want to configure.</key>
<key name="striker_0123">'root' Password</key>
<key name="striker_0124"><![CDATA[The test connection was successful!<br />If you initialize, the target will have the Alteeve repo added and: [#!variable!package!#] installed.<br />The target will be configured to use this and our peer's databases.]]></key>
<key name="striker_0125">Initialize Host</key>
<key name="striker_0126">Current host name</key>
<key name="striker_0127">Host UUID</key>
<key name="striker_0128">Initialize</key>
<key name="striker_0129">The target will now be initialized. How long this takes will depend on how fast files can be downloaded and, when needed, how long it takes to register with Red Hat and add the needed repositories.</key>
<key name="striker_0130">Configure the network on a node or DR host.</key>
<!-- These are generally units and appended to numbers -->
<key name="suffix_0001">#!variable!number!#/sec</key>
@ -965,6 +974,9 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="striker_warning_0007">The job: [#!variable!command!#] was picked up by: [#!variable!pid!#], but that process is not running and it appears to only be: [#!variable!percent!# %] complete. Restarting the job.</key>
<key name="striker_warning_0008">Unable to find a local IP on the same subnet as the IP/host: [#!variable!host!#] given for the target. Bi-directional setup not currently possible.</key>
<key name="striker_warning_0009">The subtask request for manipulating the 'Install Target' feature is not valid. It should be 'enabled' or 'disabled'</key> <!-- NOTE: 'enabled' and 'disabled' are variable values, don't translate them. -->
<key name="striker_warning_0010">The IP address is not a valid IPv4 address</key>
<key name="striker_warning_0011">The SSH port is not a valid (usually it is 22, but it has to be between 1 ~ 65536)</key>
<key name="striker_warning_0012">Failed to log into the host. Is the IP or root user's password right?</key>
<!-- Errors -->
<key name="error_0001">There are not enough network interfaces on this machine. You have: [#!variable!interface_count!#] interface(s), and you need at least: [#!variable!required_interfaces_for_single!#] interfaces to connect to the requested networks (one for Back-Channel and one for each Internet-Facing network).</key>

@ -830,7 +830,7 @@ sub check_setuid_wrappers
if (-e $anvil->data->{path}{exe}{'call_striker-get-peer-data'})
{
# Exists, skipping.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0436", variables => { wrapper => $anvil->data->{path}{exe}{'call_striker-get-peer-data'} }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0436", variables => { wrapper => $anvil->data->{path}{exe}{'call_striker-get-peer-data'} }});
}
else
{

@ -69,14 +69,20 @@ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list
'target::password' => $anvil->Log->is_secure($anvil->data->{target}{password}),
}});
my $host_uuid = get_host_uuid($anvil);
my $host_name = get_host_name($anvil);
my ($host_uuid) = get_host_uuid($anvil);
my ($host_name) = get_host_name($anvil);
my ($host_os,, $os_registered) = get_host_os($anvil);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_uuid => $host_uuid,
host_name => $host_name,
host_uuid => $host_uuid,
host_name => $host_name,
host_os => $host_os,
os_registered => $os_registered,
}});
print "connected=1\n";
print "host_name=".$host_name."\n";
print "host_uuid=".$host_uuid."\n";
print "host_os=".$host_os."\n";
print "os_registered=".$os_registered."\n";
$anvil->nice_exit({code => 0});
@ -85,6 +91,96 @@ $anvil->nice_exit({code => 0});
# Functions #
#############################################################################################################
# This tries to read the target's OS
sub get_host_os
{
my ($anvil) = @_;
my $host_os = "unknown";
my $os_registered = "n/a";
# We can't assume that rsync exists, so we'll cat the file.
my $shell_call = "
if [ -e '".$anvil->data->{path}{data}{'redhat-release'}."' ];
then
".$anvil->data->{path}{exe}{cat}." ".$anvil->data->{path}{data}{'redhat-release'}."
fi;
";
my ($file_body, $error, $return_code) = $anvil->Remote->call({
debug => 2,
shell_call => $shell_call,
user => $anvil->data->{target}{user},
target => $anvil->data->{target}{host},
port => $anvil->data->{target}{port},
password => $anvil->data->{target}{password},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
file_body => $file_body,
error => $error,
return_code => $return_code,
}});
$file_body =~ s/\n$//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_body => $file_body }});
if ($file_body)
{
if ($file_body =~ /Red Hat .*? release (\d+\.\d+) /)
{
$host_os = "rhel ".$1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_os => $host_os }});
}
}
# If the OS is 'rhel', see if it is registered.
if ($host_os =~ /^rhel /)
{
# Is it subscribed? This isn't the best call to make, but it seems to be the one that returns
# the fastest. Return code of '0' is registered, return code of '1' is not or not verified.
my ($output, $error, $return_code) = $anvil->Remote->call({
debug => 2,
shell_call => $anvil->data->{path}{exe}{'subscription-manager'}." identity",
user => $anvil->data->{target}{user},
target => $anvil->data->{target}{host},
port => $anvil->data->{target}{port},
password => $anvil->data->{target}{password},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
output => $output,
error => $error,
return_code => $return_code,
}});
# Possible output;
=cut
system identity: 3a9065a1-625a-48e1-86f4-97a3eac4b730
name: el8-striker01.digimer.ca
org name: xxx
org ID: yyy
# rc:0
This system is not yet registered. Try 'subscription-manager register --help' for more information.
# rc:1
Network error, unable to connect to server. Please see /var/log/rhsm/rhsm.log for more information.
# rc:70
=cut
if ($return_code eq "0")
{
$os_registered = "yes";
}
elsif ($return_code eq "1")
{
$os_registered = "no";
}
elsif ($return_code eq "70")
{
# Unable to check, maybe, maybe not?
$os_registered = "offline";
}
}
return($host_os, $os_registered);
}
# This tries to read the target's UUID either via host.uuid or via dmidecode.
sub get_host_name
{
@ -122,6 +218,7 @@ sub get_host_uuid
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }});
if (not $access)
{
print "connected=0\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0069", variables => {
host => $anvil->data->{target}{user}.'@'.$anvil->data->{target}{host}.':'.$anvil->data->{target}{port},
}});
@ -129,18 +226,31 @@ sub get_host_uuid
}
# Try to read the host.uuid file on the target, if possible.
my $host_uuid = "";
my $file_body = $anvil->Storage->read_file({
debug => 3,
file => $anvil->data->{path}{data}{host_uuid},
user => $anvil->data->{target}{user},
target => $anvil->data->{target}{host},
port => $anvil->data->{target}{port},
password => $anvil->data->{target}{password},
# We can't assume that rsync exists, so we'll cat the file.
my $shell_call = "
if [ -e '".$anvil->data->{path}{data}{host_uuid}."' ];
then
".$anvil->data->{path}{exe}{cat}." ".$anvil->data->{path}{data}{host_uuid}."
fi;
";
my ($file_body, $error, $return_code) = $anvil->Remote->call({
debug => 2,
shell_call => $shell_call,
user => $anvil->data->{target}{user},
target => $anvil->data->{target}{host},
port => $anvil->data->{target}{port},
password => $anvil->data->{target}{password},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
file_body => $file_body,
error => $error,
return_code => $return_code,
}});
$file_body =~ s/\n$//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_body => $file_body }});
if (($file_body eq "!!error!!") or (not $anvil->Validate->is_uuid({uuid => $file_body})))
my $host_uuid = "";
if ((not $file_body) or (not $anvil->Validate->is_uuid({uuid => $file_body})))
{
# No good, Try dmidecode.
my ($output, $error, $return_code) = $anvil->Remote->call({
@ -172,6 +282,7 @@ sub get_host_uuid
# Exit out if I failed to get the host's UUID.
if (not $host_uuid)
{
print "connected=0\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0070", variables => {
host => $anvil->data->{target}{user}.'@'.$anvil->data->{target}{host}.':'.$anvil->data->{target}{port},
}});
@ -192,6 +303,7 @@ sub get_password
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, exit.
print "connected=0\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0067"});
$anvil->nice_exit({exit_code => 1});
}
@ -199,12 +311,13 @@ sub get_password
my $query = "SELECT state_note FROM states WHERE state_uuid=".$anvil->Database->quote($anvil->data->{switches}{'state-uuid'}).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
my $password = $anvil->Database->query({uuid => $anvil->data->{sys}{host_uuid}, debug => 2, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
my $password = $anvil->Database->query({uuid => $anvil->data->{sys}{host_uuid}, debug => 3, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$password = "" if not defined $password;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { password => $password }});
if (not $password)
{
# Well poo.
print "connected=0\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0068"});
$anvil->nice_exit({exit_code => 2});
}

@ -0,0 +1,51 @@
#!/usr/bin/perl
#
# This program is setuid 'admin' and calls a (new) peer to read its hostname and system UUID. It takes the
# target's password in via a file.
#
# Exit codes;
# 0 = Normal exit.
# 1 = No database connection.
# 2 = Password not found in the database.
# 3 = Peer not accessible
# 4 = Unable to find the peer's host UUID
# 5 =
#
use strict;
use warnings;
use Anvil::Tools;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $anvil = Anvil::Tools->new();
$anvil->Log->level({set => 2});
$anvil->Log->secure({set => 1});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
# Read switches (target ([user@]host[:port]) and the file with the target's password.
$anvil->data->{switches}{target} = "";
$anvil->data->{switches}{'state-uuid'} = "";
$anvil->Get->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'switches::target' => $anvil->data->{switches}{target},
'switches::state-uuid' => $anvil->data->{switches}{'state-uuid'},
}});
$anvil->nice_exit({code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
Loading…
Cancel
Save