Установщик автоматически выбирает сборку под x86 Raspberry Orange Pi
Этот коммит содержится в:
@@ -2,7 +2,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Virtuality Web Panel Installer
|
# Virtuality Web Panel Installer
|
||||||
# Clean step-by-step installer with readable statuses, Linux-user auth and log file.
|
# Auto-detects x86_64 / Raspberry Pi ARM64 / Orange Pi 5 ARM64 / generic ARM64 profile.
|
||||||
|
|
||||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
WEB_DIR="${REPO_DIR}/web"
|
WEB_DIR="${REPO_DIR}/web"
|
||||||
@@ -13,7 +13,9 @@ PORT="${VIRTUALITY_WEB_PORT:-8088}"
|
|||||||
AUTH_USER="${VIRTUALITY_AUTH_USER:-${SUDO_USER:-viktor}}"
|
AUTH_USER="${VIRTUALITY_AUTH_USER:-${SUDO_USER:-viktor}}"
|
||||||
LOG_DIR="/var/log/virtuality"
|
LOG_DIR="/var/log/virtuality"
|
||||||
LOG_FILE="${LOG_DIR}/install_web_panel_$(date +%Y%m%d_%H%M%S).log"
|
LOG_FILE="${LOG_DIR}/install_web_panel_$(date +%Y%m%d_%H%M%S).log"
|
||||||
TOTAL_STEPS=11
|
PROFILE_DIR="/var/lib/virtuality/config"
|
||||||
|
PROFILE_FILE="${PROFILE_DIR}/host_profile.json"
|
||||||
|
TOTAL_STEPS=12
|
||||||
CURRENT_STEP=0
|
CURRENT_STEP=0
|
||||||
|
|
||||||
ESC="\033"
|
ESC="\033"
|
||||||
@@ -27,7 +29,7 @@ CYAN="${ESC}[36m"
|
|||||||
BLUE="${ESC}[34m"
|
BLUE="${ESC}[34m"
|
||||||
GRAY="${ESC}[90m"
|
GRAY="${ESC}[90m"
|
||||||
|
|
||||||
mkdir -p "$LOG_DIR"
|
mkdir -p "$LOG_DIR" "$PROFILE_DIR"
|
||||||
|
|
||||||
timestamp() { date '+%Y-%m-%d %H:%M:%S'; }
|
timestamp() { date '+%Y-%m-%d %H:%M:%S'; }
|
||||||
line() { printf '%*s\n' 72 '' | tr ' ' '─'; }
|
line() { printf '%*s\n' 72 '' | tr ' ' '─'; }
|
||||||
@@ -37,7 +39,7 @@ print_header() {
|
|||||||
clear 2>/dev/null || true
|
clear 2>/dev/null || true
|
||||||
echo -e "${CYAN}${BOLD}╭────────────────────────────────────────────────────────────╮${RESET}"
|
echo -e "${CYAN}${BOLD}╭────────────────────────────────────────────────────────────╮${RESET}"
|
||||||
echo -e "${CYAN}${BOLD}│${RESET} ${BOLD}Virtuality Web Panel Installer${RESET} ${CYAN}${BOLD}│${RESET}"
|
echo -e "${CYAN}${BOLD}│${RESET} ${BOLD}Virtuality Web Panel Installer${RESET} ${CYAN}${BOLD}│${RESET}"
|
||||||
echo -e "${CYAN}${BOLD}│${RESET} Панель управления VM, сервисами, сетью и storage pools ${CYAN}${BOLD}│${RESET}"
|
echo -e "${CYAN}${BOLD}│${RESET} Auto profile: x86 / VPS / Raspberry / Orange Pi 5 ${CYAN}${BOLD}│${RESET}"
|
||||||
echo -e "${CYAN}${BOLD}╰────────────────────────────────────────────────────────────╯${RESET}"
|
echo -e "${CYAN}${BOLD}╰────────────────────────────────────────────────────────────╯${RESET}"
|
||||||
echo
|
echo
|
||||||
echo -e "${GRAY}Лог установки:${RESET} ${LOG_FILE}"
|
echo -e "${GRAY}Лог установки:${RESET} ${LOG_FILE}"
|
||||||
@@ -70,6 +72,7 @@ run_logged() {
|
|||||||
|
|
||||||
service_state() { systemctl is-active "$1" 2>/dev/null || echo "inactive"; }
|
service_state() { systemctl is-active "$1" 2>/dev/null || echo "inactive"; }
|
||||||
require_root() { [[ "$EUID" -eq 0 ]] || fail "Запусти через sudo: sudo bash scripts/install_web_panel.sh"; }
|
require_root() { [[ "$EUID" -eq 0 ]] || fail "Запусти через sudo: sudo bash scripts/install_web_panel.sh"; }
|
||||||
|
json_value() { python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get(sys.argv[2], ""))' "$PROFILE_FILE" "$1" 2>/dev/null || true; }
|
||||||
|
|
||||||
print_header
|
print_header
|
||||||
|
|
||||||
@@ -83,6 +86,23 @@ ok "FastAPI entrypoint найден: web/app.py"
|
|||||||
[[ -f "$WEB_DIR/requirements.txt" ]] || fail "Не найден web/requirements.txt"
|
[[ -f "$WEB_DIR/requirements.txt" ]] || fail "Не найден web/requirements.txt"
|
||||||
ok "requirements.txt найден"
|
ok "requirements.txt найден"
|
||||||
|
|
||||||
|
step "Автоматически определяем профиль сборки"
|
||||||
|
if [[ -x "${REPO_DIR}/scripts/detect_host_profile.sh" ]]; then
|
||||||
|
PROFILE="$(${REPO_DIR}/scripts/detect_host_profile.sh 2>>"$LOG_FILE" | tail -n1)"
|
||||||
|
else
|
||||||
|
bash "${REPO_DIR}/scripts/detect_host_profile.sh" >>"$LOG_FILE" 2>&1 || true
|
||||||
|
PROFILE="$(json_value profile)"
|
||||||
|
fi
|
||||||
|
[[ -n "$PROFILE" ]] || PROFILE="x86_64"
|
||||||
|
ARCH="$(json_value arch)"
|
||||||
|
MODEL="$(json_value model)"
|
||||||
|
LABEL="$(json_value label)"
|
||||||
|
PACKAGE_PROFILE="$(json_value package_profile)"
|
||||||
|
ok "Профиль: ${LABEL:-$PROFILE}"
|
||||||
|
ok "Архитектура: ${ARCH:-unknown}"
|
||||||
|
ok "Модель: ${MODEL:-unknown}"
|
||||||
|
ok "Конфиг профиля: $PROFILE_FILE"
|
||||||
|
|
||||||
step "Проверяем Linux-пользователя для входа"
|
step "Проверяем Linux-пользователя для входа"
|
||||||
if id "$AUTH_USER" >/dev/null 2>&1; then
|
if id "$AUTH_USER" >/dev/null 2>&1; then
|
||||||
ok "Пользователь найден: $AUTH_USER"
|
ok "Пользователь найден: $AUTH_USER"
|
||||||
@@ -107,12 +127,30 @@ done
|
|||||||
step "Обновляем apt cache"
|
step "Обновляем apt cache"
|
||||||
run_logged "apt update выполнен" apt update
|
run_logged "apt update выполнен" apt update
|
||||||
|
|
||||||
step "Устанавливаем системные зависимости"
|
step "Устанавливаем системные зависимости под выбранную сборку"
|
||||||
run_logged "Пакеты Python/venv/pip/rsync/nftables установлены" apt install -y python3 python3-venv python3-pip rsync openssl nftables
|
COMMON_PACKAGES=(python3 python3-venv python3-pip rsync openssl nftables)
|
||||||
|
X86_PACKAGES=(qemu-system-x86 virtinst libvirt-daemon-system libvirt-clients bridge-utils)
|
||||||
|
ARM_PACKAGES=(qemu-system-arm qemu-efi-aarch64 virtinst libvirt-daemon-system libvirt-clients bridge-utils cloud-image-utils)
|
||||||
|
PACKAGES=("${COMMON_PACKAGES[@]}")
|
||||||
|
case "$PACKAGE_PROFILE" in
|
||||||
|
raspberry|orangepi5|arm64)
|
||||||
|
PACKAGES+=("${ARM_PACKAGES[@]}")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
PACKAGES+=("${X86_PACKAGES[@]}")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
run_logged "Системные пакеты установлены для профиля ${PACKAGE_PROFILE:-x86}" apt install -y "${PACKAGES[@]}"
|
||||||
|
|
||||||
step "Копируем web-панель в /opt/virtuality"
|
step "Копируем web-панель в /opt/virtuality"
|
||||||
run_logged "Создана директория /opt/virtuality" mkdir -p /opt/virtuality
|
run_logged "Создана директория /opt/virtuality" mkdir -p /opt/virtuality
|
||||||
run_logged "Файлы панели синхронизированы в $APP_DIR" rsync -a --delete "$WEB_DIR/" "$APP_DIR/"
|
run_logged "Файлы панели синхронизированы в $APP_DIR" rsync -a --delete "$WEB_DIR/" "$APP_DIR/"
|
||||||
|
run_logged "Конфиг профиля доступен web-панели" mkdir -p "$PROFILE_DIR"
|
||||||
|
if [[ -f "$PROFILE_FILE" ]]; then
|
||||||
|
ok "Профиль уже сохранён: $PROFILE_FILE"
|
||||||
|
else
|
||||||
|
warn "Профиль не найден после детекта, будет создан web-панелью при старте"
|
||||||
|
fi
|
||||||
|
|
||||||
step "Создаём конфиг авторизации"
|
step "Создаём конфиг авторизации"
|
||||||
SESSION_SECRET="$(openssl rand -hex 32)"
|
SESSION_SECRET="$(openssl rand -hex 32)"
|
||||||
@@ -192,11 +230,13 @@ echo -e "${GREEN}${BOLD}╰─────────────────
|
|||||||
echo
|
echo
|
||||||
echo -e "${BOLD}URL:${RESET} http://${SERVER_IP}:${PORT}"
|
echo -e "${BOLD}URL:${RESET} http://${SERVER_IP}:${PORT}"
|
||||||
echo -e "${BOLD}Login:${RESET} ${AUTH_USER} / пароль Linux-пользователя"
|
echo -e "${BOLD}Login:${RESET} ${AUTH_USER} / пароль Linux-пользователя"
|
||||||
|
echo -e "${BOLD}Profile:${RESET} ${LABEL:-$PROFILE}"
|
||||||
|
echo -e "${BOLD}Arch:${RESET} ${ARCH:-unknown}"
|
||||||
echo -e "${BOLD}Service:${RESET} virtuality-web.service"
|
echo -e "${BOLD}Service:${RESET} virtuality-web.service"
|
||||||
echo -e "${BOLD}Status:${RESET} systemctl status virtuality-web --no-pager"
|
echo -e "${BOLD}Status:${RESET} systemctl status virtuality-web --no-pager"
|
||||||
echo -e "${BOLD}Logs:${RESET} journalctl -u virtuality-web -f"
|
echo -e "${BOLD}Logs:${RESET} journalctl -u virtuality-web -f"
|
||||||
echo -e "${BOLD}Install log:${RESET} ${LOG_FILE}"
|
echo -e "${BOLD}Install log:${RESET} ${LOG_FILE}"
|
||||||
echo
|
echo
|
||||||
line
|
line
|
||||||
echo -e "${DIM}Следующий шаг: настройка сети /network и создание VM.${RESET}"
|
echo -e "${DIM}Следующий шаг: /host для проверки профиля, /network для NAT, /vm/create для VM.${RESET}"
|
||||||
line
|
line
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user