Unable to get output from remote Device using Net::SSH2 - perl

I am trying to connect to a remote device through Net::SSH2. If I am using $channel->blocking(0) method, then I am not getting any output from the device even if the connection and authentication is successful. If I dont use $channel->blocking(0), then I am getting the output but the prompt keeps on blinking, in the sense the the program gets stuck.
Code
use Net::SSH2;
use strict;
$ssh = Net::SSH2->new()
$ssh->connect('xx.xx.xx.xx'))
$ssh->auth(username => 'xxxx', password => 'xxxx'))
my $channel = $ssh->channel() or do { print" [LOG ERROR]: Failed to create channel. Exiting ...\n"};
$channel->blocking(0);
$channel->shell() ;
print $channel "show status \n";
while(<$channel>)
{
print $_;
}
$channel->close();
$ssh->disconnect();
can somebody please help.

Maybe debug could help you by troubleshooting. If there is no particular reason why you are using shell try to replace it by exec. You could adopt this example.
use warnings is also always useful.

Related

Redirect and Restore STDERR in Dancer

When starting my http server I don't want to see >> Dancer2 v0.201000 server <pid> listening on http://0.0.0.0:<port> printed on the stderr. Thats why I added the following line before calling start()
get "/pwd" => sub {
my $pwd = cwd;
print STDERR "\n\n[PWD] : $pwd\n"; # this line is not being printed
print "\n\n[STDOUT::PWD] : $pwd\n";
my %responseHash = ( pwd => $pwd );
my $response = encode_json \%responseHash;
return $response;
};
my $dancerStartErr;
sub startServer {
open (local *STDERR, ">", \$dancerStartErr)
or die "Dup err to variable error: $!\n";
start();
}
startServer();
The problem is that later I can't print something on the STERR. How can I reopen STDERR (open(STDERR, ">", \*STDERR); doesn't help)?
If you don't want your application to log anything, you can change the logging engine to use Dancer2::Logger::Null. You do that by editing your config.yml, or in one of your environments. For example, to turn it off in producion, change # appdir/environments/production.yml.
logger: 'null'
The default is the logging engine 'console', which prints stuff to your terminal.
There are other Dancer2::Logger:: classes available bundled with Dancer2 and on CPAN in their own distributions. A better solution to just dumping everything into a black hole might be to log to a file instead. Documentation of how to configure it further can be found in Dancer2::Core::Role::Logger.
Also note that instead of printing to STDERR in your code, you should use the logging keywords with the appropriate log level.
print STDERR "\n\n[PWD] : $pwd\n"; # this line is not being printed
This is not a good idea, because you cannot distinguish if this is an error, or a warning, or just debugging output. That's why there are different log levels built into Dancer2.
core
debug
info
warning
error
All of them are available as keywords. There is documentation on it in Dancer2::Manual.
Since the working directory is probably not relevant in production, but only during development, you'd go with debug.
debug "[PWD] : $pwd";
That's it. It takes care of newlines and such for you automatically.
You could use select before redirecting to save it in a variable
my $oldfh = select(STDERR);
and then use it later
select($oldfh);
Also check out:
Capture::Tiny::Extended
How to redirect and restore STDOUT/STDERR

Weird issue with Net::SSH::Expect in Perl script

I am working on putting together a perl script. I have captured it below:
#!/usr/bin/perl
use Tie::File;
use Net::SSH::Expect;
use utf8;
use warnings;
use diagnostics;
# Grab password from hidden file
$pw=`cat .password`;
chomp $pw;
#Read list of 9200's from hosts.list file into an array
tie #hosts, 'Tie::File', "hosts.list" or die;
#Loop through hosts, connect via ssh, run commands, and write out log files.
foreach (#hosts) {
#Create ssh session handle
my $ssh = Net::SSH::Expect->new (
host => $_,
password => $pw,
user => 'user',
raw_pty => 1
);
my $login_output = $ssh->login();
if ($login_output !~ /.*sbc.*>/) {
die "Login failed. Login output was $login_output";
}
$ssh->send("show sip errors");
my $line;
while ( defined ($line = $ssh->read_line()) ){
print $line . "\n";
}
$ssh->close();
}
First, I'm not a programmer, so style is probably very ugly. Sorry about that :) The goal is to run several commands on a remote appliance, capture the results in separate files, which will then be consumed by a 3rd party parsing engine (splunk).
The current implemented functionality is able to log in to remote hosts, run the command, and then print out to stdout. Not quite there, but still shows a good proof of concept.
The script runs fine for the first 3 hosts in the hosts.list file. However as soon as it gets to the fourth host, I receive this exception:
Uncaught exception from user code:
SSHAuthenticationError Login timed out. The input stream currently has the contents bellow: user#myhost.mydomain's password: at /System/Library/Perl/Extras/5.12/Expect.pm line 828
at /Library/Perl/5.12/Net/SSH/Expect.pm line 209
Net::SSH::Expect::__ANON__('ARRAY(0x7fd718a03008)') called at /System/Library/Perl/Extras/5.12/Expect.pm line 828
Expect::_multi_expect(1, 'ARRAY(0x7fd7189fbce8)', 'ARRAY(0x7fd7189f7460)') called at /System/Library/Perl/Extras/5.12/Expect.pm line 565
Expect::expect('Expect=GLOB(0x7fd7189f1878)', 1, 'ARRAY(0x7fd718a01530)', 'ARRAY(0x7fd7189f15a8)', 'ARRAY(0x7fd71890a3d0)', 'ARRAY(0x7fd718a07470)', 'ARRAY(0x7fd7189d8b18)') called at /Library/Perl/5.12/Net/SSH/Expect.pm line 580
Net::SSH::Expect::_sec_expect('Net::SSH::Expect=HASH(0x7fd718a29828)', 1, 'ARRAY(0x7fd718a01530)', 'ARRAY(0x7fd7189f15a8)', 'ARRAY(0x7fd71890a3d0)', 'ARRAY(0x7fd718a07470)', 'ARRAY(0x7fd7189d8b18)') called at /Library/Perl/5.12/Net/SSH/Expect.pm line 213
Net::SSH::Expect::login('Net::SSH::Expect=HASH(0x7fd718a29828)') called at ./pcscfFetch.pl line 26
Any ideas on what the problem could be? I am able to log in to the host with no issue manually via ssh. The script works fine for our other hosts, it's just this one outlier that I can't seem to figure out. Any advice would be appreciated. Thanks!
I did end up resolving this. In the constructor for $ssh I set the timeout to 10 seconds, instead of the default 1. The script runs significantly slower, but I don't appear to have the issues I was running into before. Appreciate the feedback!
Net::SSH::Expect is not reliable.
Use Net::OpenSSH instead, or if you want to run the same set of commands in several hosts Net::OpenSSH::Parallel.

Perl Net::FTP object expected re-use behavior?

I can't seem to re-use a Net::FTP object after using quit.
Is this expected? I was unable to tell from the documentation (cpan).
As a workaround, I'm creating a new Net::FTP object each time I need to perform a batch of ftp operations. This seems wasteful.
The following example shows: successful initial login, printing of root directory ls, quit (socket close), login failing with ftp message "Connection closed".
#!/usr/bin/env perl
use strict;
use warnings;
use Net::FTP;
my $hostname = 'foo';
my $username = 'bar';
my $password = 'baz';
# successful first pass
my $ftp = Net::FTP->new( $hostname ) or die "cannot connect to $hostname: $#";
$ftp->login( $username, $password ) or die "cannot login: ", $ftp->message;
map { print "ls_output: $_\n" } $ftp->ls; # success
$ftp->quit or die "cannot close connection: ", $ftp->message;
# re-use attempt
$ftp->login( $username, $password ) or die "cannot login: ", $ftp->message;
# never gets here since re-use attempt fails
print "done!\n";
quit causes the remote end to close the connection and there's no way to logout without closing the connection. If you're trying to avoid reconnecting, you can't.
On the other hand, maybe you're expecting login to connect you to the server. The connection is created in new, not in login, and Net::FTP does not provide a means of reconnecting.
Net::FTP subclasses IO::Socket::INET, so you could reconnect by using IO::Socket::INET's connect, but you'd also have to reinitialise a field or two the constructor normally initialises. Nothing complicated though.
But is there even a problem that needs fixing? You talk of inefficiencies, but the time it takes to create and initialise an object pales in comparison it takes to make an FTP connection.
It's not a Perl issue. It's the FTP protocol. After the quit is issued... that's it, the FTP session is over. There's nothing to issue the login command to -- no-one's listening anymore.
Try it yourself on the command line with the FTP client.
From this very documentation you linked:
quit ()
Send the QUIT command to the remote FTP server and close the socket connection.
Closing the socket connection terminates the connection to the server, not just the session on this server.
Creating a new object for each connection has two drawbacks:
Perl may or may not keep the data from the old object around in memory—but garbage collection should not bother you.
Creating a new connection involves some overhead. But unless you are creating dozens of new connections per second, this should not bother you.
So just create new objects every time; you can reuse the same variable if you like. Thinking too low level in Perl, and optimizing too early is only going to hurt.

How to run SSH commands using Net::SSH::Perl?

I don't know if I managed to install Net::SSH::Perl module successfully but I can't seem to be able to run the following code:
my $ssh = Net::SSH::Perl->new($remote_host);
$ssh->login($username, $password);
print "login done", "\n";
my ($out, $err, $exit) = $ssh->cmd($cmd);
print "$out", "\n";
I am able to login but cannot print the $out. I keep getting this error:
Use of uninitialized value $out in string at test_ssh.pl line 28.
Line 28 refers to print "$out", "\n";.
I am running this code on Cygwin. What should I do now?
EDIT:
I got the following error msg when I ran my $ssh = Net::SSH::Perl->new($remote_host, options => ["Debug yes"]);:
Use of uninitialized value $out in string at test_ssh.pl line 29 (#1)
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl will try to tell you the
name of the variable (if any) that was undefined. In some cases it cannot
do this, so it also tells you what operation you used the undefined value
in. Note, however, that perl optimizes your program and the operation
displayed in the warning may not necessarily appear literally in your
program. For example, "that $foo" is usually optimized into "that "
. $foo, and the warning will refer to the concatenation (.) operator,
even though there is no . in your program.
EDIT2:
Here's my full code
use strict;
use warnings;
use Net::SSH::Perl;
my $remote_host = '<host ip address>';
my $password = 'pass';
my $username = 'user';
my $cmd = 'copy run tftp:<my own ip address>';
warn "Starting SSH Services:...";
my $ssh = Net::SSH::Perl->new($remote_host, debug => 1);
print "done", "\n";
warn "Starting Login:...";
$ssh->login($username, $password);
print "login done", "\n";
warn "Starting command:...";
#$ssh->cmd($cmd);
#my($stdout, $stderr, $exit) = $ssh->cmd($cmd);
my ($out, $err, $exit) = $ssh->cmd($cmd);
print "$out", "\n";
The error message on "print "$out","\n";" line:
<Computername>: channel 1: new [client-session]
<Computername>: Requesting channel_open for channel 1.
<Computername>: Entering interactive session.
<Computername>: Channel open failure: 1: reason 4:
Use of uninitialized value $out in string at test_ssh.pl line 29.
LAST EDIT: I decided to use Net::Appliance::Session to login via SSH to the network devices instead. it's a lot easier to use than Net::SSH::Perl.
Please show more of your code. What is the value of $cmd?
Note that the login method doesn't perform a login: it merely stores the username and password to be used when the connection is set up for each cmd call.
The correct way of enabling debugging messages is
my $ssh = Net::SSH::Perl->new($remote_host, debug => 1);
This will give you trace information from the cmd method which should say, amongst other things
Sending command: xxx
Entering interactive session.
and should give you some clues about what is going wrong.
Your debug output shows the problem. Looking at SSH2.h, open failure reason 4 is SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED. Your username and password are incorrect.
Net::SSH::Perl does support login via username/password, I have a working example, I just got this to work. I used the code from above and took out the Double Quotes (" ") and used single quotes (' ') instead. And "debug => 1" works for debugging the code when having issues. It will display info to you when you try to login if the debug option is set.
I am connecting to a Win32-OpenSSH SSHD server based on Windows Powershell very similar to BSDLinux SSHD server with SFTP support. Supports same Linux style based connection.
I have been trying all other SSH modules all day. Hopefully someone can use this code to just run a command and get the output if required.
You can install Net::SSH::Perl with "cpan -i module-name"
use Net::SSH::Perl;
my $host = 'testmachine.acme.local'; #Or just IP Address
my $user = 'domain\username'; #Or just username
my $pass = 'Password#123';
my $cmd = 'dir C:\\Windows\\';
use Net::SSH::Perl;
my $ssh = Net::SSH::Perl->new($host, debug => 1);
$ssh->login($user, $pass);
my ($out, $err, $exit) = $ssh->cmd($cmd);
print "$out", "\n";
Net::SSH::Perl does not support login via username/password only via interactive password entry or public key. See this post for more information.
http://www.perlmonks.org/?node_id=590452

perl script working in vmware server but fails in vmware ESXi

This problem is really puzzling to me: I have the following script working on vmware server 2.0:
#!/usr/local/bin/perl
# server (transmitter)
use strict;
use IO::Socket::Multicast6;
use IO::Interface;
use constant GROUP => "235.1.1.2";
use constant PORT => "3000";
my $sock = IO::Socket::Multicast6->new(
Proto=>"udp",
Domain=>AF_INET,
PeerAddr=>GROUP,
PeerPort=>PORT);
$sock->mcast_if("eth1");
$sock->mcast_ttl(10);
while (1) {
my $message = localtime();
$sock->send($message) || die "Could not send: $!";
} continue {
sleep 4;
}
It works great on vmware server. I have cloned this VM to an EXSi server but running the same exact copy of the virtual machine running the script, and I get the following error:
Can't call method "mcast_if" on an undefined value
Im really puzzled by this as I am not sure what the problem could be.
there is really nothing different except for the CPU running on both machines, but I don't see how something so low level can be causing an issue but I could be wrong. perl -d wasn't very helpful.
Thanks.
It's failing to create the socket, use some error checking to try to find out why. Eg:
my $sock = IO::Socket::Multicast6->new(
Proto=>"udp",
Domain=>AF_INET,
PeerAddr=>GROUP,
PeerPort=>PORT)
or die "Socket failed: $!";
The new() constructor is failing, but not raising an exception. I don’t know its API: is there some way to get it to tell you why?
Otherwise you might try errno (that is, $!).