http://www.perlmonks.org?node_id=1199323

#!/usr/bin/perl -w use strict; use Net::Ping; use threads; ###################################################################### +#################################################### # # PURPOSE: This script tries to determine if the cables are plugged in +to the correct ports in the EX2200 switch in a store # # LOGIC: 1) get store number from command line argument # 2) lookup store IP address using store number # 3) Ping devices in store to populate ARP table in SRX # 4) Collect ARP table from SRX and ethernet-switching table f +rom EX2200 with an expect script # 5) Parse the outputs and merge based on MAC address. Ignore + all devices in VLAN.16 as they are wireless devices # 6) Special handling for WLA plugged into port 47. This is O +K only if there is not another WLA in port 46 # 7) Create report or pass info to next program # # USER INPUT: The store number for the store to be tested # # HARDCODED INPUT: $storeFile - this is a file created from allstores +.xlsx that maps store number to IP address and the # other data in allstores. this script + only needs the store IP address # $portFile - this file contains a mapping between +the last octet of the IP address of a store device, # the port number that it should be plu +gged into, and the description of the device. # $user - userid to access SRX and EX2200 # $pwd - password to access SRX and EX2200 # # USAGE: perl chackwiring.pl <store number> # # RETURN CODES: 0 - success # 101 - store IP address not in $storeFile # 102 - cannot reach store # 103 - could not open file created by expect script # 104 - could not open $portFile # 105 - could not open $storeFile # 106 - could not clear the old data in $filename prior + to running expect script # The following return codes are bit flags and can be combined by +adding: # 1 - could not connect to SRX # 2 - could not connect to EX2200 # 4 - did not parse any show arp records # 8 - did not parse any show ethernet-switching table + records # ###################################################################### +#################################################### # ============================ # user changeable variables # ============================ my $user = "root"; my $pwd = "Pa55word"; my $portFile = "porttable"; # table with port to IP association my $storeFile = "store_data_2.txt"; # data about stores extracted from + allstores.xlsx my $debugLevel = 10; my $icmpTimeout = 1; # timeout for ping, default of 1 is probab +ly ok. my @pingList = (); # ============================ # hashes used for data storage # ============================ my %cable_hash = (); my %port_hash = (); my %device_info_hash = (); my %store_info_hash = (); my %device_ip_hash = (); my %port_to_mac_hash = (); # ========================================= # debug info to calculate run time # ========================================= if ($debugLevel) { my $t1 = getTimestamp(); print "starting time is $t1\n"; } # ========================================= # get requested store from command line # ========================================= my $storeNum = 0; if ( $#ARGV > -1) { $storeNum = $ARGV[0]; print "asking for store $storeNum\n" if ($debugLevel > 5 ); } # ========================================= # get store info so we can get store's IP address # ========================================= getStoreInfo(); unless (defined ( $store_info_hash{$storeNum}{ip})) { print "store $storeNum not found\n"; exit 101; } # ========================================= # variables filled in based on store number # ========================================= my $storeIp = $store_info_hash{$storeNum}{ip}; print "StoreIP is $storeIp\n" if ($debugLevel > 0 ); my $base_ip = $store_info_hash{$storeNum}{ip}; my $srx_ip = $base_ip . ".193"; my $ex_ip = $base_ip . ".2"; my $filename = "report_" . $storeNum . ".txt"; my $fatalError = 0; # ========================================= # count number of reachable devices in store # ========================================= my $upCount = 0; # ========================================= # ping store devices to populate ARP table # need to ping any address that might be # assigned to a device plugged into the EX # ========================================= addRangeToPingList( 1, 15); # ISP, safes, RILO ,ATG, ATMs addRangeToPingList( 20, 24); # POS addRangeToPingList( 30, 34); # POS pinpad addRangeToPingList( 43, 44); # scanner and GOT docking stations addRangeToPingList( 50, 51); # printer addRangeToPingList( 60, 68 ); # HVAC, training, DVR addRangeToPingList( 154, 158 ); # WLA $upCount = pingArrayThreaded ($base_ip); # ========================================= # debug info to calculate run time # ========================================= if ($debugLevel) { my $t2 = getTimestamp(); print "ping complete at time is $t2\n"; } # ========================================= # if we can't reach anything, store is down # ========================================= unless ($upCount ) { print "cannot reach store\n"; exit 102; } # ========================================= # clear the data file for the expect script output # ========================================= open( OUTFILE, ">", $filename ) or do { $fatalError = 106; print "FATAL_ERROR: could not clear $filename to avoid stale data +\n"; exit $fatalError; }; print OUTFILE "Cleared to avoid stale data\nIf this message is here af +ter running the script, the expect script did not run\n"; close OUTFILE; # ========================================= # debug info to calculate run time # ========================================= if ($debugLevel) { my $t4 = getTimestamp(); print "calling expect script at time is $t4\n"; } # ========================================= # get show arp and show ethernet-switching table # ========================================= my $datestamp = getTimestamp(); my $expectCommand = "/home/jstank01/test/showarp.exp " . $srx_ip . " " + . $ex_ip . " " . $user . " " . $pwd . " " . $filename; system ( $expectCommand ); # ========================================= # debug info to calculate run time # ========================================= if ($debugLevel) { my $t5 = getTimestamp(); print "completed expect script at time is $t5\n"; } # ========================================= # get association between port number and # IP address # ========================================= getPortData(); # ========================================= # parse show arp and show ethernet-switching table # ========================================= parseData(); # ========================================= # print the report # ========================================= printData($storeNum, $datestamp ); # ========================================= # debug info to calculate run time # ========================================= if ($debugLevel) { my $t3 = getTimestamp(); print "script complete at time is $t3\n"; } # normal termination exit 0; ##################################################################### # This subroutine pings a single IP address # it is executed in a thread ##################################################################### sub threadedPing { my ($ipaddr) = @_; my $p=Net::Ping->new("icmp", $icmpTimeout ); unless($p->ping($ipaddr)){ return 0; } else { return 1} } ##################################################################### # This subroutine parses the data file created by # the expect script. The file contains the output # of "show arp" from the SRE and the output of # "show ethernet-switching table" from the EX # Once it determines which device is in each port, # it adds the expected port for the device to the # record. Additionally, it handles the special case # of 2 WLAs in the store. ##################################################################### sub parseData { open( INFILE, $filename ) or do { $fatalError = 103; print "FATAL_ERROR: could not open $filename (expect output) for +reading\n"; exit $fatalError; }; my $ap_in_46 = 0; my $arpRecordCount = 0; my $switchRecordCount = 0; while ( <INFILE>) { chomp; my $line = $_; # match ARP entry from SRX if ($line =~ /([0-9A-Fa-f\:]+)[ \t]+([0-9]+\.[0-9]+\.[0-9]+\.([0-9]+)) +[ \t]+([^ \t]+)[ \t]+(vlan\.([0-9]+))/) { my $mac = $1; my $ip = $2; my $lastOctet = $3; my $vlan = $5; my $vlanNum = $6; # ignore vlan 16 which is wireless devices if (16 != $vlanNum) { $arpRecordCount++; $cable_hash{$mac}{ip} = $ip; $cable_hash{$mac}{vlan} = $vlan; $cable_hash{$mac}{last_octet} = $lastOctet; if (exists ($port_hash{$lastOctet}{port})) { $cable_hash{$mac}{correct_port} = $port_hash{$lastOctet}{port}; $cable_hash{$mac}{description} = $port_hash{$lastOctet}{descr +iption}; } else { # octet not found in port table, put in 999 because unknown $cable_hash{$mac}{correct_port} = 999; } } # endif (16 != $vlanNum) } # endif match ARP entry # match ethernet switching table entry if ($line =~ /([^ ]+)[ \t]+([0-9A-Fa-f\:]+)[ \t]+([^ \t]+)[ \t]+[0-9]+ +[ \t]+(ge-0\/0\/([0-9]+))/) { $switchRecordCount ++; my $mac = $2; my $fullPort = $4; my $shortPort = $5; $cable_hash{$mac}{full_port} = $fullPort; $cable_hash{$mac}{short_port} = $shortPort; } # endif ethernet-switching table entry if ($line =~ /EXPECT_ERROR.*SRX/) { $fatalError = 1; print "FATAL ERROR: Could not connect to SRX\n"; } if ($line =~ /EXPECT_ERROR.*EX/) { $fatalError += 2; print "FATAL ERROR: Could not connect to EX2200\n"; } } # end while ( <INFILE>) close INFILE; # ============================================ # create hash which associates port numbers to # mac addresses. We only care about ports that # have an IP address. Also check if there is about # valid WLA plugged into port 46. This is used # to see if it is ok to have a WLA in port 47 # ============================================ foreach my $mac (keys %cable_hash) { if ((defined $cable_hash{$mac}{ip}) && (defined $cable_hash{$mac}{ful +l_port}) ){ $port_to_mac_hash{$cable_hash{$mac}{short_port}} = $mac; if ((46 == $cable_hash{$mac}{short_port}) && (46 == $cable_hash{$ma +c}{correct_port})) { $ap_in_46 = 1; } } } # ============================================ # handle stores that have 2 WLAs. Update correct # port for port 47 if there is a valid WLA in # port 46 # ============================================ if ($ap_in_46 && ( defined $port_to_mac_hash{47}) ){ if (46 == $cable_hash{$port_to_mac_hash{47}}{correct_port}) { $cable_hash{$port_to_mac_hash{47}}{correct_port} =47; } } # ============================================ # make sure we have ARP info and ethernet-switching # table info # # ============================================ unless ($arpRecordCount) { print "FATAL ERROR: Did not get ARP records from SRX\n"; $fatalError += 4; } unless ($switchRecordCount) { print "FATAL ERROR: Did not get ethernet-switching table records fr +om EX2200\n"; $fatalError += 8; } exit $fatalError if ($fatalError); } # end sub parseData ##################################################################### # This is a dummy routine to print the results # of looking a the cabling in the store. This # should be replaced with a subroutine that puts # the data where it can be sent to the end user # MAC and IP address not printed unless debugging is on ##################################################################### sub printData { my ($storeNum, $datestamp ) = @_; print "Cabling report for store $storeNum generated at $datestamp\n\n" +; foreach my $key (sort { $a <=> $b } keys %port_to_mac_hash) { my $mac = $port_to_mac_hash{$key}; if ($debugLevel >10 ) { print $mac; print "\t"; print $cable_hash{$mac}{ip}; print "\t"; print $cable_hash{$mac}{last_octet}; print "\t"; } printf '%-18s' , $cable_hash{$mac}{description}; print "\t"; print $cable_hash{$mac}{full_port}; print "\t"; if ($debugLevel >10 ) { print $cable_hash{$mac}{short_port}; print "\t"; print $cable_hash{$mac}{correct_port}; print "\t"; } if ( $cable_hash{$mac}{correct_port} != $cable_hash{$mac}{short_port +}) { print "Move cable in port " . $cable_hash{$mac}{short_port} . " to + port " . $cable_hash{$mac}{correct_port}; } else { print "OK"; } print "\n"; } } # end sub printData ##################################################################### # This subroutine reads the file that has the # expected last octet of the IP address that belongs # in each port. ##################################################################### sub getPortData { open( INFILE, $portFile ) or do { $fatalError = 104; print "FATAL_ERROR: could not open $filename (port to address ass +ociations) for reading\n"; exit $fatalError; }; while ( <INFILE>) { chomp; my $line = $_; if ($line =~ /([0-9]+)[ \t]+([0-9]+)[\t]+(.*)/) { my $octet = $1; my $port = $2; my $description = $3; $port_hash{$octet}{port} = $port; $port_hash{$octet}{description} = $description; }elsif ($line =~ /([0-9]+)[ \t]+([0-9]+)/) { my $octet = $1; my $port = $2; $port_hash{$octet}{port} = $port; $port_hash{$octet}{description} = ""; } } close INFILE; } # end sub getPortData #################################################################### # # read the store info file # This file contains the info from allstores.xlsx in a # script friendly format # ##################################################################### + sub getStoreInfo { my $COL_hostname = 0; my $COL_ip = 8; my $COL_t1_addr = 15; my $COL_t1_peer_addr = 14; #my $COL_avn_nbr = my $COL_local_as = 16; my $COL_st0_unit0_addr = 12; my $COL_st0_unit0_peer = 11; my $COL_st0_unit1_addr = 10; my $COL_st0_unit1_peer = 9; my $COL_state = 3; my $COL_city =2; # ===================================================== # device variables for all stores # ===================================================== open( STOREFILE, $storeFile ) or do { $fatalError = 105; print "FATAL_ERROR: could not open $storeFile (store information) + for reading\n"; exit $fatalError; }; # ===================================================== # go through all devices and build hash based on ip # # ===================================================== while (<STOREFILE>) { chomp; my $line = $_; if ($line =~ /<STORENUM>/){ my @input_tags = split("\t", $line); for my $i (0 .. $#input_tags) { if ($input_tags[$i] =~ /<STORENUM>/ ) { $COL_hostname = $i; } if ($input_tags[$i] =~ /<LEGACY-OCTET>/ ) { $COL_ip = $i; } if ($input_tags[$i] =~ /<T1-WAN-ADDRESS>/ ) { $COL_t1_addr = $i; +} if ($input_tags[$i] =~ /<ATT-BGP-PEER-ADDR>/ ) { $COL_t1_peer_add +r = $i; } if ($input_tags[$i] =~ /<STORE-AS>/ ) { $COL_local_as = $i; } if ($input_tags[$i] =~ /<VPN-TUNNEL-1-ADDR>/ ) { $COL_st0_unit0_a +ddr = $i; } if ($input_tags[$i] =~ /<VPN-1-TUNNEL-PEER-ADDR>/ ) { $COL_st0_un +it0_peer = $i; } if ($input_tags[$i] =~ /<VPN-TUNNEL-2-ADDR>/ ) { $COL_st0_unit1_a +ddr = $i; } if ($input_tags[$i] =~ /<VPN-2-TUNNEL-PEER-ADDR>/ ) { $COL_st0_un +it1_peer = $i; } if ($input_tags[$i] =~ /<State>/ ) { $COL_state = $i; } if ($input_tags[$i] =~ /<City>/ ) { $COL_city = $i; } # if ($input_tags[$i] =~ /<SNMP-LOCATION>/ ) { $COL_city = $i; # $COL_state = -1; + } } next; } if ($line =~ /hostname[ \t]+ip/) {next;} # ===================================================== # read variables for a store # ===================================================== my @input_vars = split("\t", $line); my $store_number = $input_vars[$COL_hostname]; my $ip = $input_vars[$COL_ip]; if ($ip =~ /([0-9]*\.[0-9]*\.[0-9]*)/) { $ip = $1; } my $t1_addr = $input_vars[$COL_t1_addr]; my $t1_peer_addr = $input_vars[$COL_t1_peer_addr]; my $avn_nbr = '13979'; my $local_as = $input_vars[$COL_local_as]; #$input_vars[16]; my $st0_unit0_addr = $input_vars[$COL_st0_unit0_addr]; my $st0_unit0_peer = $input_vars[$COL_st0_unit0_peer]; my $st0_unit1_addr = $input_vars[$COL_st0_unit1_addr]; my $st0_unit1_peer = $input_vars[$COL_st0_unit1_peer]; my $state = $input_vars[$COL_state]; my $city = $input_vars[$COL_city]; my $bb_static_ip = '1.1.1.2/30'; #$input_vars[2]; my $bb_static_nh = '1.1.1.1'; #$input_vars[2]; # print "adding store <$store_number> ip <$ip>\n"; $device_ip_hash{$ip} = $store_number; $store_info_hash{$store_number}{ip} =$ip; $store_info_hash{$store_number}{state} =$state; } } # end getStoreInfo #################################################################### # # This subroutine creates a timestamp # # ##################################################################### + sub getTimestamp { my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) + = localtime(time); if ( $year > 99 ) { $year = $year + 1900; } $mon = $mon + 1; $mon = sprintf("%02d", $mon); $sec = sprintf("%02d", $sec); $min = sprintf("%02d", $min); $hour = sprintf("%02d", $hour); $mday = sprintf("%02d", $mday); $year = sprintf("%02d", $year); $wday = sprintf("%02d", $wday); $yday = sprintf("%02d", $yday); $isdst = sprintf("%02d", $isdst); my $datestamp = $mon . '-' . $mday . '-' .$year . ' ' . $hour . ':' . $min . ' +:' . $sec; return($datestamp); } # end sub getTimestamp #################################################################### # # This subroutine adds a range of octets to the list of devices to # be pinged. This list only contains the 4th octet of the addess # The first 3 octets are specified in $base_ip ##################################################################### + sub addRangeToPingList { my ( $start , $end) = @_; my $octet = $start; while ($octet <= $end) { push (@pingList, $octet); $octet++; } } # end sub addRangeToPingList ##################################################################### # This subroutine pings a list of IP addresses where # the first 3 octets are specified in $base_ip and the # 4th octet of each device to be pinged is in the array called pingLi +st ##################################################################### sub pingArrayThreaded { my ($base_ip) = @_; my $upCount = 0; my $octet; foreach my $octet (@pingList) { my $ipaddr = $base_ip . '.' . $octet; my $thr = threads->new(\&threadedPing , $ipaddr); $octet++; } # end while my @running = threads->list(threads::running); while ($#running > 0) { print "running " . $#running . " threads\n" if ($debugLevel >5); my @joinable = threads->list(threads::joinable); foreach my $joinableThr (@joinable) { $upCount += $joinableThr->join(); } sleep(1); @running = threads->list(threads::running); } my @joinable = threads->list(threads::joinable); foreach my $joinableThr (@joinable) { $upCount += $joinableThr->join(); } return ($upCount); } # end sub pingRange
  • Comment on Script to determine whether the cables are plugged into the correct ports in a EX2200
  • Download Code

Replies are listed 'Best First'.
Re: Script to determine whether the cables are plugged into the correct ports in a EX2200
by RonW (Vicar) on Sep 14, 2017 at 23:12 UTC

    Handy tool.

    Reminds me of when, at my previous employer, years ago, we had just moved to a new office. The facilities people were complaining about the seating changes we were making. Since we had VOIP-base desk phones, I decided to team-up with one of the IT network techs for an afternoon to create a Perl program to auto-generate a "spreadsheet map" of which cubes people were in based on the location of their desk phones.

    Unfortunately, I've lost the copy of the code I had and the company isn't going to give me a copy.

    Anyway, it worked by querying the Ethernet switches for the IP and MAC addresses of devices connected to each port, looking for the MAC address prefix of the desk phones. This gave us a mapping of phones to ports. There was already a spreadsheet that had a mapping of port numbers to cube coordinates, giving us a mapping of phone IPs to cube locations. Then, our program queried the VOIP server to get the names assigned to each phone by IP. Finally, our program filled in spreadsheet cells with the names of the people. It also created a simple, 4 column CSV file with the rows alpha-sorted by name and listing the phone numbers and cube locations. We also diff'd the CSV against the previous version and auto-email'd the diff report to HR so they could update the corporate address book.

    The code was a mess of (mostly) copy-paste-modify clippings, but worked. And got the facilities people to stop hassling us. Not bad for 4 hours.

    (We also joked about using AVR Butterflys as end-of-row name displays that would cycle through the people in the row each Butterfly was located. Would have been fun, but we knew couldn't justify the purchase costs of everything we would need.)