Welcome, guest | Sign In | My Account | Store | Cart

The tftpy library provides simple classes for TFTP clients and servers alike. This is as simple as installing this library, instantiating a TftpClient object, and invoking its download() method.

Python, 95 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/usr/bin/env python

import sys, logging, os
from optparse import OptionParser
import tftpy

def main():
    usage=""
    parser = OptionParser(usage=usage)
    parser.add_option('-H',
                      '--host',
                      action='store',
                      dest='host',
                      help='remote host or ip address')
    parser.add_option('-p',
                      '--port',
                      action='store',
                      dest='port',
                      help='remote port to use (default: 69)',
                      default=69)
    parser.add_option('-f',
                      '--filename',
                      action='store',
                      dest='filename',
                      help='filename to fetch')
    parser.add_option('-b',
                      '--blocksize',
                      action='store',
                      dest='blocksize',
                      help='udp packet size to use (default: 512)',
                      default=512)
    parser.add_option('-o',
                      '--output',
                      action='store',
                      dest='output',
                      help='output file (default: same as requested filename)')
    parser.add_option('-d',
                      '--debug',
                      action='store_true',
                      dest='debug',
                      default=False,
                      help='upgrade logging from info to debug')
    parser.add_option('-q',
                      '--quiet',
                      action='store_true',
                      dest='quiet',
                      default=False,
                      help="downgrade logging from info to warning")
    options, args = parser.parse_args()
    if not options.host or not options.filename:
        sys.stderr.write("Both the --host and --filename options "
                         "are required.\n")
        parser.print_help()
        sys.exit(1)

    if options.debug and options.quiet:
        sys.stderr.write("The --debug and --quiet options are "
                         "mutually exclusive.\n")
        parser.print_help()
        sys.exit(1)

    if not options.output:
        options.output = os.path.basename(options.filename)

    class Progress(object):
        def __init__(self, out):
            self.progress = 0
            self.out = out
        def progresshook(self, pkt):
            self.progress += len(pkt.data)
            self.out("Downloaded %d bytes" % self.progress)

    if options.debug:
        tftpy.setLogLevel(logging.DEBUG)
    elif options.quiet:
        tftpy.setLogLevel(logging.WARNING)
    else:
        tftpy.setLogLevel(logging.INFO)

    progresshook = Progress(tftpy.logger.info).progresshook

    tftp_options = {}
    if options.blocksize:
        tftp_options['blksize'] = int(options.blocksize)

    tclient = tftpy.TftpClient(options.host,
                               int(options.port),
                               tftp_options)

    tclient.download(options.filename,
                     options.output,
                     progresshook)

if __name__ == '__main__':
    main()

The code included is the sample client from the tftpy library (http://tftpy.sf.net). It could be much shorter if the command-line options were dropped.

All that is really required is to instantiate the TftpClient class with a host, a port, and an optional dict of options (where only blksize is currently supported). Call the download() method on the resulting object and provide the filename, and the local file to output to, optionally passing a progress hook method that will be called as each packet is received.

From there, the download() method takes over, attempting to download the file via TFTP. A TftpException will be raised on any tftp-related errors, so that they can be handled gracefully. The progress hook provides the ability to update a progress indicator during the download without resorting to threads.

1 comment

Steven Bethard 17 years, 2 months ago  # | flag

optparse defaults. You don't need any of the dest= arguments or the action='store' arguments -- you're just repeating the optparse defaults. Also, note that argparse (http://argparse.python-hosting.com/) understands required options (so you don't need to handle them yourself) and has a smarter 'store_true' action that correctly defaults to False.

parser = argparse.ArgumentParser()
parser.add_argument('-H', '--host', required=True,
                    help='remote host or ip address')
parser.add_argument('-p', '--port', default=69,
                    help='remote port to use (default: %(default)s)')
parser.add_argument('-f', '--filename', required=True,
                    help='filename to fetch')
parser.add_argument('-b', '--blocksize', default=512,
                    help='udp packet size to use (default: %(default)s)')
parser.add_argument('-o', '--output',
                    help='output file (default: same as requested filename)')
parser.add_option('-d', '--debug', action='store_true',
                  help='upgrade logging from info to debug')
parser.add_option('-q', '--quiet', action='store_true',
                  help="downgrade logging from info to warning")
args = parser.parse_args()

if args.debug and args.quiet:
    ...