code drop for coordinated cluster bringup and shutdown
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@6933 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
parent
677a70a760
commit
a7e56eb7f7
@ -1295,6 +1295,7 @@ sub preprocess_request {
|
||||
#if ($req->{_xcatdest}) { return [$req]; } #exit if preprocessed
|
||||
if ($req->{_xcatpreprocessed}->[0] == 1 ) { return [$req]; }
|
||||
my $callback = shift;
|
||||
my $subreq = shift;
|
||||
my @requests;
|
||||
|
||||
#####################################
|
||||
@ -1319,98 +1320,201 @@ sub preprocess_request {
|
||||
####################################
|
||||
$package =~ s/xCAT_plugin:://;
|
||||
|
||||
####################################
|
||||
# Prompt for usage if needed and on MN
|
||||
####################################
|
||||
my $noderange = $req->{node}; #Should be arrayref
|
||||
my $deps;
|
||||
my $nodeseq;
|
||||
if (($req->{command}->[0] eq 'rpower') && (!grep(/^--nodeps$/, @{$req->{arg}}))
|
||||
&& (($req->{op}->[0] eq 'on') || ($req->{op}->[0] eq 'off')
|
||||
|| ($req->{op}->[0] eq 'softoff') || ($req->{op}->[0] eq 'reset'))) {
|
||||
|
||||
$deps = xCAT::SvrUtils->build_deps($req->{node}, $req->{op}->[0]);
|
||||
|
||||
# no dependencies at all
|
||||
if (!defined($deps)) {
|
||||
foreach my $node (@{$req->{node}}) {
|
||||
$nodeseq->[0]->{$node} = 1;
|
||||
}
|
||||
} else {
|
||||
$nodeseq = xCAT::SvrUtils->handle_deps($deps, $req->{node}, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
if ($nodeseq == 1) {
|
||||
return undef;
|
||||
}
|
||||
# no dependency defined in deps table,
|
||||
# generate the $nodeseq hash
|
||||
if (!$nodeseq) {
|
||||
foreach my $node (@{$req->{node}}) {
|
||||
$nodeseq->[0]->{$node} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my $i = 0;
|
||||
for ($i = 0; $i < scalar(@{$nodeseq}); $i++) {
|
||||
#reset the @requests for this loop
|
||||
@requests = ();
|
||||
####################################
|
||||
# Prompt for usage if needed and on MN
|
||||
####################################
|
||||
my @dnodes = keys(%{$nodeseq->[$i]});
|
||||
|
||||
if (scalar(@dnodes) == 0) {
|
||||
next;
|
||||
}
|
||||
if (scalar(@{$nodeseq}) > 1) {
|
||||
my %output;
|
||||
my $cnodes = join(',', @dnodes);
|
||||
$output{data} = ["Performing action against the following nodes: $cnodes"];
|
||||
$callback->( \%output );
|
||||
}
|
||||
my $noderange = \@dnodes;
|
||||
$req->{node} = \@dnodes; #Should be arrayref
|
||||
#$req->{noderange} = \@dnodes; #Should be arrayref
|
||||
my $command = $req->{command}->[0];
|
||||
my $extrargs = $req->{arg};
|
||||
my @exargs=($req->{arg});
|
||||
if (ref($extrargs)) {
|
||||
@exargs=@$extrargs;
|
||||
}
|
||||
if ($ENV{'XCATBYPASS'}){
|
||||
my $usage_string=xCAT::Usage->parseCommand($command, @exargs);
|
||||
if ($usage_string) {
|
||||
$callback->({data=>[$usage_string]});
|
||||
$req = {};
|
||||
return ;
|
||||
my $extrargs = $req->{arg};
|
||||
my @exargs=($req->{arg});
|
||||
if (ref($extrargs)) {
|
||||
@exargs=@$extrargs;
|
||||
}
|
||||
if (!$noderange) {
|
||||
$usage_string="Missing noderange";
|
||||
$callback->({data=>[$usage_string]});
|
||||
$req = {};
|
||||
return ;
|
||||
if ($ENV{'XCATBYPASS'}){
|
||||
my $usage_string=xCAT::Usage->parseCommand($command, @exargs);
|
||||
if ($usage_string) {
|
||||
$callback->({data=>[$usage_string]});
|
||||
$req = {};
|
||||
return ;
|
||||
}
|
||||
if (!$noderange) {
|
||||
$usage_string="Missing noderange";
|
||||
$callback->({data=>[$usage_string]});
|
||||
$req = {};
|
||||
return ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
##################################################################
|
||||
# get the HCPs for the LPARs in order to figure out which service
|
||||
# nodes to send the requests to
|
||||
###################################################################
|
||||
my $hcptab_name = ($package eq "fsp" or $package eq "bpa") ? "ppcdirect" : "ppchcp";
|
||||
my $hcptab = xCAT::Table->new( $hcptab_name );
|
||||
unless ($hcptab ) {
|
||||
$callback->({data=>["Cannot open $hcptab_name table"]});
|
||||
$req = {};
|
||||
return;
|
||||
}
|
||||
# Check if each node is hcp
|
||||
my %hcp_hash=();
|
||||
my @missednodes=();
|
||||
foreach ( @$noderange ) {
|
||||
my ($ent) = $hcptab->getAttribs( {hcp=>$_},"hcp" );
|
||||
if ( !defined( $ent )) {
|
||||
push @missednodes, $_;
|
||||
next;
|
||||
}
|
||||
push @{$hcp_hash{$_}{nodes}}, $_;
|
||||
}
|
||||
|
||||
#check if the left-over nodes are lpars
|
||||
if (@missednodes > 0) {
|
||||
my $ppctab = xCAT::Table->new("ppc");
|
||||
unless ($ppctab) {
|
||||
$callback->({data=>["Cannot open ppc table"]});
|
||||
|
||||
|
||||
##################################################################
|
||||
# get the HCPs for the LPARs in order to figure out which service
|
||||
# nodes to send the requests to
|
||||
###################################################################
|
||||
my $hcptab_name = ($package eq "fsp" or $package eq "bpa") ? "ppcdirect" : "ppchcp";
|
||||
my $hcptab = xCAT::Table->new( $hcptab_name );
|
||||
unless ($hcptab ) {
|
||||
$callback->({data=>["Cannot open $hcptab_name table"]});
|
||||
$req = {};
|
||||
return;
|
||||
}
|
||||
foreach my $node (@missednodes) {
|
||||
my $ent=$ppctab->getNodeAttribs($node,['hcp']);
|
||||
if (defined($ent->{hcp})) { push @{$hcp_hash{$ent->{hcp}}{nodes}}, $node;}
|
||||
else {
|
||||
$callback->({data=>["The node $node is neither a hcp nor an lpar"]});
|
||||
# Check if each node is hcp
|
||||
my %hcp_hash=();
|
||||
my @missednodes=();
|
||||
foreach ( @$noderange ) {
|
||||
my ($ent) = $hcptab->getAttribs( {hcp=>$_},"hcp" );
|
||||
if ( !defined( $ent )) {
|
||||
push @missednodes, $_;
|
||||
next;
|
||||
}
|
||||
push @{$hcp_hash{$_}{nodes}}, $_;
|
||||
}
|
||||
|
||||
#check if the left-over nodes are lpars
|
||||
if (@missednodes > 0) {
|
||||
my $ppctab = xCAT::Table->new("ppc");
|
||||
unless ($ppctab) {
|
||||
$callback->({data=>["Cannot open ppc table"]});
|
||||
$req = {};
|
||||
return;
|
||||
}
|
||||
foreach my $node (@missednodes) {
|
||||
my $ent=$ppctab->getNodeAttribs($node,['hcp']);
|
||||
if (defined($ent->{hcp})) { push @{$hcp_hash{$ent->{hcp}}{nodes}}, $node;}
|
||||
else {
|
||||
$callback->({data=>["The node $node is neither a hcp nor an lpar"]});
|
||||
$req = {};
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# find service nodes for the HCPs
|
||||
# build an individual request for each service node
|
||||
my $service = "xcat";
|
||||
my @hcps=keys(%hcp_hash);
|
||||
my $sn = xCAT::Utils->get_ServiceNode(\@hcps, $service, "MN");
|
||||
|
||||
# build each request for each service node
|
||||
foreach my $snkey (keys %$sn)
|
||||
{
|
||||
#$callback->({data=>["The service node $snkey "]});
|
||||
my $reqcopy = {%$req};
|
||||
$reqcopy->{'_xcatdest'} = $snkey;
|
||||
$reqcopy->{_xcatpreprocessed}->[0] = 1;
|
||||
my $hcps1=$sn->{$snkey};
|
||||
my @nodes=();
|
||||
foreach (@$hcps1) {
|
||||
push @nodes, @{$hcp_hash{$_}{nodes}};
|
||||
}
|
||||
@nodes = sort @nodes;
|
||||
$reqcopy->{node} = \@nodes;
|
||||
#print "nodes=@nodes\n";
|
||||
push @requests, $reqcopy;
|
||||
}
|
||||
|
||||
# No dependency, use the original logic
|
||||
if (scalar(@{$nodeseq}) == 1) {
|
||||
return \@requests;
|
||||
}
|
||||
|
||||
# do all the new request entries in this loop
|
||||
my $j = 0;
|
||||
for ($j = 0; $j < scalar(@requests); $j++) {
|
||||
$subreq->(\%{$requests[$j]}, $callback);
|
||||
}
|
||||
|
||||
# We can not afford waiting 'msdelay' for each node,
|
||||
# for performance considerations,
|
||||
# use the maximum msdelay for all nodes
|
||||
my $delay = 0;
|
||||
# do not need to calculate msdelay for the last loop
|
||||
if ($i < scalar(@{$nodeseq})) {
|
||||
foreach my $reqnode (@{$req->{node}}) {
|
||||
foreach my $depnode (keys %{$deps}) {
|
||||
foreach my $depent (@{$deps->{$depnode}}) {
|
||||
# search if the 'nodedep' includes the $reqnode
|
||||
# do not use grep, performance problem!
|
||||
foreach my $depentnode (split(/,/, $depent->{'nodedep'})) {
|
||||
if ($depentnode eq $reqnode) {
|
||||
if ($depent->{'msdelay'} > $delay) {
|
||||
$delay = $depent->{'msdelay'};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($ENV{'XCATDEBUG'}) {
|
||||
my %output;
|
||||
$output{data} = ["delay = $delay"];
|
||||
$callback->( \%output );
|
||||
}
|
||||
#convert from millisecond to second
|
||||
$delay /= 1000.0;
|
||||
if ($delay & ($i < scalar(@{$nodeseq}))) {
|
||||
my %output;
|
||||
$output{data} = ["Waiting $delay seconds for node dependencies\n"];
|
||||
$callback->( \%output );
|
||||
if ($ENV{'XCATDEBUG'}) {
|
||||
$output{data} = ["Before sleep $delay seconds"];
|
||||
$callback->( \%output );
|
||||
}
|
||||
Time::HiRes::sleep($delay);
|
||||
if ($ENV{'XCATDEBUG'}) {
|
||||
$output{data} = ["Wake up!"];
|
||||
$callback->( \%output );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# find service nodes for the HCPs
|
||||
# build an individual request for each service node
|
||||
my $service = "xcat";
|
||||
my @hcps=keys(%hcp_hash);
|
||||
my $sn = xCAT::Utils->get_ServiceNode(\@hcps, $service, "MN");
|
||||
|
||||
# build each request for each service node
|
||||
foreach my $snkey (keys %$sn)
|
||||
{
|
||||
#$callback->({data=>["The service node $snkey "]});
|
||||
my $reqcopy = {%$req};
|
||||
$reqcopy->{'_xcatdest'} = $snkey;
|
||||
$reqcopy->{_xcatpreprocessed}->[0] = 1;
|
||||
my $hcps1=$sn->{$snkey};
|
||||
my @nodes=();
|
||||
foreach (@$hcps1) {
|
||||
push @nodes, @{$hcp_hash{$_}{nodes}};
|
||||
}
|
||||
@nodes = sort @nodes;
|
||||
$reqcopy->{node} = \@nodes;
|
||||
#print "nodes=@nodes\n";
|
||||
push @requests, $reqcopy;
|
||||
}
|
||||
return \@requests;
|
||||
return undef;
|
||||
}
|
||||
####################################
|
||||
# Parse arguments
|
||||
|
@ -1116,5 +1116,224 @@ sub setupStatemnt {
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
=head3 build_deps
|
||||
Look up the "deps" table to generate the dependencies for the nodes
|
||||
Arguments:
|
||||
nodes: The nodes list in an array reference
|
||||
Returns:
|
||||
depset: dependencies hash reference
|
||||
Globals:
|
||||
none
|
||||
Error:
|
||||
none
|
||||
Example:
|
||||
my $deps = xCAT::SvrUtils->build_deps($req->{node});
|
||||
Comments:
|
||||
none
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub build_deps()
|
||||
{
|
||||
my ($class, $nodes, $cmd) = @_;
|
||||
my %depshash = ();
|
||||
|
||||
my $depstab = xCAT::Table->new('deps');
|
||||
if (!defined($depstab)) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $depset = $depstab->getNodesAttribs($nodes,[qw(nodedep msdelay cmd)]);
|
||||
if (!defined($depset))
|
||||
{
|
||||
return undef;
|
||||
}
|
||||
foreach my $node (@$nodes) {
|
||||
# Delete the nodes without dependencies from the hash
|
||||
if (!defined($depset->{$node}[0])) {
|
||||
delete($depset->{$node});
|
||||
}
|
||||
}
|
||||
|
||||
# the deps hash does not check the 'cmd',
|
||||
# use the realdeps to reflect the 'cmd' also
|
||||
my $realdep;
|
||||
foreach my $node (@$nodes) {
|
||||
foreach my $depent (@{$depset->{$node}}){
|
||||
my @depcmd = split(/,/, $depent->{'cmd'});
|
||||
#dependency match
|
||||
if (grep(/^$cmd$/, @depcmd)) {
|
||||
#expand the noderange
|
||||
my @nodedep = xCAT::NodeRange::noderange($depent->{'nodedep'},1);
|
||||
my $depsnode = join(',', @nodedep);
|
||||
if ($depsnode) {
|
||||
$depent->{'nodedep'} = $depsnode;
|
||||
push @{$realdep->{$node}}, $depent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $realdep;
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
=head3 handle_deps
|
||||
Group the nodes according to the deps hash returned from build_deps
|
||||
Arguments:
|
||||
deps: the dependencies hash reference
|
||||
nodes: The nodes list in an array reference
|
||||
$callback: sub request callback
|
||||
Returns:
|
||||
nodeseq: the nodes categorized based on dependencies
|
||||
returns 1 if runs into problem
|
||||
Globals:
|
||||
none
|
||||
Error:
|
||||
none
|
||||
Example:
|
||||
my $deps = xCAT::SvrUtils->handle_deps($deps, $req->{node});
|
||||
Comments:
|
||||
none
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub handle_deps()
|
||||
{
|
||||
my ($class, $dephash, $nodes, $callback) = @_;
|
||||
|
||||
# a small subroutine to remove some specific node from a comma separated list
|
||||
sub remove_node_from_list()
|
||||
{
|
||||
my ($string, $nodetoremove) = @_;
|
||||
my @arr = split(',', $string);
|
||||
my @newarr = ();
|
||||
foreach my $tmp (@arr) {
|
||||
if ($tmp ne $nodetoremove) {
|
||||
push @newarr, $tmp;
|
||||
}
|
||||
}
|
||||
return join(',', @newarr);
|
||||
}
|
||||
|
||||
# This is an example of the deps hash ref
|
||||
# DB<3> x $deps
|
||||
#0 HASH(0x239db47c)
|
||||
# 'aixcn1' => ARRAY(0x23a26be0)
|
||||
# 0 HASH(0x23a21968)
|
||||
# 'cmd' => 'off'
|
||||
# 'msdelay' => 10000
|
||||
# 'node' => 'aixcn1'
|
||||
# 'nodedep' => 'aixmn2'
|
||||
# 'aixsn1' => ARRAY(0x23a2219c)
|
||||
# 0 HASH(0x23a21728)
|
||||
# 'cmd' => 'off'
|
||||
# 'msdelay' => 10000
|
||||
# 'node' => 'aixsn1'
|
||||
# 'nodedep' => 'aixcn1'
|
||||
|
||||
#copy the dephash, do not manipulate the subroutine argument $dephash
|
||||
my $deps;
|
||||
foreach my $node (keys %{$dephash}) {
|
||||
my $i = 0;
|
||||
for ($i = 0; $i < scalar(@{$dephash->{$node}}); $i++) {
|
||||
foreach my $attr (keys %{$dephash->{$node}->[$i]}) {
|
||||
$deps->{$node}->[$i]->{$attr} = $dephash->{$node}->[$i]->{$attr};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#needs to search the nodes list a lot of times
|
||||
#using hash will be more effective
|
||||
my %nodelist;
|
||||
foreach my $node (@{$nodes}) {
|
||||
$nodelist{$node} = 1;
|
||||
}
|
||||
|
||||
|
||||
# check if any depnode is not in the nodelist,
|
||||
# print warning message
|
||||
my $depsnotinargs;
|
||||
foreach my $node (keys %{$deps}){
|
||||
my $keepnode = 0;
|
||||
foreach my $depent (@{$deps->{$node}}){
|
||||
# an autonomy dependency group?
|
||||
foreach my $dep (split(/,/, $depent->{'nodedep'})) {
|
||||
if (!defined($nodelist{$dep})) {
|
||||
$depsnotinargs->{$dep} = 1;
|
||||
$depent->{'nodedep'} = &remove_node_from_list($depent->{'nodedep'}, $dep);
|
||||
}
|
||||
}
|
||||
if ($depent->{'nodedep'}) {
|
||||
$keepnode = 1;
|
||||
}
|
||||
}
|
||||
if (!$keepnode) {
|
||||
delete($deps->{$node});
|
||||
}
|
||||
}
|
||||
if (scalar(keys %{$depsnotinargs}) > 0) {
|
||||
my $n = join(',', keys %{$depsnotinargs});
|
||||
|
||||
my %output;
|
||||
$output{data} = ["The following nodes are dependencies for some nodes passed in through arguments, but not in the command arguments: $n, make sure these nodes are in correct state"];
|
||||
$callback->( \%output );
|
||||
}
|
||||
|
||||
|
||||
|
||||
my $arrayindex = 0;
|
||||
my $nodeseq;
|
||||
#handle all the nodes
|
||||
while (keys %nodelist) {
|
||||
|
||||
my @curnodes;
|
||||
foreach my $node (keys %nodelist) {
|
||||
#no dependency
|
||||
if (!defined($deps->{$node})) {
|
||||
$nodeseq->[$arrayindex]->{$node} = 1;
|
||||
delete($nodelist{$node});
|
||||
push @curnodes, $node;
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar(@curnodes) == 0) {
|
||||
# no nodes in this loop at all,
|
||||
# means infinite loop???
|
||||
my %output;
|
||||
my $nodesinlist = join(',', keys %nodelist);
|
||||
$output{errorcode}=1;
|
||||
$output{data} = ["Loop dependency, check your deps table, may be related to the following nodes: $nodesinlist"];
|
||||
$callback->( \%output );
|
||||
return 1;
|
||||
}
|
||||
|
||||
# update deps for the next loop
|
||||
# remove the node from the 'nodedep' attribute
|
||||
my $keepnode = 0;
|
||||
foreach my $nodeindeps (keys %{$deps}) {
|
||||
my $keepnode = 0;
|
||||
foreach my $depent (@{$deps->{$nodeindeps}}){
|
||||
#remove the curnodes from the 'nodedep'
|
||||
foreach my $nodetoremove (@curnodes) {
|
||||
$depent->{'nodedep'} = &remove_node_from_list($depent->{'nodedep'}, $nodetoremove);
|
||||
}
|
||||
if ($depent->{'nodedep'}) {
|
||||
$keepnode = 1;
|
||||
}
|
||||
}
|
||||
if (!$keepnode) {
|
||||
delete($deps->{$nodeindeps});
|
||||
}
|
||||
}
|
||||
|
||||
# the round is over, jump to the next arrary entry
|
||||
$arrayindex++;
|
||||
}
|
||||
return $nodeseq;
|
||||
}
|
||||
|
||||
1;
|
||||
|
Loading…
Reference in New Issue
Block a user