fix url check

This commit is contained in:
Volobuev Andrey
2025-08-26 17:39:11 +03:00
parent 0e2fc14732
commit 2f8aea3620
11 changed files with 28331 additions and 374 deletions

View File

@@ -10,9 +10,11 @@
@description: Интерактивный скрипт для миграции ассетов Superset между различными окружениями.
"""
from whiptail import Whiptail
# [IMPORTS]
from superset_tool.client import SupersetClient
from superset_tool.utils.init_clients import init_superset_clients
from superset_tool.utils.init_clients import setup_clients
from superset_tool.utils.logger import SupersetLogger
from superset_tool.utils.fileio import (
save_and_unpack_dashboard,
@@ -69,48 +71,34 @@ class Migration:
"""Шаг 1: Выбор окружений (источник и назначение)."""
self.logger.info("[INFO][select_environments][ENTER] Шаг 1/4: Выбор окружений.")
available_envs = {"1": "DEV", "2": "PROD"}
try:
all_clients = setup_clients(self.logger)
available_envs = list(all_clients.keys())
except Exception as e:
self.logger.error(f"[ERROR][select_environments][FAILURE] Ошибка при инициализации клиентов: {e}", exc_info=True)
w = Whiptail(title="Ошибка", backtitle="Superset Migration Tool")
w.msgbox("Не удалось инициализировать клиенты. Проверьте конфигурацию.")
return
print("Доступные окружения:")
for key, value in available_envs.items():
print(f" {key}. {value}")
while self.from_c is None:
try:
from_env_choice = input("Выберите исходное окружение (номер): ")
from_env_name = available_envs.get(from_env_choice)
if not from_env_name:
print("Неверный выбор. Попробуйте снова.")
continue
clients = init_superset_clients(self.logger, env=from_env_name.lower())
self.from_c = clients[0]
self.logger.info(f"[INFO][select_environments][STATE] Исходное окружение: {from_env_name}")
except Exception as e:
self.logger.error(f"[ERROR][select_environments][FAILURE] Ошибка при инициализации клиента-источника: {e}", exc_info=True)
print("Не удалось инициализировать клиент. Проверьте конфигурацию.")
w = Whiptail(title="Выбор окружения", backtitle="Superset Migration Tool")
while self.to_c is None:
try:
to_env_choice = input("Выберите целевое окружение (номер): ")
to_env_name = available_envs.get(to_env_choice)
# Select source environment
(return_code, from_env_name) = w.menu("Выберите исходное окружение:", available_envs)
if return_code == 0:
self.from_c = all_clients[from_env_name]
self.logger.info(f"[INFO][select_environments][STATE] Исходное окружение: {from_env_name}")
else:
return
if not to_env_name:
print("Неверный выбор. Попробуйте снова.")
continue
if to_env_name == self.from_c.env:
print("Целевое и исходное окружения не могут совпадать.")
continue
clients = init_superset_clients(self.logger, env=to_env_name.lower())
self.to_c = clients[0]
self.logger.info(f"[INFO][select_environments][STATE] Целевое окружение: {to_env_name}")
except Exception as e:
self.logger.error(f"[ERROR][select_environments][FAILURE] Ошибка при инициализации целевого клиента: {e}", exc_info=True)
print("Не удалось инициализировать клиент. Проверьте конфигурацию.")
# Select target environment
available_envs.remove(from_env_name)
(return_code, to_env_name) = w.menu("Выберите целевое окружение:", available_envs)
if return_code == 0:
self.to_c = all_clients[to_env_name]
self.logger.info(f"[INFO][select_environments][STATE] Целевое окружение: {to_env_name}")
else:
return
self.logger.info("[INFO][select_environments][EXIT] Шаг 1 завершен.")
# END_FUNCTION_select_environments
@@ -125,51 +113,27 @@ class Migration:
self.logger.info("[INFO][select_dashboards][ENTER] Шаг 2/4: Выбор дашбордов.")
try:
all_dashboards = self.from_c.get_dashboards()
_, all_dashboards = self.from_c.get_dashboards()
if not all_dashboards:
self.logger.warning("[WARN][select_dashboards][STATE] В исходном окружении не найдено дашбордов.")
print("В исходном окружении не найдено дашбордов.")
w = Whiptail(title="Информация", backtitle="Superset Migration Tool")
w.msgbox("В исходном окружении не найдено дашбордов.")
return
while True:
print("\nДоступные дашборды:")
for i, dashboard in enumerate(all_dashboards):
print(f" {i + 1}. {dashboard['dashboard_title']}")
print("\nОпции:")
print(" - Введите номера дашбордов через запятую (например, 1, 3, 5).")
print(" - Введите 'все' для выбора всех дашбордов.")
print(" - Введите 'поиск <запрос>' для поиска дашбордов.")
print(" - Введите 'выход' для завершения.")
w = Whiptail(title="Выбор дашбордов", backtitle="Superset Migration Tool")
dashboard_options = [(str(d['id']), d['dashboard_title']) for d in all_dashboards]
(return_code, selected_ids) = w.checklist("Выберите дашборды для миграции:", dashboard_options)
choice = input("Ваш выбор: ").lower().strip()
if choice == 'выход':
break
elif choice == 'все':
self.dashboards_to_migrate = all_dashboards
self.logger.info(f"[INFO][select_dashboards][STATE] Выбраны все дашборды: {len(self.dashboards_to_migrate)}")
break
elif choice.startswith('поиск '):
search_query = choice[6:].strip()
filtered_dashboards = [d for d in all_dashboards if search_query in d['dashboard_title'].lower()]
if not filtered_dashboards:
print("По вашему запросу ничего не найдено.")
else:
all_dashboards = filtered_dashboards
continue
else:
try:
selected_indices = [int(i.strip()) - 1 for i in choice.split(',')]
self.dashboards_to_migrate = [all_dashboards[i] for i in selected_indices if 0 <= i < len(all_dashboards)]
self.logger.info(f"[INFO][select_dashboards][STATE] Выбрано дашбордов: {len(self.dashboards_to_migrate)}")
break
except (ValueError, IndexError):
print("Неверный ввод. Пожалуйста, введите корректные номера.")
if return_code == 0:
self.dashboards_to_migrate = [d for d in all_dashboards if str(d['id']) in selected_ids]
self.logger.info(f"[INFO][select_dashboards][STATE] Выбрано дашбордов: {len(self.dashboards_to_migrate)}")
except Exception as e:
self.logger.error(f"[ERROR][select_dashboards][FAILURE] Ошибка при получении или выборе дашбордов: {e}", exc_info=True)
print("Произошла ошибка при работе с дашбордами.")
w = Whiptail(title="Ошибка", backtitle="Superset Migration Tool")
w.msgbox("Произошла ошибка при работе с дашбордами.")
self.logger.info("[INFO][select_dashboards][EXIT] Шаг 2 завершен.")
# END_FUNCTION_select_dashboards
@@ -184,44 +148,20 @@ class Migration:
"""Шаг 3: Подтверждение и настройка замены конфигурации БД."""
self.logger.info("[INFO][confirm_db_config_replacement][ENTER] Шаг 3/4: Замена конфигурации БД.")
while True:
choice = input("Хотите ли вы заменить конфигурации баз данных в YAML-файлах? (да/нет): ").lower().strip()
if choice in ["да", "нет"]:
break
print("Неверный ввод. Пожалуйста, введите 'да' или 'нет'.")
w = Whiptail(title="Замена конфигурации БД", backtitle="Superset Migration Tool")
if w.yesno("Хотите ли вы заменить конфигурации баз данных в YAML-файлах?"):
(return_code, old_db_name) = w.inputbox("Введите имя заменяемой базы данных (например, db_dev):")
if return_code != 0:
return
(return_code, new_db_name) = w.inputbox("Введите новое имя базы данных (например, db_prod):")
if return_code != 0:
return
if choice == 'нет':
self.logger.info("[INFO][confirm_db_config_replacement][STATE] Замена конфигурации БД пропущена.")
return
# Эвристический расчет
from_env = self.from_c.env.upper()
to_env = self.to_c.env.upper()
heuristic_applied = False
if from_env == "DEV" and to_env == "PROD":
self.db_config_replacement = {"old": {"database_name": "db_dev"}, "new": {"database_name": "db_prod"}} # Пример
self.logger.info("[INFO][confirm_db_config_replacement][STATE] Применена эвристика DEV -> PROD.")
heuristic_applied = True
elif from_env == "PROD" and to_env == "DEV":
self.db_config_replacement = {"old": {"database_name": "db_prod"}, "new": {"database_name": "db_dev"}} # Пример
self.logger.info("[INFO][confirm_db_config_replacement][STATE] Применена эвристика PROD -> DEV.")
heuristic_applied = True
if heuristic_applied:
print(f"На основе эвристики будет произведена следующая замена: {self.db_config_replacement}")
confirm = input("Подтверждаете? (да/нет): ").lower().strip()
if confirm != 'да':
self.db_config_replacement = None
heuristic_applied = False
if not heuristic_applied:
print("Пожалуйста, введите детали для замены.")
old_key = input("Ключ для замены (например, database_name): ")
old_value = input(f"Старое значение для {old_key}: ")
new_value = input(f"Новое значение для {old_key}: ")
self.db_config_replacement = {"old": {old_key: old_value}, "new": {old_key: new_value}}
self.db_config_replacement = {"old": {"database_name": old_db_name}, "new": {"database_name": new_db_name}}
self.logger.info(f"[INFO][confirm_db_config_replacement][STATE] Установлена ручная замена: {self.db_config_replacement}")
else:
self.logger.info("[INFO][confirm_db_config_replacement][STATE] Замена конфигурации БД пропущена.")
self.logger.info("[INFO][confirm_db_config_replacement][EXIT] Шаг 3 завершен.")
# END_FUNCTION_confirm_db_config_replacement
@@ -235,60 +175,54 @@ class Migration:
def execute_migration(self):
"""Шаг 4: Выполнение миграции и обновления конфигураций."""
self.logger.info("[INFO][execute_migration][ENTER] Шаг 4/4: Выполнение миграции.")
w = Whiptail(title="Выполнение миграции", backtitle="Superset Migration Tool")
if not self.dashboards_to_migrate:
self.logger.warning("[WARN][execute_migration][STATE] Нет дашбордов для миграции.")
print("Нет дашбордов для миграции. Завершение.")
w.msgbox("Нет дашбордов для миграции. Завершение.")
return
db_configs_for_update = []
if self.db_config_replacement:
try:
from_dbs = self.from_c.get_databases()
to_dbs = self.to_c.get_databases()
total_dashboards = len(self.dashboards_to_migrate)
self.logger.info(f"[INFO][execute_migration][STATE] Начало миграции {total_dashboards} дашбордов.")
with w.gauge("Выполняется миграция...", width=60, height=10) as gauge:
for i, dashboard in enumerate(self.dashboards_to_migrate):
try:
dashboard_id = dashboard['id']
dashboard_title = dashboard['dashboard_title']
progress = int((i / total_dashboards) * 100)
self.logger.debug(f"[DEBUG][execute_migration][PROGRESS] {progress}% - Миграция: {dashboard_title}")
gauge.set_text(f"Миграция: {dashboard_title} ({i+1}/{total_dashboards})")
gauge.set_percent(progress)
self.logger.info(f"[INFO][execute_migration][PROGRESS] Миграция дашборда: {dashboard_title} (ID: {dashboard_id})")
# 1. Экспорт
exported_content, _ = self.from_c.export_dashboard(dashboard_id)
zip_path, unpacked_path = save_and_unpack_dashboard(exported_content, f"temp_export_{dashboard_id}", unpack=True)
self.logger.info(f"[INFO][execute_migration][STATE] Дашборд экспортирован и распакован в {unpacked_path}")
# 2. Обновление YAML, если нужно
if self.db_config_replacement:
update_yamls(db_configs=[self.db_config_replacement], path=str(unpacked_path))
self.logger.info(f"[INFO][execute_migration][STATE] YAML-файлы обновлены.")
# 3. Упаковка и импорт
new_zip_path = f"migrated_dashboard_{dashboard_id}.zip"
create_dashboard_export(new_zip_path, [str(unpacked_path)])
self.to_c.import_dashboard(new_zip_path)
self.logger.info(f"[INFO][execute_migration][SUCCESS] Дашборд {dashboard_title} успешно импортирован.")
except Exception as e:
self.logger.error(f"[ERROR][execute_migration][FAILURE] Ошибка при миграции дашборда {dashboard_title}: {e}", exc_info=True)
error_msg = f"Не удалось смигрировать дашборд: {dashboard_title}.\n\nОшибка: {e}"
w.msgbox(error_msg, width=60, height=15)
# Просто пример, как можно было бы сопоставить базы данных.
# В реальном сценарии логика может быть сложнее.
for from_db in from_dbs:
for to_db in to_dbs:
# Предполагаем, что мы можем сопоставить базы по имени, заменив суффикс
if from_db['database_name'].replace(self.from_c.env.upper(), self.to_c.env.upper()) == to_db['database_name']:
db_configs_for_update.append({
"old": {"database_name": from_db['database_name']},
"new": {"database_name": to_db['database_name']}
})
self.logger.info(f"[INFO][execute_migration][STATE] Сформированы конфигурации для замены БД: {db_configs_for_update}")
except Exception as e:
self.logger.error(f"[ERROR][execute_migration][FAILURE] Не удалось получить конфигурации БД: {e}", exc_info=True)
print("Не удалось получить конфигурации БД. Миграция будет продолжена без замены.")
for dashboard in self.dashboards_to_migrate:
try:
dashboard_id = dashboard['id']
self.logger.info(f"[INFO][execute_migration][PROGRESS] Миграция дашборда: {dashboard['dashboard_title']} (ID: {dashboard_id})")
# 1. Экспорт
exported_content = self.from_c.export_dashboards(dashboard_id)
zip_path, unpacked_path = save_and_unpack_dashboard(exported_content, f"temp_export_{dashboard_id}", unpack=True)
self.logger.info(f"[INFO][execute_migration][STATE] Дашборд экспортирован и распакован в {unpacked_path}")
# 2. Обновление YAML, если нужно
if db_configs_for_update:
update_yamls(db_configs=db_configs_for_update, path=str(unpacked_path))
self.logger.info(f"[INFO][execute_migration][STATE] YAML-файлы обновлены.")
# 3. Упаковка и импорт
new_zip_path = f"migrated_dashboard_{dashboard_id}.zip"
create_dashboard_export(new_zip_path, [unpacked_path])
content_to_import, _ = read_dashboard_from_disk(new_zip_path)
self.to_c.import_dashboards(content_to_import)
self.logger.info(f"[INFO][execute_migration][SUCCESS] Дашборд {dashboard['dashboard_title']} успешно импортирован.")
except Exception as e:
self.logger.error(f"[ERROR][execute_migration][FAILURE] Ошибка при миграции дашборда {dashboard['dashboard_title']}: {e}", exc_info=True)
print(f"Не удалось смигрировать дашборд: {dashboard['dashboard_title']}")
gauge.set_percent(100)
self.logger.info("[INFO][execute_migration][STATE] Миграция завершена.")
w.msgbox("Миграция завершена!", width=40, height=8)
self.logger.info("[INFO][execute_migration][EXIT] Шаг 4 завершен.")
# END_FUNCTION_execute_migration