mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-07-27 02:48:24 +00:00
273 lines
11 KiB
Bash
273 lines
11 KiB
Bash
#!/bin/bash
|
|
# ==========================================================
|
|
# Proxmox VE Update Script — Safe / Non-Invasive Variant
|
|
# ==========================================================
|
|
# Author : MacRimi
|
|
# Copyright : (c) 2024 MacRimi
|
|
# License : GPL-3.0
|
|
# ==========================================================
|
|
# Description:
|
|
# Update path intended for a Proxmox host ALREADY in
|
|
# production. Unlike scripts/global/update-pve8.sh and
|
|
# update-pve9_2.sh (invoked by post_install), this variant
|
|
# NEVER modifies the operator's own configuration:
|
|
#
|
|
# - Does NOT disable Enterprise / Ceph repositories
|
|
# - Does NOT delete legacy repo files
|
|
# - Does NOT overwrite proxmox.sources / debian.sources
|
|
# when they already exist
|
|
# - Does NOT purge alternative NTP services
|
|
# - Does NOT force-install zfsutils / chrony /
|
|
# proxmox-backup-restore-image
|
|
# - Does NOT write no-firmware-warnings.conf
|
|
#
|
|
# What it DOES:
|
|
# 1. Sanity checks (disk space, network)
|
|
# 2. ensure_repositories() — only when repos are MISSING
|
|
# 3. apt-get update, with automatic GPG key import when apt
|
|
# reports NO_PUBKEY (any repo, user's or ours)
|
|
# 4. cleanup_duplicate_repos() — exact URL+Suite+Component
|
|
# match against proxmox.sources / debian.sources; leaves
|
|
# unrelated custom `download.proxmox.com/*` and
|
|
# user-authored pve-*.list files alone; backs each file
|
|
# up before modifying
|
|
# 5. Detect pending upgrades + security count
|
|
# 6. Confirmation dialog
|
|
# 7. apt-get full-upgrade with --force-confdef / --force-confold
|
|
# (never overwrites the operator's edited config files)
|
|
# 8. lvm_repair_check() — refreshes VG metadata when disks
|
|
# passed through to guest VMs (DSM, TrueNAS, …) come back
|
|
# with old PV headers
|
|
# 9. apt-get autoremove + autoclean
|
|
#
|
|
# Reboot detection is handled by the caller (utilities/proxmox_update.sh).
|
|
# ==========================================================
|
|
|
|
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
|
|
BASE_DIR="/usr/local/share/proxmenux"
|
|
UTILS_FILE="$BASE_DIR/utils.sh"
|
|
APT_ENV="env DEBIAN_FRONTEND=noninteractive LC_ALL=C LANG=C"
|
|
|
|
if [[ -f "$UTILS_FILE" ]]; then
|
|
source "$UTILS_FILE"
|
|
fi
|
|
|
|
load_language
|
|
initialize_cache
|
|
|
|
download_common_functions() {
|
|
if ! source "$LOCAL_SCRIPTS/global/common-functions.sh"; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ensure_repositories() lives with the install helpers.
|
|
source_install_functions() {
|
|
local f="$LOCAL_SCRIPTS/global/utils-install-functions.sh"
|
|
if [[ -f "$f" ]]; then
|
|
source "$f"
|
|
fi
|
|
}
|
|
|
|
update_pve_safe() {
|
|
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 "$(translate "Unable to detect Proxmox version")"
|
|
return 1
|
|
fi
|
|
|
|
local start_time
|
|
start_time=$(date +%s)
|
|
local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
|
|
# Screen capture: replay the pre-upgrade context lines after `clear`
|
|
# so the operator keeps the visual history around the noisy apt run.
|
|
local screen_capture="/tmp/proxmenux_screen_capture_$$.txt"
|
|
: > "$screen_capture"
|
|
|
|
download_common_functions
|
|
source_install_functions
|
|
|
|
{
|
|
msg_info2 "$(translate "Detected: Proxmox VE $pve_version — running safe update path")"
|
|
} | tee -a "$screen_capture"
|
|
|
|
# ── 1. Sanity checks ──
|
|
local available_space
|
|
available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
|
|
if [ "$available_space" -lt 1024 ]; then
|
|
msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
|
|
echo -e
|
|
msg_success "$(translate "Press Enter to return to menu...")"
|
|
read -r
|
|
rm -f "$screen_capture"
|
|
return 1
|
|
fi
|
|
|
|
if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
|
|
msg_error "$(translate "Cannot reach Proxmox repositories")"
|
|
echo -e
|
|
msg_success "$(translate "Press Enter to return to menu...")"
|
|
read -r
|
|
rm -f "$screen_capture"
|
|
return 1
|
|
fi
|
|
|
|
# ── 2. ensure_repositories: adds base Proxmox+Debian repos only if
|
|
# they don't already exist. On a configured host this is a no-op. ──
|
|
if declare -f ensure_repositories >/dev/null 2>&1; then
|
|
ensure_repositories
|
|
fi
|
|
|
|
# ── 3. apt-get update with automatic key recovery ──
|
|
local update_output update_exit_code
|
|
update_output=$(apt-get update 2>&1)
|
|
update_exit_code=$?
|
|
|
|
if [ $update_exit_code -eq 0 ]; then
|
|
msg_ok "$(translate "Package lists updated successfully")" | tee -a "$screen_capture"
|
|
else
|
|
if echo "$update_output" | grep -Eq "NO_PUBKEY|GPG error"; then
|
|
local key
|
|
key=$(echo "$update_output" | sed -n 's/.*NO_PUBKEY \([0-9A-F]\{8,40\}\).*/\1/p' | head -1)
|
|
if [ -n "$key" ]; then
|
|
mkdir -p /etc/apt/keyrings
|
|
if command -v gpg >/dev/null 2>&1; then
|
|
if gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" \
|
|
&& gpg --batch --export "$key" | gpg --dearmor -o "/etc/apt/keyrings/${key}.gpg"; then
|
|
msg_ok "$(translate "Imported missing GPG key: $key")" | tee -a "$screen_capture"
|
|
else
|
|
msg_warn "$(translate "Keyrings method failed; trying apt-key fallback")"
|
|
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys "$key" >/dev/null 2>&1 || true
|
|
fi
|
|
else
|
|
msg_warn "$(translate "gpg not found; trying apt-key fallback")"
|
|
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys "$key" >/dev/null 2>&1 || true
|
|
fi
|
|
fi
|
|
if apt-get update > "$log_file" 2>&1; then
|
|
msg_ok "$(translate "Package lists updated after GPG fix")" | tee -a "$screen_capture"
|
|
else
|
|
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
|
|
rm -f "$screen_capture"
|
|
return 1
|
|
fi
|
|
elif echo "$update_output" | grep -Eq "404|Failed to fetch"; then
|
|
msg_warn "$(translate "Some repositories are not available, continuing with available ones...")"
|
|
else
|
|
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
|
|
echo "Error details: $update_output"
|
|
rm -f "$screen_capture"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# ── 4. Precise duplicate cleanup (exact URL+Suite+Component match,
|
|
# backs up files before modifying). Skipped if unavailable. ──
|
|
if declare -f cleanup_duplicate_repos >/dev/null 2>&1; then
|
|
cleanup_duplicate_repos
|
|
fi
|
|
|
|
# ── 5-6. Detect + confirm ──
|
|
local current_pve_version available_pve_version upgradable security_updates
|
|
current_pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
|
available_pve_version=$(apt-cache policy pve-manager 2>/dev/null | grep -oP 'Candidate: \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
|
upgradable=$($APT_ENV apt list --upgradable 2>/dev/null | sed '1d' | sed '/^\s*$/d' | wc -l)
|
|
security_updates=$($APT_ENV apt list --upgradable 2>/dev/null | sed '1d' | grep -ci '\-security')
|
|
|
|
local menu_text
|
|
menu_text="$(translate "System Update Information")\n\n"
|
|
menu_text+="$(translate "Current PVE Version"): $current_pve_version\n"
|
|
if [ -n "$available_pve_version" ] && [ "$available_pve_version" != "$current_pve_version" ]; then
|
|
menu_text+="$(translate "Available PVE Version"): $available_pve_version\n"
|
|
fi
|
|
menu_text+="\n$(translate "Package Updates Available"): $upgradable\n"
|
|
menu_text+="$(translate "Security Updates"): $security_updates\n\n"
|
|
|
|
if [ "$upgradable" -eq 0 ]; then
|
|
menu_text+="$(translate "System is already up to date")"
|
|
whiptail --title "$(translate "Update Status")" --msgbox "$menu_text" 15 70
|
|
apt-get -y autoremove >/dev/null 2>&1 || true
|
|
apt-get -y autoclean >/dev/null 2>&1 || true
|
|
rm -f "$screen_capture"
|
|
return 0
|
|
fi
|
|
|
|
menu_text+="$(translate "Do you want to proceed with the system update?")"
|
|
if ! whiptail --title "$(translate "Proxmox Update")" --yesno "$menu_text" 18 70; then
|
|
msg_info2 "$(translate "Update cancelled by user")"
|
|
apt-get -y autoremove >/dev/null 2>&1 || true
|
|
apt-get -y autoclean >/dev/null 2>&1 || true
|
|
rm -f "$screen_capture"
|
|
return 0
|
|
fi
|
|
|
|
# ── 7. Full upgrade — --force-confdef/confold preserves user-edited configs ──
|
|
# Redraw the ProxMenux frame before apt starts printing so the operator
|
|
# keeps the visual context around the noisy upgrade output.
|
|
clear
|
|
show_proxmenux_logo
|
|
msg_title "$(translate "$SCRIPT_TITLE")"
|
|
cat "$screen_capture"
|
|
|
|
# apt's own progress bar (Progress: [ %]) prints on stderr and only
|
|
# when stdout is a TTY. We pipe stderr through tee to keep a log copy
|
|
# while letting apt keep its interactive stdout, so the native bar
|
|
# keeps rendering at the bottom of the terminal as the user expects.
|
|
DEBIAN_FRONTEND=noninteractive apt -y \
|
|
-o Dpkg::Options::='--force-confdef' \
|
|
-o Dpkg::Options::='--force-confold' \
|
|
full-upgrade 2> >(tee -a "$log_file" >&2)
|
|
local upgrade_exit_code=$?
|
|
echo -e
|
|
|
|
# Redraw once more so the wrap-up (LVM check, cleanup, summary) reads
|
|
# cleanly instead of scrolling under half-a-screen of apt noise.
|
|
clear
|
|
show_proxmenux_logo
|
|
msg_title "$(translate "$SCRIPT_TITLE")"
|
|
cat "$screen_capture"
|
|
|
|
if [ $upgrade_exit_code -ne 0 ]; then
|
|
msg_error "$(translate "System upgrade failed. Check log: $log_file")"
|
|
rm -f "$screen_capture"
|
|
return 1
|
|
fi
|
|
|
|
msg_ok "$(translate "System upgrade completed")"
|
|
|
|
# ── 8. LVM header repair (only touches VGs actually flagged as stale) ──
|
|
if declare -f lvm_repair_check >/dev/null 2>&1; then
|
|
lvm_repair_check
|
|
fi
|
|
|
|
# ── 9. DKMS driver rebuild if a new kernel was staged ──
|
|
if declare -f pmx_rebuild_dkms_after_kernel >/dev/null 2>&1; then
|
|
pmx_rebuild_dkms_after_kernel
|
|
fi
|
|
|
|
# ── 10. Final cleanup ──
|
|
apt-get -y autoremove >/dev/null 2>&1 || true
|
|
apt-get -y autoclean >/dev/null 2>&1 || true
|
|
msg_ok "$(translate "Cleanup finished")"
|
|
|
|
local end_time duration minutes seconds
|
|
end_time=$(date +%s)
|
|
duration=$((end_time - start_time))
|
|
minutes=$((duration / 60))
|
|
seconds=$((duration % 60))
|
|
|
|
echo -e "${TAB}${BGN}$(translate "====== PVE UPDATE COMPLETED ======")${CL}"
|
|
echo -e "${TAB}${GN}⏱️ $(translate "Duration")${CL}: ${BL}${minutes}m ${seconds}s${CL}"
|
|
echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$log_file${CL}"
|
|
echo -e "${TAB}${GN}📦 $(translate "Packages upgraded")${CL}: ${BL}$upgradable${CL}"
|
|
echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}${available_pve_version:-$current_pve_version}${CL}"
|
|
|
|
msg_ok "$(translate "Proxmox VE safe update completed")"
|
|
rm -f "$screen_capture"
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
update_pve_safe
|
|
fi
|