18a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen"""This class extends pexpect.spawn to specialize setting up SSH connections. 28a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny ChenThis adds methods for login, logout, and expecting the shell prompt. 38a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 48a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen$Id: pxssh.py 513 2008-02-09 18:26:13Z noah $ 58a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen""" 68a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 78a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chenfrom pexpect import * 88a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chenimport pexpect 98a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chenimport time 108a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 118a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen__all__ = ['ExceptionPxssh', 'pxssh'] 128a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 138a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen# Exception classes used by this module. 148a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chenclass ExceptionPxssh(ExceptionPexpect): 158a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """Raised for pxssh exceptions. 168a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """ 178a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 188a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chenclass pxssh (spawn): 198a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 208a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """This class extends pexpect.spawn to specialize setting up SSH 218a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen connections. This adds methods for login, logout, and expecting the shell 228a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen prompt. It does various tricky things to handle many situations in the SSH 238a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen login process. For example, if the session is your first login, then pxssh 248a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen automatically accepts the remote certificate; or if you have public key 258a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen authentication setup then pxssh won't wait for the password prompt. 268a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 278a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen pxssh uses the shell prompt to synchronize output from the remote host. In 288a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen order to make this more robust it sets the shell prompt to something more 298a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen unique than just $ or #. This should work on most Borne/Bash or Csh style 308a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen shells. 318a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 328a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen Example that runs a few commands on a remote server and prints the result:: 338a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 348a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen import pxssh 358a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen import getpass 368a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen try: 378a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s = pxssh.pxssh() 388a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen hostname = raw_input('hostname: ') 398a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen username = raw_input('username: ') 408a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen password = getpass.getpass('password: ') 418a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.login (hostname, username, password) 428a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.sendline ('uptime') # run a command 438a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.prompt() # match the prompt 448a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen print s.before # print everything before the prompt. 458a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.sendline ('ls -l') 468a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.prompt() 478a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen print s.before 488a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.sendline ('df') 498a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.prompt() 508a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen print s.before 518a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.logout() 528a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen except pxssh.ExceptionPxssh, e: 538a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen print "pxssh failed on login." 548a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen print str(e) 558a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 568a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen Note that if you have ssh-agent running while doing development with pxssh 578a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen then this can lead to a lot of confusion. Many X display managers (xdm, 588a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen gdm, kdm, etc.) will automatically start a GUI agent. You may see a GUI 598a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen dialog box popup asking for a password during development. You should turn 608a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen off any key agents during testing. The 'force_password' attribute will turn 618a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen off public key authentication. This will only work if the remote SSH server 628a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen is configured to allow password logins. Example of using 'force_password' 638a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen attribute:: 648a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 658a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s = pxssh.pxssh() 668a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.force_password = True 678a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen hostname = raw_input('hostname: ') 688a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen username = raw_input('username: ') 698a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen password = getpass.getpass('password: ') 708a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen s.login (hostname, username, password) 718a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """ 728a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 738a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen def __init__ (self, timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None): 748a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen spawn.__init__(self, None, timeout=timeout, maxread=maxread, searchwindowsize=searchwindowsize, logfile=logfile, cwd=cwd, env=env) 758a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 768a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.name = '<pxssh>' 778a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 788a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #SUBTLE HACK ALERT! Note that the command to set the prompt uses a 798a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #slightly different string than the regular expression to match it. This 808a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #is because when you set the prompt the command will echo back, but we 818a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #don't want to match the echoed command. So if we make the set command 828a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #slightly different than the regex we eliminate the problem. To make the 838a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #set command different we add a backslash in front of $. The $ doesn't 848a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #need to be escaped, but it doesn't hurt and serves to make the set 858a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #prompt command different than the regex. 868a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 878a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # used to match the command-line prompt 888a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.UNIQUE_PROMPT = "\[PEXPECT\][\$\#] " 898a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.PROMPT = self.UNIQUE_PROMPT 908a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 918a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # used to set shell command-line prompt to UNIQUE_PROMPT. 928a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.PROMPT_SET_SH = "PS1='[PEXPECT]\$ '" 938a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.PROMPT_SET_CSH = "set prompt='[PEXPECT]\$ '" 948a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.SSH_OPTS = "-o'RSAAuthentication=no' -o 'PubkeyAuthentication=no'" 958a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # Disabling X11 forwarding gets rid of the annoying SSH_ASKPASS from 968a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # displaying a GUI password dialog. I have not figured out how to 978a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # disable only SSH_ASKPASS without also disabling X11 forwarding. 988a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # Unsetting SSH_ASKPASS on the remote side doesn't disable it! Annoying! 998a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #self.SSH_OPTS = "-x -o'RSAAuthentication=no' -o 'PubkeyAuthentication=no'" 1008a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.force_password = False 1018a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.auto_prompt_reset = True 1028a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1038a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen def levenshtein_distance(self, a,b): 1048a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1058a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """This calculates the Levenshtein distance between a and b. 1068a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """ 1078a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1088a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen n, m = len(a), len(b) 1098a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if n > m: 1108a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen a,b = b,a 1118a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen n,m = m,n 1128a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen current = range(n+1) 1138a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen for i in range(1,m+1): 1148a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen previous, current = current, [i]+[0]*n 1158a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen for j in range(1,n+1): 1168a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen add, delete = previous[j]+1, current[j-1]+1 1178a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen change = previous[j-1] 1188a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if a[j-1] != b[i-1]: 1198a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen change = change + 1 1208a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen current[j] = min(add, delete, change) 1218a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return current[n] 1228a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1238a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen def sync_original_prompt (self): 1248a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1258a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """This attempts to find the prompt. Basically, press enter and record 1268a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen the response; press enter again and record the response; if the two 1278a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen responses are similar then assume we are at the original prompt. This 1288a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen is a slow function. It can take over 10 seconds. """ 1298a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1308a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # All of these timing pace values are magic. 1318a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # I came up with these based on what seemed reliable for 1328a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # connecting to a heavily loaded machine I have. 1338a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # If latency is worse than these values then this will fail. 1348a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1358a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen try: 1368a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.read_nonblocking(size=10000,timeout=1) # GAS: Clear out the cache before getting the prompt 1378a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen except TIMEOUT: 1388a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen pass 1398a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen time.sleep(0.1) 1408a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline() 1418a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen time.sleep(0.5) 1428a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen x = self.read_nonblocking(size=1000,timeout=1) 1438a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen time.sleep(0.1) 1448a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline() 1458a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen time.sleep(0.5) 1468a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen a = self.read_nonblocking(size=1000,timeout=1) 1478a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen time.sleep(0.1) 1488a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline() 1498a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen time.sleep(0.5) 1508a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen b = self.read_nonblocking(size=1000,timeout=1) 1518a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen ld = self.levenshtein_distance(a,b) 1528a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen len_a = len(a) 1538a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if len_a == 0: 1548a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return False 1558a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if float(ld)/len_a < 0.4: 1568a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return True 1578a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return False 1588a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1598a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen ### TODO: This is getting messy and I'm pretty sure this isn't perfect. 1608a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen ### TODO: I need to draw a flow chart for this. 1618a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen def login (self,server,username,password='',terminal_type='ansi',original_prompt=r"[#$]",login_timeout=10,port=None,auto_prompt_reset=True): 1628a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1638a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """This logs the user into the given server. It uses the 1648a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 'original_prompt' to try to find the prompt right after login. When it 1658a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen finds the prompt it immediately tries to reset the prompt to something 1668a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen more easily matched. The default 'original_prompt' is very optimistic 1678a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen and is easily fooled. It's more reliable to try to match the original 1688a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen prompt as exactly as possible to prevent false matches by server 1698a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen strings such as the "Message Of The Day". On many systems you can 1708a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen disable the MOTD on the remote server by creating a zero-length file 1718a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen called "~/.hushlogin" on the remote server. If a prompt cannot be found 1728a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen then this will not necessarily cause the login to fail. In the case of 1738a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen a timeout when looking for the prompt we assume that the original 1748a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen prompt was so weird that we could not match it, so we use a few tricks 1758a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen to guess when we have reached the prompt. Then we hope for the best and 1768a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen blindly try to reset the prompt to something more unique. If that fails 1778a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen then login() raises an ExceptionPxssh exception. 1788a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1798a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen In some situations it is not possible or desirable to reset the 1808a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen original prompt. In this case, set 'auto_prompt_reset' to False to 1818a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh 1828a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen uses a unique prompt in the prompt() method. If the original prompt is 1838a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen not reset then this will disable the prompt() method unless you 1848a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen manually set the PROMPT attribute. """ 1858a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1868a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen ssh_options = '-q' 1878a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if self.force_password: 1888a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen ssh_options = ssh_options + ' ' + self.SSH_OPTS 1898a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if port is not None: 1908a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen ssh_options = ssh_options + ' -p %s'%(str(port)) 1918a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen cmd = "ssh %s -l %s %s" % (ssh_options, username, server) 1928a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1938a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # This does not distinguish between a remote server 'password' prompt 1948a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # and a local ssh 'passphrase' prompt (for unlocking a private key). 1958a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen spawn._spawn(self, cmd) 1968a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT, "(?i)connection closed by remote host"], timeout=login_timeout) 1978a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 1988a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # First phase 1998a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if i==0: 2008a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # New certificate -- always accept it. 2018a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # This is what you get if SSH does not have the remote host's 2028a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # public key stored in the 'known_hosts' cache. 2038a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline("yes") 2048a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) 2058a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if i==2: # password or passphrase 2068a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline(password) 2078a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) 2088a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if i==4: 2098a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline(terminal_type) 2108a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) 2118a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2128a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # Second phase 2138a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if i==0: 2148a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # This is weird. This should not happen twice in a row. 2158a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2168a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen raise ExceptionPxssh ('Weird error. Got "are you sure" prompt twice.') 2178a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen elif i==1: # can occur if you have a public key pair set to authenticate. 2188a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen ### TODO: May NOT be OK if expect() got tricked and matched a false prompt. 2198a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen pass 2208a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen elif i==2: # password prompt again 2218a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # For incorrect passwords, some ssh servers will 2228a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # ask for the password again, others return 'denied' right away. 2238a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # If we get the password prompt again then this means 2248a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # we didn't get the password right the first time. 2258a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2268a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen raise ExceptionPxssh ('password refused') 2278a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen elif i==3: # permission denied -- password was bad. 2288a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2298a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen raise ExceptionPxssh ('permission denied') 2308a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen elif i==4: # terminal type again? WTF? 2318a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2328a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen raise ExceptionPxssh ('Weird error. Got "terminal type" prompt twice.') 2338a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen elif i==5: # Timeout 2348a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #This is tricky... I presume that we are at the command-line prompt. 2358a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #It may be that the shell prompt was so weird that we couldn't match 2368a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #it. Or it may be that we couldn't log in for some other reason. I 2378a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #can't be sure, but it's safe to guess that we did login because if 2388a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #I presume wrong and we are not logged in then this should be caught 2398a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen #later when I try to set the shell prompt. 2408a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen pass 2418a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen elif i==6: # Connection closed by remote host 2428a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2438a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen raise ExceptionPxssh ('connection closed') 2448a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen else: # Unexpected 2458a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2468a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen raise ExceptionPxssh ('unexpected login response') 2478a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if not self.sync_original_prompt(): 2488a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2498a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen raise ExceptionPxssh ('could not synchronize with original prompt') 2508a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # We appear to be in. 2518a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen # set shell prompt to something unique. 2528a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if auto_prompt_reset: 2538a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if not self.set_unique_prompt(): 2548a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2558a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen raise ExceptionPxssh ('could not set shell prompt\n'+self.before) 2568a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return True 2578a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2588a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen def logout (self): 2598a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2608a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """This sends exit to the remote shell. If there are stopped jobs then 2618a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen this automatically sends exit twice. """ 2628a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2638a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline("exit") 2648a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen index = self.expect([EOF, "(?i)there are stopped jobs"]) 2658a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if index==1: 2668a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline("exit") 2678a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.expect(EOF) 2688a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.close() 2698a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2708a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen def prompt (self, timeout=20): 2718a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2728a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """This matches the shell prompt. This is little more than a short-cut 2738a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen to the expect() method. This returns True if the shell prompt was 2748a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen matched. This returns False if there was a timeout. Note that if you 2758a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen called login() with auto_prompt_reset set to False then you should have 2768a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen manually set the PROMPT attribute to a regex pattern for matching the 2778a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen prompt. """ 2788a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2798a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen i = self.expect([self.PROMPT, TIMEOUT], timeout=timeout) 2808a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if i==1: 2818a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return False 2828a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return True 2838a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2848a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen def set_unique_prompt (self): 2858a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2868a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen """This sets the remote prompt to something more unique than # or $. 2878a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen This makes it easier for the prompt() method to match the shell prompt 2888a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen unambiguously. This method is called automatically by the login() 2898a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen method, but you may want to call it manually if you somehow reset the 2908a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen shell prompt. For example, if you 'su' to a different user then you 2918a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen will need to manually reset the prompt. This sends shell commands to 2928a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen the remote host to set the prompt, so this assumes the remote host is 2938a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen ready to receive commands. 2948a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 2958a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen Alternatively, you may use your own prompt pattern. Just set the PROMPT 2968a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen attribute to a regular expression that matches it. In this case you 2978a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen should call login() with auto_prompt_reset=False; then set the PROMPT 2988a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen attribute. After that the prompt() method will try to match your prompt 2998a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen pattern.""" 3008a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 3018a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline ("unset PROMPT_COMMAND") 3028a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline (self.PROMPT_SET_SH) # sh-style 3038a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen i = self.expect ([TIMEOUT, self.PROMPT], timeout=10) 3048a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if i == 0: # csh-style 3058a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen self.sendline (self.PROMPT_SET_CSH) 3068a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen i = self.expect ([TIMEOUT, self.PROMPT], timeout=10) 3078a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen if i == 0: 3088a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return False 3098a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen return True 3108a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen 3118a3c0430323c28c1fbe8ceecd2cd8e58b64a9295Johnny Chen# vi:ts=4:sw=4:expandtab:ft=python: 312