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 |