Problems with "show tech" using the Net::Telnet Module

A

Asterix

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);
 
S

sln

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;
}
 
M

Martijn Lievaart

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:

There may be a completely other solution. Have a look at rancid and it's
clogin command. Maybe rancid already does what you are trying to do.

HTH,
M4
 
A

Asterix

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
 
S

sln

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
 
S

sln

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top