Norman James | 8b2b722 | 2015-10-08 07:01:38 -0500 | [diff] [blame] | 1 | """This module implements the TFTP Client functionality. Instantiate an |
| 2 | instance of the client, and then use its upload or download method. Logging is |
| 3 | performed via a standard logging object set in TftpShared.""" |
| 4 | |
| 5 | import types |
| 6 | from TftpShared import * |
| 7 | from TftpPacketTypes import * |
| 8 | from TftpContexts import TftpContextClientDownload, TftpContextClientUpload |
| 9 | |
| 10 | class TftpClient(TftpSession): |
| 11 | """This class is an implementation of a tftp client. Once instantiated, a |
| 12 | download can be initiated via the download() method, or an upload via the |
| 13 | upload() method.""" |
| 14 | |
| 15 | def __init__(self, host, port, options={}, localip = ""): |
| 16 | TftpSession.__init__(self) |
| 17 | self.context = None |
| 18 | self.host = host |
| 19 | self.iport = port |
| 20 | self.filename = None |
| 21 | self.options = options |
| 22 | self.localip = localip |
| 23 | if self.options.has_key('blksize'): |
| 24 | size = self.options['blksize'] |
| 25 | tftpassert(types.IntType == type(size), "blksize must be an int") |
| 26 | if size < MIN_BLKSIZE or size > MAX_BLKSIZE: |
| 27 | raise TftpException, "Invalid blksize: %d" % size |
| 28 | |
| 29 | def download(self, filename, output, packethook=None, timeout=SOCK_TIMEOUT): |
| 30 | """This method initiates a tftp download from the configured remote |
| 31 | host, requesting the filename passed. It writes the file to output, |
| 32 | which can be a file-like object or a path to a local file. If a |
| 33 | packethook is provided, it must be a function that takes a single |
| 34 | parameter, which will be a copy of each DAT packet received in the |
| 35 | form of a TftpPacketDAT object. The timeout parameter may be used to |
| 36 | override the default SOCK_TIMEOUT setting, which is the amount of time |
| 37 | that the client will wait for a receive packet to arrive. |
| 38 | |
| 39 | Note: If output is a hyphen, stdout is used.""" |
| 40 | # We're downloading. |
| 41 | log.debug("Creating download context with the following params:") |
| 42 | log.debug("host = %s, port = %s, filename = %s, output = %s", |
| 43 | self.host, self.iport, filename, output) |
| 44 | log.debug("options = %s, packethook = %s, timeout = %s", |
| 45 | self.options, packethook, timeout) |
| 46 | self.context = TftpContextClientDownload(self.host, |
| 47 | self.iport, |
| 48 | filename, |
| 49 | output, |
| 50 | self.options, |
| 51 | packethook, |
| 52 | timeout, |
| 53 | localip = self.localip) |
| 54 | self.context.start() |
| 55 | # Download happens here |
| 56 | self.context.end() |
| 57 | |
| 58 | metrics = self.context.metrics |
| 59 | |
| 60 | log.info('') |
| 61 | log.info("Download complete.") |
| 62 | if metrics.duration == 0: |
| 63 | log.info("Duration too short, rate undetermined") |
| 64 | else: |
| 65 | log.info("Downloaded %.2f bytes in %.2f seconds" % (metrics.bytes, metrics.duration)) |
| 66 | log.info("Average rate: %.2f kbps" % metrics.kbps) |
| 67 | log.info("%.2f bytes in resent data" % metrics.resent_bytes) |
| 68 | log.info("Received %d duplicate packets" % metrics.dupcount) |
| 69 | |
| 70 | def upload(self, filename, input, packethook=None, timeout=SOCK_TIMEOUT): |
| 71 | """This method initiates a tftp upload to the configured remote host, |
| 72 | uploading the filename passed. It reads the file from input, which |
| 73 | can be a file-like object or a path to a local file. If a packethook |
| 74 | is provided, it must be a function that takes a single parameter, |
| 75 | which will be a copy of each DAT packet sent in the form of a |
| 76 | TftpPacketDAT object. The timeout parameter may be used to override |
| 77 | the default SOCK_TIMEOUT setting, which is the amount of time that |
| 78 | the client will wait for a DAT packet to be ACKd by the server. |
| 79 | |
| 80 | Note: If input is a hyphen, stdin is used.""" |
| 81 | self.context = TftpContextClientUpload(self.host, |
| 82 | self.iport, |
| 83 | filename, |
| 84 | input, |
| 85 | self.options, |
| 86 | packethook, |
| 87 | timeout, |
| 88 | localip = self.localip) |
| 89 | self.context.start() |
| 90 | # Upload happens here |
| 91 | self.context.end() |
| 92 | |
| 93 | metrics = self.context.metrics |
| 94 | |
| 95 | log.info('') |
| 96 | log.info("Upload complete.") |
| 97 | if metrics.duration == 0: |
| 98 | log.info("Duration too short, rate undetermined") |
| 99 | else: |
| 100 | log.info("Uploaded %d bytes in %.2f seconds" % (metrics.bytes, metrics.duration)) |
| 101 | log.info("Average rate: %.2f kbps" % metrics.kbps) |
| 102 | log.info("%.2f bytes in resent data" % metrics.resent_bytes) |
| 103 | log.info("Resent %d packets" % metrics.dupcount) |