mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-27 17:35:33 +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;
 |