Hi jlc!

On Tuesday 02 Mar 2010 06:38:09 Joseph L. Casale wrote:
> Hi,
> I should state first I don't have the luxury of using Perl often
> and am super rusty:)
> 
> I have a script that sends snmp commands to a switch either by passing
> args directly or as stdin. It works when passing them directly, but
> fails when feeding them as stdin, which is ultimately the way it needs
> to be run.
> 
> I would be grateful if anyone could lend a suggestion, go easy on me if
> you see something silly, I'd be glad to take any pointers.
> 
> The only pre-reqs for this are that arguments are passes as follows:
> argument=value
> and the various return values, mostly 0|1. The input being passed is as
> the spec requires, and has all the values I otherwise pass by dashed args
> directly.
> 
> Thanks,
> jlc
> 

I'm commenting on your code below with some general remarks. Not sure if this 
will fix your problem.

> 
> 
> #!/usr/bin/perl
> 
> # Sets switch port on a procurve up or down
> 
> use Getopt::Std;
> use Net::SNMP;
> 

You should add «use strict;» and «use warnings;» and fix all the problems that 
they give you. This will make your script more robust.

Why are you using "Getopt::Std" instead of Getopt::Long? G::L is much more 
recommended.

> my $OID_ifAdminStatus = '1.3.6.1.2.1.2.2.1.7.';
> 
> # Get the program name from $0 and strip directory names
> $_=$0;

Ahmmm... please don't assign this to $_. $_ can be modified too easily.

Do:

<<<
my $progname = $0;
>>>

Or whatever.

> $|=1;

Using IO::Handle's Flush would be more readable and more idiomatic here.

> s/.*\///;
> my $pname = $_;

OK, so you did it. Do:

my $pname = $0;
$pname =~ s/.*\///;

But better use File::Basename ,  File::Spec and friends.

> 
> sub usage
> {
>       print "Usage:\n";
>       print "\n";
>       print "$pname [options]\n";
>       print "\n";
>       print "Options:\n";
> 
>       print "  -s <string>      switch address\n";
>       print "  -p <int>         port\n";
>       print "  -o <string>      action (off(default)|on|[status|
monitor])\n";
>       print "  -a <string>      authProtocol (md5 or sha1)\n";
>       print "  -A <string>      authPassword\n";
>       print "  -u <string>      secName\n";
>       print "  -x <string>      privProtocol (des, 3des, aes or aes128)\n";
>       print "  -X <string>      privPassword\n";
>       print "  -h               help\n";
>       print "  -V               version\n";
>       print "  -d               debug mode\n";
> 
>       exit 0;
> }
> 

Use a here-document here.

> sub fail
> {
>       ($msg)=...@_;
>       print $msg."\n" unless !defined $opt_d;
>       $t->close if defined $t;
>       exit 1;
> }

You need my $msg (which would be caught if you used strict. 

> 
> sub fail_usage
> {
>       ($msg)=...@_;
>       print STDERR $msg."\n" if $msg;
>       print STDERR "Please use '-h' for usage.\n";
>       exit 1;
> }
> 
> 
> sub version
> {
>       print "$pname 1.0\n";
>       exit 0;
> }
> 
> if (@ARGV > 0)
> {
>       getopts("s:p:o:a:A:u:x:X:hVd") || fail_usage ;
> 
>       usage if defined $opt_h;
>       version if defined $opt_V;
> 
>       fail_usage "Unknown parameter." if (@ARGV > 0);
> 
>       $address       = $opt_s if defined $opt_s;
>       $port          = $opt_p if defined $opt_p;
>       $action        = $opt_o if defined $opt_o;
>       $authProtocol  = $opt_a if defined $opt_a;
>       $authPassword  = $opt_A if defined $opt_A;
>       $secName       = $opt_u if defined $opt_u;
>       $privProtocol  = $opt_x if defined $opt_x;
>       $privPassword  = $opt_X if defined $opt_X;
> }
> else
> {
>       get_options_stdin();
> }
> 

Please convert this to Getopt::Long.

> $action = "off" unless defined $action;
> 
> fail "failed: no switch address defined" unless defined $address;
> fail "failed: no port defined" unless defined $port;
> fail "failed: no authProtocol defined" unless defined $authProtocol;
> fail "failed: no authPassword defined" unless defined $authPassword;
> fail "failed: no secName defined" unless defined $secName;
> fail "failed: no privProtocol defined" unless defined $privProtocol;
> fail "failed: no privPassword defined" unless defined $privPassword;
> fail "failed: unrecognised action: $action" unless ($action =~
> /^(off|on|status|monitor)$/i);

This seem like a lot of duplicate functionality to the options assignment.

> 
> sub get_options_stdin
> {
>       my $opt;
>       my $line = 0;
>       while( defined($in = <>) )

while (my $in = <>) would be enough here.

>       {
>               $_ = $in;

Don't assign to $_. You can also do «while (<>)» but please avoid abusing $_ 
in serious scripts.

>               chomp;
> 
>               # strip leading and trailing whitespace
>               s/^\s*//;
>               s/\s*$//;
> 
>               # skip comments
>               next if /^#/;
> 
>               $line+=1;

$line++; is better.

>               $opt=$_;
>               next unless $opt;
> 
>               ($name,$val)=split /\s*=\s*/, $opt;
> 
>               if ( $name eq "" )
>               {
>                       print STDERR "parse error: illegal name in option 
$line\n";
>                       exit 2;
>               }
>               # DO NOTHING -- this field is not used by fenced
>               elsif ($name eq "agent" ) { }
>               elsif ($name eq "ipaddr" ) { $address = $val; }
>               elsif ($name eq "port" ) { $port = $val; }
>               elsif ($name eq "option" ) { $action = $val; }
>               elsif ($name eq "authProtocol" ) { $authProtocol = $val; }
>               elsif ($name eq "authPassword" ) { $authPassword = $val; }
>               elsif ($name eq "login" ) { $secName = $val; }
>               elsif ($name eq "privProtocol" ) { $privProtocol = $val; }
>               elsif ($name eq "passwd" ) { $privPassword = $val; }
>               elsif ($name eq "debug" ) { $opt_d = $val; }

You should place all these variables inside a hash and avoid varvarname.

>>>
>               # FIXME should we do more error checking?
>               # Excess name/vals will be eaten for now
>               else { fail "parse error: unknown option \"$opt\""; }
>       }
> }
> 
> # Get status of port.
> # Return 0 if 'up', Return 2 if 'down' or Return 1 if otherwise.
> sub get_status
> {
>       my ($session, $error) = Net::SNMP->session(
>               -hostname  => $address,
>               -version=> 'snmpv3',
>               -username  => $secName,
>               -authprotocol => $authProtocol,
>               -authpassword=> $authPassword,
>               -privprotocol => $privProtocol,
>               -privpassword=> $privPassword,
>       );
> 
>       if (!defined $session) {
>       printf "ERROR: %s.\n", $error
>               unless defined $opt_d;
>       exit 1;
>       }

Why can't you use perldoc -f die here?

> 
>       my $result = $session->get_request(
>       -varbindlist => [ $OID_ifAdminStatus . $port],
>       );
> 
>       if (!defined $result) {
>       printf "ERROR: %s.\n", $session->error()
>               unless !defined $opt_d;
>       $session->close();
>       exit 1;
>       }
> 
>       printf "Port '%d' for switch '%s' is set to '%s'.\n",
>               $port, $session->hostname(), $result->{$OID_ifAdminStatus . 
$port}
>       unless !defined $opt_d;
> 

Don't access the global $opt_d from within this function like that, please.

>       $session->close();
> 
>       if ($result==1)
>               { return 0; }
>       elsif ($result==2)
>               { return 2; }
>       else { exit 1; }
> }
> 
> # Set switch port 'up' or 'down'
> # Return 0 if the status is as set, or non-zero on failure.
> sub set_status
> {
>       print "About to set status as: @_\n" unless !defined $opt_d;
>       my ($session, $error) = Net::SNMP->session(
>               -hostname  => $address,
>               -version=> 'snmpv3',
>               -username  => $secName,
>               -authprotocol => $authProtocol,
>               -authpassword=> $authPassword,
>               -privprotocol => $privProtocol,
>               -privpassword=> $privPassword,
>       );
> 
>       if (!defined $session) {
>       printf "ERROR: %s.\n", $error
>               unless defined $opt_d;
>       exit 1;
>       }
> 
>       my $result = $session->set_request(
>       -varbindlist => [ $OID_ifAdminStatus . $port, INTEGER, @_ ],
>       );
> 
>       if (!defined $result) {
>       printf "ERROR: %s.\n", $session->error()
>               unless !defined $opt_d;
>       $session->close();
>       exit 1;
>       }
> 
>       printf "Port '%d' for switch '%s' was set to '%s'.\n",
>               $port, $session->hostname(), $result->{$OID_ifAdminStatus . 
$port}
>               unless !defined $opt_d;
> 
>       $session->close();
>       return 0;
> }
> 
> # Perform Action
> if ($action =~ /^(status|monitor)$/i)
> {
>       if (get_status==0)
>       {
>               exit 0;
>       }
>       else
>       {
>               exit 1;
>       }
> }
> elsif ($action =~ /^off$/i)
> {
>       if (set_status(2)==0)
>       {
>               exit 0;
>       }
>       else
>       {
>               exit 1;
>       }
> }
> elsif ($action =~ /^on$/i)
> {
>       if (set_status(1)==0)
>       {
>               exit 0;
>       }
>       else
>       {
>               exit 1;
>       }
> }
> else
> {
>       die "unknown action: $action";
> }

There's a lot of duplicate code here. Consider doing this:

sub myexit
{
        my $status = shift;
        exit($status == 0 ? 0 : 1);
}

And then do «myexit(get_status())» etc.

Regards,

        Shlomi Fish

P.S: incidentally JLC is the Jerusalem Linux Club.

-- 
-----------------------------------------------------------------
Shlomi Fish       http://www.shlomifish.org/
Optimising Code for Speed - http://shlom.in/optimise

Deletionists delete Wikipedia articles that they consider lame.
Chuck Norris deletes deletionists whom he considers lame.

Please reply to list if it's a mailing list post - http://shlom.in/reply .

--
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to