Article Figure 1 Figure 2 Figure 3 Figure 4
Listing 1 Listing 2 Listing 3 Listing 4 Listing 5 jul2004.tar

Listing 1 The configurator

#!/usr/bin/perl -w

# SAN To MRTG Configurator
# Mike Scott    : mike@hindsight.it
# Version        : 1.0
# Date        : Feb 2003

######
# WARNING - THIS DATASTRUCTURE IS CASE SENSITIVE
# Make sure that hostnames match exactly...
######
%configuration= (
"sitea" => {
    "switcha1" => {
#            PORT => [HOSTNAME,      HBA NAME    ]
            "01" => ["hosta1",    "fcaw0"    ],
            "04" => ["hosta2",    "fcaw0"    ],
            "07" => ["symma",    "FA-2"    ]
    },
    "switcha2" => {
            "01" => ["hosta1",    "fcaw1"    ],
            "12" => ["hosta2",    "fcaw1"    ],
            "07" => ["symma",    "FA-1"    ],
            "15" => ["switchb1",    "port15"    ]
    }
},
"siteb" => {
    "switchb1" => {
            "01" => ["hostb1",    "fcaw0"    ],
            "09" => ["hostb2",    "fcaw0"    ],
            "03" => ["symmb",    "FA-1"    ],
            "15" => ["switcha1",    "port15"    ]
    },
    "switchb2" => {
            "06" => ["hostb1",    "fcaw1"    ],
            "09" => ["hostb2",    "fcaw1"    ],
            "03" => ["symmb",    "FA-2"    ]
    }
}
);

# Some globals...
$MRTGDIR="/export/data/MRTG";
# MRTG html directory
$HTMLDIR="$MRTGDIR/html";
# MRTG config directory
$CFGDIR="$MRTGDIR/cfg";
# MRTG log directory
$LOGDIR="$MRTGDIR/logs";



#########################
# Routine to generate the MRTG configuration per port
sub brocade_port ($$) {
    # These two values are the OIDs from the Brocade MIB that specify 
    # swFCPortTxWords and swFCPortRxWords. They represent the number
    # of 4 byte Fibrechannel words seen passing through any given port
    # (whose port number is appended to the end of the OID)
    my $OID_INPUT="1.3.6.1.4.1.1588.2.1.1.1.6.2.1.11.";
    my $OID_OUTPUT="1.3.6.1.4.1.1588.2.1.1.1.6.2.1.12.";
    my ($port,$switch)=@_;

    # Physical port 0 is referred to as port 1 in the
    # SNMP information
    $port++;


    # Multiply the SNMP values (swFCPortTxWords/swFCPortRxWords) by 4 to 
    # get the number of bytes.

    # Also notice, the ::1 - we are using SNMPv1 - I noticed that the
    # Brocade 2800 appears to support v2, but our 12000 does not.
    return "( ${OID_INPUT}${port}\&${OID_OUTPUT}${port}:public\@${switch}:::::1 * 4 )"
}

#########################
# A quick and easy routine to check if a directory exists, if not
# then create it.
sub checkAndMkdir ($) {
    my ($dir)=@_;

    if ( ! -d $dir )  {
        print "Warning: Creating directory $dir\n";
        mkdir($dir,0755) || die "Error: Could not create $dir\n";
    }
}




#########################
# Main Program

&checkAndMkdir("$CFGDIR");
&checkAndMkdir("$HTMLDIR");
&checkAndMkdir("$HTMLDIR/group");
&checkAndMkdir("$LOGDIR");
&checkAndMkdir("$LOGDIR/group");



# Loop once per group (which is a general term for "site" in our context)
foreach $group (keys %configuration) {

    # Clear the %hosts cache 
    my %hosts = ();
    print "Generating $group.cfg file...\n";

    # take a copy of the group configuration 
    # Not particularly efficient, but it makes referencing the complex
    # data structure easier...
    my %conf=%{$configuration{$group}};

    # First we need to invert the config, so we are 
    # looking at it from a host-centric view, rather than
    # the switch-centric view...
    foreach $switch (keys %conf) {

        # Loop once per defined switch port
        foreach $port (sort keys %{$conf{$switch}}) {

            # Get the hostname and HBA from the config
            ($hostname,$interface)=@{$conf{$switch}->{$port}};

            # Generate a hash of hosts in the SAN
            # each hash element is a list of switches and ports
            # that the machines' HBAs are connected to
            push(@{$hosts{$hostname}},"$switch:$port");
        }
    }

    # Create the HTML and log directories, if they don't already exist
    &checkAndMkdir("$HTMLDIR/group/$group");
    &checkAndMkdir("$HTMLDIR/group/$group/img");
    &checkAndMkdir("$LOGDIR/group/$group");

    # Each group has its' own MRTG config file to be processed separately
    open(CFG,">$CFGDIR/$group.cfg") || die "ERROR: Cannot open \
      $CFGDIR/$group.cfg\n";
    print CFG "HtmlDir:  $HTMLDIR/group/$group/\n";
    print CFG "ImageDir: $HTMLDIR/group/$group/img\n";
    print CFG "LogDir:   $LOGDIR/group/$group/\n";
    # If a global config file exists, then include it into the 
    # generated config
    if ( -f "$CFGDIR/global.inc" ) {
        print CFG "Include:  $CFGDIR/global.inc\n";
    }

    # Generate the MRTG config for each host in the SAN
    foreach $hostname (sort keys %hosts) {
        @host_aggregate=();
        @host_hbalist=();

        # Generate the config for each HBA port
        foreach $hba (@{$hosts{$hostname}}) {
            ($switch,$port)=split(/:/,$hba,2);

            # Get the HBA interface name
            (undef,$interface)=@{$conf{$switch}->{$port}};

            # Generate the simple configuration for the HBA
            print CFG "Target[${switch}_port${port}]: \
              ".&brocade_port($port,$switch)."\n";
            print CFG "Title[${switch}_port${port}]: Throughput for \
              $switch:$port ($hostname:$interface)\n";
            print CFG "PageTop[${switch}_port${port}]: <H1>Throughput \
              for $switch:$port ($hostname:$interface)</H1>\n";

            # Store the MRTG config for this HBA - this will be used
            # Once the loop is finished to generate the aggregate config
            push(@host_aggregate,&brocade_port($port,$switch));
            push(@host_hbalist,"<a href=${switch}_port${port}.html> \
              <img src=img/${switch}_port${port}-day.png height=100 \
              width=320 alt=\"${switch}:${port} to ${hostname}: \
              ${interface}\"><br><center>${switch}:${port} to \
              ${hostname}:${interface}</center></a>"); 
        }

        # Take all the HBA config cached in @host_aggregate and add them 
        # together 
        print CFG "Target[$hostname]: ".join("+",@host_aggregate)."\n";
        print CFG "Title[$hostname]: Aggregate Throughput for $hostname\n";
        print CFG "PageTop[$hostname]: <H1>Aggregate Throughput for \
          $hostname</H1>\n";
        print CFG "PageFoot[$hostname]: <hr><table>";

        # A nice pretty table at the foot of the aggregate page to
        # Contain thumbnails and links to the individual HBA graphs,
        # Which we stored in @host_hbalist
        my $count=0;
        my $GRIDWIDTH=2;
        foreach (@host_hbalist) {
            if ($count%$GRIDWIDTH==0) {
                print CFG "<tr>";
            }

            print CFG "<td>$_</td>";
    
            if ($count % $GRIDWIDTH==($GRIDWIDTH-1)) {
                print CFG "</tr>";
            }
            $count++;
        }

        while ($count % $GRIDWIDTH gt 0) {
            print CFG "<td>&nbsp;</td>";
            $count++;
        }

        print CFG "</tr></table>\n";

    }
    close(CFG);
}