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> </td>";
$count++;
}
print CFG "</tr></table>\n";
}
close(CFG);
} |