6327a8b3da
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@6035 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
1057 lines
34 KiB
Perl
1057 lines
34 KiB
Perl
#!/usr/bin/perl
|
|
# IBM(c) 2008 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
###########################################################################
|
|
# #
|
|
# Command: annotatelog #
|
|
# #
|
|
#-------------------------------------------------------------------------#
|
|
# This xCAT script is used to parse the QLogic log entries in file
|
|
# /var/log/xcat/errorlog/[xCAT Management Nodes] on xCAT Management
|
|
# Node by subnet manager, IB node, chassis, FRU(Field-Replaceable Unit) or a
|
|
# particular node. This script is supported by both AIX and Linux MN.
|
|
# From xCAT's point of view, the log to analyze must be xCAT
|
|
# consolidated log, which means this log file must come from xCAT
|
|
# syslog/errorlog monitoring mechanism, such as
|
|
# /var/log/xcat/errorlog/[xCAT Management Nodes] file. Since the log format
|
|
# is various, xCAT do not support other log files.
|
|
# Command Syntax:
|
|
# annotatelog -f log_file [-s start_time] [-e end_time]
|
|
# { [-i -g guid_file -l link_file] [-S] [-c] [-u]| [-a -g guid_file -l link_file]}
|
|
# {[-n node_list -g guid_file] [-E]}
|
|
# [-h]
|
|
#
|
|
# -A Output the combination of -i, -S, -c and -u. It should be used with -g and -l flags.
|
|
# -f log_file
|
|
# Specifies a log file fullpath name to analyze.
|
|
# Must be xCAT consolidated log got from Qlogic HSM or ESM.
|
|
# -s start_time
|
|
# Specifies the start time for analysis, where the start_time
|
|
# variable has the format ddmmyyhh:mm:ss (day, month, year,
|
|
# hour, minute, and second), 00:00:00 is valid.
|
|
# -e end_time
|
|
# Specifies the end time for analysis, where the end_time
|
|
# variable has the format ddmmyyhh:mm:ss (day, month, year,
|
|
# hour, minute, and second), 00:00:00 is valid.
|
|
# -l link_file
|
|
# Specifies a link file fullpath name, which concatenates all
|
|
# '/var/opt/iba/analysis/baseline/fabric*links' files from all fabric management nodes.
|
|
# -g guid_file
|
|
# Specifies a guid file fullpath name, which has a list of
|
|
# GUIDs as obtained from the "getGuids" script.
|
|
# -E Annotate with node ERRLOG_ON and ERRLOG_OFF information. This
|
|
# can help determine if a disappearance was caused by a node
|
|
# disappearing. It is for AIX nodes only and should be used with -x or -n flag.
|
|
# -S Sort the log entries by subnet manager only.
|
|
# -i Sort the log entries by IB node only.
|
|
# -c Sort the log entries by chassis only.
|
|
# -u Sort the log entries by FRU only.
|
|
# -n node_list
|
|
# Specifies a comma-separated list of node host names, IP addresses to look up in log entries.
|
|
# -h Display usage information.
|
|
# Exit codes:
|
|
# 0 - success
|
|
# 1 - fail
|
|
###########################################################################
|
|
|
|
use Net::Config;
|
|
use Getopt::Long;
|
|
use Time::Local;
|
|
|
|
#Maps between HCA adapter and connector
|
|
%::HCAMAP = (
|
|
"iba0_lsw1" => "C65-T1 (HV=Cx-T1)", "iba0_lsw2" => "C65-T2 (HV=Cx-T2)",
|
|
"iba1_lsw1" => "C65-T3", "iba1_lsw2" => "C65-T4",
|
|
"iba2_lsw1" => "C66-T1", "iba2_lsw2" => "C66-T2",
|
|
"iba3_lsw1" => "C66-T3", "iba3_lsw2" => "C66-T4",
|
|
"ehca0_lsw1" => "C65-T1 (HV=Cx-T1)", "ehca0_lsw2" => "C65-T2 (HV=Cx-T2)",
|
|
"ehca1_lsw1" => "C65-T3", "ehca1_lsw2" => "C65-T4",
|
|
"ehca2_lsw1" => "C66-T1", "ehca2_lsw2" => "C66-T2",
|
|
"ehca3_lsw1" => "C66-T3", "ehca3_lsw2" => "C66-T4",
|
|
);
|
|
|
|
$::OK = 0;
|
|
$::NOK = 1;
|
|
$::SHSEP = "###############################################################################";
|
|
$::DASEP = "-------------------------------------------------------------------------------";
|
|
|
|
#Logs sorted by kinds of method
|
|
%::SMLOGS;
|
|
%::FRULOGS;
|
|
%::CHASSISLOGS;
|
|
%::NODELOGS;
|
|
%::NODELOGSBYNAME;
|
|
%::OTHERLOGS;
|
|
#The start time and end time of logs that will be analyzed
|
|
$::Start_Epoch;
|
|
$::End_Epoch;
|
|
#Record the relationship between guid and it's information
|
|
%::GUIDS;
|
|
#Record links between HCA and Switch and links between Switch and Swtich
|
|
%::HCALinks;
|
|
#ERRLOG_ON and ERRLOG_OFF information
|
|
%::ELOGON;
|
|
%::ELOGOFF;
|
|
#Information related with a node
|
|
%::NodeInfo;
|
|
#List of nodes that their logs will be analyzed
|
|
@::NodeList;
|
|
|
|
# MAIN Main main#
|
|
&getArgs();
|
|
&getLogs($::LOG_FILE);
|
|
&outputLogs();
|
|
exit $::OK;
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 getArgs
|
|
Parse the command line and check the values
|
|
Notes:
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub getArgs()
|
|
{
|
|
$Getopt::Long::ignorecase = 0;
|
|
if (
|
|
!GetOptions(
|
|
'h' => \$::HELP,
|
|
'A' => \$::ALL,
|
|
'f=s' => \$::LOG_FILE,
|
|
's=s' => \$::START_TIME,
|
|
'e=s' => \$::END_TIME,
|
|
'g=s' => \$::GUIDS_FILE,
|
|
'l=s' => \$::LINKS_FILE,
|
|
'E' => \$::ERRLOG,
|
|
'S' => \$::SM,
|
|
'i' => \$::IBNODE,
|
|
'c' => \$::CHASSIS,
|
|
'u' => \$::FRU,
|
|
'n=s' => \$::NODE_LIST
|
|
)
|
|
)
|
|
{
|
|
usage();
|
|
exit $::NOK;
|
|
}
|
|
if ( $::HELP == 1 )
|
|
{
|
|
usage();
|
|
exit $::OK;
|
|
}
|
|
if ( $::LOG_FILE eq "" )
|
|
{
|
|
print "Please use \"-f\" to specify a log file.";
|
|
usage();
|
|
exit $::NOK;
|
|
}
|
|
|
|
if ( $::SM == 0 && $::IBNODE == 0 && $::CHASSIS == 0 && $::FRU == 0 && $::NODE_LIST eq "")
|
|
{
|
|
#If there is no flag specified, -A is default
|
|
$::ALL = 1;
|
|
}
|
|
|
|
if ( $::ALL == 1 && ($::SM == 1 || $::IBNODE == 1 || $::CHASSIS == 1 || $::FRU == 1))
|
|
{
|
|
print "The flag -i, -S, -c or -u should not be used with -A.";
|
|
usage();
|
|
exit $::NOK;
|
|
}
|
|
|
|
if ( ($::ALL == 1 || $::IBNODE == 1) && ( $::GUIDS_FILE eq "" || $::LINKS_FILE eq ""))
|
|
{
|
|
print "The -g flag and -l flag should be used with -a or -i flag.\n";
|
|
usage();
|
|
exit $::NOK;
|
|
}
|
|
|
|
if ( $::ERRLOG == 1 && ( $::IBNODE == 0 && $::ALL == 0 && $::NODE_LIST eq "") )
|
|
{
|
|
print "The -E flag should be used with -n or -i or -a flag.\n";
|
|
usage();
|
|
exit $::NOK;
|
|
}
|
|
|
|
if ( $::NODE_LIST ne "" && $::GUIDS_FILE eq "" )
|
|
{
|
|
print "The -n flag should be used with -g flag.\n";
|
|
usage();
|
|
exit $::NOK;
|
|
}
|
|
|
|
if ( $::NODE_LIST ne "")
|
|
{
|
|
@::NodeList = split /,/, $::NODE_LIST;
|
|
#List all nodes in Managed or MinManaged mode
|
|
my @AllNodes = `nodels`; chomp @AllNodes;
|
|
my @NotDefinedNodes;
|
|
foreach my $node (@::NodeList)
|
|
{
|
|
#If there are IP addresses in NodeList, convert IP to hostname first
|
|
if (isIpaddr($node)) # why? for xCAT, nodename can not be ip address...
|
|
{
|
|
my ($hostname, $ip) = getHost($node);
|
|
$node = $hostname;
|
|
}
|
|
#Check nodes in NodeList and find those that are not defined in xCAT database or not in Managed or MinManaged mode
|
|
if (!(grep {$_ =~ /$node/} @AllNodes))
|
|
{
|
|
push (@NotDefinedNodes, $node);
|
|
next;
|
|
}
|
|
}
|
|
if (scalar(@NotDefinedNodes))
|
|
{
|
|
my $badnodes = join ",", @NotDefinedNodes;
|
|
print "Could not find Managed Node(s) $badnodes in the node database. Run lsnode -w \"Mode='Managed' or Mode='MinManaged'\" to see the list of valid node names.\n";
|
|
exit $::NOK;
|
|
}
|
|
}
|
|
|
|
if ( $::GUIDS_FILE ne "" ) { %::GUIDS = getGUIDs($::GUIDS_FILE); }
|
|
if ( $::LINKS_FILE ne "" ) { %::HCALinks = getHCALinks($::LINKS_FILE); }
|
|
|
|
if ( $::START_TIME ne "" )
|
|
{
|
|
$::Start_Epoch = epochTime($::START_TIME);
|
|
}
|
|
else
|
|
{
|
|
$::Start_Epoch = 0;
|
|
}
|
|
|
|
if ( $::END_TIME ne "" )
|
|
{
|
|
$::End_Epoch = epochTime($::END_TIME);
|
|
}
|
|
else
|
|
{
|
|
undef $::End_Epoch;
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
=head3 isIpaddr
|
|
|
|
returns 1 if parameter is has a valid IP address form.
|
|
|
|
Arguments:
|
|
dot qulaified IP address: e.g. 1.2.3.4
|
|
Returns:
|
|
1 - if legal IP address
|
|
0 - if not legal IP address.
|
|
Globals:
|
|
none
|
|
Error:
|
|
none
|
|
Example:
|
|
if ($ipAddr) { blah; }
|
|
Comments:
|
|
Doesn't test if the IP address is on the network,
|
|
just tests its form.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------------------------------
|
|
sub isIpaddr
|
|
{
|
|
my ($class, $addr) = @_;
|
|
|
|
if ($addr =~ /:/) { #ipv6
|
|
return 1;
|
|
}
|
|
|
|
#print "addr=$addr\n";
|
|
if ($addr !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ($1 > 255 || $1 == 0 || $2 > 255 || $3 > 255 || $4 > 255)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
=head3 getHost
|
|
|
|
Return primary hostname and ip address for the given hostname or
|
|
ip address and die if hostname resolution fails.
|
|
|
|
Arguments:
|
|
A string: either an Ip Address or a HostName.
|
|
Returns:
|
|
($nodename, $nodeip) as strings
|
|
Error:
|
|
returns 0;
|
|
Example:
|
|
my ($nodename, $nodeip) = getHost($node);
|
|
Comments:
|
|
none
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
sub getHost
|
|
{
|
|
my ($class, $arg, $live) = @_;
|
|
|
|
#print "arg=$arg\n";
|
|
my ($hostname, $ipaddr);
|
|
if (isIpaddr($arg))
|
|
{
|
|
$ipaddr = $arg;
|
|
my $packedaddr = inet_aton($ipaddr);
|
|
$hostname = gethostbyaddr($packedaddr,AF_INET);
|
|
if (!$hostname)
|
|
{
|
|
$hostname = $ipaddr;
|
|
}
|
|
|
|
#print "hostname=$hostname\n";
|
|
}
|
|
else # they specified a hostname
|
|
{
|
|
$hostname = $arg; # this may be a short hostname
|
|
my ($name, $aliases, $addrtype, $length, @addrs) =
|
|
gethostbyname($hostname);
|
|
|
|
#print "name=$name, # of addrs=$#addrs.\n";
|
|
my $packedaddr = $addrs[0];
|
|
$ipaddr = inet_ntoa($packedaddr);
|
|
$hostname =
|
|
$name; # they may have specified a shorter or non-primary name
|
|
#print "ipaddr=$ipaddr, hostname=$hostname\n";
|
|
}
|
|
return ($hostname, $ipaddr);
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 getLogs
|
|
Parse the logs
|
|
Notes:
|
|
Arguments:
|
|
$logfile - The file name of log file
|
|
Returns:
|
|
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub getLogs
|
|
{
|
|
my ($logfile) = @_;
|
|
my $tsepoch;
|
|
unless (open(LOGFILE, $logfile))
|
|
{
|
|
print "Can't open '$logfile':\n\t$!";
|
|
exit $::NOK;
|
|
}
|
|
|
|
foreach my $line (<LOGFILE>)
|
|
{
|
|
chomp $line;
|
|
$tsepoch = 0;
|
|
# only work with local6 logs
|
|
#if ( $line =~ /local6/ ) {
|
|
# parse up log entries
|
|
if ($line =~ /SM:.*COND:#12/ and ($::ALL == 1 || $::SM == 1))
|
|
{
|
|
my ($ts, $level, $sm, $cond, $node, $linkedto, $detail);
|
|
#Parse a line of logs
|
|
#The DETAIL is not always there
|
|
if ( $line =~ /(.........:..:..).*MSG:(\w*)\|SM:(.*)\|COND:(.*)\|NODE:(.*)\|LINKEDTO:(.*)\|DETAIL:(.*)/ )
|
|
{
|
|
($ts, $level, $sm, $cond, $node, $linkedto, $detail) = ($1, $2, $3, $4, $5, $6, $7);
|
|
}
|
|
elsif ($line =~ /(.........:..:..).*MSG:(\w*)\|SM:(.*)\|COND:(.*)\|NODE:(.*)\|DETAIL:(.*)/)
|
|
{
|
|
($ts, $level, $sm, $cond, $node, $detail) = ($1, $2, $3, $4, $5, $6);
|
|
}
|
|
|
|
$tsepoch = epochTime($ts) if($ts);
|
|
#Check the time of this log is in the period specified
|
|
if ( $tsepoch >= $::Start_Epoch && (!defined($::End_Epoch) || ($ts && $tsepoch <= $::End_Epoch)) )
|
|
{
|
|
$::SMLOGS{$sm} = "$::SMLOGS{$sm}\n$line";
|
|
}
|
|
next;
|
|
}
|
|
|
|
if ($line =~ /CHASSIS:/ and ($::ALL == 1 || $::CHASSIS == 1 || $::FRU == 1))
|
|
{
|
|
my ($ts, $level, $chassis, $cond, $FRU, $pn, $details);
|
|
if ( $line =~ /(.........:..:..).*MSG:(\w*)\|CHASSIS:(.*)\|COND:(.*)\|FRU:(.*)\|PN:(.*)\|DETAIL:(.*)/ )
|
|
{
|
|
($ts, $level, $chassis, $cond, $FRU, $pn, $details) = ($1, $2, $3, $4, $5, $6, $7);
|
|
}
|
|
elsif ($line =~ /(.........:..:..).*MSG:(\w*)\|CHASSIS:(.*)\|COND:(.*)\|FRU:(.*)\|PN:(.*)/)
|
|
{
|
|
($ts, $level, $chassis, $cond, $FRU, $pn) = ($1, $2, $3, $4, $5, $6);
|
|
}
|
|
|
|
$tsepoch = epochTime($ts) if($ts);
|
|
if ( $tsepoch >= $::Start_Epoch && (!defined($::End_Epoch) || ($ts && $tsepoch <= $::End_Epoch)) )
|
|
{
|
|
$FRU = "$chassis - $FRU";
|
|
$::FRULOGS{$FRU} = "$::FRULOGS{$FRU}\n$line" if ($::ALL == 1 || $::FRU == 1);
|
|
$::CHASSISLOGS{$chassis} = "$::CHASSISLOGS{$chassis}\n$line" if ($::ALL == 1 || $::CHASSIS == 1);
|
|
}
|
|
next;
|
|
}
|
|
|
|
if ($line =~ /SM:/ && ($::ALL == 1 || $::SM == 1 || $::IBNODE == 1 || $::NODE_LIST ne ""))
|
|
{
|
|
my $guid;
|
|
my $port;
|
|
my $guidref;
|
|
my $realguid;
|
|
my $lsw;
|
|
my $lswref;
|
|
my $hcaphys;
|
|
my $host;
|
|
my $elog_entry;
|
|
my ($ts, $level, $sm, $cond, $node, $linkedto, $detail);
|
|
if ( $line =~ /(.........:..:..).*MSG:(\w*)\|SM:(.*)\|COND:(.*)\|NODE:(.*)\|LINKEDTO:(.*)\|DETAIL:(.*)/)
|
|
{
|
|
($ts, $level, $sm, $cond, $node, $linkedto, $detail) = ($1, $2, $3, $4, $5, $6, $7);
|
|
}
|
|
elsif ( $line =~ /(.........:..:..).*MSG:(\w*)\|SM:(.*)\|COND:(.*)\|NODE:(.*)\|LINKEDTO:(.*)/ )
|
|
{
|
|
($ts, $level, $sm, $cond, $node, $linkedto) = ($1, $2, $3, $4, $5, $6);
|
|
}
|
|
elsif ( $line =~ /(.........:..:..).*MSG:(\w*)\|SM:(.*)\|COND:(.*)\|NODE:(.*)\|DETAIL:(.*)/ )
|
|
{
|
|
($ts, $level, $sm, $cond, $node, $detail) = ($1, $2, $3, $4, $5, $6);
|
|
}
|
|
elsif ( $line =~ /(.........:..:..).*MSG:(\w*)\|SM:(.*)\|COND:(.*)\|NODE:(.*)/ )
|
|
{
|
|
($ts, $level, $sm, $cond, $node) = ($1, $2, $3, $4, $5);
|
|
}
|
|
if ( $sm eq "") { $sm = "Other"; }
|
|
|
|
$tsepoch = epochTime($ts) if($ts);
|
|
if ( $tsepoch >= $::Start_Epoch && (!defined($::End_Epoch) || ($ts && $tsepoch <= $::End_Epoch)) )
|
|
{
|
|
if ($node =~ /IBM .*Logical/ && ($::ALL == 1 || $::IBNODE == 1 || $::NODE_LIST ne ""))
|
|
{
|
|
$guid = $node;
|
|
$guid =~ s/.*:0x//;
|
|
$port = $node;
|
|
$port =~ s/.*port //;
|
|
$port =~ s/:.*//;
|
|
if ( $node =~ /IBM .*Logical Switch/ )
|
|
{
|
|
#It's for Logical Switch
|
|
$realguid = $guid;
|
|
$guid =~ s/..$/00/;
|
|
$node =~ /.*Switch (\d):.*/;
|
|
$lsw = $1;
|
|
#The key for HCALinks to find the next link of this Logical Switch
|
|
$guidref = "$realguid:1";
|
|
}
|
|
else
|
|
{
|
|
#It's for Logical HCA
|
|
$::GUIDS{$guid}{type} =~ /.*(\d+)/;
|
|
$lsw = $1;
|
|
#The key for HCALinks to find the linked Logical Switch of this Logical HCA
|
|
$guidref = "$guid:$port";
|
|
$lswref = $::HCALinks{$guidref};
|
|
$lswref =~ /.*GUID\s*0x(\w+).*/;
|
|
$lswref = $1;
|
|
#The key for HCALinks to find the next link of the Logical Switch
|
|
$lswref = "$lswref:1";
|
|
}
|
|
my $hcadev = $::GUIDS{$guid}{dev};
|
|
my $hcalu = "${hcadev}_lsw${lsw}";
|
|
#Find the connector
|
|
$hcaphys = $::HCAMAP{$hcalu};
|
|
|
|
$host = $::GUIDS{$guid}{host};
|
|
#If this it a log for HCA, the guid can be translated to a hostname using GUIDS map
|
|
$node = "$host :port $port:0x$guid" if($host);
|
|
if ( $::NODE_LIST ne "" )
|
|
{
|
|
if ( grep {$host =~ /$_/ || $_ =~ /$host/} @::NodeList )
|
|
{
|
|
$::NODELOGSBYNAME{$host} = "$::NODELOGSBYNAME{$host}\n$line";
|
|
}
|
|
}
|
|
#If -E is specified, get the ERRLOG_ON and ERRLOG_OFF information
|
|
if ( $::ERRLOG == 1 && $host && ($::ALL == 1 || $::IBNODE == 1 || ($::NODE_LIST ne "" && grep {$host =~ /$_/ || $_ =~ /$host/} @::NodeList)))
|
|
{
|
|
if ( ! exists $::ELOGON{$host})
|
|
{
|
|
my $ret = getERRLOG($host);
|
|
($::ELOGON{$host}, $::ELOGOFF{$host}) = split/[_]/, $ret;
|
|
}
|
|
$elog_entry = "\n# ERRLOG_ON: $::ELOGON{$host}\n# ERRLOG_OFF: $::ELOGOFF{$host}";
|
|
}
|
|
if ($::ALL == 1 || $::IBNODE == 1)
|
|
{
|
|
$::HCALinks{$guidref} =~ /(.+)\s*port.*/;
|
|
my $swname = $1;
|
|
my $lswn = $::HCALinks{$guidref} ? $::HCALinks{$guidref} : "Not Found";
|
|
my $swn = $::HCALinks{$lswref} ? $::HCALinks{$lswref} : "Not Found";
|
|
|
|
if ( (! exists $::NodeInfo{$node}) && $node)
|
|
{
|
|
if ( $node =~ /IBM .*Logical Switch/ )
|
|
{
|
|
$::NodeInfo{$node} = "- <-> $lswn";
|
|
}
|
|
else
|
|
{
|
|
$::NodeInfo{$node} = "- $::GUIDS{$guid}{dev} <-> $lswn\n" .
|
|
"- Connector is $hcaphys\n" .
|
|
"- $swname<-> $swn";
|
|
if ( ($::ALL == 1 || $::IBNODE == 1) && $::ERRLOG == 1 )
|
|
{
|
|
$::NodeInfo{$node} = $::NodeInfo{$node} . "\n$::DASEP\n# $host$elog_entry";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ($::ERRLOG == 1 && $host && $::NODE_LIST ne "" && (grep {$host =~ /$_/ || $_ =~ /$host/} @::NodeList))
|
|
{
|
|
$::NodeInfo{$host} = "$elog_entry";
|
|
}
|
|
}
|
|
|
|
$::SMLOGS{$sm} = "$::SMLOGS{$sm}\n$line" if ($::ALL == 1 || $::SM == 1);
|
|
$::NODELOGS{$node} = "$::NODELOGS{$node}\n$line" if ($::ALL == 1 || $::IBNODE == 1);
|
|
}
|
|
next;
|
|
}
|
|
if ($line =~ /.*/ and ($::ALL == 1))
|
|
{
|
|
$line =~ /(.........:..:..).*/;
|
|
my $tsepoch = epochTime($1) if($1);
|
|
if ( $tsepoch >= $::Start_Epoch && (!defined($::End_Epoch) || ($1 && $tsepoch <= $::End_Epoch)) )
|
|
{
|
|
$::OTHERLOGS{"others"} = "$::OTHERLOGS{\"others\"}\n$line";
|
|
}
|
|
}
|
|
#}
|
|
}
|
|
close LOGFILE;
|
|
return;
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 outputLogs
|
|
Output the results of logs analysis
|
|
Notes:
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub outputLogs
|
|
{
|
|
my $SMLOGHDR = "Logs by Subnet Manager";
|
|
my $SMLOGSECT = "Reported by subnet manager: ";
|
|
my $IBNODEHDR = "Logs by IB node";
|
|
my $IBNODESECT = "Reported by IB node: ";
|
|
my $IBNODEHDRBYNAME = "Logs for special nodes";
|
|
my $CHLOGHDR = "Logs by CHASSIS";
|
|
my $CHLOGSECT = "Reported by chassis: ";
|
|
my $FRULOGHDR = "Logs by FRUS from CHASSIS";
|
|
my $FRULOGSECT = "Associated with FRU: ";
|
|
my $OTLOGHDR = "Logs by Others";
|
|
my $OTLOGSECT = "";
|
|
|
|
print <<EOUT;
|
|
###############################################################################
|
|
Order of log output:
|
|
EOUT
|
|
|
|
my $nout = 1;
|
|
if ( $::SM == 1 or $::ALL == 1)
|
|
{
|
|
print <<SOUT;
|
|
$nout. SM log organized by subnet manager location (Fabric/MS or switch)
|
|
Look for: "$SMLOGHDR" to start the SM logs.
|
|
Each section begins with "$SMLOGSECT".
|
|
SOUT
|
|
|
|
$nout++;
|
|
}
|
|
|
|
if ( $::IBNODE == 1 or $::ALL == 1)
|
|
{
|
|
print <<SOUT;
|
|
$nout. SM log organized by IB node (switch, logical switch, logical HCA, CA)
|
|
Look for: "$IBNODEHDR" to start the IB Node logs.
|
|
Each section begins with "$IBNODESECT".
|
|
SOUT
|
|
|
|
$nout++;
|
|
}
|
|
|
|
if ( $::CHASSIS == 1 or $::ALL == 1)
|
|
{
|
|
print <<SOUT;
|
|
$nout. Chassis logs organized by chassis (good for switch FRU problems; not links)
|
|
Look for: "$CHLOGHDR" to start the Chassis logs.
|
|
Each section begins with "$CHLOGSECT".
|
|
SOUT
|
|
|
|
$nout++;
|
|
}
|
|
|
|
if ( $::FRU == 1 or $::ALL == 1)
|
|
{
|
|
print <<SOUT;
|
|
$nout. Chassis logs organized by FRU in chassis
|
|
Look for: "$FRULOGHDR" to start the FRU based chassis logs.
|
|
Each section begins with "$FRULOGSECT".
|
|
SOUT
|
|
|
|
$nout++;
|
|
}
|
|
|
|
if ( $::ALL == 1)
|
|
{
|
|
print <<SOUT;
|
|
$nout. Other logs
|
|
SOUT
|
|
|
|
$nout++;
|
|
}
|
|
|
|
if ( $::NODE_LIST ne "")
|
|
{
|
|
print <<SOUT;
|
|
$nout. SM log organized by special nodes (switch, logical switch, logical HCA, CA)
|
|
Look for: "$IBNODEHDR" to start the IB Node logs.
|
|
Each section begins with "$IBNODESECT".
|
|
SOUT
|
|
|
|
$nout++;
|
|
}
|
|
|
|
print "###############################################################################";
|
|
|
|
if ( $::SM == 1 or $::ALL == 1)
|
|
{
|
|
outputEntries($SMLOGHDR, $SMLOGSECT, %::SMLOGS);
|
|
}
|
|
if ( $::IBNODE == 1 or $::ALL == 1)
|
|
{
|
|
outputEntries($IBNODEHDR, $IBNODESECT, %::NODELOGS);
|
|
}
|
|
if ($::CHASSIS == 1 or $::ALL == 1)
|
|
{
|
|
outputEntries($CHLOGHDR, $CHLOGSECT, %::CHASSISLOGS);
|
|
}
|
|
if ($::FRU == 1 or $::ALL == 1)
|
|
{
|
|
outputEntries($FRULOGHDR, $FRULOGSECT, %::FRULOGS);
|
|
}
|
|
if ($::ALL == 1)
|
|
{
|
|
outputEntries($OTLOGHDR, $OTLOGSECT, %::OTHERLOGS);
|
|
}
|
|
if ( $::NODE_LIST ne "" )
|
|
{
|
|
outputEntries($IBNODEHDRBYNAME, $IBNODESECT, %::NODELOGSBYNAME);
|
|
}
|
|
return;
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 outputEntries
|
|
Output logs sorted by one method
|
|
Notes:
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub outputEntries
|
|
{
|
|
my ($title, $prefix, %log) = @_;
|
|
print "\n$::SHSEP\n$title\n$::SHSEP\n";
|
|
for my $k ( keys %log )
|
|
{
|
|
print "\n$::DASEP\n$prefix'$k'\n" if ( $prefix ne "");
|
|
if ( exists $::NodeInfo{$k} )
|
|
{
|
|
print "$::NodeInfo{$k}\n";
|
|
}
|
|
print "$::DASEP\n" if ( $prefix ne "");
|
|
print "$log{$k}\n";
|
|
}
|
|
return;
|
|
};
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 epochTime
|
|
Convert a time string to numberic style
|
|
Notes:
|
|
Arguments:
|
|
$timestring - A time string
|
|
Returns:
|
|
$epoch - Time in numberic style
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub epochTime
|
|
{
|
|
my ($timestring) = @_;
|
|
my $ots = $timestring;
|
|
my $epoch = "";
|
|
my $formaterror = 0;
|
|
my %themon = ( "Jan" => 0, "Feb" => 1, "Mar" => 2, "Apr" => 3, "May" => 4,
|
|
"Jun" => 5, "Jul" => 6, "Aug" => 7, "Sep" => 8, "Oct" => 9,
|
|
"Nov" => 10, "Dec" => 11,
|
|
);
|
|
|
|
if ( length($timestring) > 1 )
|
|
{
|
|
#If the time string is not of ddmmyyhh:mm:ss format, it will be a time stamp of a log
|
|
if ( $timestring =~ /\S+\s+\d+\s+\d+:\d+:\d+/ )
|
|
{
|
|
my $mont = substr $timestring, 0, 3;
|
|
my $dayt = substr $timestring, 4, 2;
|
|
my $time = substr $timestring, 7;
|
|
if (exists $themon{$mont})
|
|
{
|
|
$mont = $themon{$mont} + 1;
|
|
}
|
|
my ($psec, $pmin, $phour, $pmday, $pmon, $pyear, $pwday, $pyday, $pisdst) = localtime();
|
|
$pyear = $pyear + 1900;
|
|
my $year = substr $pyear, 2, 2;
|
|
if ($dayt < 10) { $dayt =~ s/ /0/g; }
|
|
if ($mont < 10) { $mont = "0$mont"; }
|
|
#Format the timestring to ddmmyyhh:mm:ss
|
|
$timestring = "$dayt$mont$year$time";
|
|
}
|
|
}
|
|
|
|
if ( ! ($timestring =~ /^\d+:\d+:\d+$/) )
|
|
{
|
|
print "Invalid time '$ots'\n";
|
|
exit $::NOK;
|
|
}
|
|
|
|
my $day = substr $timestring, 0, 2;
|
|
if ( !($day =~ /^\d+$/) || $day < 1 || $day > 31 )
|
|
{
|
|
$formaterror = 1;
|
|
}
|
|
my $mon = substr $timestring, 2, 2;
|
|
if ( !($mon =~ /^\d+$/) || $mon < 1 || $mon > 12 )
|
|
{
|
|
$formaterror = 1;
|
|
}
|
|
$mon = $mon - 1;
|
|
my $year = substr $timestring, 4, 2;
|
|
if ( !($year =~ /^\d+$/) )
|
|
{
|
|
$formaterror = 1;
|
|
}
|
|
$year = $year - 1900;
|
|
if ( $year < 0 ) { $year = $year + 2000 }
|
|
if ( $year > 138 ) { $formaterror = 1; }
|
|
my $hour = substr $timestring, 6, 2;
|
|
if ( !($hour =~ /^\d+$/) || $hour < 0 || $hour > 23 )
|
|
{
|
|
$formaterror = 1;
|
|
}
|
|
my $min = substr $timestring, 9, 2;
|
|
if ( !($min =~ /^\d+$/) || $min < 0 || $min > 59 )
|
|
{
|
|
$formaterror = 1;
|
|
}
|
|
my $sec = substr $timestring, 12, 2;
|
|
if ( !($sec =~ /^\d+$/) || $sec < 0 || $sec > 59 )
|
|
{
|
|
$formaterror = 1;
|
|
}
|
|
if($formaterror)
|
|
{
|
|
print "Invalid time '$ots'\n";
|
|
exit $::NOK;
|
|
}
|
|
$epoch = timegm($sec, $min, $hour, $day, $mon, $year);
|
|
|
|
return ($epoch);
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 usage
|
|
|
|
usage for annotatelog
|
|
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub usage
|
|
{
|
|
|
|
print <<EFMT;
|
|
|
|
Usage: annotatelog -f log_file [-s start_time] [-e end_time]
|
|
{ [-i -g guid_file -l link_file] [-S] [-c] [-u]| [-a -g guid_file -l link_file]}
|
|
{[-n node_list -g guid_file] [-E]}
|
|
[-h]
|
|
|
|
-A Output the combination of -i, -S, -c and -u. It should be used with -g and -l flags.
|
|
-f log_file
|
|
Specifies a log file fullpath name to analyze.
|
|
Must be xCAT consolidated log got from Qlogic HSM or ESM.
|
|
-s start_time
|
|
Specifies the start time for analysis, where the start_time
|
|
variable has the format ddmmyyhh:mm:ss (day, month, year,
|
|
hour, minute, and second), 00:00:00 is valid.
|
|
-e end_time
|
|
Specifies the end time for analysis, where the end_time
|
|
variable has the format ddmmyyhh:mm:ss (day, month, year,
|
|
hour, minute, and second), 00:00:00 is valid.
|
|
-l link_file
|
|
Specifies a link file fullpath name, which concatenates all
|
|
'/var/opt/iba/analysis/baseline/fabric*links' files from all fabric management nodes.
|
|
-g guid_file
|
|
Specifies a guid file fullpath name, which has a list of
|
|
GUIDs as obtained from the "getGuids" script.
|
|
-E Annotate with node ERRLOG_ON and ERRLOG_OFF information. This
|
|
can help determine if a disappearance was caused by a node
|
|
disappearing. It is for AIX nodes only and should be used with -x or -n flag.
|
|
-S Sort the log entries by subnet manager only.
|
|
-i Sort the log entries by IB node only.
|
|
-c Sort the log entries by chassis only.
|
|
-u Sort the log entries by FRU only.
|
|
-n node_list
|
|
Specifies a comma-separated list of node host names, IP addresses to look up in log entries.
|
|
-h Display usage information.
|
|
|
|
EFMT
|
|
|
|
return;
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 getERRLOG
|
|
Use dsh to run errpt command to get the ERRLOG_ON and ERRLOG_OFF information
|
|
Notes:
|
|
Arguments:
|
|
$host - Host that the ERRLOG_ON and ERRLOG_OFF information is get from
|
|
Returns:
|
|
$ret - ERRLOG_ON and ERRLOG_OFF information
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub getERRLOG
|
|
{
|
|
my ($host) = @_;
|
|
print "Getting errpt on $host\n";
|
|
my $rc = checkDshReachability($host);
|
|
if ( $rc != 0 )
|
|
{
|
|
print "Warning: The dsh is unreachable for the node: $host. Run updatenode to configure dsh for that node.\n";
|
|
return "";
|
|
}
|
|
my $cmd = "dsh -w $host 'errpt -J \"ERRLOG_ON ERRLOG_OFF\"'";
|
|
my $out = `$cmd`;
|
|
if ($? != 0)
|
|
{
|
|
print "Errors occured when running command: $cmd. The node $host may be not AIX node.\n";
|
|
return "";
|
|
}
|
|
my $osep = "";
|
|
my $fsep = "";
|
|
my $logon = "";
|
|
my $logoff = "";
|
|
my $ts = "";
|
|
|
|
foreach my $line (split /[\n]/, $out)
|
|
{
|
|
chomp $line;
|
|
if ($line =~/.*TIMESTAMP/) { next;}
|
|
if ($line =~ /.*ON/)
|
|
{
|
|
if ($line =~ /(\S*) *(\S*) *(\w*) .*/)
|
|
{
|
|
my ($junk, $junk, $ts) = ($1, $2, $3);
|
|
$ts = neatTime($ts);
|
|
$logon = "$logon$osep$ts";
|
|
$osep = "; ";
|
|
}
|
|
}
|
|
if ($line =~ /.*OFF/)
|
|
{
|
|
if ($line =~ /(\S*) *(\S*) *(\w*) .*/)
|
|
{
|
|
my ($junk, $junk, $ts) = ($1, $2, $3);
|
|
$ts = neatTime($ts);
|
|
$logoff = "$logoff$fsep$ts";
|
|
$fsep = "; ";
|
|
}
|
|
}
|
|
}
|
|
my $ret = "${logon}_${logoff}";
|
|
return ($ret);
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 neatTime
|
|
Convert the time stamp of ERRLOG_ON and ERRLOG_OFF information into mm/dd/yy hh:mm format
|
|
Notes:
|
|
Arguments:
|
|
$timein - Time stamp of ERRLOG_ON and ERRLOG_OFF information
|
|
Returns:
|
|
$neattime - Formatted time
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub neatTime
|
|
{
|
|
my ($timein) = @_;
|
|
my $m = substr $timein, 0, 2;
|
|
my $d = substr $timein, 2, 2;
|
|
my $h = substr $timein, 4, 2;
|
|
my $mn = substr $timein, 6, 2;
|
|
my $y = substr $timein, 8, 2;
|
|
my $neattime = "$m/$d/$y $h:$mn";
|
|
|
|
return $neattime;
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 getGUIDs
|
|
Get guid maps
|
|
Notes:
|
|
Arguments:
|
|
$guidfile - A guid file fullpath name, which has a list of GUIDs as obtained from the "getGuids" script.
|
|
Returns:
|
|
%guids - A hash table recording guid maps
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub getGUIDs
|
|
{
|
|
my ($guidfile) = @_;
|
|
my %guids;
|
|
unless(open(GUIDSFILE, $guidfile))
|
|
{
|
|
print "Can't open guid file '$guidfile':\n\t$!";
|
|
exit $::NOK;
|
|
}
|
|
|
|
foreach my $line (<GUIDSFILE>)
|
|
{
|
|
chomp $line;
|
|
if ($line =~ /(\S*): (\S*): (\S*): (\w*$)/)
|
|
{
|
|
my ($host, $dev, $type, $guid) = ($1, $2, $3, $4);
|
|
$guids{$guid}{host} = $host;
|
|
$guids{$guid}{dev} = $dev;
|
|
$guids{$guid}{type} = $type;
|
|
}
|
|
}
|
|
close GUIDSFILE;
|
|
return %guids;
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 getHCALinks
|
|
Get links between HCA and Switch and links between Switch and Swtich
|
|
Notes:
|
|
Arguments:
|
|
$hcalfile - A link file fullpath name, which concatenates all '/var/opt/iba/analysis/baseline/fabric*links' files from all fabric management nodes.
|
|
Returns:
|
|
%hcalinks - A hash table recording links
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
sub getHCALinks
|
|
{
|
|
my ($hcalfile) = @_;
|
|
my $getit = 0;
|
|
my $hcaport = "";
|
|
my %hcalinks;
|
|
|
|
unless(open(LINKS, $hcalfile))
|
|
{
|
|
print "Can't open HCA Links file '$hcalfile':\n\t$!";
|
|
exit $::NOK;
|
|
}
|
|
foreach my $line (<LINKS>)
|
|
{
|
|
if ( $getit == 1 )
|
|
{
|
|
if ($line =~ /.*0x(\S*)\s+(\d+)\s+(\w+)\s+(.*)/)
|
|
{
|
|
my ($guid, $port, $type, $name) = ($1, $2, $3, $4);
|
|
$hcalinks{$hcaport} = "$name port $port (GUID 0x$guid)";
|
|
$getit = 0;
|
|
}
|
|
}
|
|
if ($line =~ /g.*IBM.*Logical/)
|
|
{
|
|
if ($line =~ /.*g 0x(\S*) *(\d*) .*/)
|
|
{
|
|
my $port;
|
|
($hcaport, $port) = ($1, $2);
|
|
$hcaport = "$hcaport:$port";
|
|
$getit = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$getit = 0;
|
|
}
|
|
}
|
|
|
|
foreach my $key (keys %::HCALinks)
|
|
{
|
|
print "$key -> $hcalinks{$key}\n";
|
|
}
|
|
close LINKS;
|
|
return %hcalinks;
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 checkDshReachability
|
|
|
|
Notes: Check the dsh reachability between the Management Nodes
|
|
and node.
|
|
|
|
Arguments:
|
|
$node - the remote node hostname.
|
|
|
|
Returns:
|
|
$::OK - The remote node is reachable through dsh.
|
|
$::NOK - The remote node is unreachable through dsh.
|
|
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub checkDshReachability()
|
|
{
|
|
my ($node) = @_;
|
|
my $output = `dsh -Q -n $node date 2>/dev/null`;
|
|
return $?;
|
|
}
|