[PATCH 2/2] tester: Update the Python TFTP server to fix Python3 issues.
chrisj at rtems.org
chrisj at rtems.org
Wed Nov 7 04:01:41 UTC 2018
From: Chris Johns <chrisj at rtems.org>
Updated to af2f2fe89a3bf45748b78703820efb0986a8207a.
Repo is https://github.com/msoulier/tftpy.git
---
tester/rt/tftpy/TftpClient.py | 9 +-
tester/rt/tftpy/TftpContexts.py | 47 ++++++--
tester/rt/tftpy/TftpPacketFactory.py | 9 +-
tester/rt/tftpy/TftpPacketTypes.py | 217 ++++++++++++++++++++---------------
tester/rt/tftpy/TftpServer.py | 24 ++--
tester/rt/tftpy/TftpShared.py | 52 ++-------
tester/rt/tftpy/TftpStates.py | 53 +++++----
tester/rt/tftpy/__init__.py | 23 ++--
8 files changed, 241 insertions(+), 193 deletions(-)
diff --git a/tester/rt/tftpy/TftpClient.py b/tester/rt/tftpy/TftpClient.py
index 2763dda..eb82c05 100644
--- a/tester/rt/tftpy/TftpClient.py
+++ b/tester/rt/tftpy/TftpClient.py
@@ -1,13 +1,18 @@
+# vim: ts=4 sw=4 et ai:
+# -*- coding: utf8 -*-
"""This module implements the TFTP Client functionality. Instantiate an
instance of the client, and then use its upload or download method. Logging is
performed via a standard logging object set in TftpShared."""
-from __future__ import absolute_import, division, print_function, unicode_literals
+
import types
+import logging
from .TftpShared import *
from .TftpPacketTypes import *
from .TftpContexts import TftpContextClientDownload, TftpContextClientUpload
+log = logging.getLogger('tftpy.TftpClient')
+
class TftpClient(TftpSession):
"""This class is an implementation of a tftp client. Once instantiated, a
download can be initiated via the download() method, or an upload via the
@@ -23,7 +28,7 @@ class TftpClient(TftpSession):
self.localip = localip
if 'blksize' in self.options:
size = self.options['blksize']
- tftpassert(types.IntType == type(size), "blksize must be an int")
+ tftpassert(int == type(size), "blksize must be an int")
if size < MIN_BLKSIZE or size > MAX_BLKSIZE:
raise TftpException("Invalid blksize: %d" % size)
diff --git a/tester/rt/tftpy/TftpContexts.py b/tester/rt/tftpy/TftpContexts.py
index 271441b..da85886 100644
--- a/tester/rt/tftpy/TftpContexts.py
+++ b/tester/rt/tftpy/TftpContexts.py
@@ -1,3 +1,5 @@
+# vim: ts=4 sw=4 et ai:
+# -*- coding: utf8 -*-
"""This module implements all contexts for state handling during uploads and
downloads, the main interface to which being the TftpContext base class.
@@ -8,12 +10,18 @@ the next packet in the transfer, and returns a state object until the transfer
is complete, at which point it returns None. That is, unless there is a fatal
error, in which case a TftpException is returned instead."""
-from __future__ import absolute_import, division, print_function, unicode_literals
+
from .TftpShared import *
from .TftpPacketTypes import *
from .TftpPacketFactory import TftpPacketFactory
from .TftpStates import *
-import socket, time, sys
+import socket
+import time
+import sys
+import os
+import logging
+
+log = logging.getLogger('tftpy.TftpContext')
###############################################################################
# Utility classes
@@ -120,13 +128,14 @@ class TftpContext(object):
def start(self):
raise NotImplementedError("Abstract method")
- def end(self):
+ def end(self, close_fileobj=True):
"""Perform session cleanup, since the end method should always be
called explicitely by the calling code, this works better than the
- destructor."""
- log.debug("in TftpContext.end")
+ destructor.
+ Set close_fileobj to False so fileobj can be returned open."""
+ log.debug("in TftpContext.end - closing socket")
self.sock.close()
- if self.fileobj is not None and not self.fileobj.closed:
+ if close_fileobj and self.fileobj is not None and not self.fileobj.closed:
log.debug("self.fileobj is open - closing")
self.fileobj.close()
@@ -159,7 +168,7 @@ class TftpContext(object):
try:
(buffer, (raddress, rport)) = self.sock.recvfrom(MAX_BLKSIZE)
except socket.timeout:
- log.warn("Timeout waiting for traffic, retrying...")
+ log.warning("Timeout waiting for traffic, retrying...")
raise TftpTimeout("Timed-out waiting for traffic")
# Ok, we've received a packet. Log it.
@@ -173,11 +182,11 @@ class TftpContext(object):
# Check for known "connection".
if raddress != self.address:
- log.warn("Received traffic from %s, expected host %s. Discarding"
+ log.warning("Received traffic from %s, expected host %s. Discarding"
% (raddress, self.host))
if self.tidport and self.tidport != rport:
- log.warn("Received traffic from %s:%s but we're "
+ log.warning("Received traffic from %s:%s but we're "
"connected to %s:%s. Discarding."
% (raddress, rport,
self.host, self.tidport))
@@ -315,7 +324,7 @@ class TftpContextClientUpload(TftpContext):
log.debug("hit max retries, giving up")
raise
else:
- log.warn("resending last packet")
+ log.warning("resending last packet")
self.state.resendLast()
def end(self):
@@ -347,13 +356,16 @@ class TftpContextClientDownload(TftpContext):
self.file_to_transfer = filename
self.options = options
self.packethook = packethook
+ self.filelike_fileobj = False
# If the output object has a write() function,
# assume it is file-like.
if hasattr(output, 'write'):
self.fileobj = output
+ self.filelike_fileobj = True
# If the output filename is -, then use stdout
elif output == '-':
self.fileobj = sys.stdout
+ self.filelike_fileobj = True
else:
self.fileobj = open(output, "wb")
@@ -395,12 +407,23 @@ class TftpContextClientDownload(TftpContext):
log.debug("hit max retries, giving up")
raise
else:
- log.warn("resending last packet")
+ log.warning("resending last packet")
self.state.resendLast()
+ except TftpFileNotFoundError as err:
+ # If we received file not found, then we should not save the open
+ # output file or we'll be left with a size zero file. Delete it,
+ # if it exists.
+ log.error("Received File not found error")
+ if self.fileobj is not None and not self.filelike_fileobj:
+ if os.path.exists(self.fileobj.name):
+ log.debug("unlinking output file of %s", self.fileobj.name)
+ os.unlink(self.fileobj.name)
+
+ raise
def end(self):
"""Finish up the context."""
- TftpContext.end(self)
+ TftpContext.end(self, not self.filelike_fileobj)
self.metrics.end_time = time.time()
log.debug("Set metrics.end_time to %s" % self.metrics.end_time)
self.metrics.compute()
diff --git a/tester/rt/tftpy/TftpPacketFactory.py b/tester/rt/tftpy/TftpPacketFactory.py
index 928fe07..41f39a9 100644
--- a/tester/rt/tftpy/TftpPacketFactory.py
+++ b/tester/rt/tftpy/TftpPacketFactory.py
@@ -1,10 +1,15 @@
+# vim: ts=4 sw=4 et ai:
+# -*- coding: utf8 -*-
"""This module implements the TftpPacketFactory class, which can take a binary
buffer, and return the appropriate TftpPacket object to represent it, via the
parse() method."""
-from __future__ import absolute_import, division, print_function, unicode_literals
+
from .TftpShared import *
from .TftpPacketTypes import *
+import logging
+
+log = logging.getLogger('tftpy.TftpPacketFactory')
class TftpPacketFactory(object):
"""This class generates TftpPacket objects. It is responsible for parsing
@@ -25,7 +30,7 @@ class TftpPacketFactory(object):
corresponding TftpPacket object. The buffer is the raw bytes off of
the network."""
log.debug("parsing a %d byte packet" % len(buffer))
- (opcode,) = struct.unpack("!H", buffer[:2])
+ (opcode,) = struct.unpack(str("!H"), buffer[:2])
log.debug("opcode is %d" % opcode)
packet = self.__create(opcode)
packet.buffer = buffer
diff --git a/tester/rt/tftpy/TftpPacketTypes.py b/tester/rt/tftpy/TftpPacketTypes.py
index e45bb02..3d3bdf8 100644
--- a/tester/rt/tftpy/TftpPacketTypes.py
+++ b/tester/rt/tftpy/TftpPacketTypes.py
@@ -1,11 +1,16 @@
+# vim: ts=4 sw=4 et ai:
+# -*- coding: utf8 -*-
"""This module implements the packet types of TFTP itself, and the
corresponding encode and decode methods for them."""
-from __future__ import absolute_import, division, print_function, unicode_literals
+
import struct
import sys
+import logging
from .TftpShared import *
+log = logging.getLogger('tftpy.TftpPacketTypes')
+
class TftpSession(object):
"""This class is the base class for the tftp client and server. Any shared
code should be in this class."""
@@ -20,17 +25,23 @@ class TftpPacketWithOptions(object):
def __init__(self):
self.options = {}
+ # Always use unicode strings, except at the encode/decode barrier.
+ # Simpler to keep things clear.
def setoptions(self, options):
log.debug("in TftpPacketWithOptions.setoptions")
- log.debug("options: %s" % options)
+ log.debug("options: %s", options)
myoptions = {}
for key in options:
- newkey = str(key)
- myoptions[newkey] = str(options[key])
- log.debug("populated myoptions with %s = %s"
- % (newkey, myoptions[newkey]))
-
- log.debug("setting options hash to: %s" % myoptions)
+ newkey = key
+ if isinstance(key, bytes):
+ newkey = newkey.decode('ascii')
+ newval = options[key]
+ if isinstance(newval, bytes):
+ newval = newval.decode('ascii')
+ myoptions[newkey] = newval
+ log.debug("populated myoptions with %s = %s", newkey, myoptions[newkey])
+
+ log.debug("setting options hash to: %s", myoptions)
self._options = myoptions
def getoptions(self):
@@ -46,11 +57,11 @@ class TftpPacketWithOptions(object):
"""This method decodes the section of the buffer that contains an
unknown number of options. It returns a dictionary of option names and
values."""
- format = "!"
+ fmt = b"!"
options = {}
- log.debug("decode_options: buffer is: %s" % repr(buffer))
- log.debug("size of buffer is %d bytes" % len(buffer))
+ log.debug("decode_options: buffer is: %s", repr(buffer))
+ log.debug("size of buffer is %d bytes", len(buffer))
if len(buffer) == 0:
log.debug("size of buffer is zero, returning empty hash")
return {}
@@ -58,25 +69,28 @@ class TftpPacketWithOptions(object):
# Count the nulls in the buffer. Each one terminates a string.
log.debug("about to iterate options buffer counting nulls")
length = 0
- for c in buffer:
- if ord(c) == 0:
- log.debug("found a null at length %d" % length)
+ for i in range(len(buffer)):
+ if ord(buffer[i:i+1]) == 0:
+ log.debug("found a null at length %d", length)
if length > 0:
- format += "%dsx" % length
+ fmt += b"%dsx" % length
length = -1
else:
raise TftpException("Invalid options in buffer")
length += 1
- log.debug("about to unpack, format is: %s" % format)
- mystruct = struct.unpack(format, buffer)
+ log.debug("about to unpack, fmt is: %s", fmt)
+ mystruct = struct.unpack(fmt, buffer)
tftpassert(len(mystruct) % 2 == 0,
"packet with odd number of option/value pairs")
for i in range(0, len(mystruct), 2):
- log.debug("setting option %s to %s" % (mystruct[i], mystruct[i+1]))
- options[mystruct[i]] = mystruct[i+1]
+ key = mystruct[i].decode('ascii')
+ val = mystruct[i+1].decode('ascii')
+ log.debug("setting option %s to %s", key, val)
+ log.debug("types are %s and %s", type(key), type(val))
+ options[key] = val
return options
@@ -120,46 +134,59 @@ class TftpPacketInitial(TftpPacket, TftpPacketWithOptions):
tftpassert(self.filename, "filename required in initial packet")
tftpassert(self.mode, "mode required in initial packet")
# Make sure filename and mode are bytestrings.
- self.filename = self.filename.encode('ascii')
- self.mode = self.mode.encode('ascii')
+ filename = self.filename
+ mode = self.mode
+ if not isinstance(filename, bytes):
+ filename = filename.encode('ascii')
+ if not isinstance(self.mode, bytes):
+ mode = mode.encode('ascii')
ptype = None
if self.opcode == 1: ptype = "RRQ"
else: ptype = "WRQ"
- log.debug("Encoding %s packet, filename = %s, mode = %s"
- % (ptype, self.filename, self.mode))
+ log.debug("Encoding %s packet, filename = %s, mode = %s",
+ ptype, filename, mode)
for key in self.options:
- log.debug(" Option %s = %s" % (key, self.options[key]))
+ log.debug(" Option %s = %s", key, self.options[key])
- format = b"!H"
- format += b"%dsx" % len(self.filename)
- if self.mode == b"octet":
- format += b"5sx"
+ fmt = b"!H"
+ fmt += b"%dsx" % len(filename)
+ if mode == b"octet":
+ fmt += b"5sx"
else:
- raise AssertionError("Unsupported mode: %s" % self.mode)
- # Add options.
+ raise AssertionError("Unsupported mode: %s" % mode)
+ # Add options. Note that the options list must be bytes.
options_list = []
- if len(self.options.keys()) > 0:
+ if len(list(self.options.keys())) > 0:
log.debug("there are options to encode")
for key in self.options:
# Populate the option name
- format += b"%dsx" % len(key)
- options_list.append(key.encode('ascii'))
+ name = key
+ if not isinstance(name, bytes):
+ name = name.encode('ascii')
+ options_list.append(name)
+ fmt += b"%dsx" % len(name)
# Populate the option value
- format += b"%dsx" % len(self.options[key].encode('ascii'))
- options_list.append(self.options[key].encode('ascii'))
-
- log.debug("format is %s" % format)
- log.debug("options_list is %s" % options_list)
- log.debug("size of struct is %d" % struct.calcsize(format))
-
- self.buffer = struct.pack(format,
+ value = self.options[key]
+ # Work with all strings.
+ if isinstance(value, int):
+ value = str(value)
+ if not isinstance(value, bytes):
+ value = value.encode('ascii')
+ options_list.append(value)
+ fmt += b"%dsx" % len(value)
+
+ log.debug("fmt is %s", fmt)
+ log.debug("options_list is %s", options_list)
+ log.debug("size of struct is %d", struct.calcsize(fmt))
+
+ self.buffer = struct.pack(fmt,
self.opcode,
- self.filename,
- self.mode,
+ filename,
+ mode,
*options_list)
- log.debug("buffer is %s" % repr(self.buffer))
+ log.debug("buffer is %s", repr(self.buffer))
return self
def decode(self):
@@ -167,18 +194,15 @@ class TftpPacketInitial(TftpPacket, TftpPacketWithOptions):
# FIXME - this shares a lot of code with decode_options
nulls = 0
- format = ""
+ fmt = b""
nulls = length = tlength = 0
log.debug("in decode: about to iterate buffer counting nulls")
subbuf = self.buffer[2:]
- for c in subbuf:
- if sys.version_info[0] <= 2:
- c = ord(c)
- if c == 0:
+ for i in range(len(subbuf)):
+ if ord(subbuf[i:i+1]) == 0:
nulls += 1
- log.debug("found a null at length %d, now have %d"
- % (length, nulls))
- format += "%dsx" % length
+ log.debug("found a null at length %d, now have %d", length, nulls)
+ fmt += b"%dsx" % length
length = -1
# At 2 nulls, we want to mark that position for decoding.
if nulls == 2:
@@ -186,21 +210,22 @@ class TftpPacketInitial(TftpPacket, TftpPacketWithOptions):
length += 1
tlength += 1
- log.debug("hopefully found end of mode at length %d" % tlength)
+ log.debug("hopefully found end of mode at length %d", tlength)
# length should now be the end of the mode.
tftpassert(nulls == 2, "malformed packet")
shortbuf = subbuf[:tlength+1]
- log.debug("about to unpack buffer with format: %s" % format)
- log.debug("unpacking buffer: " + repr(shortbuf))
- mystruct = struct.unpack(format, shortbuf)
+ log.debug("about to unpack buffer with fmt: %s", fmt)
+ log.debug("unpacking buffer: %s", repr(shortbuf))
+ mystruct = struct.unpack(fmt, shortbuf)
tftpassert(len(mystruct) == 2, "malformed packet")
- self.filename = mystruct[0]
- self.mode = mystruct[1].lower() # force lc - bug 17
- log.debug("set filename to %s" % self.filename)
- log.debug("set mode to %s" % self.mode)
+ self.filename = mystruct[0].decode('ascii')
+ self.mode = mystruct[1].decode('ascii').lower() # force lc - bug 17
+ log.debug("set filename to %s", self.filename)
+ log.debug("set mode to %s", self.mode)
self.options = self.decode_options(subbuf[tlength+1:])
+ log.debug("options dict is now %s", self.options)
return self
class TftpPacketRRQ(TftpPacketInitial):
@@ -269,11 +294,14 @@ class TftpPacketDAT(TftpPacket):
returns self for easy method chaining."""
if len(self.data) == 0:
log.debug("Encoding an empty DAT packet")
- format = "!HH%ds" % len(self.data)
- self.buffer = struct.pack(format,
+ data = self.data
+ if not isinstance(self.data, bytes):
+ data = self.data.encode('ascii')
+ fmt = b"!HH%ds" % len(data)
+ self.buffer = struct.pack(fmt,
self.opcode,
self.blocknumber,
- self.data)
+ data)
return self
def decode(self):
@@ -281,14 +309,12 @@ class TftpPacketDAT(TftpPacket):
easy method chaining."""
# We know the first 2 bytes are the opcode. The second two are the
# block number.
- (self.blocknumber,) = struct.unpack("!H", self.buffer[2:4])
- log.debug("decoding DAT packet, block number %d" % self.blocknumber)
- log.debug("should be %d bytes in the packet total"
- % len(self.buffer))
+ (self.blocknumber,) = struct.unpack(str("!H"), self.buffer[2:4])
+ log.debug("decoding DAT packet, block number %d", self.blocknumber)
+ log.debug("should be %d bytes in the packet total", len(self.buffer))
# Everything else is data.
self.data = self.buffer[4:]
- log.debug("found %d bytes of data"
- % len(self.data))
+ log.debug("found %d bytes of data", len(self.data))
return self
class TftpPacketACK(TftpPacket):
@@ -309,9 +335,9 @@ class TftpPacketACK(TftpPacket):
return 'ACK packet: block %d' % self.blocknumber
def encode(self):
- log.debug("encoding ACK: opcode = %d, block = %d"
- % (self.opcode, self.blocknumber))
- self.buffer = struct.pack("!HH", self.opcode, self.blocknumber)
+ log.debug("encoding ACK: opcode = %d, block = %d",
+ self.opcode, self.blocknumber)
+ self.buffer = struct.pack(str("!HH"), self.opcode, self.blocknumber)
return self
def decode(self):
@@ -319,9 +345,9 @@ class TftpPacketACK(TftpPacket):
log.debug("detected TFTP ACK but request is too large, will truncate")
log.debug("buffer was: %s", repr(self.buffer))
self.buffer = self.buffer[0:4]
- self.opcode, self.blocknumber = struct.unpack("!HH", self.buffer)
- log.debug("decoded ACK packet: opcode = %d, block = %d"
- % (self.opcode, self.blocknumber))
+ self.opcode, self.blocknumber = struct.unpack(str("!HH"), self.buffer)
+ log.debug("decoded ACK packet: opcode = %d, block = %d",
+ self.opcode, self.blocknumber)
return self
class TftpPacketERR(TftpPacket):
@@ -373,9 +399,9 @@ class TftpPacketERR(TftpPacket):
def encode(self):
"""Encode the DAT packet based on instance variables, populating
self.buffer, returning self."""
- format = "!HH%dsx" % len(self.errmsgs[self.errorcode])
- log.debug("encoding ERR packet with format %s" % format)
- self.buffer = struct.pack(format,
+ fmt = b"!HH%dsx" % len(self.errmsgs[self.errorcode])
+ log.debug("encoding ERR packet with fmt %s", fmt)
+ self.buffer = struct.pack(fmt,
self.opcode,
self.errorcode,
self.errmsgs[self.errorcode])
@@ -385,18 +411,18 @@ class TftpPacketERR(TftpPacket):
"Decode self.buffer, populating instance variables and return self."
buflen = len(self.buffer)
tftpassert(buflen >= 4, "malformed ERR packet, too short")
- log.debug("Decoding ERR packet, length %s bytes" % buflen)
+ log.debug("Decoding ERR packet, length %s bytes", buflen)
if buflen == 4:
log.debug("Allowing this affront to the RFC of a 4-byte packet")
- format = "!HH"
- log.debug("Decoding ERR packet with format: %s" % format)
- self.opcode, self.errorcode = struct.unpack(format,
+ fmt = b"!HH"
+ log.debug("Decoding ERR packet with fmt: %s", fmt)
+ self.opcode, self.errorcode = struct.unpack(fmt,
self.buffer)
else:
log.debug("Good ERR packet > 4 bytes")
- format = "!HH%dsx" % (len(self.buffer) - 5)
- log.debug("Decoding ERR packet with format: %s" % format)
- self.opcode, self.errorcode, self.errmsg = struct.unpack(format,
+ fmt = b"!HH%dsx" % (len(self.buffer) - 5)
+ log.debug("Decoding ERR packet with fmt: %s", fmt)
+ self.opcode, self.errorcode, self.errmsg = struct.unpack(fmt,
self.buffer)
log.error("ERR packet - errorcode: %d, message: %s"
% (self.errorcode, self.errmsg))
@@ -419,17 +445,24 @@ class TftpPacketOACK(TftpPacket, TftpPacketWithOptions):
return 'OACK packet:\n options = %s' % self.options
def encode(self):
- format = "!H" # opcode
+ fmt = b"!H" # opcode
options_list = []
log.debug("in TftpPacketOACK.encode")
for key in self.options:
- log.debug("looping on option key %s" % key)
- log.debug("value is %s" % self.options[key])
- format += "%dsx" % len(key)
- format += "%dsx" % len(self.options[key])
+ value = self.options[key]
+ if isinstance(value, int):
+ value = str(value)
+ if not isinstance(key, bytes):
+ key = key.encode('ascii')
+ if not isinstance(value, bytes):
+ value = value.encode('ascii')
+ log.debug("looping on option key %s", key)
+ log.debug("value is %s", value)
+ fmt += b"%dsx" % len(key)
+ fmt += b"%dsx" % len(value)
options_list.append(key)
- options_list.append(self.options[key])
- self.buffer = struct.pack(format, self.opcode, *options_list)
+ options_list.append(value)
+ self.buffer = struct.pack(fmt, self.opcode, *options_list)
return self
def decode(self):
diff --git a/tester/rt/tftpy/TftpServer.py b/tester/rt/tftpy/TftpServer.py
index 07c2107..95ca70e 100644
--- a/tester/rt/tftpy/TftpServer.py
+++ b/tester/rt/tftpy/TftpServer.py
@@ -1,18 +1,23 @@
+# vim: ts=4 sw=4 et ai:
+# -*- coding: utf8 -*-
"""This module implements the TFTP Server functionality. Instantiate an
instance of the server, and then run the listen() method to listen for client
requests. Logging is performed via a standard logging object set in
TftpShared."""
-from __future__ import absolute_import, division, print_function, unicode_literals
+
import socket, os, time
import select
import threading
+import logging
from errno import EINTR
from .TftpShared import *
from .TftpPacketTypes import *
from .TftpPacketFactory import TftpPacketFactory
from .TftpContexts import TftpContextServer
+log = logging.getLogger('tftpy.TftpServer')
+
class TftpServer(TftpSession):
"""This class implements a tftp server object. Run the listen() method to
listen for client requests.
@@ -53,8 +58,7 @@ class TftpServer(TftpSession):
for name in 'dyn_file_func', 'upload_open':
attr = getattr(self, name)
if attr and not callable(attr):
- raise TftpException, "%s supplied, but it is not callable." % (
- name,)
+ raise TftpException("{} supplied, but it is not callable.".format(name))
if os.path.exists(self.root):
log.debug("tftproot %s does exist", self.root)
if not os.path.isdir(self.root):
@@ -99,7 +103,7 @@ class TftpServer(TftpSession):
log.debug("shutdown_immediately is %s" % self.shutdown_immediately)
log.debug("shutdown_gracefully is %s" % self.shutdown_gracefully)
if self.shutdown_immediately:
- log.warn("Shutting down now. Session count: %d" %
+ log.warning("Shutting down now. Session count: %d" %
len(self.sessions))
self.sock.close()
for key in self.sessions:
@@ -109,7 +113,7 @@ class TftpServer(TftpSession):
elif self.shutdown_gracefully:
if not self.sessions:
- log.warn("In graceful shutdown mode and all "
+ log.warning("In graceful shutdown mode and all "
"sessions complete.")
self.sock.close()
break
@@ -124,7 +128,7 @@ class TftpServer(TftpSession):
log.debug("Performing select on this inputlist: %s", inputlist)
try:
readyinput, readyoutput, readyspecial = \
- select.select(inputlist, [], [], timeout)
+ select.select(inputlist, [], [], SOCK_TIMEOUT)
except select.error as err:
if err[0] == EINTR:
# Interrupted system call
@@ -145,7 +149,7 @@ class TftpServer(TftpSession):
log.debug("Read %d bytes", len(buffer))
if self.shutdown_gracefully:
- log.warn("Discarding data on main port, "
+ log.warning("Discarding data on main port, "
"in graceful shutdown mode")
continue
@@ -169,10 +173,10 @@ class TftpServer(TftpSession):
log.error("Fatal exception thrown from "
"session %s: %s" % (key, str(err)))
else:
- log.warn("received traffic on main socket for "
+ log.warning("received traffic on main socket for "
"existing session??")
log.info("Currently handling these sessions:")
- for session_key, session in self.sessions.items():
+ for session_key, session in list(self.sessions.items()):
log.info(" %s" % session)
else:
@@ -234,7 +238,7 @@ class TftpServer(TftpSession):
del self.sessions[key]
log.debug("Session list is now %s" % self.sessions)
else:
- log.warn(
+ log.warning(
"Strange, session %s is not on the deletion list" % key)
self.is_running.clear()
diff --git a/tester/rt/tftpy/TftpShared.py b/tester/rt/tftpy/TftpShared.py
index 6252ebd..88530c3 100644
--- a/tester/rt/tftpy/TftpShared.py
+++ b/tester/rt/tftpy/TftpShared.py
@@ -1,10 +1,9 @@
+# vim: ts=4 sw=4 et ai:
+# -*- coding: utf8 -*-
"""This module holds all objects shared by all other modules in tftpy."""
-from __future__ import absolute_import, division, print_function, unicode_literals
-import logging
-from logging.handlers import RotatingFileHandler
-LOG_LEVEL = logging.NOTSET
+
MIN_BLKSIZE = 8
DEF_BLKSIZE = 512
MAX_BLKSIZE = 65536
@@ -16,40 +15,6 @@ DEF_TFTP_PORT = 69
# A hook for deliberately introducing delay in testing.
DELAY_BLOCK = 0
-# Initialize the logger.
-logging.basicConfig()
-
-# The logger used by this library. Feel free to clobber it with your own, if
-# you like, as long as it conforms to Python's logging.
-log = logging.getLogger('tftpy')
-
-def create_streamhandler():
- """add create_streamhandler output logging.DEBUG msg to stdout.
- """
- console = logging.StreamHandler()
- console.setLevel(logging.INFO)
- formatter = logging.Formatter('%(levelname)-8s %(message)s')
- console.setFormatter(formatter)
- return console
-
-def create_rotatingfilehandler(path, maxbytes=10*1024*1024, count=20):
- """
- add create_rotatingfilehandler record the logging.DEBUG msg to logfile. you can change the maxsize (10*1024*1024)
- and amount of the logfiles
- """
- Rthandler = RotatingFileHandler(path, maxbytes, count)
- Rthandler.setLevel(logging.INFO)
- formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
- Rthandler.setFormatter(formatter)
- return Rthandler
-
-def addHandler(hdlr):
- """add handler methods
- More details see the page:
- https://docs.python.org/2/library/logging.handlers.html#module-logging.handlers
- """
- log.addHandler(hdlr)
-
def tftpassert(condition, msg):
"""This function is a simple utility that will check the condition
passed for a false state. If it finds one, it throws a TftpException
@@ -58,12 +23,6 @@ def tftpassert(condition, msg):
if not condition:
raise TftpException(msg)
-def setLogLevel(level):
- """This function is a utility function for setting the internal log level.
- The log level defaults to logging.NOTSET, so unwanted output to stdout is
- not created."""
- log.setLevel(level)
-
class TftpErrors(object):
"""This class is a convenience for defining the common tftp error codes,
and making them more readable in the code."""
@@ -86,3 +45,8 @@ class TftpTimeout(TftpException):
"""This class represents a timeout error waiting for a response from the
other end."""
pass
+
+class TftpFileNotFoundError(TftpException):
+ """This class represents an error condition where we received a file
+ not found error."""
+ pass
diff --git a/tester/rt/tftpy/TftpStates.py b/tester/rt/tftpy/TftpStates.py
index 801e970..42bac1d 100644
--- a/tester/rt/tftpy/TftpStates.py
+++ b/tester/rt/tftpy/TftpStates.py
@@ -1,3 +1,5 @@
+# vim: ts=4 sw=4 et ai:
+# -*- coding: utf8 -*-
"""This module implements all state handling during uploads and downloads, the
main interface to which being the TftpState base class.
@@ -8,10 +10,13 @@ the next packet in the transfer, and returns a state object until the transfer
is complete, at which point it returns None. That is, unless there is a fatal
error, in which case a TftpException is returned instead."""
-from __future__ import absolute_import, division, print_function, unicode_literals
+
from .TftpShared import *
from .TftpPacketTypes import *
import os
+import logging
+
+log = logging.getLogger('tftpy.TftpStates')
###############################################################################
# State classes
@@ -34,7 +39,7 @@ class TftpState(object):
def handleOACK(self, pkt):
"""This method handles an OACK from the server, syncing any accepted
options."""
- if pkt.options.keys() > 0:
+ if len(pkt.options.keys()) > 0:
if pkt.match_options(self.context.options):
log.info("Successful negotiation of options")
# Set options to OACK options
@@ -126,9 +131,12 @@ class TftpState(object):
log.debug("In sendError, being asked to send error %d", errorcode)
errpkt = TftpPacketERR()
errpkt.errorcode = errorcode
- self.context.sock.sendto(errpkt.encode().buffer,
- (self.context.host,
- self.context.tidport))
+ if self.context.tidport == None:
+ log.debug("Error packet received outside session. Discarding")
+ else:
+ self.context.sock.sendto(errpkt.encode().buffer,
+ (self.context.host,
+ self.context.tidport))
self.context.last_pkt = errpkt
def sendOACK(self):
@@ -144,7 +152,7 @@ class TftpState(object):
def resendLast(self):
"Resend the last sent packet due to a timeout."
- log.warn("Resending packet %s on sessions %s"
+ log.warning("Resending packet %s on sessions %s"
% (self.context.last_pkt, self))
self.context.metrics.resent_bytes += len(self.context.last_pkt.buffer)
self.context.metrics.add_dup(self.context.last_pkt)
@@ -180,10 +188,10 @@ class TftpState(object):
elif pkt.blocknumber < self.context.next_block:
if pkt.blocknumber == 0:
- log.warn("There is no block zero!")
+ log.warning("There is no block zero!")
self.sendError(TftpErrors.IllegalTftpOp)
raise TftpException("There is no block zero!")
- log.warn("Dropping duplicate block %d" % pkt.blocknumber)
+ log.warning("Dropping duplicate block %d" % pkt.blocknumber)
self.context.metrics.add_dup(pkt)
log.debug("ACKing block %d again, just in case", pkt.blocknumber)
self.sendACK(pkt.blocknumber)
@@ -261,10 +269,10 @@ class TftpServerState(TftpState):
# begin with a '/' strip it off as otherwise os.path.join will
# treat it as absolute (regardless of whether it is ntpath or
# posixpath module
- if pkt.filename.startswith(self.context.root.encode()):
+ if pkt.filename.startswith(self.context.root):
full_path = pkt.filename
else:
- full_path = os.path.join(self.context.root, pkt.filename.decode().lstrip('/'))
+ full_path = os.path.join(self.context.root, pkt.filename.lstrip('/'))
# Use abspath to eliminate any remaining relative elements
# (e.g. '..') and ensure that is still within the server's
@@ -274,7 +282,7 @@ class TftpServerState(TftpState):
if self.full_path.startswith(self.context.root):
log.info("requested file is in the server root - good")
else:
- log.warn("requested file is not within the server root - bad")
+ log.warning("requested file is not within the server root - bad")
self.sendError(TftpErrors.IllegalTftpOp)
raise TftpException("bad file path")
@@ -307,11 +315,12 @@ class TftpStateServerRecvRRQ(TftpServerState):
self.sendError(TftpErrors.FileNotFound)
raise TftpException("File not found: %s" % path)
else:
+ log.warn("File not found: %s", path)
self.sendError(TftpErrors.FileNotFound)
- raise TftpException("File not found: %s" % path)
+ raise TftpException("File not found: {}".format(path))
# Options negotiation.
- if sendoack and self.context.options.has_key('tsize'):
+ if sendoack and 'tsize' in self.context.options:
# getting the file size for the tsize option. As we handle
# file-like objects and not only real files, we use this seeking
# method instead of asking the OS
@@ -368,14 +377,14 @@ class TftpStateServerRecvWRQ(TftpServerState):
f = self.context.upload_open(path, self.context)
if f is None:
self.sendError(TftpErrors.AccessViolation)
- raise TftpException, "Dynamic path %s not permitted" % path
+ raise TftpException("Dynamic path %s not permitted" % path)
else:
self.context.fileobj = f
else:
log.info("Opening file %s for writing" % path)
if os.path.exists(path):
# FIXME: correct behavior?
- log.warn("File %s exists already, overwriting..." % (
+ log.warning("File %s exists already, overwriting..." % (
self.context.file_to_transfer))
# FIXME: I think we should upload to a temp file and not overwrite
# the existing file until the file is successfully uploaded.
@@ -443,12 +452,12 @@ class TftpStateExpectACK(TftpState):
self.context.pending_complete = self.sendDAT()
elif pkt.blocknumber < self.context.next_block:
- log.warn("Received duplicate ACK for block %d"
+ log.warning("Received duplicate ACK for block %d"
% pkt.blocknumber)
self.context.metrics.add_dup(pkt)
else:
- log.warn("Oooh, time warp. Received ACK to packet we "
+ log.warning("Oooh, time warp. Received ACK to packet we "
"didn't send yet. Discarding.")
self.context.metrics.errors += 1
return self
@@ -456,7 +465,7 @@ class TftpStateExpectACK(TftpState):
log.error("Received ERR packet from peer: %s" % str(pkt))
raise TftpException("Received ERR packet from peer: %s" % str(pkt))
else:
- log.warn("Discarding unsupported packet: %s" % str(pkt))
+ log.warning("Discarding unsupported packet: %s" % str(pkt))
return self
class TftpStateExpectDAT(TftpState):
@@ -519,7 +528,7 @@ class TftpStateSentWRQ(TftpState):
log.debug("Changing state to TftpStateExpectACK")
return TftpStateExpectACK(self.context)
else:
- log.warn("Discarding ACK to block %s" % pkt.blocknumber)
+ log.warning("Discarding ACK to block %s" % pkt.blocknumber)
log.debug("Still waiting for valid response from server")
return self
@@ -588,7 +597,11 @@ class TftpStateSentRRQ(TftpState):
elif isinstance(pkt, TftpPacketERR):
self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received ERR from server: %s" % pkt)
+ log.debug("Received ERR packet: %s", pkt)
+ if pkt.errorcode == TftpErrors.FileNotFound:
+ raise TftpFileNotFoundError("File not found")
+ else:
+ raise TftpException("Received ERR from server: {}".format(pkt))
else:
self.sendError(TftpErrors.IllegalTftpOp)
diff --git a/tester/rt/tftpy/__init__.py b/tester/rt/tftpy/__init__.py
index 33f988c..71b8e3d 100644
--- a/tester/rt/tftpy/__init__.py
+++ b/tester/rt/tftpy/__init__.py
@@ -1,3 +1,5 @@
+# vim: ts=4 sw=4 et ai:
+# -*- coding: utf8 -*-
"""
This library implements the tftp protocol, based on rfc 1350.
http://www.faqs.org/rfcs/rfc1350.html
@@ -8,19 +10,18 @@ As a client of tftpy, this is the only module that you should need to import
directly. The TftpClient and TftpServer classes can be reached through it.
"""
-from __future__ import absolute_import, division, print_function, unicode_literals
+
import sys
-# Make sure that this is at least Python 2.3
-required_version = (2, 3)
+# Make sure that this is at least Python 2.7
+required_version = (2, 7)
if sys.version_info < required_version:
- raise ImportError("Requires at least Python 2.3")
+ raise ImportError("Requires at least Python 2.7")
from .TftpShared import *
-from .TftpPacketTypes import *
-from .TftpPacketFactory import *
-from .TftpClient import *
-from .TftpServer import *
-from .TftpContexts import *
-from .TftpStates import *
-
+from . import TftpPacketTypes
+from . import TftpPacketFactory
+from .TftpClient import TftpClient
+from .TftpServer import TftpServer
+from . import TftpContexts
+from . import TftpStates
--
2.14.1
More information about the devel
mailing list