diff --git a/perl-xCAT/xCAT/DSHCLI.pm b/perl-xCAT/xCAT/DSHCLI.pm index eef1b0fa1..df7279d65 100644 --- a/perl-xCAT/xCAT/DSHCLI.pm +++ b/perl-xCAT/xCAT/DSHCLI.pm @@ -1228,6 +1228,7 @@ sub fork_fanout_dsh # execute and remove the /tmp file build which is a copy of the # input -E file + #print "Command=@dsh_command\n"; @process_info = xCAT::DSHCore->fork_output($user_target, @dsh_command); if ($process_info[0] == -2) @@ -1876,10 +1877,10 @@ sub stream_error xCAT::MsgUtils->message("I", $rsp, $::CALLBACK); } - # my $rsp = {}; - # $rsp->{error}->[0] = - # "$user_target remote command had return code $$target_properties{'target-rc'}"; - # xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); + #my $rsp = {}; + #$rsp->{error}->[0] = + # "$user_target remote command had return code $$target_properties{'target-rc'}"; + #xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); my $rsp = {}; $rsp->{error}->[0] = @@ -2248,40 +2249,42 @@ sub config_dsh if ($$options{'devicetype'}) { $ENV{'DEVICETYPE'} = $$options{'devicetype'}; - my $devicepath = $$options{'devicetype'}; - $devicepath =~ s/::/\//g; + my $devicename = $$options{'devicetype'}; + $devicename =~ s/::/\//g; my $rsp = {}; - $rsp->{data}->[0] = "Processing $devicepath device type"; + $rsp->{data}->[0] = "Processing $devicename device type"; $dsh_trace && xCAT::MsgUtils->message("I", $rsp, $::CALLBACK); - # process the config file - - $devicepath = "/var/opt/xcat/" . $devicepath . "/config"; - - # Get configuration from $::XCATDEVCFGDIR - # used for QLogic and Mellanox - if (-e $devicepath) - { - my $deviceconf = get_config($devicepath); - - # Get all dsh section configuration - foreach my $entry (keys %{$$deviceconf{'xdsh'}}) - { + # process the config file. check /var/opt/xcat/... first, if the config + # file is not found, goto /opt/xcat/share/devicetype + my $devicepath = "/var/opt/xcat/" . $devicename . "/config"; + if (! -e $devicepath) { + $devicepath="$::XCATROOT/share/xcat/devicetype/" . $devicename . "/config"; + } + # Get configuration from $::XCATDEVCFGDIR + # used for QLogic and Mellanox + 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 - { + + } + } + else + { my $rsp = {}; $rsp->{error}->[0] = "EMsgMISSING_DEV_CFG"; xCAT::MsgUtils->message('E', $rsp, $::CALLBACK); - } + } } !$$options{'node-rsh'} @@ -4080,10 +4083,15 @@ sub parse_and_run_dsh 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 $devicename = $options{'devicetype'}; + $devicename =~ s/::/\//g; + my $devicepath = "/var/opt/xcat/" . $devicename . "/config"; + # go to backup directory if the config file + # cannot be found under /var/opt/xcat/... + if (! -e $devicepath) { + $devicepath="$::XCATROOT/share/xcat/devicetype/" . $devicename . "/config"; + } + if (-e $devicepath) { my $deviceconf = get_config($devicepath); diff --git a/xCAT-server/lib/perl/xCAT/RShellAPI.pm b/xCAT-server/lib/perl/xCAT/RShellAPI.pm index 08a61e67a..272ea1734 100644 --- a/xCAT-server/lib/perl/xCAT/RShellAPI.pm +++ b/xCAT-server/lib/perl/xCAT/RShellAPI.pm @@ -23,7 +23,7 @@ use xCAT::MsgUtils; A command array for the ssh command with the appropriate arguments as defined in the $config hash table =cut -##################################################### +##################################################### sub remote_shell_command { my ( $class, $config, $exec_path ) = @_; @@ -51,6 +51,23 @@ sub remote_shell_command { return @command; } +################################################################## +=head3 + run_remote_shell_api + + This routine tried ssh then telnet to logon to a node and + run a sequence of commands. + Arguments: + $node - node name + $user - user login name + $passed - user login password + $cmds - a list of commands seperated by semicolon. + Returns: + [error code, output] + error code: 0 sucess + non-zero: failed. the output contains the error message. +=cut +######################################################################### sub run_remote_shell_api { require xCAT::SSHInteract; my $node=shift; @@ -58,9 +75,13 @@ sub run_remote_shell_api { my $passwd=shift; my $args = join(" ", @_); my $t; + my $prompt='.*[\>\#\$]$'; + my $more_prompt='(.*key to continue.*|--More--\s*$)'; + my $verbose=0; + my $output; - if(0) { - print "start SSH session...\n"; + eval { + $output="start SSH session...\n"; $t = new xCAT::SSHInteract( -username=>$user, -password=>$passwd, @@ -69,108 +90,206 @@ sub run_remote_shell_api { -output_record_separator=>"\r", Timeout=>5, Errmode=>'return', - Prompt=>'/.*[\>\#]$/', + Prompt=>"/$prompt/", ); }; my $errmsg=$@; $errmsg =~ s/ at (.*) line (\d)+//g; - print "$errmsg\n"; + $output.="$errmsg\n"; my $rc=1; if (not $t) {#ssh failed.. fallback to a telnet attempt - print "start Telnet session...\n"; + $output.="start Telnet session...\n"; require Net::Telnet; $t = new Net::Telnet( Timeout=>5, Errmode=>'return', - Prompt=>'/.*[\>\#]$/', + Prompt=>"/$prompt/", ); $rc = $t->open($node); if ($rc) { my $pw_tried=0; + my $login_done=0; my ($prematch, $match)= $t->waitfor(Match => '/login[: ]*$/i', Match => '/username[: ]*$/i', Match => '/password[: ]*$/i', + Match => "/$prompt/", Errmode => "return"); - if (($match =~ /username[: ]*$/i) || ($match =~ /login[: ]*$/i )) { + if ($verbose) { + print "1. prematch=$prematch\n match=$match\n"; + } + if ($match =~ /$prompt/) { + $login_done=1; + } elsif (($match =~ /username[: ]*$/i) || ($match =~ /login[: ]*$/i )) { # user name if ($user) { if (! $t->put(String => "$user\n", Errmode => "return")) { - print "login disconnected\n"; - return [1, "login disconnected"]; + $output.="login disconnected\n"; + return [1, $output]; } } else { - print "Username is required.\n"; - return [1, "Username is required."]; + $output.="Username is required.\n"; + return [1, $output]; } } elsif ($match =~ /password[: ]*$/i) { if ($passwd) { $pw_tried=1; if (! $t->put(String => "$passwd\n", Errmode => "return")) { - print "login disconnected\n"; - return [1, "login disconnected"]; + $output.="Login disconnected\n"; + return [1, $output]; } } else { - print "password is required.\n"; - return [1, "Passwordis required."]; + $output.="Password is required.\n"; + return [1, $output]; } } - - ($prematch, $match)= $t->waitfor(Match => '/login[: ]*$/i', + + if (!$login_done) { + ($prematch, $match)= $t->waitfor(Match => '/login[: ]*$/i', Match => '/username[: ]*$/i', Match => '/password[: ]*$/i', + Match => "/$prompt/", Errmode => "return"); - if (($match =~ /username[: ]*$/i) || ($match =~ /login[: ]*$/i )) { - print "Incorrect username.\n"; - return [1, "Incorrect username."]; - } elsif ($match =~ /password[: ]*$/i) { - if ($pw_tried) { - print "Incorrect password.\n"; - return [1, "Incorrect password."]; + if ($verbose) { + print "2. prematch=$prematch\n match=$match\n"; } - if ($passwd) { - if (! $t->put(String => "$passwd\n", - Errmode => "return")) { - print "login disconnected\n"; - return [1, "login disconnected"]; + if ($match =~ /$prompt/) { + $login_done=1; + } elsif (($match =~ /username[: ]*$/i) || ($match =~ /login[: ]*$/i )) { + $output.="Incorrect username.\n"; + return [1, $output]; + } elsif ($match =~ /password[: ]*$/i) { + if ($pw_tried) { + $output.="Incorrect password.\n"; + return [1, $output]; + } + if ($passwd) { + if (! $t->put(String => "$passwd\n", + Errmode => "return")) { + $output.="Login disconnected\n"; + return [1, $output]; + } + } else { + $output.="Password is required.\n"; + return [1, $output]; + } + } + + if (!login_done) { + #Wait for command prompt + ($prematch, $match) = $t->waitfor(Match => '/login[: ]*$/i', + Match => '/username[: ]*$/i', + Match => '/password[: ]*$/i', + Match => "/$prompt/", + Errmode => "return"); + if ($verbose) { + print "3. prematch=$prematch\n match=$match\n"; + } + + if ($match =~ /$prompt/) { + $login_done=1; + } elsif ($match =~ /login[: ]*$/i or $match =~ /username[: ]*$/i or $match =~ /password[: ]*$/i) { + $output.="Login failed: bad login name or password\n"; + return [1, $output]; + } else { + if ($t->errmsg) { + $output.= $t->errmsg . "\n"; + return [1, $output]; + + } } - } else { - print "password is required.\n"; - return [1, "Passwordis required."]; } } - - - #Wait for command prompt - ($prematch, $match) = $t->waitfor(Match => '/login[: ]*$/i', - Match => '/username[: ]*$/i', - Match => '/password[: ]*$/i', - Match => '/\>/', - Errmode => "return"); - - #print "prematch=$prematch, match=$match\n"; - if ($match =~ /login[: ]*$/i or $match =~ /username[: ]*$/i or $match =~ /password[: ]*$/i) { - print "login failed: bad login name or password\n"; - return [1, "login failed: bad login name or password"]; - } } } if (!$rc) { - print "Error: " . $t->errmsg . "\n"; - return([1, $t->errmsg]); + $output.=$t->errmsg . "\n"; + return [1, $output]; } $rc = 0; - my $output; + my $try_more=0; my @cmd_array=split(';', $args); + foreach my $cmd (@cmd_array) { - #my @data = $t->cmd($cmd); - my @data= $t->cmd(String =>$cmd); - $output .= "command:$cmd\n@data\n"; - print "command:$cmd\n@data\n"; + if ($verbose) { + print "command:$cmd\n"; + } + while (1) { + if ($try_more) { + #This is for second and consequent pages. + #if the user disables the paging, then this code will never run. + #To disable paging (which is recommended), + #they need to add a command before any other commands + #For Cisco switch: terminal length 0 + #For BNT switch: terminal-length 0 + #For example: + # xdsh --type EthSwitch "terminal length 0;show vlan" + if (! $t->put(String => " ", + Errmode => "return")) { + $output.="Command $cmd failed: " . $t->errmsg() . "\n"; + return [1, $output]; + } + if ($verbose) { + my $lastline=$t->lastline(); + print "---lastline=$lastline\n"; + } + ($prematch, $match) = $t->waitfor(Match => "/$more_prompt/", + Match => "/$prompt/", + Errmode => "return", + Timeout=>3); + } else { + # for the first page which may contian all + if (! $t->put(String => "$cmd\n", + Errmode => "return")) { + $output.="Command $cmd failed." . $t->errmsg() . "\n"; + return [1, $output]; + } + if ($verbose) { + my $lastline=$t->lastline(); + print "lastline=$lastline\n"; + } + ($prematch, $match) = $t->waitfor(Match => "/$more_prompt/", + Match => "/$prompt/", + Match => '/password:\s*$/i', + Errmode => "return", + Timeout=>3); + } + + if ($verbose) { + print "-----prematch=$prematch\nmatch=$match\n"; + } + + my $error=$t->errmsg(); + if ($error) { + $output.="Command $cmd failed: $error\n"; + return [1, $output]; + } + + # remove the first line + if ($try_more) { + my @data=split("\n", $prematch); + shift @data; + #shift @data; + #shift @data; + $prematch=join("\n", @data); + #add a newline at the end if not there + my $lastchar=substr($prematch, -1, 1); + if ($lastchar ne "\n") { + $prematch .= "\n"; + } + } + $output .= $prematch; + + if ($match =~ /$more_prompt/i) { + $try_more=1; + } else { + last; + } + } } $t->close(); return [0, $output]; @@ -178,4 +297,6 @@ sub run_remote_shell_api { + + 1; diff --git a/xCAT-server/sbin/rshell_api b/xCAT-server/sbin/rshell_api index 5e7c0f9fe..13a4abbcc 100755 --- a/xCAT-server/sbin/rshell_api +++ b/xCAT-server/sbin/rshell_api @@ -28,6 +28,13 @@ if (!GetOptions( my $node = $ARGV[0]; -xCAT::RShellAPI::run_remote_shell_api($node, $username, $passwd, @ARGV[1 .. $#ARGV]); +my $output =xCAT::RShellAPI::run_remote_shell_api($node, $username, $passwd, @ARGV[1 .. $#ARGV]); +my $rc=0; +my $data; +if ($output && (@$output > 1)) { + $rc=$output->[0]; + $data=$output->[1]; +} -exit 0; +print "$data"; +exit $rc; diff --git a/xCAT-server/share/xcat/EthSwitch/config b/xCAT-server/share/xcat/devicetype/EthSwitch/BNT/config similarity index 62% rename from xCAT-server/share/xcat/EthSwitch/config rename to xCAT-server/share/xcat/devicetype/EthSwitch/BNT/config index 4aee5250b..366073db8 100644 --- a/xCAT-server/share/xcat/EthSwitch/config +++ b/xCAT-server/share/xcat/devicetype/EthSwitch/BNT/config @@ -1,5 +1,5 @@ [main] ssh-setup-command= [xdsh] -pre-command=NULL +pre-command=terminal-length 0; post-command=NULL diff --git a/xCAT-server/share/xcat/devicetype/EthSwitch/Cisco/config b/xCAT-server/share/xcat/devicetype/EthSwitch/Cisco/config new file mode 100644 index 000000000..19eb4b934 --- /dev/null +++ b/xCAT-server/share/xcat/devicetype/EthSwitch/Cisco/config @@ -0,0 +1,5 @@ +[main] +ssh-setup-command= +[xdsh] +pre-command=terminal length 0; +post-command=NULL diff --git a/xCAT-server/share/xcat/devicetype/EthSwitch/config b/xCAT-server/share/xcat/devicetype/EthSwitch/config new file mode 100644 index 000000000..503a742b5 --- /dev/null +++ b/xCAT-server/share/xcat/devicetype/EthSwitch/config @@ -0,0 +1,11 @@ +#This is a configuration file for running xdsh again ethernet switches. +#If the switch type is not defined, you can create a subdirctory for that +#type of switch. For example, for cisco swith, create a subdirectory +# called "Cisco" under EthSwitch. copy this config file there. And +#put the command that disables the paging in pre-command +#pre-command=terminal lenth 0; +[main] +ssh-setup-command= +[xdsh] +pre-command=NULL +post-command=NULL diff --git a/xCAT-server/share/xcat/ib/scripts/Mellanox/config b/xCAT-server/share/xcat/devicetype/IBSwitch/Mellanox/config similarity index 100% rename from xCAT-server/share/xcat/ib/scripts/Mellanox/config rename to xCAT-server/share/xcat/devicetype/IBSwitch/Mellanox/config diff --git a/xCAT-server/share/xcat/ib/scripts/QLogic/config b/xCAT-server/share/xcat/devicetype/IBSwitch/QLogic/config similarity index 100% rename from xCAT-server/share/xcat/ib/scripts/QLogic/config rename to xCAT-server/share/xcat/devicetype/IBSwitch/QLogic/config