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:
ligc 2010-08-02 03:29:15 +00:00
parent 677a70a760
commit a7e56eb7f7
2 changed files with 404 additions and 81 deletions

View File

@ -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

View File

@ -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;