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

Hello, I'm new to Perl, found sample telnet code that would like to modify.

I have the following:

ip.list (list of host name and related IP addresses), example:

HOST1, 10.10.10.101

HOST2, 10.10.10.102

etc

Another file has referencing host and commands:

commands.list

HOST1, CommandA

HOST1, CommandB

HOST2, CommandC

HOST3, CommandD

HOST3, CommandE

HOST3, CommandF

etc

My 1st question is, how to match host name in command file to ip.list file and use the IP address to process?

Second, if connection is made to HOST1, issue first command, if second command is for the same HOST, then issue second command. If all commands are done for the connected host, close connection, then establish new connection to the next host and repeat.

#!/usr/bin/perl -w use strict; use Net::Telnet; my $log = './log.txt'; open LOG, ">>$log"; #open log file - append mode open IPS, "<ip.list"; #open ip.list file - read mode while(<IPS>){ my $ip = $_; #current iteration (line) of IPs file $telnet = new Net::Telnet ( Timeout=>10, Errmode=>'die' Prompt => '/# $/i'); $telnet->open($ip); $telnet->login('USERNAME', 'PASSWORD'); if($telnet->cmd('CommandA')){ print LOG "$ip CommandA successful"; }else{ print LOG "$ip Unable to Connect"; } } close LOG; #close files close IPS;
  • Comment on Login to multiple devices using telnet and issue different commands
  • Download Code

Replies are listed 'Best First'.
Re: Login to multiple devices using telnet and issue different commands
by stevieb (Canon) on Aug 25, 2015 at 15:16 UTC

    I would shove the information from the two files into a hash, then iterate over the details in a for loop. I've commented inline. Note the telnet portions are untested:

    use warnings; use strict; use Net::Telnet; my $ip_list = 'ip.list'; my $cmd_list = 'commands.list'; # use the three-arg form of open, # and *always* check for problems open my $ip_fh, '<', $ip_list or die "Can't open IP list $ip_list: $!"; my %hosts; while (my $line = <$ip_fh>){ chomp $line; # split the line into host and ip, # then put this information into the %hosts hash my ($host, $ip) = split /,\s*/, $line; $hosts{$host}{ip} = $ip } close $ip_fh; open my $cmd_fh, '<', $cmd_list or die "Can't open CMD list $cmd_list: $!"; while (my $line = <$cmd_fh>){ # append each command to an array reference # under each host in the hash chomp $line; my ($host, $cmd) = split /,\s*/, $line; push @{$hosts{$host}{commands}}, $cmd; } close $cmd_fh; my $log = './log.txt'; open my $log_fh, '>>', $log or die $!; for my $host (keys %hosts){ # I think the instantiation of the object could # be done outside of the for() loop as long as each # session is closed, but I'm unsure my $telnet = new Net::Telnet ( Timeout=>10, Errmode=>'die', # you were missing a comma here Prompt => '/# $/i', ); # extract the ip for the current host my $ip = $hosts{$host}{ip}; $telnet->open($ip); $telnet->login('USERNAME', 'PASSWORD'); # loop over the command array, # and do something for each one for my $cmd (@{$hosts{$host}{commands}}){ if($telnet->cmd($cmd)){ print $log_fh "$ip $cmd successful"; }else{ print $log_fh "$ip Unable to Connect"; } } } close $log_fh;

    -stevieb

      Thank you for your reply. I got it to work. When each command processes, how do I print results from the command? I tried putting in "for my" section, but print shows only login message (response from device) but no data from the command.
      for my $cmd (@{$hosts{$host}{commands}}){ if(my @check = $telnet->cmd($cmd)){ my $response = join(" ", @check); print "response below \n"; print $response; print $log_fh "$ip $cmd successful\n"; }else{ print $log_fh "$ip Unable to Connect"; } }

        Try adding logging to the session and look at the results.

        $telnet = new Net::Telnet ( Timeout=>10, Errmode=>'die', Prompt => '/# $/i', Input_log => 'input.log', Dump_log => 'dump.log', );
        poj
Re: Login to multiple devices using telnet and issue different commands
by kcott (Archbishop) on Aug 25, 2015 at 16:58 UTC

    G'day acondor,

    Welcome to the Monastery.

    Here's the basic logic I'd use to tackle this problem:

    #!/usr/bin/env perl -l use strict; use warnings; use constant { IP => 0, CMD => 1 }; use Inline::Files; my %data; while (<IPLIST>) { chomp; my ($host, $ip) = split /,\s+/, $_, 2; $data{$host} = [$ip, []]; } while (<CMDLIST>) { chomp; my ($host, $cmd) = split /,\s+/, $_, 2; push @{$data{$host}[CMD]}, $cmd; } for my $host (sort keys %data) { print "Connect to '$host' at '$data{$host}[IP]'"; print "\tRun command '$_'" for @{$data{$host}[CMD]}; print "Disconnect from '$host' at '$data{$host}[IP]'"; } __IPLIST__ HOST1, 10.10.10.101 HOST2, 10.10.10.102 HOST3, 10.10.10.103 __CMDLIST__ HOST1, CommandA HOST1, CommandB HOST2, CommandC HOST3, CommandD HOST3, CommandE HOST3, CommandF

    Output:

    Connect to 'HOST1' at '10.10.10.101' Run command 'CommandA' Run command 'CommandB' Disconnect from 'HOST1' at '10.10.10.101' Connect to 'HOST2' at '10.10.10.102' Run command 'CommandC' Disconnect from 'HOST2' at '10.10.10.102' Connect to 'HOST3' at '10.10.10.103' Run command 'CommandD' Run command 'CommandE' Run command 'CommandF' Disconnect from 'HOST3' at '10.10.10.103'

    Note that I have shown the logic related to your question but I have totally ignored the I/O: input dummied up using Inline::Files; output to STDOUT rather than appending to a logfile.

    You'll need to write the code for the I/O yourself; however, the way you're currently doing this if far from optimal. You should use lexical filehandles, the 3-argument form of open and error checking (possibly using the autodie pragma).

    — Ken