diff --git a/perl-xCAT/xCAT/DSHCLI.pm b/perl-xCAT/xCAT/DSHCLI.pm index de77b553e..34f98c07b 100644 --- a/perl-xCAT/xCAT/DSHCLI.pm +++ b/perl-xCAT/xCAT/DSHCLI.pm @@ -2126,6 +2126,37 @@ sub config_dsh $dsh_trace && xCAT::MsgUtils->message("I", $rsp, $::CALLBACK); + # Check devicetype attr and try to load device configuration + $$options{'devicetype'} = $$options{'devicetype'} || $ENV{'DEVICETYPE'} || u +ndef; + if ( $$options{'devicetype'} ) + { + $ENV{'DEVICETYPE'} = $$options{'devicetype'}; + my $devicepath = $$options{'devicetype'}; + $devicepath =~ s/::/\//g; + $devicepath = "/var/opt/xcat/" . $devicepath. "/config"; + # Get configuration from $::XCATDEVCFGDIR + if ( -e $devicepath) + { + my $deviceconf = get_config($devicepath); + # Get all dsh section configuration + foreach my $entry (keys %{ $$deviceconf{'xdsh'} } ) + { + my $value = $$deviceconf{'xdsh'}{$entry}; + if ($value) + { + $$options{$entry} = $value; + } + + } + } + else + { + $rsp->{data}->[0] = "EMsgMISSING_DEV_CFG"; + xCAT::MsgUtils->message('E', $rsp, $::CALLBACK); + } + } + !$$options{'node-rsh'} && ( $$options{'node-rsh'} = $ENV{'DSH_NODE_RSH'} || $ENV{'DSH_REMOTE_CMD'} @@ -2225,64 +2256,95 @@ sub config_dsh $dsh_trace && xCAT::MsgUtils->message("I", $rsp, $::CALLBACK); - # Set a default PATH - $$options{'pre-command'} = $path_set; - - if (!$$options{'no-locale'}) + # Check if $$options{'pre-command'} has been overwritten + if (!$$options{'pre-command'}) { - my @output = `/usr/bin/locale`; - chomp(@output); + # Set a default PATH + $$options{'pre-command'} = $path_set; - my @settings = (); - !($$options{'syntax'} eq 'csh') && (push @settings, $env_set); - - foreach my $line (@output) + if (!$$options{'no-locale'}) { - $line =~ s/=/$env_assign/; + my @output = `/usr/bin/locale`; + chomp(@output); + + my @settings = (); + !($$options{'syntax'} eq 'csh') && (push @settings, $env_set); + + foreach my $line (@output) + { + $line =~ s/=/$env_assign/; + + if ($$options{'syntax'} eq 'csh') + { + push @settings, "$env_set $line;"; + } + + else + { + push @settings, $line; + } + } if ($$options{'syntax'} eq 'csh') { - push @settings, "$env_set $line;"; + push @settings, "$env_set PERL_BADLANG${env_assign}0;"; } else { - push @settings, $line; + push @settings, "PERL_BADLANG${env_assign}0"; } - } + + my $locale_settings = join ' ', @settings; + !($$options{'syntax'} eq 'csh') && ($locale_settings .= ' ; '); + $$options{'pre-command'} .= $locale_settings; + } + } + else + { + $$options{'pre-command'} = ''; + } + + # Check if $$options{'post-command'} has been overwritten. + if (! $$options{'post-command'} ) + { if ($$options{'syntax'} eq 'csh') { - push @settings, "$env_set PERL_BADLANG${env_assign}0;"; + $$options{'post-command'} = + "; $env_set DSH_TARGET_RC$env_assign\$status; echo \":DSH_TARGET_RC=\${DSH_TARGET_RC}:\""; } else { - push @settings, "PERL_BADLANG${env_assign}0"; + $$options{'post-command'} = + "; $env_set DSH_TARGET_RC$env_assign\$?; echo \":DSH_TARGET_RC=\${DSH_TARGET_RC}:\""; } - my $locale_settings = join ' ', @settings; - !($$options{'syntax'} eq 'csh') && ($locale_settings .= ' ; '); - - $$options{'pre-command'} .= $locale_settings; + $$options{'exit-status'} + && ($$options{'post-command'} .= + ' ; echo "Remote_command_rc = $DSH_TARGET_RC"'); } - - if ($$options{'syntax'} eq 'csh') - { - $$options{'post-command'} = - "; $env_set DSH_TARGET_RC$env_assign\$status; echo \":DSH_TARGET_RC=\${DSH_TARGET_RC}:\""; - } - else { - $$options{'post-command'} = - "; $env_set DSH_TARGET_RC$env_assign\$?; echo \":DSH_TARGET_RC=\${DSH_TARGET_RC}:\""; + # post-command is overwritten by user , set env $::USER_POST_CMD + $::USER_POST_CMD = 1; + if ($$options{'post-command'} =~ /NULL/ ) + { + $$options{'post-command'} = ''; + } + else + { + # $::DSH_EXIT_STATUS ony can be used in DSHCore::pipe_handler_buffer + # and DSHCore::pipe_handler + $$options{'exit-status'} + && ($::DSH_EXIT_STATUS = 1); + $$options{'post-command'} = ";$$options{'post-command'}"; + # Append "DSH_RC" keyword to mark output + $$options{'post-command'} = "$$options{'post-command'};echo DSH_RC"; + } } - $$options{'exit-status'} - && ($$options{'post-command'} .= - ' ; echo "Remote_command_rc = $DSH_TARGET_RC"'); - if ( !$$options{'nodes'} && ( $ENV{'DSH_NODE_LIST'} @@ -3476,7 +3538,6 @@ sub isFdNumExceed sub usage_dsh { ## usage message - my $usagemsg1 = " xdsh -h \n xdsh -q \n xdsh -v \n xdsh [noderange] [group]\n"; my $usagemsg2 = @@ -3485,7 +3546,7 @@ sub usage_dsh my $usagemsg4 = "[-m] [-o options][-q] [-Q] [-r remote_shell] [-i image path]\n"; my $usagemsg5 = - " [-s] [-S ksh | csh] [-t timeout] [-T] [-X environment variables] [-v] [-z]\n"; + " [-s] [-S ksh | csh] [-t timeout] [-T] [-X environment variables] [--devicetype type_of_device] [-v] [-z]\n"; my $usagemsg6 = " [command_list]\n"; my $usagemsg7 = "Note:Context always defaults to XCAT unless -C flag is set."; @@ -3597,6 +3658,7 @@ sub parse_and_run_dsh 'T|trace' => \$options{'trace'}, 'V|version' => \$options{'version'}, + 'devicetype|devicetype=s' => \$options{'devicetype'}, 'command-name|commandName=s' => \$options{'command-name'}, 'command-description|commandDescription=s' => \$options{'command-description'}, @@ -3695,7 +3757,19 @@ sub parse_and_run_dsh } else { - + if (defined $options{'devicetype'}) + { + $ENV{'DEVICETYPE'} = $options{'devicetype'}; + my $devicepath = $options{'devicetype'}; + $devicepath =~ s/::/\//g; + $devicepath = "/var/opt/xcat/" . $devicepath. "/config"; + if ( -e $devicepath) + { + my $deviceconf = get_config($devicepath); + # Get ssh-setup-command attribute from configuration + $ENV{'SSH_SETUP_COMMAND'} = $$deviceconf{'main'}{'ssh-setup-command'}; + } + } my $rc = xCAT::Utils->setupSSH(@nodelist); my @results = "return code = $rc"; return (@results); @@ -3864,6 +3938,7 @@ sub parse_and_run_dcp 'R|recursive' => \$options{'recursive'}, 'T|trace' => \$options{'trace'}, 'V|version' => \$options{'version'}, + 'devicetype=s' => \$options{'devicetype'}, 'X:s' => \$options{'ignore_env'} ) ) @@ -4377,4 +4452,126 @@ sub show_dsh_config } } +#------------------------------------------------------------------------------- + +=head3 get_config + + Substitute specific keywords in hash + e.g. config file: + [main] + cachedir=/var/cache/yum + keepcache=1 + [base] + name=Red Hat Linux $releasever - $basearch - Base + baseurl=http://mirror.dulug.duke.edu/pub/yum-repository/redhat/$releasev +er/$basearch/ + + %config = { + main => { + 'cachedir' => '/var/cache/yum', + 'keepcache' => '1' + }, + bash => { + 'name' => 'Red Hat Linux $relea +sever - $basearch - Base', + 'baseurl' => 'http://mirror.dulug. +duke.edu/pub/yum-repository/redhat/$releasever/$basearch/' + } + } + + Arguments: + $configfile - config file + Returns: + $config_ref - reference to config hash + Comments: + +=cut + +#------------------------------------------------------------------------------- +sub get_config +{ + my $configfile = shift; + my @content = readFile($configfile); + my $current_section = "DEFAULT"; + my %config; + my $xcat_use; + $xcat_use = 0; + foreach my $line (@content) + { + my ($entry, $value); + chomp $line; + if ( $line =~ /\QDO NOT ERASE THIS SECTION\E/ ) + { + # reverse flag + $xcat_use = ! $xcat_use; + } + if ($xcat_use) + { + # Remove leading "#". This line is used by xCAT + $line =~ s/^#//g; + } + else + { + # Remove comment line + $line =~ s/#.*$//g; + } + $line =~ s/^\s+//g; + $line =~ s/\s+$//g; + next unless $line; + if ( $line =~ /^\s*\[([\w+-\.]+)\]\s*$/ ) { + $current_section = $1; + } else { + # Ignore line doesn't key/value pair. + if ($line !~ /=/) + { + next; + } + $line =~ /^\s*([^=]*)\s*=\s*(.*)\s*$/; + $entry = $1; + $value = $2; + $entry =~ s/^#*//g; + # Remove leading and trailing spaces + $entry =~ s/^\s+//g; + $entry =~ s/\s+$//g; + $value =~ s/^\s+//g; + $value =~ s/\s+$//g; + $config{$current_section}{"$entry"} = $value; + } + } + return \%config; +} + +#------------------------------------------------------------------------------- + +=head3 readFile + + Read a file and return its content. + + Arguments: + filename + Returns: + file contents or undef + Globals: + none + Error: + undef + Example: + my $blah = readFile('/etc/redhat-release'); + Comments: + none + +=cut + +#------------------------------------------------------------------------------- + +sub readFile +{ + my $filename = shift; + open(FILE, "<$filename"); + my @contents = ; + close(FILE); + return @contents; +} + + 1; diff --git a/perl-xCAT/xCAT/DSHCore.pm b/perl-xCAT/xCAT/DSHCore.pm index 82df0cdf3..5f82822fa 100644 --- a/perl-xCAT/xCAT/DSHCore.pm +++ b/perl-xCAT/xCAT/DSHCore.pm @@ -248,7 +248,8 @@ sub ifconfig_inet =cut #--------------------------------------------------------------------------- - +# NOTE: global environment $::__DSH_LAST_LINE only can be used in DSHCore::pipe_handler and DSHCore::pipe_handler_buffer +$::__DSH_LAST_LINE = undef; sub pipe_handler { my ($class, $options, $target_properties, $read_fh, $buffer_size, $label, @@ -258,6 +259,21 @@ sub pipe_handler my $line; my $target_hostname; my $eof_reached = undef; + my $cust_rc_deal =0; + + if ($::USER_POST_CMD) + { + # If user provide post-command to display return code, + # the keyword 'DSH_RC' will be searched, + # the return code is gotten in another way as shown like below: + # ... + # + # + # DSH_RC + # + # The last two lines are needed to be moved out from output + $cust_rc_deal = 1; + } while (sysread($read_fh, $line, $buffer_size) != 0 || ($eof_reached = 1)) @@ -288,6 +304,22 @@ sub pipe_handler if (@lines) { + if ($cust_rc_deal) + { + # Dump the last line at the beginning of current buffer + if ($::__DSH_LAST_LINE) + { + unshift @lines, "$::__DSH_LAST_LINE" ; + } + # Pop current buffer to $::__DSH_LAST_LINE + $::__DSH_LAST_LINE = $lines[scalar @lines - 1]; + pop @lines; + # Skip this loop if array @lines is empty. + if (scalar @lines == 0) + { + next; + } + } $line = join "\n", @lines; $line .= "\n"; @@ -302,6 +334,21 @@ sub pipe_handler $line =~ s/:DSH_TARGET_RC=$target_rc:\n//g; $$target_properties{'target-rc'} = $target_rc; } + if ( $::__DSH_LAST_LINE =~ /DSH_RC/ && $cust_rc_deal) { + my $target_rc = undef; + # Get the number in the last line + $line =~ /[\D]*([0-9]+)\s*$/ ; + $target_rc = $1; + $$target_properties{'target-rc'} = $target_rc; + # Remove the last line + $line =~ s/$target_rc\s*\n$//g; + #$line = $line . "## ret=$target_rc"; + # Clean up $::__DSH_LAST_LINE + undef $::__DSH_LAST_LINE ; + # when '-z' is specified, display return code + $::DSH_EXIT_STATUS && + ($line .="Remote_command_rc = $target_rc"); + } if ($line ne '') { @@ -395,6 +442,7 @@ sub pipe_handler =cut #--------------------------------------------------------------------------- +# NOTE: global environment $::__DSH_LAST_LINE only can be used in DSHCore::pipe_handler and DSHCore::pipe_handler_buffer sub pipe_handler_buffer { @@ -405,11 +453,25 @@ sub pipe_handler_buffer my $line; my $eof_reached = undef; + my $cust_rc_deal =0; + if ($::USER_POST_CMD) + { + # If user provide post-command to display return code, + # the keyword 'DSH_RC' will be searched, + # the return code is gotten in another way as shown like below: + # ... + # + # + # DSH_RC + # + # The last two lines are needed to be moved out from output + $cust_rc_deal = 1; + } + while ( (sysread($read_fh, $line, $buffer_size) != 0) || ($eof_reached = 1)) { last if ($eof_reached); - if ($line =~ /^\n$/) { @@ -434,6 +496,22 @@ sub pipe_handler_buffer if (@lines) { + if ($cust_rc_deal) + { + # Dump the last line at the beginning of current buffer + if ($::__DSH_LAST_LINE) + { + unshift @lines, "$::__DSH_LAST_LINE" ; + } + # Pop current buffer to $::__DSH_LAST_LINE + $::__DSH_LAST_LINE = $lines[scalar @lines - 1]; + pop @lines; + # Skip this loop if array @lines is empty. + if (scalar @lines == 0) + { + next; + } + } $line = join "\n", @lines; $line .= "\n"; @@ -448,6 +526,21 @@ sub pipe_handler_buffer $line =~ s/:DSH_TARGET_RC=$target_rc:\n//g; $$target_properties{'target-rc'} = $target_rc; } + if ( $::__DSH_LAST_LINE =~ /DSH_RC/ && $cust_rc_deal) { + my $target_rc = undef; + # Get the number in the last line + $line =~ /[\D]*([0-9]+)\s*$/ ; + $target_rc = $1; + $$target_properties{'target-rc'} = $target_rc; + # Remove the last line + $line =~ s/$target_rc\s*\n$//g; + #$line = $line . "## ret=$target_rc"; + # Clean up $::__DSH_LAST_LINE + undef $::__DSH_LAST_LINE ; + # when '-z' is specified, display return code + $::DSH_EXIT_STATUS && + ($line .="Remote_command_rc = $target_rc"); + } if ($line ne '') { @@ -469,7 +562,6 @@ sub pipe_handler_buffer last if ($fh_count == 0); } } - return $eof_reached; } diff --git a/xCAT-client/bin/xdsh b/xCAT-client/bin/xdsh index 051715fd2..976a921bd 100644 --- a/xCAT-client/bin/xdsh +++ b/xCAT-client/bin/xdsh @@ -201,6 +201,7 @@ sub parse_args_xdsh 'T|trace' => \$options{'trace'}, 'V|version' => \$options{'version'}, + 'devicetype=s' => \$options{'devicetype'}, 'command-name|commandName=s' => \$options{'command-name'}, 'command-description|commandDescription=s' => \$options{'command-description'}, diff --git a/xCAT-client/pods/man1/xdsh.1.pod b/xCAT-client/pods/man1/xdsh.1.pod index eeef1f1fe..24fede352 100644 --- a/xCAT-client/pods/man1/xdsh.1.pod +++ b/xCAT-client/pods/man1/xdsh.1.pod @@ -7,7 +7,7 @@ B - Concurrently runs commands on multiple nodes. B I [B<-B> I] [B<-C> I] [B<-e>] [B<-E> I] [B<-f> I] [B<-K>] [B<-L>] [B<-l> I] [B<-m>] [B<-o> I] [B<-Q>] [B<-r> I] [B<-s>] [B<-S> B|B] [B<-t> I] -[B<-T>] [B<-v>] [B<-X> I] [B<-z>] I +[B<-T>] [B<-v>] [B<-X> I] [B<--devicetype> I] [B<-z>] I B [B<-i> I] I @@ -344,6 +344,10 @@ This option is useful when running B from within other scripts when you don't want the user's environment affecting the behavior of xdsh. +=item B<--devicetype> I + +Specify a user-defined device type that references the location of relevant device configuration file. The devicetype value must correspond to a valid device configuration file under the /var/opt/csm/ directory. For example, the /var/opt/csm/IBSwitch/Qlogic/config file is the location if devicetype is specified ¡°IBSwitch::Qlogic¡± + =item B<-z>|B<--exit-status> Displays the exit status for the last remotely executed diff --git a/xCAT-server/sbin/remoteshell.expect b/xCAT-server/sbin/remoteshell.expect index be39db0f2..77a904372 100644 --- a/xCAT-server/sbin/remoteshell.expect +++ b/xCAT-server/sbin/remoteshell.expect @@ -44,6 +44,13 @@ if { [info exists env(XCAT_UPD_MULTNODES)] } { } else { set manynodes "" } + +if { [info exists env(SSH_SETUP_COMMAND)] } { + set ssh_setup_cmd $env(SSH_SETUP_COMMAND) +} else { + set ssh_setup_cmd "" +} + # # check input arguments # @@ -61,7 +68,13 @@ if { [llength $argv] != 0 } { # -t means test to see if the shell is already setup if { [string compare "-t" [lindex $argv 0]] ==0 } { set env(LC_ALL) "C" - set pid [ spawn [lindex $argv 1] [lindex $argv 2] -l root echo test.success ] + if { [string compare $ssh_setup_cmd $empty] !=0 } { + set userid "admin" + } else { + set userid "root" + } + + set pid [ spawn [lindex $argv 1] [lindex $argv 2] -l $userid echo test.success ] expect { timeout { exit 1 } "Are you sure you want to continue connecting (yes/no)?" { @@ -210,6 +223,9 @@ if { [llength $argv] != 0 } { set printlist [ join $nodelist ", " ] set scp "/usr/bin/scp" set directory "/install/postscripts/.ssh" + set fh_auth_keys2 [ open "/install/postscripts/.ssh/authorized_keys2" "r"] + set auth_keys2 [read $fh_auth_keys2] + close $fh_auth_keys2 if { [info exists env(XCAT_REMOTE_PASSWORD)] } { set word $env(XCAT_REMOTE_PASSWORD) } else { @@ -224,33 +240,64 @@ if { [llength $argv] != 0 } { send_user "\n" } foreach node $nodelist { - set env(LC_ALL) "C" - set pid [ spawn $scp -r -p $directory root\@$node:/tmp ] - expect { - "Are you sure you want to continue connecting (yes/no)?" { - send "yes\r" - exp_continue + if { [string compare $ssh_setup_cmd $empty] ==0 } { + set env(LC_ALL) "C" + set pid [ spawn $scp -r -p $directory root\@$node:/tmp ] + expect { + "Are you sure you want to continue connecting (yes/no)?" { + send "yes\r" + exp_continue + } + "*ssword*" { + send "$word\r" + exp_continue + } + "Permission denied*" { + exec /bin/kill $pid + } } - "*ssword*" { - send "$word\r" - exp_continue + set pid [ spawn $remoteshell $node -l root /tmp/.ssh/copy.perl ] + expect { + "Are you sure you want to continue connecting (yes/no)?" { + send "yes\r" + exp_continue + } + "*ssword*" { + send "$word\r" + exp_continue + } + "Permission denied*" { + exec /bin/kill $pid + } } - "Permission denied*" { - exec /bin/kill $pid + } else { + # Loop all keys in $auth_keys2 and generate ssh setup command one by one + # Remove '\n' + set auth_keys2 [string range $auth_keys2 0 end-1] + set all_keys [split $auth_keys2 "\n"] + set ssh_setup_string "" + foreach each_key $all_keys { + # Skip blank line + if { [string compare each_key $empty] == 0 } { + continue; + } + set ssh_setup_string "$ssh_setup_string;$ssh_setup_cmd \"$each_key\"" } - } - set pid [ spawn $remoteshell $node -l root /tmp/.ssh/copy.perl ] - expect { - "Are you sure you want to continue connecting (yes/no)?" { - send "yes\r" - exp_continue - } - "*ssword*" { - send "$word\r" - exp_continue - } - "Permission denied*" { - exec /bin/kill $pid + # Trim semicolon at the tail + set ssh_setup_string [string range $ssh_setup_string 1 end] + set pid [ spawn $remoteshell $node -l admin $ssh_setup_string ] + expect { + "Are you sure you want to continue connecting (yes/no)?" { + send "yes\r" + exp_continue + } + "*ssword*" { + send "$word\r" + exp_continue + } + "Permission denied*" { + exec /bin/kill $pid + } } } } @@ -273,7 +320,12 @@ if { [llength $argv] != 0 } { } foreach hn $nlist { set env(LC_ALL) "C" - set pid [ spawn $remoteshell $hn -l root echo test.success ] + if { [string compare $ssh_setup_cmd $empty] !=0 } { + set userid "admin" + } else { + set userid "root" + } + set pid [ spawn $remoteshell $hn -l $userid echo test.success ] expect { "Are you sure you want to continue connecting (yes/no)?" { send "yes\r"