mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 03:12:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			386 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			386 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| #!/usr/bin/perl
 | |
| # IBM(c) 2012 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| package xCAT::RShellAPI;
 | |
| 
 | |
| BEGIN
 | |
| {
 | |
|     $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | |
| }
 | |
| use lib "$::XCATROOT/lib/perl";
 | |
| use xCAT::MsgUtils;
 | |
| #use Data::Dumper;
 | |
| 
 | |
| #######################################################
 | |
| =head3
 | |
|         remote_shell_command
 | |
| 
 | |
|         This routine constructs an remote shell command using the
 | |
|         given arguments
 | |
|         Arguments:
 | |
|         	$class - Calling module name (discarded)
 | |
|         	$config - Reference to remote shell command configuration hash table
 | |
|         	$exec_path - Path to ssh executable
 | |
|         Returns:
 | |
|         	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 ) = @_;
 | |
| 	#print Dumper($config);
 | |
| 
 | |
| 	my @command = ();
 | |
| 
 | |
| 	push @command, $exec_path;
 | |
| 
 | |
| 	if ( $$config{'options'} ) {
 | |
| 		my @options = split ' ', $$config{'options'};
 | |
| 		push @command, @options;
 | |
| 	}
 | |
| 	
 | |
|         my @tmp;
 | |
| 	if ( $$config{'trace'} ) {
 | |
| 	    push @command, "-v";
 | |
| 	}
 | |
| 	if ( $$config{'remotecmdproto'} &&  ($$config{'remotecmdproto'} =~ /^telnet$/)) {
 | |
| 	    push @command, "-t";
 | |
| 	}
 | |
| 	if ($$config{'user'} && ($$config{'user'}  !~ /^none$/i)) {
 | |
| 	    @tmp=split(' ', "-l $$config{'user'}");
 | |
| 	    push @command, @tmp;
 | |
| 	}
 | |
| 	if ($$config{'password'} && ($$config{'password'} !~ /^none$/i)) {
 | |
| 	    @tmp=split(' ', "-p $$config{'password'}");
 | |
| 	    push @command, @tmp;
 | |
| 	}
 | |
| 	push @command, "$$config{'hostname'}";
 | |
| 	push @command, $$config{'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;
 | |
|     my $user=shift;
 | |
|     my $passwd=shift;
 | |
|     my $telnet=shift;
 | |
|     my $verbose=shift;
 | |
|     my $args = join(" ", @_);
 | |
|     my $t;
 | |
|     my $prompt='.*[\>\#\$]\s*$';
 | |
|     my $more_prompt='(.*key to continue.*|.*--More--\s*|.*--\(more.*\)--.*$)';
 | |
|     my $output;
 | |
|     my $errmsg;
 | |
|     my $ssh_tried=0;
 | |
|     my $nl_tried=0;
 | |
| 
 | |
|     if (!$telnet) { 
 | |
| 	eval {
 | |
| 	    $output="start SSH session...\n";
 | |
| 	    $ssh_tried=1;
 | |
| 	    $t = new  xCAT::SSHInteract(
 | |
| 		-username=>$user,
 | |
| 		-password=>$passwd,
 | |
| 		-host=>$node,
 | |
| 		-nokeycheck=>1,
 | |
| 		-output_record_separator=>"\r",
 | |
| 		Timeout=>10, 
 | |
| 		Errmode=>'return',
 | |
| 		Prompt=>"/$prompt/",
 | |
| 		);
 | |
| 	};
 | |
| 	$errmsg=$@;
 | |
| 	$errmsg =~ s/ at (.*) line (\d)+//g;
 | |
| 	$output.="$errmsg\n";
 | |
|     }
 | |
| 
 | |
|     my $rc=1;
 | |
|     if ($t) {
 | |
| 	#Wait for command prompt
 | |
| 	my ($prematch, $match) = $t->waitfor(Match => '/login[: ]*$/i',
 | |
| 					     Match => '/username[: ]*$/i',
 | |
| 					     Match => '/password[: ]*$/i',
 | |
| 					     Match => "/$prompt/",
 | |
| 					     Errmode => "return");
 | |
| 	if ($verbose) {
 | |
| 	    print "0. prematch=$prematch\n match=$match\n";
 | |
| 	}
 | |
| 
 | |
| 	if ($match !~ /$prompt/) {
 | |
| 	    return [1, $output];
 | |
| 	}
 | |
| 
 | |
|     } else {
 | |
|         #ssh failed.. fallback to a telnet attempt
 | |
| 	if ($ssh_tried) {
 | |
| 	    $output.="Warning: SSH failed, will try Telnet. Please set switches.protocol=telnet next time if you wish to use telnet directly.\n";
 | |
| 	}
 | |
| 	$output.="start Telnet session...\n";
 | |
| 	require Net::Telnet;
 | |
| 	$t = new Net::Telnet(
 | |
| 	    Timeout=>10, 
 | |
| 	    Errmode=>'return',
 | |
| 	    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 ($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")) {
 | |
| 			$output.="login disconnected\n";
 | |
| 			return [1, $output];
 | |
| 		    }
 | |
| 		} else {
 | |
| 		    $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")) {
 | |
| 			$output.="Login disconnected\n";
 | |
| 			return [1, $output];
 | |
| 		    }
 | |
| 		} else {
 | |
| 		    $output.="Password is required.\n";
 | |
| 		    return [1, $output];
 | |
| 		}
 | |
| 	    }
 | |
| 	   
 | |
|             if (!$login_done) {
 | |
| 		($prematch, $match)= $t->waitfor(Match => '/login[: ]*$/i',
 | |
| 					     Match => '/username[: ]*$/i',
 | |
| 					     Match => '/password[: ]*$/i',
 | |
| 					     Match => "/$prompt/",
 | |
| 					     Errmode => "return");
 | |
| 	
 | |
| 		if ($verbose) {
 | |
| 		    print "2. prematch=$prematch\n match=$match\n";
 | |
| 		}
 | |
| 		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];
 | |
| 		    }
 | |
| 		} else {
 | |
|                     # for some switches like BNT, user has to type an extra new line 
 | |
|                     # in order to get the prompt. 
 | |
| 		    if ($verbose) {
 | |
| 			print " add a newline\n";
 | |
| 		    }
 | |
| 		    $nl_tried=1;
 | |
| 		    if (! $t->put(String => "\n",
 | |
| 				  Errmode => "return")) {
 | |
| 			$output.="Login disconnected\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 (!$nl_tried) {
 | |
| 			    # for some switches like BNT, user has to type an extra new line 
 | |
| 			    # in order to get the prompt. 
 | |
| 			    if ($verbose) {
 | |
| 				print " add a newline\n";
 | |
| 			    }
 | |
| 			    $nl_tried=1;
 | |
| 			    if (! $t->put(String => "\n",
 | |
| 					  Errmode => "return")) {
 | |
| 				$output.="Login disconnected\n";
 | |
| 				return [1, $output];
 | |
| 			    }
 | |
| 			}
 | |
| 			else {
 | |
| 			    if ($t->errmsg) {
 | |
| 				$output.= $t->errmsg . "\n";
 | |
| 				return [1, $output];
 | |
| 				
 | |
| 			    }
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 
 | |
|                 #check if the extra newline helps or not
 | |
| 		if (!$login_done) {
 | |
| 		    #Wait for command prompt
 | |
| 		    ($prematch, $match) = $t->waitfor(Match => "/$prompt/",
 | |
| 						      Errmode => "return");
 | |
| 		    if ($verbose) {
 | |
| 			print "4. prematch=$prematch\n match=$match\n";
 | |
| 		    }
 | |
| 		    
 | |
| 		    if ($match =~ /$prompt/) {
 | |
| 			$login_done=1;
 | |
| 		    } else {
 | |
| 			if ($t->errmsg) {
 | |
| 			    $output.= $t->errmsg . "\n";
 | |
| 			    return [1, $output];
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (!$rc) {
 | |
|         $output.=$t->errmsg . "\n";
 | |
| 	return [1, $output];
 | |
|     }
 | |
| 
 | |
|     $rc = 0;
 | |
|     my $try_more=0;
 | |
|     my @cmd_array=split(';', $args);
 | |
|     
 | |
|     foreach my $cmd (@cmd_array) {
 | |
| 	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 <swname> --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/i",
 | |
| 						  Match => "/$prompt/",
 | |
| 						  Errmode => "return",
 | |
| 						  Timeout=>10);
 | |
| 	    } 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/i",
 | |
| 						  Match => "/$prompt/",
 | |
| 						  Match => '/password:\s*$/i',
 | |
| 						  Errmode => "return",
 | |
| 						  Timeout=>10);
 | |
| 	    }
 | |
| 
 | |
| 	    if ($verbose) {
 | |
| 		print "-----prematch=$prematch\nmatch=$match\n";
 | |
|             }
 | |
| 
 | |
|             my $error=$t->errmsg();
 | |
| 	    if ($error) {
 | |
| 	    	$output.="Command $cmd failed: $error\n";
 | |
| 	    	return [1, $output];
 | |
| 	    }
 | |
|             
 | |
|             # 
 | |
|             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];
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 1;
 |