LadyEngineer has asked for the wisdom of the Perl Monks concerning the following question:

Update: Thanks for the help! I'll try the eval ( ) block in the morning.

I'm trying to write a script that will iterate over a list of IP addresses, connecting to each via SSH and executing some commands. It's possible that one or more of the IP addresses could be invalid, in which case I want my script to log that fact and move on to the next address. Instead, the script just halts.

I've tried both Net::SSH::Expect and Net::SSH::Perl.

Net::SSH::Expect code:

#!/usr/bin/perl require Net::SSH; require Net::SSH::Expect; my %hosts = ( "192.168.1.31" => "admin,password", "192.168.1.61" => "admin,password", ); my $ssh; open (LOG,">>./audit.log"); foreach $host (keys %hosts) { myAuditLog(LOG,"Accessing $host . . ."); ($login,$passwd) = split /,/,$hosts{$host}; $ssh = Net::SSH::Expect->new ( host => "$host", user => "$login", password => "$passwd", raw_pty => 1 ); $loginOutput = $ssh->login() || myAuditLog(LOG,"Login has failed. $! +"); # do stuff $ssh->close(); } close(LOG);

Net::SSH::Perl code: Same as above, except replace the Net::SSH::Expect->new and $ssh->login bit with:

$ssh = Net::SSH::Perl->new($host); $ssh->login($login,$passwd) || myAuditLog(LOG,"Login has failed. $!");

(And, obviously, require Net::SSH::Perl.)

In both cases, as soon as $ssh->login is evaluated for an invalid IP address, the script prints an error message to STDOUT. For Net::SSH::Expect the error is:

SSHAuthenticationError Login timed out. The input stream currently has the contents bellow:  at /usr/local/lib/perl5/site_perl/5.10.0/Expect.pm line 828

Then the script just . . . stops. myAuditLog is not evaluated, so I can't get at $! or $loginOutput. (myAuditLog just prints log messages and timestamps to LOG. I've tried printing to STDOUT instead of calling myAuditLog, but the print command doesn't get evaluated either.)

Help! :(

BTW, I do have root access to the machines on which this script will run, so if there's a different module I should be using instead of Net::SSH::Expect or Net::SSH::Perl, feel free to suggest it.

Thanks in advance!

Replies are listed 'Best First'.
Re: Net::SSH::Perl, Net::SSH::Expect crashes script if host is unreachable
by almut (Canon) on Sep 11, 2009 at 22:24 UTC

    Net::SSH::Expect sets up a timeout handler, which croaks (i.e. dies, stopping the script) in case it gets called:

    # line 207 [ timeout => sub { croak SSH_AUTHENTICATION_ERROR . " Login timed ou +t. " . "The input stream currently has the contents bell +ow: " . $self->peek(); } ]

    So, wrap the login call in an eval { } block (Perl's way of catching exceptions), e.g. like this:

    eval { $loginOutput = $ssh->login(); }; if ($@) { myAuditLog(LOG,"Login has failed: $@"); }
      i specified timeout => 20 in the constructor, and problem was solved.As mine was slow network i took 20seconds as timeout.

        I ended up trying the long timeout thing and it did work but it would allow the value(in seconds) of the timeout to pass in between commands.

        For example: timeout = 5

        It will take 5 seconds to login, then it will take 5 seconds for the first command to be issued, then another 5 seconds for the next command, so on so forth. and finally 5 seconds for the close command.

        So, in the end, the process ends up spending way too much time per device and therefore this was a show stopper for me.

        The solution for me (since Salva's solution didn't work for me in this thread Net::OpenSSH multiple commands ) was to remove the username/password login and make it use public-key authentication. So far (knock on wood) I haven't had this problem again and the delay in between the commands is gone. This seems to finally round off this solution for me. Here is a sample of code I used to make this work. (taken from it's documentation)

        # Starting ssh without password # 1) run the constructor my $ssh = Net::SSH::Expect->new ( host => "myserver.com", user => 'bnegrao', raw_pty => 1 ); # 2) now start the ssh process $ssh->run_ssh() or die "SSH process couldn't start: $!"; # 3) you should be logged on now. Test if remote prompt is received: ($ssh->read_all(2) =~ />\s*\z/) or die "where's the remote prompt?" $ssh->exec("whoami"); $ssh->exec("/sbin/ifconfig"); $ssh->exec("ls /"); ssh->close();

        I know this post is old but I hope this helps someone anyway.

Re: Net::SSH::Perl, Net::SSH::Expect crashes script if host is unreachable
by jrsimmon (Hermit) on Sep 11, 2009 at 21:38 UTC

    I had a similar problem and couldn't figure it out. The code snippet below usually works around it for my purposes:

    my $retry_count = 0; while(1){ $rc = eval{$ssh->login();}; last if defined $rc; last if $retry_count >= $max_retry_count; $retry_count++; sleep 1; }
Re: Net::SSH::Perl, Net::SSH::Expect crashes script if host is unreachable
by salva (Canon) on Sep 11, 2009 at 21:01 UTC
    so if there's a different module I should be using instead of Net::SSH::Expect or Net::SSH::Perl, feel free to suggest it.

    You can try Net::OpenSSH, it would even let you run the operations in parallel in all the hosts without too much hassle.