Package: fbtftp
What is fbtftp?fbtftpis Facebook's implementation of a dynamic TFTP server framework. It
lets you create custom TFTP servers and wrap your own logic into it in a very
simple manner.
Facebook currently uses it in production, and it's deployed at global scale
across all of our data centers.Why did you do that?We love to use existing open source software and to contribute upstream, but
sometimes it's just not enough at our scale. We ended up writing our own tftp
framework and decided to open source it.fbtftpwas born from the need of having an easy-to-configure and
easy-to-expand TFTP server, that would work at large scale. The standardin.tftpdis a 20+ years old piece of software written in C that is very
difficult to extend.fbtftpis written inpython3and lets you plug your own logic to:publish per session and server wide statistics to your infrastructuredefine how response data is built:can be a file from disk;can be a file created dynamically;you name it!How do you usefbtftpat Facebook?We created our own Facebook-specific server based on the framework to:stream static files (initrd and kernels) from our http repositories (no need
to fill your tftp root directory with files);generate grub2 per-machine configuration dynamically (no need to copy grub2
configuration files on disk);publish per-server and per-connection statistics to our internal monitoring
systems;deployment is easy and "container-ready", just copy the application somewhere,
start it and you are done.Is it better than the other TFTP servers?It depends on your needs!fbtftpis written in Python 3 using a
multiprocessing model; its primary focus is not speed, but flexibility and
scalability. Yet it is fast enough at our datacenter scale :)
It is well-suited for large installations where scalability and custom features
are needed.What does it support?The framework implements the following RFCs:RFC 1350(the main TFTP specification)RFC 2347(Option Extension)RFC 2348(Blocksize option)RFC 2349(Timeout Interval and Transfer
Size Options).Note that the server framework only support RRQs (read only) operations.
(Who uses WRQ TFTP requests in 2019? :P)How does it work?All you need to do is understanding three classes and two callback functions,
and you are good to go:BaseServer: This class implements the process which deals with accepting new
requests on the UDP port provided. Default TFTP parameters like timeout, port
number and number of retries can be passed. This class doesn't have to be used
directly, you must inherit from it and overrideget_handler()method to
return an instance ofBaseHandler.
The class accepts aserver_stats_callback, more about it below. the callback
is not re-entrant, if you need this you have to implement your own locking
logic. This callback is executed periodically and you can use it to publish
server level stats to your monitoring infrastructure. A series of predefined
counters are provided. Refer to the class documentation to find out more.BaseHandler: This class deals with talking to a single client. This class
lives into its separate process, process which is spawned by theBaserServerclass, which will make sure to reap the child properly when the session is
over. Do not use this class as is, instead inherit from it and override theget_response_data()method. Such method must return an instance of a subclass ofResponseData.ResponseData: it's a file-like class that implementsread(num_bytes),size()andclose(). As the previous two classes you'll have to inherit
from this and implement those methods. This class basically let you define how
to return the actual dataserver_stats_callback: function that is called periodically (every 60
seconds by default). The callback is not re-entrant, if you need this you have
to implement your own locking logic. This callback is executed periodically
and you can use it to publish server level stats to your monitoring
infrastructure. A series of predefined counters are provided.
Refer to the class documentation to find out more.session_stats_callback: function that is called when a client session is
over.RequirementsLinux (or any system that supportsepoll)BSD (or any system that supportskqueue)Python 3.4+Installationfbtftpis distributed with the standarddistutilspackage, so you can build
it with:python setup.py buildand install it with:python setup.py installBe sure to run as root if you want to installfbtftpsystem wide. You can also
use avirtualenv, or install it as user by running:python setup.py install --userExampleWriting your own server is simple. Let's take a look at how to write a simple
server that serves files from disk:fromfbtftp.base_handlerimportBaseHandlerfromfbtftp.base_handlerimportResponseDatafromfbtftp.base_serverimportBaseServerimportosclassFileResponseData(ResponseData):def__init__(self,path):self._size=os.stat(path).st_sizeself._reader=open(path,'rb')defread(self,n):returnself._reader.read(n)defsize(self):returnself._sizedefclose(self):self._reader.close()defprint_session_stats(stats):print(stats)defprint_server_stats(stats):counters=stats.get_and_reset_all_counters()print('Server stats - every{}seconds'.format(stats.interval))print(counters)classStaticHandler(BaseHandler):def__init__(self,server_addr,peer,path,options,root,stats_callback):self._root=rootsuper().__init__(server_addr,peer,path,options,stats_callback)defget_response_data(self):returnFileResponseData(os.path.join(self._root,self._path))classStaticServer(BaseServer):def__init__(self,address,port,retries,timeout,root,handler_stats_callback,server_stats_callback=None):self._root=rootself._handler_stats_callback=handler_stats_callbacksuper().__init__(address,port,retries,timeout,server_stats_callback)defget_handler(self,server_addr,peer,path,options):returnStaticHandler(server_addr,peer,path,options,self._root,self._handler_stats_callback)defmain():server=StaticServer(address='::',port=69,retries=3,timeout=5,root='/var/tftproot',handler_stats_callback=print_session_stats,server_stats_callback=print_server_stats)try:server.run()exceptKeyboardInterrupt:server.close()if__name__=='__main__':main()Who wrote it?fbtftpwas created by Marcin Wyszynski (@marcinwyszynski) and Angelo Faillapallotron@fb.comat Facebook Ireland.Other honorable contributors:Andrea Barberiobarberio@fb.comLicenseMIT License