Skip to content

Commit

Permalink
Allow serving static files from a fallback directory
Browse files Browse the repository at this point in the history
  • Loading branch information
affenull2345 committed Jan 11, 2024
1 parent 4e3508c commit 354200f
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 8 deletions.
9 changes: 8 additions & 1 deletion src/cobbler_tftp/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ def run_server(application_settings: Settings):
timeout = application_settings.tftp_timeout
# TODO: use username and password
api = xmlrpc.client.Server(application_settings.uri)
server = TFTPServer(address, port, retries, timeout, api)
server = TFTPServer(
address,
port,
retries,
timeout,
api,
application_settings.static_fallback_dir,
)
except: # pylint: disable=bare-except
logging.exception("Fatal exception while setting up server")
return
Expand Down
42 changes: 36 additions & 6 deletions src/cobbler_tftp/server/tftp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import logging
import xmlrpc.client
from io import BytesIO

from fbtftp import BaseHandler, BaseServer, ResponseData
Expand All @@ -25,6 +26,23 @@ def close(self):
self._io.close()


class FileResponseData(ResponseData):
"""Object representing a static file response from the TFTP server."""

def __init__(self, path):

Check notice on line 32 in src/cobbler_tftp/server/tftp.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/cobbler_tftp/server/tftp.py#L32

Missing docstring in __init__ (D107)
self._io = open(path, "rb")

Check notice on line 33 in src/cobbler_tftp/server/tftp.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/cobbler_tftp/server/tftp.py#L33

Consider using 'with' for resource-allocating operations (consider-using-with)
self._size = path.stat().st_size

def read(self, n):
return self._io.read(n)

def size(self):
return self._size

def close(self):
self._io.close()


def handler_stats_cb(stats):
duration = stats.duration() * 1000
logging.info(
Expand Down Expand Up @@ -52,7 +70,7 @@ class CobblerRequestHandler(BaseHandler):
Handles TFTP requests using the Cobbler API.
"""

def __init__(self, server_addr, peer, path, options, api):
def __init__(self, server_addr, peer, path, options, api, static_fallback_dir):

Check notice on line 73 in src/cobbler_tftp/server/tftp.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/cobbler_tftp/server/tftp.py#L73

Too many arguments (7/5) (too-many-arguments)
"""
Initialize a handler for a specific request.
Expand All @@ -61,20 +79,29 @@ def __init__(self, server_addr, peer, path, options, api):
:param path: Request file path.
:param options: Options requested by the client.
:param api: The Cobbler API object.
:param static_fallback_dir: Path to directory with static TFTP files.
"""
self._api = api
self._static_fallback_dir = static_fallback_dir
super().__init__(server_addr, peer, path, options, handler_stats_cb)

def get_response_data(self):
return CobblerResponseData(self._api.get_tftp_file(self._path).data)
try:
return CobblerResponseData(self._api.get_tftp_file(self._path).data)
except xmlrpc.client.Error as err:
if self._static_fallback_dir is not None:
return FileResponseData(
self._static_fallback_dir / self._path.strip("/")
)
raise err


class TFTPServer(BaseServer):
"""
Implements a TFTP server for the Cobbler API using the CobblerRequestHandler.
"""

def __init__(self, address, port, retries, timeout, api):
def __init__(self, address, port, retries, timeout, api, static_fallback_dir):

Check notice on line 104 in src/cobbler_tftp/server/tftp.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/cobbler_tftp/server/tftp.py#L104

Too many arguments (7/5) (too-many-arguments)
"""
Initialize the TFTP server.
Expand All @@ -83,10 +110,13 @@ def __init__(self, address, port, retries, timeout, api):
:param retries: Maximum number of retries when sending a packet fails.
:param timeout: Timeout for sending packets.
:param api: The Cobbler API object.
:param static_fallback_dir: Path to directory with static TFTP files.
"""
self.api = api
self.handler_stats_cb = handler_stats_cb
self._api = api
self._static_fallback_dir = static_fallback_dir
super().__init__(address, port, retries, timeout, server_stats_cb)

def get_handler(self, server_addr, peer, path, options):
return CobblerRequestHandler(server_addr, peer, path, options, self.api)
return CobblerRequestHandler(
server_addr, peer, path, options, self._api, self._static_fallback_dir
)
10 changes: 9 additions & 1 deletion src/cobbler_tftp/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def __init__(
tftp_retries: int,
tftp_timeout: int,
logging_conf: Optional[Path],
static_fallback_dir: Optional[Path],
) -> None:
"""
Initialize a new instance of the Settings.
Expand All @@ -46,6 +47,7 @@ def __init__(
:param username: Username to authenticate at Cobbler's API.
:param password: Password for authentication with Cobbler.
:param password_file: Path to the file containing the password.
:param static_fallback_dir: Path to the directory with static TFTP files.
"""
# pylint: disable=R0913

Expand All @@ -59,6 +61,7 @@ def __init__(
self.tftp_retries: int = tftp_retries
self.tftp_timeout: int = tftp_timeout
self.logging_conf: Optional[Path] = logging_conf
self.static_fallback_dir: Optional[Path] = static_fallback_dir
self.__password: Optional[str] = password
self.__password_file: Optional[Path] = password_file

Expand Down Expand Up @@ -147,11 +150,15 @@ def build_settings(
password_file: Optional[Path] = Path(cobbler_settings.get("password_file", None)) # type: ignore
else:
password_file = None
tftp_settings = self._settings_dict.get("tftp", None)
tftp_settings = self._settings_dict.get("tftp", {})
tftp_addr: str = tftp_settings.get("address", "127.0.0.1") # type: ignore
tftp_port: int = tftp_settings.get("port", 69) # type: ignore
tftp_retries: int = tftp_settings.get("retries", 5) # type: ignore
tftp_timeout: int = tftp_settings.get("timeout", 2) # type: ignore
if tftp_settings.get("static_fallback_dir", None) is not None: # type: ignore
static_fallback_dir: Optional[Path] = Path(tftp_settings.get("static_fallback_dir", None)) # type: ignore
else:
static_fallback_dir = None
if self._settings_dict.get("logging_conf", None) is not None: # type: ignore
logging_conf: Optional[Path] = Path(self._settings_dict.get("logging_conf", None)) # type: ignore
else:
Expand All @@ -171,6 +178,7 @@ def build_settings(
tftp_retries,
tftp_timeout,
logging_conf,
static_fallback_dir,
)

return settings
Expand Down
1 change: 1 addition & 0 deletions src/cobbler_tftp/settings/data/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ tftp:
port: 69
retries: 5
timeout: 2
static_fallback_dir: "/srv/tftpboot"
logging_conf: "/etc/cobbler-tftp/logging.conf"
1 change: 1 addition & 0 deletions src/cobbler_tftp/settings/migrations/v1_0.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
Optional("port"): int,
Optional("retries"): int,
Optional("timeout"): int,
Optional("static_fallback_dir"): str,
},
Optional("logging_conf"): str,
}
Expand Down

0 comments on commit 354200f

Please sign in to comment.