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