mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-08-15 09:22:23 +00:00
846 lines
25 KiB
Bash
846 lines
25 KiB
Bash
#!/bin/bash
|
|
# ==========================================================
|
|
# ProxMenuX - Upgrade PVE 8 → 9 (Simplified, per official guide)
|
|
# ==========================================================
|
|
# Author : MacRimi
|
|
# Copyright : (c) 2024 MacRimi
|
|
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
|
# Version : 1.0
|
|
# Last Updated: 14/08/2025
|
|
# ==========================================================
|
|
|
|
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
|
BASE_DIR="/usr/local/share/proxmenux"
|
|
UTILS_FILE="$BASE_DIR/utils.sh"
|
|
|
|
|
|
if [[ -f "$UTILS_FILE" ]]; then
|
|
source "$UTILS_FILE"
|
|
fi
|
|
|
|
load_language
|
|
initialize_cache
|
|
|
|
# ==========================================================
|
|
|
|
LOG="/var/log/pve8-a-pve9-$(date +%Y%m%d-%H%M%S).log"
|
|
: > "$LOG"
|
|
|
|
|
|
REPO_MODE="no-subscription"
|
|
DISABLE_AUDIT="1"
|
|
ASSUME_YES="0"
|
|
CEPH_ENFORCE="auto"
|
|
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--enterprise) REPO_MODE="enterprise" ;;
|
|
--no-subscription) REPO_MODE="no-subscription" ;;
|
|
--skip-audit-disable) DISABLE_AUDIT="0" ;;
|
|
--assume-yes|-y) ASSUME_YES="1" ;;
|
|
--ignore-ceph-check) CEPH_ENFORCE="skip" ;;
|
|
--warn-ceph-check) CEPH_ENFORCE="warn" ;;
|
|
*) ;;
|
|
esac
|
|
done
|
|
|
|
# ==========================================================
|
|
|
|
|
|
ask_run_mode() {
|
|
|
|
if [[ "${ASSUME_YES:-0}" == "1" ]]; then
|
|
msg_ok "$(translate "Run mode: Unattended")"
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
export APT_LISTCHANGES_FRONTEND=none
|
|
return 0
|
|
fi
|
|
|
|
if command -v dialog >/dev/null 2>&1; then
|
|
local title text choice
|
|
title="$(translate "Select run mode")"; [[ -z "$title" ]] && title="Select run mode"
|
|
text="$(translate "Choose how to perform the upgrade:")"; [[ -z "$text" ]] && text="Choose how to perform the upgrade:"
|
|
title=${title//$'\r'/}; title=${title//$'\n'/' '}
|
|
text=${text//$'\r'/}; text=${text//$'\n'/' '}
|
|
|
|
choice=$(
|
|
dialog --backtitle "ProxMenux" \
|
|
--title "$title" \
|
|
--menu "$text" 17 78 3 \
|
|
1 "$(translate "Automatic/Unattended")" \
|
|
2 "$(translate "Interactive (guided, prompts visible)")" \
|
|
3 "$(translate "Manual upgrade guide step by step")" \
|
|
3>&1 1>&2 2>&3
|
|
); status=$?
|
|
|
|
if [[ $status -ne 0 ]]; then
|
|
show_proxmenux_logo || true
|
|
msg_warn "$(translate "Action canceled by user")"
|
|
exit 0
|
|
fi
|
|
|
|
case "$choice" in
|
|
1)
|
|
ASSUME_YES="1"
|
|
show_proxmenux_logo || true
|
|
msg_title "$(translate "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)")"
|
|
msg_info2 "$(translate "Run mode selected: Unattended")"
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
export APT_LISTCHANGES_FRONTEND=none
|
|
;;
|
|
3)
|
|
local url="$REPO_URL/scripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh"
|
|
if command -v curl >/dev/null 2>&1; then
|
|
bash <(curl -fsSL "$url")
|
|
else
|
|
bash <(wget -qO- "$url")
|
|
fi
|
|
;;
|
|
*)
|
|
show_proxmenux_logo || true
|
|
msg_title "$(translate "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)")"
|
|
msg_info2 "$(translate "Run mode selected: Interactive")"
|
|
;;
|
|
esac
|
|
|
|
elif command -v whiptail >/dev/null 2>&1; then
|
|
local choice
|
|
if ! choice=$(
|
|
whiptail --title "$(translate "Select run mode")" \
|
|
--menu "$(translate "Choose how to perform the upgrade:")" 17 78 3 \
|
|
"1" "$(translate "Automatic/Unattended")" \
|
|
"2" "$(translate "Interactive (guided, prompts visible)")" \
|
|
"3" "$(translate "Manual upgrade guide step by step")" \
|
|
3>&1 1>&2 2>&3
|
|
); then
|
|
show_proxmenux_logo || true
|
|
msg_warn "$(translate "Action canceled by user")"
|
|
exit 0
|
|
fi
|
|
case "$choice" in
|
|
1) ASSUME_YES="1"; msg_info2 "$(translate "Run mode selected: Unattended")"
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
export APT_LISTCHANGES_FRONTEND=none ;;
|
|
3) local url="$REPO_URL/scripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh"
|
|
if command -v curl >/dev/null 2>&1; then
|
|
bash <(curl -fsSL "$url")
|
|
else
|
|
bash <(wget -qO- "$url")
|
|
fi ;;
|
|
*) msg_info2 "$(translate "Run mode selected: Interactive")" ;;
|
|
esac
|
|
|
|
else
|
|
printf "%s" "$(translate "Select run mode: [1]unattended / [2]interactive / [3]manual guide? (default: 2): ")"
|
|
read -r ans
|
|
case "$ans" in
|
|
1) ASSUME_YES="1"; msg_info2 "$(translate "Run mode selected: Unattended")"
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
export APT_LISTCHANGES_FRONTEND=none ;;
|
|
3) local url="$REPO_URL/scripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh"
|
|
if command -v curl >/dev/null 2>&1; then
|
|
bash <(curl -fsSL "$url") || exit 1
|
|
else
|
|
bash <(wget -qO- "$url") || exit 1
|
|
fi ;;
|
|
*) msg_info2 "$(translate "Run mode selected: Interactive")" ;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
|
|
|
|
# ==========================================================
|
|
|
|
|
|
confirm() {
|
|
local prompt="$1"
|
|
if [[ "$ASSUME_YES" == "1" ]]; then
|
|
return 0
|
|
fi
|
|
if command -v whiptail >/dev/null 2>&1; then
|
|
whiptail --title "$(translate "Confirmation")" --yesno "$prompt" 14 80
|
|
else
|
|
echo -n "$(translate "$prompt") [y/N]: "
|
|
read -r ans
|
|
[[ "$ans" =~ ^[Yy]$ ]]
|
|
fi
|
|
}
|
|
|
|
ask_choice_auto_or_manual() {
|
|
if [[ "$ASSUME_YES" == "1" ]]; then
|
|
echo "auto"; return 0
|
|
fi
|
|
if command -v whiptail >/dev/null 2>&1; then
|
|
if whiptail --title "$(translate "Cluster upgrade mode")" --yesno "$(translate "Pending upgrades detected on a clustered node.\n\nTo proceed safely, update this node to the latest Proxmox VE 8.x before switching to Trixie/PVE 9.\n\nSelect Yes for AUTOMATIC upgrade (recommended), or No for MANUAL instructions.")" 16 78; then
|
|
echo "auto"
|
|
else
|
|
echo "manual"
|
|
fi
|
|
else
|
|
echo -n "$(translate "Pending upgrades detected on a clustered node. Perform AUTOMATIC upgrade now? (y = automatic, n = manual): ")"
|
|
read -r ans
|
|
[[ "$ans" =~ ^[Yy]$ ]] && echo "auto" || echo "manual"
|
|
fi
|
|
}
|
|
|
|
|
|
|
|
run_step() {
|
|
|
|
local pre="$1"; local ok="$2"; local cmd="$3"
|
|
echo -ne "${TAB}${YW}$(translate "$pre")${CL}"
|
|
if bash -lc "set -o pipefail; $cmd 2>&1 | tee -a \"$LOG\""; then
|
|
echo -e "${BFR}${TAB}${CM}${GN}$(translate "$ok")${CL}"
|
|
return 0
|
|
else
|
|
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "Failed. See log:") $LOG${CL}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
run_step_interactive() {
|
|
|
|
local pre="$1"; local ok="$2"; shift 2
|
|
echo -ne "${TAB}${YW}$(translate "$pre")${CL}"
|
|
if "$@"; then
|
|
echo -e "${BFR}${TAB}${CM}${GN}$(translate "$ok")${CL}"
|
|
return 0
|
|
else
|
|
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "Failed. See log:") $LOG${CL}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
append_step() {
|
|
|
|
local pre="$1"; local ok="$2"; local cmd="$3"
|
|
echo -ne "${TAB}${YW}$(translate "$pre")${CL}"
|
|
if bash -lc "set -o pipefail; $cmd 2>&1 | tee -a \"$LOG\""; then
|
|
echo -e "${BFR}${TAB}${CM}${GN}$(translate "$ok")${CL}"
|
|
else
|
|
echo -e "${BFR}${TAB}${CM}${GN}$(translate "$ok")${CL}"
|
|
fi
|
|
}
|
|
|
|
file_contains() {
|
|
local file="$1"
|
|
local pattern="$2"
|
|
grep -qE "$pattern" "$file" 2>/dev/null
|
|
}
|
|
|
|
is_cluster_node() {
|
|
if command -v pvecm >/dev/null 2>&1 && pvecm status >> "$LOG" 2>&1; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
is_hyperconverged_ceph_node() {
|
|
if command -v pveceph >/dev/null 2>&1; then
|
|
if pveceph status >> "$LOG" 2>&1; then
|
|
return 0
|
|
fi
|
|
fi
|
|
if systemctl list-units --type=service --all 2>/dev/null | grep -Eq 'ceph-(mon|osd|mgr)@'; then
|
|
return 0
|
|
fi
|
|
if ls /var/lib/ceph/osd/ceph-* >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
if [[ -f /etc/pve/ceph.conf ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
|
|
# ==========================================================
|
|
|
|
|
|
create_pve_repo_enterprise_if_missing() {
|
|
local f="/etc/apt/sources.list.d/pve-enterprise.sources"
|
|
[[ -f "$f" ]] && return 0
|
|
cat > "$f" << 'EOF'
|
|
Types: deb
|
|
URIs: https://enterprise.proxmox.com/debian/pve
|
|
Suites: trixie
|
|
Components: pve-enterprise
|
|
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
|
EOF
|
|
chmod 644 "$f"
|
|
}
|
|
|
|
create_pve_repo_nosub_if_missing() {
|
|
local f="/etc/apt/sources.list.d/proxmox.sources"
|
|
[[ -f "$f" ]] && return 0
|
|
cat > "$f" << 'EOF'
|
|
Types: deb
|
|
URIs: http://download.proxmox.com/debian/pve
|
|
Suites: trixie
|
|
Components: pve-no-subscription
|
|
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
|
EOF
|
|
chmod 644 "$f"
|
|
}
|
|
|
|
create_ceph_repo_enterprise_if_missing() {
|
|
local f="/etc/apt/sources.list.d/ceph.sources"
|
|
[[ -f "$f" ]] && return 0
|
|
cat > "$f" << 'EOF'
|
|
Types: deb
|
|
URIs: https://enterprise.proxmox.com/debian/ceph-squid
|
|
Suites: trixie
|
|
Components: enterprise
|
|
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
|
EOF
|
|
chmod 644 "$f"
|
|
}
|
|
|
|
create_ceph_repo_nosub_if_missing() {
|
|
local f="/etc/apt/sources.list.d/ceph.sources"
|
|
[[ -f "$f" ]] && return 0
|
|
cat > "$f" << 'EOF'
|
|
Types: deb
|
|
URIs: http://download.proxmox.com/debian/ceph-squid
|
|
Suites: trixie
|
|
Components: no-subscription
|
|
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
|
EOF
|
|
chmod 644 "$f"
|
|
}
|
|
|
|
disable_enterprise_repo_if_present() {
|
|
local s="/etc/apt/sources.list.d/pve-enterprise.sources"
|
|
local l="/etc/apt/sources.list.d/pve-enterprise.list"
|
|
if [[ -f "$s" ]]; then
|
|
if grep -qi '^Enabled:' "$s"; then
|
|
sed -i 's/^Enabled:.*/Enabled: false/i' "$s"
|
|
else
|
|
echo "Enabled: false" >> "$s"
|
|
fi
|
|
fi
|
|
if [[ -f "$l" ]]; then
|
|
sed -i 's/^[[:space:]]*deb/# deb/' "$l"
|
|
fi
|
|
}
|
|
|
|
comment_legacy_pve8_lists() {
|
|
for f in /etc/apt/sources.list.d/pve-public-repo.list /etc/apt/sources.list.d/pve-install-repo.list; do
|
|
[[ -f "$f" ]] || continue
|
|
sed -i 's/^[[:space:]]*deb/# deb/' "$f" || true
|
|
done
|
|
}
|
|
|
|
comment_legacy_ceph_list() {
|
|
local f="/etc/apt/sources.list.d/ceph.list"
|
|
[[ -f "$f" ]] || return 0
|
|
sed -i 's/^[[:space:]]*deb/# deb/' "$f" || true
|
|
}
|
|
|
|
apt_update_with_repo_fallback() {
|
|
|
|
local output status
|
|
output="$(apt-get update >/dev/null 2>&1 | tee -a "$LOG")"; status=${PIPESTATUS[0]}
|
|
if [[ $status -eq 0 ]]; then
|
|
msg_ok2 "$(translate "APT indexes updated for Trixie")"
|
|
return 0
|
|
fi
|
|
if [[ "$REPO_MODE" == "enterprise" ]] && { echo "$output" | grep -qE '401[[:space:]]+Unauthorized' || echo "$output" | grep -qi 'enterprise\.proxmox\.com'; }; then
|
|
if [[ "$ASSUME_YES" == "1" ]] || confirm "$(translate "Enterprise repository returned 401 Unauthorized (no valid subscription). Switch to the no-subscription repository and retry?")"; then
|
|
|
|
disable_enterprise_repo_if_present
|
|
create_pve_repo_nosub_if_missing
|
|
if [[ -f /etc/apt/sources.list.d/ceph.sources ]]; then
|
|
create_ceph_repo_nosub_if_missing
|
|
fi
|
|
REPO_MODE="no-subscription"
|
|
msg_ok2 "$(translate "Repositories switched to no-subscription")"
|
|
|
|
if apt-get update >> "$LOG" >/dev/null 2>&1; then
|
|
msg_ok2 "$(translate "APT indexes updated for Trixie (no-subscription)")"
|
|
return 0
|
|
else
|
|
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "APT index update failed after switching to no-subscription. See log:") $LOG${CL}"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "Enterprise repository unauthorized and fallback declined by user") $LOG${CL}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "APT index update failed. See log:") $LOG${CL}"
|
|
exit 1
|
|
}
|
|
|
|
# ==========================================================
|
|
|
|
proxmox_repo_candidate_ok() {
|
|
local cand
|
|
cand="$(apt-cache policy proxmox-ve 2>/dev/null | awk -F': ' '/Candidate:/{print $2}')"
|
|
[[ -n "$cand" && "$cand" != "(none)" && "$cand" =~ ^9 ]]
|
|
}
|
|
|
|
|
|
simulate_would_remove_proxmox_ve() {
|
|
apt-get -s dist-upgrade >/dev/null 2>&1 | grep -Eq 'Remv[[:space:]]+proxmox-ve|The following packages will be REMOVED:.*proxmox-ve'
|
|
}
|
|
|
|
|
|
guard_against_proxmox_ve_removal() {
|
|
msg_info "$(translate "Validating Proxmox 9 repositories (checking 'proxmox-ve' candidate)...")"
|
|
if proxmox_repo_candidate_ok; then
|
|
msg_ok "$(translate "Proxmox repository OK (candidate is 9.x)")"
|
|
else
|
|
if [[ "$REPO_MODE" == "enterprise" ]]; then
|
|
msg_warn "$(translate "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...")"
|
|
disable_enterprise_repo_if_present
|
|
create_pve_repo_nosub_if_missing
|
|
REPO_MODE="no-subscription"
|
|
apt_update_with_repo_fallback
|
|
if proxmox_repo_candidate_ok; then
|
|
msg_ok "$(translate "Proxmox repository fixed (no-subscription, candidate is 9.x)")"
|
|
else
|
|
msg_error "$(translate "Could not find a valid 'proxmox-ve' 9.x candidate after switching to no-subscription. Please verify your repository configuration and network, then retry.")"
|
|
exit 1
|
|
fi
|
|
else
|
|
msg_error "$(translate "Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.")"
|
|
exit 1
|
|
fi
|
|
fi
|
|
msg_info "$(translate "Running pre-upgrade simulation to verify 'proxmox-ve' will remain installed...")"
|
|
if simulate_would_remove_proxmox_ve; then
|
|
msg_error "$(translate "Pre-upgrade check FAILED: the simulation shows that 'proxmox-ve' would be REMOVED.\n This indicates a repository or dependency issue and upgrading now could break your Proxmox installation.")"
|
|
echo "---- $(translate "Recommended diagnostic commands") ----"
|
|
echo "apt-cache policy proxmox-ve"
|
|
echo "apt policy | sed -n '1,120p'"
|
|
echo "cat /etc/apt/sources.list"
|
|
echo "ls -l /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources"
|
|
exit 1
|
|
else
|
|
msg_ok "$(translate "Pre-upgrade simulation passed: 'proxmox-ve' will be kept or upgraded safely.")"
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
# ==========================================================
|
|
|
|
|
|
check_not_web_terminal() {
|
|
[[ "${PVE_ALLOW_WEBTERM:-0}" = "1" ]] && return 0
|
|
|
|
[[ -n "${SSH_CONNECTION:-}" ]] && return 0
|
|
|
|
local pid ppid comm args i
|
|
pid=$$
|
|
|
|
show_web_terminal_block_msg() {
|
|
local title msg_line1 msg_line2 msg
|
|
|
|
title="$(translate "Unsupported Terminal")"
|
|
msg_line1="$(translate "This script cannot be executed from the Proxmox web terminal.")"
|
|
msg_line2="$(translate "Please use an SSH session (Linux, macOS, Windows/PuTTY) or a physical console to perform the upgrade.")"
|
|
|
|
msg="${msg_line1}"$'\n\n'"${msg_line2}"
|
|
|
|
whiptail --title "$title" --msgbox "$msg" 12 72
|
|
}
|
|
|
|
for i in {1..12}; do
|
|
ppid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ')
|
|
[[ -z "$ppid" || "$ppid" -le 1 ]] && break
|
|
|
|
comm=$(ps -o comm= -p "$ppid" 2>/dev/null || true)
|
|
args=$(ps -o args= -p "$ppid" 2>/dev/null || true)
|
|
|
|
if echo "$comm $args" | grep -Eqi 'termproxy|vncshell'; then
|
|
show_web_terminal_block_msg
|
|
msg_warn "$(translate "Upgrade canceled by user")"
|
|
exit 1
|
|
fi
|
|
|
|
pid="$ppid"
|
|
done
|
|
}
|
|
|
|
ask_run_mode
|
|
|
|
check_not_web_terminal
|
|
|
|
|
|
|
|
export NCURSES_NO_UTF8_ACS=1
|
|
|
|
show_upgrade_confirm() {
|
|
|
|
local title
|
|
title="$(translate "Upgrade to Proxmox VE 9")"
|
|
|
|
local intro req l1 l2 l3 l4 cont
|
|
intro="$(translate "This will upgrade this node to Proxmox VE 9 on Debian Trixie.")"
|
|
req="$(translate "REQUIREMENTS:")"
|
|
l1="$(translate "Valid backups for all VMs/CTs")"
|
|
l2="$(translate "Run from console, or SSH inside tmux/screen")"
|
|
l3="$(translate "Migrate away any guests that must keep running")"
|
|
l4=""
|
|
cont="$(translate "Continue?")"
|
|
|
|
|
|
local bullet="•"
|
|
if ! locale charmap 2>/dev/null | grep -qi 'utf-8'; then
|
|
bullet="-"
|
|
fi
|
|
|
|
local msg
|
|
msg="${intro}"$'\n\n'"${req}"$'\n'
|
|
msg+="${bullet} ${l1}"$'\n'
|
|
msg+="${bullet} ${l2}"$'\n'
|
|
msg+="${bullet} ${l3}"
|
|
|
|
[[ -n "$l4" ]] && msg+=$'\n'"${bullet} ${l4}"
|
|
msg+=$'\n\n'"${cont}"
|
|
|
|
local cols width height
|
|
cols=$(tput cols 2>/dev/null || echo 80)
|
|
width=$(( cols < 78 ? (cols>40?cols-2:40) : 78 ))
|
|
height=18
|
|
|
|
if whiptail --title "$title" --yesno "$msg" "$height" "$width"; then
|
|
return 0
|
|
else
|
|
msg_warn "$(translate "Upgrade canceled by user")"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
show_upgrade_confirm
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 1
|
|
# ---------------------------
|
|
|
|
|
|
apt_upgrade() {
|
|
local pve_version
|
|
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
|
|
|
if [[ -z "$pve_version" ]]; then
|
|
msg_error "Unable to detect Proxmox version."
|
|
return 1
|
|
fi
|
|
|
|
if [[ "$pve_version" -ge 9 ]]; then
|
|
msg_info2 "$(translate "Proxmox 9 system update allready")"
|
|
msg_success "$(translate "Press Enter to return to menu...")"
|
|
read -r
|
|
exit 1
|
|
|
|
else
|
|
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve8.sh")
|
|
hash -r
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
apt_upgrade
|
|
|
|
run_pve8to9_check() {
|
|
local tmp
|
|
tmp="$(mktemp)"
|
|
echo -e
|
|
set -o pipefail
|
|
pve8to9 --full 2>&1 | tee -a "$LOG" | tee "$tmp"
|
|
local rc=${PIPESTATUS[0]}
|
|
|
|
local fails warns
|
|
fails=$(grep -c 'FAIL:' "$tmp" || true)
|
|
warns=$(grep -c 'WARN:' "$tmp" || true)
|
|
|
|
|
|
if (( fails > 0 )); then
|
|
echo -e
|
|
echo -e "${BFR}${RD}[ERROR] $(translate "Pre-check found") $fails $(translate "blocking issue(s).")\n$(translate "Please resolve the problem(s) as described above, then re-run the upgrade script.")${CL}"
|
|
echo -e
|
|
|
|
if grep -q 'systemd-boot meta-package installed' "$tmp"; then
|
|
echo -e
|
|
echo -e "$(translate "Fix: remove the conflicting meta-package:") apt remove systemd-boot"
|
|
echo -e
|
|
fi
|
|
msg_success "$(translate "Press Enter to exit and repair")"
|
|
read -r
|
|
rm -f "$tmp"
|
|
exit 1
|
|
fi
|
|
echo -e
|
|
msg_ok "$(translate "Checklist pre-check finished. Warnings:") $warns"
|
|
rm -f "$tmp"
|
|
return $rc
|
|
}
|
|
|
|
run_pve8to9_check
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 2
|
|
# ---------------------------
|
|
|
|
run_step \
|
|
"" \
|
|
"Connectivity to Proxmox repository OK" \
|
|
"ping -c1 -W2 download.proxmox.com >/dev/null"
|
|
|
|
|
|
msg_info "$(translate "Checking free space in /var/cache/apt/archives...")"
|
|
FREE_MB=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
|
|
if [[ "${FREE_MB:-0}" -lt 1024 ]]; then
|
|
msg_error "$(translate "Insufficient space:") ${FREE_MB}MB $(translate "(need ≥ 1024MB)")"
|
|
exit 1
|
|
else
|
|
msg_ok "$(translate "Free space OK:") ${FREE_MB}MB"
|
|
fi
|
|
|
|
|
|
if [[ "$DISABLE_AUDIT" == "1" ]]; then
|
|
append_step \
|
|
"" \
|
|
"Audit socket disabled or not required" \
|
|
"systemctl disable --now systemd-journald-audit.socket >/dev/null 2>&1 || true"
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 3
|
|
# ---------------------------
|
|
|
|
if command -v ceph >/dev/null 2>&1; then
|
|
if [[ "$CEPH_ENFORCE" == "skip" ]]; then
|
|
msg_ok2 "$(translate "Ceph check skipped by user flag (--ignore-ceph-check)")"
|
|
else
|
|
if is_hyperconverged_ceph_node; then
|
|
msg_info "$(translate "Ceph detected as hyper-converged on this node. Checking version (require 19.x Squid)...")"
|
|
CEPH_V=$(ceph --version 2>/dev/null | head -1 || true)
|
|
if echo "$CEPH_V" | grep -Eq 'ceph.*(version 19|squid)'; then
|
|
msg_ok "$(translate "Ceph version OK:") $CEPH_V"
|
|
else
|
|
if [[ "$CEPH_ENFORCE" == "warn" ]]; then
|
|
msg_warn "$(translate "Ceph is not 19.x (Squid). Proceeding due to --warn-ceph-check. Detected:") ${CEPH_V:-N/A}"
|
|
else
|
|
msg_error "$(translate "Ceph is not 19.x (Squid). Upgrade Ceph first. Detected:") ${CEPH_V:-N/A}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
else
|
|
CEPH_V=$(ceph --version 2>/dev/null | head -1 || true)
|
|
echo -e "${BFR}${TAB}${CM}${GN}${CEPH_V:-N/A}${CL}"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 4
|
|
# ---------------------------
|
|
|
|
OS_FILE="/etc/apt/sources.list"
|
|
if [[ -f "$OS_FILE" ]]; then
|
|
msg_info "$(translate "Updating Debian Bookworm → Trixie in sources.list...")"
|
|
if sed -i 's/bookworm/trixie/g' "$OS_FILE"; then
|
|
msg_ok "$(translate "sources.list updated to Trixie")"
|
|
else
|
|
msg_ok "$(translate "sources.list update skipped (no change)")"
|
|
fi
|
|
else
|
|
msg_ok "$(translate "No main sources.list present (skipped)")"
|
|
fi
|
|
|
|
PVE_ENT_LIST="/etc/apt/sources.list.d/pve-enterprise.list"
|
|
msg_info "$(translate "Updating pve-enterprise.list (if present) to Trixie...")"
|
|
if [[ -f "$PVE_ENT_LIST" ]]; then
|
|
if sed -i 's/bookworm/trixie/g' "$PVE_ENT_LIST"; then
|
|
msg_ok "$(translate "pve-enterprise.list updated to Trixie")"
|
|
else
|
|
msg_ok "$(translate "pve-enterprise.list update skipped (no change)")"
|
|
fi
|
|
else
|
|
msg_ok "$(translate "No pve-enterprise.list present (skipped)")"
|
|
fi
|
|
|
|
|
|
msg_info "$(translate "Commenting any residual Bookworm lines in *.list...")"
|
|
for f in /etc/apt/sources.list.d/*.list; do
|
|
[[ -f "$f" ]] || continue
|
|
sed -i '/bookworm/s/^/# /' "$f" || true
|
|
done
|
|
sed -i '/bookworm/s/^/# /' "$OS_FILE" 2>/dev/null || true
|
|
msg_ok "$(translate "Residual Bookworm entries commented where applicable")"
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 5
|
|
# ---------------------------
|
|
|
|
if [[ "$REPO_MODE" == "enterprise" ]]; then
|
|
msg_info "$(translate "Ensuring pve-enterprise.sources (PVE 9, deb822) is present...")"
|
|
create_pve_repo_enterprise_if_missing
|
|
msg_ok "$(translate "Enterprise repository present")"
|
|
else
|
|
msg_info "$(translate "Ensuring proxmox.sources (PVE 9, no-subscription, deb822) is present...")"
|
|
create_pve_repo_nosub_if_missing
|
|
msg_ok "$(translate "No-subscription repository present")"
|
|
fi
|
|
|
|
|
|
msg_info "$(translate "Commenting legacy PVE 8 repository .list files (if any)...")"
|
|
comment_legacy_pve8_lists
|
|
msg_ok "$(translate "Legacy PVE 8 .list files commented or not present")"
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 6
|
|
# ---------------------------
|
|
|
|
if command -v ceph >/dev/null 2>&1; then
|
|
if [[ "$REPO_MODE" == "enterprise" ]]; then
|
|
msg_info "$(translate "Ensuring ceph.sources (enterprise, Trixie) is present...")"
|
|
create_ceph_repo_enterprise_if_missing
|
|
msg_ok "$(translate "Ceph enterprise repository present")"
|
|
else
|
|
msg_info "$(translate "Ensuring ceph.sources (no-subscription, Trixie) is present...")"
|
|
create_ceph_repo_nosub_if_missing
|
|
msg_ok "$(translate "Ceph no-subscription repository present")"
|
|
fi
|
|
|
|
msg_info "$(translate "Commenting legacy ceph.list (if present)...")"
|
|
comment_legacy_ceph_list
|
|
msg_ok "$(translate "Legacy ceph.list commented or not present")"
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 7
|
|
# ---------------------------
|
|
|
|
apt_update_with_repo_fallback
|
|
|
|
run_step \
|
|
"" \
|
|
"Repository verification completed (see log)" \
|
|
"apt policy >/dev/null 2>&1"
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 8
|
|
# ---------------------------
|
|
|
|
guard_against_proxmox_ve_removal
|
|
|
|
disable_translation_post_upgrade() {
|
|
translate() { echo "$1"; }
|
|
}
|
|
disable_translation_post_upgrade
|
|
|
|
|
|
|
|
# ==========================================================
|
|
|
|
|
|
# ---------------------------
|
|
# Step 9
|
|
# ---------------------------
|
|
|
|
if [[ "$ASSUME_YES" == "1" ]]; then
|
|
run_step \
|
|
"" \
|
|
"System upgraded to Trixie/PVE 9" \
|
|
"DEBIAN_FRONTEND=noninteractive APT_LISTCHANGES_FRONTEND=none apt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade"
|
|
else
|
|
run_step_interactive \
|
|
"" \
|
|
"System upgraded to Trixie/PVE 9" \
|
|
bash -lc "apt-get dist-upgrade"
|
|
fi
|
|
|
|
|
|
|
|
# ==========================================================
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
# Step 10
|
|
# ---------------------------
|
|
if [[ -d /sys/firmware/efi ]]; then
|
|
if [[ "$ASSUME_YES" == "1" ]]; then
|
|
append_step \
|
|
"Installing grub-efi-amd64" \
|
|
"grub-efi-amd64 installed or already present" \
|
|
"apt-get install -y grub-efi-amd64 >/dev/null 2>&1"
|
|
else
|
|
run_step \
|
|
"Installing grub-efi-amd64" \
|
|
"grub-efi-amd64 installed or already present" \
|
|
"apt-get install grub-efi-amd64 >/dev/null 2>&1"
|
|
fi
|
|
fi
|
|
|
|
|
|
if command -v pve8to9 >/dev/null 2>&1; then
|
|
sleep 2
|
|
append_step \
|
|
"" \
|
|
"Checklist post-upgrade finished" \
|
|
"pve8to9 --full"
|
|
fi
|
|
|
|
echo
|
|
echo
|
|
echo
|
|
echo "════════════════════════════════════════════════════════════════"
|
|
echo " UPGRADE TO PVE 9 COMPLETED (reboot required) "
|
|
echo "════════════════════════════════════════════════════════════════"
|
|
echo
|
|
echo
|
|
echo
|
|
|
|
if confirm "It is RECOMMENDED to reboot now to load the new kernel and services.\n\nReboot now?"; then
|
|
echo -e
|
|
msg_success "$(translate "Press Enter to continue...")"
|
|
read -r
|
|
msg_warn "$(translate "Rebooting the system...")"
|
|
echo -e
|
|
reboot
|
|
else
|
|
msg_info2 "$(translate "You can reboot later manually.")"
|
|
echo -e
|
|
msg_success "$(translate "Press Enter to continue...")"
|
|
read -r
|
|
fi
|
|
|
|
exit 0
|