1219820Sjeff#!/usr/bin/perl
2219820Sjeff#
3219820Sjeff# Copyright (c) 2006 The Regents of the University of California.
4219820Sjeff# Copyright (c) 2006-2008 Voltaire, Inc. All rights reserved.
5219820Sjeff#
6219820Sjeff# Produced at Lawrence Livermore National Laboratory.
7219820Sjeff# Written by Ira Weiny <weiny2@llnl.gov>.
8219820Sjeff#            Erez Strauss from Voltaire for help in the get_link_ends code.
9219820Sjeff#
10219820Sjeff# This software is available to you under a choice of one of two
11219820Sjeff# licenses.  You may choose to be licensed under the terms of the GNU
12219820Sjeff# General Public License (GPL) Version 2, available from the file
13219820Sjeff# COPYING in the main directory of this source tree, or the
14219820Sjeff# OpenIB.org BSD license below:
15219820Sjeff#
16219820Sjeff#     Redistribution and use in source and binary forms, with or
17219820Sjeff#     without modification, are permitted provided that the following
18219820Sjeff#     conditions are met:
19219820Sjeff#
20219820Sjeff#      - Redistributions of source code must retain the above
21219820Sjeff#        copyright notice, this list of conditions and the following
22219820Sjeff#        disclaimer.
23219820Sjeff#
24219820Sjeff#      - Redistributions in binary form must reproduce the above
25219820Sjeff#        copyright notice, this list of conditions and the following
26219820Sjeff#        disclaimer in the documentation and/or other materials
27219820Sjeff#        provided with the distribution.
28219820Sjeff#
29219820Sjeff# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30219820Sjeff# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31219820Sjeff# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32219820Sjeff# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
33219820Sjeff# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
34219820Sjeff# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
35219820Sjeff# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36219820Sjeff# SOFTWARE.
37219820Sjeff#
38219820Sjeff
39219820Sjeffuse strict;
40219820Sjeff
41219820Sjeff%IBswcountlimits::cur_counts      = ();
42219820Sjeff%IBswcountlimits::new_counts      = ();
43219820Sjeff@IBswcountlimits::suppress_errors = ();
44219820Sjeff$IBswcountlimits::link_ends       = undef;
45219820Sjeff$IBswcountlimits::pause_time      = 10;
46219820Sjeff$IBswcountlimits::cache_dir       = "/var/cache/infiniband-diags";
47219820Sjeff
48219820Sjeff# all the PerfMgt counters
49219820Sjeff@IBswcountlimits::counters = (
50219820Sjeff	"SymbolErrors",        "LinkRecovers",
51219820Sjeff	"LinkDowned",          "RcvErrors",
52219820Sjeff	"RcvRemotePhysErrors", "RcvSwRelayErrors",
53219820Sjeff	"XmtDiscards",         "XmtConstraintErrors",
54219820Sjeff	"RcvConstraintErrors", "LinkIntegrityErrors",
55219820Sjeff	"ExcBufOverrunErrors", "VL15Dropped",
56219820Sjeff	"XmtData",             "RcvData",
57219820Sjeff	"XmtPkts",             "RcvPkts"
58219820Sjeff);
59219820Sjeff
60219820Sjeff# non-critical counters
61219820Sjeff%IBswcountlimits::error_counters = (
62219820Sjeff	"SymbolErrors",
63219820Sjeff"No action is required except if counter is increasing along with LinkRecovers",
64219820Sjeff	"LinkRecovers",
65219820Sjeff"If this is increasing along with SymbolErrors this may indicate a bad link, run ibswportwatch.pl on this port",
66219820Sjeff	"LinkDowned",
67219820Sjeff	"Number of times the port has gone down (Usually for valid reasons)",
68219820Sjeff	"RcvErrors",
69219820Sjeff"This is a bad link, if the link is internal to a 288 try setting SDR, otherwise check the cable",
70219820Sjeff	"RcvRemotePhysErrors",
71219820Sjeff	"This indicates a problem ELSEWHERE in the fabric.",
72219820Sjeff	"XmtDiscards",
73219820Sjeff"This is a symptom of congestion and may require tweaking either HOQ or switch lifetime values",
74219820Sjeff	"XmtConstraintErrors",
75219820Sjeff	"This is a result of bad partitioning, check partition configuration.",
76219820Sjeff	"RcvConstraintErrors",
77219820Sjeff	"This is a result of bad partitioning, check partition configuration.",
78219820Sjeff	"LinkIntegrityErrors",
79219820Sjeff	"May indicate a bad link, run ibswportwatch.pl on this port",
80219820Sjeff	"ExcBufOverrunErrors",
81219820Sjeff"This is a flow control state machine error and can be caused by packets with physical errors",
82219820Sjeff	"VL15Dropped",
83219820Sjeff	"check with ibswportwatch.pl, if increasing in SMALL increments, OK",
84219820Sjeff	"RcvSwRelayErrors",
85219820Sjeff	"This counter can increase due to a valid network event"
86219820Sjeff);
87219820Sjeff
88219820Sjeffsub check_counters
89219820Sjeff{
90219820Sjeff	my $print_action = $_[0];
91219820Sjeff	my $actions      = undef;
92219820Sjeff
93219820Sjeff	COUNTER: foreach my $cnt (keys %IBswcountlimits::error_counters) {
94219820Sjeff		if ($IBswcountlimits::cur_counts{$cnt} > 0) {
95219820Sjeff			foreach my $sup_cnt (@IBswcountlimits::suppress_errors) {
96219820Sjeff				if ("$cnt" eq $sup_cnt) { next COUNTER; }
97219820Sjeff			}
98219820Sjeff			print " [$cnt == $IBswcountlimits::cur_counts{$cnt}]";
99219820Sjeff			if ("$print_action" eq "yes") {
100219820Sjeff				$actions = join " ",
101219820Sjeff				  (
102219820Sjeff					$actions,
103219820Sjeff					"         $cnt: $IBswcountlimits::error_counters{$cnt}\n"
104219820Sjeff				  );
105219820Sjeff			}
106219820Sjeff		}
107219820Sjeff	}
108219820Sjeff
109219820Sjeff	if ($actions) {
110219820Sjeff		print "\n         Actions:\n$actions";
111219820Sjeff	}
112219820Sjeff}
113219820Sjeff
114219820Sjeff# Data counters
115219820Sjeff%IBswcountlimits::data_counters = (
116219820Sjeff	"XmtData",
117219820Sjeff"Total number of data octets, divided by 4, transmitted on all VLs from the port",
118219820Sjeff	"RcvData",
119219820Sjeff"Total number of data octets, divided by 4, received on all VLs to the port",
120219820Sjeff	"XmtPkts",
121219820Sjeff"Total number of packets, excluding link packets, transmitted on all VLs from the port",
122219820Sjeff	"RcvPkts",
123219820Sjeff"Total number of packets, excluding link packets, received on all VLs to the port"
124219820Sjeff);
125219820Sjeff
126219820Sjeffsub check_data_counters
127219820Sjeff{
128219820Sjeff	my $print_action = $_[0];
129219820Sjeff	my $actions      = undef;
130219820Sjeff
131219820Sjeff	COUNTER: foreach my $cnt (keys %IBswcountlimits::data_counters) {
132219820Sjeff		print " [$cnt == $IBswcountlimits::cur_counts{$cnt}]";
133219820Sjeff		if ("$print_action" eq "yes") {
134219820Sjeff			$actions = join " ",
135219820Sjeff			  (
136219820Sjeff				$actions,
137219820Sjeff				"         $cnt: $IBswcountlimits::data_counters{$cnt}\n"
138219820Sjeff			  );
139219820Sjeff		}
140219820Sjeff	}
141219820Sjeff	if ($actions) {
142219820Sjeff		print "\n         Descriptions:\n$actions";
143219820Sjeff	}
144219820Sjeff}
145219820Sjeff
146219820Sjeffsub print_data_rates
147219820Sjeff{
148219820Sjeff	COUNTER: foreach my $cnt (keys %IBswcountlimits::data_counters) {
149219820Sjeff		my $cnt_per_second = calculate_rate(
150219820Sjeff			$IBswcountlimits::cur_counts{$cnt},
151219820Sjeff			$IBswcountlimits::new_counts{$cnt}
152219820Sjeff		);
153219820Sjeff		print "   $cnt_per_second $cnt/second\n";
154219820Sjeff	}
155219820Sjeff}
156219820Sjeff
157219820Sjeff# =========================================================================
158219820Sjeff# Rate dependent counters
159219820Sjeff# calculate the count/sec
160219820Sjeff# calculate_rate old_count new_count
161219820Sjeffsub calculate_rate
162219820Sjeff{
163219820Sjeff	my $rate    = 0;
164219820Sjeff	my $old_val = $_[0];
165219820Sjeff	my $new_val = $_[1];
166219820Sjeff	my $rate    = ($new_val - $old_val) / $IBswcountlimits::pause_time;
167219820Sjeff	return ($rate);
168219820Sjeff}
169219820Sjeff%IBswcountlimits::rate_dep_thresholds = (
170219820Sjeff	"SymbolErrors", 10, "LinkRecovers",        10,
171219820Sjeff	"RcvErrors",    10, "LinkIntegrityErrors", 10,
172219820Sjeff	"XmtDiscards",  10
173219820Sjeff);
174219820Sjeff
175219820Sjeffsub check_counter_rates
176219820Sjeff{
177219820Sjeff	foreach my $rate_count (keys %IBswcountlimits::rate_dep_thresholds) {
178219820Sjeff		my $rate = calculate_rate(
179219820Sjeff			$IBswcountlimits::cur_counts{$rate_count},
180219820Sjeff			$IBswcountlimits::new_counts{$rate_count}
181219820Sjeff		);
182219820Sjeff		if ($rate > $IBswcountlimits::rate_dep_thresholds{$rate_count}) {
183219820Sjeff			print "Detected excessive rate for $rate_count ($rate cnts/sec)\n";
184219820Sjeff		} elsif ($rate > 0) {
185219820Sjeff			print "Detected rate for $rate_count ($rate cnts/sec)\n";
186219820Sjeff		}
187219820Sjeff	}
188219820Sjeff}
189219820Sjeff
190219820Sjeff# =========================================================================
191219820Sjeff#
192219820Sjeffsub clear_counters
193219820Sjeff{
194219820Sjeff	# clear the counters
195219820Sjeff	foreach my $count (@IBswcountlimits::counters) {
196219820Sjeff		$IBswcountlimits::cur_counts{$count} = 0;
197219820Sjeff	}
198219820Sjeff}
199219820Sjeff
200219820Sjeff# =========================================================================
201219820Sjeff#
202219820Sjeffsub any_counts
203219820Sjeff{
204219820Sjeff	my $total = 0;
205219820Sjeff	my $count = 0;
206219820Sjeff	foreach $count (keys %IBswcountlimits::critical) {
207219820Sjeff		$total = $total + $IBswcountlimits::cur_counts{$count};
208219820Sjeff	}
209219820Sjeff	COUNTER: foreach $count (keys %IBswcountlimits::error_counters) {
210219820Sjeff		foreach my $sup_cnt (@IBswcountlimits::suppress_errors) {
211219820Sjeff			if ("$count" eq $sup_cnt) { next COUNTER; }
212219820Sjeff		}
213219820Sjeff		$total = $total + $IBswcountlimits::cur_counts{$count};
214219820Sjeff	}
215219820Sjeff	return ($total);
216219820Sjeff}
217219820Sjeff
218219820Sjeff# =========================================================================
219219820Sjeff#
220219820Sjeffsub ensure_cache_dir
221219820Sjeff{
222219820Sjeff	if (!(-d "$IBswcountlimits::cache_dir") &&
223219820Sjeff	    !mkdir($IBswcountlimits::cache_dir, 0700)) {
224219820Sjeff		die "cannot create $IBswcountlimits::cache_dir: $!\n";
225219820Sjeff	}
226219820Sjeff}
227219820Sjeff
228219820Sjeff# =========================================================================
229219820Sjeff# get_cache_file(ca_name, ca_port)
230219820Sjeff#
231219820Sjeffsub get_cache_file
232219820Sjeff{
233219820Sjeff	my $ca_name = $_[0];
234219820Sjeff	my $ca_port = $_[1];
235219820Sjeff	ensure_cache_dir;
236219820Sjeff	return (
237219820Sjeff		"$IBswcountlimits::cache_dir/ibnetdiscover-$ca_name-$ca_port.topology");
238219820Sjeff}
239219820Sjeff
240219820Sjeff# =========================================================================
241219820Sjeff# get_ca_name_port_param_string(ca_name, ca_port)
242219820Sjeff#
243219820Sjeffsub get_ca_name_port_param_string
244219820Sjeff{
245219820Sjeff	my $ca_name = $_[0];
246219820Sjeff	my $ca_port = $_[1];
247219820Sjeff
248219820Sjeff	if ("$ca_name" ne "") { $ca_name = "-C $ca_name"; }
249219820Sjeff	if ("$ca_port" ne "") { $ca_port = "-P $ca_port"; }
250219820Sjeff
251219820Sjeff	return ("$ca_name $ca_port");
252219820Sjeff}
253219820Sjeff
254219820Sjeff# =========================================================================
255219820Sjeff# generate_ibnetdiscover_topology(ca_name, ca_port)
256219820Sjeff#
257219820Sjeffsub generate_ibnetdiscover_topology
258219820Sjeff{
259219820Sjeff	my $ca_name      = $_[0];
260219820Sjeff	my $ca_port      = $_[1];
261219820Sjeff	my $cache_file   = get_cache_file($ca_name, $ca_port);
262219820Sjeff	my $extra_params = get_ca_name_port_param_string($ca_name, $ca_port);
263219820Sjeff
264219820Sjeff	if (`ibnetdiscover -g $extra_params > $cache_file`) {
265219820Sjeff		die "Execution of ibnetdiscover failed: $!\n";
266219820Sjeff	}
267219820Sjeff}
268219820Sjeff
269219820Sjeff# =========================================================================
270219820Sjeff# get_link_ends(regenerate_map, ca_name, ca_port)
271219820Sjeff#
272219820Sjeffsub get_link_ends
273219820Sjeff{
274219820Sjeff	my $regenerate_map = $_[0];
275219820Sjeff	my $ca_name        = $_[1];
276219820Sjeff	my $ca_port        = $_[2];
277219820Sjeff
278219820Sjeff	my $cache_file = get_cache_file($ca_name, $ca_port);
279219820Sjeff
280219820Sjeff	if ($regenerate_map || !(-f "$cache_file")) {
281219820Sjeff		generate_ibnetdiscover_topology($ca_name, $ca_port);
282219820Sjeff	}
283219820Sjeff	open IBNET_TOPO, "<$cache_file"
284219820Sjeff	  or die "Failed to open ibnet topology: $!\n";
285219820Sjeff	my $in_switch  = "no";
286219820Sjeff	my $desc       = "";
287219820Sjeff	my $guid       = "";
288219820Sjeff	my $loc_sw_lid = "";
289219820Sjeff
290219820Sjeff	my $loc_port = "";
291219820Sjeff	my $line     = "";
292219820Sjeff
293219820Sjeff	while ($line = <IBNET_TOPO>) {
294219820Sjeff		if ($line =~ /^Switch.*\"S-(.*)\"\s+#.*\"(.*)\".* lid (\d+).*/) {
295219820Sjeff			$guid       = $1;
296219820Sjeff			$desc       = $2;
297219820Sjeff			$loc_sw_lid = $3;
298219820Sjeff			$in_switch  = "yes";
299219820Sjeff		}
300219820Sjeff		if ($in_switch eq "yes") {
301219820Sjeff			my $rec = undef;
302219820Sjeff			if ($line =~
303219820Sjeff/^\[(\d+)\]\s+\"[HSR]-(.+)\"\[(\d+)\](\(.+\))?\s+#.*\"(.*)\"\.* lid (\d+).*/
304219820Sjeff			  )
305219820Sjeff			{
306219820Sjeff				$loc_port = $1;
307219820Sjeff				my $rem_guid      = $2;
308219820Sjeff				my $rem_port      = $3;
309219820Sjeff				my $rem_port_guid = $4;
310219820Sjeff				my $rem_desc      = $5;
311219820Sjeff				my $rem_lid       = $6;
312219820Sjeff				$rec = {
313219820Sjeff					loc_guid      => "0x$guid",
314219820Sjeff					loc_port      => $loc_port,
315219820Sjeff					loc_ext_port  => "",
316219820Sjeff					loc_desc      => $desc,
317219820Sjeff					loc_sw_lid    => $loc_sw_lid,
318219820Sjeff					rem_guid      => "0x$rem_guid",
319219820Sjeff					rem_lid       => $rem_lid,
320219820Sjeff					rem_port      => $rem_port,
321219820Sjeff					rem_ext_port  => "",
322219820Sjeff					rem_desc      => $rem_desc,
323219820Sjeff					rem_port_guid => $rem_port_guid
324219820Sjeff				};
325219820Sjeff			}
326219820Sjeff			if ($line =~
327219820Sjeff/^\[(\d+)\]\[ext (\d+)\]\s+\"[HSR]-(.+)\"\[(\d+)\](\(.+\))?\s+#.*\"(.*)\"\.* lid (\d+).*/
328219820Sjeff			  )
329219820Sjeff			{
330219820Sjeff				$loc_port = $1;
331219820Sjeff				my $loc_ext_port  = $2;
332219820Sjeff				my $rem_guid      = $3;
333219820Sjeff				my $rem_port      = $4;
334219820Sjeff				my $rem_port_guid = $5;
335219820Sjeff				my $rem_desc      = $6;
336219820Sjeff				my $rem_lid       = $7;
337219820Sjeff				$rec = {
338219820Sjeff					loc_guid      => "0x$guid",
339219820Sjeff					loc_port      => $loc_port,
340219820Sjeff					loc_ext_port  => $loc_ext_port,
341219820Sjeff					loc_desc      => $desc,
342219820Sjeff					loc_sw_lid    => $loc_sw_lid,
343219820Sjeff					rem_guid      => "0x$rem_guid",
344219820Sjeff					rem_lid       => $rem_lid,
345219820Sjeff					rem_port      => $rem_port,
346219820Sjeff					rem_ext_port  => "",
347219820Sjeff					rem_desc      => $rem_desc,
348219820Sjeff					rem_port_guid => $rem_port_guid
349219820Sjeff				};
350219820Sjeff			}
351219820Sjeff			if ($line =~
352219820Sjeff/^\[(\d+)\]\s+\"[HSR]-(.+)\"\[(\d+)\]\[ext (\d+)\](\(.+\))?\s+#.*\"(.*)\"\.* lid (\d+).*/
353219820Sjeff			  )
354219820Sjeff			{
355219820Sjeff				$loc_port = $1;
356219820Sjeff				my $rem_guid      = $2;
357219820Sjeff				my $rem_port      = $3;
358219820Sjeff				my $rem_ext_port  = $4;
359219820Sjeff				my $rem_port_guid = $5;
360219820Sjeff				my $rem_desc      = $6;
361219820Sjeff				my $rem_lid       = $7;
362219820Sjeff				$rec = {
363219820Sjeff					loc_guid      => "0x$guid",
364219820Sjeff					loc_port      => $loc_port,
365219820Sjeff					loc_ext_port  => "",
366219820Sjeff					loc_desc      => $desc,
367219820Sjeff					loc_sw_lid    => $loc_sw_lid,
368219820Sjeff					rem_guid      => "0x$rem_guid",
369219820Sjeff					rem_lid       => $rem_lid,
370219820Sjeff					rem_port      => $rem_port,
371219820Sjeff					rem_ext_port  => $rem_ext_port,
372219820Sjeff					rem_desc      => $rem_desc,
373219820Sjeff					rem_port_guid => $rem_port_guid
374219820Sjeff				};
375219820Sjeff			}
376219820Sjeff			if ($line =~
377219820Sjeff/^\[(\d+)\]\[ext (\d+)\]\s+\"[HSR]-(.+)\"\[(\d+)\]\[ext (\d+)\](\(.+\))?\s+#.*\"(.*)\"\.* lid (\d+).*/
378219820Sjeff			  )
379219820Sjeff			{
380219820Sjeff				$loc_port = $1;
381219820Sjeff				my $loc_ext_port  = $2;
382219820Sjeff				my $rem_guid      = $3;
383219820Sjeff				my $rem_port      = $4;
384219820Sjeff				my $rem_ext_port  = $5;
385219820Sjeff				my $rem_port_guid = $6;
386219820Sjeff				my $rem_desc      = $7;
387219820Sjeff				my $rem_lid       = $8;
388219820Sjeff				$rec = {
389219820Sjeff					loc_guid      => "0x$guid",
390219820Sjeff					loc_port      => $loc_port,
391219820Sjeff					loc_ext_port  => $loc_ext_port,
392219820Sjeff					loc_desc      => $desc,
393219820Sjeff					loc_sw_lid    => $loc_sw_lid,
394219820Sjeff					rem_guid      => "0x$rem_guid",
395219820Sjeff					rem_lid       => $rem_lid,
396219820Sjeff					rem_port      => $rem_port,
397219820Sjeff					rem_ext_port  => $rem_ext_port,
398219820Sjeff					rem_desc      => $rem_desc,
399219820Sjeff					rem_port_guid => $rem_port_guid
400219820Sjeff				};
401219820Sjeff			}
402219820Sjeff			if ($rec) {
403219820Sjeff				$rec->{rem_port_guid} =~ s/\((.*)\)/$1/;
404219820Sjeff				$IBswcountlimits::link_ends{"0x$guid"}{$loc_port} = $rec;
405219820Sjeff			}
406219820Sjeff		}
407219820Sjeff
408219820Sjeff		if ($line =~ /^Ca.*/ || $line =~ /^Rt.*/) { $in_switch = "no"; }
409219820Sjeff	}
410219820Sjeff	close IBNET_TOPO;
411219820Sjeff}
412219820Sjeff
413219820Sjeff# =========================================================================
414219820Sjeff# get_num_ports(switch_guid, ca_name, ca_port)
415219820Sjeff#
416219820Sjeffsub get_num_ports
417219820Sjeff{
418219820Sjeff	my $guid         = $_[0];
419219820Sjeff	my $ca_name      = $_[1];
420219820Sjeff	my $ca_port      = $_[2];
421219820Sjeff	my $num_ports    = 0;
422219820Sjeff	my $extra_params = get_ca_name_port_param_string($ca_name, $ca_port);
423219820Sjeff
424219820Sjeff	my $data         = `smpquery $extra_params -G nodeinfo $guid` ||
425219820Sjeff		die "'smpquery $extra_params -G nodeinfo $guid' faild\n";
426219820Sjeff	my @lines        = split("\n", $data);
427219820Sjeff	my $pkt_lifetime = "";
428219820Sjeff	foreach my $line (@lines) {
429219820Sjeff		if ($line =~ /^NumPorts:\.+(.*)/) { $num_ports = $1; }
430219820Sjeff	}
431219820Sjeff	return ($num_ports);
432219820Sjeff}
433219820Sjeff
434219820Sjeff# =========================================================================
435219820Sjeff# format_guid(guid)
436219820Sjeff# The diags store the guids as strings.  This converts the guid supplied
437219820Sjeff# to the correct string format.
438219820Sjeff# eg: 0x0008f10400411f56 == 0x8f10400411f56
439219820Sjeff#
440219820Sjeffsub format_guid
441219820Sjeff{
442219820Sjeff	my $guid     = $_[0];
443219820Sjeff	my $guid_str = "";
444219820Sjeff
445219820Sjeff	$guid =~ tr/[A-F]/[a-f]/;
446219820Sjeff	if ($guid =~ /0x(.*)/) {
447219820Sjeff		$guid_str = sprintf("0x%016s", $1);
448219820Sjeff	} else {
449219820Sjeff		$guid_str = sprintf("0x%016s", $guid);
450219820Sjeff	}
451219820Sjeff	return ($guid_str);
452219820Sjeff}
453219820Sjeff
454219820Sjeff# =========================================================================
455219820Sjeff# convert_dr_to_guid(direct_route)
456219820Sjeff#
457219820Sjeffsub convert_dr_to_guid
458219820Sjeff{
459219820Sjeff	my $guid = undef;
460219820Sjeff
461219820Sjeff	my $data = `smpquery nodeinfo -D $_[0]` ||
462219820Sjeff		die "'mpquery nodeinfo -D $_[0]' failed\n";
463219820Sjeff	my @lines = split("\n", $data);
464219820Sjeff	foreach my $line (@lines) {
465219820Sjeff		if ($line =~ /^PortGuid:\.+(.*)/) { $guid = $1; }
466219820Sjeff	}
467219820Sjeff	return format_guid($guid);
468219820Sjeff}
469219820Sjeff
470219820Sjeff# =========================================================================
471219820Sjeff# get_node_type(guid_or_direct_route)
472219820Sjeff#
473219820Sjeffsub get_node_type
474219820Sjeff{
475219820Sjeff	my $type      = undef;
476219820Sjeff	my $query_arg = "smpquery nodeinfo ";
477219820Sjeff	if ($_[0] =~ /x/) {
478219820Sjeff		# assume arg is a guid if contains an x
479219820Sjeff		$query_arg .= "-G " . $_[0];
480219820Sjeff	} else {
481219820Sjeff		# assume arg is a direct path
482219820Sjeff		$query_arg .= "-D " . $_[0];
483219820Sjeff	}
484219820Sjeff
485219820Sjeff	my $data = `$query_arg` ||
486219820Sjeff		die "'$query_arg' failed\n";
487219820Sjeff	my @lines = split("\n", $data);
488219820Sjeff	foreach my $line (@lines) {
489219820Sjeff		if ($line =~ /^NodeType:\.+(.*)/) { $type = $1; }
490219820Sjeff	}
491219820Sjeff	return $type;
492219820Sjeff}
493219820Sjeff
494219820Sjeff# =========================================================================
495219820Sjeff# is_switch(guid_or_direct_route)
496219820Sjeff#
497219820Sjeffsub is_switch
498219820Sjeff{
499219820Sjeff	my $node_type = &get_node_type($_[0]);
500219820Sjeff	return ($node_type =~ /Switch/);
501219820Sjeff}
502