Script runs fine when passed args directly, not as stdin
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=3Dvalue
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
#!/usr/bin/perl
# Sets switch port on a procurve up or down
use Getopt::Std;
use Net::SNMP;
my $OID_ifAdminStatus =3D '1.3.6.1.2.1.2.2.1.7.';
# Get the program name from $0 and strip directory names
$_=3D$0;
$|=3D1;
s/.*\///;
my $pname =3D $_;
sub usage
{
print "Usage:\n";
print "\n";
print "$pname [options]\n";
print "\n";
print "Options:\n";
=09
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;
}
sub fail
{
($msg)=3D [at] _;
print $msg."\n" unless !defined $opt_d;
$t->close if defined $t;
exit 1;
}
sub fail_usage
{
($msg)=3D [at] _;
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 ( [at] 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 ( [at] ARGV > 0);
$address =3D $opt_s if defined $opt_s;
$port =3D $opt_p if defined $opt_p;
$action =3D $opt_o if defined $opt_o;
$authProtocol =3D $opt_a if defined $opt_a;
$authPassword =3D $opt_A if defined $opt_A;
$secName =3D $opt_u if defined $opt_u;
$privProtocol =3D $opt_x if defined $opt_x;
$privPassword =3D $opt_X if defined $opt_X;
}
else
{
get_options_stdin();
}
$action =3D "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 =3D~ /^(off|on|=
status|monitor)$/i);
sub get_options_stdin
{
my $opt;
my $line =3D 0;
while( defined($in =3D <>) )
{
$_ =3D $in;
chomp;
# strip leading and trailing whitespace
s/^\s*//;
s/\s*$//;
# skip comments
next if /^#/;
$line+=3D1;
$opt=3D$_;
next unless $opt;
($name,$val)=3Dsplit /\s*=3D\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 =3D $val; }
elsif ($name eq "port" ) { $port =3D $val; }
elsif ($name eq "option" ) { $action =3D $val; }
elsif ($name eq "authProtocol" ) { $authProtocol =3D $val; }
elsif ($name eq "authPassword" ) { $authPassword =3D $val; }
elsif ($name eq "login" ) { $secName =3D $val; }
elsif ($name eq "privProtocol" ) { $privProtocol =3D $val; }
elsif ($name eq "passwd" ) { $privPassword =3D $val; }
elsif ($name eq "debug" ) { $opt_d =3D $val; }
# 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) =3D Net::SNMP->session(
-hostname =3D> $address,
-version=3D> 'snmpv3',
-username =3D> $secName,
-authprotocol =3D> $authProtocol,
-authpassword=3D> $authPassword,
-privprotocol =3D> $privProtocol,
-privpassword=3D> $privPassword,
);
if (!defined $session) {
printf "ERROR: %s.\n", $error
unless defined $opt_d;
exit 1;
}
=09
my $result =3D $session->get_request(
-varbindlist =3D> [ $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;
$session->close();
=09
if ($result=3D=3D1)
{ return 0; }
elsif ($result=3D=3D2)
{ 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: [at] _\n" unless !defined $opt_d;
my ($session, $error) =3D Net::SNMP->session(
-hostname =3D> $address,
-version=3D> 'snmpv3',
-username =3D> $secName,
-authprotocol =3D> $authProtocol,
-authpassword=3D> $authPassword,
-privprotocol =3D> $privProtocol,
-privpassword=3D> $privPassword,
);
if (!defined $session) {
printf "ERROR: %s.\n", $error
unless defined $opt_d;
exit 1;
}
my $result =3D $session->set_request(
-varbindlist =3D> [ $OID_ifAdminStatus . $port, INTEGER, [at] _ ],
);
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 =3D~ /^(status|monitor)$/i)
{
if (get_status=3D=3D0)
{
exit 0;
}
else
{
exit 1;
}
}
elsif ($action =3D~ /^off$/i)
{
if (set_status(2)=3D=3D0)
{
exit 0;
}
else
{
exit 1;
}
}
elsif ($action =3D~ /^on$/i)
{
if (set_status(1)=3D=3D0)
{
exit 0;
}
else
{
exit 1;
}
}
else
{
die "unknown action: $action";
}
--
To unsubscribe, e-mail: beginners-unsubscribe [at] perl.org
For additional commands, e-mail: beginners-help [at] perl.org
http://learn.perl.org/
Re: Script runs fine when passed args directly, not as stdin
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=3Dvalue
> 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 th=
is
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 =ABuse strict;=BB and =ABuse warnings;=BB and fix all the pr=
oblems 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 =3D '1.3.6.1.2.1.2.2.1.7.';
>
> # Get the program name from $0 and strip directory names
> $_=3D$0;
Ahmmm... please don't assign this to $_. $_ can be modified too easily.
Do:
<<<
my $progname =3D $0;
>>>
Or whatever.
> $|=3D1;
Using IO::Handle's Flush would be more readable and more idiomatic here.
> s/.*\///;
> my $pname =3D $_;
OK, so you did it. Do:
my $pname =3D $0;
$pname =3D~ 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)=3D [at] _;
> 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)=3D [at] _;
> 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 ( [at] 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 ( [at] ARGV > 0);
>
> $address =3D $opt_s if defined $opt_s;
> $port =3D $opt_p if defined $opt_p;
> $action =3D $opt_o if defined $opt_o;
> $authProtocol =3D $opt_a if defined $opt_a;
> $authPassword =3D $opt_A if defined $opt_A;
> $secName =3D $opt_u if defined $opt_u;
> $privProtocol =3D $opt_x if defined $opt_x;
> $privPassword =3D $opt_X if defined $opt_X;
> }
> else
> {
> get_options_stdin();
> }
>
Please convert this to Getopt::Long.
> $action =3D "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 =3D~
> /^(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 =3D 0;
> while( defined($in =3D <>) )
while (my $in =3D <>) would be enough here.
> {
> $_ =3D $in;
Don't assign to $_. You can also do =ABwhile (<>)=BB but please avoid abusi=
ng $_
in serious scripts.
> chomp;
>
> # strip leading and trailing whitespace
> s/^\s*//;
> s/\s*$//;
>
> # skip comments
> next if /^#/;
>
> $line+=3D1;
$line++; is better.
> $opt=3D$_;
> next unless $opt;
>
> ($name,$val)=3Dsplit /\s*=3D\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 =3D $val; }
> elsif ($name eq "port" ) { $port =3D $val; }
> elsif ($name eq "option" ) { $action =3D $val; }
> elsif ($name eq "authProtocol" ) { $authProtocol =3D $val; }
> elsif ($name eq "authPassword" ) { $authPassword =3D $val; }
> elsif ($name eq "login" ) { $secName =3D $val; }
> elsif ($name eq "privProtocol" ) { $privProtocol =3D $val; }
> elsif ($name eq "passwd" ) { $privPassword =3D $val; }
> elsif ($name eq "debug" ) { $opt_d =3D $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) =3D Net::SNMP->session(
> -hostname =3D> $address,
> -version=3D> 'snmpv3',
> -username =3D> $secName,
> -authprotocol =3D> $authProtocol,
> -authpassword=3D> $authPassword,
> -privprotocol =3D> $privProtocol,
> -privpassword=3D> $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 =3D $session->get_request(
> -varbindlist =3D> [ $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=3D=3D1)
> { return 0; }
> elsif ($result=3D=3D2)
> { 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: [at] _\n" unless !defined $opt_d;
> my ($session, $error) =3D Net::SNMP->session(
> -hostname =3D> $address,
> -version=3D> 'snmpv3',
> -username =3D> $secName,
> -authprotocol =3D> $authProtocol,
> -authpassword=3D> $authPassword,
> -privprotocol =3D> $privProtocol,
> -privpassword=3D> $privPassword,
> );
>
> if (!defined $session) {
> printf "ERROR: %s.\n", $error
> unless defined $opt_d;
> exit 1;
> }
>
> my $result =3D $session->set_request(
> -varbindlist =3D> [ $OID_ifAdminStatus . $port, INTEGER, [at] _ ],
> );
>
> 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 =3D~ /^(status|monitor)$/i)
> {
> if (get_status=3D=3D0)
> {
> exit 0;
> }
> else
> {
> exit 1;
> }
> }
> elsif ($action =3D~ /^off$/i)
> {
> if (set_status(2)=3D=3D0)
> {
> exit 0;
> }
> else
> {
> exit 1;
> }
> }
> elsif ($action =3D~ /^on$/i)
> {
> if (set_status(1)=3D=3D0)
> {
> 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 =3D shift;
exit($status =3D=3D 0 ? 0 : 1);
}
And then do =ABmyexit(get_status())=BB etc.
Regards,
Shlomi Fish
P.S: incidentally JLC is the Jerusalem Linux Club.
=2D-
=2D--------------------------------------------------------- -------
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-unsubscribe [at] perl.org
For additional commands, e-mail: beginners-help [at] perl.org
http://learn.perl.org/
RE: Script runs fine when passed args directly, not as stdin
>I'm commenting on your code below with some general remarks. Not sure if t=
his
>will fix your problem.
Shlomi,
I greatly appreciate your time going through this, I have undergone rewriti=
ng it
from scratch with your suggestions. It was a script I copied from an existi=
ng one
which was a bad idea:)
Thank you again!
jlc
--
To unsubscribe, e-mail: beginners-unsubscribe [at] perl.org
For additional commands, e-mail: beginners-help [at] perl.org
http://learn.perl.org/
Re: Script runs fine when passed args directly, not as stdin
On Tuesday 02 Mar 2010 22:07:28 Joseph L. Casale wrote:
> >I'm commenting on your code below with some general remarks. Not sure if
> >this will fix your problem.
>
> Shlomi,
> I greatly appreciate your time going through this, I have undergone
> rewriting it from scratch with your suggestions. It was a script I copied
> from an existing one which was a bad idea:)
>
> Thank you again!
You're welcome. I'm glad I was able to enlighten you.
Regards,
Shlomi Fish
--
------------------------------------------------------------ -----
Shlomi Fish http://www.shlomifish.org/
Funny Anti-Terrorism Story - http://shlom.in/enemy
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-unsubscribe [at] perl.org
For additional commands, e-mail: beginners-help [at] perl.org
http://learn.perl.org/