summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--t/test.py14
-rw-r--r--tftpy/TftpClient.py16
-rw-r--r--tftpy/TftpContexts.py15
-rw-r--r--tftpy/TftpServer.py7
-rw-r--r--tftpy/TftpShared.py2
5 files changed, 39 insertions, 15 deletions
diff --git a/t/test.py b/t/test.py
index b0bf7fa..f7ca98e 100644
--- a/t/test.py
+++ b/t/test.py
@@ -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.