#!/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