diff options
-rw-r--r-- | t/test.py | 14 | ||||
-rw-r--r-- | tftpy/TftpClient.py | 16 | ||||
-rw-r--r-- | tftpy/TftpContexts.py | 15 | ||||
-rw-r--r-- | tftpy/TftpServer.py | 7 | ||||
-rw-r--r-- | tftpy/TftpShared.py | 2 |
5 files changed, 39 insertions, 15 deletions
@@ -185,7 +185,11 @@ class TestTftpyState(unittest.TestCase): else: server.listen('localhost', 20001) - def clientServerDownloadOptions(self, options, output='/tmp/out'): + def clientServerDownloadOptions(self, + options, + output='/tmp/out', + cretries=tftpy.DEF_TIMEOUT_RETRIES, + sretries=tftpy.DEF_TIMEOUT_RETRIES): """Fire up a client and a server and do a download.""" root = os.path.dirname(os.path.abspath(__file__)) server = tftpy.TftpServer(root) @@ -199,13 +203,14 @@ class TestTftpyState(unittest.TestCase): try: time.sleep(1) client.download('640KBFILE', - output) + output, + retries=cretries) finally: os.kill(child_pid, 15) os.waitpid(child_pid, 0) else: - server.listen('localhost', 20001) + server.listen('localhost', 20001, retries=sretries) @contextmanager def dummyServerDir(self): @@ -223,6 +228,9 @@ class TestTftpyState(unittest.TestCase): def testClientServerNoOptions(self): self.clientServerDownloadOptions({}) + def testClientServerNoOptionsRetries(self): + self.clientServerDownloadOptions({}, cretries=5, sretries=5) + def testClientServerTsizeOptions(self): self.clientServerDownloadOptions({'tsize': 64*1024}) diff --git a/tftpy/TftpClient.py b/tftpy/TftpClient.py index eb82c05..8b215a3 100644 --- a/tftpy/TftpClient.py +++ b/tftpy/TftpClient.py @@ -32,7 +32,7 @@ class TftpClient(TftpSession): if size < MIN_BLKSIZE or size > MAX_BLKSIZE: raise TftpException("Invalid blksize: %d" % size) - def download(self, filename, output, packethook=None, timeout=SOCK_TIMEOUT): + def download(self, filename, output, packethook=None, timeout=SOCK_TIMEOUT, retries=DEF_TIMEOUT_RETRIES): """This method initiates a tftp download from the configured remote host, requesting the filename passed. It writes the file to output, which can be a file-like object or a path to a local file. If a @@ -41,6 +41,9 @@ class TftpClient(TftpSession): form of a TftpPacketDAT object. The timeout parameter may be used to override the default SOCK_TIMEOUT setting, which is the amount of time that the client will wait for a receive packet to arrive. + The retires paramater may be used to override the default DEF_TIMEOUT_RETRIES + settings, which is the amount of retransmission attemtpts the client will initiate + after encountering a timeout. Note: If output is a hyphen, stdout is used.""" # We're downloading. @@ -54,7 +57,8 @@ class TftpClient(TftpSession): self.options, packethook, timeout, - localip = self.localip) + retries=retries, + localip=self.localip) self.context.start() # Download happens here self.context.end() @@ -71,7 +75,7 @@ class TftpClient(TftpSession): log.info("%.2f bytes in resent data" % metrics.resent_bytes) log.info("Received %d duplicate packets" % metrics.dupcount) - def upload(self, filename, input, packethook=None, timeout=SOCK_TIMEOUT): + def upload(self, filename, input, packethook=None, timeout=SOCK_TIMEOUT, retries=DEF_TIMEOUT_RETRIES): """This method initiates a tftp upload to the configured remote host, uploading the filename passed. It reads the file from input, which can be a file-like object or a path to a local file. If a packethook @@ -80,6 +84,9 @@ class TftpClient(TftpSession): TftpPacketDAT object. The timeout parameter may be used to override the default SOCK_TIMEOUT setting, which is the amount of time that the client will wait for a DAT packet to be ACKd by the server. + The retires paramater may be used to override the default DEF_TIMEOUT_RETRIES + settings, which is the amount of retransmission attemtpts the client will initiate + after encountering a timeout. Note: If input is a hyphen, stdin is used.""" self.context = TftpContextClientUpload(self.host, @@ -89,7 +96,8 @@ class TftpClient(TftpSession): self.options, packethook, timeout, - localip = self.localip) + retries=retries, + localip=self.localip) self.context.start() # Upload happens here self.context.end() diff --git a/tftpy/TftpContexts.py b/tftpy/TftpContexts.py index 418aac4..f01509e 100644 --- a/tftpy/TftpContexts.py +++ b/tftpy/TftpContexts.py @@ -77,7 +77,7 @@ class TftpMetrics(object): class TftpContext(object): """The base class of the contexts.""" - def __init__(self, host, port, timeout, localip = ""): + def __init__(self, host, port, timeout, retries=DEF_TIMEOUT_RETRIES, localip = ""): """Constructor for the base context, setting shared instance variables.""" self.file_to_transfer = None @@ -89,6 +89,7 @@ class TftpContext(object): self.sock.bind((localip, 0)) self.sock.settimeout(timeout) self.timeout = timeout + self.retries = retries self.state = None self.next_block = 0 self.factory = TftpPacketFactory() @@ -213,11 +214,13 @@ class TftpContextServer(TftpContext): timeout, root, dyn_file_func=None, - upload_open=None): + upload_open=None, + retries=DEF_TIMEOUT_RETRIES): TftpContext.__init__(self, host, port, timeout, + retries ) # At this point we have no idea if this is a download or an upload. We # need to let the start state determine that. @@ -268,11 +271,13 @@ class TftpContextClientUpload(TftpContext): options, packethook, timeout, + retries=DEF_TIMEOUT_RETRIES, localip = ""): TftpContext.__init__(self, host, port, timeout, + retries, localip) self.file_to_transfer = filename self.options = options @@ -321,7 +326,7 @@ class TftpContextClientUpload(TftpContext): except TftpTimeout as err: log.error(str(err)) self.retry_count += 1 - if self.retry_count >= TIMEOUT_RETRIES: + if self.retry_count >= self.retries: log.debug("hit max retries, giving up") raise else: @@ -347,11 +352,13 @@ class TftpContextClientDownload(TftpContext): options, packethook, timeout, + retries=DEF_TIMEOUT_RETRIES, localip = ""): TftpContext.__init__(self, host, port, timeout, + retries, localip) # FIXME: should we refactor setting of these params? self.file_to_transfer = filename @@ -404,7 +411,7 @@ class TftpContextClientDownload(TftpContext): except TftpTimeout as err: log.error(str(err)) self.retry_count += 1 - if self.retry_count >= TIMEOUT_RETRIES: + if self.retry_count >= self.retries: log.debug("hit max retries, giving up") raise else: diff --git a/tftpy/TftpServer.py b/tftpy/TftpServer.py index 95ca70e..8201c6a 100644 --- a/tftpy/TftpServer.py +++ b/tftpy/TftpServer.py @@ -77,7 +77,7 @@ class TftpServer(TftpSession): raise TftpException("The tftproot does not exist.") def listen(self, listenip="", listenport=DEF_TFTP_PORT, - timeout=SOCK_TIMEOUT): + timeout=SOCK_TIMEOUT, retries=DEF_TIMEOUT_RETRIES): """Start a server listening on the supplied interface and port. This defaults to INADDR_ANY (all interfaces) and UDP port 69. You can also supply a different socket timeout value, if desired.""" @@ -165,7 +165,8 @@ class TftpServer(TftpSession): timeout, self.root, self.dyn_file_func, - self.upload_open) + self.upload_open, + retries=retries) try: self.sessions[key].start(buffer) except TftpException as err: @@ -210,7 +211,7 @@ class TftpServer(TftpSession): except TftpTimeout as err: log.error(str(err)) self.sessions[key].retry_count += 1 - if self.sessions[key].retry_count >= TIMEOUT_RETRIES: + if self.sessions[key].retry_count >= self.sessions[key].retries: log.debug("hit max retries on %s, giving up" % self.sessions[key]) deletion_list.append(key) diff --git a/tftpy/TftpShared.py b/tftpy/TftpShared.py index 88530c3..7f618e9 100644 --- a/tftpy/TftpShared.py +++ b/tftpy/TftpShared.py @@ -9,7 +9,7 @@ DEF_BLKSIZE = 512 MAX_BLKSIZE = 65536 SOCK_TIMEOUT = 5 MAX_DUPS = 20 -TIMEOUT_RETRIES = 5 +DEF_TIMEOUT_RETRIES = 3 DEF_TFTP_PORT = 69 # A hook for deliberately introducing delay in testing. |