From: Asterix on
I've made a little script to capture the output of networking
devices.
Everything works fine, until I use the "show tech" command in an Cisco
device.
The script does not save anything form the "show tech",
but I can see that the TELNET Session gets back some output.
But I can't figure out how to save this output from "show tech" into a
file.
There is no problem with commands which produce smaller output.
I don't want to use the "Net::Telnet:Cisco" Module:

#!/usr/bin/perl
# Autor: Ing. Gerhard Hermann Lange
# Date: 12/04/2010
# Description: Script for doing a TELNET to a CISCO devices and
executing some comands.
# Long outputs from the TELNET Session (-> show tech) is not
possible
# due to the TELNET session timeout of 5 seconds
use Net::Telnet;
#
my $prg_name = $0; $prg_name =~ s|^.*[\\/]||;
# Usage:
my $usage="Syntax: $prg_name <hostname> <ip> <username> <password>
<enable-pwd>\n";
if (! defined ($ARGV[0])) { print "$usage"; exit; }
#
$SIG{'INT'} = sub {
# Subprocedure for finishing all the work
# printf OUTPUT_FILE "@output\n\n";
close(OUTPUT_FILE);
print STDERR "Termination of program ... !\n";
exit;
};
#
$hostname = $ARGV[0];
$ip = $ARGV[1];
$username = $ARGV[2];
$password = $ARGV[3];
$enpwd = $ARGV[4];
$cmd_file = "cmds.txt";
#
my ($sec, $min, $hr, $mday, $mon, $year, @etc) = localtime(time);
$mon++; $year=$year+1900;
my $now=sprintf("%.4d%.2d%.2d%.2d%.2d", $year, $mon, $mday, $min,
$sec);
my $today=sprintf("%.2d/%.2d/%.4d", $mday, $mon, $year );
#
# $out_file=$ip;
$out_file=$ip."_$now".".cfg";
#
# For debugging the TELNET Session: uncomment this line !
# $filename="telnet_dump.txt";
#
$errmode='return'; # Default: 'die'
#
# Set cmd_remove_mode to the number of lines to remove (0 in this
case).
# http://www.perlmonks.org/?node_id=736670
$errmode="return";
$telnet = new Net::Telnet ( Timeout=>5, Errmode=>$errmode ,
cmd_remove_mode => '0', Dump_Log =>
$filename);

# $telnet = new Net::Telnet ( Timeout=>3, Errmode=>$errmode ,
Output_record_separator => "\r",cmd_remove_mode => '2', Dump_Log =>
$filename);
# -output_record_separator => "\r");
$telnet->open($ip);
#
$telnet->waitfor('/Username: $/i');
$telnet->print($username);
$telnet->waitfor('/Password: $/i');
$telnet->print($password);
#
# Wait for the prompt, send "enable" and enable-pwd
@output = $telnet->waitfor('/>/');
$telnet->print('enable');
$telnet->waitfor('/Password:/i');
$telnet->print($enpwd);
#
@output = $telnet->waitfor('/#/i');
#
open(OUTPUT_FILE, ">>$out_file");
printf OUTPUT_FILE "IP Address: $ip\n";
printf OUTPUT_FILE "Hostname: $hostname\n";
printf OUTPUT_FILE "Data: $today\n";
printf OUTPUT_FILE "\n";
#
# Creat an universal Prompt for this hostname
my $prompt = $hostname."#";
$telnet->prompt("/$prompt\$/");
@output = $telnet->cmd(String => "terminal length 0");
printf OUTPUT_FILE "@output\n\n";
#
open(CMDFILE, "<$cmd_file");
while (my $record = <CMDFILE>) {
if ($record !~ /^(#|!|\s)/) {
chomp($record);
my @record_array = (split(/;/,$record));
my $command = $record_array[0]; chomp($cmd);
#
$telnet->prompt("/$prompt\$/");
@output = $telnet->cmd(String => "$command", Timeout=>30);
#
printf OUTPUT_FILE "@output\n\n";
}
}
# Close the command File for this device
close(CMDFILE);
#
$telnet->print('exit');
close(OUTPUT_FILE);
From: sln on
On Mon, 19 Apr 2010 10:49:37 -0700 (PDT), Asterix <lange.gerhard(a)gmail.com> wrote:

>I've made a little script to capture the output of networking
>devices.
>Everything works fine, until I use the "show tech" command in an Cisco
>device.
>The script does not save anything form the "show tech",
>but I can see that the TELNET Session gets back some output.
>But I can't figure out how to save this output from "show tech" into a
>file.
>There is no problem with commands which produce smaller output.
>I don't want to use the "Net::Telnet:Cisco" Module:
>
>#!/usr/bin/perl
># Autor: Ing. Gerhard Hermann Lange
># Date: 12/04/2010
># Description: Script for doing a TELNET to a CISCO devices and
>executing some comands.
># Long outputs from the TELNET Session (-> show tech) is not
>possible
># due to the TELNET session timeout of 5 seconds
>use Net::Telnet;
>#
>my $prg_name = $0; $prg_name =~ s|^.*[\\/]||;
># Usage:
>my $usage="Syntax: $prg_name <hostname> <ip> <username> <password>
><enable-pwd>\n";
>if (! defined ($ARGV[0])) { print "$usage"; exit; }
>#
>$SIG{'INT'} = sub {
> # Subprocedure for finishing all the work
> # printf OUTPUT_FILE "@output\n\n";
> close(OUTPUT_FILE);
> print STDERR "Termination of program ... !\n";
> exit;
>};
>#
>$hostname = $ARGV[0];
>$ip = $ARGV[1];
>$username = $ARGV[2];
>$password = $ARGV[3];
>$enpwd = $ARGV[4];
>$cmd_file = "cmds.txt";
>#
>my ($sec, $min, $hr, $mday, $mon, $year, @etc) = localtime(time);
>$mon++; $year=$year+1900;
>my $now=sprintf("%.4d%.2d%.2d%.2d%.2d", $year, $mon, $mday, $min,
>$sec);
>my $today=sprintf("%.2d/%.2d/%.4d", $mday, $mon, $year );
>#
># $out_file=$ip;
>$out_file=$ip."_$now".".cfg";
>#
># For debugging the TELNET Session: uncomment this line !
># $filename="telnet_dump.txt";
>#
>$errmode='return'; # Default: 'die'
>#
># Set cmd_remove_mode to the number of lines to remove (0 in this
>case).
># http://www.perlmonks.org/?node_id=736670
>$errmode="return";
>$telnet = new Net::Telnet ( Timeout=>5, Errmode=>$errmode ,
>cmd_remove_mode => '0', Dump_Log =>
>$filename);
>
># $telnet = new Net::Telnet ( Timeout=>3, Errmode=>$errmode ,
>Output_record_separator => "\r",cmd_remove_mode => '2', Dump_Log =>
>$filename);
># -output_record_separator => "\r");
>$telnet->open($ip);
>#
>$telnet->waitfor('/Username: $/i');
>$telnet->print($username);
>$telnet->waitfor('/Password: $/i');
>$telnet->print($password);
>#
># Wait for the prompt, send "enable" and enable-pwd
>@output = $telnet->waitfor('/>/');
>$telnet->print('enable');
>$telnet->waitfor('/Password:/i');
>$telnet->print($enpwd);
>#
>@output = $telnet->waitfor('/#/i');
>#
>open(OUTPUT_FILE, ">>$out_file");
>printf OUTPUT_FILE "IP Address: $ip\n";
>printf OUTPUT_FILE "Hostname: $hostname\n";
>printf OUTPUT_FILE "Data: $today\n";
>printf OUTPUT_FILE "\n";
>#
># Creat an universal Prompt for this hostname
>my $prompt = $hostname."#";
>$telnet->prompt("/$prompt\$/");
>@output = $telnet->cmd(String => "terminal length 0");
>printf OUTPUT_FILE "@output\n\n";
>#
>open(CMDFILE, "<$cmd_file");
>while (my $record = <CMDFILE>) {
> if ($record !~ /^(#|!|\s)/) {
> chomp($record);
> my @record_array = (split(/;/,$record));
> my $command = $record_array[0]; chomp($cmd);
> #
> $telnet->prompt("/$prompt\$/");
> @output = $telnet->cmd(String => "$command", Timeout=>30);
> #
> printf OUTPUT_FILE "@output\n\n";
> }
>}
># Close the command File for this device
>close(CMDFILE);
>#
>$telnet->print('exit');
>close(OUTPUT_FILE);

Its hard to tell what the problem is.

What does the LOG tell you? Where does that stop at?
Does it complete all the commands?

If something doesen't go right you should ISOLATE it, and just send that
command to find out why!

Chop up your code into a test bed for that item. Do enough to log in
and send the command then log off (which btw, you don't end the session
in the code).

Some things you can try:

- Use dump_log() to debug (but I think you do this).

- max_buffer_length($len); the default is 1 MB, take it up to 10

- Consider using a combination of print() and waitfor() as an alternative
to cmd() if the command you send prompts for input.
** The last time I used Telnet (2004), I only used this combination.
You can still capture the output if you use waitfor() in list context.

- When mode is "return" then the method generating the error places an
error message in the object and returns an undefined value in a scalar
context and an empty list in list context.
The error message may be obtained using errmsg().
** You have set this, but you don't check for !@output to see if
there was an error. Try using the default 'die' error mode.

- Make sure the server shell is not in console mode that buffers output.
You want it in line or stream mode.

Finally, using the print()-waitfor() pair, try to modify the below
method so its a function. You can use the $prematch to capture the output.
But more importantly, it gives you a finer granularity and more flexibility
deciphering the output stream. (There is a whole module this goes with,
but I didn't include it because it was written with a broader scope than
what your looking for).

-sln

#####################################################
# Send command and wait for reply
# - May wait for one of many reply regxs' passed in
# via the 'Reply' array. Each MUST be single
# quoted regex expressions. ie: '/any/i'
# IN:
# cmd - the shell command or program
# waitsecs - total secs willing to wait (up to)
# show_wait - 'yes' shows the seconds while waiting
# Reply - list of matches will wait for
# OUT:
# Returns index+1 into the 'Reply' list passed in,
# of the first match found in reply stream.
# Otherwise returns 0, meaning timeout or other
# error (check $self->{'Error'})
#####################################################
sub SendCommand
{
my ($self, $cmd, $waitsecs, $show_wait, @Reply) = @_;
my ($pre, $match);

if (!defined $tln) {
$self->{'Error'} = "Session not open";
return 0;
}
$waitsecs = $self->{'Waitsecs'} unless (defined $waitsecs);
$show_wait = $self->{'Show_Wait'} unless (defined $show_wait);

my @args = ('Timeout', 0);
if (@Reply == 0) { push (@Reply, $self->{'Prompt'}) }
for (@Reply) {
push (@args, 'Match');
push (@args, $_);
}
my $savedtimeout = $tln->timeout(0);
$tln->print ($cmd);
print "Sent: $cmd\n" if ($self->{'Debug'});

for (my $i = 0; $i < $waitsecs; $i++) {
($pre, $match) = $tln->waitfor(@args);
if (!$tln->timed_out) {
print "\rRecieved ($i seconds): $match \n" if ($self->{'Debug'});
print "\n$pre\n" if (lc($self->{'Show_Prematch'}) eq 'yes');
last;
}
sleep (1);
if ($show_wait eq lc('yes')) {
print "\rWait progress: ".($i+1)." seconds " ;
print "\n" if ($i == ($waitsecs-1));
}
}
$tln->timeout($savedtimeout);

## check if timed out
if ($tln->timed_out) {
print "\r** WAIT EXPIRED - $waitsecs seconds ** \n" if ($self->{'Debug'});
$self->{'Error'} = "Timed out ($waitsecs) executing command: $cmd";
return 0;
}
## return the index of the matched @Reply
#return 1 if (!@Reply);
my $pos = 0;
for (@Reply) {
$pos++;
my $patcheck = "last if (\$match =~ $_);"; # pattern match check
#print "$patcheck\n";
eval $patcheck;
}
$self->{'Error'} = '';
return $pos;
}

From: Asterix on
On 20 Apr, 00:44, s...(a)netherlands.com wrote:
> On Mon, 19 Apr 2010 10:49:37 -0700 (PDT),Asterix<lange.gerh...(a)gmail.com> wrote:
> >I've made a little script to capture the output of networking
> >devices.
> >Everything works fine, until I use the "show tech" command in an Cisco
> >device.
> >The script does not save anything form the "show tech",
> >but I can see that theTELNETSession gets back some output.
> >But I can't figure out how to save this output from "show tech" into a
> >file.
> >There is no problem with commands which produce smaller output.
> >I don't want to use the "Net::Telnet:Cisco" Module:
>
> >#!/usr/bin/perl
> ># Autor:            Ing. Gerhard Hermann Lange
> ># Date:             12/04/2010
> ># Description:      Script for doing aTELNETto a CISCO devices and
> >executing some comands.
> >#                           Long outputs from theTELNETSession (->show tech) is not
> >possible
> >#                           due to theTELNETsession timeout of 5 seconds
> >use Net::Telnet;
> >#
> >my $prg_name = $0; $prg_name =~ s|^.*[\\/]||;
> ># Usage:
> >my $usage="Syntax: $prg_name <hostname> <ip> <username> <password>
> ><enable-pwd>\n";
> >if (! defined ($ARGV[0])) { print "$usage"; exit; }
> >#
> >$SIG{'INT'} = sub {
> >    # Subprocedure for finishing all the work
> >    # printf OUTPUT_FILE "@output\n\n";
> >    close(OUTPUT_FILE);
> >    print STDERR "Termination of program ... !\n";
> >    exit;
> >};
> >#
> >$hostname = $ARGV[0];
> >$ip = $ARGV[1];
> >$username = $ARGV[2];
> >$password = $ARGV[3];
> >$enpwd = $ARGV[4];
> >$cmd_file = "cmds.txt";
> >#
> >my ($sec, $min, $hr, $mday, $mon, $year, @etc) = localtime(time);
> >$mon++; $year=$year+1900;
> >my $now=sprintf("%.4d%.2d%.2d%.2d%.2d", $year, $mon, $mday, $min,
> >$sec);
> >my $today=sprintf("%.2d/%.2d/%.4d", $mday, $mon, $year );
> >#
> ># $out_file=$ip;
> >$out_file=$ip."_$now".".cfg";
> >#
> ># For debugging theTELNETSession: uncomment this line !
> ># $filename="telnet_dump.txt";
> >#
> >$errmode='return'; # Default: 'die'
> >#
> ># Set cmd_remove_mode to the number of lines to remove (0 in this
> >case).
> >#http://www.perlmonks.org/?node_id=736670
> >$errmode="return";
> >$telnet= new Net::Telnet( Timeout=>5, Errmode=>$errmode ,
> >cmd_remove_mode => '0', Dump_Log =>
> >$filename);
>
> ># $telnet= new Net::Telnet( Timeout=>3, Errmode=>$errmode ,
> >Output_record_separator => "\r",cmd_remove_mode => '2', Dump_Log =>
> >$filename);
> ># -output_record_separator => "\r");
> >$telnet->open($ip);
> >#
> >$telnet->waitfor('/Username: $/i');
> >$telnet->print($username);
> >$telnet->waitfor('/Password: $/i');
> >$telnet->print($password);
> >#
> ># Wait for the prompt, send "enable" and enable-pwd
> >@output = $telnet->waitfor('/>/');
> >$telnet->print('enable');
> >$telnet->waitfor('/Password:/i');
> >$telnet->print($enpwd);
> >#
> >@output = $telnet->waitfor('/#/i');
> >#
> >open(OUTPUT_FILE, ">>$out_file");
> >printf OUTPUT_FILE "IP Address: $ip\n";
> >printf OUTPUT_FILE "Hostname:   $hostname\n";
> >printf OUTPUT_FILE "Data:       $today\n";
> >printf OUTPUT_FILE "\n";
> >#
> ># Creat an universal Prompt for this hostname
> >my $prompt = $hostname."#";
> >$telnet->prompt("/$prompt\$/");
> >@output = $telnet->cmd(String => "terminal length 0");
> >printf OUTPUT_FILE "@output\n\n";
> >#
> >open(CMDFILE, "<$cmd_file");
> >while (my $record = <CMDFILE>) {
> >    if ($record !~ /^(#|!|\s)/) {
> >            chomp($record);
> >            my @record_array = (split(/;/,$record));
> >            my $command = $record_array[0]; chomp($cmd);
> >            #
> >            $telnet->prompt("/$prompt\$/");
> >            @output = $telnet->cmd(String => "$command", Timeout=>30);
> >            #
> >            printf OUTPUT_FILE "@output\n\n";
> >    }
> >}
> ># Close the command File for this device
> >close(CMDFILE);
> >#
> >$telnet->print('exit');
> >close(OUTPUT_FILE);
>
> Its hard to tell what the problem is.
>
> What does the LOG tell you? Where does that stop at?
> Does it complete all the commands?
>
> If something doesen't go right you should ISOLATE it, and just send that
> command to find out why!
>
> Chop up your code into a test bed for that item. Do enough to log in
> and send the command then log off (which btw, you don't end the session
> in the code).
>
> Some things you can try:
>
>   - Use dump_log() to debug (but I think you do this).
>
>   - max_buffer_length($len);  the default is 1 MB, take it up to 10
>
>   - Consider using a combination of print() and waitfor() as an alternative
>     to cmd() if the command you send prompts for input.
>     ** The last time I usedTelnet(2004), I only used this combination..
>        You can still capture the output if you use waitfor() in list context.
>
>   - When mode is "return" then the method generating the error places an
>     error message in the object and returns an undefined value in a scalar
>     context and an empty list in list context.
>     The error message may be obtained using errmsg().
>     ** You have set this, but you don't check for !@output to see if
>        there was an error. Try using the default 'die' error mode..
>
>   - Make sure the server shell is not in console mode that buffers output.
>     You want it in line or stream mode.
>
> Finally, using the print()-waitfor() pair, try to modify the below
> method so its a function. You can use the $prematch to capture the output..
> But more importantly, it gives you a finer granularity and more flexibility
> deciphering the output stream. (There is a whole module this goes with,
> but I didn't include it because it was written with a broader scope than
> what your looking for).
>
> -sln
>
> #####################################################
> # Send command and wait for reply
> # - May wait for one of many reply regxs' passed in
> #   via the 'Reply' array. Each MUST be single
> #   quoted regex expressions. ie: '/any/i'
> # IN:
> #  cmd       - the shell command or program
> #  waitsecs  - total secs willing to wait (up to)
> #  show_wait - 'yes' shows the seconds while waiting
> #  Reply     - list of matches will wait for
> # OUT:
> #  Returns index+1 into the 'Reply' list passed in,
> #  of the first match found in reply stream.
> #  Otherwise returns 0, meaning timeout or other
> #  error (check $self->{'Error'})
> #####################################################
> sub SendCommand
> {
>         my ($self, $cmd, $waitsecs, $show_wait, @Reply) = @_;
>         my ($pre, $match);
>
>         if (!defined $tln) {
>                 $self->{'Error'} = "Session not open";
>                 return 0;
>         }
>         $waitsecs = $self->{'Waitsecs'} unless (defined $waitsecs);
>         $show_wait = $self->{'Show_Wait'} unless (defined $show_wait);
>
>         my @args = ('Timeout', 0);
>         if (@Reply == 0) { push (@Reply, $self->{'Prompt'}) }
>         for (@Reply) {
>                 push (@args, 'Match');
>                 push (@args, $_);
>         }
>         my $savedtimeout = $tln->timeout(0);
>         $tln->print ($cmd);
>         print "Sent:  $cmd\n" if ($self->{'Debug'});
>
>         for (my $i = 0; $i < $waitsecs; $i++) {
>                 ($pre, $match) = $tln->waitfor(@args);
>                 if (!$tln->timed_out) {
>                         print "\rRecieved ($i seconds):   $match               \n" if ($self->{'Debug'});
>                         print "\n$pre\n" if (lc($self->{'Show_Prematch'}) eq 'yes');
>                         last;
>                 }
>                 sleep (1);
>                 if ($show_wait eq lc('yes')) {
>                         print "\rWait progress:  ".($i+1)." seconds           " ;
>                         print "\n" if ($i == ($waitsecs-1));
>                 }
>         }
>         $tln->timeout($savedtimeout);
>
>         ## check if timed out
>         if ($tln->timed_out) {
>                 print "\r** WAIT EXPIRED - $waitsecs seconds **                           \n" if ($self->{'Debug'});
>                 $self->{'Error'} = "Timed out ($waitsecs) executing command:  $cmd";
>                 return 0;
>         }
>         ## return the index of the matched @Reply
>         #return 1 if (!@Reply);
>         my $pos = 0;
>         for (@Reply) {
>                 $pos++;
>                 my $patcheck = "last if (\$match =~ $_);"; # pattern match check
>                 #print "$patcheck\n";
>                 eval $patcheck;
>         }
>         $self->{'Error'} = '';
>         return $pos;
>
>
>
> }- Nascondi testo citato
>
> - Mostra testo citato -- Nascondi testo citato
>
> - Mostra testo citato -

========

Thank you for your response !
I've figured out the problem. It was not a problem regarding TELNET
but regarding the flushing of the
Buffer verso STDOUT:

Instead of this printf OUTPUT_FILE "@output\n\n";
I've used print OUTPUT_FILE "@output\n\n";

At the top of the script I also inserted "Autoflush":
local $| = 1; # Default is 0

http://perldoc.perl.org/perlvar.html


From: sln on
On Sat, 24 Apr 2010 11:28:55 -0700 (PDT), Asterix <lange.gerhard(a)gmail.com> wrote:

>I've figured out the problem. It was not a problem regarding TELNET
>but regarding the flushing of the
>Buffer verso STDOUT:
>
>Instead of this printf OUTPUT_FILE "@output\n\n";
>I've used print OUTPUT_FILE "@output\n\n";
>
>At the top of the script I also inserted "Autoflush":
>local $| = 1; # Default is 0
>
>http://perldoc.perl.org/perlvar.html
>

It always amazes me that people write all-encompassing
complete code and look for problem solutions at the
ends of the chain instead of the links themselves.

In list context, on sucess of cmd(), the sizeof @output is at least 1,
otherwise there was an error, and with errormode set to "return",
its size is 0.

Debugging is subtle. People usually develop a sixth sense
over time, unless they write perfect code.
You should have found this earlier.

-sln
From: sln on
On Sat, 24 Apr 2010 11:28:55 -0700 (PDT), Asterix <lange.gerhard(a)gmail.com> wrote:

>I've figured out the problem. It was not a problem regarding TELNET
>but regarding the flushing of the
>Buffer verso STDOUT:
>
>Instead of this printf OUTPUT_FILE "@output\n\n";
>I've used print OUTPUT_FILE "@output\n\n";
>
>At the top of the script I also inserted "Autoflush":
>local $| = 1; # Default is 0
>
>http://perldoc.perl.org/perlvar.html
>

The docs say $| only sets the current selected filehandle,
which is usually STDOUT, which is line buffered anyway if its output
goes to a terminal.

To tell it to autoflush your handle you could do it a couple
of ways:

my $oldfh = select(OUTPUT_FILE); $| = 1; select($oldfh);

## or ..

use IO::Handle;
OUTPUT_FILE->autoflush(1);

========================
use strict;
use warnings;

my $oldfh = select(OUTPUT_FILE); $| = 1; select($oldfh);
open (OUTPUT_FILE,'>', 'some.txt') or die $!;
my @output = qw(one two three);
printf OUTPUT_FILE "@output\n\n";

<>;
__END__

-sln