Skip to content

Commit 6aac9e6

Browse files
committed
tftp: add docs
Signed-off-by: Benny Zlotnik <[email protected]>
1 parent 03ff9e2 commit 6aac9e6

File tree

3 files changed

+155
-2
lines changed

3 files changed

+155
-2
lines changed

docs/source/api-reference/drivers/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ Drivers packages from the [drivers](https://github.com/jumpstarter-dev/jumpstart
1010
can.md
1111
pyserial.md
1212
sdwire.md
13+
tftp.md
1314
ustreamer.md
1415
yepkit.md
15-
```
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# TFTP Driver
2+
3+
**driver**: `jumpstarter_driver_tftp.driver.Tftp`
4+
5+
The TFTP driver provides a read-only TFTP server that can be used to serve files.
6+
7+
## Driver Configuration
8+
```yaml
9+
export:
10+
tftp:
11+
type: jumpstarter_driver_tftp.driver.Tftp
12+
config:
13+
root_dir: /var/lib/tftpboot # Directory to serve files from
14+
host: 192.168.1.100 # Host IP to bind to (optional)
15+
port: 69 # Port to listen on (optional)
16+
```
17+
18+
### Config parameters
19+
20+
| Parameter | Description | Type | Required | Default |
21+
|-----------|-------------|------|----------|---------|
22+
| root_dir | Root directory for the TFTP server | str | no | "/var/lib/tftpboot" |
23+
| host | IP address to bind the server to | str | no | auto-detect |
24+
| port | Port number to listen on | int | no | 69 |
25+
26+
## TftpClient API
27+
28+
```{eval-rst}
29+
.. autoclass:: jumpstarter_driver_tftp.driver.Tftp
30+
:members:
31+
:show-inheritance:
32+
```
33+
34+
## Exception Classes
35+
36+
```{eval-rst}
37+
.. autoclass:: jumpstarter_driver_tftp.driver.TftpError
38+
:members:
39+
:show-inheritance:
40+
41+
.. autoclass:: jumpstarter_driver_tftp.driver.ServerNotRunning
42+
:members:
43+
:show-inheritance:
44+
45+
.. autoclass:: jumpstarter_driver_tftp.driver.FileNotFound
46+
:members:
47+
:show-inheritance:
48+
```
49+
50+
## Examples
51+
52+
### Starting a TFTP server
53+
```python
54+
from jumpstarter_driver_tftp import Tftp
55+
56+
# Create and start a TFTP server
57+
tftp = Tftp(root_dir="/tftpboot", host="192.168.1.100")
58+
tftp.start()
59+
60+
# Upload a file
61+
tftp.put_file("kernel.img", file_stream, checksum)
62+
63+
# List available files
64+
files = tftp.list_files()
65+
66+
# Stop the server
67+
tftp.stop()
68+
```

packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,30 @@ class FileNotFound(TftpError):
2929

3030
@dataclass(kw_only=True)
3131
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+
"""
3356

3457
root_dir: str = "/var/lib/tftpboot"
3558
host: str = field(default='')
@@ -97,6 +120,14 @@ async def _wait_for_shutdown(self):
97120

98121
@export
99122
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+
"""
100131
if self.server_thread is not None and self.server_thread.is_alive():
101132
self.logger.warning("TFTP server is already running")
102133
return
@@ -116,6 +147,11 @@ def start(self):
116147

117148
@export
118149
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+
"""
119155
if self.server_thread is None or not self.server_thread.is_alive():
120156
self.logger.warning("stop called - TFTP server is not running")
121157
return
@@ -131,10 +167,28 @@ def stop(self):
131167

132168
@export
133169
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+
"""
134175
return os.listdir(self.root_dir)
135176

136177
@export
137178
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+
"""
138192
file_path = os.path.join(self.root_dir, filename)
139193

140194
try:
@@ -152,6 +206,18 @@ async def put_file(self, filename: str, src_stream, client_checksum: str):
152206

153207
@export
154208
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+
"""
155221
file_path = os.path.join(self.root_dir, filename)
156222

157223
if not os.path.exists(file_path):
@@ -165,6 +231,15 @@ def delete_file(self, filename: str):
165231

166232
@export
167233
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+
"""
168243
file_path = os.path.join(self.root_dir, filename)
169244
self.logger.debug(f"checking checksum for file: {filename}")
170245
self.logger.debug(f"file path: {file_path}")
@@ -181,10 +256,20 @@ def check_file_checksum(self, filename: str, client_checksum: str) -> bool:
181256

182257
@export
183258
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+
"""
184264
return self.host
185265

186266
@export
187267
def get_port(self) -> int:
268+
"""Get the port number the server is listening on.
269+
270+
Returns:
271+
int: The port number
272+
"""
188273
return self.port
189274

190275
def close(self):

0 commit comments

Comments
 (0)