mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-11-03 21:02:34 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1022 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1022 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
						|
package xCAT_plugin::destiny;
 | 
						|
 | 
						|
BEGIN
 | 
						|
{
 | 
						|
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | 
						|
}
 | 
						|
use lib "$::XCATROOT/lib/perl";
 | 
						|
 | 
						|
use xCAT::NodeRange;
 | 
						|
use Data::Dumper;
 | 
						|
use xCAT::Utils;
 | 
						|
use xCAT::TableUtils;
 | 
						|
use Sys::Syslog;
 | 
						|
use xCAT::GlobalDef;
 | 
						|
use xCAT::Table;
 | 
						|
use xCAT_monitoring::monitorctrl;
 | 
						|
use Getopt::Long;
 | 
						|
use strict;
 | 
						|
 | 
						|
my $request;
 | 
						|
my $callback;
 | 
						|
my $subreq;
 | 
						|
my $errored = 0;
 | 
						|
 | 
						|
#DESTINY SCOPED GLOBALS
 | 
						|
my $chaintab;
 | 
						|
my $iscsitab;
 | 
						|
my $typetab;
 | 
						|
my $restab;
 | 
						|
 | 
						|
#my $sitetab;
 | 
						|
my $hmtab;
 | 
						|
my $tftpdir = "/tftpboot";
 | 
						|
 | 
						|
 | 
						|
my $nonodestatus = 0;
 | 
						|
my %failurenodes = ();
 | 
						|
 | 
						|
#my $sitetab = xCAT::Table->new('site');
 | 
						|
#if ($sitetab) {
 | 
						|
#(my $ref1) = $sitetab->getAttribs({key => 'nodestatus'}, 'value');
 | 
						|
my @entries    = xCAT::TableUtils->get_site_attribute("nodestatus");
 | 
						|
my $site_entry = $entries[0];
 | 
						|
if (defined($site_entry)) {
 | 
						|
    if ($site_entry =~ /0|n|N/) { $nonodestatus = 1; }
 | 
						|
}
 | 
						|
 | 
						|
#}
 | 
						|
 | 
						|
 | 
						|
sub handled_commands {
 | 
						|
    return {
 | 
						|
        setdestiny  => "destiny",
 | 
						|
        getdestiny  => "destiny",
 | 
						|
        nextdestiny => "destiny"
 | 
						|
      }
 | 
						|
}
 | 
						|
 | 
						|
sub process_request {
 | 
						|
    $request  = shift;
 | 
						|
    $callback = shift;
 | 
						|
    $subreq   = shift;
 | 
						|
    if (not $::XCATSITEVALS{disablecredfilecheck} and xCAT::Utils->isMN()) {
 | 
						|
        my $result = xCAT::TableUtils->checkCredFiles($callback);
 | 
						|
    }
 | 
						|
    if ($request->{command}->[0] eq 'getdestiny') {
 | 
						|
        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
 | 
						|
 | 
						|
    } 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;
 | 
						|
    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;
 | 
						|
    # 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 {
 | 
						|
    my $req      = shift;
 | 
						|
    my $flag     = shift;
 | 
						|
    my $noupdate = shift;
 | 
						|
    $chaintab = xCAT::Table->new('chain', -create => 1);
 | 
						|
    my $bphash = $req->{bootparams};
 | 
						|
 | 
						|
    @ARGV = @{ $req->{arg} };
 | 
						|
    my $noupdateinitrd;
 | 
						|
    my $ignorekernelchk;
 | 
						|
 | 
						|
    #>>>>>>>used for trace log>>>>>>>
 | 
						|
    my $verbose;
 | 
						|
    GetOptions('noupdateinitrd' => \$noupdateinitrd,
 | 
						|
        'ignorekernelchk' => \$ignorekernelchk,
 | 
						|
        'V'               => \$verbose);       #>>>>>>>used for trace log>>>>>>>
 | 
						|
 | 
						|
    #>>>>>>>used for trace log start>>>>>>>
 | 
						|
    my $verbose_on_off = 0;
 | 
						|
    if ($verbose) { $verbose_on_off = 1; }
 | 
						|
 | 
						|
    #>>>>>>>used for trace log end>>>>>>>
 | 
						|
 | 
						|
    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)) {
 | 
						|
        if ($value && $value->[0]->{"addkcmdline"}) {
 | 
						|
            my $addkcmdline = $value->[0]->{"addkcmdline"};
 | 
						|
            # $key is node name
 | 
						|
            $bphash->{$key}->[0]->{"addkcmdline"} = $addkcmdline;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    $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);
 | 
						|
    chomp($state);
 | 
						|
 | 
						|
    if ($state eq "enact") {
 | 
						|
        my $nodetypetab = xCAT::Table->new('nodetype', -create => 1);
 | 
						|
        my %nodestates;
 | 
						|
        my %stents = %{ $chaintab->getNodesAttribs($req->{node}, "currstate") };
 | 
						|
        my %ntents = %{ $nodetypetab->getNodesAttribs($req->{node}, "provmethod") };
 | 
						|
        my $state;
 | 
						|
        my $sninit = 0;
 | 
						|
        if (exists($req->{inittime})) {    # this is called in AAsn.pm
 | 
						|
            $sninit = $req->{inittime}->[0];
 | 
						|
        }
 | 
						|
 | 
						|
        foreach (@{ $req->{node} }) { #First, build a hash of all of the states to attempt to keep things as aggregated as possible
 | 
						|
            if ($stents{$_}->[0]->{currstate}) {
 | 
						|
                $state = $stents{$_}->[0]->{currstate};
 | 
						|
                $state =~ s/ .*//;
 | 
						|
 | 
						|
                #skip the node if state=ondiscover
 | 
						|
                if ($state eq 'ondiscover') {
 | 
						|
                    next;
 | 
						|
                }
 | 
						|
 | 
						|
                #get the osimagename if nodetype.provmethod has osimage specified
 | 
						|
                #use it for both sninit and genesis operating
 | 
						|
                if (($state eq 'install') || ($state eq 'netboot') || ($state eq 'statelite')) {
 | 
						|
                    my $osimage = $ntents{$_}->[0]->{provmethod};
 | 
						|
                    if (($osimage) && ($osimage ne 'install') && ($osimage ne 'netboot') && ($osimage ne 'statelite')) {
 | 
						|
                        $state = "osimage=$osimage";
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                push @{ $nodestates{$state} }, $_;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        foreach (keys %nodestates) {
 | 
						|
            $req->{arg}->[0] = $_;
 | 
						|
            $req->{node} = $nodestates{$_};
 | 
						|
            setdestiny($req, 30, 1); #ludicrous flag to denote no table updates can be inferred.
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    } elsif ($state eq "next") {
 | 
						|
        return nextdestiny($flag + 1); #this is special case where updateflag is called
 | 
						|
    } 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], errorabort => [1] });
 | 
						|
        }
 | 
						|
        my $nodetype = xCAT::Table->new('nodetype');
 | 
						|
        my $ntents = $nodetype->getNodesAttribs($req->{node}, [qw(os arch profile)]);
 | 
						|
        my $ients = $iscsitab->getNodesAttribs($req->{node}, [qw(kernel kcmdline initrd)]);
 | 
						|
        foreach (@{ $req->{node} }) {
 | 
						|
            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")) {
 | 
						|
                    $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};
 | 
						|
            if ($ient->{initrd})   { $bphash->{initrd}   = $ient->{initrd} }
 | 
						|
            if ($ient->{kcmdline}) { $bphash->{kcmdline} = $ient->{kcmdline} }
 | 
						|
        }
 | 
						|
    } elsif ($state =~ /ondiscover/) {
 | 
						|
        my $target;
 | 
						|
        if ($state =~ /=/) {
 | 
						|
            ($state, $target) = split '=', $state, 2;
 | 
						|
        }
 | 
						|
        if(!$target){
 | 
						|
            $callback->({ error => "invalid argument: \"$state\"", errorcode => [1] });
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        my @cmds = split '\|', $target;
 | 
						|
        foreach my $tmpnode (@{ $req->{node} }) {
 | 
						|
            foreach my $cmd (@cmds) {
 | 
						|
                my $action;
 | 
						|
               ($cmd, $action) = split ':', $cmd;
 | 
						|
                my $runcmd = "$cmd $tmpnode $action";
 | 
						|
                xCAT::Utils->runcmd($runcmd, 0);
 | 
						|
                xCAT::MsgUtils->trace($verbose, "d", "run ondiscover command: $runcmd");
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } 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 $target;
 | 
						|
        my $action;
 | 
						|
        my $rawstate=$state;
 | 
						|
        if ($state =~ /=/) {
 | 
						|
            ($state, $target) = split '=', $state, 2;
 | 
						|
 | 
						|
            if(!$target){
 | 
						|
                $callback->({ error => "invalid argument: \"$rawstate\"", errorcode => [1] });
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            if ($target =~ /:/) {
 | 
						|
                ($target, $action) = split ':', $target, 2;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if ($state =~ /:/) {
 | 
						|
                ($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"]); # 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=<osimage_name>\" instead.", errorcode => [1], errorabort => [1] });
 | 
						|
            return;
 | 
						|
 | 
						|
            my $updateattribs;
 | 
						|
            if ($target) {
 | 
						|
                my $archentries = $nodetypetable->getNodesAttribs($req->{node}, ['supportedarchs']);
 | 
						|
                if ($target =~ /^([^-]*)-([^-]*)-(.*)/) {
 | 
						|
                    $updateattribs->{os}      = $1;
 | 
						|
                    $updateattribs->{arch}    = $2;
 | 
						|
                    $updateattribs->{profile} = $3;
 | 
						|
                    my $nodearch = $2;
 | 
						|
                    foreach (@{ $req->{node} }) {
 | 
						|
                        if ($archentries->{$_}->[0]->{supportedarchs} and $archentries->{$_}->[0]->{supportedarchs} !~ /(^|,)$nodearch(\z|,)/) {
 | 
						|
                            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 = ();
 | 
						|
            foreach (@{ $req->{node} }) {
 | 
						|
                if ($failurenodes{$_}) {
 | 
						|
                    delete $state_hash{$_};
 | 
						|
                    next;
 | 
						|
                }
 | 
						|
                push @tmpnodelist, $_;
 | 
						|
            }
 | 
						|
            $nodetypetable->setNodesAttribs(\@tmpnodelist, $updateattribs);
 | 
						|
 | 
						|
        } else {    #state is osimage
 | 
						|
            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 {
 | 
						|
                        $callback->({ errorcode => [1], error => "osimage.provmethod for $target must be set.", errorabort => [1] });
 | 
						|
                        return;
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    $callback->({ errorcode => [1], error => "Cannot find the OS image $target in the osimage table.", errorabort => [1] });
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                my $netbootval = xCAT::Utils->lookupNetboot($ref->{osvers}, $ref->{osarch}, $ref->{imagetype});
 | 
						|
                unless ($netbootval =~ /$curnetboot/i) {
 | 
						|
 | 
						|
                    #$errored =1;
 | 
						|
                    $callback->({ warning => [ join(",", @{ $req->{node} }) . ": $curnetboot might be invalid when provisioning $target,valid options: \"$netbootval\". \nFor more details see the 'netboot' description in the output of \"tabdump -d noderes\"." ] });
 | 
						|
 | 
						|
                    #return;
 | 
						|
                }
 | 
						|
 | 
						|
 | 
						|
                my $updateattribs;
 | 
						|
                $updateattribs->{provmethod} = $target;
 | 
						|
                $updateattribs->{profile}    = $ref->{profile};
 | 
						|
                $updateattribs->{os}         = $ref->{osvers};
 | 
						|
                $updateattribs->{arch}       = $ref->{osarch};
 | 
						|
                my @tmpnodelist = ();
 | 
						|
                foreach ( @nodes ) {
 | 
						|
                    if (exists($failurenodes{$_})) {
 | 
						|
                        delete $state_hash{$_};
 | 
						|
                        next;
 | 
						|
                    }
 | 
						|
                    $state_hash{$_} = $state;
 | 
						|
                    push @tmpnodelist, $_;
 | 
						|
                }
 | 
						|
                $nodetypetable->setNodesAttribs(\@tmpnodelist, $updateattribs);
 | 
						|
 | 
						|
            } else {
 | 
						|
                my $invalidosimghash;
 | 
						|
                my $updatestuff;
 | 
						|
                my $nodetypetable = xCAT::Table->new('nodetype', -create => 1);
 | 
						|
                my %ntents = %{ $nodetypetable->getNodesAttribs($req->{node}, "provmethod") };
 | 
						|
 | 
						|
                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})) { #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) {
 | 
						|
 | 
						|
                                #check whether the noderes.netboot is set appropriately
 | 
						|
                                #if not,push the nodes into $invalidosimghash->{$osimage}->{netboot}
 | 
						|
                                my $netbootval = xCAT::Utils->lookupNetboot($ref->{osvers}, $ref->{osarch}, $ref->{imagetype});
 | 
						|
                                if ($netbootval =~ /$curnetboot/i) {
 | 
						|
                                    1;
 | 
						|
                                } else {
 | 
						|
                                    push(@{ $invalidosimghash->{$osimage}->{nodes} }, $tmpnode);
 | 
						|
                                    $invalidosimghash->{$osimage}->{netboot} = $netbootval;
 | 
						|
                                    next;
 | 
						|
                                }
 | 
						|
 | 
						|
                                if ($ref->{provmethod} && $ref->{profile} && $ref->{osvers} && $ref->{osarch}) {
 | 
						|
                                    $state = $ref->{provmethod};
 | 
						|
                                    $state_hash{$tmpnode} = $state;
 | 
						|
 | 
						|
                                    $updatestuff->{$osimage}->{state} = $state;
 | 
						|
                                    $updatestuff->{$osimage}->{nodes} = [$tmpnode];
 | 
						|
                                    $updatestuff->{$osimage}->{profile} = $ref->{profile};
 | 
						|
                                    $updatestuff->{$osimage}->{os} = $ref->{osvers};
 | 
						|
                                    $updatestuff->{$osimage}->{arch} = $ref->{osarch};
 | 
						|
                                } else {
 | 
						|
                                    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 {
 | 
						|
                                push(@{ $invalidosimghash->{$osimage}->{nodes} }, $tmpnode);
 | 
						|
                                $invalidosimghash->{$osimage}->{error}->[0] = "Cannot find the OS image $osimage in the osimage table";
 | 
						|
                                next;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
                        # 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 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]);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    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);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                # upddate DB for the nodes which pass the checking
 | 
						|
                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 (%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) {
 | 
						|
            my $cmd = "chmod -R a+r $postscripts";
 | 
						|
            xCAT::Utils->runcmd($cmd, 0);
 | 
						|
            my $rsp = {};
 | 
						|
            if ($::RUNCMD_RC != 0)
 | 
						|
            {
 | 
						|
                $callback->({ info => "$cmd failed" });
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        # 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($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_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->setdestiny: issue mk$tempstate request");
 | 
						|
            $errored = 0;
 | 
						|
            $subreq->({ command => ["mk$tempstate"],
 | 
						|
                    node            => $samestatenodes,
 | 
						|
                    noupdateinitrd  => $noupdateinitrd,
 | 
						|
                    ignorekernelchk => $ignorekernelchk,
 | 
						|
                    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->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 {
 | 
						|
                        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 {
 | 
						|
                    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 {
 | 
						|
                        xCAT::MsgUtils->report_node_error($callback, $_, "nodetype.profile not defined for $_.");
 | 
						|
                        $failurenodes{$_} = 1;
 | 
						|
                        next;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                $updates->{$_}->{'currchain'} = "boot";
 | 
						|
            }
 | 
						|
            unless ($tempstate =~ /^netboot|^statelite/) {
 | 
						|
                $chaintab->setNodesAttribs($updates);
 | 
						|
            }
 | 
						|
 | 
						|
            if ($action eq "reboot4deploy") {
 | 
						|
 | 
						|
                # this action is used in the discovery process for deployment of the node
 | 
						|
                # e.g. set chain.chain to 'osimage=rhels6.2-x86_64-netboot-compute:reboot4deploy'
 | 
						|
                # Set the status of the node to be 'installing' or 'netbooting'
 | 
						|
                my %newnodestatus;
 | 
						|
                my $newstat = xCAT_monitoring::monitorctrl->getNodeStatusFromNodesetState($tempstate, "rpower");
 | 
						|
                $newnodestatus{$newstat} = $samestatenodes;
 | 
						|
                xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%newnodestatus, 1);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } 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 wget $path. Verify it's downloadable."], 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');
 | 
						|
 | 
						|
        #my $sitetab = xCAT::Table->new('site');
 | 
						|
        my $nodehm = xCAT::Table->new('nodehm');
 | 
						|
        my $hments = $nodehm->getNodesAttribs(\@nodes, [ 'serialport', 'serialspeed', 'serialflow' ]);
 | 
						|
 | 
						|
        #(my $portent) = $sitetab->getAttribs({key=>'xcatdport'},'value');
 | 
						|
        my @entries    = xCAT::TableUtils->get_site_attribute("xcatdport");
 | 
						|
        my $port_entry = $entries[0];
 | 
						|
 | 
						|
        #(my $mastent) = $sitetab->getAttribs({key=>'master'},'value');
 | 
						|
        my @entries      = xCAT::TableUtils->get_site_attribute("master");
 | 
						|
        my $master_entry = $entries[0];
 | 
						|
        my $enthash      = $nodetype->getNodesAttribs(\@nodes, [qw(arch)]);
 | 
						|
        my $resents      = $restab->getNodesAttribs(\@nodes, [qw(xcatmaster)]);
 | 
						|
        foreach (@nodes) {
 | 
						|
            my $ent = $enthash->{$_}->[0]; #$nodetype->getNodeAttribs($_,[qw(arch)]);
 | 
						|
            unless ($ent and $ent->{arch}) {
 | 
						|
                $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") {
 | 
						|
                $arch = "ppc64";
 | 
						|
            }
 | 
						|
            my $ent = $resents->{$_}->[0]; #$restab->getNodeAttribs($_,[qw(xcatmaster)]);
 | 
						|
            my $master;
 | 
						|
            my $kcmdline = "quiet ";
 | 
						|
 | 
						|
            #the node.xcatmaster take precedence
 | 
						|
            if ($ent and $ent->{xcatmaster}) {
 | 
						|
                $master = $ent->{xcatmaster};
 | 
						|
            }
 | 
						|
 | 
						|
            #if node.xcatmaster not specified, take the ip address facing the node
 | 
						|
            unless($master){
 | 
						|
                my @nxtsrvd = xCAT::NetworkUtils->my_ip_facing($_);
 | 
						|
                unless ($nxtsrvd[0]) {
 | 
						|
                    $master = $nxtsrvd[1];
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            #the site.master takes the last precedence
 | 
						|
            unless($master){
 | 
						|
                if (defined($master_entry)) {
 | 
						|
                    $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})) {
 | 
						|
                if ($arch eq "ppc64") {
 | 
						|
                    $kcmdline .= "console=tty0 console=hvc" . $ent->{serialport};
 | 
						|
                } else {
 | 
						|
                    $kcmdline .= "console=tty0 console=ttyS" . $ent->{serialport};
 | 
						|
                }
 | 
						|
 | 
						|
                #$ent = $nodehm->getNodeAttribs($_,['serialspeed']);
 | 
						|
                unless ($ent and defined($ent->{serialspeed})) {
 | 
						|
                    xCAT::MsgUtils->report_node_error($callback, $_, "serialport defined, but no serialspeed for this node in nodehm table");
 | 
						|
                    $failurenodes{$_} = 1;
 | 
						|
                    next;
 | 
						|
                }
 | 
						|
                $kcmdline .= "," . $ent->{serialspeed};
 | 
						|
 | 
						|
                #$ent = $nodehm->getNodeAttribs($_,['serialflow']);
 | 
						|
                $kcmdline .= " ";
 | 
						|
            }
 | 
						|
 | 
						|
            my $xcatdport = "3001";
 | 
						|
            if (defined($port_entry)) {
 | 
						|
                $xcatdport = $port_entry;
 | 
						|
            }
 | 
						|
            if (-r "$tftpdir/xcat/genesis.kernel.$arch") {
 | 
						|
                my $bestsuffix  = "lzma";
 | 
						|
                my $othersuffix = "gz";
 | 
						|
                if (-r "$tftpdir/xcat/genesis.fs.$arch.lzma" and -r "$tftpdir/xcat/genesis.fs.$arch.gz") {
 | 
						|
                    if (-C "$tftpdir/xcat/genesis.fs.$arch.lzma" > -C "$tftpdir/xcat/genesis.fs.$arch.gz") { #here, lzma is older for whatever reason
 | 
						|
                        $bestsuffix  = "gz";
 | 
						|
                        $othersuffix = "lzma";
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                if (-r "$tftpdir/xcat/genesis.fs.$arch.$bestsuffix") {
 | 
						|
                    $bphash->{$_}->[0]->{kernel} = "xcat/genesis.kernel.$arch";
 | 
						|
                    $bphash->{$_}->[0]->{initrd} = "xcat/genesis.fs.$arch.$bestsuffix";
 | 
						|
                    $bphash->{$_}->[0]->{kcmdline} = $kcmdline . "xcatd=$master:$xcatdport destiny=$state";
 | 
						|
                } else {
 | 
						|
                    $bphash->{$_}->[0]->{kernel} = "xcat/genesis.kernel.$arch";
 | 
						|
                    $bphash->{$_}->[0]->{initrd} = "xcat/genesis.fs.$arch.$othersuffix";
 | 
						|
                    $bphash->{$_}->[0]->{kcmdline} = $kcmdline . "xcatd=$master:$xcatdport destiny=$state";
 | 
						|
                }
 | 
						|
            } else {    #'legacy' environment
 | 
						|
                    $bphash->{$_}->[0]->{kernel} = "xcat/nbk.$arch";
 | 
						|
                    $bphash->{$_}->[0]->{initrd} = "xcat/nkfs.$arch.gz";
 | 
						|
                    $bphash->{$_}->[0]->{kcmdline} = $kcmdline . "xcatd=$master:$xcatdport";
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
    } 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;
 | 
						|
    }
 | 
						|
 | 
						|
    #blank out the nodetype.provmethod if the previous provisioning method is not 'install'
 | 
						|
    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(\@normalnodes, [qw(os arch profile provmethod)]);
 | 
						|
        my @nodestoblank = ();
 | 
						|
        my %osimage_hash = ();
 | 
						|
        foreach (@normalnodes) {
 | 
						|
            my $ntent = $ntents->{$_}->[0];
 | 
						|
 | 
						|
            #if the previous nodeset staute is not install, then blank nodetype.provmethod
 | 
						|
            if ($ntent and $ntent->{provmethod}) {
 | 
						|
                my $provmethod = $ntent->{provmethod};
 | 
						|
                if (($provmethod ne 'install') && ($provmethod ne 'netboot') && ($provmethod ne 'statelite')) {
 | 
						|
                    if (exists($osimage_hash{$provmethod})) {
 | 
						|
                        $provmethod = $osimage_hash{$provmethod};
 | 
						|
                    } else {
 | 
						|
                        (my $ref) = $osimagetab->getAttribs({ imagename => $provmethod }, 'provmethod');
 | 
						|
                        if (($ref) && $ref->{provmethod}) {
 | 
						|
                            $osimage_hash{$provmethod} = $ref->{provmethod};
 | 
						|
                            $provmethod = $ref->{provmethod};
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                #if ($provmethod ne 'install')
 | 
						|
                #fix bug: in sysclone, provmethod attribute gets cleared
 | 
						|
                if ($provmethod ne 'install' && $provmethod ne 'sysclone') {
 | 
						|
                    push(@nodestoblank, $_);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }    #end foreach
 | 
						|
 | 
						|
        #now blank out the nodetype.provmethod
 | 
						|
        #print "nodestoblank=@nodestoblank\n";
 | 
						|
        if (@nodestoblank > 0) {
 | 
						|
            my $newhash;
 | 
						|
            $newhash->{provmethod} = "";
 | 
						|
            $nodetype->setNodesAttribs(\@nodestoblank, $newhash);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ($noupdate) { return; }    #skip table manipulation if just doing 'enact'
 | 
						|
    my $updates;
 | 
						|
    foreach (@normalnodes) {
 | 
						|
 | 
						|
        my $lstate = $state;
 | 
						|
        if ($nstates{$_}) {
 | 
						|
            $lstate = $nstates{$_};
 | 
						|
        }
 | 
						|
        $updates->{$_}->{'currstate'} = $lstate;
 | 
						|
 | 
						|
        # if there are multiple actions in the state argument, set the rest of states (shift out the first one)
 | 
						|
        # to chain.currchain so that the rest ones could be used by nextdestiny command
 | 
						|
        if ($reststates) {
 | 
						|
            $updates->{$_}->{'currchain'} = $reststates;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    $chaintab->setNodesAttribs($updates);
 | 
						|
    return getdestiny($flag + 1, \@normalnodes);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub nextdestiny {
 | 
						|
    my $flag        = shift;
 | 
						|
    my $callnodeset = 0;
 | 
						|
    if (scalar(@_)) {
 | 
						|
        $callnodeset = 1;
 | 
						|
    }
 | 
						|
    my @nodes;
 | 
						|
    if ($request and $request->{node}) {
 | 
						|
        if (ref($request->{node})) {
 | 
						|
            @nodes = @{ $request->{node} };
 | 
						|
        } else {
 | 
						|
            @nodes = ($request->{node});
 | 
						|
        }
 | 
						|
 | 
						|
        #TODO: service third party getdestiny..
 | 
						|
    } else {    #client asking to move along its own chain
 | 
						|
         #TODO: SECURITY with this, any one on a node could advance the chain, for node, need to think of some strategy to deal with...
 | 
						|
        my $node;
 | 
						|
        if ($::XCATSITEVALS{nodeauthentication}) { #if requiring node authentication, this request will have a certificate associated with it, use it instead of name resolution
 | 
						|
            unless (ref $request->{username}) { return; } #TODO: log an attempt without credentials?
 | 
						|
            $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);
 | 
						|
    }
 | 
						|
 | 
						|
    my $node;
 | 
						|
    my $noupdate_flag = 0;
 | 
						|
    $chaintab = xCAT::Table->new('chain');
 | 
						|
    my $chainents = $chaintab->getNodesAttribs(\@nodes, [qw(currstate currchain chain)]);
 | 
						|
    foreach $node (@nodes) {
 | 
						|
        unless ($chaintab) {
 | 
						|
            syslog("local4|err", "ERROR: $node requested destiny update, no chain table");
 | 
						|
            return;    #nothing to do...
 | 
						|
        }
 | 
						|
        my $ref = $chainents->{$node}->[0]; #$chaintab->getNodeAttribs($node,[qw(currstate currchain chain)]);
 | 
						|
        unless ($ref->{chain} or $ref->{currchain}) {
 | 
						|
            syslog("local4|err", "ERROR: node requested destiny update, no path in chain.currchain");
 | 
						|
            return;    #Can't possibly do anything intelligent..
 | 
						|
        }
 | 
						|
        unless ($ref->{currchain}) {    #If no current chain, copy the default
 | 
						|
            $ref->{currchain} = $ref->{chain};
 | 
						|
        } elsif ($ref->{currchain} !~ /[,;]/){
 | 
						|
            if ($ref->{currstate} and ($ref->{currchain} =~ /$ref->{currstate}/)) {
 | 
						|
                $ref->{currchain} = 'standby';
 | 
						|
                $callnodeset = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        my @chain = split /[,;]/, $ref->{currchain};
 | 
						|
 | 
						|
        $ref->{currstate} = shift @chain;
 | 
						|
        $ref->{currchain} = join(',', @chain);
 | 
						|
        unless ($ref->{currchain}) { #If we've gone off the end of the chain, have currchain stick
 | 
						|
            $ref->{currchain} = $ref->{currstate};
 | 
						|
        }
 | 
						|
        $chaintab->setNodeAttribs($node, $ref); #$ref is in a state to commit back to db
 | 
						|
 | 
						|
        my %requ;
 | 
						|
        $requ{node} = [$node];
 | 
						|
        $requ{arg}  = [ $ref->{currstate} ];
 | 
						|
        if ($ref->{currstate} =~ /noupdateinitrd$/)
 | 
						|
        {
 | 
						|
            my @items = split /[:]/, $ref->{currstate};
 | 
						|
            $requ{arg} = \@items;
 | 
						|
            $noupdate_flag = 1;
 | 
						|
        }
 | 
						|
        setdestiny(\%requ, $flag + 1);
 | 
						|
    }
 | 
						|
 | 
						|
    if ($callnodeset) {
 | 
						|
        my $args;
 | 
						|
        if ($noupdate_flag)
 | 
						|
        {
 | 
						|
            $args = [ 'enact', '--noupdateinitrd' ];
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            $args = ['enact'];
 | 
						|
        }
 | 
						|
        $subreq->({ command => ['nodeset'],
 | 
						|
                node => \@nodes,
 | 
						|
                arg  => $args });
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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 $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 $bptab = xCAT::Table->new('bootparams', -create => 1);
 | 
						|
    my $bpents = $bptab->getNodesAttribs($nodes, [qw(kernel initrd kcmdline xcatmaster)]);
 | 
						|
 | 
						|
    #my $sitetab= xCAT::Table->new('site');
 | 
						|
    #(my $sent) = $sitetab->getAttribs({key=>'master'},'value');
 | 
						|
    my @entries      = xCAT::TableUtils->get_site_attribute("master");
 | 
						|
    my $master_value = $entries[0];
 | 
						|
 | 
						|
    my %node_status = ();
 | 
						|
    foreach $node (@$nodes) {
 | 
						|
        unless ($chaintab) { #Without destiny, have the node wait with ssh hopefully open at least
 | 
						|
            my $stat = xCAT_monitoring::monitorctrl->getNodeStatusFromNodesetState("standby", "getdestiny");
 | 
						|
            if ($stat) {
 | 
						|
                if (exists($node_status{$stat})) {
 | 
						|
                    push @{ $node_status{$stat} }, $node;
 | 
						|
                } else {
 | 
						|
                    $node_status{$stat} = [$node];
 | 
						|
                }
 | 
						|
                xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%node_status, 1);
 | 
						|
            }
 | 
						|
 | 
						|
            $callback->({ node => [ { name => [$node], data => ['standby'], destiny => ['standby'] } ] });
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        my $ref = $chainents->{$node}->[0]; #$chaintab->getNodeAttribs($node,[qw(currstate chain)]);
 | 
						|
        unless ($ref) {
 | 
						|
 | 
						|
            #collect node status for certain states
 | 
						|
            if (($nonodestatus == 0) && (($flag == 0) || ($flag == 3))) {
 | 
						|
                my $stat = xCAT_monitoring::monitorctrl->getNodeStatusFromNodesetState("standby", "getdestiny");
 | 
						|
 | 
						|
                #print "node=$node, stat=$stat\n";
 | 
						|
                if ($stat) {
 | 
						|
                    if (exists($node_status{$stat})) {
 | 
						|
                        push @{ $node_status{$stat} }, $node;
 | 
						|
                    } else {
 | 
						|
                        $node_status{$stat} = [$node];
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            $callback->({ node => [ { name => [$node], data => ['standby'], destiny => ['standby'] } ] });
 | 
						|
            next;
 | 
						|
        }
 | 
						|
        unless ($ref->{currstate}) {    #Has a record, but not yet in a state...
 | 
						|
             # we set a 1 here so that it does the nodeset to create tftpboot files
 | 
						|
            return nextdestiny(0, 1);    #Becomes a nextdestiny...
 | 
						|
 | 
						|
            #      my @chain = split /,/,$ref->{chain};
 | 
						|
            #      $ref->{currstate} = shift @chain;
 | 
						|
            #      $chaintab->setNodeAttribs($node,{currstate=>$ref->{currstate}});
 | 
						|
        }
 | 
						|
        my %response;
 | 
						|
        $response{name}    = [$node];
 | 
						|
        $response{data}    = [ $ref->{currstate} ];
 | 
						|
        $response{destiny} = [ $ref->{currstate} ];
 | 
						|
        my $nrent = $nrents->{$node}->[0]; #$noderestab->getNodeAttribs($node,[qw(tftpserver xcatmaster)]);
 | 
						|
        my $bpent = $bpents->{$node}->[0]; #$bptab->getNodeAttribs($node,[qw(kernel initrd kcmdline xcatmaster)]);
 | 
						|
        if (defined $bpent->{kernel}) {
 | 
						|
            $response{kernel} = $bpent->{kernel};
 | 
						|
        }
 | 
						|
        if (defined $bpent->{initrd}) {
 | 
						|
            $response{initrd} = $bpent->{initrd};
 | 
						|
        }
 | 
						|
        if (defined $bpent->{kcmdline}) {
 | 
						|
            $response{kcmdline} = $bpent->{kcmdline};
 | 
						|
        }
 | 
						|
        if (defined $nrent->{tftpserver}) {
 | 
						|
            $response{imgserver} = $nrent->{tftpserver};
 | 
						|
        } elsif (defined $nrent->{xcatmaster}) {
 | 
						|
            $response{imgserver} = $nrent->{xcatmaster};
 | 
						|
        } elsif (defined($master_value)) {
 | 
						|
            $response{imgserver} = $master_value;
 | 
						|
        } else {
 | 
						|
            my @resd = xCAT::NetworkUtils->my_ip_facing($node);
 | 
						|
            unless ($resd[0]) { $response{imgserver} = $resd[1]; }
 | 
						|
        }
 | 
						|
 | 
						|
        #collect node status for certain states
 | 
						|
        if (($flag == 0) || ($flag == 3)) {
 | 
						|
            my $stat = xCAT_monitoring::monitorctrl->getNodeStatusFromNodesetState($response{destiny}->[0], "getdestiny");
 | 
						|
 | 
						|
            #print  "node=$node, stat=$stat\n";
 | 
						|
            if ($stat) {
 | 
						|
                if (exists($node_status{$stat})) {
 | 
						|
                    push @{ $node_status{$stat} }, $node;
 | 
						|
                } else {
 | 
						|
                    $node_status{$stat} = [$node];
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        $callback->({ node => [ \%response ] });
 | 
						|
    }
 | 
						|
 | 
						|
    #setup the nodelist.status
 | 
						|
    if (($nonodestatus == 0) && (($flag == 0) || ($flag == 3))) {
 | 
						|
 | 
						|
        #print "save status\n";
 | 
						|
        if (keys(%node_status) > 0) { xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%node_status, 1); }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
1;
 |