Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Call for (gentle) critique.

by c (Hermit)
on Aug 27, 2001 at 23:14 UTC ( [id://108233]=perlmeditation: print w/replies, xml ) Need Help??

Okay, here it is! My first attempt at a piece of open source scripting to give to the world. You can see by its revision, its at 0.x so no formal release yet. Since I am less than 6 months old in the perl world, I can't help but ask for review from my big brothers. Regardless of its level, or lack thereof, in maturity, I was only able to write this script because of my membership here.
Thanks! -c

#!/usr/bin/perl -w # $Id: pancho,v 0.41 2001/08/27 18:48:18 charles Exp $ ## module calls use strict; use Net::SNMP; use Getopt::Long; my $community = ''; # your RW snmp community string my $tftpserver = ""; # ip address of your tftpserver my @routers = qw(); # list of routers ############ NO FURTHER EDITING SHOULD BE REQUIRED. ############ ############ DOING SO IS AT YOUR OWN RISK. ############ ## development variables my $rcs = (qw$Revision: 0.41 $)[-1]; ## ensure some arguments are given if ($#ARGV < 0) { print "\nPancho requires some flags to be specified.\nPlease try run +ning ./pan cho --help\n\n"; exit(1); } ## command line options my $upload; my $download; my $filename; my $commit; my $list; my $host; my $string; my $version; my $old; my $server; my $regex; my $help; GetOptions ( 'upload' => \$upload, 'download' => \$download, 'filename=s' => \$filename, 'commit' => \$commit, 'list=s' => \$list, 'host=s' => \$host, 'server=s' => \$server, 'string=s' => \$string, 'version' => \$version, 'old' => \$old, 'regex' => \$regex, 'help' => \$help, ); ## set up oids my $wrnet; my $confnet; my $wrmem; my $starttotftp; my $tftptostart; my $reload; my $flashtotftp; my $tftptoflash; my $eraseflash; ## for now there is no difference between the old oids ## and new used by cisco. cisco is stating that the older ## versions may no longer be valid in ios revisions 12.1 ## and greater with some 11.2 and 12.0 revisions also ## showing problems. ## updated oids will be put into place with a future revsion ## of pancho. if ($old) { ## deprecated oids $wrnet = ".1.3.6.1.4.1.9.2.1.55.0"; $confnet = ".1.3.6.1.4.1.9.2.1.53.0"; $wrmem = ".1.3.6.1.4.1.9.2.1.54.0"; $flashtotftp = ".1.3.6.1.4.1.9.2.10.9.0"; $tftptoflash = ".1.3.6.1.4.1.9.2.10.12.0"; $eraseflash = ".1.3.6.1.4.1.9.2.10.6.0"; } else { ## oids $wrnet = ".1.3.6.1.4.1.9.2.1.55.0"; $confnet = ".1.3.6.1.4.1.9.2.1.53.0"; $wrmem = ".1.3.6.1.4.1.9.2.1.54.0"; $flashtotftp = ".1.3.6.1.4.1.9.2.10.9.0"; $tftptoflash = ".1.3.6.1.4.1.9.2.10.12.0"; $eraseflash = ".1.3.6.1.4.1.9.2.10.6.0"; } ## show version and exit if ($version) { &version; exit(0); } ## show the help menu and exit if ($help) { &usage; exit(0); } ## build out count for error checking my $count = 0; $count++ if ($upload); $count++ if ($download); ## ensure that we have a node or list of nodes if ($host) { push(@routers, $host); } elsif ($list) { open(FH, $list); @routers = <FH>; close(FH); } elsif ( $#routers < 0 ) { print "\nYou have not specified a node or list of nodes to act upon! +\n\n" unless ($count == 0); exit(1); } ## determine if we are using the default server or another if ($server) { $tftpserver = $server; } ## determine if we are using the default community string or another if ($string) { $community = $string; } ## check to ensure that we have the information required if (!$community) { print "\nYou have not specified an SNMP community.\n\n"; exit(1); } elsif (!$tftpserver and $upload or $download) { print "\nYou have not specified a tftp server.\n\n"; exit(1); } if ($upload && $count == 1) { if (!$filename) { print "\nIn order to tftp a configuration to a remote device\nyou +must indic ate a specific filename using -f.\n\n"; exit(1); } else { &copy_tftp_run; exit(0); } } elsif ($download && $count == 1) { &copy_run_tftp; exit(0); } elsif ($commit) { &copy_run_start; exit(0); } else { print "\nYou cannot upload and download at the same time.\n\n" unless ($count == 0); } ## subroutines sub version { print "\n This is Pancho version $rcs\n\n"; } sub copy_run_start { if ($regex) { my $mib = "$wrmem"; for my $host(@routers) { next unless /$regex/; $filename = "$host.cfg"; my $s = Net::SNMP->session( -hostname => $host, -community => $community ); $s->set_request($mib, INTEGER, "1"); $s->close; print "\nSuccessfully wrote config to memory on $host.\n\n" } } else { my $mib = "$wrmem"; for my $host(@routers) { my $s = Net::SNMP->session( -hostname => $host, -community => $community ); $s->set_request($mib, INTEGER, "1"); $s->close; print "\nSuccessfully wrote config to memory on $host.\n\n" } } } sub copy_run_tftp { if ($regex) { my $mib = "$wrnet$tftpserver"; for my $host(@routers) { next unless /$regex/; $filename = "$host.cfg"; my $s = Net::SNMP->session( -hostname => $host, -community => $community ); $s->set_request($mib, OCTET_STRING, $filename); my $error = $s->error; $s->close; if ($error) { print "\n$error\n\n"; } else { print "\nSuccessfully wrote config to tftpserver for $host.\n\ +n" } } } else { my $mib = "$wrnet$tftpserver"; for my $host(@routers) { $filename = "$host.cfg"; my $s = Net::SNMP->session( -hostname => $host, -community => $community ); $s->set_request($mib, OCTET_STRING, $filename); my $error = $s->error; $s->close; if ($error) { print "\n$error\n\n"; } else { print "\nSuccessfully wrote config to tftpserver for $host.\n\ +n" } } } } sub copy_tftp_run { if ($regex) { my $mib = "$confnet$tftpserver"; for my $host(@routers) { next unless /$regex/; my $s = Net::SNMP->session( -hostname => $host, -community => $community ); $s->set_request($mib, OCTET_STRING, $filename); my $error = $s->error; $s->set_request($wrmem, INTEGER, "1") if ($commit); $s->close; if ($error) { print "\n$error\n\n" } else { print "\nSuccessfully sent config to $host.\n\n" } } } else { my $mib = "$confnet$tftpserver"; for my $host(@routers) { my $s = Net::SNMP->session( -hostname => $host, -community => $community ); $s->set_request($mib, OCTET_STRING, $filename); my $error = $s->error; $s->set_request($wrmem, INTEGER, "1") if ($commit); $s->close; if ($error) { print"\n$error\n\n"; } else { print "\nSuccessfully sent config to $host.\n\n" } } } } sub usage { print <<USAGE; NAME pancho SYNOPSIS options [ --upload | --download | --commit ] [ --filename <filename> ] [ --list <list> ] [ --host <hostname> ] [ --server <tftp server ip> ] [ --string <snmp community> ] [ --regex <regular expression> ] [ --version ] [ --help ] requires perl, net::snmp DESCRIPTION this utility provides a front end to managing cisco(c) router configurations and administration. pancho currently supports the following cisco(c) command line options: copy tftp run copy run tftp copy run start pancho in conjunction with a text file following standard cisco ios(c) options allows an administrator to make the same changes to a group of routers or a single host through a single command line string. in addition, pancho can be used to archive router configurations through automated or manual runs. OPTIONS -u, --upload specify that pancho send a configuration file TO the remote device(s). -d, --download specify that pancho retrieve a configuration file FROM the remote device(s). -c. --commit specify that pancho perform a 'write memory' or 'copy run start' on the remote device(s). -f, --filename specify the local filename that pancho should send to the remote device. -l, --list specify a local file that hold a list of device hostnames or ip addresses that pancho should perform actions against. --host specify an individual host on which pancho will perform actions. --server specify a tftp server that pancho will push or pull configurations to and from. --string specify a snmp read-write string. -r, --regex specify a regular expression that pancho can use to filter out specific hosts from the host file that actions should be taken against. --version display the current version of pancho. --help this display. pancho gives a network administrator the power to make tedious work very simple when configuring a group of cisco routers. through a single command line statement, a change that is needed to be made on a large number of routers can be done without having to manually log into any remote devices. pancho also provide the flexibility to allow administrators to use its function against a single host, a select group or the entire whole. at first glance, pancho appears to have a significantly long command line. however, many of the option flags seen above can be set as default such that the flags themselves would only be called to override the default settings. for example a default tftpserver may be set within the pancho configuration, however you will still be able to specify: --server 172.16.254.16 in case you would like to push/pull your config to an alternate server. please read through the accompanying README file to learn how to set up pancho defaults. pancho can be set up to have a default group of nodes that it will affect when ran. this list can be altered by specifying the --host flag to indicate a single node or the --list flag which will let you direct pancho to read the group to be effected from a plain text file. another method which allows pancho to further granulize the group of devices touched is the --regex option. this feature is very powerful in the hands of someone with a fair amount of knowledge concerning regular expressions and nodes grouped with logical naming conventions. --regex ^core.* would indicate that within the default list of nodes, or within the list obtained from an external text file, only those with a name beginning in "core" would be affected. the final optional flag is --filename. if unspecified, this value defaults to the hostname of the device being touched with an extension of ".cfg". the --filename flag argument should always be used when a single configuration file will be uploaded to a group of devices. pancho's only mandatory flag is one of two options, --upload or --download. this specifies whether or not pancho will push a configuration to the router or pull the remote device's config down to the local tftp server. pancho lives remotely from the router and therefore views the world from that perspective. --upload signifies that a file will be uploaded TO the router. conversely, --download indicates that the config will be brought down FROM the router. the final option of --commit is used to perform a remote "write memory" or "copy run start". this option can be used either individually, or in conjunction with --upload essentially committing to memory the changes as they are being made. EXAMPLES in order to utilize pancho's full capacity for router configuration, one needs to be familiar with cisco's 'copy tftp run' or 'config net' syntax. in depth discussion on the procedure can be found on cisco's website at http://www.cisco.com. a summary of the process is as follows. a remote router will tftp a configuration file consisting of standard ios commands into its running-config. the router will merge the current running-config and the command options received from the text file and then apply the whole to its new running configuration. an example may better state the process. on the tftp server, we create a plain text file to update the access-list 5 on a group of remote routers. the plain text file, is as follows: ! no access-list 5 access-list 5 permit 10.6.21.64 0.0.0.31 access-list 5 permit 10.12.71.0 0.0.0.255 ! end since we plan on completely rewriting access-list 5 on the remote routers with this new acl, our first statement is 'no access-list 5' which allows us to start a new acl listing. the following acl statements will then build the new access-list. since cisco's 'copy tftp run' process first merges the current running-config and our plain text commands before it applies the newly created configuration, there will be no interruption in traffic being inspected by access- list 5. very different than just copying and pasting the same commands into the cisco command prompt which applies each statement with the hit of the carriage return allowing for the possibility of serious consequences. with knowledge of cisco's ios syntax, the possibilities for remote configuration are endless, and with pancho's capability for customization, an entire network may be updated or archived from a single point of management. the following a just a few of pancho's command line options in running syntax: update all routers with changes held in a flat file pancho -u -f acl_update.txt archive all router configurations locally pancho -d or to a remote tftp server pancho -d --server 10.219.0.25 update all border routers with a new motd using an alternate snmp community pancho -u -f motd.txt --regex border --string f00B\@ +r CAVEATS pancho's power does come with some caveats that should not be considered lightly. pancho depends upon remote routers having a read-write(rw) snmp community string configured. since read-write strings offer full snmp control to a machine the potential for exploit could be high. it is HIGHLY recommended, if not considered mandatory by this author that all routers using rw snmp strings should have an acl configured to limit what machines can gain such access to the device. again, full documentation can be found on cisco's website, however a short synopsis is as follows: ! access-list specifying nodes that will be capable ! of using snmp access to routers access-list 10 permit host 10.10.220.78 access-list 10 permit 192.168.96.4 0.0.0.3 ! ! apply access-list to snmp community snmp-server community 9Eck#0-A rw 10 ! pancho also relies on the tftp protocol and server. files held within the tftproot and its subdirectories are generally world readable. moreover the tftp protocol does not inherently provi +de any method for user authentication. because of this, it is generally good measure to try to limit the nodes which have access to the tftp mechanism. this can be achieved through a local firewall specifying remote nodes or subnets that can push/pull to and from the tftpserver. with re +mote routers all belonging to different subnets, this would at firs +t seem tedious to set up and even worse to maintain. a common convention to combat this is to use loopback addresses from an aggregate on all remote devices. it is then possible to specif +y ip tftp source-interface Loopback 100 and ensure that all tftp requests coming in from remote device +s will have a predictable source address, that can be grouped in +to the aggregate. using this mechanism, a large group of routers numbered with Loopback addresses having /32 masks can be conso +lidated into a larger aggregate with a shorter mask allowing for less configuration within the firewall rules. COMMENTS Please send all comments regarding pancho to: pancho\@lunarmedia.net Check for new releases of pancho at: http://pancho.lunarmedia.net/ BUGS AUTHOR Charles J. Menzes <charles\@lunarmedia.net> Pancho Copyright(C) 2001 USAGE }

Replies are listed 'Best First'.
Re: Call for (gentle) critique.
by princepawn (Parson) on Aug 27, 2001 at 23:53 UTC
    if ($old) { ## deprecated oids $wrnet = ".1.3.6.1.4.1.9.2.1.55.0"; $confnet = ".1.3.6.1.4.1.9.2.1.53.0"; $wrmem = ".1.3.6.1.4.1.9.2.1.54.0"; $flashtotftp = ".1.3.6.1.4.1.9.2.10.9.0"; $tftptoflash = ".1.3.6.1.4.1.9.2.10.12.0"; $eraseflash = ".1.3.6.1.4.1.9.2.10.6.0"; } else { ## oids $wrnet = ".1.3.6.1.4.1.9.2.1.55.0"; $confnet = ".1.3.6.1.4.1.9.2.1.53.0"; $wrmem = ".1.3.6.1.4.1.9.2.1.54.0"; $flashtotftp = ".1.3.6.1.4.1.9.2.10.9.0"; $tftptoflash = ".1.3.6.1.4.1.9.2.10.12.0"; $eraseflash = ".1.3.6.1.4.1.9.2.10.6.0"; }
    These variables that you are binding and using as separate scalars should be encapsulated into two different hashes, one for each set oids:
    my $data{old} = { wrnet = ".1.3.6.1.4.1.9.2.1.55.0", confnet = ".1.3.6.1.4.1.9.2.1.53.0", wrmem = ".1.3.6.1.4.1.9.2.1.54.0", flashtotftp = ".1.3.6.1.4.1.9.2.10.9.0", tftptoflash = ".1.3.6.1.4.1.9.2.10.12.0", eraseflash = ".1.3.6.1.4.1.9.2.10.6.0" } my $data{new} = { wrnet = ".1.3.6.1.4.1.9.2.1.55.0", confnet = ".1.3.6.1.4.1.9.2.1.53.0", wrmem = ".1.3.6.1.4.1.9.2.1.54.0", flashtotftp = ".1.3.6.1.4.1.9.2.10.9.0", tftptoflash = ".1.3.6.1.4.1.9.2.10.12.0", eraseflash = ".1.3.6.1.4.1.9.2.10.6.0" }
    also since the first part of these numbers does not change, you should store that in a variable:
    my $prefix = ".1.3.6.1.4.1.9.2"; my $number = "$prefix.10.6.0";

      I think you meant to say:

      my %data; $data{old} = { wrnet => ".1.3.6.1.4.1.9.2.1.55.0", confnet => ".1.3.6.1.4.1.9.2.1.53.0", ... }; # and etc.

      conv

Re: Call for (gentle) critique.
by princepawn (Parson) on Aug 27, 2001 at 23:40 UTC
    You repeat the exact-same assignment in both the if and thenparts of your program:
    sub copy_run_start { if ($regex) { my $mib = "$wrmem"; ... } } else { my $mib = "$wrmem"; ... } } } sub copy_run_tftp { if ($regex) { my $mib = "$wrnet$tftpserver"; ... # and the same for the else
    It increases the lines of code un-necessarily.
      Thanks for pointing that out. I think I knew it was there, but was turning my head out of laziness, i replaced all of the subroutines with a single new one that seems to get the job done:

      sub execute { my $i = shift; my $mib = "$oid{$i}$tftpserver"; for my $host(@routers) { chomp $host; next if (($regex) and ($host !~ /$regex/)); $filename = "$host.cfg" if ($i eq "wrnet"); my $s = Net::SNMP->session( -hostname => $host, -community => $community ); $s->set_request($mib, OCTET_STRING, $filename); my $error = $s->error; $s->set_request($oid{wrmem}, INTEGER, "1") if ($commit); $s->close; if ($error) { print "\n$error\n\n"; } elsif ($i eq "wrnet") { print "\nSuccessfully wrote config to tftpserver for $host.\n\n" +; } elsif ($i eq "confnet") { print "\nSuccessfully sent config to $host.\n\n"; } } }

      humbly -c

Re: Call for (gentle) critique.
by princepawn (Parson) on Aug 27, 2001 at 23:46 UTC
    How about changing
    ## ensure some arguments are given if ($#ARGV < 0) { print "\nPancho requires some flags to be specified.\nPlease try run +ning ./pan cho --help\n\n"; exit(1); }
    To:
    die "Pancho requires some flags to be specified. Please try running $0 + --help" unless (@ARGV);

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://108233]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (4)
As of 2024-04-24 03:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found