[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