From 8dd105afb992debb9b4aabbe1bbcbd9ff1ee9f6b Mon Sep 17 00:00:00 2001 From: hu-weihua Date: Thu, 16 Jun 2016 22:14:48 -0400 Subject: [PATCH] implement discovery basic function --- xCAT-probe/lib/perl/probe_utils.pm | 100 +++- xCAT-probe/subcmds/discovery | 760 +++++++++++++++++++++++++++-- xCAT-probe/xcatprobe | 93 +++- 3 files changed, 897 insertions(+), 56 deletions(-) diff --git a/xCAT-probe/lib/perl/probe_utils.pm b/xCAT-probe/lib/perl/probe_utils.pm index c4ed6ddaf..e0d5b7074 100644 --- a/xCAT-probe/lib/perl/probe_utils.pm +++ b/xCAT-probe/lib/perl/probe_utils.pm @@ -5,7 +5,7 @@ package probe_utils; use strict; use File::Path; use File::Copy; - +use Socket; #----------------------------------------- =head3 @@ -341,4 +341,102 @@ sub is_dns_ready { } } +#------------------------------------------ + +=head3 + Description: + Convert host name to ip address + Arguments: + hostname: The hostname need to convert + Returns: + ip: The ip address +=cut + +#------------------------------------------ +sub get_ip_from_hostname{ + my $hostname = shift; + $hostname=shift if(($hostname) && ($hostname =~ /probe_utils/)); + my $ip = ""; + + my @output = `ping -c 1 $hostname 2>&1`; + if(!$?){ + if($output[0] =~ /^PING.+\s+\((\d+\.\d+\.\d+\.\d+)\).+/){ + $ip=$1; + } + } + return $ip; +} + +#------------------------------------------ + +=head3 + Description: + Calculate network address from ip and netmask + Arguments: + ip: ip address + mask: network mask + Returns: + network : The network address +=cut + +#------------------------------------------ +sub get_network{ + my $ip = shift; + $ip=shift if(($ip) && ($ip =~ /probe_utils/)); + my $mask = shift; + my $net=""; + + return $net if (!is_ip_addr($ip)); + return $net if ($mask !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); + + my $bin_mask = 0; + $bin_mask = (($1 + 0) << 24) + (($2 + 0) << 16) + (($3 + 0) << 8) + ($4 + 0) if ($mask =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); + + my $bin_ip = 0; + $bin_ip = (($1 + 0) << 24) + (($2 + 0) << 16) + (($3 + 0) << 8) + ($4 + 0) if ($ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); + + my $net_int32 = $bin_mask & $bin_ip; + $net = ($net_int32 >> 24) . "." . (($net_int32 >> 16) & 0xff) . "." . (($net_int32 >> 8) & 0xff) . "." . ($net_int32 & 0xff); + return "$net/$mask"; +} + +#------------------------------------------ + +=head3 + Description: + Convert ip to hostname + Arguments: + ip: The ip need to convert + Returns: + hostname: hostname or "" +=cut + +#------------------------------------------ +sub get_hostname_from_ip{ + my $ip = shift; + $ip=shift if(($ip) && ($ip =~ /probe_utils/)); + my $dns_server = shift; + my $hostname=""; + my $output=""; + + `which nslookup > /dev/null 2>&1`; + if(!$?){ + $output = `nslookup $ip $dns_server 2>&1`; + if (!$?) { + chomp($output); + my $rc = $hostname = `echo "$output"|awk -F" " '/name =/ {print \$4}'|awk -F"." '{print \$1}'`; + chomp($hostname); + return $hostname if (!$rc); + } + } + if(($hostname eq "") && (-e "/etc/hosts")){ + $output = `cat /etc/hosts 2>&1 |grep $ip`; + if(!$?){ + my @splitoutput = split(" ", $output); + $hostname = $splitoutput[1]; + } + } + return $hostname; +} + 1; diff --git a/xCAT-probe/subcmds/discovery b/xCAT-probe/subcmds/discovery index be50a3efd..dfae06764 100755 --- a/xCAT-probe/subcmds/discovery +++ b/xCAT-probe/subcmds/discovery @@ -6,38 +6,71 @@ BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/o use lib "$::XCATROOT/probe/lib/perl"; use probe_utils; use File::Basename; +use IO::Select; use Getopt::Long qw(:config no_ignore_case); +use Data::Dumper; my $program_name = basename("$0"); my $help; my $test; -my $output = "stdout"; -my $verbose = 0; -my $rst = 0; +my $output = "stdout"; +my $verbose = 0; +my $rst = 0; +my $pre_check = 0; +my $monitor = 0; +my $discovery_type; +my @valid_discovery_type = ("mtms", "switch"); +my $valid_discovery_type_str = join(",", @valid_discovery_type); +my $noderange; +my $nics; + +#used for discovery monitor +my %rawdata; +my %ipmacmap; +my $terminal = 0; +my %monitor_nodes; $::USAGE = "Usage: $program_name -h $program_name -t - $program_name [-V] - $program_name -s + $program_name -p [-V] [-T ] [-n ] + $program_name -m + $program_name [-V] [-T ] [-n ] Description: - Check discovery files or process. + Do probe for discovery process, including pro-check for required configuration and realtime monitor of discovery process. + If without any option, $program_name will do pro-check first, if all checking point pass, then goes to monitor directly. Options: - -h : Get usage information of $program_name - -t : To verify if $program_name can work, reserve option for probe framework - -V : Output more information for debug - -s : Discovery static check, check whether genesis files are ready + -h : Get usage information of $program_name. + -t : To verify if $program_name can work, reserve option for probe framework. + -V : Output more information for debug. + -p : Do pre-check for disvoery, check whether required configuration have been done ahead. + -T : The type of discovery, the valid values are $valid_discovery_type_str. + -n : The range of predefined node, must used with option -m. + -m : Do realtime monitor for discovery process. "; +#------------------------------------------ + +=head3 + Description: + Check if all genesis files are available. + Arguments: + arch: valid value are ppc64 and x86_64 + Returns: + 0 : pass + 1 : failed +=cut + +#------------------------------------------ sub check_genesis_file { - my $arch = shift; + my $arch = shift; if (($arch ne "ppc64") and ($arch ne "x86_64")) { - probe_utils->send_msg("$output", "f", "Please input correct arch type"); + probe_utils->send_msg("$output", "d", "Please input correct arch type") if ($verbose); return 1; } - + my $rst_f = 0; probe_utils->send_msg("$output", "d", "Start to check genesis files for $arch...") if ($verbose); @@ -229,14 +262,669 @@ sub check_genesis_file { return $rst_f; } +#------------------------------------------ + +=head3 + Description: + 1. check if there are dynamic range for specific networks defineded in dhcp conf file + 2. check if these specific networks have corresponding genesis configuration + Arguments: + networks: A Comma separated list of networks. Every network is combined by network address and mask(i.e. network/mask). + For example: 10.0.0.0/255.0.0.0,50.1.0.0/255.255.0.0 + Returns: + 0 : pass + 1 : failed +=cut + +#------------------------------------------ +sub dhcp_dynamic_range_check { + my $nets = shift; + my $rst = 0; + + return $rst; +} + +#------------------------------------------ + +=head3 + Description: + Handle one line log come from tftp log file + Arguments: + msg: one line tftp log + Returns: + 0 : pass + 1 : failed +=cut + +#------------------------------------------ +sub handle_tftp_msg { + my $msg = shift; + if ($msg =~ /RRQ\s+from\s+(.+)\s+filename\s+(.+)/i) { + my $ip = $1; + my $file = $2; + my $record = "Via TFTP $ip download $file"; + probe_utils->send_msg("$output", "d", "$record"); + if (exists($rawdata{"$ipmacmap{$ip}"})) { + + #push(@{$rawdata{$ipmacmap{$ip}}{"history"}}, $record) unless (/^$record$/ ~~ @{$rawdata{$ipmacmap{$ip}}{"history"}}); + push(@{ $rawdata{ $ipmacmap{$ip} }{"history"} }, $record); + } + } + return 0; +} + +#------------------------------------------ + +=head3 + Description: + Handle one line log come from http log file + Arguments: + msg: one line http log + Returns: + 0 : pass + 1 : failed +=cut + +#------------------------------------------ +sub handle_http_msg { + my $msg = shift; + if ($msg =~ /(\d+\.\d+.\d+.\d+)\s.+GET\s+(.+)\s+HTTP.+/) { + my $ip = $1; + my $file = $2; + my $record = "Via HTTP $ip download $file"; + probe_utils->send_msg("$output", "d", "$record"); + if (exists($rawdata{"$ipmacmap{$ip}"})) { + push(@{ $rawdata{ $ipmacmap{$ip} }{"history"} }, $record); + } + } + return 0; +} + +#------------------------------------------ + +=head3 + Description: + Handle one line log come from dhcp log file + Arguments: + msg: one line http log + nics: target network interfaces + Returns: + 0 : pass + 1 : failed +=cut + +#------------------------------------------ +sub handle_dhcp_msg { + my $msg = shift; + my $nics = shift; + my @nicarray = split(",", $nics); + if ($msg =~ /.+DHCPDISCOVER\s+from\s+(.+)\s+via\s+(.+)/i) { + my @tmpmsg = split(" ", $msg); + my $mac = $tmpmsg[7]; + my $nic = $tmpmsg[9]; + $nic =~ s/(.+):/$1/g if ($nic =~ /:$/); + return 0 unless (@nicarray ~~ /^$nic$/); + if ($msg =~ /no free leases/) { + probe_utils->send_msg("$output", "d", "Receive DHCPDISCOVER from $mac via $nic, no free leases"); + return 0; + } + my $record = "Receive DHCPDISCOVER from $mac via $nic"; + probe_utils->send_msg("$output", "d", "$record"); + if (!exists($rawdata{$mac})) { + my @history; + push(@history, $record); + $rawdata{$mac}{"history"} = \@history; + } else { + push(@{ $rawdata{$mac}{"history"} }, $record); + } + } elsif ($msg =~ /.+DHCPOFFER\s+on\s+(.+)\s+to\s+(.+)\s+via\s+(.+)/i) { + my $ip = $1; + my $mac = $2; + my $nic = $3; + return 0 unless (@nicarray ~~ /^$nic$/); + my $record = "Send DHCPOFFER on $ip back to $mac via $nic"; + probe_utils->send_msg("$output", "d", "$record"); + if (exists($rawdata{$mac})) { + push(@{ $rawdata{$mac}{"history"} }, $record); + } + } elsif ($msg !~ /unknown lease/ && $msg !~ /ignored/ && $msg =~ /.+DHCPREQUEST\s+for\s+(.+)\s\((.+)\)\s+from\s+(.+)\s+via\s+(.+)/) { + my $ip = $1; + my $server = $2; + my $mac = $3; + my $nic = $4; + return 0 unless (@nicarray ~~ /^$nic$/); + my $record = "Receive DHCPREQUEST from $mac for $ip via $nic"; + probe_utils->send_msg("$output", "d", "$record"); + if (exists($rawdata{$mac})) { + push(@{ $rawdata{$mac}{"history"} }, $record); + } + } elsif ($msg =~ /.+DHCPACK\s+on\s+(.+)\s+to\s+(.+)\s+via\s+(.+)/) { + my $ip = $1; + my $mac = $2; + my $nic = $3; + return 0 unless (@nicarray ~~ /^$nic$/); + my $record = "Send DHCPACK on $ip back to $mac via $nic"; + probe_utils->send_msg("$output", "d", "$record"); + if (exists($rawdata{$mac})) { + $rawdata{$mac}{"ip"} = $ip; + push(@{ $rawdata{$mac}{"history"} }, $record); + $ipmacmap{$ip} = $mac; + } + } elsif ($msg =~ /.+BOOTREQUEST\s+from\s+(.+)\s+via\s+(.+)/) { + my @tmpmsg = split(" ", $msg); + my $mac = $tmpmsg[7]; + my $nic = $tmpmsg[9]; + $nic =~ s/(.+):/$1/g if ($nic =~ /:$/); + return 0 unless (@nicarray ~~ /^$nic$/); + if ($msg =~ /no dynamic leases/) { + probe_utils->send_msg("$output", "d", "Receive DHCPDISCOVER from $mac via $nic, no dynamic leases"); + return 0; + } + my $record = "Receive BOOTREQUEST from $mac via $nic"; + probe_utils->send_msg("$output", "d", "$record"); + if (!exists($rawdata{$mac})) { + my @history; + push(@history, $record); + $rawdata{$mac}{"history"} = \@history; + } else { + push(@{ $rawdata{$mac}{"history"} }, $record); + } + } elsif ($msg =~ /.+BOOTREPLY\s+for\s+(.+)\s+to\s+.+(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w).+via\s+(.+)/) { + my $ip = $1; + my $mac = $2; + my $nic = $3; + return 0 unless (@nicarray ~~ /^$nic$/); + my $record = "Send BOOTREPLY on $ip back to $mac via $nic"; + probe_utils->send_msg("$output", "d", "$record"); + if (exists($rawdata{$mac})) { + $rawdata{$mac}{"ip"} = $ip; + push(@{ $rawdata{$mac}{"history"} }, $record); + $ipmacmap{$ip} = $mac; + } + } + return 0; +} + +#------------------------------------------ + +=head3 + Description: + Handle one line log come from computes.log + Arguments: + msg: one line compute log + Returns: + 0 : pass + 1 : failed +=cut + +#------------------------------------------ +sub handle_compute_msg { + + my $line = shift; + my $sender = ""; + my $ip = ""; + my $msg; + + my @splitline = split(" ", $line); + if (($splitline[4] =~ /^xcat/i) || ($splitline[5] =~ /^xcat/i)) { + $sender = $splitline[3]; + for (my $i = 0 ; $i < 5 ; $i++) { + shift(@splitline); + } + $msg = join(" ", @splitline); + if (!probe_utils->is_ip_addr("$sender")) { + $ip = probe_utils->get_ip_from_hostname("$sender"); + } else { + $ip = $sender; + } + if ($ip ne "" && exists($ipmacmap{$ip})) { + my $record = "Recv from $ipmacmap{$ip}($ip) : $msg"; + probe_utils->send_msg("$output", "d", "$record"); + push(@{ $rawdata{ $ipmacmap{$ip} }{"history"} }, $record); + } + } + return 0; +} + +#------------------------------------------ + +=head3 + Description: + Handle one line log come from cluster.log + Arguments: + msg: one line log + Returns: + 0 : pass + 1 : failed +=cut + +#------------------------------------------ +sub handle_cluster_msg { + my $line = shift; + my $sender = ""; + my $ip = ""; + my $msg; + + if ($line =~ /.+\d+:\d+:\d+\s+(.+)\s+(xcat.+)/i) { + $sender = $1; + $msg = $2; + if (!probe_utils->is_ip_addr("$sender")) { + $ip = probe_utils->get_ip_from_hostname("$sender"); + } else { + $ip = $sender; + } + if ($ip ne "" && exists($ipmacmap{$ip})) { + my $record = "Recv from $ipmacmap{$ip}($ip) : $msg"; + probe_utils->send_msg("$output", "d", "$record"); + push(@{ $rawdata{ $ipmacmap{$ip} }{"history"} }, $record); + } + } + return 0; +} + +#------------------------------------------ + +=head3 + Description: + Dump monitor history, categorised by mac address. + Arguments: + NULL + Returns: +=cut + +#------------------------------------------ +sub dump_history { + probe_utils->send_msg("$output", "d", "======================================="); + probe_utils->send_msg("$output", "d", "= The summary of discovery:"); + probe_utils->send_msg("$output", "d", "======================================="); + + my $masterip = `tabdump site 2>&1 | awk -F',' '/master/ { gsub(/"/, "", \$2) ; print \$2 }'`; + chomp($masterip); + foreach $mac (keys %rawdata) { + my $nodehostname = probe_utils->get_hostname_from_ip($rawdata{$mac}{"ip"}, $masterip); + if ($nodehostname ne "") { + probe_utils->send_msg("$output", "d", "[$mac ($nodehostname)]"); + } else { + probe_utils->send_msg("$output", "d", "[$mac]:"); + } + foreach my $line (@{ $rawdata{$mac}{"history"} }) { + probe_utils->send_msg("$output", "d", "| $line"); + } + } +} + +#------------------------------------------ + +=head3 + Description: + Check if all predefined node are valid + Arguments: + discovery_type: valid value are mtms and switch + noderange: node range + Returns: + 0: pass + 1: failed +=cut + +#------------------------------------------ +sub check_pre_defined_node { + my $discovery_type = shift; + my $noderange = shift; + + my $rst = 0; + my @cmdoutput; + my %nodecheckrst; + my $currentnode = ""; + + @cmdoutput = `lsdef $noderange 2>&1`; + foreach (@cmdoutput) { + if ($_ =~ /^Error: Could not find an object named '(\w+)' .+/i) { + $currentnode = $1; + $nodecheckrst{$currentnode}{"error"} = "Could not find node definition"; + $rst = 1; + } elsif ($_ =~ /^\s*Object name: (\w+)/i) { + $currentnode = $1; + } elsif ($_ =~ /^\s+(\w+)\s*=\s*(\w+)/) { + $nodecheckrst{$currentnode}{$1} = $2; + } + } + + #print Dumper(%nodecheckrst); + + foreach my $node (keys %nodecheckrst) { + if (!exists($nodecheckrst{$node}{error})) { + if ($discovery_type eq "mtms") { + if (exists($nodecheckrst{$node}{"mtm"}) && exists($nodecheckrst{$node}{"serial"})) { + $nodecheckrst{$node}{"error"} = "node definition is good for '$discovery_type' type discovery"; + } else { + $nodecheckrst{$node}{"error"} = "node definition is wrong for '$discovery_type' type discovery"; + $rst = 1; + } + } + + if ($discovery_type eq "switch") { + if (exists($nodecheckrst{$node}{"switch"}) && exists($nodecheckrst{$node}{"switchport"})) { + $nodecheckrst{$node}{"error"} = "node definition is good for '$discovery_type' type discovery"; + } else { + $nodecheckrst{$node}{"error"} = "node definition is wrong for '$discovery_type' type discovery"; + $rst = 1; + } + } + } + } + + if ($verbose) { + foreach my $node (keys %nodecheckrst) { + probe_utils->send_msg("$output", "d", "$node : $nodecheckrst{$node}{error}"); + } + } + + return $rst; +} + +#------------------------------------------ + +=head3 + Description: + Get monitor nodes list + Arguments: + One golble attribute %monitor_nodes; + Returns: +=cut + +#------------------------------------------ +sub get_monitor_nodes_list { + my @cmdoutput = `lsdef $noderange 2>&1`; + foreach (@cmdoutput) { + if ($_ =~ /^Error: Could not find an object named '(\w+)' .+/i) { + $monitor_nodes{$1} = 0; + } elsif ($_ =~ /^\s*Object name: (\w+)/i) { + $monitor_nodes{$1} = 0; + } + } +} + +#------------------------------------------ + +=head3 + Description: + Test if all nodes have finished job + Arguments: + One golble attribute %monitor_nodes; + Returns: +=cut + +#------------------------------------------ +sub all_monitor_node_done { + my $done = 1; + foreach my $node (keys %monitor_nodes) { + $done = 0 if ($monitor_nodes{$node} == 0); + } + return $done; +} + +#------------------------------------------ + +=head3 + Description: + Do pre_checking + Arguments: + nics: target network interface + if not specified, using the network which master belongs to + Returns: + 0: pass + 1: failed +=cut + +#------------------------------------------ +sub do_pre_check { + my $nics = shift; + my @nets = (); + my $rst = 0; + my $msg; + + if (defined($noderange) && defined($discovery_type)) { + $msg = "All pre_defined nodes are valid"; + my $rc = check_pre_defined_node($discovery_type, $noderange); + if ($rc) { + probe_utils->send_msg("$output", "f", $msg); + $rst = 1; + } else { + probe_utils->send_msg("$output", "o", $msg); + } + } + + + if ($nics) { + if ($nics =~ /[^\w,]/) { + probe_utils->send_msg("$output", "f", "Invalid NIC list"); + probe_utils->send_msg("$output", "d", "$::USAGE"); + exit 1; + } + + $msg = "The input network interfaces $nics exist and are configured well on current server"; + my $miss = 0; + my @nic_array = split(",", $nics); + foreach my $nic (@nic_array) { + my $tmp_nic = `ip addr show $nic >/dev/null 2>&1`; + if ($?) { + probe_utils->send_msg("$output", "d", "Network interface $nic doesn't exist on current server") if ($verbose); + $miss = 1; + } else { + my $tmp = `ifconfig $nic|awk -F" " '/inet / {print \$2,\$4}'`; + chomp($tmp); + if (!$tmp) { + probe_utils->send_msg("$output", "d", "Network interface $nic isn't set IP address") if ($verbose); + $miss = 1; + } else { + my ($ip, $mask) = split(" ", $tmp); + push(@nets, probe_utils->get_network($ip, $mask)); + } + } + } + if ($miss) { + probe_utils->send_msg("$output", "f", $msg); + $rst = 1; + } else { + probe_utils->send_msg("$output", "o", $msg); + } + } else { + $msg = "Attribute 'master' in 'site' table is configured well"; + my $masteripinsite = `tabdump site | awk -F',' '/master/ { gsub(/"/, "", \$2) ; print \$2 }'`; + chomp($masteripinsite); + if ($masteripinsite eq "") { + probe_utils->send_msg("$output", "d", "There isn't 'master' definition in 'site' talbe") if ($verbose); + probe_utils->send_msg("$output", "f", $msg); + exit 1; + } + if (!probe_utils->is_ip_addr("$masteripinsite")) { + probe_utils->send_msg("$output", "d", "The value of 'master' in 'site' table isn't a IP address") if ($verbose); + probe_utils->send_msg("$output", "f", $msg); + exit 1; + } + `ifconfig -a 2>&1 |grep $masteripinsite`; + if ($?) { + probe_utils->send_msg("$output", "d", "The IP $masteripinsite of 'master' in 'site' table dosen't belong to any network on current server") if ($verbose); + probe_utils->send_msg("$output", "f", $msg); + exit 1; + } + probe_utils->send_msg("$output", "o", $msg); + my $tmp = `ifconfig -a|awk -F" " '/$masteripinsite/ {print \$2,\$4}'`; + chomp($tmp); + my ($ip, $mask) = split(" ", $tmp); + push(@nets, probe_utils->get_network($ip, $mask)); + } + + my $arch = `uname -i`; + chomp($arch); + $msg = "Genesis files for $arch are available"; + if ($arch =~ /ppc64/i) { + if (check_genesis_file("ppc64")) { + probe_utils->send_msg("$output", "f", $msg); + $rst = 1; + } else { + probe_utils->send_msg("$output", "o", $msg); + if (check_genesis_file("x86_64")) { + probe_utils->send_msg("$output", "w", "Genesis files for x86 are not available"); + probe_utils->send_msg("$output", "i", "If don't plan to manage a x86 server, please ignore above warning"); + } else { + probe_utils->send_msg("$output", "o", "Genesis files for x86 are available"); + } + } + } elsif ($arch =~ /x86/i) { + if (check_genesis_file("x86_64")) { + probe_utils->send_msg("$output", "f", $msg); + $rst = 1; + } else { + probe_utils->send_msg("$output", "o", $msg); + if (check_genesis_file("ppc64")) { + probe_utils->send_msg("$output", "w", "Genesis files for ppc64/ppc64le are not available"); + probe_utils->send_msg("$output", "i", "If don't plan to manage a ppc64/ppc64le server, please ignore above warning"); + } else { + probe_utils->send_msg("$output", "o", "Genesis files for ppc64/ppc64le are available"); + } + } + } + + $msg = "DHCP dynamic range is configured well"; + if (dhcp_dynamic_range_check(\@nets)) { + probe_utils->send_msg("$output", "f", $msg); + $rst = 1; + } else { + probe_utils->send_msg("$output", "o", $msg); + } + + return $rst; +} + +#------------------------------------------ + +=head3 + Description: + Monitor the process of discovery + Arguments: + nics: target network interface + if not specified, using the network which master belongs to + Returns: + 0: pass + 1: failed +=cut + +#------------------------------------------ +sub do_monitor { + $SIG{TERM} = $SIG{INT} = sub { + $terminal = 1; + }; + + if ($monitor && !$pre_check && defined($noderange) && defined($discovery_type)) { + $msg = "All pre_defined nodes are valid"; + my $rc = check_pre_defined_node($discovery_type, $noderange); + if ($rc) { + probe_utils->send_msg("$output", "f", $msg); + exit 1; + } else { + probe_utils->send_msg("$output", "o", $msg); + } + } + + &get_monitor_nodes_list if (defined($noderange) && defined($discovery_type)); + + if (!$nics) { + my $masteripinsite = `tabdump site | awk -F',' '/master/ { gsub(/"/, "", \$2) ; print \$2 }'`; + chomp($masteripinsite); + $nics = `ip addr |grep -B2 $masteripinsite|awk -F" " '/mtu/{gsub(/:/,"",\$2); print \$2}'`; + chomp($nics); + if (!$nics) { + probe_utils->send_msg("$output", "f", "The value of master in site table is $masteripinsite, can't get corresponding network interface"); + exit 1; + } + } + + probe_utils->send_msg("$output", "d", "Start to capture every message during discovery process......"); + + my $varlogmsg = "/var/log/messages"; + my $clusterlog = "/var/log/xcat/cluster.log"; + my $computelog = "/var/log/xcat/computes.log"; + + my $httplog; + my $os = probe_utils->get_os(); + if ($os eq "redhat") { + $httplog = "/var/log/httpd/access_log"; + } elsif ($os eq "sles") { + $httplog = "/var/log/apache2access_log"; + } elsif ($os eq "ubuntu") { + $httplog = "/var/log/apache2/access.log"; + } + + open(VARLOGMSGFILE, "tail -f $varlogmsg 2>&1 |"); + open(CLUSTERLOGFILE, "tail -f $clusterlog 2>&1 |"); + open(HTTPLOGFILE, "tail -f $httplog 2>&1 |"); + open(COMPUTERFILE, "tail -f $computelog 2>&1 |"); + + my $select = new IO::Select; + $select->add(\*VARLOGMSGFILE); + $select->add(\*CLUSTERLOGFILE); + $select->add(\*HTTPLOGFILE); + $select->add(\*COMPUTERFILE); + $| = 1; + + my $line = ""; + my @hdls; + my $hdl; + for (; ;) { + if (@hdls = $select->can_read(0)) { + foreach $hdl (@hdls) { + if ($hdl == \*VARLOGMSGFILE) { + chomp($line = ); + my @tmp = split(/ /, $line); + if ($tmp[4] =~ /dhcpd:/i) { + handle_dhcp_msg("$line", $nics); + } elsif ($tmp[4] =~ /in.tftpd/i) { + handle_tftp_msg("$line"); + } + } elsif ($hdl == \*CLUSTERLOGFILE) { + chomp($line = ); + handle_cluster_msg("$line"); + } elsif ($hdl == \*HTTPLOGFILE) { + chomp($line = ); + handle_http_msg("$line"); + } elsif ($hdl == \*COMPUTERFILE) { + chomp($line = ); + handle_compute_msg("$line"); + } + } + } + + if ($terminal || (%monitor_nodes && all_monitor_node_done())) { + &dump_history; + close(VARLOGMSGFILE); + close(CLUSTERLOGFILE); + close(HTTPLOGFILE); + close(COMPUTERFILE); + exit 0; + } + sleep 0.01; + } + + close(VARLOGMSGFILE); + close(CLUSTERLOGFILE); + close(HTTPLOGFILE); + close(COMPUTERFILE); + return 0; +} + #------------------------------------- # main process #------------------------------------- if ( !GetOptions("--help|h|?" => \$help, - "t" => \$test, - "V" => \$verbose, - "s" => \$static)) + "t" => \$test, + "V" => \$verbose, + "m" => \$monitor, + "p" => \$pre_check, + "T=s" => \$discovery_type, + "n=s" => \$noderange, + "N=s" => \$nics)) { probe_utils->send_msg("$output", "f", "Invalid parameter for $program_name"); probe_utils->send_msg("$output", "d", "$::USAGE"); @@ -253,23 +941,37 @@ if ($help) { } if ($test) { - probe_utils->send_msg("$output", "o", "Discovery Check."); + probe_utils->send_msg("$output", "o", "Do probe for discovery process, including pro-check for required configuration and realtime monitor of discovery process.Before using this command, please install nslookup command ahead."); exit 0; } -if ($static) { - $rst = check_genesis_file("ppc64"); - if ($rst) { - probe_utils->send_msg("$output", "f", "Genesis files for ppc64/ppc64le failed."); - } else { - probe_utils->send_msg("$output", "o", "Genesis files for ppc64/ppc64le success."); - } - $rst = check_genesis_file("x86_64"); - if ($rst) { - probe_utils->send_msg("$output", "f", "Genesis files for x86_64 failed."); - } else { - probe_utils->send_msg("$output", "o", "Genesis files for x86_64 success."); +if (defined($noderange) && !defined($discovery_type)) { + probe_utils->send_msg("$output", "f", "Option '-n' must used with '-m'"); + probe_utils->send_msg("$output", "d", "$::USAGE"); + exit 1; +} + +if (defined($discovery_type)) { + unless (@valid_discovery_type ~~ /^$discovery_type$/) { + probe_utils->send_msg("$output", "f", "Invalid discovery type. the vaild types are $valid_discovery_type_str"); + probe_utils->send_msg("$output", "d", "$::USAGE"); + exit 1; } } +if ($pre_check) { + exit 1 if (do_pre_check($nics)); +} + +if ($monitor) { + $rst = do_monitor(); +} + +if (!$monitor && !$pre_check) { + $rst = do_pre_check(); + exit 1 if ($rst); + + $rst = do_monitor(); +} + exit $rst; diff --git a/xCAT-probe/xcatprobe b/xCAT-probe/xcatprobe index 99b3c4b62..c911c23a3 100755 --- a/xCAT-probe/xcatprobe +++ b/xCAT-probe/xcatprobe @@ -84,34 +84,54 @@ sub loadsubcmds { sub format_cmd_output { my $line = shift; my $nocolor = shift; - if ($line =~ /\[(\w+)\]\s*:\s*(.+)/) { - my $flag = $1; - my $msg = $2; - if ($flag =~ /failed/i) { - if ($nocolor) { - print "[FAIL] "; - } else { - print BOLD RED "[FAIL] "; + my $flag = $1; + my $msg = $2; + my @tmpmsg = split(" ", $msg); + my $maxlen = 80; + my @finalmsg = (); + my $str = ""; + foreach my $word (@tmpmsg) { + $str .= $word . " "; + if (length($str) > $maxlen) { + push @finalmsg, $str; + $str = ""; + } + } + push @finalmsg, $str if ($str ne ""); + + for (my $i = 0 ; $i < $#finalmsg + 1 ; $i++) { + if ($i ne $#finalmsg) { + print "$finalmsg[$i]\n"; + next; + } + my $space = " " x ($maxlen - length($finalmsg[$i]) + 5); + print "$finalmsg[$i]$space"; + + if ($flag =~ /failed/i) { + if ($nocolor) { + print "[FAIL]\n"; + } else { + print BOLD RED "[FAIL]\n"; + } + } elsif ($flag =~ /warning/i) { + if ($nocolor) { + print "[WARN]\n"; + } else { + print BOLD BLUE "[WARN]\n"; + } + } elsif ($flag =~ /ok/i) { + if ($nocolor) { + print "[ OK ]\n"; + } else { + print BOLD GREEN "[ OK ]\n"; + } + } elsif ($flag =~ /debug/i) { + print "\n"; + } elsif ($flag =~ /info/i) { + print "[INFO]\n"; } - } elsif ($flag =~ /warning/i) { - if ($nocolor) { - print "[WARN] "; - } else { - print BOLD BLUE "[WARN] "; - } - } elsif ($flag =~ /ok/i) { - if ($nocolor) { - print "[ OK ] "; - } else { - print BOLD GREEN "[ OK ] "; - } - } elsif ($flag =~ /debug/i) { - print " "; - } elsif ($flag =~ /info/i) { - print "[INFO] "; } - print "$msg\n"; } else { print "$line\n"; } @@ -210,10 +230,11 @@ if ($ARGV[0] eq "-l") { } if (!defined($pluginname)) { - print "There isn't sub command input from command line\n"; + print "There isn't sub command input from command line, please use '-l' to list all valid subcommand\n"; exit 0; } + for (my $i = 0 ; $i < $optnum + 1 ; $i++) { shift @tmpargv; } @@ -223,9 +244,29 @@ my $subcmd = "$plugin_dir/$pluginname $pluginattrs"; print "\nsubcmd = $subcmd\n" if ($verbose); open(PIPE, "$subcmd |"); + +my $terminal = 0; +my $subcmdpid = 0; +my @tmpoutput = `ps axjf |grep -v grep|grep -v ps|grep -v bash|grep $$`; +foreach (@tmpoutput) { + @psline = split(" ", $_); $subcmdpid = $psline[1] if (($psline[0] eq $$) && ($psline[1] ne $$) && ($psline[2] eq $$)); +} +$SIG{TERM} = $SIG{INT} = sub { + $terminal = 1; +}; + while () { chomp; format_cmd_output($_, $nocolor); + if ($terminal && $subcmdpid) { + kill 'INT', $subcmdpid; + while () { + chomp; + format_cmd_output($_, $nocolor); + } + close(PIPE); + exit $?; + } } close(PIPE); # This will set the $? properly