| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 """ | 2 """ |
| 3 Client for file transfer services offered by RSS (Remote Shell Server). | 3 Client for file transfer services offered by RSS (Remote Shell Server). |
| 4 | 4 |
| 5 @author: Michael Goldish (mgoldish@redhat.com) | 5 @author: Michael Goldish (mgoldish@redhat.com) |
| 6 @copyright: 2008-2010 Red Hat Inc. | 6 @copyright: 2008-2010 Red Hat Inc. |
| 7 """ | 7 """ |
| 8 | 8 |
| 9 import socket, struct, time, sys, os, glob | 9 import socket, struct, time, sys, os, glob |
| 10 | 10 |
| 11 # Globals | 11 # Globals |
| 12 CHUNKSIZE = 65536 | 12 CHUNKSIZE = 65536 |
| 13 | 13 |
| 14 # Protocol message constants | 14 # Protocol message constants |
| 15 RSS_MAGIC = 0x525353 | 15 RSS_MAGIC = 0x525353 |
| 16 RSS_OK = 1 | 16 RSS_OK = 1 |
| 17 RSS_ERROR = 2 | 17 RSS_ERROR = 2 |
| 18 RSS_UPLOAD = 3 | 18 RSS_UPLOAD = 3 |
| 19 RSS_DOWNLOAD = 4 | 19 RSS_DOWNLOAD = 4 |
| 20 RSS_SET_PATH = 5 | 20 RSS_SET_PATH = 5 |
| 21 RSS_CREATE_FILE = 6 | 21 RSS_CREATE_FILE = 6 |
| 22 RSS_CREATE_DIR = 7 | 22 RSS_CREATE_DIR = 7 |
| 23 RSS_LEAVE_DIR = 8 | 23 RSS_LEAVE_DIR = 8 |
| 24 RSS_DONE = 9 | 24 RSS_DONE = 9 |
| 25 | 25 |
| 26 # See rss.cpp for protocol details. | 26 # See rss.cpp for protocol details. |
| 27 | 27 |
| 28 | 28 |
| 29 class FileTransferError(Exception): | 29 class FileTransferError(Exception): |
| 30 pass | 30 def __init__(self, msg, e=None, filename=None): |
| 31 Exception.__init__(self, msg, e, filename) |
| 32 self.msg = msg |
| 33 self.e = e |
| 34 self.filename = filename |
| 35 |
| 36 def __str__(self): |
| 37 s = self.msg |
| 38 if self.e and self.filename: |
| 39 s += " (error: %s, filename: %s)" % (self.e, self.filename) |
| 40 elif self.e: |
| 41 s += " (%s)" % self.e |
| 42 elif self.filename: |
| 43 s += " (filename: %s)" % self.filename |
| 44 return s |
| 31 | 45 |
| 32 | 46 |
| 33 class FileTransferConnectError(FileTransferError): | 47 class FileTransferConnectError(FileTransferError): |
| 34 pass | 48 pass |
| 35 | 49 |
| 36 | 50 |
| 37 class FileTransferTimeoutError(FileTransferError): | 51 class FileTransferTimeoutError(FileTransferError): |
| 38 pass | 52 pass |
| 39 | 53 |
| 40 | 54 |
| 41 class FileTransferProtocolError(FileTransferError): | 55 class FileTransferProtocolError(FileTransferError): |
| 42 pass | 56 pass |
| 43 | 57 |
| 44 | 58 |
| 45 class FileTransferSendError(FileTransferError): | 59 class FileTransferSocketError(FileTransferError): |
| 46 pass | 60 pass |
| 47 | 61 |
| 48 | 62 |
| 49 class FileTransferServerError(FileTransferError): | 63 class FileTransferServerError(FileTransferError): |
| 50 pass | 64 def __init__(self, errmsg): |
| 65 FileTransferError.__init__(self, None, errmsg) |
| 66 |
| 67 def __str__(self): |
| 68 s = "Server said: %r" % self.e |
| 69 if self.filename: |
| 70 s += " (filename: %s)" % self.filename |
| 71 return s |
| 51 | 72 |
| 52 | 73 |
| 53 class FileTransferNotFoundError(FileTransferError): | 74 class FileTransferNotFoundError(FileTransferError): |
| 54 pass | 75 pass |
| 55 | 76 |
| 56 | 77 |
| 57 class FileTransferClient(object): | 78 class FileTransferClient(object): |
| 58 """ | 79 """ |
| 59 Connect to a RSS (remote shell server) and transfer files. | 80 Connect to a RSS (remote shell server) and transfer files. |
| 60 """ | 81 """ |
| 61 | 82 |
| 62 def __init__(self, address, port, timeout=10): | 83 def __init__(self, address, port, log_func=None, timeout=20): |
| 63 """ | 84 """ |
| 64 Connect to a server. | 85 Connect to a server. |
| 65 | 86 |
| 66 @param address: The server's address | 87 @param address: The server's address |
| 67 @param port: The server's port | 88 @param port: The server's port |
| 89 @param log_func: If provided, transfer stats will be passed to this |
| 90 function during the transfer |
| 68 @param timeout: Time duration to wait for connection to succeed | 91 @param timeout: Time duration to wait for connection to succeed |
| 69 @raise FileTransferConnectError: Raised if the connection fails | 92 @raise FileTransferConnectError: Raised if the connection fails |
| 70 @raise FileTransferProtocolError: Raised if an incorrect magic number | |
| 71 is received | |
| 72 """ | 93 """ |
| 73 self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | 94 self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 74 self._socket.settimeout(timeout) | 95 self._socket.settimeout(timeout) |
| 75 try: | 96 try: |
| 76 self._socket.connect((address, port)) | 97 self._socket.connect((address, port)) |
| 77 except socket.error: | 98 except socket.error, e: |
| 78 raise FileTransferConnectError("Could not connect to server") | 99 raise FileTransferConnectError("Cannot connect to server at " |
| 100 "%s:%s" % (address, port), e) |
| 79 try: | 101 try: |
| 80 if self._receive_msg(timeout) != RSS_MAGIC: | 102 if self._receive_msg(timeout) != RSS_MAGIC: |
| 81 raise FileTransferConnectError("Received wrong magic number") | 103 raise FileTransferConnectError("Received wrong magic number") |
| 82 except FileTransferTimeoutError: | 104 except FileTransferTimeoutError: |
| 83 raise FileTransferConnectError("Timeout expired while waiting to " | 105 raise FileTransferConnectError("Timeout expired while waiting to " |
| 84 "receive magic number") | 106 "receive magic number") |
| 85 self._send(struct.pack("=i", CHUNKSIZE)) | 107 self._send(struct.pack("=i", CHUNKSIZE)) |
| 108 self._log_func = log_func |
| 109 self._last_time = time.time() |
| 110 self._last_transferred = 0 |
| 111 self.transferred = 0 |
| 86 | 112 |
| 87 | 113 |
| 88 def __del__(self): | 114 def __del__(self): |
| 89 self.close() | 115 self.close() |
| 90 | 116 |
| 91 | 117 |
| 92 def close(self): | 118 def close(self): |
| 93 """ | 119 """ |
| 94 Close the connection. | 120 Close the connection. |
| 95 """ | 121 """ |
| 96 self._socket.close() | 122 self._socket.close() |
| 97 | 123 |
| 98 | 124 |
| 99 def _send(self, str): | 125 def _send(self, str, timeout=60): |
| 100 try: | 126 try: |
| 127 if timeout <= 0: |
| 128 raise socket.timeout |
| 129 self._socket.settimeout(timeout) |
| 101 self._socket.sendall(str) | 130 self._socket.sendall(str) |
| 102 except socket.error: | 131 except socket.timeout: |
| 103 raise FileTransferSendError("Could not send data to server") | 132 raise FileTransferTimeoutError("Timeout expired while sending " |
| 133 "data to server") |
| 134 except socket.error, e: |
| 135 raise FileTransferSocketError("Could not send data to server", e) |
| 104 | 136 |
| 105 | 137 |
| 106 def _receive(self, size, timeout=10): | 138 def _receive(self, size, timeout=60): |
| 107 strs = [] | 139 strs = [] |
| 108 end_time = time.time() + timeout | 140 end_time = time.time() + timeout |
| 109 while size > 0: | 141 try: |
| 110 try: | 142 while size > 0: |
| 111 self._socket.settimeout(max(0.0001, end_time - time.time())) | 143 timeout = end_time - time.time() |
| 144 if timeout <= 0: |
| 145 raise socket.timeout |
| 146 self._socket.settimeout(timeout) |
| 112 data = self._socket.recv(size) | 147 data = self._socket.recv(size) |
| 113 except socket.timeout: | 148 if not data: |
| 114 raise FileTransferTimeoutError("Timeout expired while " | 149 raise FileTransferProtocolError("Connection closed " |
| 115 "receiving data from server") | 150 "unexpectedly while " |
| 116 except socket.error: | 151 "receiving data from " |
| 117 raise FileTransferProtocolError("Error receiving data from " | 152 "server") |
| 118 "server") | 153 strs.append(data) |
| 119 if not data: | 154 size -= len(data) |
| 120 raise FileTransferProtocolError("Connection closed " | 155 except socket.timeout: |
| 121 "unexpectedly") | 156 raise FileTransferTimeoutError("Timeout expired while receiving " |
| 122 strs.append(data) | 157 "data from server") |
| 123 size -= len(data) | 158 except socket.error, e: |
| 159 raise FileTransferSocketError("Error receiving data from server", |
| 160 e) |
| 124 return "".join(strs) | 161 return "".join(strs) |
| 125 | 162 |
| 126 | 163 |
| 127 def _send_packet(self, str): | 164 def _report_stats(self, str): |
| 128 self._send(struct.pack("=I", len(str))) | 165 if self._log_func: |
| 129 self._send(str) | 166 dt = time.time() - self._last_time |
| 167 if dt >= 1: |
| 168 transferred = self.transferred / 1048576. |
| 169 speed = (self.transferred - self._last_transferred) / dt |
| 170 speed /= 1048576. |
| 171 self._log_func("%s %.3f MB (%.3f MB/sec)" % |
| 172 (str, transferred, speed)) |
| 173 self._last_time = time.time() |
| 174 self._last_transferred = self.transferred |
| 130 | 175 |
| 131 | 176 |
| 132 def _receive_packet(self, timeout=10): | 177 def _send_packet(self, str, timeout=60): |
| 133 size = struct.unpack("=I", self._receive(4))[0] | 178 self._send(struct.pack("=I", len(str))) |
| 134 return self._receive(size, timeout) | 179 self._send(str, timeout) |
| 180 self.transferred += len(str) + 4 |
| 181 self._report_stats("Sent") |
| 135 | 182 |
| 136 | 183 |
| 137 def _send_file_chunks(self, filename, timeout=30): | 184 def _receive_packet(self, timeout=60): |
| 185 size = struct.unpack("=I", self._receive(4))[0] |
| 186 str = self._receive(size, timeout) |
| 187 self.transferred += len(str) + 4 |
| 188 self._report_stats("Received") |
| 189 return str |
| 190 |
| 191 |
| 192 def _send_file_chunks(self, filename, timeout=60): |
| 193 if self._log_func: |
| 194 self._log_func("Sending file %s" % filename) |
| 138 f = open(filename, "rb") | 195 f = open(filename, "rb") |
| 139 try: | 196 try: |
| 140 end_time = time.time() + timeout | 197 try: |
| 141 while time.time() < end_time: | 198 end_time = time.time() + timeout |
| 142 data = f.read(CHUNKSIZE) | 199 while True: |
| 143 self._send_packet(data) | 200 data = f.read(CHUNKSIZE) |
| 144 if len(data) < CHUNKSIZE: | 201 self._send_packet(data, end_time - time.time()) |
| 145 break | 202 if len(data) < CHUNKSIZE: |
| 146 else: | 203 break |
| 147 raise FileTransferTimeoutError("Timeout expired while sending " | 204 except FileTransferError, e: |
| 148 "file %s" % filename) | 205 e.filename = filename |
| 206 raise |
| 149 finally: | 207 finally: |
| 150 f.close() | 208 f.close() |
| 151 | 209 |
| 152 | 210 |
| 153 def _receive_file_chunks(self, filename, timeout=30): | 211 def _receive_file_chunks(self, filename, timeout=60): |
| 212 if self._log_func: |
| 213 self._log_func("Receiving file %s" % filename) |
| 154 f = open(filename, "wb") | 214 f = open(filename, "wb") |
| 155 try: | 215 try: |
| 156 end_time = time.time() + timeout | 216 try: |
| 157 while True: | 217 end_time = time.time() + timeout |
| 158 try: | 218 while True: |
| 159 data = self._receive_packet(end_time - time.time()) | 219 data = self._receive_packet(end_time - time.time()) |
| 160 except FileTransferTimeoutError: | 220 f.write(data) |
| 161 raise FileTransferTimeoutError("Timeout expired while " | 221 if len(data) < CHUNKSIZE: |
| 162 "receiving file %s" % | 222 break |
| 163 filename) | 223 except FileTransferError, e: |
| 164 except FileTransferProtocolError: | 224 e.filename = filename |
| 165 raise FileTransferProtocolError("Error receiving file %s" % | 225 raise |
| 166 filename) | |
| 167 f.write(data) | |
| 168 if len(data) < CHUNKSIZE: | |
| 169 break | |
| 170 finally: | 226 finally: |
| 171 f.close() | 227 f.close() |
| 172 | 228 |
| 173 | 229 |
| 174 def _send_msg(self, msg, timeout=10): | 230 def _send_msg(self, msg, timeout=60): |
| 175 self._send(struct.pack("=I", msg)) | 231 self._send(struct.pack("=I", msg)) |
| 176 | 232 |
| 177 | 233 |
| 178 def _receive_msg(self, timeout=10): | 234 def _receive_msg(self, timeout=60): |
| 179 s = self._receive(4, timeout) | 235 s = self._receive(4, timeout) |
| 180 return struct.unpack("=I", s)[0] | 236 return struct.unpack("=I", s)[0] |
| 181 | 237 |
| 182 | 238 |
| 183 def _handle_transfer_error(self): | 239 def _handle_transfer_error(self): |
| 184 # Save original exception | 240 # Save original exception |
| 185 e = sys.exc_info() | 241 e = sys.exc_info() |
| 186 try: | 242 try: |
| 187 # See if we can get an error message | 243 # See if we can get an error message |
| 188 msg = self._receive_msg() | 244 msg = self._receive_msg() |
| 189 except FileTransferError: | 245 except FileTransferError: |
| 190 # No error message -- re-raise original exception | 246 # No error message -- re-raise original exception |
| 191 raise e[0], e[1], e[2] | 247 raise e[0], e[1], e[2] |
| 192 if msg == RSS_ERROR: | 248 if msg == RSS_ERROR: |
| 193 errmsg = self._receive_packet() | 249 errmsg = self._receive_packet() |
| 194 raise FileTransferServerError("Server said: %s" % errmsg) | 250 raise FileTransferServerError(errmsg) |
| 195 raise e[0], e[1], e[2] | 251 raise e[0], e[1], e[2] |
| 196 | 252 |
| 197 | 253 |
| 198 class FileUploadClient(FileTransferClient): | 254 class FileUploadClient(FileTransferClient): |
| 199 """ | 255 """ |
| 200 Connect to a RSS (remote shell server) and upload files or directory trees. | 256 Connect to a RSS (remote shell server) and upload files or directory trees. |
| 201 """ | 257 """ |
| 202 | 258 |
| 203 def __init__(self, address, port, timeout=10): | 259 def __init__(self, address, port, log_func=None, timeout=20): |
| 204 """ | 260 """ |
| 205 Connect to a server. | 261 Connect to a server. |
| 206 | 262 |
| 207 @param address: The server's address | 263 @param address: The server's address |
| 208 @param port: The server's port | 264 @param port: The server's port |
| 265 @param log_func: If provided, transfer stats will be passed to this |
| 266 function during the transfer |
| 209 @param timeout: Time duration to wait for connection to succeed | 267 @param timeout: Time duration to wait for connection to succeed |
| 210 @raise FileTransferConnectError: Raised if the connection fails | 268 @raise FileTransferConnectError: Raised if the connection fails |
| 211 @raise FileTransferProtocolError: Raised if an incorrect magic number | 269 @raise FileTransferProtocolError: Raised if an incorrect magic number |
| 212 is received | 270 is received |
| 213 @raise FileTransferSendError: Raised if the RSS_UPLOAD message cannot | 271 @raise FileTransferSocketError: Raised if the RSS_UPLOAD message cannot |
| 214 be sent to the server | 272 be sent to the server |
| 215 """ | 273 """ |
| 216 super(FileUploadClient, self).__init__(address, port, timeout) | 274 super(FileUploadClient, self).__init__(address, port, log_func, timeout) |
| 217 self._send_msg(RSS_UPLOAD) | 275 self._send_msg(RSS_UPLOAD) |
| 218 | 276 |
| 219 | 277 |
| 220 def _upload_file(self, path, end_time): | 278 def _upload_file(self, path, end_time): |
| 221 if os.path.isfile(path): | 279 if os.path.isfile(path): |
| 222 self._send_msg(RSS_CREATE_FILE) | 280 self._send_msg(RSS_CREATE_FILE) |
| 223 self._send_packet(os.path.basename(path)) | 281 self._send_packet(os.path.basename(path)) |
| 224 self._send_file_chunks(path, max(0, end_time - time.time())) | 282 self._send_file_chunks(path, end_time - time.time()) |
| 225 elif os.path.isdir(path): | 283 elif os.path.isdir(path): |
| 226 self._send_msg(RSS_CREATE_DIR) | 284 self._send_msg(RSS_CREATE_DIR) |
| 227 self._send_packet(os.path.basename(path)) | 285 self._send_packet(os.path.basename(path)) |
| 228 for filename in os.listdir(path): | 286 for filename in os.listdir(path): |
| 229 self._upload_file(os.path.join(path, filename), end_time) | 287 self._upload_file(os.path.join(path, filename), end_time) |
| 230 self._send_msg(RSS_LEAVE_DIR) | 288 self._send_msg(RSS_LEAVE_DIR) |
| 231 | 289 |
| 232 | 290 |
| 233 def upload(self, src_pattern, dst_path, timeout=600): | 291 def upload(self, src_pattern, dst_path, timeout=600): |
| 234 """ | 292 """ |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 270 except FileTransferError: | 328 except FileTransferError: |
| 271 self._handle_transfer_error() | 329 self._handle_transfer_error() |
| 272 else: | 330 else: |
| 273 # If nothing was transferred, raise an exception | 331 # If nothing was transferred, raise an exception |
| 274 if not matches: | 332 if not matches: |
| 275 raise FileTransferNotFoundError("Pattern %s does not " | 333 raise FileTransferNotFoundError("Pattern %s does not " |
| 276 "match any files or " | 334 "match any files or " |
| 277 "directories" % | 335 "directories" % |
| 278 src_pattern) | 336 src_pattern) |
| 279 # Look for RSS_OK or RSS_ERROR | 337 # Look for RSS_OK or RSS_ERROR |
| 280 msg = self._receive_msg(max(0, end_time - time.time())) | 338 msg = self._receive_msg(end_time - time.time()) |
| 281 if msg == RSS_OK: | 339 if msg == RSS_OK: |
| 282 return | 340 return |
| 283 elif msg == RSS_ERROR: | 341 elif msg == RSS_ERROR: |
| 284 errmsg = self._receive_packet() | 342 errmsg = self._receive_packet() |
| 285 raise FileTransferServerError("Server said: %s" % errmsg) | 343 raise FileTransferServerError(errmsg) |
| 286 else: | 344 else: |
| 287 # Neither RSS_OK nor RSS_ERROR found | 345 # Neither RSS_OK nor RSS_ERROR found |
| 288 raise FileTransferProtocolError("Received unexpected msg") | 346 raise FileTransferProtocolError("Received unexpected msg") |
| 289 except: | 347 except: |
| 290 # In any case, if the transfer failed, close the connection | 348 # In any case, if the transfer failed, close the connection |
| 291 self.close() | 349 self.close() |
| 292 raise | 350 raise |
| 293 | 351 |
| 294 | 352 |
| 295 class FileDownloadClient(FileTransferClient): | 353 class FileDownloadClient(FileTransferClient): |
| 296 """ | 354 """ |
| 297 Connect to a RSS (remote shell server) and download files or directory trees
. | 355 Connect to a RSS (remote shell server) and download files or directory trees
. |
| 298 """ | 356 """ |
| 299 | 357 |
| 300 def __init__(self, address, port, timeout=10): | 358 def __init__(self, address, port, log_func=None, timeout=20): |
| 301 """ | 359 """ |
| 302 Connect to a server. | 360 Connect to a server. |
| 303 | 361 |
| 304 @param address: The server's address | 362 @param address: The server's address |
| 305 @param port: The server's port | 363 @param port: The server's port |
| 364 @param log_func: If provided, transfer stats will be passed to this |
| 365 function during the transfer |
| 306 @param timeout: Time duration to wait for connection to succeed | 366 @param timeout: Time duration to wait for connection to succeed |
| 307 @raise FileTransferConnectError: Raised if the connection fails | 367 @raise FileTransferConnectError: Raised if the connection fails |
| 308 @raise FileTransferProtocolError: Raised if an incorrect magic number | 368 @raise FileTransferProtocolError: Raised if an incorrect magic number |
| 309 is received | 369 is received |
| 310 @raise FileTransferSendError: Raised if the RSS_UPLOAD message cannot | 370 @raise FileTransferSendError: Raised if the RSS_UPLOAD message cannot |
| 311 be sent to the server | 371 be sent to the server |
| 312 """ | 372 """ |
| 313 super(FileDownloadClient, self).__init__(address, port, timeout) | 373 super(FileDownloadClient, self).__init__(address, port, log_func, timeou
t) |
| 314 self._send_msg(RSS_DOWNLOAD) | 374 self._send_msg(RSS_DOWNLOAD) |
| 315 | 375 |
| 316 | 376 |
| 317 def download(self, src_pattern, dst_path, timeout=600): | 377 def download(self, src_pattern, dst_path, timeout=600): |
| 318 """ | 378 """ |
| 319 Receive files or directory trees from the server. | 379 Receive files or directory trees from the server. |
| 320 The semantics of src_pattern and dst_path are similar to those of scp. | 380 The semantics of src_pattern and dst_path are similar to those of scp. |
| 321 For example, the following are OK: | 381 For example, the following are OK: |
| 322 src_pattern='C:\\foo.txt', dst_path='/tmp' | 382 src_pattern='C:\\foo.txt', dst_path='/tmp' |
| 323 (downloads a single file) | 383 (downloads a single file) |
| (...skipping 27 matching lines...) Expand all Loading... |
| 351 self._send_packet(src_pattern) | 411 self._send_packet(src_pattern) |
| 352 except FileTransferError: | 412 except FileTransferError: |
| 353 self._handle_transfer_error() | 413 self._handle_transfer_error() |
| 354 while True: | 414 while True: |
| 355 msg = self._receive_msg() | 415 msg = self._receive_msg() |
| 356 if msg == RSS_CREATE_FILE: | 416 if msg == RSS_CREATE_FILE: |
| 357 # Receive filename and file contents | 417 # Receive filename and file contents |
| 358 filename = self._receive_packet() | 418 filename = self._receive_packet() |
| 359 if os.path.isdir(dst_path): | 419 if os.path.isdir(dst_path): |
| 360 dst_path = os.path.join(dst_path, filename) | 420 dst_path = os.path.join(dst_path, filename) |
| 361 self._receive_file_chunks( | 421 self._receive_file_chunks(dst_path, end_time - time.time()) |
| 362 dst_path, max(0, end_time - time.time())) | |
| 363 dst_path = os.path.dirname(dst_path) | 422 dst_path = os.path.dirname(dst_path) |
| 364 file_count += 1 | 423 file_count += 1 |
| 365 elif msg == RSS_CREATE_DIR: | 424 elif msg == RSS_CREATE_DIR: |
| 366 # Receive dirname and create the directory | 425 # Receive dirname and create the directory |
| 367 dirname = self._receive_packet() | 426 dirname = self._receive_packet() |
| 368 if os.path.isdir(dst_path): | 427 if os.path.isdir(dst_path): |
| 369 dst_path = os.path.join(dst_path, dirname) | 428 dst_path = os.path.join(dst_path, dirname) |
| 370 if not os.path.isdir(dst_path): | 429 if not os.path.isdir(dst_path): |
| 371 os.mkdir(dst_path) | 430 os.mkdir(dst_path) |
| 372 dir_count += 1 | 431 dir_count += 1 |
| 373 elif msg == RSS_LEAVE_DIR: | 432 elif msg == RSS_LEAVE_DIR: |
| 374 # Return to parent dir | 433 # Return to parent dir |
| 375 dst_path = os.path.dirname(dst_path) | 434 dst_path = os.path.dirname(dst_path) |
| 376 elif msg == RSS_DONE: | 435 elif msg == RSS_DONE: |
| 377 # Transfer complete | 436 # Transfer complete |
| 378 if not file_count and not dir_count: | 437 if not file_count and not dir_count: |
| 379 raise FileTransferNotFoundError("Pattern %s does not " | 438 raise FileTransferNotFoundError("Pattern %s does not " |
| 380 "match any files or " | 439 "match any files or " |
| 381 "directories that " | 440 "directories that " |
| 382 "could be downloaded" % | 441 "could be downloaded" % |
| 383 src_pattern) | 442 src_pattern) |
| 384 break | 443 break |
| 385 elif msg == RSS_ERROR: | 444 elif msg == RSS_ERROR: |
| 386 # Receive error message and abort | 445 # Receive error message and abort |
| 387 errmsg = self._receive_packet() | 446 errmsg = self._receive_packet() |
| 388 raise FileTransferServerError("Server said: %s" % errmsg) | 447 raise FileTransferServerError(errmsg) |
| 389 else: | 448 else: |
| 390 # Unexpected msg | 449 # Unexpected msg |
| 391 raise FileTransferProtocolError("Received unexpected msg") | 450 raise FileTransferProtocolError("Received unexpected msg") |
| 392 except: | 451 except: |
| 393 # In any case, if the transfer failed, close the connection | 452 # In any case, if the transfer failed, close the connection |
| 394 self.close() | 453 self.close() |
| 395 raise | 454 raise |
| 396 | 455 |
| 397 | 456 |
| 398 def upload(address, port, src_pattern, dst_path, timeout=60, | 457 def upload(address, port, src_pattern, dst_path, log_func=None, timeout=60, |
| 399 connect_timeout=10): | 458 connect_timeout=20): |
| 400 """ | 459 """ |
| 401 Connect to server and upload files. | 460 Connect to server and upload files. |
| 402 | 461 |
| 403 @see: FileUploadClient | 462 @see: FileUploadClient |
| 404 """ | 463 """ |
| 405 client = FileUploadClient(address, port, connect_timeout) | 464 client = FileUploadClient(address, port, log_func, connect_timeout) |
| 406 client.upload(src_pattern, dst_path, timeout) | 465 client.upload(src_pattern, dst_path, timeout) |
| 407 client.close() | 466 client.close() |
| 408 | 467 |
| 409 | 468 |
| 410 def download(address, port, src_pattern, dst_path, timeout=60, | 469 def download(address, port, src_pattern, dst_path, log_func=None, timeout=60, |
| 411 connect_timeout=10): | 470 connect_timeout=20): |
| 412 """ | 471 """ |
| 413 Connect to server and upload files. | 472 Connect to server and upload files. |
| 414 | 473 |
| 415 @see: FileDownloadClient | 474 @see: FileDownloadClient |
| 416 """ | 475 """ |
| 417 client = FileDownloadClient(address, port, connect_timeout) | 476 client = FileDownloadClient(address, port, log_func, connect_timeout) |
| 418 client.download(src_pattern, dst_path, timeout) | 477 client.download(src_pattern, dst_path, timeout) |
| 419 client.close() | 478 client.close() |
| 420 | 479 |
| 421 | 480 |
| 422 def main(): | 481 def main(): |
| 423 import optparse | 482 import optparse |
| 424 | 483 |
| 425 usage = "usage: %prog [options] address port src_pattern dst_path" | 484 usage = "usage: %prog [options] address port src_pattern dst_path" |
| 426 parser = optparse.OptionParser(usage=usage) | 485 parser = optparse.OptionParser(usage=usage) |
| 427 parser.add_option("-d", "--download", | 486 parser.add_option("-d", "--download", |
| 428 action="store_true", dest="download", | 487 action="store_true", dest="download", |
| 429 help="download files from server") | 488 help="download files from server") |
| 430 parser.add_option("-u", "--upload", | 489 parser.add_option("-u", "--upload", |
| 431 action="store_true", dest="upload", | 490 action="store_true", dest="upload", |
| 432 help="upload files to server") | 491 help="upload files to server") |
| 492 parser.add_option("-v", "--verbose", |
| 493 action="store_true", dest="verbose", |
| 494 help="be verbose") |
| 433 parser.add_option("-t", "--timeout", | 495 parser.add_option("-t", "--timeout", |
| 434 type="int", dest="timeout", default=3600, | 496 type="int", dest="timeout", default=3600, |
| 435 help="transfer timeout") | 497 help="transfer timeout") |
| 436 options, args = parser.parse_args() | 498 options, args = parser.parse_args() |
| 437 if options.download == options.upload: | 499 if options.download == options.upload: |
| 438 parser.error("you must specify either -d or -u") | 500 parser.error("you must specify either -d or -u") |
| 439 if len(args) != 4: | 501 if len(args) != 4: |
| 440 parser.error("incorrect number of arguments") | 502 parser.error("incorrect number of arguments") |
| 441 address, port, src_pattern, dst_path = args | 503 address, port, src_pattern, dst_path = args |
| 442 port = int(port) | 504 port = int(port) |
| 443 | 505 |
| 506 logger = None |
| 507 if options.verbose: |
| 508 def p(s): |
| 509 print s |
| 510 logger = p |
| 511 |
| 444 if options.download: | 512 if options.download: |
| 445 download(address, port, src_pattern, dst_path, options.timeout) | 513 download(address, port, src_pattern, dst_path, logger, options.timeout) |
| 446 elif options.upload: | 514 elif options.upload: |
| 447 upload(address, port, src_pattern, dst_path, options.timeout) | 515 upload(address, port, src_pattern, dst_path, logger, options.timeout) |
| 448 | 516 |
| 449 | 517 |
| 450 if __name__ == "__main__": | 518 if __name__ == "__main__": |
| 451 main() | 519 main() |
| OLD | NEW |