118 lines
5.3 KiB
Python
118 lines
5.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
transfer.py — Адаптивный перенос архива (scp/rsync/fallback)
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
from core.color import info, success, warn, error as cerror, prompt, confirm, step
|
|
from core import state
|
|
from core.runner import run, exists
|
|
|
|
|
|
def do_transfer():
|
|
st = state.load_state()
|
|
archive_path = st.get("archive_path")
|
|
if not archive_path or not os.path.isfile(archive_path):
|
|
raise RuntimeError("Архив не найден. Сначала выполните сборку (Source → Pack).")
|
|
|
|
host = st.get("target_host")
|
|
user = st.get("target_user")
|
|
port = st.get("target_port", 22)
|
|
|
|
if not host or host.lower() in ("none", "", "localhost"):
|
|
warn("Target host не указан. Перенос невозможен.")
|
|
state.set_stage("TRANSFER_SKIPPED")
|
|
return
|
|
if not user or user.lower() == "none":
|
|
user = "root"
|
|
warn(f"SSH user не указан, используем default: {user}")
|
|
|
|
step(5, "ПЕРЕНОС АРХИВА")
|
|
info(f"Target: {user}@{host}:{port}")
|
|
|
|
# Определяем размер
|
|
size = os.path.getsize(archive_path)
|
|
size_mb = size / 1024 / 1024
|
|
info(f"Размер архива: {size_mb:.2f} MB")
|
|
|
|
# Определяем путь назначения
|
|
remote_dir = "/tmp/docker-migrate-incoming"
|
|
remote_path = os.path.join(remote_dir, os.path.basename(archive_path))
|
|
|
|
# Копируем bootstrap (migrate + core/) чтобы target мог запуститься
|
|
# Найдём корень проекта
|
|
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
tar_script = os.path.join(project_root, "tar_project.sh")
|
|
|
|
# Выбираем метод
|
|
if size_mb < 50 and exists("scp"):
|
|
method = "scp"
|
|
elif size_mb >= 50 and exists("rsync"):
|
|
method = "rsync"
|
|
else:
|
|
if exists("rsync"):
|
|
method = "rsync"
|
|
elif exists("scp"):
|
|
method = "scp"
|
|
else:
|
|
raise RuntimeError("Ни rsync, ни scp не найдены. Установите один из них.")
|
|
|
|
info(f"Выбран метод переноса: {method}")
|
|
|
|
ssh_opts = f"-p {port}"
|
|
key_path = st.get("ssh_key")
|
|
if key_path and os.path.isfile(key_path):
|
|
ssh_opts += f" -i '{key_path}'"
|
|
info(f"Используем SSH-ключ: {key_path}")
|
|
# Предварительная проверка уже сделана в pick_or_setup_ssh_key, повторно не делаем
|
|
|
|
if method == "scp":
|
|
info("Копируем архив через scp ...")
|
|
r = run(f"scp {ssh_opts} {archive_path} {user}@{host}:{remote_path}", check=False)
|
|
else:
|
|
info("Копируем архив через rsync ...")
|
|
r = run(f"rsync -avz --progress -e 'ssh {ssh_opts}' {archive_path} {user}@{host}:{remote_path}", check=False)
|
|
|
|
if r.returncode != 0:
|
|
state.set_error(
|
|
step="TRANSFER",
|
|
stdout=r.stdout,
|
|
stderr=r.stderr,
|
|
suggestion=f"Проверьте SSH-доступ к {user}@{host}:{port}. После исправления: docker-migrate --resume"
|
|
)
|
|
raise RuntimeError(f"Передача файла не удалась. Код возврата: {r.returncode}")
|
|
|
|
success(f"Архив передан на {host}:{remote_path}")
|
|
|
|
# Распаковка на target во временную директорию
|
|
info("Распаковываем архив на target ...")
|
|
r2 = run(f"ssh {ssh_opts} {user}@{host} 'mkdir -p {remote_dir} && tar xzf {remote_path} -C {remote_dir}'", check=False)
|
|
if r2.returncode != 0:
|
|
warn(f"Не удалось распаковать архив на target: {r2.stderr}")
|
|
state.set_error(
|
|
step="TRANSFER_UNPACK",
|
|
stdout=r2.stdout,
|
|
stderr=r2.stderr,
|
|
suggestion=f"Проверьте доступ SSH и tar на {host}. После исправления: docker-migrate --resume"
|
|
)
|
|
raise RuntimeError(f"Распаковка на target не удалась: {r2.stderr}")
|
|
|
|
# Сохраняем remote_dir в state для target
|
|
state.set_stage("TRANSFER", target_remote_dir=remote_dir)
|
|
|
|
# Предлагаем сразу запустить target-режим удалённо
|
|
if confirm("Сразу запустить восстановление на новом сервере (remote target mode)", default="y"):
|
|
info("Запускаем target-mode удалённо ...")
|
|
# Передаём скрипт на target
|
|
project_local = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
run(f"scp {ssh_opts} -r {project_local} {user}@{host}:/opt/docker-migrate-tool", check=False)
|
|
# Запуск target как remote python
|
|
run(f"ssh {ssh_opts} {user}@{host} 'cd /opt/docker-migrate-tool && python3 core/main.py --mode=target --remote-dir={remote_dir}'", check=False)
|
|
else:
|
|
success(f"Архив передан. Запустите на target: python3 core/main.py --mode=target --remote-dir={remote_dir}")
|
|
|
|
# Transfer выполнен — отмечаем выполненным, чтобы resume не повторял
|
|
state.mark_completed("TRANSFER")
|
|
state.set_stage("DONE")
|