Blame scripts/check_lft_balance.pl

Packit db064d
#!/usr/bin/perl
Packit db064d
#
Packit db064d
# Copyright (C) 2001-2003 The Regents of the University of California.
Packit db064d
# Copyright (c) 2006 The Regents of the University of California.
Packit db064d
# Copyright (c) 2007-2008 Voltaire, Inc. All rights reserved.
Packit db064d
#
Packit db064d
# Produced at Lawrence Livermore National Laboratory.
Packit db064d
# Written by Ira Weiny <weiny2@llnl.gov>
Packit db064d
#            Jim Garlick <garlick@llnl.gov>
Packit db064d
#            Albert Chu <chu11@llnl.gov>
Packit db064d
#
Packit db064d
# This software is available to you under a choice of one of two
Packit db064d
# licenses.  You may choose to be licensed under the terms of the GNU
Packit db064d
# General Public License (GPL) Version 2, available from the file
Packit db064d
# COPYING in the main directory of this source tree, or the
Packit db064d
# OpenIB.org BSD license below:
Packit db064d
#
Packit db064d
#     Redistribution and use in source and binary forms, with or
Packit db064d
#     without modification, are permitted provided that the following
Packit db064d
#     conditions are met:
Packit db064d
#
Packit db064d
#      - Redistributions of source code must retain the above
Packit db064d
#        copyright notice, this list of conditions and the following
Packit db064d
#        disclaimer.
Packit db064d
#
Packit db064d
#      - Redistributions in binary form must reproduce the above
Packit db064d
#        copyright notice, this list of conditions and the following
Packit db064d
#        disclaimer in the documentation and/or other materials
Packit db064d
#        provided with the distribution.
Packit db064d
#
Packit db064d
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit db064d
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit db064d
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit db064d
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
Packit db064d
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
Packit db064d
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
Packit db064d
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Packit db064d
# SOFTWARE.
Packit db064d
#
Packit db064d
Packit db064d
use strict;
Packit db064d
Packit db064d
use Getopt::Std;
Packit db064d
Packit db064d
my $ibnetdiscover_cache = "";
Packit db064d
my $dump_lft_file       = "";
Packit db064d
my $verbose             = 0;
Packit db064d
Packit db064d
my $switch_lid                            = undef;
Packit db064d
my $switch_guid                           = undef;
Packit db064d
my $switch_name                           = undef;
Packit db064d
my %switch_port_count                     = ();
Packit db064d
my @switch_maybe_directly_connected_hosts = ();
Packit db064d
my $host                                  = undef;
Packit db064d
my @host_ports                            = ();
Packit db064d
Packit db064d
my @lft_lines = ();
Packit db064d
my $lft_line;
Packit db064d
Packit db064d
my $lids_per_port;
Packit db064d
my $lids_per_port_calculated;
Packit db064d
Packit db064d
my $heuristic_flag = 0;
Packit db064d
Packit db064d
sub usage
Packit db064d
{
Packit db064d
	my $prog = `basename $0`;
Packit db064d
Packit db064d
	chomp($prog);
Packit db064d
	print "Usage: $prog -l lft-output -i ibnetdiscover-cache [-e] [-v]\n";
Packit db064d
	print "  Generate lft-output via \"dump_lfts.sh > lft-output\"\n";
Packit db064d
	print "  Generate ibnetdiscover-cache via \"ibnetdiscover --cache ibnetdiscover-cache\"\n";
Packit db064d
	print "  -e turn on heuristic(s) to look at switch balances deeper\n";
Packit db064d
	print "  -v verbose output, output all switches\n";
Packit db064d
	exit 2;
Packit db064d
}
Packit db064d
Packit db064d
sub is_port_up
Packit db064d
{
Packit db064d
	my $iblinkinfo_output = $_[0];
Packit db064d
	my $port              = $_[1];
Packit db064d
	my $decport;
Packit db064d
	my @lines;
Packit db064d
	my $line;
Packit db064d
Packit db064d
	$port =~ /0+(.+)/;
Packit db064d
	$decport = $1;
Packit db064d
Packit db064d
	# Add a space if necessary
Packit db064d
	if ($decport >= 1 && $decport <= 9) {
Packit db064d
		$decport = " $decport";
Packit db064d
	}
Packit db064d
Packit db064d
	@lines = split("\n", $iblinkinfo_output);
Packit db064d
	foreach $line (@lines) {
Packit db064d
		if ($line =~ /$decport\[..\] ==/) {
Packit db064d
			if ($line =~ /Down/) {
Packit db064d
				return 0;
Packit db064d
			}
Packit db064d
			else {
Packit db064d
				return 1;
Packit db064d
			}
Packit db064d
		}
Packit db064d
	}
Packit db064d
Packit db064d
	# return 0 if not found
Packit db064d
	return 0;
Packit db064d
}
Packit db064d
Packit db064d
sub is_directly_connected
Packit db064d
{
Packit db064d
	my $iblinkinfo_output = $_[0];
Packit db064d
	my $port              = $_[1];
Packit db064d
	my $decport;
Packit db064d
	my $str;
Packit db064d
	my $rv = 0;
Packit db064d
	my $host_tmp;
Packit db064d
	my @lines;
Packit db064d
	my $line;
Packit db064d
Packit db064d
	if (($switch_port_count{$port} != $lids_per_port)
Packit db064d
		|| !(@switch_maybe_directly_connected_hosts))
Packit db064d
	{
Packit db064d
		return $rv;
Packit db064d
	}
Packit db064d
Packit db064d
	$port =~ /0+(.+)/;
Packit db064d
	$decport = $1;
Packit db064d
Packit db064d
	# Add a space if necessary
Packit db064d
	if ($decport >= 1 && $decport <= 9) {
Packit db064d
		$decport = " $decport";
Packit db064d
	}
Packit db064d
Packit db064d
	@lines = split("\n", $iblinkinfo_output);
Packit db064d
	foreach $line (@lines) {
Packit db064d
		if ($line =~ /$decport\[..\] ==/) {
Packit db064d
			$str = $line;
Packit db064d
		}
Packit db064d
	}
Packit db064d
Packit db064d
	if ($str =~ "Active") {
Packit db064d
		$str =~
Packit db064d
/[\d]+[\s]+[\d]+\[.+\]  \=\=.+\=\=>[\s]+[\d]+[\s]+[\d]+\[.+\] \"(.+)\".+/;
Packit db064d
		for $host_tmp (@switch_maybe_directly_connected_hosts) {
Packit db064d
			if ($1 == $host_tmp) {
Packit db064d
				$rv = 1;
Packit db064d
				last;
Packit db064d
			}
Packit db064d
		}
Packit db064d
	}
Packit db064d
Packit db064d
	return $rv;
Packit db064d
}
Packit db064d
Packit db064d
sub output_switch_port_usage
Packit db064d
{
Packit db064d
	my $min_usage = 999999;
Packit db064d
	my $max_usage = 0;
Packit db064d
	my $min_usage2 = 999999;
Packit db064d
	my $max_usage2 = 0;
Packit db064d
	my @ports     = (
Packit db064d
		"001", "002", "003", "004", "005", "006", "007", "008",
Packit db064d
		"009", "010", "011", "012", "013", "014", "015", "016",
Packit db064d
		"017", "018", "019", "020", "021", "022", "023", "024",
Packit db064d
		"025", "026", "027", "028", "029", "030", "031", "032",
Packit db064d
		"033", "034", "035", "036"
Packit db064d
	);
Packit db064d
	my @output_ports = ();
Packit db064d
	my @double_check_ports = ();
Packit db064d
	my $port;
Packit db064d
	my $iblinkinfo_output;
Packit db064d
	my $is_unbalanced = 0;
Packit db064d
	my $ports_on_switch = 0;
Packit db064d
	my $all_zero_flag = 1;
Packit db064d
	my $ret;
Packit db064d
Packit db064d
        $iblinkinfo_output = `iblinkinfo --load-cache $ibnetdiscover_cache -S $switch_guid`;
Packit db064d
Packit db064d
	for $port (@ports) {
Packit db064d
		if (!defined($switch_port_count{$port})) {
Packit db064d
			$switch_port_count{$port} = 0;
Packit db064d
		}
Packit db064d
Packit db064d
		if ($switch_port_count{$port} == 0) {
Packit db064d
			# If port is down, don't use it in this calculation
Packit db064d
			$ret = is_port_up($iblinkinfo_output, $port);
Packit db064d
			if ($ret == 0) {
Packit db064d
				next;
Packit db064d
			}
Packit db064d
		}
Packit db064d
Packit db064d
		$ports_on_switch++;
Packit db064d
Packit db064d
		# If port is directly connected to a node, don't use
Packit db064d
		# it in this calculation.
Packit db064d
		if (is_directly_connected($iblinkinfo_output, $port) == 1) {
Packit db064d
			next;
Packit db064d
		}
Packit db064d
Packit db064d
		# Save off ports that should be output later
Packit db064d
		push(@output_ports, $port);
Packit db064d
Packit db064d
		if ($switch_port_count{$port} < $min_usage) {
Packit db064d
			$min_usage = $switch_port_count{$port};
Packit db064d
		}
Packit db064d
		if ($switch_port_count{$port} > $max_usage) {
Packit db064d
			$max_usage = $switch_port_count{$port};
Packit db064d
		}
Packit db064d
	}
Packit db064d
Packit db064d
	if ($max_usage > ($min_usage + 1)) {
Packit db064d
		$is_unbalanced = 1;
Packit db064d
	}
Packit db064d
Packit db064d
	# In the event this is a switch lineboard, it will almost always never
Packit db064d
	# balanced.  Half the ports go up to the spine, and the rest of the ports
Packit db064d
	# go down to HCAs.  So we will do a special heuristic:
Packit db064d
	#
Packit db064d
	# If about 1/2 of the remaining ports are balanced, then we will consider the
Packit db064d
	# entire switch balanced.
Packit db064d
	#
Packit db064d
	# Also, we do this only if there are enough alive ports on the switch to care.
Packit db064d
	# I picked 12 somewhat randomly
Packit db064d
	if ($heuristic_flag == 1
Packit db064d
	    && $is_unbalanced == 1
Packit db064d
	    && $ports_on_switch > 12) {
Packit db064d
Packit db064d
		@double_check_ports = ();
Packit db064d
Packit db064d
		for $port (@output_ports) {
Packit db064d
			if ($switch_port_count{$port} == $max_usage
Packit db064d
			    || $switch_port_count{$port} == ($max_usage - 1)
Packit db064d
			    || $switch_port_count{$port} == 0) {
Packit db064d
				next;
Packit db064d
			}
Packit db064d
Packit db064d
			push(@double_check_ports, $port);
Packit db064d
		}
Packit db064d
Packit db064d
		# we'll call half +/- 1 "about half"
Packit db064d
		if (@double_check_ports == int($ports_on_switch / 2)
Packit db064d
		    || @double_check_ports == int($ports_on_switch / 2) + 1
Packit db064d
		    || @double_check_ports == int($ports_on_switch / 2) - 1) {
Packit db064d
			for $port (@double_check_ports) {
Packit db064d
				if ($switch_port_count{$port} < $min_usage2) {
Packit db064d
					$min_usage2 = $switch_port_count{$port};
Packit db064d
				}
Packit db064d
				if ($switch_port_count{$port} > $max_usage2) {
Packit db064d
					$max_usage2 = $switch_port_count{$port};
Packit db064d
				}
Packit db064d
			}
Packit db064d
Packit db064d
			if (!($max_usage2 > ($min_usage2 + 1))) {
Packit db064d
				$is_unbalanced = 0;
Packit db064d
			}
Packit db064d
		}
Packit db064d
	}
Packit db064d
Packit db064d
	# Another special case is when you have a non-fully-populated switch
Packit db064d
	# Many ports will be zero.  So if all active ports != max or max-1 are = 0
Packit db064d
	# we will also consider this balanced.
Packit db064d
	if ($heuristic_flag == 1
Packit db064d
	    && $is_unbalanced == 1
Packit db064d
	    && $ports_on_switch > 12) {
Packit db064d
Packit db064d
		@double_check_ports = ();
Packit db064d
Packit db064d
		for $port (@output_ports) {
Packit db064d
			if ($switch_port_count{$port} == $max_usage
Packit db064d
			    || $switch_port_count{$port} == ($max_usage - 1)) {
Packit db064d
				next;
Packit db064d
			}
Packit db064d
Packit db064d
			push(@double_check_ports, $port);
Packit db064d
		}
Packit db064d
Packit db064d
		for $port (@double_check_ports) {
Packit db064d
			if ($switch_port_count{$port} != 0) {
Packit db064d
				$all_zero_flag = 0;
Packit db064d
				last;
Packit db064d
			}
Packit db064d
		}
Packit db064d
Packit db064d
		if ($all_zero_flag == 1) {
Packit db064d
			$is_unbalanced = 0;
Packit db064d
		}
Packit db064d
	}
Packit db064d
Packit db064d
	if ($verbose || $is_unbalanced == 1) {
Packit db064d
		if ($is_unbalanced == 1) {
Packit db064d
			print "Unbalanced Switch Port Usage: ";
Packit db064d
			print "$switch_name, $switch_guid\n";
Packit db064d
		} else {
Packit db064d
			print
Packit db064d
			  "Switch Port Usage: $switch_name, $switch_guid\n";
Packit db064d
		}
Packit db064d
		for $port (@output_ports) {
Packit db064d
			print "Port $port: $switch_port_count{$port}\n";
Packit db064d
		}
Packit db064d
	}
Packit db064d
}
Packit db064d
Packit db064d
sub process_host_ports
Packit db064d
{
Packit db064d
	my $test_port;
Packit db064d
	my $tmp;
Packit db064d
	my $flag = 0;
Packit db064d
Packit db064d
	if (@host_ports == $lids_per_port) {
Packit db064d
		# Are all the host ports identical?
Packit db064d
		$test_port = $host_ports[0];
Packit db064d
		for $tmp (@host_ports) {
Packit db064d
			if ($tmp != $test_port) {
Packit db064d
				$flag = 1;
Packit db064d
				last;
Packit db064d
			}
Packit db064d
		}
Packit db064d
		# If all host ports are identical, maybe its directly
Packit db064d
		# connected to a host.
Packit db064d
		if ($flag == 0) {
Packit db064d
			push(@switch_maybe_directly_connected_hosts, $host);
Packit db064d
		}
Packit db064d
	}
Packit db064d
}
Packit db064d
Packit db064d
if (!getopts("hl:i:ve")) {
Packit db064d
	usage();
Packit db064d
}
Packit db064d
Packit db064d
if (defined($main::opt_h)) {
Packit db064d
	usage();
Packit db064d
}
Packit db064d
Packit db064d
if (defined($main::opt_l)) {
Packit db064d
	$dump_lft_file = $main::opt_l;
Packit db064d
} else {
Packit db064d
	print STDERR ("Must specify dump lfts file\n");
Packit db064d
	usage();
Packit db064d
	exit 1;
Packit db064d
}
Packit db064d
Packit db064d
if (defined($main::opt_i)) {
Packit db064d
	$ibnetdiscover_cache = $main::opt_i;
Packit db064d
} else {
Packit db064d
	print STDERR ("Must specify ibnetdiscover cache\n");
Packit db064d
	usage();
Packit db064d
	exit 1;
Packit db064d
}
Packit db064d
Packit db064d
if (defined($main::opt_v)) {
Packit db064d
	$verbose = 1;
Packit db064d
}
Packit db064d
Packit db064d
if (defined($main::opt_e)) {
Packit db064d
	$heuristic_flag = 1;
Packit db064d
}
Packit db064d
Packit db064d
if (!open(FH, "< $dump_lft_file")) {
Packit db064d
	print STDERR ("Couldn't open dump lfts file: $dump_lft_file: $!\n");
Packit db064d
}
Packit db064d
Packit db064d
@lft_lines = <FH>;
Packit db064d
Packit db064d
foreach $lft_line (@lft_lines) {
Packit db064d
	chomp($lft_line);
Packit db064d
	if ($lft_line =~ /Unicast/) {
Packit db064d
		if (@host_ports) {
Packit db064d
			process_host_ports();
Packit db064d
		}
Packit db064d
		if (defined($switch_name)) {
Packit db064d
			output_switch_port_usage();
Packit db064d
		}
Packit db064d
		if ($lft_line =~ /Unicast lids .+ of switch DR path slid .+ guid (.+) \((.+)\)/) {
Packit db064d
			$switch_guid                           = $1;
Packit db064d
			$switch_name                           = $2;
Packit db064d
		}
Packit db064d
		if ($lft_line =~ /Unicast lids .+ of switch Lid .+ guid (.+) \((.+)\)/) {
Packit db064d
			$switch_guid                           = $1;
Packit db064d
			$switch_name                           = $2;
Packit db064d
		}
Packit db064d
		@switch_maybe_directly_connected_hosts = ();
Packit db064d
		%switch_port_count                     = ();
Packit db064d
		@host_ports                            = ();
Packit db064d
		$lids_per_port                         = 0;
Packit db064d
		$lids_per_port_calculated              = 0;
Packit db064d
	} elsif ($lft_line =~ /Channel/ || $lft_line =~ /Router/) {
Packit db064d
		$lft_line =~ /.+ (.+) : \(.+ portguid .+: '(.+)'\)/;
Packit db064d
		$host = $2;
Packit db064d
		$switch_port_count{$1}++;
Packit db064d
		if (@host_ports) {
Packit db064d
			process_host_ports();
Packit db064d
		}
Packit db064d
		@host_ports = ($1);
Packit db064d
Packit db064d
		if ($lids_per_port == 0) {
Packit db064d
			$lids_per_port++;
Packit db064d
		} else {
Packit db064d
			$lids_per_port_calculated++;
Packit db064d
		}
Packit db064d
	} elsif ($lft_line =~ /path/) {
Packit db064d
		$lft_line =~ /.+ (.+) : \(path #. out of .: portguid .+\)/;
Packit db064d
		$switch_port_count{$1}++;
Packit db064d
		if ($lids_per_port_calculated == 0) {
Packit db064d
			$lids_per_port++;
Packit db064d
		}
Packit db064d
		push(@host_ports, $1);
Packit db064d
	} else {
Packit db064d
		if ($lids_per_port) {
Packit db064d
			$lids_per_port_calculated++;
Packit db064d
		}
Packit db064d
		next;
Packit db064d
	}
Packit db064d
}
Packit db064d
Packit db064d
if (@host_ports) {
Packit db064d
	process_host_ports();
Packit db064d
}
Packit db064d
output_switch_port_usage();