Perl Script to set snmp setting on a router - perl

I want to telnet to router and set snmp setting ( automated rather than typing it manually)
This is what I do manually:
[asafaaya#util-server-1 ~]$ telnet 10.222.24.152
Trying 10.222.24.152...
Connected to 10.222.24.152.
Escape character is '^]'.
Account:admin
Password: *********
Type ? for command help
>mngt snmp -e 1 -g OPTmr9 -s OPTmr9 -t OPTmr9
I tried to automate the above but the script is not working as expected. Appreciated if you could check the code below.
#!/usr/bin/perl
use strict;
use warnings;
use Net::Telnet;
use Regexp::Common qw/ net number /;
use Data::Dumper;
my $username = 'admin';
my $password = 'TuR!!!xxx';
my $router = $ARGV[0];
print Dumper $router;
my $t = new Net::Telnet (Timeout=>10);
$t->open($router);
$t->prompt('/router login:/i');
$t->print($username);
print Dumper $username, "\n";
$t->prompt('/Password:/i');
$t->print($password);
print Dumper $password, "\n";
$t->prompt('/^> /');
$t->cmd("mngt snmp -e 1 -g OPTmr9 -s OPTmr9 -t OPTmr9");
$t->print('exit');

Related

perl : making a script as efficient as a perl one-liner

I'm able to do this on the command line and it works :
~/Tools/perl/edif_extr_cell.pl design.edif nmos1p8v | perl -p -e 's/^/\n/ if /portImplementation|figure\s+device/;' | perl -n -000 -e 'print if /portImplementation/;'
(basically, extracting a section of the EDIF file).
Now, I want to make a utility of this. And my script is below. Question : can this code be more efficient? If feel like it's very inelegant. I could pipe streams easily on the command line but, in a script, I feel lost.
#!/usr/bin/perl -w -p
BEGIN{ $file = '';}
s/^/\n/ if /portImplementation|figure\s+device/;
$file .= $_;
END{
$cmd = q{\rm -f /tmp/dump}.$$.'.txt';
system( $cmd );
open( OUT, ">/tmp/dump$$.txt");
print OUT $file;
close OUT;
$out = `perl -n -000 -e 'print if /portImplementation/;' /tmp/dump$$.txt`;
system( $cmd );
print $out;
}
If I understand correct, you want to be able to do
~/Tools/perl/edif_extr_cell.pl design.edif nmos1p8v | myfilter
Ideally, you'd merge the two Perl scripts into one rather than having one script launch two instances of Perl, but this turns out to be rather hard because of the change to $/ (via -00) and because you insert newlines in the first filter.
The simplest answer:
#!/bin/sh
perl -pe's/^/\n/ if /portImplementation|figure\s+device/' |
perl -00ne'print if /portImplementation/'
It appears that you were trying to write the equivalent of that sh script in Perl. It would look like the following:
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open qw( open3 );
# open3 has issues with lexical file handles.
pipe(local *PIPE_READER, local *PIPE_WRITER)
or die($!);
my $pid1 = open3('<&STDIN', '>&PIPE_WRITER', '>&STDERR',
'perl', '-pes/^/\n/ if /portImplementation|figure\s+device/');
my $pid2 = open3('<&PIPE_READER', '>&STDOUT', '>&STDERR',
'perl', '-00neprint if /portImplementation/');
waitpid($pid1);
waitpid($pid2);
I'd normally recommend IPC::Run3 or IPC::Run for launching and interfacing with child processes, but low-level open3 does the trick nicely in this particular situation.
I downloaded a random EDIF file from GitHub, running the following script on it gives the same output as your code:
#! /usr/bin/perl
use warnings;
use strict;
my #buffer;
my $found;
my $prepend = q();
while (<>) {
if (/portImplementation|figure\s+device/) {
if ($found && #buffer) {
print $prepend, #buffer;
$prepend = "\n";
}
undef $found;
#buffer = ();
}
$found ||= /portImplementation/;
push #buffer, $_;
}
# Don't forget to output the last paragraph!
print $prepend, #buffer if $found && #buffer;

Diff two remote files using Perl

I have an array of file paths:
#files = ('/home/.../file.txt', '/home/.../file2.txt',...);
I have multiple remote machines, with a similar filestructure. How can I diff these remote files using Perl?
I thought of using Perl backticks, ssh and using diff, but I am having issues with sh (it doesn't like diff <() <()).
Is there a good Perl way of comparing at least two remote files?
Use rsync to copy the remote files to the local machine, then use diff to find out the differences:
use Net::OpenSSH;
my $ssh1 = Net::OpenSSH->new($host1);
$ssh1->rsync_get($file, 'master');
my $ssh2 = Net::OpenSSH->new($host2);
system('cp -R master remote');
$ssh2->rsync_get($file, 'remote');
system('diff -u master remote');
You can use the Perl Module on CPAN called Net::SSH::Perl to run remote commands.
Link: http://metacpan.org/pod/Net::SSH::Perl
Example from the Synopsis:
use Net::SSH::Perl;
my $ssh = Net::SSH::Perl->new($host);
$ssh->login($user, $pass);
my($stdout, $stderr, $exit) = $ssh->cmd($cmd);
You command would look something like
my $cmd = "diff /home/.../file.txt /home/.../file2.txt";
edit: The files are on different servers.
You can still use Net::SSH::Perl to read the files.
#!/bin/perl
use strict;
use warnings;
use Net::SSH::Perl;
my $host = "First_host_name";
my $user = "First_user_name";
my $pass = "First_password";
my $cmd1 = "cat /home/.../file1";
my $ssh = Net::SSH::Perl->new($host);
$ssh->login($user, $pass);
my($stdout1, $stderr1, $exit1) = $ssh->cmd($cmd1);
#now stdout1 has the contents of the first file
$host = "Second_host_name";
$user = "Second_user_name";
$pass = "Second_password";
my $cmd2 = "cat /home/.../file2";
$ssh = Net::SSH::Perl->new($host);
$ssh->login($user, $pass);
my($stdout2, $stderr2, $exit2) = $ssh->cmd($cmd2);
#now stdout2 has the contents of the second file
#write the contents to local files to diff
open(my $fh1, '>', "./temp_file1") or DIE "Failed to open file 1";
print $fh1 $stdout1;
close $fh1;
open(my $fh2, '>', "./temp_file2") or DIE "Failed to open file 2";
print $fh2 $stdout2;
close $fh2;
my $difference = `diff ./temp_file1 ./temp_file2`;
print $difference . "\n";
I haven't tested this code, but you could do something like this. Remember to download the Perl Module Net::SSH::Perl to run remote commands.
Diff is not implemented in the Perl Core Modules, but there another called Text::Diff on CPAN so maybe that would work too. Hope this helps!

Safe system call with multiple commands with perl

I have a Perl script that reads some information from a web form. In order to do proper sanitation, I want to use the system syntax described here.
They suggest that you should form system commands in the following form system ("cat", "/usr/stats/$username"); so that the username variable would only get interpreted as a argument to cat.
If I had a command that had the form of system("export REPLYTO=\"$from\"; echo \"$body\" | mail -s \"$subject\""); which has multiple system commands, how can I properly sanitize the system call?
Before I start, note that you can do the export in Perl by setting $ENV{REPLY_TO}.
Option 1.
You can use String::ShellQuote's shell_quote.
use autodie qw( :all );
my $cmd = shell_quote('echo', $body) .
'|' . shell_quote('mail', '-s', $subject);
local $ENV{REPLY_TO} = $from;
system($cmd);
Option 2.
Pass everything by env var.
use autodie qw( :all );
local $ENV{REPLY_TO} = $from;
local $ENV{SUBJECT} = $subject;
local $ENV{BODY} = $body;
system('echo "$BODY" | mail -s "$SUBJECT"');
Option 3.
Get rid of echo
use autodie qw( :all );
local $ENV{REPLY_TO} = $from;
open(my $pipe, '|-', 'mail', '-s', $subject);
print($pipe $body);
close($pipe);
die "Child died from signal ".($? & 0x7F)."\n" if $? & 0x7F;
die "Child exited from error ".($? >> 8)."\n" if $? >> 8;

Unknown reason for Known error in the perl script

I am writing following script to read list of servers from the text file them ssh to them and run ldd command to fetch the version that is installed on the server.
The only problem is the error that I am seeing following error which says Bad Host name:
adev#abclnxdev:[/home/adev/perl-scripts] {63} % perl try.pl
Net::SSH: Bad host name: abclnxtest01
at try.pl line 21
when I manually do the ssh to this host. It gets connect.
Here is script :
#!/mu/bin/perl
use Net::SSH::Perl;
use warnings;
my $file = "server-list.txt";
my $usr = "user";
my $pwd = "password";
my $output_file = "GlibC-version.txt";
open(HANDLE, $file) or die("Cant open the file :( ");
#server_list = <HANDLE>;
close(HANDLE);
#debug_print_array(#server_list);
open(HANDLE, ">>$output_file"); #opening file for output.
foreach $host (#server_list)
{
my $ssh = Net::SSH::Perl->new($host);
$ssh->login($usr,$pwd,$ssh);
my($stdout, $stderr, $exit) = $ssh->cmd("ldd --version|grep ldd");
print HANDLE "----------------------------------";
print HANDLE "Hostname : $host";
print HANDLE "GLIBC Version : $stdout";
print HANDLE "----------------------------------\n\n";
}
You have a newline at the end of the server name.
Add:
chomp #server_list;
(And incidentally, it's better to use the newer 3-argument open(); see http://perlmaven.com/open-files-in-the-old-way .)

How can I ping a host with a Perl one-liner with Net::Ping?

Trying to integrate the following Perl one-liner into a shell script. This code works within a Perl script but not as a one-liner executed from a shell script.
I've tried replacing $host with a real hostname with no luck.
#!/bin/ksh
hosts="host1 host2 host3"
PERL=/usr/bin/perl
# Check to see if hosts are accessible.
for host in $hosts
do
#echo $host
$PERL -e 'use Net::Ping; $timeout=5; $p=Net::Ping->new("icmp", $timeout) or die bye ; print "$host is alive \n" if $p->ping($host); $p->close;'
done
The single quotes in the shell stop the $host from being interpreted. So you can just stop and restart the single quotes as required:
perl -MNet::Ping -e 'if (Net::Ping->new("icmp", 5)->ping("'$host'")) {print "'$host' is alive\n"}'
Alternatively, you can pass the host in as a parameter - see the other answer.
Try to replace $host:
$PERL -e 'use Net::Ping; $timeout=5; $p=Net::Ping->new("icmp", $timeout) or die bye ; print "$host is alive \n" if $p->ping($host); $p->close;'
with $ARGV[0], the first command line argument:
$PERL -e 'use Net::Ping; $timeout=5; $p=Net::Ping->new("icmp", $timeout) or die bye ; print "$ARGV[0] is alive \n" if $p->ping($ARGV[0]); $p->close;' $host
If you want to use Perl, then use the Perl interpreter to run your script.
#!/usr/bin/env perl -w
use Net::Ping;
$timeout=5;
$p=Net::Ping->new("icmp", $timeout) or die bye ;
#hosts=qw/localhost 10.10.10.10/;
foreach my $host (#hosts) {
print "$host is alive \n" if $p->ping($host);
}
$p->close;
Otherwise, you might as well use the ping command directly from the shell
#!/bin/bash
for hosts in host1 host2 host3
do
if ping ...... "$hosts" >/dev/null ;then
.....
fi
done