Device with Telnet/SSH not responding, show error - perl

Strange issue I am running into. I have a few devices with Telnet/SSH issues. When I run my script the results are saying the script was successful. When debug is on I get the follow results..
[ 0.012464] pr finding prompt
[ 0.016593] tr creating Net::Telnet wrapper for telnet
[ 0.017859] tr connecting with: telnet Host 10.xx.xx.xx Port 23
How could I add something to show a error if a promopt is not present or the connection times out?
Thanks
#!/usr/bin/perl
use Net::Appliance::Session;
$file = '1list';
open (FH, "< $file") or die "Can't open $file for read: $!";
my #ios_list = <FH>;
close FH or die "Cannot close $file: $!";
chomp(#ios_list);
my $ios_username = 'xxxx';
my $ios_password = 'xxxx';
DEVICE:
for my $ios_device_ip ( #ios_list ) {
my #version_info;
my $proto = shift;
if (($proto == 43)||($proto == 44)){
$tran = "SSH";
$app="/usr/local/bin/ssh";
}else{
$tran = "Telnet";
$app="/bin/telnet";
}
my $session_obj = Net::Appliance::Session->new(
host => $ios_device_ip,
transport => $tran,
personality => 'ios',
);
#interace
eval {
# try to login to the ios device, ignoring host check
$session_obj->connect(
username => $ios_username,
password => $ios_password,
#SHKC => 0
);
# get our running config
$session_obj->begin_privileged;
$session_obj->cmd('conf t');
$session_obj->cmd('aaa authorization config-commands');
$session_obj->cmd('exit');
$session_obj->end_privileged;
$session_obj->cmd('write memory');
# close down our session
$session_obj->close;
};
#error check
if ($#) {
if ( UNIVERSAL::isa($#, 'Net::Appliance::Session::Exception') ) {
# fault description from Net::Appliance::Session
print "We had an error during our Telnet/SSH session to device : $ios_devi
ce_ip \n";
print $#->message . " \n";
# message from Net::Telnet
print "Net::Telnet message : " . $#->errmsg . "\n";
# last line of output from your appliance
print "Last line of output from device : " . $#->lastline . "\n\n";
}
elsif (UNIVERSAL::isa($#, 'Net::Appliance::Session::Error') ) {
# fault description from Net::Appliance::Session
print "We had an issue during program execution to device : $ios_device_ip
\n";
# print $#->message . " \n";
}
else {
# we had some other error that wasn't a deliberately created exception
print "We had an issue when accessing the device : $ios_device_ip \n";
print "$ios_device_ip The reported error was : $# \n";
}
next DEVICE;
}
print #version_info;
print "$ios_device_ip ok \n";
#end
}

If you're having trouble connecting, it may help to check $# after the eval as there may be an error you're ignoring.
#interace
eval {
# try to login to the ios device, ignoring host check
$session_obj->connect(
username => $ios_username,
password => $ios_password,
#SHKC => 0
);
It's also worth noting that this doesn't do anything:
my $proto = shift;
if (($proto == 43)||($proto == 44)){
$tran = "SSH";
$app="/usr/local/bin/ssh";
}else{
shift is operating on #_ which isn't populated, so $proto will always be undef.

Related

Can't call method “send” on an undefined value at - Net::SSH::Expect Perl error

I am running some commands in a remote server by connecting from my local server. To achieve this I am connecting to remote server using Net::SSH::Expect perl module. Connection is establishing here, but sometimes when I execute the command the following error comes up -
Can't call method "send" on an undefined value at .....
Here is my code:
my ($ip, $user, $passwd) = ("my.ip.address.here","user", "password");
my $ssh = SSH_Connection( $ip, $user, $passwd );
my $command_to_execute = "<Command to be executed will build here>";
print "$command_to_execute\n";
$str = 'Bye';
$ssh->send("$command_to_execute; echo $str");
$output = $ssh->waitfor($str, undef);
$ssh->close();
print "END\n";
sub SSH_Connection {
my ( $host, $user, $passwd ) = #_;
my $ssh = Net::SSH::Expect->new (
host => $host, #ip
user => $user, #'user'
password => $passwd, #'password'
raw_pty => 1,
no_terminal => 0,
);
my $login_output;
my $handledie = eval {
$login_output = $ssh->login();
};
if ( $# ) {
if ($# =~ m/SSHConnectionError/i ) {
print "SSH Connection Error\n";
} elsif ( $# =~ m/SSHProcessError/ix ) {
print "SSH Process Error\n";
} elsif ( $# =~ m/SSHConnectionAborted/ix ) {
print "SSH Connection Aborted\n";
} else {
print "SSH Unknown Error: $#\n";
}
}
if ($login_output !~ /Last login/) {
die "Login has failed.";
} else {
return $ssh;
}
print "SSH to ip - $host failed\n";
}
First I'm building the command and storing it in $command_to_execute variable.
At the end of command execution I'll get keyword Bye. So I am waiting for that keyword to match.
My question is -
Why I am getting above mentioned error?
Suppose if my command execution is failed, will the control will come back to my script ? Because its waiting for $str word.
I doubt about the error catching method is not proper. Please suggest a better solution.

Output Lines are missing

I have written small program for getting output from router. but the starting content of the output is missing in output file.
#!C:\strawberry\perl\bin\perl -w
open ( OUTPUT,"> D:\\Routerbkp\\router\\abc.txt" );
use Control::CLI;
# Create the object instance for SSH
$cli = new Control::CLI('SSH');
# Connect to host - Note that with SSH,
# authentication is part of the connection process
$cli->connect( Host => '10.0.0.1',
Username => 'abc',
Password => 'abc',
PrivateKey => 'C:\Users\Administrator\.ssh\key_10.0.0.1_22.pub',
);
# Send a command and read the resulting output
$output1 = $cli->cmd("terminal length 0");
sleep(1);
$output2 = $cli->cmd("show running-config");
sleep(5);
$output8 = $cli->cmd("show alarm current");
sleep(2);
$cli->disconnect;
print OUTPUT $output1;
print OUTPUT $output2;
print OUTPUT $output8;
If you're having a problem with your code, your first port of call is ALWAYS use strict; and use warnings;.
Then - fix that open statement. Try in the style of:
open ( my $output_fh, ">", "D:\\Routerbkp\\router\\abc.txt" ) or die $!;
You probably also want to trap any errors from $cli -> connect() because there's no guarantee that's worked.
my $result = $cli -> connect ( ...
if ( not $result ) { print "Connect failed: ", $cli -> errormode(), ":", $cli -> errormsg(), "\n"; };

Perl sftp downloads with Net::SFTP::Foreign

Im a beginner. I have written a perl script which does the following
-Create a directory under “/x01/abcd/abc_logs/abcd_Logs” by the current date, in the format of “YYYYMMDD” if it has not already been created.
i.e: if the script is run on “01st of jan 2013”, the directory “20130101” will be created under the said path. So whenever there is a need to inspect the logs always look for a directory by the current date.
-Check if the log file(s) have already been downloaded earlier within the same day, and if not log(s) will be downloaded to the TODAY’s directory.
Im having a hard time, coming up with a solution to print a message when there are no files in the share. This is of course when the user specify 2 or more files that are not there in the share. I know that this happens because there is a "die" statement in the "sub get_LOGS". I just cannot seem to understand how to return a message when all the files I specify do not happen to be in the share.
usage of this script is as follows
./abc_logs ....<file(n)>
following is the script.
my $LOGS_LOCAL_PATH = "/x02/abc/abcba2/";
chomp $LOGS_LOCAL_PATH;
my $LOGS_REM_PATH = "/x01/INT/abc/vabc2/";
chomp $LOGS_REM_PATH;
my $TODAY = `date +%Y%m%d`;
chomp $TODAY;
my #GETLOOP = #ARGV;
unless ($#ARGV >= 0) {
print "\nUsage: gtp_logs.pl <file1> <file2> <file3>.....<file(n)>\n\n";
exit;
}
system("clear");
unless ( -d "$LOGS_LOCAL_PATH"."$TODAY") {
print "Directory \"$TODAY\" doesn't exist. So creating the directory..!\n";
print "OK..Done.....!\n\n";
system("mkdir $LOGS_LOCAL_PATH/$TODAY");
}
else {
print "Directory already exists. Logs will be downloaded to ==> \"$LOGS_LOCAL_PATH$TODAY\".....!\n\n";
}
# if_DOWNLOADED($LOGS_LOCAL_PATH,$TODAY,#GETLOOP);
chdir("$LOGS_LOCAL_PATH"."$TODAY") || die "cannot cd to ($!)";
foreach my $GETL (#GETLOOP) {
my $is_downloaded = if_DOWNLOADED($LOGS_LOCAL_PATH,$TODAY,$GETL);
if(!$is_downloaded)
{
get_LOGS("172.25.70.221","abc","abc2","/x01/INT/abc",$GETL);
print "File \"$GETL\" downloaded to ==> \"$LOGS_LOCAL_PATH$TODAY\"\n\n";
}
else
{
print "File \"$GETL\" has already been Downloaded to ==> \"$LOGS_LOCAL_PATH$TODAY\"\n\n";
}
}
sub get_LOGS {
my $LOG_HOST = shift;
my $REM_USER = shift;
my $REM_PASSW = shift;
my $REM_PATH = shift;
my $REM_FILE = shift;
print "Connecting to the sftp share! Please wait....!\n";
my $sftp = Net::SFTP::Foreign->new($LOG_HOST, user => $REM_USER, password => $REM_PASSW);
$sftp->setcwd($REM_PATH) or die "unable to change cwd: " . $sftp->error;
print "OK. On the share! Downloading the file \"$REM_FILE\"...................!\n\n\n\n";
$sftp->error and die "Problem connecting to the share...!!!! " . $sftp->error;
$sftp->get($REM_FILE) or die "File does not seem to be present on the remote share. Please re-request..!!!" . $sftp->error;
return $REM_FILE;
}
sub if_DOWNLOADED {
my $DWD_FILE_PATH = shift;
my $DWD_DIR = shift;
my $DWD_FILE = shift;
if (-e "$DWD_FILE_PATH/$DWD_DIR/$DWD_FILE")
{
return 1;
}
else
{
return 0;
}
}
Please can someone help me finding a solution to this matter? Please try to use the same script and modify.
/V
Some comments to your code:
Use strict and warnings in order to catch lots of errors early.
Read some book on style (i.e. Damian Conway's Perl Best Practices). But in any case try to be consistent when naming variables, subroutines, and everything and also with their case.
When you have to use some calculated value in several places, try to calculate it once and save it in a variable.
Don't use subroutines for trivial things.
You don't need to call chomp on variables you have defined and that don't have a "\n" character at the end.
Opening a new SFTP connection for every file transfer is very inefficient. You can open just one at the beginning and use it for all the transfers.
And now, a simplified version of your script:
#!/usr/bin/perl
use strict;
use warnings;
my $host = "172.25.70.221";
my $user = "abc";
my $password = "abc1234321";
my $LOGS_LOCAL_PATH = "/x02/ABC/abc2";
my $LOGS_REM_PATH = "/x01/INT/abc/vim";
my $TODAY = `date +%Y%m%d`;
chomp $TODAY;
my $TODAY_LOCAL_PATH = "$LOGS_LOCAL_PATH/$TODAY";
my #files = #ARGV;
#files or die "\nUsage: gtp_logs.pl <file1> <file2> <file3>.....<file(n)>\n\n";
system("clear");
if ( -d $TODAY_LOCAL_PATH) {
print "Directory already exists. Logs will be downloaded to ==> \"$TODAY_LOCAL_PATH\".....!\n\n";
}
else {
print "Directory \"$TODAY\" doesn't exist. So creating the directory..!\n";
mkdir "$TODAY_LOCAL_PATH" or die "unable to create directory: $!\n";
print "OK..Done.....!\n\n";
}
chdir $TODAY_LOCAL_PATH or die "cannot cd to ($!)\n";
my $sftp = Net::SFTP::Foreign->new($host, user => $user, password => $password);
$sftp->error
and die "Problem connecting to the share...!!!! " . $sftp->error;
my $ok = 0;
my $failed = 0;
foreach my $file (#files) {
if (-e "$TODAY_LOCAL_PATH/$file") {
print "File \"$file\" has already been Downloaded to ==> \"$TODAY_LOCAL_PATH\"\n";
}
else {
if ($sftp->get("$LOGS_REM_PATH/$file")) {
print "File \"$file\" downloaded to ==> \"$TODAY_LOCAL_PATH\"\n";
$ok++;
}
else {
print "Unable to download file \"$file\" : " . $sftp->error . "\n";
$failed++;
}
}
}
print "$ok files have been downloaded, $failed files failed!\n\n";

Perl Script cannot fork more than 10 times

My perl code does not allow more than 10 forks. For the following perl code, whenever I use more than 10 machines in the list of machines read in to the script, the perl script only forks 10 processes for 10 machines and for the rest it dies with error:
SSHProcessError The ssh process was terminated. at serverLogin.pl 44
It dies at the line where it says $ssh->waitfor('The authenticity of host*',15);.
PERL SCRIPT:
#!/usr/bin/perl -w
use Net::SSH::Expect;
use Term::ReadKey;
print "please enter filename:\n";
$filename = ReadLine;
chomp $filename;
print "please enter user ID:\n";
$userID = ReadLine;
chomp $userID;
print "please enter password:\n";
ReadMode 'noecho';
$passwordforuser = ReadLine 0;
chomp $passwordforuser;
ReadMode 'normal';
open READFILE,"<","$filename" or die "Could not open file listofmachines\n";
my #listofmachines = <READFILE>;
foreach $machine (#listofmachines)
{
my $pid=fork();
if ($pid){
push(#childprocs,$pid);
}
elsif ( $pid == 0 ) {
my $ssh = Net::SSH::Expect->new (
host => "$machine",
user => "$userID",
password=> "$passwordforuser",
timeout => 25,
raw_pty => 1,
);
my $login_output = $ssh->run_ssh or die "Could not launch SSH\n";
$ssh->waitfor('The authenticity of host*',15);
#print "This output for machine $machine\n";
$ssh->send("yes");
$ssh->waitfor('password: ', 15);
$ssh->send("$passwordforuser");
$ssh->waitfor('$ ', 10);
my #commresult=$ssh->exec("uptime");
print $login_output;
print #commresult;
exit 0;
}
else {
die "Could not Fork()\n";
}
}
foreach(#childprocs){
waitpid($_, 0)
}
Please help. Thanks, nblu.
Your script using Net::OpenSSH::Parallel instead of Net::SSH::Expect.
The number of simultaneous connections is limited to 10 to overcome any resource exhaustion problem as happening in your script (probably PTYs):
#!/usr/bin/perl -w
use Net::OpenSSH::Parallel;
use Term::ReadKey;
print "please enter filename:\n";
$filename = ReadLine;
chomp $filename;
print "please enter user ID:\n";
$userID = ReadLine;
chomp $userID;
print "please enter password:\n";
ReadMode 'noecho';
$passwordforuser = ReadLine 0;
chomp $passwordforuser;
ReadMode 'normal';
open READFILE,"<","$filename" or die "Could not open file listofmachines\n";
my #listofmachines = <READFILE>;
chomp #listofmachines;
my $pssh = Net::OpenSSH::Parallel->new(connections => 10);
$pssh->add_host($_,
user => $userID, password => $passwordforuser,
master_opts => [-o => 'StrictHostKeyChecking=no'])
for #listofmachines;
sub do_ssh_task {
my ($host, $ssh) = #_;
my $output = $ssh->capture('uptime');
print "$host: $output";
}
$pssh->all(parsub => \&do_ssh_task);
$pssh->run;
for my $host (#listofmachines) {
if (my $error = $pssh->get_error($host)) {
print STDERR "remote task failed for host $host: $error\n";
}
}
By default, the remote ssh daemon limits the number of concurrent ssh connections to something like 10 per userid. If that is a problem for you, you will need to change the server configuration...
Perhaps you have a limit to the number of processes you can create? Can you create 30 or more processes in a loop where the children just sleep(60)?
If in fact you have a limit of how many you can do at once, try using Parallel::ForkManager.
If this is from hitting a limit on pseudoterminals, how you set that depends on kernel version; what does uname -a say? also depends on whether the code is using BSD or SysV/UNIX98 ptys. If you see it opening files like /dev/ptyXY where X is one of a-e or p-z, it's the former, and you will have a hard limit of 256 systemwide.
You can change passwords without a pseudoterminal using usermod instead of passwd, but this momentarily exposes the crypted password in the process list; that may be acceptable in your case.

Perl: SSH Tail as File Handler

I am creating a log parser that has the ability to "stream" a log as it is written.
The log resides on a remote host, so I am creating a file handler using a combination of
SSH and tail. The following works quite well, but I do have some questions regarding error handling.
If the user doesn't enter the password for the SSH connection prior to the alarm delay expiring, the alarm will start triggering. This leads to the console being cleared so it is not clear that the password needs to be entered.
If i enter the wrong password, i still enable the alarm leading to screen clears, ect...
Password:
Password:
Password:
Permission denied (publickey,keyboard-interactive).
If i provide a log filename that doesn't exist, the code continues....
tail: cannot open `/path_to_log/mylog.logXXXX' for reading: No such file or directory
tail: no files remaining
So, my question is what is the best way add some additional error handling. Alternatively, can the File::Tail module be used in combination with SSH, telnet, rlogin, etc to provide the same functionality?
Thanks!
my $stopMsg = "Use CTRL+C to stop streaming at any time...\n";
my $SSH = sprintf "ssh %s#%s tail -f %s | ", $user, $host, $log;
printf "Log: %s\n", $log;
printf "Handle: %s\n", $SSH;
my $errMsg = sprintf "Couldn't establish SSH connection to \"%s\":",
$host;
open my $pipe, $SSH or error( $errMsg );
my $loadTime = time;
printf $stopMsg;
setSignalHandler( 'INT', sub{ stopAlarm( TRUE ); } );
startAlarm( $delay,
$interval,
sub { system "clear"; $handler->( \#sysLogArr ); printf $stopMsg; } );
while ( alarmHandlerSet() )
{
my $data = <$pipe>;
next unless defined $data;
mapSysLog( line => $data,
arrRef => $logRef,
varRef => \%sysLogVars,
dbRef => $dbRef );
}
clearSignalHandler( 'INT' );
sub error(#)
{
my $color = "BOLD";
$color = $CONFIG{errorPrinter} if ( $CONFIG{colorEnable} &&
defined $CONFIG{errorPrinter} );
color2PrinterRef( $color )->( "\nERROR: " );
printf "%s\n", shift;
printf " %s\n", $_ foreach ( #_ );
printf "Called From: %s::%d\n", (caller)[1], (caller)[2];
printf "\n";
exit EXIT_FAILURE;
}
See the sftp_tail.pl sample included with Net::SFTP::Foreign.