diff --git a/xCAT-probe/subcmds/clusterstatus b/xCAT-probe/subcmds/clusterstatus index 23b542cd1..bc3836772 100755 --- a/xCAT-probe/subcmds/clusterstatus +++ b/xCAT-probe/subcmds/clusterstatus @@ -14,25 +14,70 @@ use warnings; my $program_name = basename("$0"); my $help; -my $noderange = "compute"; +my $noderange = undef; my $test; my $output = "stdout"; my $verbose = 0; my $rst = 0; +my $interval = 5; +my $terminal = 0; +my $timeout = 0; +my $start_time; + +#----------------------------- +# To store the node number of a specified type +# May like this: +# $type_nodesnum{pdu} = 2; +# {switch} = 3; +# {node} = 10; +#----------------------------- +my %type_nodesnum = (); + +#----------------------------- +# To store the type, state hash +# May like this: +# $state_node_hash{pdu}{state1}{pdu1} = 1; +# $state_node_hash{pdu}{state1}{pdu2} = 1; +# $state_node_hash{pdu}{state2}{pdu3} = 1; +# $state_node_hash{pdu}{state3}{pdu4} = 1; +# The state include but not limited to below: +# matching --> matched --> configured (For PDU/Switch) +# matching --> matched --> installing --> booting --> booted +# matching --> matched --> booting --> installing --> xxx --> booted +# The terminal state is configured(For PDU/Switch) or booted(for Node) +#----------------------------- +my %state_node_hash = (); + +# $state_node_hash{pdu}{state1}{number} = xx; +my %state_node_number = (); + +#---------------------------- +# To store the node based current state +# May like this: +# $node_info{node1}{state} = "install"; +# $node_info{node1}{type} = "node"; +#---------------------------- +my %node_info = (); + +#--------------------------- +# To store nodes who haven't finished the status +#--------------------------- +my %unfinished_nodes = (); +my %unmatched_nodes = (); $::USAGE = "Usage: $program_name -h - $program_name {-d|-g} [-n noderange] [-V|--verbose] + $program_name [-n noderange] [-V|--verbose] [-i|--interval ] [--timeout ] Description: Use this command to get a summary of the cluster. Options: -h : Get usage information of $program_name - -n : Range of nodes to check. Default is \"compute\". - -d : Discovery. Display count of discovered nodes. - -g : Group count. Display count of nodes in each group. + -n : Range of nodes to check. All node objects will be used if not specified. + -i : The interval for screen refreshing, unit is second(s), 5 seconds by default. -V : To print additional debug information. + --timeout: The timout if not all nodes finish, unit is second(s), no timeout by default. "; #------------------------------------- @@ -44,6 +89,8 @@ if ( "V|verbose" => \$VERBOSE, "d|discovery" => \$DISCOVERY, "g|groupcount" => \$GROUPCOUNT, + "i|interval=s" => \$interval, + "timeout=s" => \$timeout, "n=s" => \$noderange)) { probe_utils->send_msg("$output", "f", "Invalid parameter for $program_name"); @@ -65,12 +112,6 @@ if ($test) { exit 0; } -if ((!$DISCOVERY) and (!$GROUPCOUNT)) { - probe_utils->send_msg("$output", "f", "Discovery or groupcount option is required for $program_name"); - probe_utils->send_msg("$output", "d", "$::USAGE"); - exit 1; -} - if (scalar(@ARGV) >= 1) { # After processing all the expected flags and arguments, @@ -79,10 +120,42 @@ if (scalar(@ARGV) >= 1) { probe_utils->send_msg("$output", "d", "$::USAGE"); exit 1; } +if (!$DISCOVERY and !$GROUPCOUNT) { + if(!defined($noderange)) { + $noderange = "all"; + } +} else { + if (!defined($noderange)) { + $noderange = "compute"; + } + check_for_discovered_nodes() if ($DISCOVERY); + groupcount_nodes() if ($GROUPCOUNT); + exit 0; +} -check_for_discovered_nodes() if ($DISCOVERY); -groupcount_nodes() if ($GROUPCOUNT); +$SIG{TERM} = $SIG{INT} = sub { + $terminal = 1; +}; +unless ($interval) { + $interval = 5; +} +&check_nodes_attributes(); +$start_time = time; +while (1) { + update_nodes_info(); + alarm_output(1); + if ($terminal) { + alarm_output(0); + last; + } + if ($timeout and (time() - $start_time > $timeout)) { + alarm_output(0); + last; + } + sleep $interval; +} +exit 0; # Check for node definitions with MAC address defined sub check_for_discovered_nodes { my $na = "N/A"; @@ -132,3 +205,128 @@ sub groupcount_nodes { probe_utils->send_msg("$output", "w", "Group count function is not yet implemented."); return $rc; } + +sub alarm_output { + my $flag = shift; + if ($flag) { + probe_utils->send_msg("$output", "xx", `clear`); + my $time_elapsed = time - $start_time; + probe_utils->send_msg("$output", "xx", "====".localtime()."($time_elapsed seconds Elapsed)"); + } else { + probe_utils->send_msg("$output", "xx", "\nThe cluster state==============================="); + } + foreach my $type (keys(%state_node_hash)) { + unless ($type_nodesnum{$type}) { + probe_utils->send_msg("$output", "w", "$type Total number: $type_nodesnum{$type}"); + next; + } + probe_utils->send_msg("$output", "xx", "$type(Total: $type_nodesnum{$type})--------------------------"); + foreach my $state (keys(%{$state_node_hash{$type}})) { + my $node_number = scalar(keys %{$state_node_hash{$type}{$state}}); + if ($flag) { + my $number = sprintf("%.2f", $node_number * 100.0/ $type_nodesnum{$type}); + probe_utils->send_msg("$output", "xx", "\t$state : $node_number($number%)"); + } else { + probe_utils->send_msg("$output", "xx", "\t$state($node_number): ". join(",",keys %{$state_node_hash{$type}{$state}}) ); + } + } + } +} + +sub update_nodes_info { + if (keys(%unmatched_nodes)) { + my $unmatched_noderange = join(",", keys(%unmatched_nodes)); + my @unmatched_nodes_attributes = `lsdef -i mac -c $unmatched_noderange 2> /dev/null`; + foreach (@unmatched_nodes_attributes) { + if (/^(.*):\s*mac=(.*)$/) { + if ($2) { + update_node_info($1, "Matched"); + } + } + } + } + if (keys(%unfinished_nodes)) { + my $unfinished_noderange = join(",", keys(%unfinished_nodes)); + my @unfinished_nodes_attributes = `lsdef -i status -c $unfinished_noderange 2> /dev/null`; + foreach (@unfinished_nodes_attributes) { + if (/^(.*):\s*status=(.*)$/) { + if ($2) { + update_node_info($1, "$2"); + } + } + } + } + unless(scalar keys(%unfinished_nodes)) { + $terminal = 1; + } +} + +sub update_node_info { + my $node = shift; + my $state = shift; + my $node_type = $node_info{$node}{type}; + my $node_state = $node_info{$node}{state}; + if ($state and $state ne '') { + if (exists($unmatched_nodes{$node})) { + delete($unmatched_nodes{$node}); + $unfinished_nodes{$node} = 1; + } + } + if ($state eq $node_state) { + return; + } + + if (exists($state_node_hash{$node_type}{$node_state}{$node})) { + delete($state_node_hash{$node_type}{$node_state}{$node}); + } + unless (scalar keys ($state_node_hash{$node_type}{$node_state})) { + delete($state_node_hash{$node_type}{$node_state}); + } + + $state_node_hash{$node_type}{$state}{$node} = 1; + + $node_info{$node}{state} = $state; + if ($state eq 'booted' or $state eq 'configured') { + delete $unfinished_nodes{$node}; + } +} +sub check_nodes_attributes { + my @nodes_attributes = `lsdef -i status,mac,mgt -c $noderange 2> /dev/null`; + my %nodehash = (); + foreach (@nodes_attributes) { + if (/^(.*):\s*([^=]*)=(.*)$/) { + $nodehash{$1}{$2}=$3; + } + } + foreach (keys %nodehash) { + if (!defined($nodehash{$_}{mgt})) { + probe_utils->send_msg("$output", "w", "No 'mgt' set for node:$_"); + next; + } + if ($nodehash{$_}{status}) { + $node_info{$_}{state} = $nodehash{$_}{status}; + $unfinished_nodes{$_} = 1; + } elsif ($nodehash{$_}{mac}) { + $node_info{$_}{state} = "Matched"; + $unfinished_nodes{$_} = 1; + } else { + $node_info{$_}{state} = "matching"; + $unmatched_nodes{$_} = 1; + } + if ($nodehash{$_}{mgt} eq 'pdu') { + $node_info{$_}{type} = 'pdu'; + } elsif($nodehash{$_}{mgt} eq 'switch') { + $node_info{$_}{type} = 'switch'; + } else { + $node_info{$_}{type} = 'node'; + } + my $node_type = $node_info{$_}{type}; + if (!exists($type_nodesnum{$node_type})) { + $type_nodesnum{$node_type} = 1; + } else { + $type_nodesnum{$node_type} += 1; + } + $state_node_hash{$node_type}{$node_info{$_}{state}}{$_} = 1; + } +} +exit 0;