Package diffpy :: Package pdfgui :: Package control :: Module connection
[hide private]
[frames] | no frames]

Source Code for Module diffpy.pdfgui.control.connection

  1  #!/usr/bin/env python 
  2  ############################################################################## 
  3  # 
  4  # PDFgui            by DANSE Diffraction group 
  5  #                   Simon J. L. Billinge 
  6  #                   (c) 2006 trustees of the Michigan State University. 
  7  #                   All rights reserved. 
  8  # 
  9  # File coded by:    Jiwu Liu 
 10  # 
 11  # See AUTHORS.txt for a list of people who contributed. 
 12  # See LICENSE.txt for license information. 
 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      #paramiko.util.log_to_file("/tmp/paramiko.log") 
 27  except ImportError: 
 28      # no remote execution available 
 29      pass 
 30         
31 -class _RawTunnel(object):
32 """A class simulates xmlrpclib.Transport. It wrapps up a paramiko channel to 33 provide a Transport interface. 34 """
35 - def __init__(self, conn):
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 # use a file interface to read/write 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
68 - def _parse(self, f, chan):
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
88 -class _WrapTunnel(xmlrpclib.Transport):
89 """A overload class of xmlrpclib.Transport. It changes make_connection() 90 method to use a paramiko channel instead of real socket 91 """
92 - def __init__(self, conn):
93 """initialize 94 95 conn -- Connection object 96 """ 97 self.conn = conn 98 self.chan = None
99
100 - def make_connection(self, address):
101 """make a HTTPConnection to remote host. 102 103 address -- hostname:port 104 """ 105 if self.chan is not None: 106 # clean up the previous channel 107 self.chan.close() 108 self.chan = self.conn.openTunnel(address) 109 h = httplib.HTTP(address) 110 111 #IMPORTANT: set its socket to our chan to prevent real connection 112 h._conn.sock = self.chan 113 return h
114 115
116 -class Connection:
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
137 - def __init__(self, owner):
138 """initialize 139 140 owner -- object who starts the connection 141 """ 142 self.owner = owner 143 144 # channels 145 self.transport = None 146 self.session = None 147 self.channel = None 148 149 # settings 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
158 - def isConnected(self):
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 # use password 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 # setup the transport channel 206 self.transport = transport 207 # stay alive over NAT 208 self.transport.set_keepalive(60) 209 return
210
211 - def close(self):
212 """close remote connection 213 All the tunnel will be closed as well. 214 """ 215 if self.transport: 216 self.transport.close()
217
218 - def __rsa_auth(self,transport):
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
234 - def __dsa_auth(self, transport):
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
250 - def getXMLRPCTransport(self):
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 # else, use _WrapTunnel for this purpose 259 return _RawTunnel(self)
260 #return _WrapTunnel(self) 261
262 - def openTunnel(self, address):
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: # It is for backward compatibility of paramiko, 275 raise ControlConnectError, "Can not open tunnel to '%s'"%address 276 277 return chan
278
279 - def execute(self, cmd, n = 1024):
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 # Check to see if the server started nicely 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 # Take the standard output from the server and get the port number. 296 return self.session.recv(n)
297 298 # version 299 __id__ = "$Id: connection.py 2980 2009-04-02 00:14:33Z juhas $" 300 301 # End of file 302