download SwitchMap.pl
Language: Perl
License: GPL
Copyright: Copyright 2008 University Corporation for Atmospheric Research
LOC: 233
Project Info
switchmap
Server: Spider_20090227_inc
Type: filesystem
009\switchmap\switchmap‑11.4\
   CiscoConstants.pm
   Constants.pm
   EtherChannel.pm
   FindOffice.pl
   GetArp.pl
   GetIsPortTrunking.pm
   MacIpTables.pm
   ModuleList.pm
   OuiCodes.pm
   PetesUtils.pm
   PopulateEtherChannels.pm
   PopulatePorts.pm
   Port.pm
   ScanSwitch.pl
   SnmpCommunities.pm
   Stats.pm
   Switch.pm
   SwitchMap.pl
   SwitchUtils.pm
   ThisSite.pm
   UpdateOuiCodes.pl
   VerifyPortLabels.pm
   Vlan.pm
   ...igePerVlansDirectory.pm
   WriteModulesFile.pm
   WriteNcarFiles.pm
   WritePortsDirectory.pm
   WriteSwitchesDirectory.pm
   WriteTextDirectory.pm
   WriteUnusedDirectory.pm
   WriteVlansDirectory.pm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
#!/usr/bin/perl -w
#
#   SwitchMap.pl - generate web pages that describe Cisco switches
#
# This program's version number is in file Constants.pm.
#
# AUTHOR
#
# Pete Siemsen, siemsen@ucar.edu, 303-497-1810
#
# AVAILABILITY
#
# The current version should always be available at
#    http://sourceforge.net/projects/switchmap/
#
#-------------------------------------------------------------------------
# Copyright 2008 University Corporation for Atmospheric Research
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#
# For more information, please contact
# Pete Siemsen, siemsen@ucar.edu
#--------------------------------------------------------------------------
#
# This script outputs HTML files and text files that describe Cisco
# Ethernet switches.  The files show information about each switch
# port, including how long the port has been idle and if possible,
# what machines are connected to each port.  This script can use
# information from a machine running HP Network Node Manager, or
# information from local configuration files.
#
# The basic algorithm is:
#
# Every hour, a cron job runs the ScanSwitch.pl script, which checks
# every switch to see which ports are active.  For each switch,
# ScanSwitch.pl uses SNMP to download the operational status of each
# port on the switch.  ScanSwitch.pl stores the state of each port in
# "idlesince" files, for later use by this script.
#
# Every hour, another cron job runs GetArp.pl, a script that gets the
# ARP caches from the routers and updates MacList, a file that provides
# MAC-address to IP-address information for later use by this script.
#
# Every day, a cron job runs this script, which integrates the data in
# the idlesince files, possibly data from HP NNM, possibly data from
# the MacList file, and more SNMP data from the switches themselves,
# and outputs HTML and text files.
#
# These are the various files that this script needs, reads or writes:
#
#   1. SwitchMap.pl (this script), which is run in a cron job every day.
#   2. ScanSwitch.pl, a Perl script which is run in a cron job every hour.
#   3. GetArp.pl, a Perl script which is run in a cron job every hour.
#   4. MacList, a text file containing IP addresses for every MAC
#      address, as collected by GetArp.pl.
#   5. OuiCodes.txt, a text file containing the manufacturer codes that
#      are found in MAC addresses.  OuiCodes.txt is read by this script.
#      It is generated by UpdateOuiCodes.pl.
#   6. UpdateOuiCodes.pl, a Perl script that generates the OuiCodes.txt
#      file.  See comments in the script for details.
#   7. "idlesince" files, one for each switch.  These files live in
#      the "idlesince" subdirectory, are updated every hour by
#      ScanSwitch.pl, and read by SwitchMap.pl.  These files contain, on
#      each line, a port name and Unix timestamp of the most recent time
#      that the port was found to be idle, or 0 if the port was active the
#      last time ScanSwitch.pl was run.  Thus, the port has been "idle
#      since" the given time.
#   8. HTML files in the "switches", "unused" and "vlans" subdirectories.
#      These files are generated by SwitchMap.pl.  These are the Web
#      pages that are the point of all this.
#   9. Text files named "*.map" in the "text" sub directory, one for each
#      switch.  These files are generated by SwitchMap.pl.  These are
#      simple text versions of the "switches" HTML files.  They are needed
#      to support the search function, and are nice to have when you want
#      to grep for something.
#
# BUGS
#
# . the string "port is active, but no packets have been seen recently"
#   appears on too many ports.  Like, several ports on l3-gw-1.frgp.net
#   show it when they clearly have seen packets.
#
# . Suns with multiple network interfaces use the same MAC address
#   on all the interfaces.  This causes incorrect entries in some IP
#   address columns.  Dunno how to fix this.
#
# . Sometimes, a 6509 will report an ifName table that is missing an
#   entry.  Other SNMP tables will refer to the ifIndex for the entry,
#   causing SwitchMap to generate "Warning: no interface name for SNMP
#   ifIndex <n> on <SwitchName>, skipping <Mac>" messages.  Rebooting
#   the switch makes the problem go away.  I opened Cisco TAC case
#   602418175, but the problem switch was rebooted before I could
#   supply enough information to Cisco, so I asked them to close the
#   case.  Two different 6509s did it.  Both were running 7.6(12).
#   This bug existed as of 2005-11-10, and is still seen on some
#   of our switches as of 2007-09-06.
#
# TODO
#
# . Consider the SNMP-BridgeQuery module (Google for it)
#
# . Rewrite FindOffice.pl so it parses the HTML files instead of the
#   .map files.  Then relax the 20-character limit on Port Labels.
#   This is needed because IOS systems can have "description" fields
#   that are longer than 20 characters, so there's no reason other
#   than the .map files to retain the limit.  Remember to truncate
#   the Port Labels as they are written to the .map files, to preserve
#   the formatting of the .map files.
#
# . Add "SNMP agent has been up for xxx" to the Model/Contact/Location
#   information at the top of switch web pages.
#
# . An easy one: as we read manufacturer names from OuiCodes file,
#   convert any spaces into &nbsp;.  This will make entries in the
#   column be lined up with entries in other columns.
#
# . In ModuleList.pm, modules are represented by a set of hashes, each
#   of which uses the same keys (module number).  At one time, this
#   seemed cool because GetSnmpTable can read right into these arrays.
#   Now that I've added support for getting module data from the Entity
#   MIB, it would be cleaner to represent modules as an array of module
#   objects.  This would be more natural, and would get rid of the
#   $NbrModules variables, and I could initialize the fields in the
#   objects to 'unknown', which would be cleaner than the tests I do
#   now.
#
# . David Mitchell asked
#   "is it possible to indicate the auto negotiation status on the
#   port lists? I assume you are grabbing the duplex out of portDuplex
#   in the Cisco StackMIB?  If so, grab the previous column
#   portAdminSpeed and if it is a 1 or 2, prepend 'a-' onto the
#   displayed duplex.  Or something like that.  Basically speed and
#   duplex cannot have their negotiation status changed
#   independently.  The portAdminSpeed value indicates whether both
#   of them are auto negotiate or fixed."
#
# . Lance Vermillion noticed that the Spare Ports pages count
#   trunk ports, which is wrong - trunk ports are not contenders
#   for use as spare ports.
#
# . For etherchannels, David Mitchell wants to see MACs on both ports
#   somehow.  The key is that the MAC addresses are hooked to the
#   *parent* port, not the children.
#
# . Show port errors.  There isn't room to add more columns to the
#   existing web pages, so make a new set of web pages under "ports"
#   for "error" ports, which are ports that have errors that exceed
#   some threshold.  Columns in these new pages might include
#
#      FCS
#      runts
#      giants
#      last change counter/bouncing
#      100Mbps but not full duplex
#      admin auto but not full
#      not admin auto
#      input traffic but no output traffic
#      output traffic but no input traffic
#
#   Note that some errors are not shown by the "show ports"
#   command - there are more errors shown by the "show counters"
#   command.
#
#   For some of these, I'll have to put new data into the idlesince
#   files to track state.  Perhaps I should put a single "error" link
#   on each row of the main web pages - if the link exists, there's
#   something wrong with the port.  If I do this, maybe empty
#   "comment" fields shouldn't be indicated with color on the main
#   pages any more - it should be in the "error" pages.
#
# . When SwitchMap reads the MacList file, it does a DNS lookup on
#   every one of the IP addresses in the file.  If a DNS server is
#   down, these DNS lookups can time out on each lookup, causing
#   SwitchMap to appear to hang.  Dunno how to fix this one.
#
# . See the comments about doDNS in MacIpTables.pm.  There's gotta be
#   a better way.
#
# . Make the switch names be links to/from the switch web pages, and
#   make links from those pages back to these Web pages.
#
# . Make links to/from the NOC pages
#
# . Make links from the each switch page to it's corresponding
#   unusedbyswitch page.
#
# . In the header text, show the switch and the uptime for the switch
#
# . Peter Silva <Peter.Silva@pt.ibm.com> requested that the portlist
#   code be able to get data from the host running OpenView via telnet
#   instead of ssh.
#
# . In the spare port lists:
#     . Change the Spare Ports web page so that it shows how many
#       ports are spare on each VLAN on each switch.  Then mark each
#       place where this number is 0 in red.  This is just an idea,
#       and may not be practical.  If I do this, see if it makes
#       sense to identify the spare ports on each VLAN.
#
# . Start using ifLastOperChange, and stop doing ScanSwitch.pl?  Would
#   this work when switches reboot?
#
# . use a database to store: MACs, idlesince times, SNMPv2 capability.
#

use strict;
#use Data::Dumper;
use Getopt::Std;
use Log::Log4perl qw(get_logger :levels);
use File::Spec;
use FindBin;
use lib $FindBin::Bin;
use ThisSite;
use Constants;
use CiscoConstants;
use SnmpCommunities;
use Switch;
use Vlan;
use MacIpTables;
use PetesUtils;                 # InitializeLogging
#use SwitchUtils;
use Stats;                      # WriteSwitchStats
use WriteNcarFiles;
use WriteModulesFile;
use WritePortsDirectory;
use WriteSwitchesDirectory;
use WriteTextDirectory;
use WriteGigePerVlansDirectory;
use WriteVlansDirectory;

sub version { $Constants::VERSION; }

sub Usage () {
  my $MyName = PetesUtils::ThisScriptName();
  die <<WARNING;

 Usage: SwitchMap.pl [-c] [-d n] [-v] [switchname]

 This program creates text and HTML files representing one
 or more Cisco Ethernet switches.  For each switch,
 information about each module and port is generated.

     -c          Function as a cgi script, requires the
                 switchname argument

     -d n        Debugging level from 0 to $Constants::MAX_DEBUGGING_MESSAGE_LEVELS,
                 default is 0

     -i n        Informational level from 0 to $Constants::MAX_INFORMATIONAL_MESSAGE_LEVELS,
                 default is 0

     -f          Write log message to a file named
                 $MyName.log

     -v          Display the version and exit

     switchname  The name of a switch.  The name must be
                 composed of only lowercase letters,
                 digits, dashes and periods.

                 If no switch name is given, all switches
                 are processed.  In this case, the list
                 of switches is retrieved from HP Network
                 Node Manager or the hard-coded list in
                 ThisSite.pm.

WARNING
}


#
# Get the switch name from the command line.  If there is no
# switch name, then do all switches.
#
sub ParseCommandLineAndInitializeLogging ($$) {
  my $CgiRef           = shift;
  my $SwitchNameRef    = shift;

  my %options;
  if (getopts('cd:fi:sv', \%options) == 0) {
    Usage();
  }
  $$CgiRef = (exists $options{'c'});
  my $opt_d = 0;
  my $opt_i = 0;
  my $opt_s = 0;
  if (exists $options{'d'}) {
    $opt_d = $options{'d'};
    Usage unless $opt_d =~ /^\d+$/ and $opt_d >= 0 and $opt_d <= $Constants::MAX_DEBUGGING_MESSAGE_LEVELS;
  } elsif (exists $options{'i'}) {
    $opt_i = $options{'i'};
    Usage unless $opt_i =~ /^\d+$/ and $opt_i >= 0 and $opt_i <= $Constants::MAX_DEBUGGING_MESSAGE_LEVELS;
  }
  if (exists $options{'v'}) {
    my $version = version();
    die "SwitchMap version $version\n";
  }
  my $LogToFile = 0;
  if (exists $options{'f'}) {
    $LogToFile = 1;
  }
  if ($#ARGV == -1) {           # if no arguments
    if ($$CgiRef) {
      die "-c option requires that a switchname be suppiled, exiting\n";
    }
  } elsif ($#ARGV == 0) {       # if there's one argument
    $$SwitchNameRef = $ARGV[0]; # must be a switch name
  } else {                      # else, too many arguments
    Usage();
  }
  PetesUtils::InitializeLogging($LogToFile, $opt_i, $opt_d, $Constants::MAX_DEBUGGING_MESSAGE_LEVELS);
}


sub WriteSearchHelpFile () {
  my $logger = get_logger('log2');
  my $SearchHelpFileName = File::Spec->catfile($ThisSite::DestinationDirectory, $Constants::SearchHelpFile);
  $logger->debug("called, writing $SearchHelpFileName");

  $logger->info("writing $SearchHelpFileName");
  open SEARCHHELPFILE, ">$SearchHelpFileName" or do {
    $logger->fatal("Couldn't open $SearchHelpFileName for writing, $!");
    exit;
  };

  print SEARCHHELPFILE SwitchUtils::HtmlHeader("Help searching the Cisco port lists", 0);
  print SEARCHHELPFILE <<SBODY;

You can use this to get answers to questions like

<ul>
<li>What ports are active in a given office?
<li>Where is a given MAC address?
<li>What switch/port is the machine named fileserver connected to?
</ul>

$ThisSite::ExtraHelpText
<p>
Case is not significant in the search.
SBODY
  print SEARCHHELPFILE SwitchUtils::HtmlTrailer;
  close SEARCHHELPFILE;
  SwitchUtils::AllowAllToReadFile $SearchHelpFileName;
  $logger->debug("returning");
}


sub WriteMainIndexFile () {
  my $logger = get_logger('log2');
  my $IndexFileName = File::Spec->catfile($ThisSite::DestinationDirectory, 'index.html');
  $logger->debug("called, writing main $IndexFileName");

  $logger->info("writing $IndexFileName");
  open INDEXFILE, ">$IndexFileName" or do {
    $logger->fatal("Couldn't open $IndexFileName for writing, $!");
    exit;
  };
  print INDEXFILE SwitchUtils::HtmlHeader("Ethernet Switch Port Lists", 0);

  print INDEXFILE <<IDX1;
Information about switches is available in various forms.  You can
<ul>
  <li>
    <a href="SearchPortlists.html">Search the portlist web pages</a>
    for various text strings
  </li>
IDX1

  if ($ThisSite::HasFinder) {
    print INDEXFILE <<IDX2;
  <li>
    <a href="SearchNetwork.html">Search the network itself</a>
    for an address
  </li>
IDX2
  }

  print INDEXFILE <<IDX3;
  <li>Browse the portlist web pages:
    <ul>
    <li><a href = "switches/">Switches</a></li>
    <li><a href = "$Constants::ModulesBySwitchFile">Modules</a></li>
    <li><a href = "ports/">Ports</a></li>
    <li><a href = "vlans/">VLANs</a></li>
    <li><a href = "$Constants::SwitchStatsFile">Statistics</a></li>
IDX3

  print INDEXFILE <<IDX4;
    </ul>
  </li>
</ul>
IDX4
  print INDEXFILE SwitchUtils::HtmlTrailer;
  close INDEXFILE;
  SwitchUtils::AllowAllToReadFile $IndexFileName;
  $logger->debug("returning");
}                               # sub WriteMainIndexFile


#
# Given a list of switch names, return a list of switch objects.
# In other words, create a switch object for each switch, and
# populate the object with real data by doing SNMP to the switch.
#
sub CreateSwitches ($) {
  my $SwitchNames = shift;
  my $logger = get_logger('log2');
  $logger->debug("called");

  my @Switches;
  foreach my $SwitchName (@$SwitchNames) {
    $logger->info("getting data from $SwitchName");
    my $Switch = new Switch $SwitchName;
    if ($Switch->PopulateSwitch()) {
      push @Switches, $Switch;  # save the object
    }
  }
  $logger->debug("returning");
  return @Switches;
}


#
# Go through all the ports in all the switches, to create $VlansRef,
# a hash of Vlan objects.  The keys of the hash serve as a list of
# all VLANs.
#
sub CreateVlans ($$) {
  my $SwitchesRef = shift;    # passed in array of Switch objects
  my $VlansRef    = shift;    # hash of Vlan objects, filled by this subroutine
  my $logger = get_logger('log2');
  $logger->debug("called");

  foreach my $Switch (@$SwitchesRef) {
    my $SwitchName = $Switch->GetName;
    foreach my $PortName (keys %{$Switch->{Ports}}) {
      my $Port = $Switch->{Ports}{$PortName};
      if (exists $Port->{VlanNbr}) {
        my $VlanNbr = $Port->{VlanNbr};
        my $Vlan;
        if (exists $$VlansRef{$VlanNbr}) {
          $Vlan = $$VlansRef{$VlanNbr};
        } else {
          $Vlan = new Vlan $VlanNbr;
          $$VlansRef{$VlanNbr} = $Vlan;
        }
        $Vlan->{Switches}{$SwitchName} = $Switch;
        $Vlan->{NbrPorts}++;
        $Vlan->{NbrUnusedPorts}++ if $Port->{Unused};
      }
    }
  }
  $logger->debug("returning");
}


#
# Main.  ======================================================================
#

my $Cgi;
my $SwitchName = '';
ParseCommandLineAndInitializeLogging(\$Cgi, \$SwitchName);
my $logger = get_logger('log1');
$logger->debug("SwitchMap version $Constants::VERSION starting...");

CiscoConstants::initialize();   # read Cisco MIBs and initialize chassis and module types
SnmpCommunities::initialize();  # read SNMP community strings
MacIpTables::initialize(1);     # read MacList file or OpenView file

my @SwitchNames;
if ($SwitchName) {              # if there is a single switch name on the command line
  @SwitchNames = ( $SwitchName );
} else {
  @SwitchNames = MacIpTables::getAllSwitchNames();
}

$logger->info("getting data from switches ...");
my @Switches = CreateSwitches(\@SwitchNames);
if ($#Switches == -1) {
  $logger->fatal("no switches processed, dying");
  exit;
}
my %Vlans;                         # a hash of Vlan objects indexed by Vlan number
CreateVlans(\@Switches, \%Vlans);  # fill the hash of vlan objects

$logger->info("creating output files...");
WriteMainIndexFile();
WriteSwitchesDirectory::WriteSwitchesFiles(\@Switches);
WriteTextDirectory::WriteSwitchTextFiles(\@Switches);
WriteVlansDirectory::WriteVlansDirectory(\@Switches);
WritePortsDirectory::WritePortsDirectory(\@Switches, \%Vlans);
if ($ThisSite::DnsDomain eq '.ucar.edu') { # only if we're at NCAR
  WriteNcarFiles::WriteNcarFiles(\@Switches);
}
WriteSearchHelpFile();
WriteModulesFile::WriteModulesFile(\@Switches);
Stats::WriteStatisticsFile(\@Switches);
$logger->info("exiting normally...");

About Koders | Resources | Downloads | Support | Black Duck | Submit Project | Terms of Service | DMCA | Privacy Policy | Site Map| Contact Us