Files
ComConfigCopy/src/network/servers/tftp_server.py

181 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import socket
import threading
from typing import Optional, Dict, Any, Callable
import tftpy
from core.config import AppConfig
from core.exceptions import TFTPError
from .base_server import BaseServer
class TFTPServer(BaseServer):
"""TFTP сервер для передачи файлов."""
def __init__(self):
super().__init__()
self._server: Optional[tftpy.TftpServer] = None
self._root_dir = AppConfig.CONFIGS_DIR
self._host = "0.0.0.0"
self._port = AppConfig.TFTP_PORT
self._timeout = AppConfig.TFTP_TIMEOUT
# Создаем директорию, если её нет
os.makedirs(self._root_dir, exist_ok=True)
def configure(self, root_dir: Optional[str] = None, host: Optional[str] = None,
port: Optional[int] = None, timeout: Optional[int] = None) -> None:
"""
Конфигурация TFTP сервера.
Args:
root_dir: Корневая директория для файлов
host: IP-адрес для прослушивания
port: Порт сервера
timeout: Таймаут операций
"""
if root_dir is not None:
self._root_dir = root_dir
os.makedirs(self._root_dir, exist_ok=True)
if host is not None:
self._host = host
if port is not None:
self._port = port
if timeout is not None:
self._timeout = timeout
def start(self) -> None:
"""
Запуск TFTP сервера.
Raises:
TFTPError: При ошибке запуска сервера
"""
if self.is_running:
self._logger.warning("TFTP сервер уже запущен")
return
try:
# Создаем серверный объект
self._server = tftpy.TftpServer(self._root_dir)
# Запускаем сервер в отдельном потоке
self._start_in_thread(self._serve)
self._notify_started({
"host": self._host,
"port": self._port,
"root_dir": self._root_dir
})
self._logger.info(f"TFTP сервер запущен на {self._host}:{self._port}")
except Exception as e:
self._notify_error(e)
raise TFTPError(f"Ошибка запуска TFTP сервера: {e}")
def stop(self) -> None:
"""Остановка TFTP сервера."""
if not self.is_running:
return
try:
if self._server:
self._server.stop()
self._server = None
self._stop_thread()
self._notify_stopped()
self._logger.info("TFTP сервер остановлен")
except Exception as e:
self._logger.error(f"Ошибка при остановке TFTP сервера: {e}")
def _serve(self) -> None:
"""Основной цикл сервера."""
try:
self._server.listen(self._host, self._port, timeout=self._timeout)
except Exception as e:
self._notify_error(e)
self._logger.error(f"Ошибка в работе TFTP сервера: {e}")
self.stop()
def get_server_info(self) -> Dict[str, Any]:
"""
Получение информации о сервере.
Returns:
Dict[str, Any]: Информация о сервере
"""
return {
"running": self.is_running,
"host": self._host,
"port": self._port,
"root_dir": self._root_dir,
"timeout": self._timeout
}
def list_files(self) -> list[str]:
"""
Получение списка файлов в корневой директории.
Returns:
list[str]: Список файлов
"""
try:
return os.listdir(self._root_dir)
except Exception as e:
self._logger.error(f"Ошибка при получении списка файлов: {e}")
return []
def get_file_info(self, filename: str) -> Optional[Dict[str, Any]]:
"""
Получение информации о файле.
Args:
filename: Имя файла
Returns:
Optional[Dict[str, Any]]: Информация о файле или None
"""
file_path = os.path.join(self._root_dir, filename)
if not os.path.exists(file_path):
return None
try:
stat = os.stat(file_path)
return {
"name": filename,
"size": stat.st_size,
"modified": stat.st_mtime,
"created": stat.st_ctime
}
except Exception as e:
self._logger.error(f"Ошибка при получении информации о файле: {e}")
return None
def delete_file(self, filename: str) -> bool:
"""
Удаление файла из корневой директории.
Args:
filename: Имя файла
Returns:
bool: True если файл удален успешно
"""
file_path = os.path.join(self._root_dir, filename)
if not os.path.exists(file_path):
return False
try:
os.remove(file_path)
self._logger.info(f"Файл удален: {filename}")
return True
except Exception as e:
self._logger.error(f"Ошибка при удалении файла: {e}")
return False