add clean folders in backup

This commit is contained in:
Volobuev Andrey
2025-06-30 13:09:25 +03:00
parent 7b68b2c8cc
commit 79984ab56b
2 changed files with 144 additions and 30 deletions

View File

@@ -24,7 +24,7 @@ import keyring
from superset_tool.models import SupersetConfig
from superset_tool.client import SupersetClient
from superset_tool.utils.logger import SupersetLogger
from superset_tool.utils.fileio import save_and_unpack_dashboard, archive_exports, sanitize_filename
from superset_tool.utils.fileio import save_and_unpack_dashboard, archive_exports, sanitize_filename,consolidate_archive_folders,remove_empty_directories
# [COHERENCE_CHECK_PASSED] Все необходимые модули импортированы и согласованы.
# [FUNCTION] setup_clients
@@ -92,25 +92,35 @@ def setup_clients(logger: SupersetLogger):
raise
# [FUNCTION] backup_dashboards
# @contract: Выполняет бэкап всех доступных дашбордов для заданного клиента и окружения.
# @pre:
# - `client` должен быть инициализированным экземпляром `SupersetClient`.
# - `env_name` должен быть строкой, обозначающей окружение.
# - `backup_root` должен быть валидным путем к корневой директории бэкапа.
# - `logger` должен быть инициализирован.
# @post:
# - Дашборды экспортируются и сохраняются в поддиректориях `backup_root/env_name/dashboard_title`.
# - Старые экспорты архивируются.
# - Возвращает `True` если все дашборды были экспортированы без критических ошибок, `False` иначе.
# @side_effects:
# - Создает директории и файлы в файловой системе.
# - Логирует статус выполнения, успешные экспорты и ошибки.
# @exceptions:
# - `SupersetAPIError`, `NetworkError`, `DashboardNotFoundError`, `ExportError` могут быть подняты методами `SupersetClient` и будут логированы.
def backup_dashboards(client: SupersetClient, env_name: str, backup_root: Path, logger: SupersetLogger) -> bool:
"""Выполнение бэкапа дашбордов с детальным логированием ошибок"""
def backup_dashboards(client: SupersetClient,
env_name: str,
backup_root: Path,
logger: SupersetLogger,
consolidate: bool = True,
rotate_archive: bool = True,
clean_folders:bool = True) -> bool:
""" [CONTRACT] Выполняет бэкап всех доступных дашбордов для заданного клиента и окружения.
@pre:
- `client` должен быть инициализированным экземпляром `SupersetClient`.
- `env_name` должен быть строкой, обозначающей окружение.
- `backup_root` должен быть валидным путем к корневой директории бэкапа.
- `logger` должен быть инициализирован.
@post:
- Дашборды экспортируются и сохраняются в поддиректориях `backup_root/env_name/dashboard_title`.
- Старые экспорты архивируются.
- Возвращает `True` если все дашборды были экспортированы без критических ошибок, `False` иначе.
@side_effects:
- Создает директории и файлы в файловой системе.
- Логирует статус выполнения, успешные экспорты и ошибки.
@exceptions:
- `SupersetAPIError`, `NetworkError`, `DashboardNotFoundError`, `ExportError` могут быть подняты методами `SupersetClient` и будут логированы."""
# [ANCHOR] DASHBOARD_BACKUP_PROCESS
logger.info(f"[INFO] Запуск бэкапа дашбордов для окружения: {env_name}")
logger.debug(
"[PARAMS] Флаги: consolidate=%s, rotate_archive=%s, clean_folders=%s",
consolidate, rotate_archive, clean_folders,
extra={"env": env_name} # контекст для логирования
)
try:
dashboard_count, dashboard_meta = client.get_dashboards()
logger.info(f"[INFO] Найдено {dashboard_count} дашбордов для экспорта в {env_name}")
@@ -139,7 +149,7 @@ def backup_dashboards(client: SupersetClient, env_name: str, backup_root: Path,
try:
# [ANCHOR] CREATE_DASHBOARD_DIR
# Используем slug в пути для большей уникальности и избежания конфликтов имен
dashboard_base_dir_name = sanitize_filename(f"{dashboard_slug}-{dashboard_title}")
dashboard_base_dir_name = sanitize_filename(f"{dashboard_title}")
dashboard_dir = backup_root / env_name / dashboard_base_dir_name
dashboard_dir.mkdir(parents=True, exist_ok=True)
logger.debug(f"[DEBUG] Директория для дашборда: {dashboard_dir}")
@@ -158,18 +168,20 @@ def backup_dashboards(client: SupersetClient, env_name: str, backup_root: Path,
)
logger.info(f"[INFO] Дашборд '{dashboard_title}' (ID: {dashboard_id}) успешно экспортирован.")
if rotate_archive:
# [ANCHOR] ARCHIVE_OLD_BACKUPS
try:
archive_exports(dashboard_dir, logger=logger)
logger.debug(f"[DEBUG] Старые экспорты для '{dashboard_title}' архивированы.")
except Exception as cleanup_error:
logger.warning(
f"[WARN] Ошибка архивирования старых бэкапов для '{dashboard_title}': {cleanup_error}",
exc_info=False # Не показываем полный traceback для очистки, т.к. это второстепенно
)
try:
archive_exports(dashboard_dir, logger=logger)
logger.debug(f"[DEBUG] Старые экспорты для '{dashboard_title}' архивированы.")
except Exception as cleanup_error:
logger.warning(
f"[WARN] Ошибка архивирования старых бэкапов для '{dashboard_title}': {cleanup_error}",
exc_info=False # Не показываем полный traceback для очистки, т.к. это второстепенно
)
success_count += 1
except Exception as db_error:
error_info = {
'dashboard_id': dashboard_id,
@@ -184,6 +196,28 @@ def backup_dashboards(client: SupersetClient, env_name: str, backup_root: Path,
extra=error_info, exc_info=True # Логируем полный traceback для ошибок экспорта
)
if consolidate:
# [ANCHOR] Объединяем архивы по SLUG в одну папку с максимальной датой
try:
consolidate_archive_folders(backup_root / env_name , logger=logger)
logger.debug(f"[DEBUG] Файлы для '{dashboard_title}' консолидированы.")
except Exception as consolidate_error:
logger.warning(
f"[WARN] Ошибка консолидации файлов для '{backup_root / env_name}': {consolidate_error}",
exc_info=False # Не показываем полный traceback для консолидации, т.к. это второстепенно
)
if clean_folders:
# [ANCHOR] Удаляем пустые папки
try:
dirs_count = remove_empty_directories(backup_root / env_name , logger=logger)
logger.debug(f"[DEBUG] {dirs_count} пустых папок в '{backup_root / env_name }' удалены.")
except Exception as clean_error:
logger.warning(
f"[WARN] Ошибка очистки пустых директорий в '{backup_root / env_name}': {clean_error}",
exc_info=False # Не показываем полный traceback для консолидации, т.к. это второстепенно
)
if error_details:
logger.error(
f"[COHERENCE_CHECK_FAILED] Итоги экспорта для {env_name}:",
@@ -195,7 +229,7 @@ def backup_dashboards(client: SupersetClient, env_name: str, backup_root: Path,
f"[COHERENCE_CHECK_PASSED] Все {success_count} дашбордов для {env_name} успешно экспортированы."
)
return True
except Exception as e:
logger.critical(
f"[CRITICAL] Фатальная ошибка бэкапа для окружения {env_name}: {str(e)}",