diff options
Diffstat (limited to 'cxmanage_api/tftp.py')
-rw-r--r-- | cxmanage_api/tftp.py | 107 |
1 files changed, 44 insertions, 63 deletions
diff --git a/cxmanage_api/tftp.py b/cxmanage_api/tftp.py index 02b7c49..e3aaec3 100644 --- a/cxmanage_api/tftp.py +++ b/cxmanage_api/tftp.py @@ -1,4 +1,7 @@ -# Copyright (c) 2012, Calxeda Inc. +"""Calxeda: tftp.py""" + + +# Copyright (c) 2012-2013, Calxeda Inc. # # All rights reserved. # @@ -29,22 +32,21 @@ # DAMAGE. -import os -import sys -import atexit import shutil import socket import logging import traceback +from datetime import datetime, timedelta from tftpy import TftpClient, TftpServer, setLogLevel from threading import Thread from cxmanage_api import temp_dir from tftpy.TftpShared import TftpException -class InternalTftp(object): - """Internally serves files using the `Trivial File Transfer Protocol <http://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol>`_. +class InternalTftp(Thread): + """Internally serves files using the `Trivial File Transfer Protocol \ +<http://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol>`_. >>> # Typical instantiation ... >>> from cxmanage_api.tftp import InternalTftp @@ -60,56 +62,45 @@ class InternalTftp(object): :type verbose: boolean """ + _default = None + + @staticmethod + def default(): + """ Return the default InternalTftp server """ + if InternalTftp._default == None: + InternalTftp._default = InternalTftp() + return InternalTftp._default def __init__(self, ip_address=None, port=0, verbose=False): - """Default constructor for the InternalTftp class.""" + super(InternalTftp, self).__init__() + self.daemon = True + self.tftp_dir = temp_dir() self.verbose = verbose - pipe = os.pipe() - pid = os.fork() - if (not pid): - # Force tftpy to use sys.stdout and sys.stderr - try: - os.dup2(sys.stdout.fileno(), 1) - os.dup2(sys.stderr.fileno(), 2) - - except AttributeError, err_msg: - if (self.verbose): - print ('Passing on exception: %s' % err_msg) - pass - - # Create a PortThread class only if needed ... - class PortThread(Thread): - """Thread that sends the port number through the pipe.""" - def run(self): - """Run function override.""" - # Need to wait for the server to open its socket - while not server.sock: - pass - with os.fdopen(pipe[1], "w") as a_file: - a_file.write("%i\n" % server.sock.getsockname()[1]) - # - # Create an Internal TFTP server thread - # - server = TftpServer(tftproot=self.tftp_dir) - thread = PortThread() - thread.start() - try: - if not self.verbose: - setLogLevel(logging.CRITICAL) - # Start accepting connections ... - server.listen(listenport=port) - except KeyboardInterrupt: - # User @ keyboard cancelled server ... - if (self.verbose): - traceback.format_exc() - sys.exit(0) - - self.server = pid + + self.server = TftpServer(tftproot=self.tftp_dir) self.ip_address = ip_address - with os.fdopen(pipe[0]) as a_fd: - self.port = int(a_fd.readline()) - atexit.register(self.kill) + self.port = port + self.start() + + # Get the port we actually hosted on + if port == 0: + deadline = datetime.now() + timedelta(seconds=10) + while datetime.now() < deadline: + try: + self.port = self.server.sock.getsockname()[1] + break + except (AttributeError, socket.error): + pass + else: + # don't catch the error on our last attempt + self.port = self.server.sock.getsockname()[1] + + def run(self): + """ Run the server. Listens indefinitely. """ + if not self.verbose: + setLogLevel(logging.CRITICAL) + self.server.listen(listenport=self.port) def get_address(self, relative_host=None): """Returns the ipv4 address of this server. @@ -136,16 +127,6 @@ class InternalTftp(object): sock.close() return ipv4 - def kill(self): - """Kills the InternalTftpServer. - - >>> i_tftp.kill() - - """ - if (self.server): - os.kill(self.server, 15) - self.server = None - def get_file(self, src, dest): """Download a file from the tftp server to local_path. @@ -153,7 +134,7 @@ class InternalTftp(object): :param src: Source file path on the tftp_server. :type src: string - :param dest: Destination path (on your machine) to copy the TFTP file to. + :param dest: Destination path (local machine) to copy the TFTP file to. :type dest: string """ @@ -222,7 +203,7 @@ class ExternalTftp(object): >>> e_tftp.get_address() '1.2.3.4' - :param relative_host: Unused parameter present only for function signature. + :param relative_host: Unused parameter, for function signature. :type relative_host: None :returns: The ip address of the external TFTP server. |