@@ -29,7 +29,30 @@ class FileNotFound(TftpError):
29
29
30
30
@dataclass (kw_only = True )
31
31
class Tftp (Driver ):
32
- """TFTP Server driver for Jumpstarter"""
32
+ """TFTP Server driver for Jumpstarter
33
+
34
+ This driver implements a TFTP read-only server.
35
+
36
+ Attributes:
37
+ root_dir (str): Root directory for the TFTP server. Defaults to "/var/lib/tftpboot"
38
+ host (str): IP address to bind the server to. If empty, will use the default route interface
39
+ port (int): Port number to listen on. Defaults to 69 (standard TFTP port)
40
+
41
+ Example:
42
+ >>> from jumpstarter_driver_tftp import Tftp
43
+ >>> # Create and start a TFTP server
44
+ >>> tftp = Tftp(root_dir="/tftpboot", host="192.168.1.100")
45
+ >>> tftp.start()
46
+ >>>
47
+ >>> # Upload a file
48
+ >>> tftp.put_file("kernel.img", file_stream, checksum)
49
+ >>>
50
+ >>> # List available files
51
+ >>> files = tftp.list_files()
52
+ >>>
53
+ >>> # Stop the server
54
+ >>> tftp.stop()
55
+ """
33
56
34
57
root_dir : str = "/var/lib/tftpboot"
35
58
host : str = field (default = '' )
@@ -97,6 +120,14 @@ async def _wait_for_shutdown(self):
97
120
98
121
@export
99
122
def start (self ):
123
+ """Start the TFTP server.
124
+
125
+ The server will start listening for incoming TFTP requests on the configured
126
+ host and port. If the server is already running, a warning will be logged.
127
+
128
+ Raises:
129
+ TftpError: If the server fails to start or times out during initialization
130
+ """
100
131
if self .server_thread is not None and self .server_thread .is_alive ():
101
132
self .logger .warning ("TFTP server is already running" )
102
133
return
@@ -116,6 +147,11 @@ def start(self):
116
147
117
148
@export
118
149
def stop (self ):
150
+ """Stop the TFTP server.
151
+
152
+ Initiates a graceful shutdown of the server and waits for all active transfers
153
+ to complete. If the server is not running, a warning will be logged.
154
+ """
119
155
if self .server_thread is None or not self .server_thread .is_alive ():
120
156
self .logger .warning ("stop called - TFTP server is not running" )
121
157
return
@@ -131,10 +167,28 @@ def stop(self):
131
167
132
168
@export
133
169
def list_files (self ) -> list [str ]:
170
+ """List all files available in the TFTP server root directory.
171
+
172
+ Returns:
173
+ list[str]: A list of filenames present in the root directory
174
+ """
134
175
return os .listdir (self .root_dir )
135
176
136
177
@export
137
178
async def put_file (self , filename : str , src_stream , client_checksum : str ):
179
+ """Upload a file to the TFTP server.
180
+
181
+ Args:
182
+ filename (str): Name of the file to create
183
+ src_stream: Source stream to read the file data from
184
+ client_checksum (str): SHA256 checksum of the file for verification
185
+
186
+ Returns:
187
+ str: The filename that was uploaded
188
+
189
+ Raises:
190
+ TftpError: If the file upload fails or path validation fails
191
+ """
138
192
file_path = os .path .join (self .root_dir , filename )
139
193
140
194
try :
@@ -152,6 +206,18 @@ async def put_file(self, filename: str, src_stream, client_checksum: str):
152
206
153
207
@export
154
208
def delete_file (self , filename : str ):
209
+ """Delete a file from the TFTP server.
210
+
211
+ Args:
212
+ filename (str): Name of the file to delete
213
+
214
+ Returns:
215
+ str: The filename that was deleted
216
+
217
+ Raises:
218
+ FileNotFound: If the specified file does not exist
219
+ TftpError: If the deletion operation fails
220
+ """
155
221
file_path = os .path .join (self .root_dir , filename )
156
222
157
223
if not os .path .exists (file_path ):
@@ -165,6 +231,15 @@ def delete_file(self, filename: str):
165
231
166
232
@export
167
233
def check_file_checksum (self , filename : str , client_checksum : str ) -> bool :
234
+ """Check if a file matches the expected checksum.
235
+
236
+ Args:
237
+ filename (str): Name of the file to check
238
+ client_checksum (str): Expected SHA256 checksum
239
+
240
+ Returns:
241
+ bool: True if the file exists and matches the checksum, False otherwise
242
+ """
168
243
file_path = os .path .join (self .root_dir , filename )
169
244
self .logger .debug (f"checking checksum for file: { filename } " )
170
245
self .logger .debug (f"file path: { file_path } " )
@@ -181,10 +256,20 @@ def check_file_checksum(self, filename: str, client_checksum: str) -> bool:
181
256
182
257
@export
183
258
def get_host (self ) -> str :
259
+ """Get the host address the server is bound to.
260
+
261
+ Returns:
262
+ str: The IP address or hostname
263
+ """
184
264
return self .host
185
265
186
266
@export
187
267
def get_port (self ) -> int :
268
+ """Get the port number the server is listening on.
269
+
270
+ Returns:
271
+ int: The port number
272
+ """
188
273
return self .port
189
274
190
275
def close (self ):
0 commit comments