1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import xmlrpclib
17 import httplib
18 import socket
19
20 from diffpy.pdfgui.control.controlerrors import *
21
22 RemoteExecution = 0
23 try:
24 import paramiko
25 RemoteExecution = 1
26
27 except ImportError:
28
29 pass
30
32 """A class simulates xmlrpclib.Transport. It wrapps up a paramiko channel to
33 provide a Transport interface.
34 """
36 """initialize
37
38 conn -- Connection object
39 """
40 self.conn = conn
41
42 - def request(self, address, handler, request_body, verbose=0):
43 """function to handle XMLRPC request
44
45 address -- string of hostname:port for remote host
46 handler --
47 request_body -- xml encoded request
48 verbose -- if it is 1, print verbose information
49 """
50 chan = self.conn.openTunnel(address)
51
52 f = chan.makefile('rw')
53 header = "POST /%s HTTP/1.1\n"%handler
54 header += "User-Agent: PDFGUI\n"
55 header += "Host: %s\n"%address
56 header += "Content-Type: text/xml\n"
57 header += "Content-Length: %d\n\n"%len(request_body)
58
59 try:
60 f.write(header)
61 f.write(request_body)
62 f.flush()
63 return self._parse(f, chan)
64 except (xmlrpclib.Error, socket.error, Exception),err:
65 self.conn.owner.onError(address)
66 raise ControlConnectError, "XMLRPC Connection error: " + str(err)
67
69 n = 0
70 line = f.readline()
71 if line.split()[1] != '200':
72 raise ControlConnectError, 'HTTP request failed' + line
73
74 while n == 0:
75 if line.lower().startswith('content-length:'):
76 n = int(line[15:].strip())
77 line = f.readline()
78 content = f.read(n)
79 f.close()
80 chan.close()
81
82 p, u = xmlrpclib.getparser()
83 p.feed(content)
84 p.close()
85 return u.close()
86
87
89 """A overload class of xmlrpclib.Transport. It changes make_connection()
90 method to use a paramiko channel instead of real socket
91 """
93 """initialize
94
95 conn -- Connection object
96 """
97 self.conn = conn
98 self.chan = None
99
101 """make a HTTPConnection to remote host.
102
103 address -- hostname:port
104 """
105 if self.chan is not None:
106
107 self.chan.close()
108 self.chan = self.conn.openTunnel(address)
109 h = httplib.HTTP(address)
110
111
112 h._conn.sock = self.chan
113 return h
114
115
117 """SSH2 Connection and tunnel support
118
119 Data member:
120 transport: a paramiko transport object
121 session: a paramiko session
122 channel: a fake channel for HTTP transport through ssh tunnelling
123
124 host: remote host name
125 user: remote account user name
126 port: remote port
127 auth: authentication type
128 passwd: password for remote account
129 keyFile: local file for RSA/DSA keys
130 passphrase: passphrase of RSA/DSA
131 """
132 DefaultPort = 22
133 PSWDAUTH = 0
134 RSAAUTH = 1
135 DSAAUTH = 2
136
138 """initialize
139
140 owner -- object who starts the connection
141 """
142 self.owner = owner
143
144
145 self.transport = None
146 self.session = None
147 self.channel = None
148
149
150 self.host = None
151 self.user = None
152 self.port = Connection.DefaultPort
153 self.auth = Connection.PSWDAUTH
154 self.passwd = None
155 self.keyFile = None
156 self.passphrase = None
157
159 """check if this connection is still alive
160 """
161 return self.transport is not None and self.transport.is_active()
162
163 - def connect(self, host, user, port, auth, passwd=None, keyFile=None, passphrase=None):
164 """make a ssh2 connection to the remote host.
165
166 host -- remote host machine
167 user -- username
168 port -- port of remote ssh service
169 auth -- authenticate method, PSWDAUTH,RSAAUTH,DSAAUTH
170 passwd -- only required if use PSWDAUTH
171 keyFile -- only required if use RSAAUTH or DSAAUTH
172 passphrase -- only required if use RSAAUTH or DSAAUTH, and passphrase is set
173 """
174 self.host = host
175 self.user = user
176 self.port = port
177 self.auth = auth
178 self.passwd = passwd
179 self.keyFile = keyFile
180 self.passphrase = passphrase
181
182 try:
183 transport = paramiko.Transport((self.host, self.port))
184 transport.start_client()
185 if auth == Connection.RSAAUTH:
186 self.__rsa_auth(transport)
187 elif auth == Connection.DSAAUTH:
188 self.__dsa_auth(transport)
189 else:
190
191 transport.auth_password(self.user, self.passwd)
192 if not transport.is_authenticated():
193 raise ControlAuthError,\
194 "Connection: '%s@%s' does not exist or password is wrong"%(self.user, self.host)
195 except IOError:
196 raise ControlFileError,\
197 "Connection: can't open rsa/dsa file %s"%self.keyFile
198 except (paramiko.SSHException, paramiko.BadAuthenticationType):
199 raise ControlAuthError,\
200 "Connection: '%s@%s' failed in authentication"%(self.user, self.host)
201 except socket.error,errmsg:
202 raise ControlConnectError,\
203 "Connection: %s %s"%(self.host, errmsg)
204
205
206 self.transport = transport
207
208 self.transport.set_keepalive(60)
209 return
210
212 """close remote connection
213 All the tunnel will be closed as well.
214 """
215 if self.transport:
216 self.transport.close()
217
219 """Authorize connection with default rsa key location.
220
221 transport -- paramiko transport
222 """
223 try:
224 key = paramiko.RSAKey.from_private_key_file(self.keyFile)
225 except paramiko.PasswordRequiredException:
226 if self.passphrase is not None:
227 key = paramiko.RSAKey.from_private_key_file(self.keyFile, self.passphrase)
228 else:
229 raise ControlAuthError,\
230 "Connection: '%s@%s' requires passphrase for RSA"%(self.user, self.host)
231 transport.auth_publickey(self.user, key)
232 return
233
235 """Authorize connection with default dsa key location.
236
237 transport -- paramiko transport
238 """
239 try:
240 key = paramiko.DSAKey.from_private_key_file(self.keyFile)
241 except paramiko.PasswordRequiredException:
242 if self.passphrase is not None:
243 key = paramiko.RSAKey.from_private_key_file(self.keyFile, self.passphrase)
244 else:
245 raise ControlAuthError,\
246 "Connection: '%s@%s' requires passphrase for DSA"%(self.user, self.host)
247 transport.auth_publickey(self.user, key)
248 return
249
251 """make a HTTP transport to remote host through SSH tunnelling.
252
253 return: a xmlrpc transport instance
254 """
255 if self.transport is None:
256 raise ControlRuntimeError, "ssh connection not available"
257
258
259 return _RawTunnel(self)
260
261
263 """As requested by xmlrpclib.Transport, open a ssh2 tunnel
264
265 address -- host:port string
266 return: a sshe tunnel
267 """
268 host,port = address.split(':')
269 port = int (port)
270 try:
271 chan = self.transport.open_channel('direct-tcpip',(host,port), ('localhost',port))
272 except paramiko.SSHException:
273 chan = None
274 if chan is None:
275 raise ControlConnectError, "Can not open tunnel to '%s'"%address
276
277 return chan
278
280 """execute a command
281
282 cmd -- command to be run remotely
283 n -- size of output to be returned
284 return -- output
285 """
286 self.session = self.transport.open_session()
287 self.session.exec_command(cmd)
288
289
290 exit_status = self.session.recv_exit_status()
291 if exit_status:
292 errmsg = self.session.recv_stderr(n)
293 raise ControlRuntimeError, "Connection: '%s' failed: %s"% (cmd,errmsg)
294
295
296 return self.session.recv(n)
297
298
299 __id__ = "$Id: connection.py 2980 2009-04-02 00:14:33Z juhas $"
300
301
302