diff --git a/perl-xCAT/xCAT/MsgUtils.pm b/perl-xCAT/xCAT/MsgUtils.pm index 577d97ec4..079b5e3a7 100644 --- a/perl-xCAT/xCAT/MsgUtils.pm +++ b/perl-xCAT/xCAT/MsgUtils.pm @@ -884,5 +884,28 @@ sub perf_log_process } } +#------------------------------------------------------------------ + +=head3 report_node_error + Using the passing callback to report node level error + Arguments: + $cb (callback reference) + $node (node name) + $msg (error message) +=cut + +#----------------------------------------------------------------- +sub report_node_error +{ + shift; + my ($cb, $node, $msg) = @_; + + my $rsp; + $rsp->{node}->[0]->{name}->[0] = $node; + $rsp->{node}->[0]->{error}->[0] = $msg; + $rsp->{node}->[0]->{errorcode}->[0] = 1; + $cb->($rsp); +} + 1; diff --git a/xCAT-server/lib/xcat/plugins/anaconda.pm b/xCAT-server/lib/xcat/plugins/anaconda.pm index c5d79e7d0..e6f759104 100644 --- a/xCAT-server/lib/xcat/plugins/anaconda.pm +++ b/xCAT-server/lib/xcat/plugins/anaconda.pm @@ -174,43 +174,32 @@ sub mknetboot $statelite = "true"; } my $bootparams = ${$req->{bootparams}}; - my $globaltftpdir = "/tftpboot"; - my $nodes = @{ $req->{node} }; my @args = @{ $req->{arg} } if (exists($req->{arg})); my @nodes = @{ $req->{node} }; my $noupdateinitrd = $req->{'noupdateinitrd'}; my $ignorekernelchk = $req->{'ignorekernelchk'}; - my $ostab = xCAT::Table->new('nodetype'); - #my $sitetab = xCAT::Table->new('site'); my $linuximagetab; my $osimagetab; my %img_hash = (); - my $installroot; - $installroot = "/install"; my $xcatdport = "3001"; my $xcatiport = "3002"; - my $nodestatus = "y"; + my @myself = xCAT::NetworkUtils->determinehostname(); my $myname = $myself[ (scalar @myself) - 1 ]; - #if ($sitetab) - #{ - # (my $ref) = $sitetab->getAttribs({key => 'installdir'}, 'value'); - my @ents = xCAT::TableUtils->get_site_attribute("installdir"); + my $installroot = xCAT::TableUtils->getInstallDir(); + my $globaltftpdir = xCAT::TableUtils->getTftpDir(); + xCAT::MsgUtils->trace(0, "d", "anaconda->mknetboot: installroot=$installroot globaltftpdir=$globaltftpdir"); + + my $nodestatus = "y"; + my @ents = xCAT::TableUtils->get_site_attribute("nodestatus"); my $site_ent = $ents[0]; if (defined($site_ent)) - { - $installroot = $site_ent; - } - @ents = xCAT::TableUtils->get_site_attribute("nodestatus"); - $site_ent = $ents[0]; - if (defined($site_ent)) { $nodestatus = $site_ent; } - # ($ref) = $sitetab->getAttribs({key => 'xcatdport'}, 'value'); @ents = xCAT::TableUtils->get_site_attribute("xcatdport"); $site_ent = $ents[0]; if (defined($site_ent)) @@ -224,14 +213,8 @@ sub mknetboot $xcatiport = $site_ent; } - # ($ref) = $sitetab->getAttribs({key => 'tftpdir'}, 'value'); - @ents = xCAT::TableUtils->get_site_attribute("tftpdir"); - $site_ent = $ents[0]; - if (defined($site_ent)) - { - $globaltftpdir = $site_ent; - } my %donetftp = (); + my $ostab = xCAT::Table->new('nodetype'); my %oents = %{ $ostab->getNodesAttribs(\@nodes, [qw(os arch profile provmethod)]) }; my $restab = xCAT::Table->new('noderes'); my $hmtab = xCAT::Table->new('nodehm'); @@ -240,9 +223,8 @@ sub mknetboot my $machash = $mactab->getNodesAttribs(\@nodes, [ 'interface', 'mac' ]); my $reshash = $restab->getNodesAttribs(\@nodes, [ 'primarynic', 'tftpserver', 'tftpdir', 'xcatmaster', 'nfsserver', 'nfsdir', 'installnic' ]); - my $hmhash = - $hmtab->getNodesAttribs(\@nodes, - [ 'serialport', 'serialspeed', 'serialflow' ]); + my $hmhash = $hmtab->getNodesAttribs(\@nodes, [ 'serialport', 'serialspeed', 'serialflow' ]); + my $statetab; my $stateHash; if ($statelite) { @@ -250,31 +232,6 @@ sub mknetboot $stateHash = $statetab->getNodesAttribs(\@nodes, ['statemnt']); } - - # Warning message for nodeset install/netboot/statelite - foreach my $knode (keys %oents) - { - my $ent = $oents{$knode}->[0]; - if ($ent && $ent->{provmethod} - && (($ent->{provmethod} eq 'install') || ($ent->{provmethod} eq 'netboot') || ($ent->{provmethod} eq 'statelite'))) - { - my @ents = xCAT::TableUtils->get_site_attribute("disablenodesetwarning"); - my $site_ent = $ents[0]; - if (!defined($site_ent) || ($site_ent =~ /no/i) || ($site_ent =~ /0/)) - { - if (!defined($::DISABLENODESETWARNING)) { # set by AAsn.pm - $callback->( - { - error => ["The options \"install\", \"netboot\", and \"statelite\" have been deprecated, use \"nodeset osimage=\" instead."], errorcode => [1] - } - ); - - # Do not print this warning message multiple times - exit(1); - } - } - } - } foreach my $node (@nodes) { my $osver; @@ -298,7 +255,9 @@ sub mknetboot my $ent = $oents{$node}->[0]; #ostab->getNodeAttribs($node, ['os', 'arch', 'profile']); - if ($ent and $ent->{provmethod} and ($ent->{provmethod} ne 'install') and ($ent->{provmethod} ne 'netboot') and ($ent->{provmethod} ne 'statelite')) { + if ($ent and $ent->{provmethod} and \ + ($ent->{provmethod} ne 'install') and ($ent->{provmethod} ne 'netboot') and ($ent->{provmethod} ne 'statelite')) { + $imagename = $ent->{provmethod}; #print "imagename=$imagename\n"; @@ -355,9 +314,7 @@ sub mknetboot $img_hash{$imagename}->{'partfile'} = $ref1->{'partitionfile'}; } } else { - $callback->( - { error => ["The os image $imagename does not exists on the osimage table for $node"], - errorcode => [1] }); + xCAT::MsgUtils->report_node_error($callback, $node, "The OS image '$imagename' for node does not exist"); next; } } @@ -379,6 +336,10 @@ sub mknetboot $cfgpart = $ph->{'cfgpart'}; } else { + # This is deprecated mode to define node's provmethod, not supported now. + xCAT::MsgUtils->report_node_error($callback, $node, "OS image name must be specified in nodetype.provmethod"); + next; + $osver = $ent->{os}; $arch = $ent->{arch}; $profile = $ent->{profile}; @@ -402,10 +363,10 @@ sub mknetboot $rootfstype = $ref1->{'rootfstype'}; } } else { - $callback->( - { error => [qq{Cannot find the linux image called "$osver-$arch-$imgname-$profile", maybe you need to use the "nodeset osimage=" command to set the boot state}], - errorcode => [1] } - ); + xCAT::MsgUtils->report_node_error($callback, $node, + qq{Cannot find the linux image called "$osver-$arch-$imgname-$profile", maybe you need to use the "nodeset osimage=" command to set the boot state} + ); + next; } if (!$linuximagetab) { @@ -441,22 +402,17 @@ sub mknetboot } } } else { - $callback->( - { error => [qq{ Cannot find the linux image called "$osver-$arch-$imgname-$profile", maybe you need to use the "nodeset osimage=" command to set the boot state}], - errorcode => [1] } - ); + xCAT::MsgUtils->report_node_error($callback, $node, + qq{Cannot find the linux image called "$osver-$arch-$imgname-$profile", maybe you need to use the "nodeset osimage=" command to set the boot state} + ); + next; } } #print"osvr=$osver, arch=$arch, profile=$profile, imgdir=$rootimgdir\n"; unless ($osver and $arch and $profile) { - $callback->( - { - error => ["Insufficient nodetype entry or osimage entry for $node"], - errorcode => [1] - } - ); + xCAT::MsgUtils->report_node_error($callback, $node, "Insufficient nodetype entry or osimage entry for $node"); next; } @@ -467,18 +423,16 @@ sub mknetboot # statelite images are not packed. if ($statelite) { unless (-r "$rootimgdir/kernel") { - $callback->({ - error => [qq{Did you run "genimage" before running "liteimg"? kernel cannot be found at $rootimgdir/kernel on $myname}], - errorcode => [1] - }); + xCAT::MsgUtils->report_node_error($callback, $node, + qq{Did you run "genimage" before running "liteimg"? kernel cannot be found at $rootimgdir/kernel on $myname} + ); next; } if (!-r "$rootimgdir/initrd-statelite.gz") { if (!-r "$rootimgdir/initrd.gz") { - $callback->({ - error => [qq{Did you run "genimage" before running "liteimg"? initrd.gz or initrd-statelite.gz cannot be found at $rootimgdir/initrd.gz on $myname}], - errorcode => [1] - }); + xCAT::MsgUtils->report_node_error($callback, $node, + qq{Did you run "genimage" before running "liteimg"? initrd.gz or initrd-statelite.gz cannot be found at $rootimgdir/initrd.gz on $myname} + ); next; } else { @@ -486,35 +440,32 @@ sub mknetboot } } if ($rootfstype eq "ramdisk" and !-r "$rootimgdir/rootimg-statelite.gz") { - $callback->({ - error => [qq{No packed image for platform $osver, architecture $arch and profile $profile, please run "liteimg" to create it.}], - errorcode => [1] - }); + xCAT::MsgUtils->report_node_error($callback, $node, + qq{No packed image for platform $osver, architecture $arch and profile $profile, please run "liteimg" to create it.} + ); next; } } else { unless (-r "$rootimgdir/kernel") { - $callback->({ - error => [qq{Did you run "genimage" before running "packimage"? kernel cannot be found at $rootimgdir/kernel on $myname}], - errorcode => [1] - }); + xCAT::MsgUtils->report_node_error($callback, $node, + qq{Did you run "genimage" before running "packimage"? kernel cannot be found at $rootimgdir/kernel on $myname} + ); next; } if (!-r "$rootimgdir/initrd-stateless.gz") { if (!-r "$rootimgdir/initrd.gz") { - $callback->({ - error => [qq{Did you run "genimage" before running "packimage"? initrd.gz or initrd-stateless.gz cannot be found at $rootimgdir/initrd.gz on $myname}], - errorcode => [1] - }); + xCAT::MsgUtils->report_node_error($callback, $node, + qq{Did you run "genimage" before running "packimage"? kernel cannot be found at $rootimgdir/kernel on $myname} + ); next; } else { copy("$rootimgdir/initrd.gz", "$rootimgdir/initrd-stateless.gz"); } } unless ( -f -r "$rootimgdir/$compressedrootimg") { - $callback->({ - error => ["No packed image for platform $osver, architecture $arch, and profile $profile found at $rootimgdir/rootimg.gz or $rootimgdir/rootimg.sfs on $myname, please run packimage (e.g. packimage -o $osver -p $profile -a $arch"], - errorcode => [1] }); + xCAT::MsgUtils->report_node_error($callback, $node, + "No packed image for platform $osver, architecture $arch, and profile $profile found at $rootimgdir/rootimg.gz or $rootimgdir/rootimg.sfs on $myname, please run packimage (e.g. packimage -o $osver -p $profile -a $arch" + ); next; } } @@ -567,21 +518,13 @@ sub mknetboot } else { $initrdloc .= "/initrd-statelite.gz"; } - unless (-r "$tftppath/kernel" - and -r $initrdloc) { - $callback->({ - error => [qq{copying to $tftppath failed}], - errorcode => [1] - }); + unless (-r "$tftppath/kernel" and -r $initrdloc) { + xCAT::MsgUtils->report_node_error($callback, $node, qq{Copying to $tftppath failed.}); next; } } else { - unless (-r "$tftppath/kernel" - and -r "$tftppath/initrd-stateless.gz") { - $callback->({ - error => [qq{copying to $tftppath failed}], - errorcode => [1] - }); + unless (-r "$tftppath/kernel" and -r "$tftppath/initrd-stateless.gz") { + xCAT::MsgUtils->report_node_error($callback, $node, qq{Copying to $tftppath failed.}); next; } } @@ -600,51 +543,24 @@ sub mknetboot $ient = $reshash->{$node}->[0]; #$restab->getNodeAttribs($node, ['tftpserver']); - if ($ient and $ient->{xcatmaster}) - { + if ($ient and $ient->{xcatmaster}) { $xcatmaster = $ient->{xcatmaster}; } else { $xcatmaster = '!myipfn!'; #allow service nodes to dynamically nominate themselves as a good contact point, this is of limited use in the event that xcat is not the dhcp/tftp server } - if ($ient and $ient->{tftpserver} and $ient->{tftpserver} ne '') - { + if ($ient and $ient->{tftpserver} and $ient->{tftpserver} ne '') { $imgsrv = $ient->{tftpserver}; - } - else - { - $ient = $reshash->{$node}->[0]; #$restab->getNodeAttribs($node, ['xcatmaster']); - #if ($ient and $ient->{xcatmaster}) - #{ - # $imgsrv = $ient->{xcatmaster}; - #} - #else - #{ - # master not correct for service node pools - #$ient = $sitetab->getAttribs({key => master}, value); - #if ($ient and $ient->{value}) - #{ - # $imgsrv = $ient->{value}; - #} - #else - #{ - # $imgsrv = '!myipfn!'; - #} - #} + } else { + $ient = $reshash->{$node}->[0]; $imgsrv = $xcatmaster; } - unless ($imgsrv) - { - $callback->( - { - error => [ -"Unable to determine or reasonably guess the image server for $node" - ], - errorcode => [1] - } - ); + unless ($imgsrv) { + xCAT::MsgUtils->report_node_error($callback, $node, "Unable to determine or reasonably guess the image server for $node"); next; } + + # Start to build kcmdline my $kcmdline; # add more arguments: XCAT=xcatmaster:xcatport NODE= @@ -753,11 +669,9 @@ sub mknetboot } else { if (-r "$rootimgdir/$compressedrootimg.metainfo") { - $kcmdline = -"imgurl=$httpmethod://$imgsrv:$httpport/$rootimgdir/$compressedrootimg.metainfo "; + $kcmdline = "imgurl=$httpmethod://$imgsrv:$httpport/$rootimgdir/$compressedrootimg.metainfo "; } else { - $kcmdline = -"imgurl=$httpmethod://$imgsrv:$httpport/$rootimgdir/$compressedrootimg "; + $kcmdline = "imgurl=$httpmethod://$imgsrv:$httpport/$rootimgdir/$compressedrootimg "; } $kcmdline .= "XCAT=$xcatmaster:$xcatdport "; $kcmdline .= "NODE=$node "; @@ -826,20 +740,11 @@ sub mknetboot } - if (defined $sent->{serialport}) - { + if (defined $sent->{serialport}) { #my $sent = $hmtab->getNodeAttribs($node,['serialspeed','serialflow']); - unless ($sent->{serialspeed}) - { - $callback->( - { - error => [ -"serialport defined, but no serialspeed for $node in nodehm table" - ], - errorcode => [1] - } - ); + unless ($sent->{serialspeed}) { + xCAT::MsgUtils->report_node_error($callback, $node,"serialport defined, but no serialspeed for $node in nodehm table"); next; } if ($arch =~ /ppc64/i) { @@ -966,16 +871,11 @@ sub mkinstall my $ignorekernelchk = $request->{'ignorekernelchk'}; my $bootparams = ${$request->{bootparams}}; - #my $sitetab = xCAT::Table->new('site'); my $linuximagetab; my $osimagetab; my $osdistrouptab; my %img_hash = (); - my $installroot; - my $globaltftpdir; - $installroot = "/install"; - $globaltftpdir = "/tftpboot"; #>>>>>>>used for trace log start>>>>>>> my @args = (); @@ -992,27 +892,11 @@ sub mkinstall #>>>>>>>used for trace log end>>>>>>> - #if ($sitetab) - #{ - # (my $ref) = $sitetab->getAttribs({key => 'installdir'}, 'value'); - my @ents = xCAT::TableUtils->get_site_attribute("installdir"); - my $site_ent = $ents[0]; - if (defined($site_ent)) - { - $installroot = $site_ent; - } - #( $ref) = $sitetab->getAttribs({key => 'tftpdir'}, 'value'); - @ents = xCAT::TableUtils->get_site_attribute("tftpdir"); - $site_ent = $ents[0]; - if (defined($site_ent)) - { - $globaltftpdir = $site_ent; - } + my $installroot = xCAT::TableUtils->getInstallDir(); + my $globaltftpdir = xCAT::TableUtils->getTftpDir(); xCAT::MsgUtils->trace($verbose_on_off, "d", "anaconda->mkinstall: installroot=$installroot globaltftpdir=$globaltftpdir"); - #} - my $node; my $ostab = xCAT::Table->new('nodetype'); my %donetftp; @@ -1028,30 +912,6 @@ sub mkinstall require xCAT::Template; - # Warning message for nodeset install/netboot/statelite - foreach my $knode (keys %osents) - { - my $ent = $osents{$knode}->[0]; - if ($ent && $ent->{provmethod} - && (($ent->{provmethod} eq 'install') || ($ent->{provmethod} eq 'netboot') || ($ent->{provmethod} eq 'statelite'))) - { - my @ents = xCAT::TableUtils->get_site_attribute("disablenodesetwarning"); - my $site_ent = $ents[0]; - if (!defined($site_ent) || ($site_ent =~ /no/i) || ($site_ent =~ /0/)) - { - if (!defined($::DISABLENODESETWARNING)) { # set by AAsn.pm - $callback->( - { - error => ["The options \"install\", \"netboot\", and \"statelite\" have been deprecated, use \"nodeset osimage=\" instead."], errorcode => [1] - } - ); - - # Do not print this warning message multiple times - exit(1); - } - } - } - } foreach $node (@nodes) { my $os; @@ -1188,9 +1048,7 @@ sub mkinstall $img_hash{$imagename}->{'osupdir'} = $osdisupdir; } } else { - $callback->( - { error => ["The os image $imagename does not exists on the osimage table for $node"], - errorcode => [1] }); + xCAT::MsgUtils->report_node_error($callback, $node, "The OS image '$imagename' for this node does not exist."); next; } } @@ -1215,6 +1073,10 @@ sub mkinstall xCAT::MsgUtils->trace($verbose_on_off, "d", "anaconda->mkinstall: imagename=$imagename pkgdir=$pkgdir pkglistfile=$pkglistfile tmplfile=$tmplfile partfile=$partfile"); } else { + # Not support the depreate mode now. + xCAT::MsgUtils->report_node_error($callback, $node, "OS image name must be specified in nodetype.provmethod"); + next; + $os = $ent->{os}; $arch = $ent->{arch}; $profile = $ent->{profile}; @@ -1290,29 +1152,15 @@ sub mkinstall if ($imagename) { push @missingparms, "osimage.profile"; } else { push @missingparms, "nodetype.profile"; } } - unless ($os and $arch and $profile) - { - $callback->( - { - error => [ "Missing " . join(',', @missingparms) . " for $node" ], - errorcode => [1] - } - ); + unless ($os and $arch and $profile) { + xCAT::MsgUtils->report_node_error($callback, $node, "Missing " . join(',', @missingparms) . " for $node"); next; #No profile } - unless (-r "$tmplfile") - { - $callback->( - { - error => [ - "No $platform kickstart template exists for " - . $profile - . " in directory $installroot/custom/install/$platform or $::XCATROOT/share/xcat/install/$platform" - ], - errorcode => [1] - } - ); + unless (-r "$tmplfile") { + xCAT::MsgUtils->report_node_error($callback, $node, + "No $platform kickstart template exists for " . $profile . " in directory $installroot/custom/install/$platform or $::XCATROOT/share/xcat/install/$platform" + ); next; } @@ -1340,14 +1188,8 @@ sub mkinstall ); } - if ($tmperr) - { - $callback->( - { - node => - [ { name => [$node], error => [$tmperr], errorcode => [1] } ] - } - ); + if ($tmperr) { + xCAT::MsgUtils->report_node_error($callback, $node, $tmperr); next; } @@ -1517,14 +1359,8 @@ sub mkinstall if ($::XCATSITEVALS{managedaddressmode} =~ /static/) { my ($ipaddr, $hostname, $gateway, $netmask) = xCAT::NetworkUtils->getNodeNetworkCfg($node); unless ($ipaddr) { - $callback->( - { - error => [ - "cannot resolve the ip address of $node" - ], - errorcode => [1] - } - ); + xCAT::MsgUtils->report_node_error($callback, $node, "cannot resolve the ip address of $node"); + next; } if ($gateway eq '') { @@ -1607,18 +1443,9 @@ sub mkinstall #TODO: dd= for driver disks - if (defined($sent->{serialport})) - { - unless ($sent->{serialspeed}) - { - $callback->( - { - error => [ -"serialport defined, but no serialspeed for $node in nodehm table" - ], - errorcode => [1] - } - ); + if (defined($sent->{serialport})) { + unless ($sent->{serialspeed}) { + xCAT::MsgUtils->report_node_error($callback, $node, "serialport defined, but no serialspeed for this node in nodehm table"); next; } @@ -1640,8 +1467,7 @@ sub mkinstall $kcmdline .= " console=tty0 console=ttyS" . $sent->{serialport} . "," . $sent->{serialspeed}; } - if ($sent->{serialflow} =~ /(hard|cts|ctsrts)/) - { + if ($sent->{serialflow} =~ /(hard|cts|ctsrts)/) { $kcmdline .= "n8r"; } } @@ -1680,16 +1506,11 @@ sub mkinstall $bootparams->{$node}->[0]->{initrd} = $i; $bootparams->{$node}->[0]->{kcmdline} = $kcmdline; } - else - { - $callback->( - { - error => ["Install image not found in $pkgdir"], - errorcode => [1] - } - ); + else { + xCAT::MsgUtils->report_node_error($callback, $node, "Install image not found in $pkgdir"); + next; } - } + } # end foreach node #my $rc = xCAT::TableUtils->create_postscripts_tar(); #if ($rc != 0) @@ -1709,23 +1530,10 @@ sub mksysclone my $osimagetab; my %img_hash = (); - my $installroot; - my $globaltftpdir; - $installroot = "/install"; - $globaltftpdir = "/tftpboot"; + my $installroot = xCAT::TableUtils->getInstallDir(); + my $globaltftpdir = xCAT::TableUtils->getTftpDir(); + xCAT::MsgUtils->trace(0, "d", "anaconda->mksysclone: installroot=$installroot globaltftpdir=$globaltftpdir"); - my @ents = xCAT::TableUtils->get_site_attribute("installdir"); - my $site_ent = $ents[0]; - if (defined($site_ent)) - { - $installroot = $site_ent; - } - @ents = xCAT::TableUtils->get_site_attribute("tftpdir"); - $site_ent = $ents[0]; - if (defined($site_ent)) - { - $globaltftpdir = $site_ent; - } my $node; my $ostab = xCAT::Table->new('nodetype'); @@ -2294,8 +2102,7 @@ sub copycd { $callback->( { - error => -"Requested distribution architecture $arch, but media is $darch" + error => "Requested distribution architecture $arch, but media is $darch" } ); return; diff --git a/xCAT-server/lib/xcat/plugins/destiny.pm b/xCAT-server/lib/xcat/plugins/destiny.pm index ae72167d6..48b8411a2 100755 --- a/xCAT-server/lib/xcat/plugins/destiny.pm +++ b/xCAT-server/lib/xcat/plugins/destiny.pm @@ -35,6 +35,7 @@ my $tftpdir = "/tftpboot"; my $nonodestatus = 0; +my %failurenodes = (); #my $sitetab = xCAT::Table->new('site'); #if ($sitetab) { @@ -64,36 +65,62 @@ sub process_request { my $result = xCAT::TableUtils->checkCredFiles($callback); } if ($request->{command}->[0] eq 'getdestiny') { - getdestiny(0); - } - if ($request->{command}->[0] eq 'nextdestiny') { + xCAT::MsgUtils->trace(0, "d", "destiny->process_request: starting getdestiny..."); + my @nodes; + if ($request->{node}) { + if (ref($request->{node})) { + @nodes = @{ $request->{node} }; + } else { + @nodes = ($request->{node}); + } + } else { # a client asking for it's own destiny. + unless ($request->{'_xcat_clienthost'}->[0]) { + $callback->({ destiny => ['discover'] }); + return; + } + my ($node) = noderange($request->{'_xcat_clienthost'}->[0]); + unless ($node) { # it had a valid hostname, but isn't a node + $callback->({ destiny => ['discover'] }); + return; + } + @nodes = ($node); + } + getdestiny(0, \@nodes); + + } elsif ($request->{command}->[0] eq 'nextdestiny') { + xCAT::MsgUtils->trace(0, "d", "destiny->process_request: starting nextdestiny..."); nextdestiny(0, 1); #it is called within dodestiny - } - if ($request->{command}->[0] eq 'setdestiny') { + + } elsif ($request->{command}->[0] eq 'setdestiny') { + xCAT::MsgUtils->trace(0, "d", "destiny->process_request: starting setdestiny..."); setdestiny($request, 0); + } + xCAT::MsgUtils->trace(0, "d", "destiny->process_request: processing is finished for " . $request->{command}->[0]); } sub relay_response { my $resp = shift; - my $failure = 0; + return unless ($resp); + $callback->($resp); if ($resp and ($resp->{errorcode} and $resp->{errorcode}->[0]) or ($resp->{error} and $resp->{error}->[0])) { - $failure = 1; + $errored = 1; } - # quick return when detect failure. - unless ( $failure ) { - foreach (@{ $resp->{node} }) { - if ($_->{error} or $_->{errorcode}) { - $failure = 1; - last; + + my $failure = 0; + # Partial error on nodes, it allows to continue the rest of business on the sucessful nodes. + foreach (@{ $resp->{node} }) { + if ($_->{error} or $_->{errorcode}) { + $failure = 1; + if ($_->{name}) { + $failurenodes{$_->{name}->[0]} = 2; } } } if ( $failure ) { $errored = $failure; } - } sub setdestiny { @@ -101,7 +128,6 @@ sub setdestiny { my $flag = shift; my $noupdate = shift; $chaintab = xCAT::Table->new('chain', -create => 1); - my @nodes = @{ $req->{node} }; my $bphash = $req->{bootparams}; @ARGV = @{ $req->{arg} }; @@ -120,9 +146,11 @@ sub setdestiny { #>>>>>>>used for trace log end>>>>>>> - my $state = $ARGV[0]; - my $reststates; - + if (@{ $req->{node} } == 0) { + xCAT::MsgUtils->trace($verbose, "d", "destiny->setdestiny: no nodes left to process, we are done"); + return; + } + my @nodes = @{ $req->{node} }; my $bptab = xCAT::Table->new('bootparams', -create => 1); my %tempbh = %{ $bptab->getNodesAttribs(\@nodes, [qw(addkcmdline)]) }; while(my ($key, $value) = each(%tempbh)) { @@ -134,9 +162,13 @@ sub setdestiny { } $bptab->close(); + my $state = $ARGV[0]; + my $reststates; + my %nstates; # to support the case that the state could be runimage=xxx,runimage=yyy,osimage=xxx ($state, $reststates) = split(/,/, $state, 2); - my %nstates; + chomp($state); + if ($state eq "enact") { my $nodetypetab = xCAT::Table->new('nodetype', -create => 1); my %nodestates; @@ -175,7 +207,7 @@ sub setdestiny { } elsif ($state eq "iscsiboot") { my $iscsitab = xCAT::Table->new('iscsi'); unless ($iscsitab) { - $callback->({ error => "Unable to open iscsi table to get iscsiboot parameters", errorcode => [1] }); + $callback->({ error => "Unable to open iscsi table to get iscsiboot parameters", errorcode => [1], errorabort => [1] }); } my $nodetype = xCAT::Table->new('nodetype'); my $ntents = $nodetype->getNodesAttribs($req->{node}, [qw(os arch profile)]); @@ -184,7 +216,10 @@ sub setdestiny { my $ient = $ients->{$_}->[0]; #$iscsitab->getNodeAttribs($_,[qw(kernel kcmdline initrd)]); my $ntent = $ntents->{$_}->[0]; unless ($ient and $ient->{kernel}) { - unless ($ntent and $ntent->{arch} =~ /x86/ and -f ("$tftpdir/undionly.kpxe" or -f "$tftpdir/xcat/xnba.kpxe")) { $callback->({ error => "$_: No iscsi boot data available", errorcode => [1] }); } #If x86 node and undionly.kpxe exists, presume they know what they are doing + unless ($ntent and $ntent->{arch} =~ /x86/ and -f ("$tftpdir/undionly.kpxe" or -f "$tftpdir/xcat/xnba.kpxe")) { + $failurenodes{$_} = 1; + xCAT::MsgUtils->report_node_error($callback, $_, "No iscsi boot data available"); + } #If x86 node and undionly.kpxe exists, presume they know what they are doing next; } $bphash->{kernel} = $ient->{kernel}; @@ -192,8 +227,6 @@ sub setdestiny { if ($ient->{kcmdline}) { $bphash->{kcmdline} = $ient->{kcmdline} } } } elsif ($state =~ /^install[=\$]/ or $state eq 'install' or $state =~ /^netboot[=\$]/ or $state eq 'netboot' or $state eq "image" or $state eq "winshell" or $state =~ /^osimage/ or $state =~ /^statelite/) { - my %state_hash; - chomp($state); my $target; my $action; my $rawstate=$state; @@ -213,16 +246,24 @@ sub setdestiny { ($state, $action) = split ':', $state, 2; } } - + xCAT::MsgUtils->trace($verbose, "d", "destiny->setdestiny: state=$state, target=$target, action=$action"); + my %state_hash; + # 1, Set an initial state for all requested nodes foreach my $tmpnode (@{ $req->{node} }) { + next if ($failurenodes{$tmpnode}); $state_hash{$tmpnode} = $state; } + # 2, Filter those unsuitable nodes in 'state_hash' my $nodetypetable = xCAT::Table->new('nodetype', -create => 1); my $noderestable = xCAT::Table->new('noderes', -create => 1); - my $nbents = $noderestable->getNodeAttribs($req->{node}->[0], ["netboot"]); + my $nbents = $noderestable->getNodeAttribs($req->{node}->[0], ["netboot"]); # It is assumed that all nodes has the same `netboot` attribute my $curnetboot = $nbents->{netboot}; + if ($state ne 'osimage') { + $callback->({ error => "The options \"install\", \"netboot\", and \"statelite\" have been deprecated, use \"osimage=\" instead.", errorcode => [1], errorabort => [1] }); + return; + my $updateattribs; if ($target) { my $archentries = $nodetypetable->getNodesAttribs($req->{node}, ['supportedarchs']); @@ -233,33 +274,44 @@ sub setdestiny { my $nodearch = $2; foreach (@{ $req->{node} }) { if ($archentries->{$_}->[0]->{supportedarchs} and $archentries->{$_}->[0]->{supportedarchs} !~ /(^|,)$nodearch(\z|,)/) { - $callback->({ errorcode => [1], error => "Requested architecture " . $nodearch . " is not one of the architectures supported by $_ (per nodetype.supportedarchs, it supports " . $archentries->{$_}->[0]->{supportedarchs} . ")" }); - return; + xCAT::MsgUtils->report_node_error($callback, $_, + "Requested architecture " . $nodearch . " is not one of the architectures supported by $_ (per nodetype.supportedarchs, it supports " . $archentries->{$_}->[0]->{supportedarchs} . ")" + ); + $failurenodes{$_} = 1; + next; } } #end foreach } else { $updateattribs->{profile} = $target; } } #end if($target) + $updateattribs->{provmethod} = $state; - my @tmpnodelist = @{ $req->{node} }; + my @tmpnodelist = (); + foreach (@{ $req->{node} }) { + if ($failurenodes{$_}) { + delete $state_hash{$_}; + next; + } + push @tmpnodelist, $_; + } $nodetypetable->setNodesAttribs(\@tmpnodelist, $updateattribs); + } else { #state is osimage - if (@{ $req->{node} } == 0) { return; } if ($target) { + if (@{ $req->{node} } == 0) { return; } my $osimagetable = xCAT::Table->new('osimage'); (my $ref) = $osimagetable->getAttribs({ imagename => $target }, 'provmethod', 'osvers', 'profile', 'osarch', 'imagetype'); - if ($ref) { if ($ref->{provmethod}) { $state = $ref->{provmethod}; } else { - $errored = 1; $callback->({ errorcode => [1], error => "osimage.provmethod for $target must be set." }); + $callback->({ errorcode => [1], error => "osimage.provmethod for $target must be set.", errorabort => [1] }); return; } } else { - $errored = 1; $callback->({ errorcode => [1], error => "Cannot find the OS image $target on the osimage table." }); + $callback->({ errorcode => [1], error => "Cannot find the OS image $target on the osimage table.", errorabort => [1] }); return; } @@ -278,25 +330,37 @@ sub setdestiny { $updateattribs->{profile} = $ref->{profile}; $updateattribs->{os} = $ref->{osvers}; $updateattribs->{arch} = $ref->{osarch}; - my @tmpnodelist = @{ $req->{node} }; + my @tmpnodelist = (); + foreach ( @nodes ) { + if (exists($failurenodes{$_})) { + delete $state_hash{$_}; + next; + } + $state_hash{$_} = $state; + push @tmpnodelist, $_; + } $nodetypetable->setNodesAttribs(\@tmpnodelist, $updateattribs); - - foreach my $tmpnode (@{ $req->{node} }) { - $state_hash{$tmpnode} = $state; - } - } else { - my @errornodes = (); my $invalidosimghash; - my @validnodes; my $updatestuff; my $nodetypetable = xCAT::Table->new('nodetype', -create => 1); my %ntents = %{ $nodetypetable->getNodesAttribs($req->{node}, "provmethod") }; - foreach my $tmpnode (@{ $req->{node} }) { + + foreach my $tmpnode (@nodes) { + next if (exists($failurenodes{$tmpnode})); + my $osimage = $ntents{$tmpnode}->[0]->{provmethod}; if (($osimage) && ($osimage ne 'install') && ($osimage ne 'netboot') && ($osimage ne 'statelite')) { - if (!exists($updatestuff->{$osimage})) { + if (exists($updatestuff->{$osimage})) { #valid osimage + my $vnodes = $updatestuff->{$osimage}->{nodes}; + push(@$vnodes, $tmpnode); + $state_hash{$tmpnode} = $updatestuff->{$osimage}->{state}; + } elsif (exists($invalidosimghash->{$osimage})) { #valid osimage + push(@{ $invalidosimghash->{$osimage}->{nodes} }, $tmpnode); + next; + } + else { #Get a new osimage, to valid it and put invalid osimage into `invalidosimghash` my $osimagetable = xCAT::Table->new('osimage'); (my $ref) = $osimagetable->getAttribs({ imagename => $osimage }, 'provmethod', 'osvers', 'profile', 'osarch', 'imagetype'); if ($ref) { @@ -305,14 +369,14 @@ sub setdestiny { #if not,push the nodes into $invalidosimghash->{$osimage}->{netboot} my $netbootval = xCAT::Utils->lookupNetboot($ref->{osvers}, $ref->{osarch}, $ref->{imagetype}); if ($netbootval =~ /$curnetboot/i) { - push(@validnodes, $tmpnode); + 1; } else { push(@{ $invalidosimghash->{$osimage}->{nodes} }, $tmpnode); $invalidosimghash->{$osimage}->{netboot} = $netbootval; next; } - if ($ref->{provmethod}) { + if ($ref->{provmethod} && $ref->{profile} && $ref->{osvers} && $ref->{osarch}) { $state = $ref->{provmethod}; $state_hash{$tmpnode} = $state; @@ -322,93 +386,112 @@ sub setdestiny { $updatestuff->{$osimage}->{os} = $ref->{osvers}; $updatestuff->{$osimage}->{arch} = $ref->{osarch}; } else { - $errored = 1; $callback->({ errorcode => [1], error => "osimage.provmethod for $osimage must be set." }); - return; + push(@{ $invalidosimghash->{$osimage}->{nodes} }, $tmpnode); + $invalidosimghash->{$osimage}->{error}->[0] = "osimage.provmethod, osimage.osvers, osimage.osarch and osimage.profile for $osimage must be set"; + next; } } else { - $errored = 1; $callback->({ errorcode => [1], error => "Cannot find the OS image $osimage on the osimage table." }); - return; + push(@{ $invalidosimghash->{$osimage}->{nodes} }, $tmpnode); + $invalidosimghash->{$osimage}->{error}->[0] = "Cannot find the OS image $osimage on the osimage table"; + next; } - } else { - my $nodes = $updatestuff->{$osimage}->{nodes}; - push(@$nodes, $tmpnode); - push(@validnodes, $tmpnode); - $state_hash{$tmpnode} = $updatestuff->{$osimage}->{state}; } - } else { - push(@errornodes, $tmpnode); + # not supported legacy mode + push(@{ $invalidosimghash->{__xcat_legacy_provmethod_mode}->{nodes} }, $tmpnode); + $invalidosimghash->{__xcat_legacy_provmethod_mode}->{error}->[0] = "OS image name must be specified in nodetype.provmethod"; + next; } } - if (@errornodes) { - $errored = 1; $callback->({ errorcode => [1], error => "OS image name must be specified in nodetype.provmethod for nodes: @errornodes." }); - return; - } else { - foreach my $tmpimage (keys %$updatestuff) { - my $updateattribs = $updatestuff->{$tmpimage}; - my @tmpnodelist = @{ $updateattribs->{nodes} }; - - delete $updateattribs->{nodes}; #not needed for nodetype table - delete $updateattribs->{state}; #node needed for nodetype table - $nodetypetable->setNodesAttribs(\@tmpnodelist, $updateattribs); + #if any node with inappropriate noderes.netboot,report the warning + foreach my $tmpimage (keys %$invalidosimghash) { + my @fnodes = @{ $invalidosimghash->{$tmpimage}->{nodes} }; + for (@fnodes) { + $failurenodes{$_} = 1; + delete $state_hash{$_}; + if ($invalidosimghash->{$tmpimage}->{error}) { + xCAT::MsgUtils->report_node_error($callback, $_, $invalidosimghash->{$tmpimage}->{error}->[0]); + } } - - #if any node with inappropriate noderes.netboot,report the error and return - foreach my $tmpimage (keys %$invalidosimghash) { - - #$errored =1; - $callback->({ warning => [ join(",", @{ $invalidosimghash->{$tmpimage}->{nodes} }) . ": $curnetboot might be invalid when provisioning $tmpimage,valid options: \"$invalidosimghash->{$tmpimage}->{netboot}\". \nFor more details see the 'netboot' description in the output of \"tabdump -d noderes\"." ] }); + my $netbootwarn = $invalidosimghash->{$tmpimage}->{netboot}; + if ($netbootwarn) { + my $rsp; + $rsp->{warning}->[0] = join(",", @fnodes) . ": $curnetboot might be invalid when provisioning $tmpimage,valid options: \"$netbootwarn\". \nFor more details see the 'netboot' description in the output of \"tabdump -d noderes\"."; + $callback->($rsp); } + } - if ("$errored" ne "0") { - return; - } + # upddate DB for the nodes which pass the checking + foreach my $tmpimage (keys %$updatestuff) { + my $updateattribs = $updatestuff->{$tmpimage}; + my @tmpnodelist = @{ $updateattribs->{nodes} }; - #$req->{node}=(); - #push(@{$req->{node}},@validnodes); - #print Dumper($req->{node}); + delete $updateattribs->{nodes}; #not needed for nodetype table + delete $updateattribs->{state}; #node needed for nodetype table + $nodetypetable->setNodesAttribs(\@tmpnodelist, $updateattribs); } } + + if (%state_hash) { # To valide mac here + my @tempnodes = keys(%state_hash); + my $mactab = xCAT::Table->new('mac', -create => 1); + my $machash = $mactab->getNodesAttribs(\@tempnodes, ['mac']); + + foreach (@tempnodes) { + my $macs = $machash->{$_}->[0]; + unless ($macs and $macs->{mac}) { + $failurenodes{$_} = 1; + xCAT::MsgUtils->report_node_error($callback, $_, "No MAC address available for this node"); + delete $state_hash{$_}; + } + } + } + } + + #print Dumper(\%state_hash); + my @validnodes = keys(%state_hash); + unless (@validnodes) { + # just return if no valid nodes left + $callback->({ errorcode => [1]}); + return; } #if the postscripts directory exists then make sure it is # world readable and executable by root; otherwise wget fails my $installdir = xCAT::TableUtils->getInstallDir(); my $postscripts = "$installdir/postscripts"; - if (-e $postscripts) - { + if (-e $postscripts) { my $cmd = "chmod -R a+r $postscripts"; xCAT::Utils->runcmd($cmd, 0); my $rsp = {}; if ($::RUNCMD_RC != 0) { $callback->({ info => "$cmd failed" }); - } - } - #print Dumper($req); - # if precreatemypostscripts=1, create each mypostscript for each node + # 3, if precreatemypostscripts=1, create each mypostscript for each valid node # otherwise, create it during installation /updatenode my $notmpfiles = 0; # create tmp files if precreate=0 my $nofiles = 0; # create files, do not return array + my $reqcopy = {%$req}; + $reqcopy->{node} = \@validnodes; require xCAT::Postage; - xCAT::Postage::create_mypostscript_or_not($request, $callback, $subreq, $notmpfiles, $nofiles); + xCAT::Postage::create_mypostscript_or_not($reqcopy, $callback, $subreq, $notmpfiles, $nofiles); + # 4, Issue the sub-request for each state in 'state_hash' my %state_hash1; foreach my $tmpnode (keys(%state_hash)) { push @{ $state_hash1{ $state_hash{$tmpnode} } }, $tmpnode; } - #print Dumper(%state_hash); - #print Dumper(%state_hash1); + #print Dumper(\%state_hash1); foreach my $tempstate (keys %state_hash1) { my $samestatenodes = $state_hash1{$tempstate}; #print "state=$tempstate nodes=@$samestatenodes\n"; - xCAT::MsgUtils->trace($verbose_on_off, "d", "destiny->process_request: issue mk$tempstate request"); + xCAT::MsgUtils->trace($verbose_on_off, "d", "destiny->setdestiny: issue mk$tempstate request"); $errored = 0; $subreq->({ command => ["mk$tempstate"], node => $samestatenodes, @@ -417,32 +500,46 @@ sub setdestiny { bootparams => \$bphash}, \&relay_response); if ($errored) { # The error messeage for mkinstall/mknetboot/mkstatelite had been output within relay_response function above, don't need to output more - xCAT::MsgUtils->trace($verbose_on_off, "d", "destiny->process_request: Failed in processing mk$tempstate. Processing will not continue."); - return; + xCAT::MsgUtils->trace($verbose_on_off, "d", "destiny->setdestiny: Failed in processing mk$tempstate."); + next if ($errored > 1); } my $ntents = $nodetypetable->getNodesAttribs($samestatenodes, [qw(os arch profile)]); my $updates; foreach (@{$samestatenodes}) { + next if (exists($failurenodes{$_})); #Filter the failure nodes + $nstates{$_} = $tempstate; #local copy of state variable for mod my $ntent = $ntents->{$_}->[0]; #$nodetype->getNodeAttribs($_,[qw(os arch profile)]); if ($tempstate ne "winshell") { if ($ntent and $ntent->{os}) { $nstates{$_} .= " " . $ntent->{os}; - } else { $errored = 1; $callback->({ errorcode => [1], error => "nodetype.os not defined for $_" }); } + } else { + xCAT::MsgUtils->report_node_error($callback, $_, "nodetype.os not defined for $_."); + $failurenodes{$_} = 1; + next; + } } else { $nstates{$_} .= " winpe"; } if ($ntent and $ntent->{arch}) { $nstates{$_} .= "-" . $ntent->{arch}; - } else { $errored = 1; $callback->({ errorcode => [1], error => "nodetype.arch not defined for $_" }); } + } else { + xCAT::MsgUtils->report_node_error($callback, $_, "nodetype.arch not defined for $_."); + $failurenodes{$_} = 1; + next; + } + if (($tempstate ne "winshell") && ($tempstate ne "sysclone")) { if ($ntent and $ntent->{profile}) { $nstates{$_} .= "-" . $ntent->{profile}; - } else { $errored = 1; $callback->({ errorcode => [1], error => "nodetype.profile not defined for $_" }); } + } else { + xCAT::MsgUtils->report_node_error($callback, $_, "nodetype.profile not defined for $_."); + $failurenodes{$_} = 1; + next; + } } - if ($errored) { return; } $updates->{$_}->{'currchain'} = "boot"; } unless ($tempstate =~ /^netboot|^statelite/) { @@ -461,6 +558,37 @@ sub setdestiny { } } } elsif ($state eq "shell" or $state eq "standby" or $state =~ /^runcmd/ or $state =~ /^runimage/) { + + if ($state =~ /^runimage/) { # try to check the existence of the image for runimage + + my @runimgcmds; + push @runimgcmds, $state; + if ($reststates) { + my @rstates = split(/,/, $reststates); + foreach (@rstates) { + if (/^runimage/) { + push @runimgcmds, $_; + } + } + } + + foreach (@runimgcmds) { + my (undef, $path) = split(/=/, $_); + if ($path) { + if ($path =~ /\$/) { next; } # Ignore the path with including variable like $xcatmaster + my $cmd = "wget --spider --timeout 3 --tries=1 $path"; + my @output = xCAT::Utils->runcmd("$cmd", -1); + unless (grep /^Remote file exists/, @output) { + $callback->({ error => ["Cannot get $path with wget. Could you confirm it's downloadable by wget?"], errorcode => [1], errorabort => [1]}); + return; + } + } else { + $callback->({ error => "An image path should be specified to runimage.", errorcode => [1], errorabort => [1] }); + return; + } + } + } + $restab = xCAT::Table->new('noderes', -create => 1); my $nodetype = xCAT::Table->new('nodetype'); @@ -480,8 +608,9 @@ sub setdestiny { foreach (@nodes) { my $ent = $enthash->{$_}->[0]; #$nodetype->getNodeAttribs($_,[qw(arch)]); unless ($ent and $ent->{arch}) { - $callback->({ error => ["No archictecture defined in nodetype table for $_"], errorcode => [1] }); - return; + $failurenodes{$_} = 1; + xCAT::MsgUtils->report_node_error($callback, $_, "No archictecture defined in nodetype table for the node."); + next; } my $arch = $ent->{arch}; if ($arch eq "ppc64le" or $arch eq "ppc64el") { @@ -510,6 +639,11 @@ sub setdestiny { $master = $master_entry; } } + unless ($master) { + xCAT::MsgUtils->report_node_error($callback, $_, "No master in site table nor noderes table for the node."); + $failurenodes{$_} = 1; + next; + } $ent = $hments->{$_}->[0]; #$nodehm->getNodeAttribs($_,['serialport','serialspeed','serialflow']); if ($ent and defined($ent->{serialport})) { @@ -521,8 +655,9 @@ sub setdestiny { #$ent = $nodehm->getNodeAttribs($_,['serialspeed']); unless ($ent and defined($ent->{serialspeed})) { - $callback->({ error => ["Serial port defined in noderes, but no nodehm.serialspeed set for $_"], errorcode => [1] }); - return; + xCAT::MsgUtils->report_node_error($callback, $_, "serialport defined, but no serialspeed for this node in nodehm table"); + $failurenodes{$_} = 1; + next; } $kcmdline .= "," . $ent->{serialspeed}; @@ -530,10 +665,6 @@ sub setdestiny { $kcmdline .= " "; } - unless ($master) { - $callback->({ error => ["No master in site table nor noderes table for $_"], errorcode => [1] }); - return; - } my $xcatdport = "3001"; if (defined($port_entry)) { $xcatdport = $port_entry; @@ -563,39 +694,21 @@ sub setdestiny { } } - # try to check the existence of the image for runimage - my @runimgcmds; - if ($state =~ /^runimage/) { - push @runimgcmds, $state; - } - if ($reststates) { - my @rstates = split(/,/, $reststates); - foreach (@rstates) { - if (/^runimage/) { - push @runimgcmds, $_; - } - } - } - - foreach (@runimgcmds) { - my (undef, $path) = split(/=/, $_); - if ($path) { - if ($path =~ /\$/) { next; } # Ignore the path with including variable like $xcatmaster - my $cmd = "wget --spider --timeout 3 --tries=1 $path"; - my @output = xCAT::Utils->runcmd("$cmd", -1); - unless (grep /^Remote file exists/, @output) { - $callback->({ error => ["Cannot get $path with wget. Could you confirm it's downloadable by wget?"], errorcode => [1] }); - return; - } - } else { - $callback->({ error => "An image path should be specified to runnimage.", errorcode => [1] }); - return; - } - } } elsif ($state eq "offline" || $state eq "shutdown") { 1; } elsif (!($state eq "boot")) { $callback->({ error => ["Unknown state $state requested"], errorcode => [1] }); + exit(1); + } + + #Exclude the failure nodes + my @normalnodes = (); + foreach (@nodes) { + next if (exists($failurenodes{$_})); #Filter the failure nodes + push @normalnodes, $_; + } + + unless (@normalnodes) { return; } @@ -603,10 +716,10 @@ sub setdestiny { if ($state eq "iscsiboot" or $state eq "boot") { my $nodetype = xCAT::Table->new('nodetype', -create => 1); my $osimagetab = xCAT::Table->new('osimage', -create => 1); - my $ntents = $nodetype->getNodesAttribs($req->{node}, [qw(os arch profile provmethod)]); + my $ntents = $nodetype->getNodesAttribs(\@normalnodes, [qw(os arch profile provmethod)]); my @nodestoblank = (); my %osimage_hash = (); - foreach (@{ $req->{node} }) { + foreach (@normalnodes) { my $ntent = $ntents->{$_}->[0]; #if the previous nodeset staute is not install, then blank nodetype.provmethod @@ -643,7 +756,8 @@ sub setdestiny { if ($noupdate) { return; } #skip table manipulation if just doing 'enact' my $updates; - foreach (@nodes) { + foreach (@normalnodes) { + my $lstate = $state; if ($nstates{$_}) { $lstate = $nstates{$_}; @@ -657,7 +771,7 @@ sub setdestiny { } } $chaintab->setNodesAttribs($updates); - return getdestiny($flag + 1); + return getdestiny($flag + 1, \@normalnodes); } @@ -684,16 +798,16 @@ sub nextdestiny { $node = $request->{username}->[0]; } else { unless ($request->{'_xcat_clienthost'}->[0]) { - #ERROR? malformed request + xCAT::MsgUtils->trace(0, "d", "destiny->nextdestiny: Cannot determine the client from the received request"); return; #nothing to do here... } $node = $request->{'_xcat_clienthost'}->[0]; } ($node) = noderange($node); unless ($node) { - #not a node, don't trust it + xCAT::MsgUtils->trace(0, "d", "destiny->nextdestiny: $node is not managed yet."); return; } @nodes = ($node); @@ -757,40 +871,22 @@ sub nextdestiny { sub getdestiny { my $flag = shift; + my $nodes = shift; # flag value: # 0--getdestiny is called by dodestiny # 1--called by nextdestiny in dodestiny. The node calls nextdestiny before boot and runcmd. # 2--called by nodeset command # 3--called by updateflag after the node finished installation and before booting - my @args; - my @nodes; - if ($request->{node}) { - if (ref($request->{node})) { - @nodes = @{ $request->{node} }; - } else { - @nodes = ($request->{node}); - } - } else { # a client asking for it's own destiny. - unless ($request->{'_xcat_clienthost'}->[0]) { - $callback->({ destiny => ['discover'] }); - return; - } - my ($node) = noderange($request->{'_xcat_clienthost'}->[0]); - unless ($node) { # it had a valid hostname, but isn't a node - $callback->({ destiny => ['discover'] }); - return; - } - @nodes = ($node); - } + my $node; xCAT::MsgUtils->trace(0, "d", "destiny->process_request: getdestiny..."); $restab = xCAT::Table->new('noderes'); my $chaintab = xCAT::Table->new('chain'); - my $chainents = $chaintab->getNodesAttribs(\@nodes, [qw(currstate chain)]); - my $nrents = $restab->getNodesAttribs(\@nodes, [qw(tftpserver xcatmaster)]); + my $chainents = $chaintab->getNodesAttribs($nodes, [qw(currstate chain)]); + my $nrents = $restab->getNodesAttribs($nodes, [qw(tftpserver xcatmaster)]); my $bptab = xCAT::Table->new('bootparams', -create => 1); - my $bpents = $bptab->getNodesAttribs(\@nodes, [qw(kernel initrd kcmdline xcatmaster)]); + my $bpents = $bptab->getNodesAttribs($nodes, [qw(kernel initrd kcmdline xcatmaster)]); #my $sitetab= xCAT::Table->new('site'); #(my $sent) = $sitetab->getAttribs({key=>'master'},'value'); @@ -798,7 +894,7 @@ sub getdestiny { my $master_value = $entries[0]; my %node_status = (); - foreach $node (@nodes) { + foreach $node (@$nodes) { unless ($chaintab) { #Without destiny, have the node wait with ssh hopefully open at least $callback->({ node => [ { name => [$node], data => ['standby'], destiny => ['standby'] } ] }); return; diff --git a/xCAT-server/lib/xcat/plugins/grub2.pm b/xCAT-server/lib/xcat/plugins/grub2.pm index 8906d15f6..705792153 100644 --- a/xCAT-server/lib/xcat/plugins/grub2.pm +++ b/xCAT-server/lib/xcat/plugins/grub2.pm @@ -14,8 +14,6 @@ use Getopt::Long; use xCAT::Table; use xCAT::Usage; my $request; -my %breaknetbootnodes; -our %normalnodes; my %tftpserverip; my $callback; my $sub_req; @@ -102,18 +100,14 @@ sub setstate { my $tftpdir = shift; my %nrhash = %{ shift() }; my $linuximghash = shift(); + my $nodearch = shift; my $kern = $bphash{$node}->[0]; #$bptab->getNodeAttribs($node,['kernel','initrd','kcmdline']); if ($kern->{kcmdline} =~ /!myipfn!/) { my $ipfn; my @ipfnd = xCAT::NetworkUtils->my_ip_facing($node); if ($ipfnd[0] == 1) { - $::callback->( - { - error => [ $ipfnd[1] ], - errorcode => [1] - }); - return; + return (1, $ipfnd[1]); } elsif ($ipfnd[0] == 2) { my $servicenodes = $nrhash{$node}->[0]; @@ -123,27 +117,11 @@ sub setstate { # We are in the service node pools, print error if no facing ip. if (xCAT::InstUtils->is_me($sn)) { - $::callback->( - { - error => [ - "$::myxcatname: $ipfnd[1] on service node $sn" - ], - errorcode => [1] - } - ); - return; + return (1, "$::myxcatname: $ipfnd[1] on service node $sn"); } } } else { - $::callback->( - { - error => [ - "$::myxcatname: $ipfnd[1]" - ], - errorcode => [1] - } - ); - return; + return (1, "$::myxcatname: $ipfnd[1]"); } } else { $ipfn = $ipfnd[1]; @@ -178,15 +156,17 @@ sub setstate { unless (-d "$bootloader_root") { mkpath("$bootloader_root"); } - my $nodemac; - my %client_nethash = xCAT::DBobjUtils->getNetwkInfo([$node]); - my $cref = $chainhash{$node}->[0]; #$chaintab->getNodeAttribs($node,['currstate']); + my $cref = $chainhash{$node}->[0]; + unless ($cref->{currstate}) { # the currstate should be set during 'setdestiny' + return (1, "Cannot determine current state for this node"); + } # remove the old boot configuration files and create a new one, but only if not offline directive system("find $bootloader_root/ -inum \$(stat --printf \%i $bootloader_root/$node 2>/dev/null) -exec rm -f {} \\; 2>/dev/null"); + my $pcfg; - if ($cref and $cref->{currstate} ne "offline") { + if ($cref->{currstate} ne "offline") { open($pcfg, '>', "$bootloader_root/" . $node); print $pcfg "#" . $cref->{currstate} . "\n"; @@ -197,15 +177,7 @@ sub setstate { print $pcfg "set timeout=5\n"; } - $normalnodes{$node} = 1; #Assume a normal netboot (well, normal dhcp, - #which is normally with a valid 'filename' field, - #but the typical ppc case will be 'special' makedhcp - #to clear the filename field, so the logic is a little - #opposite - - if ($cref and $cref->{currstate} eq "boot") { - $breaknetbootnodes{$node} = 1; - delete $normalnodes{$node}; #Signify to omit this from one makedhcp command + if ($cref->{currstate} eq "boot") { close($pcfg); } elsif ($kern and $kern->{kernel}) { @@ -226,26 +198,24 @@ sub setstate { my @nxtsrvd = xCAT::NetworkUtils->my_ip_facing($node); unless ($nxtsrvd[0]) { $serverip = $nxtsrvd[1]; - } else { - $callback->({ error => [ $nxtsrvd[1] ], errorcode => [1] }); - return; + } else { + return (1, $nxtsrvd[1]); } - }else{ + } else { if (defined($tftpserverip{$tftpserver})) { $serverip = $tftpserverip{$tftpserver}; } else { $serverip = xCAT::NetworkUtils->getipaddr($tftpserver); unless ($serverip) { - syslog("local1|err", "xCAT unable to resolve $tftpserver"); - return; + return (1, "xCAT unable to resolve $tftpserver"); } $tftpserverip{$tftpserver} = $serverip; } } unless($serverip){ - $callback->({ error => ["Unable to determine the tftpserver for $node"], errorcode => [1] }); - return; + close($pcfg); + return (1, "Unable to determine the tftpserver for $node"); } my $grub2protocol = "tftp"; if (defined($nrhash{$node}->[0]) && $nrhash{$node}->[0]->{'netboot'} @@ -254,15 +224,8 @@ sub setstate { } unless ($grub2protocol =~ /^http|tftp$/) { - $::callback->( - { - error => [ -"Invalid netboot method, please check noderes.netboot for $node" - ], - errorcode => [1] - } - ); - return; + close($pcfg); + return (1, "Invalid netboot method, please check noderes.netboot for $node"); } # write entries to boot config file, but only if not offline directive @@ -296,26 +259,43 @@ sub setstate { } else { close($pcfg); } + + unless ($nodearch) { + return (1, "No archictecture defined in nodetype table for the node."); + } + if ($nodearch =~ /ppc64/i) { + $nodearch = "ppc" + } + my $grub2bin = "$bootloader_root/grub2." . $nodearch; + unless (-e "$grub2bin") { + return (1, "Stop grub2 configuration for this node, \"$grub2bin\" does not exits."); + } + + chdir("$bootloader_root"); + if ($cref->{currstate} eq "offline" or $cref->{currstate} eq "boot") { + unlink("grub2-$node"); + } elsif (! -e "grub2-$node") { + symlink("grub2." . $nodearch, "grub2-$node"); + } + my $ip = xCAT::NetworkUtils->getipaddr($node); unless ($ip) { - syslog("local1|err", "xCAT unable to resolve IP in grub2 plugin"); - return; + return (1, "xCAT unable to resolve IP in grub2 plugin"); } - my $mactab = xCAT::Table->new('mac'); + #my $mactab = xCAT::Table->new('mac'); my %ipaddrs; my $macstring; $ipaddrs{$ip} = 1; - if ($mactab) { - my $ment = $machash{$node}->[0]; #$mactab->getNodeAttribs($node,['mac']); - if ($ment and $ment->{mac}) { - $macstring = $ment->{mac}; - my @macs = split(/\|/, $ment->{mac}); - foreach (@macs) { - if (/!(.*)/) { - my $ipaddr = xCAT::NetworkUtils->getipaddr($1); - if ($ipaddr) { - $ipaddrs{$ipaddr} = 1; - } + + my $ment = $machash{$node}->[0]; + if ($ment and $ment->{mac}) { + $macstring = $ment->{mac}; + my @macs = split(/\|/, $ment->{mac}); + foreach (@macs) { + if (/!(.*)/) { + my $ipaddr = xCAT::NetworkUtils->getipaddr($1); + if ($ipaddr) { + $ipaddrs{$ipaddr} = 1; } } } @@ -329,11 +309,10 @@ sub setstate { # remove the old boot configuration file and copy (link) a new one, but only if not offline directive unlink("$bootloader_root/" . $pname); - if ($cref and $cref->{currstate} ne "offline") { - link("$bootloader_root/" . $node, "$bootloader_root/" . $pname); - } + link("$bootloader_root/" . $node, "$bootloader_root/" . $pname) if ($cref->{currstate} ne "offline"); } - + + my $nodemac; my $nrent=$nrhash{$node}->[0]; if($nrent and $nrent->{installnic}){ my $myinstallnic=$nrent->{installnic}; @@ -341,8 +320,6 @@ sub setstate { $nodemac=$myinstallnic; } } - - if (! $nodemac and $macstring) { $nodemac = xCAT::Utils->parseMacTabEntry($macstring, $node); } @@ -354,27 +331,43 @@ sub setstate { # remove the old boot configuration file and copy (link) a new one, but only if not offline directive unlink("$bootloader_root/" . $pname); - if ($cref and $cref->{currstate} ne "offline") { - link("$bootloader_root/" . $node, "$bootloader_root/" . $pname); - } + link("$bootloader_root/" . $node, "$bootloader_root/" . $pname) if ($cref->{currstate} ne "offline"); } - return; + return (0, ""); } my $errored = 0; sub pass_along { my $resp = shift; + return unless ($resp); - $callback->($resp); - if ($resp and ($resp->{errorcode} and $resp->{errorcode}->[0]) or ($resp->{error} and $resp->{error}->[0])) { - $errored = 1; + my $failure = 0; + if ($resp->{errorabort}) { # Global error, it normally means to stop the parent execution. For example, DB operation error. + $failure = 2; + delete $resp->{errorabort}; + } elsif (($resp->{errorcode} and $resp->{errorcode}->[0]) or ($resp->{error} and $resp->{error}->[0])) { + $failure = 1; } + $callback->($resp); + + if ($failure > 1) { # quick abort + $errored = $failure; + return; + } + + # Partial error on nodes, it allows to continue the rest of business on the sucessful nodes. foreach (@{ $resp->{node} }) { if ($_->{error} or $_->{errorcode}) { - $errored = 1; + $failure = 1; + if ($_->{name}) { + $failurenodes{$_->{name}->[0]} = 2; + } } } + if ( $failure ) { + $errored = $failure; + } } @@ -468,7 +461,7 @@ sub preprocess_request { my %rsp; $rsp{errorcode}->[0] = 1; $rsp{error}->[0] = -"Nodeset was run with a noderange containing both service nodes and compute nodes. This is not valid. You must submit with either compute nodes in the noderange or service nodes. \n"; + "Nodeset was run with a noderange containing both service nodes and compute nodes. This is not valid. You must submit with either compute nodes in the noderange or service nodes. \n"; $callback1->(\%rsp); return; } @@ -517,11 +510,8 @@ sub preprocess_request { sub process_request { $request = shift; $callback = shift; - $::callback = $callback; $sub_req = shift; my $command = $request->{command}->[0]; - %breaknetbootnodes = (); - %normalnodes = (); # It will be fill-up by method: setstate. my @args; #>>>>>>>used for trace log start>>>>>>> @@ -661,11 +651,8 @@ sub process_request { arg => [ $args[0] ] }, \&pass_along); } if ($errored) { - my $rsp; - $rsp->{errorcode}->[0] = 1; - $rsp->{error}->[0] = "Failed in running begin prescripts.\n"; - $callback->($rsp); - return; + xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: Failed in running begin prescripts."); + return if ($errored > 1); } } @@ -685,8 +672,8 @@ sub process_request { bootparams => \%bphash }, \&pass_along); if ($errored) { - xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: Failed in processing setdestiny. Processing will not continue."); - return; + xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: Failed in processing setdestiny."); + return if ($errored > 1); } } @@ -707,156 +694,62 @@ sub process_request { my $rc; my $errstr; - my $tftpdir; + my @normalnodeset = (); foreach (@nodes) { - my %response; + next if (exists($failurenodes{$_})); + + my $tftpdir = $globaltftpdir; if ($nodereshash->{$_} and $nodereshash->{$_}->[0] and $nodereshash->{$_}->[0]->{tftpdir}) { $tftpdir = $nodereshash->{$_}->[0]->{tftpdir}; - } else { - $tftpdir = $globaltftpdir; } + + my %response; $response{node}->[0]->{name}->[0] = $_; if ($args[0]) { # Send it on to the destiny plugin, then setstate my $ent = $typehash->{$_}->[0]; + my $nodearch = $ent->{'arch'}; my $osimgname = $ent->{'provmethod'}; my $linuximghash = undef; unless ($osimgname =~ /^(install|netboot|statelite)$/) { $linuximghash = $linuximgtab->getAttribs({ imagename => $osimgname }, 'boottarget', 'addkcmdline'); } - ($rc, $errstr) = setstate($_, \%bphash, $chainhash, $machash, $tftpdir, $nrhash, $linuximghash); + ($rc, $errstr) = setstate($_, \%bphash, $chainhash, $machash, $tftpdir, $nrhash, $linuximghash, $nodearch); if ($rc) { $response{node}->[0]->{errorcode}->[0] = $rc; - $response{node}->[0]->{errorc}->[0] = $errstr; + $response{node}->[0]->{error}->[0] = $errstr; + $failurenodes{$_} = 1; $callback->(\%response); + } else { + push @normalnodeset, $_; } + } } # end of foreach node xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: Finish to handle configurations"); - my @normalnodeset = keys %normalnodes; - my @breaknetboot = keys %breaknetbootnodes; - - my %osimagenodehash; - for my $nn (@normalnodeset) { - - #record the os version for node - my $ent = $typehash->{$nn}->[0]; - my $osimage = $ent->{'provmethod'}; - if ($osimage =~ /^(install|netboot|statelite)$/) { - $osimage = ($ent->{'os'}) . '-' . ($ent->{'arch'}) . '-' . ($ent->{'provmethod'}) . '-' . ($ent->{'profile'}); - } - push @{ $osimagenodehash{$osimage} }, $nn; - } - - my $do_dhcpsetup = 1; - my @entries = xCAT::TableUtils->get_site_attribute("dhcpsetup"); - my $t_entry = $entries[0]; - if (defined($t_entry)) { - if ($t_entry =~ /0|n|N/) { $do_dhcpsetup = 0; } - } - #Don't bother to try dhcp binding changes if sub_req not passed, i.e. service node build time - unless (($inittime) || ($args[0] eq 'offline')) { - foreach my $osimage (keys %osimagenodehash) { + unless ($inittime) { - #TOTO check the existence of grub2 executable files for corresponding arch - my $osimgent = $osimagetab->getAttribs({ imagename => $osimage }, 'osarch'); - my $validarch = undef; - if ($osimgent and $osimgent->{'osarch'}) - { - $validarch = $osimgent->{'osarch'}; - } - else - { - # Can not determine arch from osimage definition. This is most likely - # the case when nodeset is "shell" or "shutdown". Look at node definition, to - # figure out arch to use. - - # get nodename from osimagenodehash hash - my $node_name = $osimagenodehash{$osimage}[0]; - - # lookup node arch setting - my $node_entry = $typetab->getNodeAttribs($node_name, ['arch']); - if ($node_entry and $node_entry->{'arch'}) - { - # Extracted arch from node definition - $validarch = $node_entry->{'arch'}; - } - else - { - # At this point we can not determine architecture either - # from osimage or node definition. - my $rsp; - push @{ $rsp->{data} }, "Not able to determine architecture of node $node_name. Verify arch attribute setting.\n"; - xCAT::MsgUtils->message("E", $rsp, $callback); - } - } - if ($validarch =~ /ppc64/i) - { - $validarch = "ppc" - } - my $grub2 = "/boot/grub2/grub2." . $validarch; - my $tftppath = $tftpdir . $grub2; - unless (-e "$tftppath") { - my $rsp; - push @{ $rsp->{data} }, - "stop configuration, missing $tftppath.\n"; - xCAT::MsgUtils->message("E", $rsp, $callback); - return; - } - chdir("$tftpdir/boot/grub2/"); - foreach my $tmp_node (@{ $osimagenodehash{$osimage} }) { - unless (-e "grub2-$tmp_node") { - symlink("grub2." . $validarch, "grub2-$tmp_node"); - } - } - if ($do_dhcpsetup) { - if ($request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command - xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue makedhcp request"); - $sub_req->({ command => ['makedhcp'], - node => \@{ $osimagenodehash{$osimage} }, arg => ['-l'] }, $callback); - } else { - xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue makedhcp request"); - $sub_req->({ command => ['makedhcp'], - node => \@{ $osimagenodehash{$osimage} } }, $callback); - } - } - } #end of foreach osimagenodehash - - foreach my $tmp_node (@breaknetboot) { - if (-e "$tftpdir/boot/grub2/grub2-$tmp_node") { - unlink("$tftpdir/boot/grub2/grub2-$tmp_node"); - } + #dhcp stuff + my $do_dhcpsetup = 1; + my @entries = xCAT::TableUtils->get_site_attribute("dhcpsetup"); + my $t_entry = $entries[0]; + if (defined($t_entry)) { + if ($t_entry =~ /0|n|N/) { $do_dhcpsetup = 0; } } - if ($do_dhcpsetup) { - if ($request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command - xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue makedhcp request"); - $sub_req->({ command => ['makedhcp'], - node => \@breaknetboot, - arg => ['-l'] }, $callback); - } else { - xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue makedhcp request"); - $sub_req->({ command => ['makedhcp'], - node => \@breaknetboot }, $callback); - } - } - } + # For offline operation, remove the dhcp entries whatever dhcpset is disabled in site ( existing code logic, just keep it as is) + if ($do_dhcpsetup || $args[0] eq 'offline') { + my @parameter; + push @parameter, '-l' if ($request->{'_disparatetftp'}->[0]); + push @parameter, '-d' if ($args[0] eq 'offline'); + xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue makedhcp request"); - if ($args[0] eq 'offline') { - my @rmdhcp_nodes; - - # If nodeset directive was offline we need to remove the architecture file link and remove dhcp entries - foreach my $osimage (keys %osimagenodehash) { - foreach my $tmp_node (@{ $osimagenodehash{$osimage} }) { - unlink("$tftpdir/boot/grub2/grub2-$tmp_node"); - push(@rmdhcp_nodes, $tmp_node); - } - } - if ($request->{'_disparatetftp'}->[0]) { - $sub_req->({ command => ['makedhcp'], arg => ['-d', '-l'], node => \@rmdhcp_nodes }, $callback); + $sub_req->({ command => ['makedhcp'], + arg => \@parameter, + node => \@normalnodeset }, $callback); } else { - $sub_req->({ command => ['makedhcp'], arg => ['-d'], node => \@rmdhcp_nodes }, $callback); + xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: dhcpsetup=$do_dhcpsetup"); } } @@ -866,20 +759,16 @@ sub process_request { if ($request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handles my own children xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue runendpre request"); $sub_req->({ command => ['runendpre'], - node => \@nodes, + node => \@normalnodeset, arg => [ $args[0], '-l' ] }, \&pass_along); } else { #nodeset did not distribute to the service node, here we need to let runednpre to distribute the nodes to their masters xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue runendpre request"); $sub_req->({ command => ['runendpre'], - node => \@rnodes, + node => \@normalnodeset, arg => [ $args[0] ] }, \&pass_along); } if ($errored) { - my $rsp; - $rsp->{errorcode}->[0] = 1; - $rsp->{error}->[0] = "Failed in running end prescripts\n"; - $callback->($rsp); - return; + xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: Failed in running end prescripts."); } } diff --git a/xCAT-server/lib/xcat/plugins/petitboot.pm b/xCAT-server/lib/xcat/plugins/petitboot.pm index a307fab3a..28287b14a 100644 --- a/xCAT-server/lib/xcat/plugins/petitboot.pm +++ b/xCAT-server/lib/xcat/plugins/petitboot.pm @@ -89,8 +89,7 @@ sub setstate { my $tftpdir = shift; my %nrhash = %{ shift() }; my $linuximghash = shift(); - my $kern = $bphash{$node}->[0]; #$bptab->getNodeAttribs($node,['kernel','initrd','kcmdline']); - #my $nodereshash=$noderestab->getNodesAttribs(\@nodes,['tftpdir','xcatmaster','nfsserver', 'servicenode']); + my $kern = $bphash{$node}->[0]; if ($kern->{kernel} !~ /^$tftpdir/) { my $nodereshash = $nrhash{$node}->[0]; @@ -110,12 +109,7 @@ sub setstate { my @ipfnd = xCAT::NetworkUtils->my_ip_facing($node); if ($ipfnd[0] == 1) { - $::callback->( - { - error => [ $ipfnd[1] ], - errorcode => [1] - }); - return; + return (1, $ipfnd[1]); } elsif ($ipfnd[0] == 2) { my $servicenodes = $nrhash{$node}->[0]; @@ -125,27 +119,11 @@ sub setstate { # We are in the service node pools, print error if no facing ip. if (xCAT::InstUtils->is_me($sn)) { - $::callback->( - { - error => [ - "$::myxcatname: $ipfnd[1] on service node $sn" - ], - errorcode => [1] - } - ); - return; + return (1, "$::myxcatname: $ipfnd[1] on service node $sn"); } } } else { - $::callback->( - { - error => [ - "$::myxcatname: $ipfnd[1]" - ], - errorcode => [1] - } - ); - return; + return (1, "$::myxcatname: $ipfnd[1]"); } } else { $ipfn = $ipfnd[1]; @@ -172,9 +150,11 @@ sub setstate { unless (-d "$bootloader_root") { mkpath("$bootloader_root"); } - my $nodemac; - my $cref = $chainhash{$node}->[0]; #$chaintab->getNodeAttribs($node,['currstate']); + my $cref = $chainhash{$node}->[0]; + unless ($cref->{currstate}) { # the currstate should be set during 'setdestiny' + return (1, "Cannot determine current state for this node"); + } my $pcfg; # remove the old boot configuration file and create a new one, but only if not offline directive @@ -183,21 +163,8 @@ sub setstate { open($pcfg, '>', "$bootloader_root/" . $node); print $pcfg "#" . $cref->{currstate} . "\n"; } - $normalnodes{$node} = 1; #Assume a normal netboot (well, normal dhcp, - #which is normally with a valid 'filename' field, - #but the typical ppc case will be 'special' makedhcp - #to clear the filename field, so the logic is a little - #opposite - # $sub_req->({command=>['makedhcp'], #This is currently batched elswhere - # node=>[$node]},$callback); #It hopefully will perform correctly if ($cref and $cref->{currstate} eq "boot") { - $breaknetbootnodes{$node} = 1; - delete $normalnodes{$node}; #Signify to omit this from one makedhcp command - #$sub_req->({command=>['makedhcp'], #batched elsewhere, this code is stale, hopefully - # node=>[$node], - # arg=>['-s','filename = \"xcat/nonexistant_file_to_intentionally_break_netboot_for_localboot_to_work\";']},$callback); - #print $pcfg "bye\n"; close($pcfg); } elsif ($kern and $kern->{kernel} and $cref and $cref->{currstate} ne "offline") { @@ -222,8 +189,7 @@ sub setstate { } my $ip = xCAT::NetworkUtils->getipaddr($node); unless ($ip) { - syslog("local1|err", "xCAT unable to resolve IP for $node in petitboot plugin"); - return; + return (1, "xCAT unable to resolve IP for $node in petitboot plugin."); } my @ipa = split(/\./, $ip); @@ -235,30 +201,44 @@ sub setstate { if ($cref and $cref->{currstate} ne "offline") { link("$bootloader_root/" . $node, "$tftpdir/" . $pname); } - return; + return (0, ""); } - my $errored = 0; sub pass_along { my $resp = shift; + return unless ($resp); - # print Dumper($resp); - - $callback->($resp); - if ($resp and ($resp->{errorcode} and $resp->{errorcode}->[0]) or ($resp->{error} and $resp->{error}->[0])) { - $errored = 1; + my $failure = 0; + if ($resp->{errorabort}) { # Global error, it normally means to stop the parent execution. For example, DB operation error. + $failure = 2; + delete $resp->{errorabort}; + } elsif (($resp->{errorcode} and $resp->{errorcode}->[0]) or ($resp->{error} and $resp->{error}->[0])) { + $failure = 1; } + $callback->($resp); + + if ($failure > 1) { # quick abort + $errored = $failure; + return; + } + + # Partial error on nodes, it allows to continue the rest of business on the sucessful nodes. foreach (@{ $resp->{node} }) { if ($_->{error} or $_->{errorcode}) { - $errored = 1; + $failure = 1; + if ($_->{name}) { + $failurenodes{$_->{name}->[0]} = 2; + } } } + if ( $failure ) { + $errored = $failure; + } } - sub preprocess_request { my $req = shift; if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; } @@ -407,8 +387,8 @@ sub process_request { $::callback = $callback; $sub_req = shift; my $command = $request->{command}->[0]; - %breaknetbootnodes = (); - %normalnodes = (); # It will be fill-up by method: setstate. + + undef %failurenodes; #>>>>>>>used for trace log start>>>>>>> my @args = (); @@ -464,7 +444,6 @@ sub process_request { my @nodes = (); # Filter those nodes which have bad DNS: not resolvable or inconsistent IP - my %failurenodes = (); my %preparednodes = (); foreach (@rnodes) { my $ipret = xCAT::NetworkUtils->checkNodeIPaddress($_); @@ -551,11 +530,8 @@ sub process_request { arg => [ $args[0] ] }, \&pass_along); } if ($errored) { - my $rsp; - $rsp->{errorcode}->[0] = 1; - $rsp->{error}->[0] = "Failed in running begin prescripts.\n"; - $callback->($rsp); - return; + xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: Failed in running begin prescripts."); + return if ($errored > 1); } } @@ -575,8 +551,8 @@ sub process_request { bootparams => \%bphash}, \&pass_along); if ($errored) { - xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: Failed in processing setdestiny. Processing will not continue."); - return; + xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: Failed in processing setdestiny."); + return if ($errored > 1); } } @@ -589,7 +565,7 @@ sub process_request { #this does not hurt anything for other plugins environment => {XCAT_OPENBMC_DEVEL=>"YES"} }); - xCAT::MsgUtils->message("S", "xCAT: petitboot netboot: clear node(s): @nodes boot device setting."); + xCAT::MsgUtils->message("S", "xCAT: petitboot netboot: clear node(s): $str_node boot device setting."); } xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: starting to handle configuration..."); @@ -597,24 +573,25 @@ sub process_request { my $chainhash = $chaintab->getNodesAttribs(\@nodes, ['currstate']); my $noderestab = xCAT::Table->new('noderes', -create => 1); my $nodereshash = $noderestab->getNodesAttribs(\@nodes, [ 'tftpdir', 'xcatmaster', 'nfsserver', 'servicenode' ]); - my $mactab = xCAT::Table->new('mac', -create => 1); - my $machash = $mactab->getNodesAttribs(\@nodes, ['mac']); my $typetab = xCAT::Table->new('nodetype', -create => 1); my $typehash = $typetab->getNodesAttribs(\@nodes, [ 'os', 'provmethod', 'arch', 'profile' ]); my $linuximgtab = xCAT::Table->new('linuximage', -create => 1); my $osimagetab = xCAT::Table->new('osimage', -create => 1); + my %machash = (); my $rc; my $errstr; - my $tftpdir; + my @normalnodeset = (); foreach (@nodes) { - my %response; + next if (exists($failurenodes{$_})); + + my $tftpdir = $globaltftpdir; if ($nodereshash->{$_} and $nodereshash->{$_}->[0] and $nodereshash->{$_}->[0]->{tftpdir}) { $tftpdir = $nodereshash->{$_}->[0]->{tftpdir}; - } else { - $tftpdir = $globaltftpdir; } + + my %response; $response{node}->[0]->{name}->[0] = $_; if ($args[0]) { # send it on to the destiny plugin, then setstate my $ent = $typehash->{$_}->[0]; @@ -625,24 +602,16 @@ sub process_request { ($rc, $errstr) = setstate($_, \%bphash, $chainhash, $machash, $tftpdir, $nodereshash, $linuximghash); if ($rc) { $response{node}->[0]->{errorcode}->[0] = $rc; - $response{node}->[0]->{errorc}->[0] = $errstr; + $response{node}->[0]->{error}->[0] = $errstr; + $failurenodes{$_} = 1; $callback->(\%response); + } else { + push @normalnodeset, $_; } } } # end of foreach node xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: Finish to handle configurations"); - my @normalnodeset = keys %normalnodes; - my @breaknetboot = keys %breaknetbootnodes; - my %osimagenodehash; - for my $nn (@normalnodeset) { - - #record the os version for node - my $ent = $typehash->{$nn}->[0]; - my $osimage = $ent->{'provmethod'}; - push @{ $osimagenodehash{$osimage} }, $nn; - } - #Don't bother to try dhcp binding changes if sub_req not passed, i.e. service node build time unless ($inittime) { @@ -675,20 +644,16 @@ sub process_request { if ($request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handles my own children xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue runendpre request"); $sub_req->({ command => ['runendpre'], - node => \@nodes, + node => \@normalnodeset, arg => [ $args[0], '-l' ] }, \&pass_along); } else { #nodeset did not distribute to the service node, here we need to let runednpre to distribute the nodes to their masters xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue runendpre request"); $sub_req->({ command => ['runendpre'], - node => \@nodes, + node => \@normalnodeset, arg => [ $args[0] ] }, \&pass_along); } if ($errored) { - my $rsp; - $rsp->{errorcode}->[0] = 1; - $rsp->{error}->[0] = "Failed in running end prescripts\n"; - $callback->($rsp); - return; + xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: Failed in running end prescripts."); } } diff --git a/xCAT-server/lib/xcat/plugins/xnba.pm b/xCAT-server/lib/xcat/plugins/xnba.pm index 7410eeb42..7fdaec0d7 100644 --- a/xCAT-server/lib/xcat/plugins/xnba.pm +++ b/xCAT-server/lib/xcat/plugins/xnba.pm @@ -185,10 +185,28 @@ sub setstate { $pxelinuxkcmdline =~ s/!myipfn!/$ipfn/g; } } - my $pcfg; - unlink($tftpdir . "/xcat/xnba/nodes/" . $node . ".pxelinux"); - open($pcfg, '>', $tftpdir . "/xcat/xnba/nodes/" . $node); + + my $bootloader_root = "$tftpdir/xcat/xnba/nodes"; + unless (-d "$bootloader_root") { + mkpath("$bootloader_root"); + } + my $cref = $chainhash{$node}->[0]; #$chaintab->getNodeAttribs($node,['currstate']); + unless ($cref->{currstate}) { # the currstate should be set during 'setdestiny' + return (1, "Cannot determine current state for this node"); + } + + if ($cref and $cref->{currstate} eq "offline") { + unlink("$bootloader_root/$node"); + unlink("$bootloader_root/$node.pxelinux"); + unlink("$bootloader_root/$node.uefi"); + unlink("$bootloader_root/$node.elilo"); + return (0, ""); + } + + my $pcfg; + unlink("$bootloader_root/$node.pxelinux"); + open($pcfg, '>', "$bootloader_root/$node"); print $pcfg "#!gpxe\n"; if ($cref->{currstate}) { print $pcfg "#" . $cref->{currstate} . "\n"; @@ -284,30 +302,48 @@ sub setstate { print $pcfg "LOCALBOOT 0\n"; close($pcfg); } + return (0, ""); } my $errored = 0; - +my %failurenodes; sub pass_along { my $resp = shift; + return unless ($resp); + if ($resp->{error} and not ref $resp->{error}) { $resp->{error} = [ $resp->{error} ]; } - if ($resp and ($resp->{errorcode} and $resp->{errorcode}->[0]) or ($resp->{error} and $resp->{error}->[0])) { - $errored = 1; + + my $failure = 0; + if ($resp->{errorabort}) { # Global error, it normally means to stop the parent execution. For example, DB operation error. + $failure = 2; + delete $resp->{errorabort}; + } elsif (($resp->{errorcode} and $resp->{errorcode}->[0]) or ($resp->{error} and $resp->{error}->[0])) { + $failure = 1; } + foreach (@{ $resp->{node} }) { - if ($_->{error} or $_->{errorcode}) { - $errored = 1; - } if ($_->{_addkcmdlinehandled}) { $::XNBA_addkcmdlinehandled->{ $_->{name}->[0] } = 1; return; #Don't send back to client this internal hint } + + if ($_->{error} or $_->{errorcode}) { + $failure = 1 unless ($failure); # keep its value if $failure is 2 + if ($_->{name}) { + $failurenodes{$_->{name}->[0]} = 2; + } + } } $::XNBA_callback->($resp); + + # Set the module scope error flag. + if ( $failure ) { + $errored = $failure; + } } @@ -406,7 +442,7 @@ sub preprocess_request { my %rsp; $rsp{errorcode}->[0] = 1; $rsp{error}->[0] = -"Nodeset was run with a noderange containing both service nodes and compute nodes. This is not valid. You must submit with either compute nodes in the noderange or service nodes. \n"; + "Nodeset was run with a noderange containing both service nodes and compute nodes. This is not valid. You must submit with either compute nodes in the noderange or service nodes. \n"; $callback1->(\%rsp); return; } @@ -458,8 +494,8 @@ sub process_request { my $sub_req = shift; undef $::XNBA_addkcmdlinehandled; # clear out any previous value my @args; - my @nodes; my @rnodes; + undef %failurenodes; #>>>>>>>used for trace log start>>>>>>> my %opt; @@ -510,7 +546,6 @@ sub process_request { my @nodes = (); # Filter those nodes which have bad DNS: not resolvable or inconsistent IP - my %failurenodes = (); my %preparednodes = (); foreach (@rnodes) { my $ipret = xCAT::NetworkUtils->checkNodeIPaddress($_); @@ -597,10 +632,8 @@ sub process_request { arg => [ $args[0] ] }, \&pass_along); } if ($errored) { - my $rsp; - $rsp->{errorcode}->[0] = 1; - $rsp->{error}->[0] = "Failed in running begin prescripts. Processing will still continue.\n"; - $::XNBA_callback->($rsp); + xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: Failed in running begin prescripts."); + return if ($errored > 1); } } @@ -635,8 +668,8 @@ sub process_request { bootparams => \%bphash}, \&pass_along); if ($errored) { - xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: Failed in processing setdestiny. Processing will not continue."); - return; + xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: Failed in processing setdestiny."); + return if ($errored > 1); } } @@ -644,11 +677,11 @@ sub process_request { #Time to actually configure the nodes, first extract database data with the scalable calls my $chaintab = xCAT::Table->new('chain'); my $noderestab = xCAT::Table->new('noderes'); #in order to detect per-node tftp directories - my $mactab = xCAT::Table->new('mac'); #to get all the hostnames my %nrhash = %{ $noderestab->getNodesAttribs(\@nodes, [qw(tftpdir)]) }; my %chainhash = %{ $chaintab->getNodesAttribs(\@nodes, [qw(currstate)]) }; my %iscsihash; my $iscsitab = xCAT::Table->new('iscsi'); + my %machash = (); if ($iscsitab) { %iscsihash = %{ $iscsitab->getNodesAttribs(\@nodes, [qw(server target)]) }; @@ -657,15 +690,15 @@ sub process_request { my $typehash = $typetab->getNodesAttribs(\@nodes, ['provmethod']); my $linuximgtab = xCAT::Table->new('linuximage', -create => 1); - my %machash = %{ $mactab->getNodesAttribs(\@nodes, [qw(mac)]) }; + my @normalnodeset = (); foreach (@nodes) { - my $tftpdir; + next if (exists($failurenodes{$_})); + + my $tftpdir = $globaltftpdir; if ($nrhash{$_}->[0] and $nrhash{$_}->[0]->{tftpdir}) { $tftpdir = $nrhash{$_}->[0]->{tftpdir}; - } else { - $tftpdir = $globaltftpdir; } - mkpath($tftpdir . "/xcat/xnba/nodes/"); + my %response; $response{node}->[0]->{name}->[0] = $_; if ($args[0]) { # Send it on to the destiny plugin, then setstate @@ -678,18 +711,13 @@ sub process_request { $linuximghash = $linuximgtab->getAttribs({ imagename => $osimgname }, 'boottarget', 'addkcmdline'); } ($rc, $errstr) = setstate($_, \%bphash, \%chainhash, \%machash, \%iscsihash, $tftpdir, $linuximghash); - - #currently, it seems setstate doesn't return error codes... - #if ($rc) { - # $response{node}->[0]->{errorcode}->[0]= $rc; - # $response{node}->[0]->{errorc}->[0]= $errstr; - # $::XNBA_callback->(\%response); - #} - if ($args[0] eq 'offline') { - unlink($tftpdir . "/xcat/xnba/nodes/" . $_); - unlink($tftpdir . "/xcat/xnba/nodes/" . $_ . ".pxelinux"); - unlink($tftpdir . "/xcat/xnba/nodes/" . $_ . ".uefi"); - unlink($tftpdir . "/xcat/xnba/nodes/" . $_ . ".elilo"); + if ($rc) { + $response{node}->[0]->{errorcode}->[0] = $rc; + $response{node}->[0]->{error}->[0] = $errstr; + $failurenodes{$_} = 1; + $::XNBA_callback->(\%response); + } else { + push @normalnodeset, $_; } } } @@ -712,7 +740,7 @@ sub process_request { $sub_req->({ command => ['makedhcp'], arg => \@parameter, - node => \@nodes }, $::XNBA_callback); + node => \@normalnodeset }, $::XNBA_callback); } else { xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: dhcpsetup=$do_dhcpsetup"); } @@ -724,19 +752,16 @@ sub process_request { if ($::XNBA_request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handle my own children xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue runendpre request"); $sub_req->({ command => ['runendpre'], - node => \@nodes, + node => \@normalnodeset, arg => [ $args[0], '-l' ] }, \&pass_along); } else { #nodeset did not distribute to the service node, here we need to let runednpre to distribute the nodes to their masters xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue runendpre request"); $sub_req->({ command => ['runendpre'], - node => \@nodes, + node => \@normalnodeset, arg => [ $args[0] ] }, \&pass_along); } if ($errored) { - my $rsp; - $rsp->{errorcode}->[0] = 1; - $rsp->{error}->[0] = "Failed in running end prescripts. Processing will still continue.\n"; - $::XNBA_callback->($rsp); + xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: Failed in running end prescripts."); } }