* Created Network->check_internet() that, well, checks to see if a machine has access to the Internet or not.

* Moved System->is_local to Network->is_local, and System->ping to Network->ping.
* Added a check to tools/striker-get-peer-data that will report if the target has Internet access or not.
* Cleaned up the form that prompts the user to enter their Red Hat credentials.
* Updated tools/anvil-manage-keys (and related code) to no longer distinguish by user. If a target is flagged as changed, it is removed from the root and all user's known_hosts files.
* Updated Storage->write_file() and ->update_file() to accept the 'backup' parameter to control if an file that exists is backed up before being updated/replaced.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 5 years ago
parent f65a2897a7
commit 65a27ba2f8
  1. 3
      Anvil/Tools.pm
  2. 4
      Anvil/Tools/Database.pm
  3. 396
      Anvil/Tools/Network.pm
  4. 2
      Anvil/Tools/Remote.pm
  5. 87
      Anvil/Tools/Storage.pm
  6. 276
      Anvil/Tools/System.pm
  7. 33
      cgi-bin/striker
  8. 54
      html/skins/alteeve/anvil.html
  9. 27
      html/skins/alteeve/striker.html
  10. 5
      rpm/SPECS/anvil.spec
  11. 15
      share/words.xml
  12. 248
      tools/anvil-manage-keys
  13. 18
      tools/striker-get-peer-data
  14. 15
      tools/striker-initialize-host

@ -983,6 +983,9 @@ sub _set_defaults
subnet => "10.100.0.0", subnet => "10.100.0.0",
netmask => "255.255.0.0", netmask => "255.255.0.0",
}, },
test => {
domains => ["alteeve.com", "redhat.com", "google.com"],
},
}, },
template => { template => {
html => "alteeve", html => "alteeve",

@ -806,7 +806,7 @@ sub connect
if ($anvil->data->{database}{$uuid}{ping}) if ($anvil->data->{database}{$uuid}{ping})
{ {
# Can I ping? # Can I ping?
my ($pinged) = $anvil->System->ping({ my ($pinged) = $anvil->Network->ping({
debug => $debug, debug => $debug,
ping => $host, ping => $host,
count => 1, count => 1,
@ -833,7 +833,7 @@ sub connect
} }
# Before we try to connect, see if this is a local database and, if so, make sure it's setup. # Before we try to connect, see if this is a local database and, if so, make sure it's setup.
my $is_local = $anvil->System->is_local({debug => $debug, host => $host}); my $is_local = $anvil->Network->is_local({debug => $debug, host => $host});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
if ($is_local) if ($is_local)
{ {

@ -12,10 +12,13 @@ our $VERSION = "3.0.0";
my $THIS_FILE = "Network.pm"; my $THIS_FILE = "Network.pm";
### Methods; ### Methods;
# check_internet
# find_matches # find_matches
# get_ips # get_ips
# get_network # get_network
# is_local # is_local
# is_remote
# ping
=pod =pod
@ -76,6 +79,123 @@ sub parent
# Public methods # # Public methods #
############################################################################################################# #############################################################################################################
=head2 check_internet
This method tries to connect to the internet. If successful, C<< 1 >> is returned. Otherwise, C<< 0 >> is returned.
Paramters;
=head3 domains (optional, default 'defaults::network::test::domains')
If passed an array reference, the domains in the array will be checked in the order they are found in the array. As soon as any respond to a ping, the check exits and C<< 1 >> is returned.
If not passed, C<< defaults::network::test::domains >> are used.
=head3 password (optional)
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made (though this will usually fail).
=head3 port (optional, default 22)
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 remote_user (optional)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=head3 target (optional)
If set, the file will be read from the target machine. This must be either an IP address or a resolvable host name.
=head3 tries (optional, default 3)
This is how many times we'll try to ping the target. Pings are done one ping at a time, so that if the first ping succeeds, the test can exit quickly and return success.
=cut
sub check_internet
{
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 => "Network->find_matches()" }});
my $access = 0;
my $domains = defined $parameter->{domains} ? $parameter->{domains} : $anvil->data->{defaults}{network}{test}{domains};
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "local";
my $tries = defined $parameter->{tries} ? $parameter->{tries} : 3;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
domains => $domains,
password => $anvil->Log->is_secure($password),
port => $port,
remote_user => $remote_user,
target => $target,
tries => $tries,
}});
if (ref($domains) eq "ARRAY")
{
my $domain_count = @{$domains};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { domain_count => $domain_count }});
if (not $domain_count)
{
# Array is empty
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0440", variables => { name => "domain" }});
return($access);
}
}
else
{
# Domains isn't an array.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0218", variables => { name => "domain", value => $domains }});
return($access);
}
if (($tries =~ /\D/) or ($tries < 1))
{
# Invalid
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0441", variables => { name => "tries", value => $tries }});
return($access);
}
foreach my $domain (@{$domains})
{
# Is the domain valid?
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { domain => $domain }});
if ((not $anvil->Validate->is_domain_name({debug => $debug, name => $domain})) and
(not $anvil->Validate->is_ipv4({debug => $debug, ip => $domain})))
{
# Not valid, skip
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0442", variables => { name => $domain }});
next;
}
my $pinged = $anvil->Network->ping({
debug => $debug,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
ping => $domain,
count => 3,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { pinged => $pinged }});
if ($pinged)
{
$access = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { access => $access }});
}
last if $pinged;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { access => $access }});
return($access);
}
=head2 find_matches =head2 find_matches
This takes two hash keys from prior C<< Network->get_ips() >> runs and finds which are on the same network. This takes two hash keys from prior C<< Network->get_ips() >> runs and finds which are on the same network.
@ -631,6 +751,59 @@ sub get_network
return($network); return($network);
} }
### TODO: Merge the logic with ->is_remote and then make one of them simply invert the output of the other.
=head2 is_local
This method takes a host name or IP address and looks to see if it matches the local system. If it does, it returns C<< 1 >>. Otherwise it returns C<< 0 >>.
Parameters;
=head3 host (required)
This is the host name (or IP address) to check against the local system.
=cut
sub is_local
{
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 => "System->_is_local()" }});
my $host = $parameter->{host} ? $parameter->{host} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }});
my $is_local = 0;
if (($host eq $anvil->_host_name) or
($host eq $anvil->_short_host_name) or
($host eq "localhost") or
($host eq "127.0.0.1"))
{
# It's local
$is_local = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
}
else
{
# Get the list of current IPs and see if they match.
$anvil->Network->get_ips;
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{'local'}{interface}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "network::local::interface::${interface}::ip" => $anvil->data->{network}{'local'}{interface}{$interface}{ip} }});
if ($host eq $anvil->data->{network}{'local'}{interface}{$interface}{ip})
{
$is_local = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
last;
}
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
return($is_local);
}
=head2 is_remote =head2 is_remote
This looks at the C<< target >> and determines if it relates to the local system or not. If the C<< target >> is remote, C<< 1 >> is returned. Otherwise, C<< 0 >> is returned. This looks at the C<< target >> and determines if it relates to the local system or not. If the C<< target >> is remote, C<< 1 >> is returned. Otherwise, C<< 0 >> is returned.
@ -669,6 +842,229 @@ sub is_remote
# #
# =cut # =cut
=head2 ping
This method will attempt to ping a target, by host name or IP, and returns C<< 1 >> if successful, and C<< 0 >> if not.
Example;
# Test access to the internet. Allow for three attempts to account for network jitter.
my $pinged = $anvil->Network->ping({
ping => "google.ca",
count => 3,
});
# Test 9000-byte jumbo-frame access to a target over the BCN.
my $jumbo_to_peer = $anvil->Network->ping({
ping => "an-a01n02.bcn",
count => 1,
payload => 9000,
fragment => 0,
});
# Check to see if an Anvil! node has internet access
my $pinged = $anvil->Network->ping({
target => "an-a01n01.alteeve.com",
port => 22,
password => "super secret",
remote_user => "admin",
ping => "google.ca",
count => 3,
});
Parameters;
=head3 count (optional, default '1')
This tells the method how many time to try to ping the target. The method will return as soon as any ping attemp succeeds (unlike pinging from the command line, which always pings the requested count times).
=head3 debug (optional, default '3')
This is an optional way to alter to level at which this method is logged. Useful when the caller is trying to debug a problem. Generally this can be ignored.
=head3 fragment (optional, default '1')
When set to C<< 0 >>, the ping will fail if the packet has to be fragmented. This is meant to be used along side C<< payload >> for testing MTU sizes.
=head3 password (optional)
This is the password used to access a remote machine. This is used when pinging from a remote machine to a given ping target.
=head3 payload (optional)
This can be used to force the ping packet size to a larger number of bytes. It is most often used along side C<< fragment => 0 >> as a way to test if jumbo frames are working as expected.
B<NOTE>: The payload will have 28 bytes removed to account for ICMP overhead. So if you want to test an MTU of '9000', specify '9000' here. You do not need to account for the ICMP overhead yourself.
=head3 port (optional, default '22')
This is the port used to access a remote machine. This is used when pinging from a remote machine to a given ping target.
B<NOTE>: See C<< Remote->call >> for additional information on specifying the SSH port as part of the target.
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the user we will use to log into the remote machine to run the actual ping.
=head3 target (optional)
This is the host name or IP address of a remote machine that you want to run the ping on. This is used to test a remote machine's access to a given ping target.
=head3 timeout (optional, default '1')
This is how long we will wait for a ping to return, in seconds. Any real number is allowed (C<< 1 >> (one second), C<< 0.25 >> (1/4 second), etc). If set to C<< 0 >>, we will wait for the ping command to exit without limit.
=cut
sub ping
{
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 => "Network->ping()" }});
# my $start_time = [gettimeofday];
# print "Start time: [".$start_time->[0].".".$start_time->[1]."]\n";
#
# my $ping_time = tv_interval ($start_time, [gettimeofday]);
# print "[".$ping_time."] - Pinged: [$host]\n";
# If we were passed a target, try pinging from it instead of locally
my $count = defined $parameter->{count} ? $parameter->{count} : 1; # How many times to try to ping it? Will exit as soon as one succeeds
my $fragment = defined $parameter->{fragment} ? $parameter->{fragment} : 1; # Allow fragmented packets? Set to '0' to check MTU.
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $payload = defined $parameter->{payload} ? $parameter->{payload} : 0; # The size of the ping payload. Use when checking MTU.
my $ping = defined $parameter->{ping} ? $parameter->{ping} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $timeout = defined $parameter->{timeout} ? $parameter->{timeout} : 1; # This sets the 'timeout' delay.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
count => $count,
fragment => $fragment,
payload => $payload,
password => $anvil->Log->is_secure($password),
ping => $ping,
port => $port,
remote_user => $remote_user,
target => $target,
}});
# Was timeout specified as a simple integer?
if (($timeout !~ /^\d+$/) && ($timeout !~ /^\d+\.\d+$/))
{
# The timeout was invalid, switch it to 1
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { timeout => $timeout }});
$timeout = 1;
}
# If the payload was set, take 28 bytes off to account for ICMP overhead.
if ($payload)
{
$payload -= 28;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { payload => $payload }});
}
# Build the call. Note that we use 'timeout' because if there is no connection and the host name is
# used to ping and DNS is not available, it could take upwards of 30 seconds time timeout otherwise.
my $shell_call = "";
if ($timeout)
{
$shell_call = $anvil->data->{path}{exe}{timeout}." $timeout ";
}
$shell_call .= $anvil->data->{path}{exe}{'ping'}." -W 1 -n $ping -c 1";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
if (not $fragment)
{
$shell_call .= " -M do";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
}
if ($payload)
{
$shell_call .= " -s $payload";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
}
$shell_call .= " || ".$anvil->data->{path}{exe}{echo}." timeout";
my $pinged = 0;
my $average_ping_time = 0;
foreach my $try (1..$count)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count, try => $try }});
last if $pinged;
my $output = "";
my $error = "";
# If the 'target' is set, we'll call over SSH unless 'target' is 'local' or our host name.
if ($anvil->Network->is_remote($target))
{
### Remote calls
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
($output, $error, my $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,
return_code => $return_code,
}});
}
else
{
### Local calls
($output, my $return_code) = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output, return_code => $return_code }});
}
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /(\d+) packets transmitted, (\d+) received/)
{
# This isn't really needed, but might help folks watching the logs.
my $pings_sent = $1;
my $pings_received = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
pings_sent => $pings_sent,
pings_received => $pings_received,
}});
if ($pings_received)
{
# Contact!
$pinged = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { pinged => $pinged }});
}
else
{
# Not yet... Sleep to give time for transient network problems to
# pass.
sleep 1;
}
}
if ($line =~ /min\/avg\/max\/mdev = .*?\/(.*?)\//)
{
$average_ping_time = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { average_ping_time => $average_ping_time }});
}
}
}
# 0 == Ping failed
# 1 == Ping success
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
pinged => $pinged,
average_ping_time => $average_ping_time,
}});
return($pinged, $average_ping_time);
}
############################################################################################################# #############################################################################################################
# Private functions # # Private functions #
############################################################################################################# #############################################################################################################

@ -502,7 +502,7 @@ sub call
{ {
my ($state_uuid) = $anvil->Database->insert_or_update_states({ my ($state_uuid) = $anvil->Database->insert_or_update_states({
debug => 2, debug => 2,
state_name => "host_key_changed::".$target."::".$user, state_name => "host_key_changed::".$target,
state_note => "file=".$bad_file.",line=".$bad_line, state_note => "file=".$bad_file.",line=".$bad_line,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { state_uuid => $state_uuid }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { state_uuid => $state_uuid }});

@ -2470,6 +2470,10 @@ The return code indicates success; C<< 0 >> is returns if anything goes wrong. C
Parameters; Parameters;
=head3 backup (optional, default '1')
If the file needs to be updated, and if this is set to C<< 1 >>, a backup will be make before the file is updated.
=head3 body (optional) =head3 body (optional)
This is the new body of the file. It should always be set, of course, but it is optional in case the new file is supposed to be empty. This is the new body of the file. It should always be set, of course, but it is optional in case the new file is supposed to be empty.
@ -2506,6 +2510,7 @@ sub update_file
my $anvil = $self->parent; my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $backup = defined $parameter->{backup} ? $parameter->{backup} : 1;
my $body = defined $parameter->{body} ? $parameter->{body} : ""; my $body = defined $parameter->{body} ? $parameter->{body} : "";
my $file = defined $parameter->{file} ? $parameter->{file} : ""; my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $password = defined $parameter->{password} ? $parameter->{password} : ""; my $password = defined $parameter->{password} ? $parameter->{password} : "";
@ -2515,6 +2520,7 @@ sub update_file
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root"; my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $update = 0; my $update = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
backup => $backup,
body => (not $body) ? $body : $anvil->Log->is_secure($body), body => (not $body) ? $body : $anvil->Log->is_secure($body),
file => $file, file => $file,
password => $anvil->Log->is_secure($password), password => $anvil->Log->is_secure($password),
@ -2569,17 +2575,20 @@ sub update_file
$update = 1; $update = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { update => $update }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { update => $update }});
# Backup the file now. if ($backup)
my $backup_file = $anvil->Storage->backup({ {
file => $file, # Backup the file now.
debug => $debug, my $backup_file = $anvil->Storage->backup({
target => $target, file => $file,
port => $port, debug => $debug,
user => $remote_user, target => $target,
password => $password, port => $port,
remote_user => $remote_user, user => $remote_user,
}); password => $password,
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }}); remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }});
}
} }
else else
{ {
@ -2635,6 +2644,10 @@ Returns C<< 0 >> on success. C<< 1 >> or an error string will be returned otherw
Parameters; Parameters;
=head3 backup (optional, default '1')
When writing to a file that already exists, and C<< overwrite >> is true, the existing backup will be backed up prior to being rewritten.
=head3 body (optional) =head3 body (optional)
This is the contents of the file. If it is blank, an empty file will be created (similar to using 'C<< touch >>' on the command line). This is the contents of the file. If it is blank, an empty file will be created (similar to using 'C<< touch >>' on the command line).
@ -2691,6 +2704,7 @@ sub write_file
my $anvil = $self->parent; my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $backup = defined $parameter->{backup} ? $parameter->{backup} : 1;
my $body = defined $parameter->{body} ? $parameter->{body} : ""; my $body = defined $parameter->{body} ? $parameter->{body} : "";
my $file = defined $parameter->{file} ? $parameter->{file} : ""; my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $group = defined $parameter->{group} ? $parameter->{group} : getgrgid($(); my $group = defined $parameter->{group} ? $parameter->{group} : getgrgid($();
@ -2704,6 +2718,7 @@ sub write_file
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root"; my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $error = 0; my $error = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
backup => $backup,
body => (not $secure) ? $body : $anvil->Log->is_secure($body), body => (not $secure) ? $body : $anvil->Log->is_secure($body),
file => $file, file => $file,
group => $group, group => $group,
@ -2790,19 +2805,20 @@ fi";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0040", variables => { file => $file }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0040", variables => { file => $file }});
$error = 1; $error = 1;
} }
}
else if ($backup)
{ {
# Back it up. # Back it up.
my $backup_file = $anvil->Storage->backup({ my $backup_file = $anvil->Storage->backup({
file => $file, debug => $debug,
debug => $debug, file => $file,
target => $target, target => $target,
port => $port, port => $port,
user => $remote_user, user => $remote_user,
password => $password, password => $password,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }});
}
} }
# Make sure the directory exists on the remote machine. In this case, we'll use 'mkdir -p' if it isn't. # Make sure the directory exists on the remote machine. In this case, we'll use 'mkdir -p' if it isn't.
@ -2893,12 +2909,25 @@ fi";
else else
{ {
# Local # Local
if ((-e $file) && (not $overwrite)) if (-e $file)
{ {
# Nope. if (not $overwrite)
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0040", variables => { file => $file }}); {
$error = 1; # Nope.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { error => $error }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0040", variables => { file => $file }});
$error = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { error => $error }});
}
if ($backup)
{
# Back it up.
my $backup_file = $anvil->Storage->backup({
debug => $debug,
file => $file,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }});
}
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { error => $error }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { error => $error }});

@ -30,10 +30,8 @@ my $THIS_FILE = "System.pm";
# get_uptime # get_uptime
# get_os_type # get_os_type
# host_name # host_name
# is_local
# maintenance_mode # maintenance_mode
# manage_firewall # manage_firewall
# ping
# read_ssh_config # read_ssh_config
# reload_daemon # reload_daemon
# reboot_needed # reboot_needed
@ -1394,58 +1392,6 @@ sub host_name
return($host_name, $descriptive); return($host_name, $descriptive);
} }
=head2 is_local
This method takes a host name or IP address and looks to see if it matches the local system. If it does, it returns C<< 1 >>. Otherwise it returns C<< 0 >>.
Parameters;
=head3 host (required)
This is the host name (or IP address) to check against the local system.
=cut
sub is_local
{
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 => "System->_is_local()" }});
my $host = $parameter->{host} ? $parameter->{host} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }});
my $is_local = 0;
if (($host eq $anvil->_host_name) or
($host eq $anvil->_short_host_name) or
($host eq "localhost") or
($host eq "127.0.0.1"))
{
# It's local
$is_local = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
}
else
{
# Get the list of current IPs and see if they match.
$anvil->Network->get_ips;
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{'local'}{interface}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "network::local::interface::${interface}::ip" => $anvil->data->{network}{'local'}{interface}{$interface}{ip} }});
if ($host eq $anvil->data->{network}{'local'}{interface}{$interface}{ip})
{
$is_local = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
last;
}
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
return($is_local);
}
=head2 maintenance_mode =head2 maintenance_mode
This sets, clears or checks if the local system is in maintenance mode. Any system in maintenance mode will not be used by normal Anvil! tasks. This sets, clears or checks if the local system is in maintenance mode. Any system in maintenance mode will not be used by normal Anvil! tasks.
@ -2099,228 +2045,6 @@ sub pids
} }
=head2 ping
This method will attempt to ping a target, by host name or IP, and returns C<< 1 >> if successful, and C<< 0 >> if not.
Example;
# Test access to the internet. Allow for three attempts to account for network jitter.
my $pinged = $anvil->System->ping({
ping => "google.ca",
count => 3,
});
# Test 9000-byte jumbo-frame access to a target over the BCN.
my $jumbo_to_peer = $anvil->System->ping({
ping => "an-a01n02.bcn",
count => 1,
payload => 9000,
fragment => 0,
});
# Check to see if an Anvil! node has internet access
my $pinged = $anvil->System->ping({
target => "an-a01n01.alteeve.com",
port => 22,
password => "super secret",
ping => "google.ca",
count => 3,
});
Parameters;
=head3 count (optional, default '1')
This tells the method how many time to try to ping the target. The method will return as soon as any ping attemp succeeds (unlike pinging from the command line, which always pings the requested count times).
=head3 debug (optional, default '3')
This is an optional way to alter to level at which this method is logged. Useful when the caller is trying to debug a problem. Generally this can be ignored.
=head3 fragment (optional, default '1')
When set to C<< 0 >>, the ping will fail if the packet has to be fragmented. This is meant to be used along side C<< payload >> for testing MTU sizes.
=head3 password (optional)
This is the password used to access a remote machine. This is used when pinging from a remote machine to a given ping target.
=head3 payload (optional)
This can be used to force the ping packet size to a larger number of bytes. It is most often used along side C<< fragment => 0 >> as a way to test if jumbo frames are working as expected.
B<NOTE>: The payload will have 28 bytes removed to account for ICMP overhead. So if you want to test an MTU of '9000', specify '9000' here. You do not need to account for the ICMP overhead yourself.
=head3 port (optional, default '22')
This is the port used to access a remote machine. This is used when pinging from a remote machine to a given ping target.
B<NOTE>: See C<< Remote->call >> for additional information on specifying the SSH port as part of the target.
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the user we will use to log into the remote machine to run the actual ping.
=head3 target (optional)
This is the host name or IP address of a remote machine that you want to run the ping on. This is used to test a remote machine's access to a given ping target.
=head3 timeout (optional, default '1')
This is how long we will wait for a ping to return, in seconds. Any real number is allowed (C<< 1 >> (one second), C<< 0.25 >> (1/4 second), etc). If set to C<< 0 >>, we will wait for the ping command to exit without limit.
=cut
sub ping
{
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 => "System->ping()" }});
# my $start_time = [gettimeofday];
# print "Start time: [".$start_time->[0].".".$start_time->[1]."]\n";
#
# my $ping_time = tv_interval ($start_time, [gettimeofday]);
# print "[".$ping_time."] - Pinged: [$host]\n";
# If we were passed a target, try pinging from it instead of locally
my $count = defined $parameter->{count} ? $parameter->{count} : 1; # How many times to try to ping it? Will exit as soon as one succeeds
my $fragment = defined $parameter->{fragment} ? $parameter->{fragment} : 1; # Allow fragmented packets? Set to '0' to check MTU.
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $payload = defined $parameter->{payload} ? $parameter->{payload} : 0; # The size of the ping payload. Use when checking MTU.
my $ping = defined $parameter->{ping} ? $parameter->{ping} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $timeout = defined $parameter->{timeout} ? $parameter->{timeout} : 1; # This sets the 'timeout' delay.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
count => $count,
fragment => $fragment,
payload => $payload,
password => $anvil->Log->is_secure($password),
ping => $ping,
port => $port,
remote_user => $remote_user,
target => $target,
}});
# Was timeout specified as a simple integer?
if (($timeout !~ /^\d+$/) && ($timeout !~ /^\d+\.\d+$/))
{
# The timeout was invalid, switch it to 1
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { timeout => $timeout }});
$timeout = 1;
}
# If the payload was set, take 28 bytes off to account for ICMP overhead.
if ($payload)
{
$payload -= 28;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { payload => $payload }});
}
# Build the call. Note that we use 'timeout' because if there is no connection and the host name is
# used to ping and DNS is not available, it could take upwards of 30 seconds time timeout otherwise.
my $shell_call = "";
if ($timeout)
{
$shell_call = $anvil->data->{path}{exe}{timeout}." $timeout ";
}
$shell_call .= $anvil->data->{path}{exe}{'ping'}." -W 1 -n $ping -c 1";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
if (not $fragment)
{
$shell_call .= " -M do";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
}
if ($payload)
{
$shell_call .= " -s $payload";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
}
$shell_call .= " || ".$anvil->data->{path}{exe}{echo}." timeout";
my $pinged = 0;
my $average_ping_time = 0;
foreach my $try (1..$count)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count, try => $try }});
last if $pinged;
my $output = "";
my $error = "";
# If the 'target' is set, we'll call over SSH unless 'target' is 'local' or our host name.
if ($anvil->Network->is_remote($target))
{
### Remote calls
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
($output, $error, my $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,
return_code => $return_code,
}});
}
else
{
### Local calls
($output, my $return_code) = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output, return_code => $return_code }});
}
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /(\d+) packets transmitted, (\d+) received/)
{
# This isn't really needed, but might help folks watching the logs.
my $pings_sent = $1;
my $pings_received = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
pings_sent => $pings_sent,
pings_received => $pings_received,
}});
if ($pings_received)
{
# Contact!
$pinged = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { pinged => $pinged }});
}
else
{
# Not yet... Sleep to give time for transient network problems to
# pass.
sleep 1;
}
}
if ($line =~ /min\/avg\/max\/mdev = .*?\/(.*?)\//)
{
$average_ping_time = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { average_ping_time => $average_ping_time }});
}
}
}
# 0 == Ping failed
# 1 == Ping success
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
pinged => $pinged,
average_ping_time => $average_ping_time,
}});
return($pinged, $average_ping_time);
}
=head2 read_ssh_config =head2 read_ssh_config
This reads /etc/ssh/ssh_config and notes hosts with defined ports. When found, the associated port will be automatically used for a given host name or IP address. This reads /etc/ssh/ssh_config and notes hosts with defined ports. When found, the associated port will be automatically used for a given host name or IP address.

@ -619,10 +619,9 @@ AND
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bad_line => $bad_line }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bad_line => $bad_line }});
} }
} }
my ($target, $user) = ($state_name =~ /host_key_changed::(.*)::(.*)$/); my ($target) = ($state_name =~ /host_key_changed::(.*)$/);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
target => $target, target => $target,
user => $user,
bad_file => $bad_file, bad_file => $bad_file,
bad_line => $bad_line, bad_line => $bad_line,
}}); }});
@ -637,7 +636,6 @@ AND
checkbox_name => $checkbox_key, checkbox_name => $checkbox_key,
checkbox_checked => $checked, checkbox_checked => $checked,
target => $target, target => $target,
user => $user,
file => $bad_file, file => $bad_file,
host => $host_name, host => $host_name,
}}); }});
@ -849,21 +847,26 @@ sub process_prep_host_page
}}); }});
# If the target is RHEL and it is not registered, offer the user to provide the RH user and password. # If the target is RHEL and it is not registered, offer the user to provide the RH user and password.
my $rh_template = ""; my $redhat_message = "";
my $redhat_form = "";
if (($data->{host_os} =~ /^rhel/) && ($data->{os_registered} ne "yes")) if (($data->{host_os} =~ /^rhel/) && ($data->{os_registered} ne "yes"))
{ {
$rh_template = $anvil->Template->get({file => "anvil.html", name => "host-setup-redhat", variables => { $redhat_message = $anvil->Template->get({file => "anvil.html", name => "host-setup-redhat-message"});
$redhat_form = $anvil->Template->get({file => "anvil.html", name => "host-setup-redhat-form", variables => {
rh_user => $rh_user, rh_user => $rh_user,
rh_password => $rh_password, rh_password => $rh_password,
}}); }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { rh_template => $rh_template }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
redhat_form => $redhat_form,
redhat_message => $redhat_message,
}});
} }
### NOTE: Left off here. Need to pick up the message from a bad/changed fingerprint when it's the cause of a failed login. Create a button to remove the bad key. # Did we connect?
if (not $connected) if (not $connected)
{ {
# Is it because the target's key is bad or has changed? # Nope. Is it because the target's key is bad or has changed?
my $query = "SELECT state_uuid, state_note FROM states WHERE state_name LIKE ".$anvil->Database->quote("host_key_changed::".$host_ip_address."::%").";"; my $query = "SELECT state_uuid, state_note FROM states WHERE state_name LIKE ".$anvil->Database->quote("host_key_changed::".$host_ip_address).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
@ -922,11 +925,19 @@ sub process_prep_host_page
else else
{ {
# Connected! Ask th euser to confirm. # Connected! Ask th euser to confirm.
my $new_host_name = "#!string!striker_0139!#";
if ((exists $anvil->data->{cgi}{host_name}) && ($anvil->data->{cgi}{host_name}{value}))
{
$new_host_name = $anvil->data->{cgi}{host_name}{value};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_host_name => $new_host_name }});
$anvil->data->{form}{body} = $anvil->Template->get({file => "anvil.html", name => "confirm-initialize-host", variables => { $anvil->data->{form}{body} = $anvil->Template->get({file => "anvil.html", name => "confirm-initialize-host", variables => {
'package' => $type eq "dr" ? "anvil-dr" : "anvil-node", 'package' => $type eq "dr" ? "anvil-dr" : "anvil-node",
redhat => $rh_template, redhat_message => $redhat_message,
redhat_form => $redhat_form,
access => "root\@".$host_ip_address.":".$ssh_port, access => "root\@".$host_ip_address.":".$ssh_port,
host_name => $target_host_name, current_host_name => $target_host_name,
new_host_name => $new_host_name,
host_uuid => $target_host_uuid, host_uuid => $target_host_uuid,
default_host_name => $default_host_name, default_host_name => $default_host_name,
}}); }});

@ -165,7 +165,7 @@
&nbsp; &nbsp;
</td> </td>
</tr> </tr>
#!variable!redhat!# #!variable!redhat_message!#
<tr> <tr>
<td> <td>
<table align="center" class="data_table"> <table align="center" class="data_table">
@ -174,26 +174,35 @@
<td class="top_padded_cell"> <td class="top_padded_cell">
#!string!striker_0125!#: #!string!striker_0125!#:
</td> </td>
<td class="top_padded_cell" class="fixed_width"> <td class="top_padded_cell">
#!variable!access!# <span class="fixed_width">#!variable!access!#</span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="padded_cell"> <td class="padded_cell">
#!string!striker_0126!#: #!string!striker_0126!#:
</td> </td>
<td class="padded_cell" class="fixed_width"> <td class="padded_cell">
#!variable!host_name!# <span class="fixed_width">#!variable!current_host_name!#</span>
</td>
</tr>
<tr>
<td class="padded_cell">
#!string!striker_0138!#:
</td>
<td class="padded_cell">
<span class="fixed_width">#!variable!new_host_name!#</span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="padded_cell"> <td class="padded_cell">
#!string!striker_0127!#: #!string!striker_0127!#:
</td> </td>
<td class="padded_cell" class="fixed_width"> <td class="padded_cell">
#!variable!host_uuid!# <span class="fixed_width">#!variable!host_uuid!#</span>
</td> </td>
</tr> </tr>
#!variable!redhat_form!#
<tr> <tr>
<td class="button_cell" style="text-align: left;"> <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> <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>
@ -218,7 +227,7 @@
</table> </table>
<!-- end confirm-initialize-host --> <!-- end confirm-initialize-host -->
<!-- start host-setup-redhat --> <!-- start host-setup-redhat-message -->
<tr> <tr>
<td class="menu_details"> <td class="menu_details">
#!string!message_0148!# #!string!message_0148!#
@ -229,26 +238,23 @@
&nbsp; &nbsp;
</td> </td>
</tr> </tr>
<tr> <!-- end host-setup-redhat-message -->
<td>
<table align="center" width="90%"> <!-- start host-setup-redhat-form -->
<tr> <tr>
<td> <td class="padded_cell">
#!string!message_0144!#:
</td>
<td class="padded_cell">
<input type="text" name="rh_user" id="rh_user" value="#!variable!rh_user!#" placeholder="#!string!message_0144!#" /> <input type="text" name="rh_user" id="rh_user" value="#!variable!rh_user!#" placeholder="#!string!message_0144!#" />
</td> </td>
<td> </tr>
&nbsp; <tr>
<td class="padded_cell">
#!string!message_0145!#:
</td> </td>
<td> <td class="padded_cell">
<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!#" /> <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> </td>
</tr> </tr>
</table> <!-- end host-setup-redhat-form -->
</td>
</tr>
<tr>
<td>
&nbsp;
</td>
</tr>
<!-- end host-setup-redhat -->

@ -1,19 +1,16 @@
<!-- start broken-key-entry --> <!-- start broken-key-entry -->
<tr> <tr>
<td> <td>
<input type="checkbox" id="#!variable!checkbox_name!#" name="#!variable!checkbox_name!#" #!variable!checkbox_checked!# /> &nbsp; <input type="checkbox" id="#!variable!checkbox_name!#" name="#!variable!checkbox_name!#" #!variable!checkbox_checked!# /> &nbsp;
</td> </td>
<td class="column_row_value_fixed"> <td class="column_row_value_fixed">
#!variable!host!# - #!variable!host!#
</td> </td>
<td class="column_row_value_fixed"> <td class="column_row_value_fixed">
#!variable!user!# - &nbsp;-&nbsp;
</td> </td>
<td class="column_row_value_fixed"> <td class="column_row_value_fixed">
#!variable!target!# - #!variable!target!# &nbsp;
</td>
<td class="column_row_value_fixed">
#!variable!file!#
</td> </td>
</tr> </tr>
<!-- end broken-key-entry --> <!-- end broken-key-entry -->
@ -47,33 +44,27 @@
<td class="column_header"> <td class="column_header">
#!string!header_0015!# #!string!header_0015!#
</td> </td>
<!-- User --> <td>
<td class="column_header"> &nbsp;
#!string!header_0013!#
</td>
</td> </td>
<!-- Target --> <!-- Target -->
<td class="column_header"> <td class="column_header">
#!string!header_0012!# #!string!header_0012!#
</td> </td>
<!-- File -->
<td class="column_header">
#!string!header_0014!#
</td>
</tr> </tr>
<tr> <tr>
<td colspan="5"> <td colspan="4">
&nbsp; &nbsp;
</td> </td>
</tr> </tr>
#!variable!bad_keys!# #!variable!bad_keys!#
<tr> <tr>
<td colspan="5"> <td colspan="4">
&nbsp; &nbsp;
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="button_cell" colspan="5" align="center"> <td class="button_cell" colspan="4" align="center">
<input type="submit" id="delete" name="delete" class="button" value="#!string!striker_0068!#" /> <input type="submit" id="delete" name="delete" class="button" value="#!string!striker_0068!#" />
</td> </td>
</tr> </tr>

@ -3,7 +3,7 @@
%define anvilgroup admin %define anvilgroup admin
Name: anvil Name: anvil
Version: 3.0 Version: 3.0
Release: 26%{?dist} Release: 27%{?dist}
Summary: Alteeve Anvil! complete package. Summary: Alteeve Anvil! complete package.
License: GPLv2+ License: GPLv2+
@ -287,6 +287,9 @@ firewall-cmd --add-service=postgresql --permanent
%changelog %changelog
* tbd Madison Kelly <mkelly@alteeve.ca> 3.0-27
-
* Wed Oct 02 2019 Madison Kelly <mkelly@alteeve.ca> 3.0-26 * Wed Oct 02 2019 Madison Kelly <mkelly@alteeve.ca> 3.0-26
- Updated source - Updated source

@ -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_0145">Red Hat password</key>
<key name="message_0146">What kind of machine will this host be?</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_0147"><![CDATA[What is the host's <b>current</b> IP address and password?]]></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> <key name="message_0148"><![CDATA[This is a RHEL host and has not yet been subscribed. You can enter your Red Hat subscription credentials below. If the host has internet access, the host will be subscribed during setup.]]></key>
<key name="message_0149">The target's host key has changed. If the target has been rebuilt, or the target IP reused, the old key will need to be removed. If this is the case, remove line: [#!variable!line!#] from: [#!variable!file!#].</key> <key name="message_0149">The target's host key has changed. If the target has been rebuilt, or the target IP reused, the old key will need to be removed. If this is the case, remove line: [#!variable!line!#] from: [#!variable!file!#].</key>
<key name="message_0150">Set the new host name.</key> <key name="message_0150">Set the new host name.</key>
@ -764,6 +764,9 @@ Failed to promote the DRBD resource: [#!variable!resource!#] primary. Expected a
<key name="log_0437">The anvil version cache file: [#!variable!file!#] for: [#!variable!target!#] needs to be created/updated.</key> <key name="log_0437">The anvil version cache file: [#!variable!file!#] for: [#!variable!target!#] needs to be created/updated.</key>
<key name="log_0438"><![CDATA[[ Error ] - No job was found for the 'job_uuid': [#!variable!job_uuid!#].]]></key> <key name="log_0438"><![CDATA[[ Error ] - No job was found for the 'job_uuid': [#!variable!job_uuid!#].]]></key>
<key name="log_0439">No databases available yet, continuing to wait.</key> <key name="log_0439">No databases available yet, continuing to wait.</key>
<key name="log_0440">The variable: [#!variable!name!#] is an array reference, but it doesn't have any entries in it.</key>
<key name="log_0441">The variable: [#!variable!name!#] was expected to be a positive integer, but: [#!variable!value!#] was received.</key>
<key name="log_0442">The domain: [#!variable!name!#] does not appear to be a valid domain name or an ipv4 IP address. Skipping it.</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>
@ -930,8 +933,8 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="striker_0121">Prepare Node or DR Host</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_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_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_0124"><![CDATA[The test connection was successful! If you proceed with initialization, the target will have the Alteeve repo added and: [#!variable!package!#] installed. The target will also be configured to use our database.]]></key>
<key name="striker_0125">Initialize Host</key> <key name="striker_0125">Host to Initialize</key>
<key name="striker_0126">Current host name</key> <key name="striker_0126">Current host name</key>
<key name="striker_0127">Host UUID</key> <key name="striker_0127">Host UUID</key>
<key name="striker_0128">Initialize</key> <key name="striker_0128">Initialize</key>
@ -950,6 +953,8 @@ The machines responding when we try to connect to the targets below are respondi
<br /> <br />
If you are comfortable that the target has changed for a known reason, you can select the broken keys below to have them removed.<br /> If you are comfortable that the target has changed for a known reason, you can select the broken keys below to have them removed.<br />
]]></key> ]]></key>
<key name="striker_0138">New host name</key>
<key name="striker_0139"><![CDATA[<unchanged>]]></key>
<!-- These are generally units and appended to numbers --> <!-- These are generally units and appended to numbers -->
<key name="suffix_0001">#!variable!number!#/sec</key> <key name="suffix_0001">#!variable!number!#/sec</key>
@ -1014,10 +1019,10 @@ Failure! The return code: [#!variable!return_code!#] was received ('0' was expec
<key name="job_0043">Verifying installation.</key> <key name="job_0043">Verifying installation.</key>
<key name="job_0044">[ Failed ] - There may be more information in #!data!path::log::file!#.</key> <key name="job_0044">[ Failed ] - There may be more information in #!data!path::log::file!#.</key>
<key name="job_0045">Success!</key> <key name="job_0045">Success!</key>
<key name="job_0046">Adding our database connection information to the target's anvil.conf file!</key> <key name="job_0046">Adding our database connection information to the target's anvil.conf file.</key>
<key name="job_0047">Finished! The target should be ready for initial configuration shortly. If it isn't, please check that the 'anvil-daemon' daemon is running.</key> <key name="job_0047">Finished! The target should be ready for initial configuration shortly. If it isn't, please check that the 'anvil-daemon' daemon is running.</key>
<key name="job_0048">Removing bad machine keys.</key> <key name="job_0048">Removing bad machine keys.</key>
<key name="job_0049">Removing line: [#!variable!line!#] from: [#!variable!file!#] for the target machine: [#!variable!target!#].</key> <key name="job_0049">Removing existing entries for the target machine: [#!variable!target!#] from: [#!variable!file!#].</key>
<key name="job_0050">[ Error ] - The known hosts file: [#!variable!file!#] was not found. Skipping it.</key> <key name="job_0050">[ Error ] - The known hosts file: [#!variable!file!#] was not found. Skipping it.</key>
<key name="job_0051">Finished.</key> <key name="job_0051">Finished.</key>
<key name="job_0052">[ Error ] - There was a problem reading the known hosts file: [#!variable!file!#]. Skipping it.</key> <key name="job_0052">[ Error ] - There was a problem reading the known hosts file: [#!variable!file!#]. Skipping it.</key>

@ -128,7 +128,8 @@ WHERE
next; next;
} }
### NOTE: We don't need the line anymore, but we're not removing it yet. ### NOTE: We don't need the file or line anymore, but we're not removing it as having
### a record of the trigger might be useful someday.
# Pull out the details. # Pull out the details.
my $bad_file = ""; my $bad_file = "";
my $bad_line = ""; my $bad_line = "";
@ -151,125 +152,164 @@ WHERE
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bad_line => $bad_line }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bad_line => $bad_line }});
} }
} }
my ($target, $user) = ($state_name =~ /host_key_changed::(.*)::(.*)$/); my ($target) = ($state_name =~ /host_key_changed::(.*)$/);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
target => $target, target => $target,
user => $user,
bad_file => $bad_file, bad_file => $bad_file,
bad_line => $bad_line, bad_line => $bad_line,
}}); }});
$anvil->data->{job}{progress} += 5; # Read in the specified bad file, then find any other files that might have matching bad keys.
update_progress($anvil, $anvil->data->{job}{progress}, "job_0049,!!line!:".$bad_line."!!,!!file!".$bad_file."!!,!!target!".$target."!!"); process_file($anvil, "/root/.ssh/known_hosts", $target);
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0049", variables => {
line => $bad_line,
file => $bad_file,
target => $target,
}});
# Read in the file, if it exists. # Walk through any other users.
if (not -e $bad_file) my $directory = "/home";
local(*DIRECTORY);
opendir(DIRECTORY, $directory);
while(my $file = readdir(DIRECTORY))
{ {
$anvil->data->{job}{progress} += 10; next if $file eq ".";
update_progress($anvil, $anvil->data->{job}{progress}, "job_0050,!!file!".$bad_file."!!"); next if $file eq "..";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0050", variables => { file => $bad_file }}); my $full_path = $directory."/".$file;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
file => $file,
full_path => $full_path,
}});
# Remove this job and go on to the next bad key (if any). # If we're looking at a directory, scan it. Otherwise, see if it's an executable and that it
delete_state($anvil, $state_uuid); # starts with 'scan-*'.
next; if (-d $full_path)
{
# Check for a known_hosts file.
my $known_hosts = $full_path."/.ssh/known_hosts";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { known_hosts => $known_hosts }});
if (-e $known_hosts)
{
process_file($anvil, $known_hosts, $target);
}
}
} }
closedir(DIRECTORY);
# Read in the file delete_state($anvil, $state_uuid);
my ($old_body) = $anvil->Storage->read_file({file => $bad_file}); }
if ($old_body eq "!!error!!") }
{
# Failed to read the file
$anvil->data->{job}{progress} += 10;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0052,!!file!".$bad_file."!!");
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0052", variables => { file => $bad_file }});
# Remove this job and go on to the next bad key (if any). return(0);
delete_state($anvil, $state_uuid); }
next;
}
# Find our key # Look through the file for bad keys.
my $line_number = 0; sub process_file
my $new_body = ""; {
my $update = 0; my ($anvil, $file, $target) = @_;
foreach my $line (split/\n/, $old_body) $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
{ file => $file,
$line_number++; target => $target,
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { }});
's1:line_number' => $line_number,
's2:bad_line' => $bad_line,
's3:line' => $line,
}});
# If the line starts with our target, remove it. $anvil->data->{job}{progress} += 5;
if ($line =~ /^$target /) update_progress($anvil, $anvil->data->{job}{progress}, "job_0049,!!file!".$file."!!,!!target!".$target."!!");
{ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0049", variables => {
# Found it! file => $file,
$anvil->data->{job}{progress} += 5; target => $target,
update_progress($anvil, $anvil->data->{job}{progress}, "job_0053,!!line!".$line_number."!!"); }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0053", variables => { line => $line_number }});
$update = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }});
}
else
{
$new_body .= $line."\n";
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { # Read in the file, if it exists.
's1:old_body' => $old_body, if (not -e $file)
's2:new_body' => $new_body, {
's3:update' => $update, # File doesn't actually exist, wtf?
}}); $anvil->data->{job}{progress} += 10;
if ($update) update_progress($anvil, $anvil->data->{job}{progress}, "job_0050,!!file!".$file."!!");
{ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0050", variables => { file => $file }});
# Write the file out.
$anvil->data->{job}{progress} += 5;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0055,!!file!".$bad_file."!!");
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0055", variables => { file => $bad_file }});
# Get the owning user and group.
my ($owning_uid, $owning_gid) = (stat($bad_file))[4,5];
my $owning_user = getpwuid($owning_uid);
my $owning_group = getpwuid($owning_gid);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
owning_uid => $owning_uid,
owning_gid => $owning_gid,
owning_user => $owning_user,
owning_group => $owning_group,
}});
my $error = $anvil->Storage->write_file({ return(1);
body => $new_body, }
debug => 2,
file => $bad_file, # Read in the file
overwrite => 1, my ($old_body) = $anvil->Storage->read_file({file => $file});
user => $owning_user, if ($old_body eq "!!error!!")
group => $owning_group {
}); # Failed to read the file
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { error => $error }}); $anvil->data->{job}{progress} += 5;
if ($error) update_progress($anvil, $anvil->data->{job}{progress}, "job_0052,!!file!".$file."!!");
{ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0052", variables => { file => $file }});
$anvil->data->{job}{progress} += 5;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0059,!!file!".$bad_file."!!"); return(1);
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0059", variables => { file => $bad_file }}); }
}
else # Find our key(s)
{ my $line_number = 0;
# Success! my $new_body = "";
delete_state($anvil, $state_uuid); my $update = 0;
$anvil->data->{job}{progress} += 5; foreach my $line (split/\n/, $old_body)
update_progress($anvil, $anvil->data->{job}{progress}, "job_0060,!!file!".$bad_file."!!"); {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0060", variables => { file => $bad_file }}); $line_number++;
} $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
} 's1:line_number' => $line_number,
's2:line' => $line,
}});
# If the line starts with our target, remove it.
if ($line =~ /^$target /)
{
# Found it!
$anvil->data->{job}{progress} += 5;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0053,!!line!".$line_number."!!");
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0053", variables => { line => $line_number }});
$update = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }});
}
else
{
$new_body .= $line."\n";
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:old_body' => $old_body,
's2:new_body' => $new_body,
's3:update' => $update,
}});
if ($update)
{
# Write the file out.
$anvil->data->{job}{progress} += 5;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0055,!!file!".$file."!!");
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0055", variables => { file => $file }});
# Get the owning user and group.
my ($owning_uid, $owning_gid) = (stat($file))[4,5];
my $owning_user = getpwuid($owning_uid);
my $owning_group = getpwuid($owning_gid);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
owning_uid => $owning_uid,
owning_gid => $owning_gid,
owning_user => $owning_user,
owning_group => $owning_group,
}});
my $error = $anvil->Storage->write_file({
body => $new_body,
debug => 3,
file => $file,
overwrite => 1,
user => $owning_user,
group => $owning_group
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { error => $error }});
if ($error)
{
$anvil->data->{job}{progress} += 5;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0059,!!file!".$file."!!");
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0059", variables => { file => $file }});
}
else
{
# Success!
$anvil->data->{job}{progress} += 5;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0060,!!file!".$file."!!");
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "job_0060", variables => { file => $file }});
} }
} }
@ -364,7 +404,7 @@ sub delete_state
if ($state_uuid) if ($state_uuid)
{ {
my $query = "DELETE FROM states WHERE state_uuid = ".$anvil->Database->quote($state_uuid).";"; my $query = "DELETE FROM states WHERE state_uuid = ".$anvil->Database->quote($state_uuid).";";
$anvil->Database->write({debug => 2, query => $query, source => $THIS_FILE, line => __LINE__}); $anvil->Database->write({debug => 3, query => $query, source => $THIS_FILE, line => __LINE__});
} }
return(0); return(0);

@ -72,9 +72,16 @@ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list
'target::password' => $anvil->Log->is_secure($anvil->data->{target}{password}), 'target::password' => $anvil->Log->is_secure($anvil->data->{target}{password}),
}}); }});
my ($host_uuid) = get_host_uuid($anvil); my ($host_uuid) = get_host_uuid($anvil);
my ($host_name) = get_host_name($anvil); my ($host_name) = get_host_name($anvil);
my ($host_os,, $os_registered) = get_host_os($anvil); my ($host_os, $os_registered) = get_host_os($anvil);
my $internet = $anvil->Network->check_internet({
remote_user => $anvil->data->{target}{user},
target => $anvil->data->{target}{host},
port => $anvil->data->{target}{port},
password => $anvil->data->{target}{password},
tries => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_uuid => $host_uuid, host_uuid => $host_uuid,
host_name => $host_name, host_name => $host_name,
@ -86,6 +93,7 @@ print "host_name=".$host_name."\n";
print "host_uuid=".$host_uuid."\n"; print "host_uuid=".$host_uuid."\n";
print "host_os=".$host_os."\n"; print "host_os=".$host_os."\n";
print "os_registered=".$os_registered."\n"; print "os_registered=".$os_registered."\n";
print "internet=".$internet."\n";
$anvil->nice_exit({code => 0}); $anvil->nice_exit({code => 0});
@ -139,14 +147,14 @@ fi;
# Is it subscribed? This isn't the best call to make, but it seems to be the one that returns # 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. # 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({ my ($output, $error, $return_code) = $anvil->Remote->call({
debug => 3, debug => 2,
shell_call => $anvil->data->{path}{exe}{'subscription-manager'}." identity", shell_call => $anvil->data->{path}{exe}{'subscription-manager'}." identity",
user => $anvil->data->{target}{user}, user => $anvil->data->{target}{user},
target => $anvil->data->{target}{host}, target => $anvil->data->{target}{host},
port => $anvil->data->{target}{port}, port => $anvil->data->{target}{port},
password => $anvil->data->{target}{password}, password => $anvil->data->{target}{password},
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output, output => $output,
error => $error, error => $error,
return_code => $return_code, return_code => $return_code,

@ -175,8 +175,7 @@ sub add_databases
last if $db_host; last if $db_host;
} }
### TODO: Left off here. The password written to the target isn't right. Also, host.uuid is failing to be written. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { db_host => $db_host }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { db_host => $db_host }});
if ($db_host) if ($db_host)
{ {
my $failed = $anvil->Database->manage_anvil_conf({ my $failed = $anvil->Database->manage_anvil_conf({
@ -204,6 +203,13 @@ sub add_databases
update_progress($anvil, 100, "job_0047"); update_progress($anvil, 100, "job_0047");
} }
} }
else
{
### TODO: LEft off here; change the message to a proper one.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0076"});
update_progress($anvil, 100, "error_0076");
$anvil->nice_exit({exit_code => 6});
}
return(0); return(0);
} }
@ -557,10 +563,14 @@ sub wait_for_access
{ {
my ($anvil) = @_; my ($anvil) = @_;
### TODO: If the keys changed, the user may have been prompted to fix the keys for the admin user,
### but not the 'root' user. This, we may fail to access the node as this runs as 'root'. Tell
### the user to check for bad keys and clear them if/when access fails.
# Test access # Test access
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "job_0023", variables => { target => $anvil->data->{data}{say_target} }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "job_0023", variables => { target => $anvil->data->{data}{say_target} }});
$anvil->data->{job}{progress} += 5; $anvil->data->{job}{progress} += 5;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0023,!!target!".$anvil->data->{data}{say_target}."!!"); update_progress($anvil, $anvil->data->{job}{progress}, "job_0023,!!target!".$anvil->data->{data}{say_target}."!!");
$anvil->data->{job}{progress} += 5;
my $waiting = 1; my $waiting = 1;
my $access = 0; my $access = 0;
my $timeout = time + 600; my $timeout = time + 600;
@ -597,7 +607,6 @@ sub wait_for_access
target => $anvil->data->{data}{say_target}, target => $anvil->data->{data}{say_target},
timeout => $time_left, timeout => $time_left,
}}); }});
$anvil->data->{job}{progress} += 5;
update_progress($anvil, $anvil->data->{job}{progress}, "job_0025,!!target!".$anvil->data->{data}{say_target}."!!,!!timeout!".$time_left."!!"); update_progress($anvil, $anvil->data->{job}{progress}, "job_0025,!!target!".$anvil->data->{data}{say_target}."!!,!!timeout!".$time_left."!!");
sleep 5; sleep 5;
} }

Loading…
Cancel
Save