10 """A simple ssh/scp wrapper for creating and interacting with ssh sessions via
14 ip_address (str): IP address of the node.
15 username (str): A username with access to the node.
16 prompt (str) (optional): Expected prompt on the host.
19 ssh (pexpect child): A handle to a session started by pexpect.spawn()
24 def __init__(self, ip_address, username=None, password=None, prompt=DEFAULT_PROMPT, key=None):
26 self.ip_address = ip_address
30 self.username = os.environ['USER']
32 logging.error('no USER variable in environment variable and no username provided')
37 self.password = os.environ['KEYPWORD']
39 logging.info('no ssh key password in environment, assuming unencrypted')
40 self.password = password
44 self.key = os.environ['CERT']
46 logging.info('no ssh key path provided in environment, assuming not using a ssh key')
50 """Opens a connection to `self.ip_address` using `self.username`."""
54 cmd = 'ssh -l {} {}'.format(self.username, self.ip_address)
56 # this is to provide a public key for the ssh session, which might be the most
58 cmd = 'ssh -i {} -l {} {}'.format(self.key, self.username, self.ip_address)
59 while retry_count < 4:
60 self.ssh = pexpect.spawn(cmd, timeout=5)
61 self.sshresponse = self.ssh.expect([self.prompt,
62 'Are you sure you want to continue connecting (yes/no)?',
64 'Enter passphrase for key.*:',
67 if self.sshresponse == 0:
69 elif self.sshresponse == 1:
70 self.ssh.sendline('yes')
71 self.sshresponse = self.ssh.expect([self.prompt,
72 'Enter passphrase for key.*:',
76 if self.sshresponse == 0:
78 elif self.sshresponse == 1:
79 if self.password is None:
80 logging.error('failed to login --- ssh key is encrypted but no pword provided')
83 self.ssh.sendline(self.password)
84 self.sshresponse = self.ssh.expect([self.prompt, 'Permission denied', pexpect.EOF, pexpect.TIMEOUT])
85 if self.sshresponse == 0:
88 logging.debug('failed to login --- response: {}'.format(self.sshresponse))
89 logging.debug('retry count: {}'.format(retry_count))
91 logging.debug('failed to login --- response: {}'.format(self.sshresponse))
92 logging.debug('retry count: {}'.format(retry_count))
94 elif self.sshresponse == 2:
95 # Verify we've connected to the self.ip_address
96 self.command('ifconfig | egrep --color=never "inet addr:|inet "', self.prompt)
97 self.sshresponse = self.ssh.expect([self.prompt, pexpect.EOF, pexpect.TIMEOUT])
98 result = re.search(str(self.ip_address), str(self.ssh.before))
100 logging.debug('not on host with ip {}'.format(self.ip_address))
101 logging.debug('retry count: {}'.format(retry_count))
105 elif self.sshresponse == 3:
106 if self.password is None:
107 logging.error('failed to login --- ssh key is encrypted but no pword provided')
110 self.ssh.sendline(self.password)
111 self.sshresponse = self.ssh.expect([self.prompt, 'Permission denied', pexpect.EOF, pexpect.TIMEOUT])
113 if self.sshresponse == 0:
116 logging.debug('failed to login --- response: {}'.format(self.sshresponse))
117 logging.debug('retry count: {}'.format(retry_count))
119 elif self.sshresponse == 4:
120 logging.debug('Unexpected EOF')
121 logging.debug('retry count: {}'.format(retry_count))
122 logging.debug('ssh.before: ' + str(self.ssh.before))
123 elif self.sshresponse == 5:
124 logging.debug('Unexpected Timeout')
125 logging.debug('retry count: {}'.format(retry_count))
126 logging.debug('ssh.before: ' + str(self.ssh.before))
131 logging.error('failed to login --- could not connect to host.')
134 def command(self, commandline, expectedline=DEFAULT_PROMPT, timeout=5):
135 """Sends `commandline` to `self.ip_address` and waits for `expectedline`."""
136 logging.debug(commandline)
137 self.ssh.sendline(commandline)
138 self.sshresponse = self.ssh.expect([expectedline, pexpect.EOF, pexpect.TIMEOUT], timeout=timeout)
139 if self.sshresponse == 0:
141 elif self.sshresponse == 1:
142 logging.debug('Unexpected EOF --- Expected: ' + expectedline)
143 logging.debug('ssh.before: ' + str(self.ssh.before))
144 elif self.sshresponse == 2:
145 logging.debug('Unexpected Timeout --- Expected: ' + expectedline)
146 logging.debug('ssh.before: ' + str(self.ssh.before))
148 return self.sshresponse
150 def close(self, timeout):
151 self.ssh.sendline('exit')
152 self.sshresponse = self.ssh.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=timeout)
153 if self.sshresponse == 0:
155 elif self.sshresponse == 1:
156 logging.debug('Unexpected Timeout --- Expected: EOF')
157 logging.debug('ssh.before: ' + str(self.ssh.before))
159 return self.sshresponse
161 def copy_from(self, remote_path, local_path):
162 cmd = 'scp {}@{}:{} {}'.format(self.username, self.ip_address, remote_path, local_path)
163 return self.copy(cmd)
165 def copy_to(self, local_path, remote_path):
166 cmd = 'scp {} {}@{}:{}'.format(local_path, self.username, self.ip_address, remote_path)
167 return self.copy(cmd)
172 while retry_count < 10:
173 scp_spawn = pexpect.spawn(cmd, timeout = 100)
174 scp_response = scp_spawn.expect(['Are you sure you want to continue connecting (yes/no)?',
175 'Enter passphrase for key.*:',
178 if scp_response == 0:
179 scp_spawn.sendline('yes')
180 scp_response = scp_spawn.expect([self.prompt,
181 'Enter passphrase for key.*:',
185 if scp_response == 0:
187 elif scp_response == 1:
188 if self.password is None:
189 logging.error('failed to scp --- ssh key is encrypted but no pword provided')
192 scp_spawn.sendline(self.password)
193 scp_response = scp_spawn.expect([self.prompt, 'Permission denied', pexpect.EOF, pexpect.TIMEOUT])
194 if scp_response == 0:
197 logging.debug('failed to scp --- response: {}'.format(scp_response))
198 logging.debug('retry count: {}'.format(retry_count))
201 logging.debug('scp failed with scp response {}'.format(scp_response))
202 logging.debug('retry count: {}'.format(retry_count))
203 elif scp_response == 1:
204 if self.password is None:
205 logging.error('failed to scp --- ssh key is encrypted but no pword provided')
208 scp_spawn.sendline(self.password)
209 scp_response = scp_spawn.expect([self.prompt, 'Permission denied', pexpect.EOF, pexpect.TIMEOUT])
210 if scp_response == 0:
213 logging.debug('failed to scp --- response: {}'.format(scp_response))
214 logging.debug('retry count: {}'.format(retry_count))
216 elif scp_response == 2:
217 logging.debug('copy succeeded')