* Finished Convert->time (needs testing).

* Started reworking Remote->call to use Net::OpenSSH instead of Net::SSH2.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 6 years ago
parent b1326e1b4e
commit 559dbafb39
  1. 3
      Anvil/Tools.pm
  2. 23
      Anvil/Tools/Convert.pm
  3. 277
      Anvil/Tools/Remote.pm
  4. 1
      Anvil/Tools/System.pm
  5. 3
      notes
  6. 5
      rpm/SPECS/anvil.spec

@ -674,8 +674,7 @@ sub _get_hash_reference
my $parameter = shift;
my $anvil = $self;
#print "$THIS_FILE ".__LINE__."; hash: [".$an."], key: [$parameter->{key}]\n";
die "$THIS_FILE ".__LINE__."; The hash key string: [$parameter->{key}] doesn't seem to be valid. It should be a string in the format 'foo::bar::baz'.\n" if $parameter->{key} !~ /::/;
die "$THIS_FILE ".__LINE__."; The hash key string: [".$parameter->{key}."] doesn't seem to be valid. It should be a string in the format 'foo::bar::baz'.\n" if $parameter->{key} !~ /::/;
# Split up the keys.
my $key = $parameter->{key} ? $parameter->{key} : "";

@ -1011,7 +1011,7 @@ sub time
}
# Remote commas and verify we're left with a number.
my $time =~ s/,//g;
$time =~ s/,//g;
if ($time =~ /^\d+\.\d+$/)
{
# Round the time
@ -1073,41 +1073,46 @@ sub time
{
$say_time =~ s/ sec.$/$suffix_seconds/;
$say_time = sprintf("%01d", $remaining_minutes).$suffix_minutes." $say_time";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
}
elsif (($hours > 0) or ($days > 0) or ($weeks > 0))
{
$say_time = "0".$suffix_minutes." ".$say_time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
}
if ($remaining_hours > 0)
{
$say_time = sprintf("%01d", $remaining_hours)."$suffix_hours $say_time";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
}
elsif (($days > 0) or ($weeks > 0))
{
$say_time = "0".$suffix_hours." ".$say_time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
}
if ($days > 0)
{
$say_time = sprintf("%01d", $remaining_days).$suffix_days." ".$say_time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
}
elsif ($weeks > 0)
{
$say_time = "0".$suffix_days." ".$say_time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
}
if ($weeks > 0)
{
$weeks = $an->Readable->comma($weeks);
$weeks = $anvil->Convert->add_commas({number => $weeks});
$say_time = $weeks.$suffix_weeks." ".$say_time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
weeks => $weeks,
say_time => $say_time,
}});
}
# Return an already-translated string
$say_time = $an->String->_process_string({
string => $say_time,
language => $an->default_language,
hash => $an->data,
variables => {},
});
$say_time = $anvil->Words->string({debug => $debug, string => $say_time});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
return($say_time);
}

@ -7,6 +7,9 @@ use strict;
use warnings;
use Data::Dumper;
use Scalar::Util qw(weaken isweak);
use Net::SSH2; ### TODO: Phase out.
use Net::OpenSSH;
use Capture::Tiny ':all';
our $VERSION = "3.0.0";
my $THIS_FILE = "Remote.pm";
@ -321,6 +324,280 @@ sub call
$port = $anvil->data->{hosts}{$target}{port};
}
# Break out the port, if needed.
if ($target =~ /^(.*):(\d+)$/)
{
$target = $1;
$port = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
port => $port,
target => $target,
}});
# If the user passed a port, override this.
if ($parameter->{port} =~ /^\d+$/)
{
$port = $parameter->{port};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { port => $port }});
}
}
else
{
# In case the user is using ports in /etc/ssh/ssh_config, we'll want to check for an entry.
$anvil->System->read_ssh_config();
$anvil->data->{hosts}{$target}{port} = "" if not defined $anvil->data->{hosts}{$target}{port};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "hosts::${target}::port" => $anvil->data->{hosts}{$target}{port} }});
if ($anvil->data->{hosts}{$target}{port} =~ /^\d+$/)
{
$port = $anvil->data->{hosts}{$target}{port};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { port => $port }});
}
}
# Make sure the port is valid.
if ($port eq "")
{
$port = 22;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { port => $port }});
}
elsif ($port !~ /^\d+$/)
{
$port = getservbyname($port, 'tcp');
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { port => $port }});
}
if ((not defined $port) or (($port !~ /^\d+$/) or ($port < 0) or ($port > 65536)))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0058", variables => { port => $port }});
return("!!error!!");
}
# If the target is a host name, convert it to an IP.
if (not $anvil->Validate->is_ipv4({ip => $target}))
{
my $new_target = $anvil->Convert->hostname_to_ip({host_name => $target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { new_target => $new_target }});
if ($new_target)
{
$target = $new_target;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { target => $target }});
}
}
# If the user set 'no_cache', don't use any existing 'ssh_fh'.
if (($no_cache) && ($ssh_fh))
{
# Close the connection.
$ssh_fh->disconnect();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "message_0010", variables => { target => $target }});
# For good measure, blank both variables.
$anvil->data->{cache}{ssh_fh}{$ssh_fh_key} = "";
$ssh_fh = "";
}
# This will store the output
my $output = "";
my $state = "";
my $error = "";
my $connect_output = "";
# If I don't already have an active SSH file handle, connect now.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_fh => $ssh_fh }});
if ($ssh_fh !~ /^Net::OpenSSH/)
{
my $ssh_fh = "";
my $connected = 0;
foreach (my $i = 0; $i <= 9; $i++)
{
($connect_output) = capture_merged {
$ssh_fh = Net::OpenSSH->new($target,
user => $remote_user,
port => $port,
batch_mode => 1,
);
};
$connect_output =~ s/\n$//;
$connect_output =~ s/\r$//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:target' => $target,
's2:port' => $port,
's3:ssh_fh' => $ssh_fh,
's4:ssh_fh->error' => $ssh_fh->error,
's5:connect_output' => $connect_output,
}});
#print "ssh error: [".$ssh_fh->error."]\n";
#print "output: [".$connect_."]\n";
# print "Results:\n"; print Dumper @result;
# If I didn't connect, try again if I have a password.
if (($ssh_fh->error) && ($password) && ($connect_output =~ /Permission denied/i))
{
# Try again.
#print "Connection without a password failed, trying again with the password.\n";
$connect_output = "";
($connect_output) = capture_merged {
$ssh_fh = Net::OpenSSH->new($target,
user => $remote_user,
port => $port,
passwd => $password,
batch_mode => 1,
);
};
$connect_output =~ s/\n$//;
$connect_output =~ s/\r$//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:target' => $target,
's2:port' => $port,
's3:ssh_fh' => $ssh_fh,
's4:ssh_fh->error' => $ssh_fh->error,
's5:connect_output' => $connect_output,
}});
}
if (not $ssh_fh->error)
{
# Connected!
$connected = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { connected => $connected }});
last;
}
elsif ($i < 9)
{
# Sleep and then try again.
$connect_output = "";
sleep 1;
}
}
# Try ten times. 9 in the loop, last try after.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:connected' => $connected,
's2:ssh_fh->error' => $ssh_fh->error,
}});
if ((not $connected) && ($ssh_fh->error))
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", list => {
remote_user => $remote_user,
target => $target,
port => $port,
shell_call => $shell_call,
error => $ssh_fh->error,
}});
# We'll now try to get a more useful message for the user and logs.
my $message_key = "message_0005";
my $variables = {
target => $target,
error => $ssh_fh->error,
};
if (($ssh_fh->error =~ /Bad hostname/i) or ($connect_output =~ /Bad hostname/i))
{
$message_key = "message_0001";
}
elsif (($ssh_fh->error =~ /Connection refused/i) or ($connect_output =~ /Connection refused/i))
{
$message_key = "message_0002";
$variables = {
target => $target,
port => $port,
remote_user => $remote_user,
};
}
elsif (($ssh_fh->error =~ /No route to host/) or ($connect_output =~ /No route to host/i))
{
$message_key = "message_0003";
}
elsif (($ssh_fh->error =~ /timeout/) or ($connect_output =~ /timeout/i))
{
$message_key = "message_0004";
}
$error = $anvil->Words->string({key => $message_key, variables => $variables});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => $message_key, variables => $variables});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => {
error => $error,
ssh_fh => $ssh_fh,
output => $output,
}});
return($error, $output);
}
### TODO: Delete this once we finish converting to Net::OpenSSH
sub call2
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
# Get the target and port so that we can create the ssh_fh key
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $ssh_fh_key = $target.":".$port;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
port => $port,
target => $target,
}});
# This will store the SSH file handle for the given target after the initial connection.
$anvil->data->{cache}{ssh_fh}{$ssh_fh_key} = defined $anvil->data->{cache}{ssh_fh}{$ssh_fh_key} ? $anvil->data->{cache}{ssh_fh}{$ssh_fh_key} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "cache::ssh_fh::${ssh_fh_key}" => $anvil->data->{cache}{ssh_fh}{$ssh_fh_key} }});
# Now pick up the rest of the variables.
my $close = defined $parameter->{'close'} ? $parameter->{'close'} : 0;
my $no_cache = defined $parameter->{no_cache} ? $parameter->{no_cache} : 0;
my $password = defined $parameter->{password} ? $parameter->{password} : $anvil->data->{sys}{root_password};
my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0;
my $shell_call = defined $parameter->{shell_call} ? $parameter->{shell_call} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $start_time = time;
my $ssh_fh = $anvil->data->{cache}{ssh_fh}{$ssh_fh_key};
# NOTE: The shell call might contain sensitive data, so we show '--' if 'secure' is set and $anvil->Log->secure is not.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
'close' => $close,
password => $anvil->Log->secure ? $password : $anvil->Words->string({key => "log_0186"}),
secure => $secure,
shell_call => ((not $anvil->Log->secure) && ($secure)) ? $anvil->Words->string({key => "log_0186"}) : $shell_call,
ssh_fh => $ssh_fh,
start_time => $start_time,
remote_user => $remote_user,
port => $port,
target => $target,
}});
if (not $shell_call)
{
# No shell call
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Remote->call()", parameter => "shell_call" }});
return("!!error!!");
}
if (not $target)
{
# No target
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Remote->call()", parameter => "target" }});
return("!!error!!");
}
if (not $remote_user)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Remote->call()", parameter => "remote_user" }});
return("!!error!!");
}
# If the user didn't pass a port, but there is an entry in 'hosts::<host>::port', use it.
if ((not $parameter->{port}) && ($anvil->data->{hosts}{$target}{port}))
{
$port = $anvil->data->{hosts}{$target}{port};
}
# Break out the port, if needed.
my $state = "";
my $error = "";

@ -6,7 +6,6 @@ package Anvil::Tools::System;
use strict;
use warnings;
use Data::Dumper;
use Net::SSH2;
use Scalar::Util qw(weaken isweak);
use Time::HiRes qw(gettimeofday tv_interval);
use Proc::Simple;

@ -1,3 +1,5 @@
============
NEXT; -
RHEL 8 package changes:
@ -1058,4 +1060,3 @@ pcs resource create dlm ocf:pacemaker:controld op monitor interval=60s on-fail=f
pcs resource create lvmlock ocf:heartbeat:lvmlockd op monitor interval=60s on-fail=fence clone meta interleave=true
pcs constraint order start dlm-clone then lvmlock-clone
pcs constraint colocation add lvmlock-clone with dlm-clone

@ -3,7 +3,7 @@
%define anvilgroup admin
Name: anvil
Version: 3.0
Release: 23%{?dist}
Release: 24%{?dist}
Summary: Alteeve Anvil! complete package.
License: GPLv2+
@ -38,6 +38,7 @@ Requires: htop
Requires: iproute
Requires: lsscsi
Requires: mlocate
Requires: perl-Capture-Tiny
Requires: perl-Data-Dumper
Requires: perl-DBD-Pg
Requires: perl-DBI
@ -45,10 +46,12 @@ Requires: perl-Digest-SHA
Requires: perl-File-MimeInfo
Requires: perl-HTML-FromText
Requires: perl-HTML-Strip
Requires: perl-IO-Tty
Requires: perl-JSON
Requires: perl-Log-Journald
Requires: perl-Net-SSH2
Requires: perl-Net-Netmask
Requires: perl-Net-OpenSSH
Requires: perl-NetAddr-IP
Requires: perl-Proc-Simple
Requires: perl-Sys-Syslog

Loading…
Cancel
Save