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