Загрузка данных
import logging
import subprocess
from typing import Optional
from clients.subprocess_client import SubprocessClient
from constants.architecture_constants import ImitatorConstants as Im_const
logger = logging.getLogger(__name__)
class ImitatorManager:
"""
Класс управления имитатором
Для запуска имитатора:
from clients.subprocess_client import SubprocessClient
from infra.imitator_manager import ImitatorManager
client = SubprocessClient(your_user, your_host)
imitator_manager = ImitatorManager(client, your_command)
imitator_manager.run_imitator()
imitator_manager.log_imitator_stdout()
imitator_manager.wait_and_stop_imitator()
Для получения процесса с запущенным имитатором:
imitator_process = imitator_manager.imitator_process
Для остановки имитатора:
imitator_manager.stop_imitator()
Для получения процесса с запущенным имитатором
"""
def __init__(self, client: SubprocessClient, imitator_run_cmd: str) -> None:
self._client = client
self._imitator_run_cmd = imitator_run_cmd
self._logger: logging.getLogger() = logging.getLogger(self.__class__.__name__)
self._setup_logger()
self._imitator_process: Optional[subprocess.Popen] = None
@property
def imitator_process(self) -> Optional[subprocess.Popen]:
return self._imitator_process
def _setup_logger(self) -> None:
"""
Настраивает отдельный логер для имитатора
"""
# Запись логов имитатора в отдельный файл
# Кодировка установлена под WIN, для gitlab нужно установить utf-8
file_handler = logging.FileHandler(Im_const.IMITATOR_LOG_FILE_NAME, encoding=Im_const.WIN_ENCODING_CP1251)
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter()
file_handler.setFormatter(formatter)
self._logger.addHandler(file_handler)
# Отключает дублирование логов в консоль
self._logger.propagate = False
def run_imitator(self) -> None:
"""
Запускает имитатор на удаленном сервере
"""
if self._imitator_process is not None:
logger.error("[IMITATOR] [ERROR] Имитатор уже запущен")
raise
process = self._client.exec_popen(self._imitator_run_cmd)
if process.poll() is None:
self._imitator_process = process
logger.info("[IMITATOR] [OK] Имитатор запущен успешно")
else:
logger.error("[IMITATOR] [ERROR] Ошибка запуска имитатора")
raise
def log_imitator_stdout(self) -> None:
"""
Записывает логи имитатора в файл с помощью отдельного логера
"""
try:
if self._imitator_process.poll() is None:
for line in self._imitator_process.stdout:
self._logger.info(line)
else:
logger.error("[IMITATOR] [ERROR] Ошибка записи логов имитатора: Имитатор не запущен")
raise
except (OSError, ValueError):
logging.exception("[IMITATOR] [ERROR] Ошибка при получении логов имитатора!")
def _is_imitator_running(self) -> bool:
"""
Проверяет наличие PID имитатора на стенде
"""
result = self._client.run_cmd(Im_const.IMITATOR_CHECK_CMD, check=False, need_output=True)
if result:
logger.warning(f"[IMITATOR] [WARNING] Имитатор не остановлен! PID: {result}")
else:
logger.info("[IMITATOR] [OK] Имитатор остановлен успешно")
return bool(result and result.strip())
def stop_imitator(self) -> None:
"""
Останавливает имитатор
"""
try:
self._client.terminate_process(self._imitator_process, timeout=Im_const.POPEN_WAIT_TIMOUT_S)
if self._is_imitator_running():
# Останавливает имитатор на стенде
self._client.run_cmd(Im_const.IMITATOR_KILL_CMD)
logger.info("[IMITATOR] [OK] Имитатор остановлен успешно")
self._imitator_process = None
except RuntimeError:
logger.exception("[IMITATOR] [ERROR] Ошибка остановки имитатора")
raise
def wait_and_stop_imitator(self) -> None:
"""
Ожидает окончания работы имитатора и завершает его работу
"""
try:
self._imitator_process.wait()
except KeyboardInterrupt:
# На случай остановки принудительно, через Ctrl+C например
logging.exception("[IMITATOR] [ERROR] Принудительная остановка имитатора!")
raise
finally:
self.stop_imitator()