1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 import os
34 import sys
35 import shlex
36 import subprocess
37 import threading
38
39 try:
40 import paramiko
41 except Exception, e:
42 print >> sys.stderr, e
43 sys.exit(1)
44
45 import rospy
46 import node_manager_fkie as nm
47 from supervised_popen import SupervisedPopen
48
50 ''' '''
51
57
59 return "AuthenticationRequest on " + self.host + " for " + self.user + "::" + repr(self.error)
60
61
63 '''
64 The class to handle the SSH sessions to the remote hosts.
65 '''
66 SSH_SESSIONS = {}
67 SSH_AUTH = {}
68
69
71 self.mutex = threading.RLock()
72
83
84 - def transfer(self, host, local_file, remote_file, user=None, pw=None, auto_pw_request=False):
85 '''
86 Copies a file to a remote location using paramiko sfpt.
87 @param host: the host
88 @type host: C{str}
89 @param local_file: the local file
90 @type local_file: str
91 @param remote_file: the remote file name
92 @type remote_file: str
93 @param user: user name
94 @param pw: the password
95 '''
96 with self.mutex:
97 try:
98 ssh = self._getSSH(host, nm.settings().host_user(host) if user is None else user, pw, True, auto_pw_request)
99 if not ssh is None:
100 sftp = ssh.open_sftp()
101 try:
102 sftp.mkdir(os.path.dirname(remote_file))
103 except:
104 pass
105 sftp.put(local_file, remote_file)
106 rospy.loginfo("SSH COPY %s -> %s@%s:%s", local_file, ssh._transport.get_username(), host, remote_file)
107 except AuthenticationRequest as e:
108 raise
109 except Exception, e:
110 raise
111
112 - def ssh_exec(self, host, cmd, user=None, pw=None, auto_pw_request=False, get_pty=False, close_stdin=False, close_stdout=False, close_stderr=False):
113 '''
114 Executes a command on remote host. Returns the output channels with
115 execution result or None. The connection will be established using paramiko
116 SSH library.
117 @param host: the host
118 @type host: C{str}
119 @param cmd: the list with command and arguments
120 @type cmd: C{[str,...]}
121 @param user: user name
122 @param pw: the password
123 @return: the 4-tuple stdin, stdout, stderr and boolean of the executing command
124 @rtype: C{tuple(ChannelFile, ChannelFile, ChannelFile, boolean)}
125 @see: U{http://www.lag.net/paramiko/docs/paramiko.SSHClient-class.html#exec_command}
126 '''
127 with self.mutex:
128 try:
129 ssh = self._getSSH(host, nm.settings().host_user(host) if user is None else user, pw, True, auto_pw_request)
130 if not ssh is None:
131 cmd_str = str(' '.join(cmd))
132 rospy.loginfo("REMOTE execute on %s@%s: %s", ssh._transport.get_username(), host, cmd_str)
133 (stdin, stdout, stderr) = (None, None, None)
134 if get_pty:
135 (stdin, stdout, stderr) = ssh.exec_command(cmd_str, get_pty=get_pty)
136 else:
137 (stdin, stdout, stderr) = ssh.exec_command(cmd_str)
138 if close_stdin:
139 stdin.close()
140 if close_stdout:
141 stdout.close()
142 if close_stderr:
143 stderr.close()
144 return stdin, stdout, stderr, True
145 except AuthenticationRequest as e:
146 raise
147 except Exception as e:
148 raise
149 raise Exception('Can not login @%s'%host)
150
151
153 '''
154 Executes a command on remote host using a terminal with X11 forwarding.
155 @todo: establish connection using paramiko SSH library.
156 @param host: the host
157 @type host: C{str}
158 @param cmd: the list with command and arguments
159 @type cmd: C{[str,...]}
160 @param title: the title of the new opened terminal, if it is None, no new terminal will be created
161 @type title: C{str} or C{None}
162 @param user: user name
163 @return: the result of C{subprocess.Popen(command)}
164 @see: U{http://docs.python.org/library/subprocess.html?highlight=subproces#subprocess}
165 '''
166 with self.mutex:
167 try:
168
169 user = nm.settings().host_user(host) if user is None else user
170 if self.SSH_AUTH.has_key(host):
171 user = self.SSH_AUTH[host]
172
173 ssh_str = ' '.join(['/usr/bin/ssh',
174 '-aqtx',
175 '-oClearAllForwardings=yes',
176 '-oConnectTimeout=5',
177 '-oStrictHostKeyChecking=no',
178 '-oVerifyHostKeyDNS=no',
179 '-oCheckHostIP=no',
180 ''.join([user, '@', host])])
181 if not title is None:
182 cmd_str = nm.settings().terminal_cmd([ssh_str, ' '.join(cmd)], title)
183 else:
184 cmd_str = str(' '.join([ssh_str, ' '.join(cmd)]))
185 rospy.loginfo("REMOTE x11 execute on %s: %s", host, cmd_str)
186 return SupervisedPopen(shlex.split(cmd_str), id=str(title), description="REMOTE x11 execute on %s: %s"%(host, cmd_str))
187 except:
188 raise
189
190 - def _getSSH(self, host, user, pw=None, do_connect=True, auto_pw_request=False):
191 '''
192 @return: the paramiko ssh client
193 @rtype: L{paramiko.SSHClient}
194 @raise BadHostKeyException: - if the server's host key could not be verified
195 @raise AuthenticationException: - if authentication failed
196 @raise SSHException: - if there was any other error connecting or establishing an SSH session
197 @raise socket.error: - if a socket error occurred while connecting
198 '''
199 session = SSHhandler.SSH_SESSIONS.get(host, paramiko.SSHClient())
200 if session is None or (not session.get_transport() is None and (not session.get_transport().is_active() or session._transport.get_username() != user)):
201 t = SSHhandler.SSH_SESSIONS.pop(host)
202 del t
203 if self.SSH_AUTH.has_key(host):
204 del self.SSH_AUTH[host]
205 session = SSHhandler.SSH_SESSIONS.get(host, paramiko.SSHClient())
206 if session._transport is None:
207 session.set_missing_host_key_policy(paramiko.AutoAddPolicy())
208 while (session.get_transport() is None or not session.get_transport().authenticated) and do_connect:
209 try:
210 session.connect(host, username=user, password=pw, timeout=3, compress=True)
211 self.SSH_AUTH[host] = user
212 except Exception, e:
213
214
215 if str(e) in ['Authentication failed.', 'No authentication methods available', 'Private key file is encrypted']:
216 if auto_pw_request:
217
218 res, user, pw = self._requestPW(user, host)
219 if not res:
220 return None
221 self.SSH_AUTH[host] = user
222 else:
223 raise AuthenticationRequest(user, host, str(e))
224 else:
225 rospy.logwarn("ssh connection to %s failed: %s", host, str(e))
226 raise Exception(' '.join(["ssh connection to", host, "failed:", str(e)]))
227 else:
228 SSHhandler.SSH_SESSIONS[host] = session
229 if not session.get_transport() is None:
230 session.get_transport().set_keepalive(10)
231 return session
232
234 '''
235 Open the dialog to input the user name and password to open an SSH connection.
236 '''
237 from python_qt_binding import QtCore
238 from python_qt_binding import loadUi
239 from python_qt_binding import QtGui
240 import os
241 result = False
242 pw = None
243 pwInput = QtGui.QDialog()
244 ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'PasswordInput.ui')
245 loadUi(ui_file, pwInput)
246 pwInput.setWindowTitle(''.join(['Enter the password for user ', user, ' on ', host]))
247 pwInput.userLine.setText(str(user))
248 pwInput.pwLine.setText("")
249 pwInput.pwLine.setFocus(QtCore.Qt.OtherFocusReason)
250 if pwInput.exec_():
251 result = True
252 user = pwInput.userLine.text()
253 pw = pwInput.pwLine.text()
254 return result, user, pw
255