#!/opt/vdops/bin/perl # This script automates setting ifAdminStatus on an interface # V Who When What # --------------------------------------------------------------------------- # 1.4.0 skendric 2011-02-21 Upgrade to Netops 1.4.0 # 1.3.1 skendric 2010-04-23 Retire walk_oid # 1.3.0 skendric 2010-02-07 Upgrade to perl 5.10.1 # 1.2.0 skendric 2009-12-20 Support changing VLAN membership # 1.1.3 skendric 2009-07-09 Support Dot11Radio interfaces # 1.1.2 skendric 2009-03-22 Give the switch's mgmt agent more time # 1.1.1 skendric 2009-01-26 Support new get_time function # 1.1.0 skendric 2009-01-02 Be patient when getting ifAdmin/OperStatus # 1.0.9 skendric 2008-12-31 Support new snmpSet format # 1.0.8 skendric 2008-12-19 Suport new set_if_admin_status arg format # 1.0.7 skendric 2008-11-30 Be patient when asking for ifAdmin/OperStatus # 1.0.6 skendric 2008-01-21 Add -p parameter to modify toggle action # 1.0.5 skendric 2008-01-08 Arg checks # 1.0.4 skendric 2007-12-04 Insert pause during toggles # 1.0.3 skendric 2007-11-04 Support Vlan interfaces # 1.0.2 skendric 2007-07-06 Add repetitions # 1.0.1 skendric 2007-07-04 Increase logging # 1.0.0 skendric 2007-07-03 First Version # # Author: Stuart Kendrick, sbk {put at sign here} skendric {put dot here} com # # Source: http://www.skendric.com/ # # This software is available under the GNU GENERAL PUBLIC LICENSE, see # http://www.fsf.org/licenses/gpl.html # # # This script takes the following approach: # -Accepts a switch name, an interface name, and an action # # -Perform the action on that switch/port combination # # -If the operator specifies a vlan using '-v xxxx', then set the # VLAN membership of this interface to that vlan. At that point, # the only valid actions (see below) would be 'none' or 'toggle' # # -Action definitions: # enable: set interface to 'up' # up: same as enable # noshut: same as enable # disable: set interface to 'down' # down: same as disable # dn: same as disable # shut: same as disable # flip: set interface to the opposite of its current state # (do nothing if current state is 'testing') # toggle: like flip, but performed twice (i.e. return the # interface to its current state) # none: Useful when setting Vlan instead of ifAdminStatus # # -Repeats the action, if '-r' specified # # -Wait w seconds between each repetition, if '-w' specified # # Requirements: # -The following MIB modules stashed in /opt/vdops/share/snmp/mibs, # or wherever it is that you store MIB modules: # CISCO-PRODUCTS-MIB.my # # -PERL modules: the WI::Netops collection # # # Assumptions: # # # Tested on: # -perl-5.12.2 # -net-snmp-5.6 # # # Instructions: # -Customize the script for your site: find the 'user-configurable # variables' section and modify as appropriate # -Type "twiddle-interface" to see the command-line options # -Try it out # # # # Caveats: # # # Known Bugs: # # # To do: # -Add support for SNMPv3 # # Begin script # Load modules use strict; use warnings; use feature 'say'; use feature 'switch'; use Carp qw(carp cluck croak confess); use Data::Dumper; use English qw( -no_match_vars ); use Getopt::Std; use Regexp::Common; use WI::Netops::CiscoTools 1.4.3; use WI::Netops::HostTools 1.0.4; use WI::Netops::IFTools 1.3.1; use WI::Netops::NetopsTools 2.2.3; use WI::Netops::NetopsData 1.4.0; use WI::Netops::PingTools 1.1.7; use WI::Netops::SNMPTools 1.5.3; use WI::Netops::Utilities 1.4.4; # Declare global variables my $action; # Action I will perform on the port my $index; # ifIndex version of interface index my $interface; # ifName version of interface name my $pause; # Number of seconds to pause in the middle # of a toggle my $port; # Port of interface my $repeat; # Number of times to repeat the action my $slot; # Slot (module) containing interface my $stack; # Stack holding slot (if any) my $user_action; # The name the user employed to define $action my $vlan; # Vlan to which we will assign this interface my $wait; # Number of seconds to wait before # repeating action # Define global variables $program_name = 'twiddle-interface'; $usage = 'Usage: twiddle-interface -s {yes|no} [-d {integer}] -a [enable|disable|flip|toggle] [-p {number of seconds to pause in the middle of a toggle}] -h {switch name} -i {interface name} [-n {number of repetitions} [-v xxxx] -w {number of seconds to wait before continuing with the next repetition}]'; $version = '1.4.0'; # Action modifiers (can be overridden by command-line arguments) $pause = 5; $repeat = 1; $wait = 5; # Grab arguments getopts('a:d:h:i:n:p:s:v:w:', \%option); @target = @ARGV; # Check arguments die 'Must declare intention with -s {yes|no}' unless defined $option{s}; die 'Must provide the name of a device with -h' unless defined $option{h}; die 'Must provide the name of an interface with -i' unless defined $option{i}; die 'Must provide an action with -a' unless defined $option{a}; # Extract arguments $interface = $option{i}; $target[0] = $option{h}; $user_action = $option{a}; $repeat = $option{n} if defined $option{n}; $pause = $option{p} if defined $option{p}; $vlan = $option{v} if defined $option{v}; $wait = $option{w} if defined $option{w}; # Set mode if ($option{r}) { $mode = 'report' } elsif (-t STDIN) { $mode = 'interactive' } else { $mode = 'batch' } ### Begin Main Program ############################################### { check_args(); # Check arguments read_config(); # Read Netops config file compile_mibs(); # Compile MIB files sanity_check(); # Regularize interface name target_check(); # Look for errors in @target basic_info(); # Gather information do_the_work(); # Do the work } ##### End Main Program ############################################### ######################################################################## # Do the work: perform action on interface ######################################################################## sub do_the_work { my (%arg, $ifDescr, $result, $val); my ($ifAdminStatus_before, $ifAdminStatus_after); my ($ifOperStatus_before, $ifOperStatus_after); my $target = $target[0]; my $vmVlan_after; my $vmVlan_before; # Debug trace trace_location('begin') if $debug; # Notify operator say "Analyzing $target..." if $mode eq 'interactive'; # Acquire ifName $val = snmpWalk( {host => $target, oid => $ifName_oid} ); # Find interface index IFNAME: for my $varbind (@$val) { my $ifName = $varbind->{val}; say "ifName = $ifName" if $debug > 1; if ($ifName eq $interface) { $index = $varbind->{iid}; say "Found $ifName at $index" if $debug; last IFNAME; } } die "Unable to find $interface on $target" unless defined $index; # Find interface info $ifDescr = snmpGet( {host => $target, oid => "$ifDescr_oid.$index"} ); $ifAdminStatus_before = snmpGet( {host => $target, oid => "ifAdminStatus.$index"} ); $ifOperStatus_before = snmpGet( {host => $target, oid => "ifOperStatus.$index"} ); $vmVlan_before = snmpGet( {host => $target, oid => "vmVlan.$index"} ); unless (defined $ifDescr) { die "Unable to find ifDescr on $target"; } unless (defined $ifAdminStatus_before) { die "Unable to find ifAdminStatus on $target"; } unless (defined $ifOperStatus_before) { die "Unable to find ifOperStatus on $target"; } unless (defined $vmVlan_before) { die "Unable to find vmVlan on $target"; } unless ($RE{num}{int}->matches($vmVlan_before)) { die "Weird VLAN membership: $vmVlan_before"; } # Don't change interfaces which are 'testing' if ($ifAdminStatus_before eq 'testing') { say "Interface $interface is in the 'testing' state, bailing" if $mode eq 'interactive'; log_it("Interface $interface is in the 'testing' state, bailing"); last TARGET; } # Notify operator if ($mode eq 'interactive') { say(''); if (defined $vlan) { given ($action) { when ($_ eq 'none') { say "For $target, I plan to change $interface ($ifDescr) to VLAN $vlan"; } when ($_ eq 'toggle') { say "For $target, I plan to toggle $interface ($ifDescr) from VLAN $vmVlan_before to VLAN $vlan and back again"; } } } else { say "I plan to $user_action $interface ($ifDescr) on $target"; } say "Currently:"; say " ifAdminStatus = $ifAdminStatus_before"; say " ifOperStatus = $ifOperStatus_before"; say " vmVlan = $vmVlan_before\n"; say(''); if (defined $vlan) { say "I will change VLAN membership ... this is the real thing" if $dome; say "I will not change VLAN membership ... this is a dry run" unless $dome; } else { say "I will $user_action it ... this is the real thing" if $dome; say "I will not $user_action it ... this is a dry run" unless $dome; } say "I will repeat this action $repeat times, waiting $wait seconds between each iteration" if $repeat > 1; say "And pausing $pause seconds between each toggle" if $action eq 'toggle'; say "Stalling for $long seconds to give you time to cancel\n"; sleep $long; } # No one watching, so log instead else { log_it("For $interface on $target, ifAdminStatus = $ifAdminStatus_before; ifOperStatus = $ifOperStatus_before, vmVlan = $vmVlan_before"); } # Walk through repetitions for (my $i = 0; $i < $repeat; $i++) { my (%arg, $time); %arg = ( host => $target, index => $index, action => $action ); $time = get_time(); # Perform action given ($action) { # None when ($_ eq 'none') { if (defined $vlan) { print "Setting vlan to $vlan at $time ..." if $mode eq 'interactive'; $arg{'vlan'} = $vlan; $result = set_vm_vlan(\%arg) if $dome; } else { print "Doing nothing at $time ..." if $mode eq 'interactive'; $result = 1; } } # Up when ($_ eq 'up') { print "Enabling at $time ... " if $mode eq 'interactive'; $result = set_if_admin_status(\%arg) if $dome; } # Down when ($_ eq 'down') { print "Disabling at $time ... " if $mode eq 'interactive'; $result = set_if_admin_status(\%arg) if $dome; } # Flip when ($_ eq 'flip') { print "Flipping at $time ... " if $mode eq 'interactive'; my $real_action; given ($ifAdminStatus_before) { when ($_ eq 'up') { $real_action = 'down' } when ($_ eq 'down') { $real_action = 'up' } } $arg{'action'} = $real_action; $result = set_if_admin_status(\%arg) if $dome; } # Toggle when ($_ eq 'toggle') { print "Toggling at $time ... " if $mode eq 'interactive'; my ($first_action, $second_action); # Handle Vlan case if (defined $vlan) { $arg{'vlan'} = $vlan; $result = set_vm_vlan(\%arg) if $dome; sleep $pause; $arg{'vlan'} = $vmVlan_before; $result = set_vm_vlan(\%arg) if $dome; } else { given ($ifAdminStatus_before) { when ($_ eq 'up') { $first_action = 'down'; $second_action = 'up'; } when ($_ eq 'down') { $first_action = 'up'; $second_action = 'down'; } } $arg{'action'} = $first_action; $result = set_if_admin_status(\%arg) if $dome; sleep $pause; $arg{'action'} = $second_action; $result = set_if_admin_status(\%arg) if $dome; } } # Default default { die "Undefined action: should not reach here"; } } # End switch stmt # Pause before repeating sleep $wait if $repeat > 1; } # Announce the result if ($result) { say " succeeded\n" if $mode eq 'interactive'; log_it("Performed $user_action on $interface at $target"); } else { say " failed\n" if $mode eq 'interactive'; log_it("snmpSet for $user_action on $interface at $target failed"); } # Keep the operator posted say 'Figuring out what happened...' if $mode eq 'interactive'; # Give the switch's management agent time to figure out current state sleep $long; # Figure out what happened $ifAdminStatus_after = snmpGet( {host => $target, oid => "ifAdminStatus.$index"} ); $ifOperStatus_after = snmpGet( {host => $target, oid => "ifOperStatus.$index"} ); $vmVlan_after = snmpGet( {host => $target, oid => "vmVlan.$index"} ); # If one of these came back undefined, try again unless (defined $ifAdminStatus_after) { sleep $mid; $ifAdminStatus_after = snmpGet( {host => $target, oid => "ifAdminStatus.$index"} ); $ifAdminStatus_after //= $QUERY; } unless (defined $ifOperStatus_after) { sleep $mid; $ifOperStatus_after = snmpGet( {host => $target, oid => "ifOperStatus.$index"} ); $ifOperStatus_after //= $QUERY; } unless (defined $vmVlan_after) { sleep $mid; $vmVlan_after = snmpGet( {host => $target, oid => "vmVlan.$index"} ); $vmVlan_after //= $QUERY; } # Report on status if ($mode eq 'interactive') { say "Afterward, interface $interface ($ifDescr) on $target:"; say " ifAdminStatus = $ifAdminStatus_after"; say " ifOperStatus = $ifOperStatus_after"; say " vmVlan = $vmVlan_after"; } else { log_it("For $interface on $target, ifAdminStatus = $ifAdminStatus_after; ifOperStatus = $ifOperStatus_after, vmVlan = $vmVlan_after"); } # Make things look pretty say('') if $mode eq 'interactive'; # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Sanity check ######################################################################## sub sanity_check { my $type; # Debug trace trace_location('begin') if $debug; # Check repetition and pause parameters die 'Option p must be an integer' unless $RE{num}{int}->matches($pause); die 'Option n must be an integer' unless $RE{num}{int}->matches($repeat); die 'Option w must be an integer' unless $RE{num}{int}->matches($wait); # Check vlan, if any if (defined $vlan) { die 'Vlan must be an integer' unless $RE{num}{int}->matches($vlan); die 'Vlan must be > 0 and less than 4097' unless ($vlan > 0 and $vlan < 4097); } # Regularize action given ($user_action) { when ($_ eq 'enable') { $action = 'up' } when ($_ eq 'up') { $action = 'up' } when ($_ eq 'noshut') { $action = 'up' } when ($_ eq 'disable') { $action = 'down' } when ($_ eq 'down') { $action = 'down' } when ($_ eq 'dn') { $action = 'down' } when ($_ eq 'shut') { $action = 'down' } when ($_ eq 'flip') { $action = 'flip' } when ($_ eq 'toggle') { $action = 'toggle' } when ($_ eq 'none') { $action = 'none' } when ($_ eq 'null') { $action = 'none' } default { die "I don't recognize action $user_action\n" } } # Weed out weird combinations if (defined $vlan) { unless ($user_action eq 'none' or $user_action eq 'toggle') { say "Action $user_action doesn't make sense when specifying VLAN"; } } # Regularize the interface name if ($interface =~ /v/i) { # VLAN interface my ($vlan) = ($interface =~ /(\d+)/); $interface = 'Vl' . $vlan; } elsif ($interface =~ /Radio/) { my ($int, $vlan); if ($interface =~ /\./) { ($int, $vlan) = ($interface =~ /Radio(\d+)\.(\d+)/); $interface = 'Do' . $int . $DOT . $vlan; } else { ($int) = ($interface =~ /Radio(\d+)/); $interface = 'Do' . $int; } $type = 'Radio'; } else { # Physical interface my @slashes = $interface =~ /\//g; say "number of slashes in $interface = ", scalar @slashes if $debug; given (scalar @slashes) { when ($_ == 1) { # Handle slot and port ($type, $slot, $port) = ($interface =~ /(\w+)(\d+)\/(\d+)\Z/); } when ($_ == 2) { # Handle slot, stack, and port ($type, $slot, $stack, $port) = ($interface =~ /(\w+)(\d+)\/(\d+)\/(\d+)\Z/); } default { die "I don't know how to find slot/stack/port in $interface"; } } die "I cannot find slot and port in $interface" unless (defined $slot and defined $port); # Sanitize interface name given ($type) { when ($_ eq'Radio') { $type = $type } when ($_ =~ /^Fa|^fa/) { $type = 'Fa' } when ($_ =~ /^Gi|^gi/) { $type = 'Gi' } when ($_ =~ /^Se|^se/) { $type = 'Se' } when ($_ =~ /^Te|^te/) { $type = 'Te' } default { say "I don't know how to find type in $interface" } } die "I cannot find type in $interface" unless defined $type; # Produce regularized form of interface name if (defined $stack) { $interface = $type . $slot . $SLASH . $stack . $SLASH . $port; } elsif ($type eq 'Radio') { $interface = $interface; } else { $interface = $type . $slot . $SLASH . $port; } } # End 'regularize interface name' # Debug info say "interface = $interface" if $debug; # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Output help ######################################################################## sub HELP_MESSAGE { print <