mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-11-04 05:12:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			269 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
package hierarchy;
 | 
						|
 | 
						|
# IBM(c) 2016 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
						|
 | 
						|
BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr'; }
 | 
						|
use lib "$::XCATROOT/probe/lib/perl";
 | 
						|
use probe_utils;
 | 
						|
use xCAT::ServiceNodeUtils;
 | 
						|
 | 
						|
use strict;
 | 
						|
use Data::Dumper;
 | 
						|
use IO::Select;
 | 
						|
use File::Basename;
 | 
						|
use POSIX ":sys_wait_h";
 | 
						|
 | 
						|
sub new {
 | 
						|
    my $self  = {};
 | 
						|
    my $class = shift;
 | 
						|
 | 
						|
    $self->{program_name} = basename("$0");
 | 
						|
 | 
						|
    my %dispatchcmd;
 | 
						|
    $self->{dispatchcmd} = \%dispatchcmd;
 | 
						|
 | 
						|
    my @subjobpids = ();
 | 
						|
    my @subjobfds  = ();
 | 
						|
    my %subjobstates;
 | 
						|
    my %fdnodemap;
 | 
						|
    $self->{subjobpids}    = \@subjobpids;
 | 
						|
    $self->{subjobfds}     = \@subjobfds;
 | 
						|
    $self->{subjobstates}  = \%subjobstates;
 | 
						|
    $self->{allsubjobdone} = 0;
 | 
						|
    $self->{fdnodemap}     = \%fdnodemap;
 | 
						|
    $self->{select}        = new IO::Select;
 | 
						|
 | 
						|
    bless($self, ref($class) || $class);
 | 
						|
    return $self;
 | 
						|
}
 | 
						|
 | 
						|
sub calculate_dispatch_cmd {
 | 
						|
    my $self      = shift;
 | 
						|
    my $noderange = shift;
 | 
						|
    my $argv_ref  = shift;
 | 
						|
    my $error_ref = shift;
 | 
						|
 | 
						|
    @{$error_ref} = ();
 | 
						|
 | 
						|
    my @snlist = xCAT::ServiceNodeUtils->getAllSN();
 | 
						|
    if ($noderange) {
 | 
						|
        my @nodes = probe_utils->parse_node_range($noderange);
 | 
						|
 | 
						|
        #if there is error in noderange
 | 
						|
        if ($?) {
 | 
						|
            my $error = join(" ", @nodes);
 | 
						|
            if ($error =~ /Error: Invalid nodes and\/or groups in noderange: (.+)/) {
 | 
						|
                push @{$error_ref}, "There are invaild nodes ($1) in command line attribute node range";
 | 
						|
            } else {
 | 
						|
                push @{$error_ref}, "There is error in command line attribute node range, please using nodels to check";
 | 
						|
            }
 | 
						|
            return 1;
 | 
						|
        } else {
 | 
						|
 | 
						|
            #calculate the mapping between SN and the nodes which belong to it.
 | 
						|
            chomp foreach (@nodes);
 | 
						|
            my $snnodemap = xCAT::ServiceNodeUtils->get_ServiceNode(\@nodes, "xcat", "MN");
 | 
						|
 | 
						|
            my %newsnnodemap;
 | 
						|
            my $rst = 0;
 | 
						|
            foreach my $sn (keys %$snnodemap) {
 | 
						|
                if (grep(/^$sn$/, @snlist)) {   # the node just belong to one SN
 | 
						|
                    push(@{ $newsnnodemap{$sn} }, @{ $snnodemap->{$sn} });
 | 
						|
                } elsif ($sn =~ /(\w+),.+/) { # the node belong to more than one SN, count it into first SN
 | 
						|
                    if (grep(/^$1$/, @snlist)) {
 | 
						|
                        push(@{ $newsnnodemap{$1} }, @{ $snnodemap->{$sn} });
 | 
						|
                    } else {
 | 
						|
                        push @{$error_ref}, "The value $1  of 'servicenode' isn't a service node";
 | 
						|
                        $rst = 1;
 | 
						|
                    }
 | 
						|
                } else { # the nodes don't belong to any SN will be handled by MN
 | 
						|
                    push(@{ $newsnnodemap{mn} }, @{ $snnodemap->{$sn} });
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return 1 if ($rst);
 | 
						|
 | 
						|
            #print Dumper \%newsnnodemap;
 | 
						|
            #generate new command for each SN, replace noderange
 | 
						|
            foreach my $sn (keys %newsnnodemap) {
 | 
						|
                my $nodes = join(",", @{ $newsnnodemap{$sn} });
 | 
						|
                for (my $i = 0 ; $i <= @$argv_ref ; $i++) {
 | 
						|
                    if ($argv_ref->[$i] eq "-n") {
 | 
						|
                        $argv_ref->[ $i + 1 ] = $nodes;
 | 
						|
                        last;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                my $args = join(" ", @$argv_ref);
 | 
						|
                $self->{dispatchcmd}->{$sn} = "$::XCATROOT/probe/subcmds/$self->{program_name} $args -H 2>&1";
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
 | 
						|
        #there isn't noderange input from STDIN, dispatch command to all SN if there are SN defined in MN
 | 
						|
        #if there isn't SN defined in MN, just dispatch command to MN itself
 | 
						|
        my $args = join(" ", @$argv_ref);
 | 
						|
        $self->{dispatchcmd}->{mn} = "$::XCATROOT/probe/subcmds/$self->{program_name} $args -H 2>&1";
 | 
						|
        if (@snlist) {
 | 
						|
            my $sns  = join(",", @snlist);
 | 
						|
            $self->{dispatchcmd}->{$sns} = "$::XCATROOT/probe/subcmds/$self->{program_name} $args -H 2>&1";
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
sub dispatch_cmd {
 | 
						|
    my $self      = shift;
 | 
						|
    my $noderange = shift;
 | 
						|
    my $argv_ref  = shift;
 | 
						|
    my $error_ref = shift;
 | 
						|
 | 
						|
    @$error_ref = ();
 | 
						|
    my $rst = 0;
 | 
						|
 | 
						|
    $rst = $self->calculate_dispatch_cmd($noderange, $argv_ref, $error_ref);
 | 
						|
    return $rst if ($rst);
 | 
						|
 | 
						|
    foreach my $target_server (keys %{ $self->{dispatchcmd} }) {
 | 
						|
        my $subjobcmd = undef;
 | 
						|
        if ($target_server eq "mn") {
 | 
						|
            $subjobcmd = $self->{dispatchcmd}->{$target_server};
 | 
						|
        } else {
 | 
						|
            $subjobcmd = "xdsh $target_server -s \"$self->{dispatchcmd}->{$target_server}\" 2>&1";
 | 
						|
        }
 | 
						|
 | 
						|
        #print "$subjobcmd\n";
 | 
						|
 | 
						|
        my $subjobfd;
 | 
						|
        my $subjobpid;
 | 
						|
        if (!($subjobpid = open($subjobfd, "$subjobcmd |"))) {
 | 
						|
            push @{$error_ref}, "Fork process to dispatch cmd $subjobcmd to $target_server failed: $!";
 | 
						|
            $rst = 1;
 | 
						|
            last;
 | 
						|
        }
 | 
						|
        push(@{ $self->{subjobpids} }, $subjobpid);
 | 
						|
        push(@{ $self->{subjobfds} },  $subjobfd);
 | 
						|
        $self->{fdnodemap}->{$subjobfd} = $target_server;
 | 
						|
    }
 | 
						|
 | 
						|
    if (@{ $self->{subjobpids} })
 | 
						|
    {
 | 
						|
        $self->{select}->add(\*$_) foreach (@{ $self->{subjobfds} });
 | 
						|
        $| = 1;
 | 
						|
 | 
						|
        foreach (@{ $self->{subjobfds} }) {
 | 
						|
            $self->{subjobstates}->{$_} = 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return $rst;
 | 
						|
}
 | 
						|
 | 
						|
sub read_reply {
 | 
						|
    my $self            = shift;
 | 
						|
    my $reply_cache_ref = shift;
 | 
						|
 | 
						|
    %$reply_cache_ref = ();
 | 
						|
 | 
						|
    my @hdls;
 | 
						|
    while (!$self->{allsubjobdone} && !%$reply_cache_ref) {
 | 
						|
        if (@hdls = $self->{select}->can_read(0)) {
 | 
						|
            foreach my $hdl (@hdls) {
 | 
						|
                foreach my $fd (@{ $self->{subjobfds} }) {
 | 
						|
                    if (!$self->{subjobstates}->{$_} && $hdl == \*$fd) {
 | 
						|
                        if (eof($fd)) {
 | 
						|
                            $self->{subjobstates}->{$fd} = 1;
 | 
						|
                        } else {
 | 
						|
                            my $line;
 | 
						|
                            chomp($line = <$fd>);
 | 
						|
 | 
						|
                            #print ">>>$line\n";
 | 
						|
                            $line = "mn:$line" if ($self->{fdnodemap}->{$fd} eq "mn");
 | 
						|
                            push @{ $reply_cache_ref->{ $self->{fdnodemap}->{$fd} } }, $line;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        sleep 0.1;
 | 
						|
 | 
						|
        #check if all sub job have done
 | 
						|
        $self->{allsubjobdone} = 1;
 | 
						|
        $self->{allsubjobdone} &= $self->{subjobstates}->{$_} foreach (keys %{ $self->{subjobstates} });
 | 
						|
    }
 | 
						|
 | 
						|
    if (%$reply_cache_ref) {
 | 
						|
        return 1;
 | 
						|
    } else {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
sub destory {
 | 
						|
    my $self      = shift;
 | 
						|
    my $error_ref = shift;
 | 
						|
 | 
						|
    my $rst = 0;
 | 
						|
    @$error_ref = ();
 | 
						|
 | 
						|
    close($_) foreach (@{ $self->{subjobfds} });
 | 
						|
 | 
						|
    my %runningpid;
 | 
						|
    $runningpid{$_} = 1 foreach (@{ $self->{subjobpids} });
 | 
						|
    my $existrunningpid = 0;
 | 
						|
    $existrunningpid = 1 if (%runningpid);
 | 
						|
 | 
						|
    my $try = 0;
 | 
						|
    while ($existrunningpid) {
 | 
						|
 | 
						|
        #send terminal signal to all running process at same time
 | 
						|
        #try INT 5 up to 5 times
 | 
						|
        if ($try < 5) {
 | 
						|
            foreach my $pid (keys %runningpid) {
 | 
						|
                kill 'INT', $pid if ($runningpid{$pid});
 | 
						|
            }
 | 
						|
 | 
						|
            #try TERM 5 up to 5 times
 | 
						|
        } elsif ($try < 10) {
 | 
						|
            foreach my $pid (keys %runningpid) {
 | 
						|
                kill 'TERM', $pid if ($runningpid{$pid});
 | 
						|
            }
 | 
						|
 | 
						|
            #try KILL 1 time
 | 
						|
        } else {
 | 
						|
            foreach my $pid (keys %runningpid) {
 | 
						|
                kill 'KILL', $pid if ($runningpid{$pid});
 | 
						|
            }
 | 
						|
        }
 | 
						|
        ++$try;
 | 
						|
 | 
						|
        sleep 1;
 | 
						|
 | 
						|
        #To check how many process exit, set the flag of exited process to 0
 | 
						|
        foreach my $pid (keys %runningpid) {
 | 
						|
            $runningpid{$pid} = 0 if (waitpid($pid, WNOHANG));
 | 
						|
        }
 | 
						|
 | 
						|
        #To check if there are processes still running, if there are, try kill again in next loop
 | 
						|
        $existrunningpid = 0;
 | 
						|
        $existrunningpid |= $runningpid{$_} foreach (keys %runningpid);
 | 
						|
 | 
						|
        #just try 10 times, if still can't kill some process, give up
 | 
						|
        if ($try > 10) {
 | 
						|
            my $leftpid;
 | 
						|
            foreach my $pid (keys %runningpid) {
 | 
						|
                $leftpid .= "$pid " if ($runningpid{$pid});
 | 
						|
            }
 | 
						|
            push @{$error_ref}, "Can't stop process $leftpid, please handle manually.";
 | 
						|
            $rst = 1;
 | 
						|
            last;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return $rst;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
1;
 |