mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-02 10:52:22 +00:00
feat(pbs): post-install menu for Proxmox Backup Server
- Open the post-install menu from the PBS menu - List only the customizable options that apply to PBS - Offer the PBS no-subscription repository through the native repository API - Upgrade PBS with the package list shown before applying it - Remove and restore the subscription notice on the PBS web interface - Install Figurine on PBS without the Proxmox VE aliases - Offer the Helper-Scripts PBS post-install and microcode scripts - Tune firewall bridges after the systemd udev rules in the automated post-install - Add PBS repository policy tests and syntax checks - Document the PBS differences on the post-install page - Use backups in the Spanish PBS update message
This commit is contained in:
@@ -283,6 +283,10 @@ reload_services() {
|
||||
systemctl is-active --quiet pvedaemon 2>/dev/null && {
|
||||
systemctl reload pvedaemon 2>/dev/null || true
|
||||
}
|
||||
# Proxmox Backup Server serves the same toolkit from its own proxy.
|
||||
systemctl is-active --quiet proxmox-backup-proxy 2>/dev/null && {
|
||||
systemctl reload proxmox-backup-proxy 2>/dev/null || systemctl restart proxmox-backup-proxy 2>/dev/null || true
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
@@ -316,9 +320,12 @@ EOFAPT
|
||||
# Main function to remove subscription banner
|
||||
remove_subscription_banner_v3() {
|
||||
local pve_version
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1 || echo "unknown")
|
||||
|
||||
msg_info "$(translate "Detected Proxmox VE") ${pve_version} - $(translate "applying minimal banner patch")"
|
||||
if command -v pveversion >/dev/null 2>&1; then
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1 || echo "unknown")
|
||||
msg_info "$(translate "Detected Proxmox VE") ${pve_version} - $(translate "applying minimal banner patch")"
|
||||
else
|
||||
msg_info "$(translate "Detected Proxmox Backup Server") - $(translate "applying minimal banner patch")"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -62,3 +62,48 @@ ensure_repositories() {
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# The no-subscription switch of a Proxmox Backup Server: the same policy and
|
||||
# API as on PVE, offered only when the host has no active subscription.
|
||||
ensure_pbs_repositories() {
|
||||
local suite decision refresh_status
|
||||
if [[ "$(pmx_platform_field supported)" != true ]]; then
|
||||
msg_error "$(translate 'This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.')"
|
||||
return 1
|
||||
fi
|
||||
suite=$(pmx_platform_field codename)
|
||||
decision=$(repository_policy plan "$suite" pbs) || return 1
|
||||
case "$decision" in
|
||||
preserve) return 0 ;;
|
||||
offer) ;;
|
||||
*) msg_error "$(translate 'Repository policy returned an unexpected result.')"; return 1 ;;
|
||||
esac
|
||||
if ! declare -F hybrid_yesno >/dev/null || { [[ ! -t 0 ]] && ! { declare -F is_web_mode >/dev/null && is_web_mode; }; }; then
|
||||
msg_error "$(translate 'No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.')"
|
||||
return 1
|
||||
fi
|
||||
if ! hybrid_yesno "$(translate 'Proxmox repository')" \
|
||||
"$(translate 'This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.')" 14 90; then
|
||||
msg_error "$(translate 'Repository switch declined; no APT source changed.')"
|
||||
return 1
|
||||
fi
|
||||
decision=$(repository_policy apply "$suite" pbs) || return 1
|
||||
[[ "$decision" == changed || "$decision" == preserve ]] || return 1
|
||||
if [[ "$decision" == changed ]]; then
|
||||
if declare -F pmx_record_execution >/dev/null; then
|
||||
local journal_function="${PMX_JOURNAL_FUNCTION:-}" journal_version="${PMX_JOURNAL_VERSION:-}"
|
||||
pmx_journal_context "ensure_pbs_repositories" "" "${PMX_JOURNAL_SOURCE:-}"
|
||||
pmx_record_execution "Switch to the PBS no-subscription repository" \
|
||||
"proxmox-backup-debug api set /nodes/localhost/apt/repositories --handle no-subscription"
|
||||
PMX_JOURNAL_FUNCTION="$journal_function" PMX_JOURNAL_VERSION="$journal_version"
|
||||
fi
|
||||
if apt-get update; then
|
||||
return 0
|
||||
else
|
||||
refresh_status=$?
|
||||
msg_error "$(translate 'Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.')"
|
||||
return "$refresh_status"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Proxmox repository policy via its own parsed APT repository API.
|
||||
"""Proxmox VE and Proxmox Backup Server repository policy via their own parsed APT
|
||||
repository API.
|
||||
|
||||
Only `notfound` or `expired` permits an offered switch. Never print raw subscription or
|
||||
repository API responses: they can contain subscription keys or credentials.
|
||||
@@ -12,6 +13,19 @@ import sys
|
||||
|
||||
ENDPOINT = '/nodes/localhost/apt/repositories'
|
||||
PVE_CHANNELS = {'pve-enterprise', 'pve-no-subscription', 'pve-test', 'pvetest'}
|
||||
# Proxmox VE and Proxmox Backup Server share the repository API; each has its
|
||||
# own command line, channels and place in the web interface.
|
||||
PRODUCTS = {
|
||||
'pve': {'api': ['pvesh'], 'name': 'Proxmox VE', 'short': 'PVE',
|
||||
'channels': PVE_CHANNELS, 'enterprise': 'pve-enterprise', 'public': 'pve-no-subscription',
|
||||
'test': {'pve-no-subscription', 'pve-test', 'pvetest'},
|
||||
'ui': 'Node > Updates > Repositories', 'ceph': True},
|
||||
'pbs': {'api': ['proxmox-backup-debug', 'api'], 'name': 'Proxmox Backup Server', 'short': 'PBS',
|
||||
'channels': {'pbs-enterprise', 'pbs-no-subscription', 'pbs-test', 'pbstest'},
|
||||
'enterprise': 'pbs-enterprise', 'public': 'pbs-no-subscription',
|
||||
'test': {'pbs-no-subscription', 'pbs-test', 'pbstest'},
|
||||
'ui': 'Administration > Repositories', 'ceph': False},
|
||||
}
|
||||
|
||||
|
||||
def run(args):
|
||||
@@ -21,7 +35,16 @@ def run(args):
|
||||
raise ValueError(f'Unable to query or change Proxmox repository state ({args[0]}). Check the service and retry.') from exc
|
||||
|
||||
|
||||
def subscription_status():
|
||||
def subscription_status(product='pve'):
|
||||
if product == 'pbs':
|
||||
# Only the status field is read; the answer also carries the server ID.
|
||||
try:
|
||||
status = json.loads(run(['proxmox-backup-manager', 'subscription', 'get', '--output-format', 'json'])).get('status')
|
||||
except (ValueError, AttributeError):
|
||||
status = None
|
||||
if status not in ('active', 'notfound', 'expired'):
|
||||
raise ValueError('Subscription status is not unambiguously active, notfound or expired. Check proxmox-backup-manager subscription get locally; no repository changed.')
|
||||
return status
|
||||
# pvesubscription's CLI get printer emits sorted "key: value" lines,
|
||||
# including a secret key and server ID. Only parse the exact status line.
|
||||
output = run(['pvesubscription', 'get'])
|
||||
@@ -31,9 +54,10 @@ def subscription_status():
|
||||
return statuses[0]
|
||||
|
||||
|
||||
def repository_state(suite):
|
||||
def repository_state(suite, product='pve'):
|
||||
p = PRODUCTS[product]
|
||||
try:
|
||||
data = json.loads(run(['pvesh', 'get', ENDPOINT, '--output-format', 'json']))
|
||||
data = json.loads(run([*p['api'], 'get', ENDPOINT, '--output-format', 'json']))
|
||||
if not isinstance(data, dict) or not isinstance(data['files'], list) or not isinstance(data['errors'], list) or not isinstance(data['digest'], str) or not isinstance(data['standard-repos'], list):
|
||||
raise ValueError()
|
||||
if data['errors']:
|
||||
@@ -86,13 +110,14 @@ def repository_state(suite):
|
||||
entries.append((file['path'], index, row))
|
||||
return data, entries
|
||||
except (KeyError, TypeError, ValueError, IndexError) as exc:
|
||||
raise ValueError('Proxmox APT repository inventory is invalid or reports parse errors. Fix sources in Node > Updates > Repositories; no repository changed.') from exc
|
||||
raise ValueError(f"Proxmox APT repository inventory is invalid or reports parse errors. Fix sources in {p['ui']}; no repository changed.") from exc
|
||||
|
||||
|
||||
def evaluate(suite, apply=False):
|
||||
def evaluate(suite, apply=False, product='pve'):
|
||||
if suite not in ('bookworm', 'trixie'):
|
||||
raise ValueError('Unsupported Proxmox suite; no repository changed.')
|
||||
data, entries = repository_state(suite)
|
||||
p = PRODUCTS[product]
|
||||
data, entries = repository_state(suite, product)
|
||||
pve = []
|
||||
ceph = []
|
||||
debian = False
|
||||
@@ -102,40 +127,40 @@ def evaluate(suite, apply=False):
|
||||
if not row['Enabled'] or 'deb' not in row['Types']:
|
||||
continue
|
||||
components = set(row['Components'])
|
||||
if components & PVE_CHANNELS:
|
||||
if components & p['channels']:
|
||||
if suite not in row['Suites']:
|
||||
wrong_pve.append(f"{' '.join(sorted(components & PVE_CHANNELS))} in {path} (suite {' '.join(row['Suites'])})")
|
||||
wrong_pve.append(f"{' '.join(sorted(components & p['channels']))} in {path} (suite {' '.join(row['Suites'])})")
|
||||
pve.append((path, index, row))
|
||||
if 'main' in components and suite in row['Suites'] and not components & PVE_CHANNELS:
|
||||
if 'main' in components and suite in row['Suites'] and not components & p['channels']:
|
||||
debian = True
|
||||
if 'enterprise' in components and any('/ceph-' in uri for uri in row['URIs']):
|
||||
if p['ceph'] and 'enterprise' in components and any('/ceph-' in uri for uri in row['URIs']):
|
||||
if suite not in row['Suites']:
|
||||
wrong_ceph.append(f"{path} (suite {' '.join(row['Suites'])})")
|
||||
ceph.append((path, index, row))
|
||||
# Name the stale sources, never their URIs: those can carry credentials.
|
||||
if wrong_pve:
|
||||
raise ValueError(f"This host runs Proxmox VE on {suite}, but these Proxmox VE repositories are for another suite: "
|
||||
f"{'; '.join(wrong_pve)}. Disable or remove them in Node > Updates > Repositories and run the update again; no repository changed.")
|
||||
raise ValueError(f"This host runs {p['name']} on {suite}, but these {p['name']} repositories are for another suite: "
|
||||
f"{'; '.join(wrong_pve)}. Disable or remove them in {p['ui']} and run the update again; no repository changed.")
|
||||
if wrong_ceph:
|
||||
raise ValueError(f"This host runs Proxmox VE on {suite}, but these Enterprise Ceph repositories are for another suite: "
|
||||
f"{'; '.join(wrong_ceph)}. Correct them in Node > Updates > Repositories before switching; no repository changed.")
|
||||
|
||||
if any(set(row['Components']) & {'pve-no-subscription', 'pve-test', 'pvetest'} for _, _, row in pve):
|
||||
if any(set(row['Components']) & p['test'] for _, _, row in pve):
|
||||
return 'preserve'
|
||||
# Entitlement lookup is only needed when considering a switch. Existing
|
||||
# public/test channels are the operator's choice, even if lookup fails.
|
||||
status = subscription_status() # fail closed before any write
|
||||
status = subscription_status(product) # fail closed before any write
|
||||
if status == 'active':
|
||||
if not pve:
|
||||
raise ValueError('Host has an active subscription but no active PVE repository. Configure its Enterprise source in Node > Updates > Repositories; no repository changed.')
|
||||
raise ValueError(f"Host has an active subscription but no active {p['short']} repository. Configure its Enterprise source in {p['ui']}; no repository changed.")
|
||||
return 'preserve'
|
||||
if not debian:
|
||||
raise ValueError('No active Debian base repository for this suite; configure it in the Proxmox repository UI before continuing.')
|
||||
# No active PVE source or only inaccessible Enterprise. A mixed stanza
|
||||
# No active product source or only inaccessible Enterprise. A mixed stanza
|
||||
# cannot be disabled without also disabling an unrelated component.
|
||||
disable = [item for item in pve if 'pve-enterprise' in item[2]['Components']] + ceph
|
||||
disable = [item for item in pve if p['enterprise'] in item[2]['Components']] + ceph
|
||||
for _, _, row in disable:
|
||||
expected = {'pve-enterprise'} if 'pve-enterprise' in row['Components'] else {'enterprise'}
|
||||
expected = {p['enterprise']} if p['enterprise'] in row['Components'] else {'enterprise'}
|
||||
if set(row['Components']) != expected:
|
||||
raise ValueError('Enterprise shares an APT stanza with other components. Split it in the Proxmox repository UI; no repository changed.')
|
||||
handles = [r.get('handle') for r in data['standard-repos'] if r.get('handle') == 'no-subscription']
|
||||
@@ -149,9 +174,9 @@ def evaluate(suite, apply=False):
|
||||
try:
|
||||
for path, index, original in disable:
|
||||
next(file for file in expected if file['path'] == path)['repositories'][index]['Enabled'] = False
|
||||
run(['pvesh', 'create', ENDPOINT, '--path', path, '--index', str(index),
|
||||
run([*p['api'], 'create', ENDPOINT, '--path', path, '--index', str(index),
|
||||
'--enabled', '0', '--digest', data['digest']])
|
||||
refreshed, current = repository_state(suite)
|
||||
refreshed, current = repository_state(suite, product)
|
||||
# Per-file digests change when Proxmox serializes the disabled entry.
|
||||
inventory = lambda files: sorted(
|
||||
({key: value for key, value in file.items() if key != 'digest'} for file in files),
|
||||
@@ -159,23 +184,25 @@ def evaluate(suite, apply=False):
|
||||
if inventory(refreshed['files']) != inventory(expected):
|
||||
raise ValueError('Could not verify the expected repository inventory after Enterprise disable; possible concurrent edit. Inspect the Proxmox repository UI before retrying.')
|
||||
data = refreshed
|
||||
run(['pvesh', 'set', ENDPOINT, '--handle', 'no-subscription', '--digest', data['digest']])
|
||||
_, current = repository_state(suite)
|
||||
run([*p['api'], 'set', ENDPOINT, '--handle', 'no-subscription', '--digest', data['digest']])
|
||||
_, current = repository_state(suite, product)
|
||||
if not any(row['Enabled'] and 'deb' in row['Types'] and suite in row['Suites']
|
||||
and 'pve-no-subscription' in row['Components'] for _, _, row in current) \
|
||||
and p['public'] in row['Components'] for _, _, row in current) \
|
||||
or any(p == path and i == index and row['Enabled']
|
||||
for path, index, _ in disable for p, i, row in current):
|
||||
raise ValueError('Could not verify the switched repositories; inspect the Proxmox repository UI before retrying.')
|
||||
except ValueError as exc:
|
||||
raise ValueError('Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect Node > Updates > Repositories before retrying; no automatic rollback was attempted.') from exc
|
||||
raise ValueError(f"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {p['ui']} before retrying; no automatic rollback was attempted.") from exc
|
||||
return 'changed'
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in ('plan', 'apply'):
|
||||
raise ValueError('Usage: repository_policy.py plan|apply bookworm|trixie')
|
||||
print(evaluate(sys.argv[2], apply=sys.argv[1] == 'apply'))
|
||||
if len(sys.argv) not in (3, 4) or sys.argv[1] not in ('plan', 'apply') \
|
||||
or (len(sys.argv) == 4 and sys.argv[3] not in PRODUCTS):
|
||||
raise ValueError('Usage: repository_policy.py plan|apply bookworm|trixie [pve|pbs]')
|
||||
product = sys.argv[3] if len(sys.argv) == 4 else 'pve'
|
||||
print(evaluate(sys.argv[2], apply=sys.argv[1] == 'apply', product=product))
|
||||
except ValueError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# Proxmox Backup Server Update Script (post-install flows)
|
||||
# ==========================================================
|
||||
|
||||
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
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
source "$LOCAL_SCRIPTS/global/common-functions.sh"
|
||||
source "$LOCAL_SCRIPTS/global/repository-functions.sh"
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
update_pbs() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "update_pbs" "$FUNC_VERSION"
|
||||
|
||||
if ! pmx_is_pbs || [[ "$(pmx_platform_field supported)" != true ]]; then
|
||||
msg_error "$(translate 'This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.')"
|
||||
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"
|
||||
local screen_capture="/tmp/proxmenux_screen_capture_$$.txt"
|
||||
local OS_CODENAME
|
||||
OS_CODENAME=$(pmx_platform_field codename)
|
||||
|
||||
{
|
||||
echo "=== ProxMenux — Proxmox Backup Server update ==="
|
||||
echo "Started: $(date -Iseconds)"
|
||||
echo "Host: $(hostname)"
|
||||
echo "Running: proxmox-backup-server $(dpkg-query -W -f='${Version}' proxmox-backup-server 2>/dev/null)"
|
||||
} > "$log_file"
|
||||
|
||||
if ! ensure_pbs_repositories; then
|
||||
msg_error "$(translate "Failed to configure Proxmox Backup Server repositories")"
|
||||
rm -f "$screen_capture"
|
||||
return 1
|
||||
fi
|
||||
|
||||
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")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local update_output update_exit_code
|
||||
update_output=$(apt-get -o APT::Update::Error-Mode=any update 2>&1)
|
||||
update_exit_code=$?
|
||||
{
|
||||
echo
|
||||
echo "--- apt-get update (exit $update_exit_code) ---"
|
||||
printf '%s\n' "$update_output"
|
||||
} >> "$log_file"
|
||||
if [ $update_exit_code -ne 0 ]; then
|
||||
msg_error "$(translate "APT refresh failed. Check the PBS subscription and repositories in Administration > Repositories. Log:") $log_file"
|
||||
return 1
|
||||
fi
|
||||
msg_ok "$(translate "Package lists updated successfully")" | tee -a "$screen_capture"
|
||||
|
||||
local current_version available_version
|
||||
current_version=$(dpkg-query -W -f='${Version}' proxmox-backup-server 2>/dev/null)
|
||||
available_version=$(apt-cache policy proxmox-backup-server 2>/dev/null | awk '/Candidate:/ {print $2; exit}')
|
||||
|
||||
local upgradable upgradable_raw upgradable_list security_updates
|
||||
upgradable_raw=$($APT_ENV apt list --upgradable 2>/dev/null \
|
||||
| sed '1d' \
|
||||
| sed '/^\s*$/d')
|
||||
upgradable=$(printf '%s' "$upgradable_raw" | grep -c . )
|
||||
security_updates=$(printf '%s\n' "$upgradable_raw" | grep -ci '\-security')
|
||||
upgradable_list=$(printf '%s\n' "$upgradable_raw" | pmx_format_upgradable)
|
||||
|
||||
{
|
||||
echo
|
||||
echo "--- Packages to upgrade ($upgradable) ---"
|
||||
[ "$upgradable" -gt 0 ] && printf '%s\n' "$upgradable_list"
|
||||
} >> "$log_file"
|
||||
|
||||
local menu_text
|
||||
menu_text="$(translate "System Update Information")\n\n"
|
||||
menu_text+="$(translate "Current PBS Version"): $current_version\n"
|
||||
if [ -n "$available_version" ] && [ "$available_version" != "$current_version" ]; then
|
||||
menu_text+="$(translate "Available PBS Version"): $available_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
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
cat "$screen_capture"
|
||||
msg_ok "$(translate "System is already up to date. No update needed.")"
|
||||
echo -e "\nSystem is already up to date." >> "$log_file"
|
||||
rm -f "$screen_capture"
|
||||
return 0
|
||||
fi
|
||||
|
||||
menu_text+="$(translate "Packages to be upgraded"):\n$upgradable_list\n\n"
|
||||
menu_text+="$(translate "Do you want to proceed with the system update?")"
|
||||
local proceed=0
|
||||
whiptail --title "$(translate "Proxmox Backup Server Update")" --scrolltext --yesno "$menu_text" 24 78 || proceed=1
|
||||
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
cat "$screen_capture"
|
||||
|
||||
if [ $proceed -ne 0 ]; then
|
||||
msg_info2 "$(translate "Update cancelled by user")"
|
||||
rm -f "$screen_capture"
|
||||
return 0
|
||||
fi
|
||||
|
||||
pmx_record_execution "Upgrade Proxmox Backup Server packages" "apt -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold full-upgrade"
|
||||
local dpkg_mark upgrade_exit_code
|
||||
dpkg_mark=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "\n--- apt full-upgrade ---" >> "$log_file"
|
||||
DEBIAN_FRONTEND=noninteractive apt -y \
|
||||
-o Dpkg::Options::='--force-confdef' \
|
||||
-o Dpkg::Options::='--force-confold' \
|
||||
full-upgrade 2> >(tee -a "$log_file" >&2)
|
||||
upgrade_exit_code=$?
|
||||
echo -e
|
||||
{
|
||||
echo
|
||||
echo "--- Packages changed (exit $upgrade_exit_code) ---"
|
||||
pmx_dpkg_changes_since "$dpkg_mark"
|
||||
} >> "$log_file"
|
||||
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
cat "$screen_capture"
|
||||
rm -f "$screen_capture"
|
||||
|
||||
if [ $upgrade_exit_code -ne 0 ]; then
|
||||
msg_error "$(translate "System upgrade failed. Check log: $log_file")"
|
||||
echo "Finished: $(date -Iseconds) — upgrade failed (exit $upgrade_exit_code)" >> "$log_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
pmx_record_execution "Remove unused packages" "apt-get -y autoremove"
|
||||
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 "====== PBS 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 Backup Server")${CL}: ${BL}$(dpkg-query -W -f='${Version}' proxmox-backup-server 2>/dev/null) (Debian $OS_CODENAME)${CL}"
|
||||
|
||||
{
|
||||
echo
|
||||
echo "Finished: $(date -Iseconds) — ${minutes}m ${seconds}s"
|
||||
echo "Running: proxmox-backup-server $(dpkg-query -W -f='${Version}' proxmox-backup-server 2>/dev/null)"
|
||||
} >> "$log_file"
|
||||
|
||||
if [ -f /var/run/reboot-required ] || [ "$(uname -r)" != "$(ls -1 /boot/vmlinuz-* 2>/dev/null | sed 's|/boot/vmlinuz-||' | sort -V | tail -1)" ]; then
|
||||
msg_warn "$(translate "A newer kernel is installed. Reboot before the next backup window to use it.")"
|
||||
fi
|
||||
msg_ok "$(translate "Proxmox Backup Server update completed.")"
|
||||
}
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
update_pbs
|
||||
fi
|
||||
@@ -28,6 +28,12 @@ fi
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
PMX_ON_PBS=false
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/platform-functions.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/platform-functions.sh"
|
||||
pmx_is_pbs && PMX_ON_PBS=true
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
confirm_and_run() {
|
||||
local name="$1"
|
||||
@@ -55,8 +61,13 @@ confirm_automated_script() {
|
||||
local script_info=""
|
||||
|
||||
|
||||
script_info+="$(translate "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server"):\n\n"
|
||||
script_info+="• $(translate "Configure") \Z4free repositories\Z0 $(translate "and upgrade the system (disables the enterprise repo)")\n"
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
script_info+="$(translate "This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server"):\n\n"
|
||||
script_info+="• $(translate "Upgrade the system, offering the") \Z4no-subscription repository\Z0 $(translate "when the server has no subscription (you will be asked)")\n"
|
||||
else
|
||||
script_info+="$(translate "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server"):\n\n"
|
||||
script_info+="• $(translate "Configure") \Z4free repositories\Z0 $(translate "and upgrade the system (disables the enterprise repo)")\n"
|
||||
fi
|
||||
script_info+="• $(translate "Optionally remove") \Z4subscription banner\Z0 $(translate "from Proxmox web interface (you will be asked)")\n"
|
||||
script_info+="• $(translate "Optimize") \Z4memory\Z0, \Z4kernel\Z0, $(translate "and") \Z4network\Z0 $(translate "for better performance and stability")\n"
|
||||
script_info+="• $(translate "Install and configure") \Z4Log2RAM\Z0 $(translate "(only on SSD/NVMe) to protect your disk")\n"
|
||||
@@ -229,6 +240,12 @@ declare -a COMMUNITY_SCRIPTS=(
|
||||
"Proxmox VE Post Install|Helper-Scripts|bash -c \"\$(wget -qLO - https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/post-pve-install.sh); msg_success \\\"\$(translate 'Press ENTER to continue...')\\\"; read -r _\""
|
||||
"Proxmox VE Microcode|Helper-Scripts|bash -c \"\$(wget -qLO - https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/microcode.sh); msg_success \\\"\$(translate 'Press ENTER to continue...')\\\"; read -r _\""
|
||||
)
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
COMMUNITY_SCRIPTS=(
|
||||
"Proxmox Backup Server Post Install|Helper-Scripts|bash -c \"\$(wget -qLO - https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/post-pbs-install.sh); msg_success \\\"\$(translate 'Press ENTER to continue...')\\\"; read -r _\""
|
||||
"Proxmox Backup Server Microcode|Helper-Scripts|bash -c \"\$(wget -qLO - https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/pbs-microcode.sh); msg_success \\\"\$(translate 'Press ENTER to continue...')\\\"; read -r _\""
|
||||
)
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
|
||||
@@ -271,6 +288,12 @@ post_install_label() {
|
||||
"Proxmox VE Microcode")
|
||||
echo "Proxmox VE Microcode"
|
||||
;;
|
||||
"Proxmox Backup Server Post Install")
|
||||
echo "Proxmox Backup Server Post Install"
|
||||
;;
|
||||
"Proxmox Backup Server Microcode")
|
||||
echo "Proxmox Backup Server Microcode"
|
||||
;;
|
||||
"Community Scripts")
|
||||
translate "Community Scripts"
|
||||
;;
|
||||
|
||||
+15
-13
@@ -2,8 +2,8 @@
|
||||
# ==========================================================
|
||||
# ProxMenux - Proxmox Backup Server menu
|
||||
# ==========================================================
|
||||
# PBS exposes compatible host tools; PVE guest/repository optimizations
|
||||
# stay in the PVE menu.
|
||||
# PBS exposes compatible host tools and its own post-install profile;
|
||||
# PVE guest optimizations stay in the PVE menu.
|
||||
# ==========================================================
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
LOCAL_SCRIPTS="$BASE_DIR/scripts"
|
||||
@@ -31,20 +31,22 @@ pbs_show() {
|
||||
|
||||
while true; do
|
||||
choice=$(dialog --clear --backtitle "ProxMenux" --title "Proxmox Backup Server" \
|
||||
--menu "$(translate 'Select an option:')" 18 75 8 \
|
||||
1 "$(translate 'PBS version and packages')" \
|
||||
2 "$(translate 'PBS datastores')" \
|
||||
3 "$(translate 'Recent PBS tasks')" \
|
||||
4 "$(translate 'System Utilities Installer')" \
|
||||
5 "$(translate 'Update PBS using configured repositories')" \
|
||||
--menu "$(translate 'Select an option:')" 19 75 9 \
|
||||
1 "$(translate "Settings post-install Proxmox")" \
|
||||
2 "$(translate 'PBS version and packages')" \
|
||||
3 "$(translate 'PBS datastores')" \
|
||||
4 "$(translate 'Recent PBS tasks')" \
|
||||
5 "$(translate 'System Utilities Installer')" \
|
||||
6 "$(translate 'Update PBS using configured repositories')" \
|
||||
s "$(translate 'Settings')" \
|
||||
0 "$(translate 'Exit')" 2>&1 >/dev/tty) || break
|
||||
case "$choice" in
|
||||
1) pbs_show "$(translate 'PBS version and packages')" proxmox-backup-manager versions --verbose ;;
|
||||
2) pbs_show "$(translate 'PBS datastores')" proxmox-backup-manager datastore list ;;
|
||||
3) pbs_show "$(translate 'Recent PBS tasks')" proxmox-backup-manager task list --all true --limit 20 ;;
|
||||
4) bash "$LOCAL_SCRIPTS/utilities/system_utils.sh" ;;
|
||||
5) bash "$LOCAL_SCRIPTS/utilities/pbs_update.sh" ;;
|
||||
1) exec bash "$LOCAL_SCRIPTS/menus/menu_post_install.sh" ;;
|
||||
2) pbs_show "$(translate 'PBS version and packages')" proxmox-backup-manager versions --verbose ;;
|
||||
3) pbs_show "$(translate 'PBS datastores')" proxmox-backup-manager datastore list ;;
|
||||
4) pbs_show "$(translate 'Recent PBS tasks')" proxmox-backup-manager task list --all true --limit 20 ;;
|
||||
5) bash "$LOCAL_SCRIPTS/utilities/system_utils.sh" ;;
|
||||
6) bash "$LOCAL_SCRIPTS/utilities/pbs_update.sh" ;;
|
||||
s) exec bash "$LOCAL_SCRIPTS/menus/config_menu.sh" ;;
|
||||
0) break ;;
|
||||
esac
|
||||
|
||||
@@ -59,7 +59,12 @@ initialize_cache
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/platform-functions.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/platform-functions.sh"
|
||||
pmx_require_pve || exit 1
|
||||
fi
|
||||
# Proxmox Backup Server runs the same host optimizations; repositories,
|
||||
# package upgrade and the subscription notice follow its own tooling.
|
||||
PMX_ON_PBS=false
|
||||
if declare -F pmx_is_pbs >/dev/null && pmx_is_pbs; then
|
||||
PMX_ON_PBS=true
|
||||
fi
|
||||
|
||||
# Global variables
|
||||
@@ -142,6 +147,10 @@ $(translate "Do you want to continue anyway?")" 13 70
|
||||
|
||||
|
||||
apt_upgrade() {
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
bash "$LOCAL_SCRIPTS/global/update-pbs.sh"
|
||||
return
|
||||
fi
|
||||
local pve_version
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
|
||||
@@ -171,6 +180,20 @@ apt_upgrade() {
|
||||
remove_subscription_banner() {
|
||||
local FUNC_VERSION="1.0"
|
||||
# description: Patch the Proxmox web UI to suppress the subscription dialog and register a successful patch.
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
stop_spinner
|
||||
if ! whiptail --title "Proxmox Backup Server Subscription Banner Removal" \
|
||||
--yesno "$(translate "Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?")\n\n$(translate "Attention: Removing the subscription banner may cause issues in the web interface after a future update.")\n\n$(translate "If this happens, you can restore the backup from the 'Subscription Banner Removal' option in 'Uninstall optimizations'.")\n\n$(translate "Are you sure you want to continue?")" 14 75; then
|
||||
msg_warn "Banner removal cancelled by user."
|
||||
return 1
|
||||
fi
|
||||
if ! bash "$LOCAL_SCRIPTS/global/remove-banner-pve-v3.sh"; then
|
||||
msg_error "$(translate "Subscription banner removal failed")"
|
||||
return 1
|
||||
fi
|
||||
register_tool "subscription_banner" true "$FUNC_VERSION"
|
||||
return 0
|
||||
fi
|
||||
local pve_version
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
|
||||
@@ -225,6 +248,17 @@ setup_proxmox_repositories() {
|
||||
|
||||
msg_info2 "$(translate "Configuring Proxmox APT repositories...")"
|
||||
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
if ! ensure_pbs_repositories; then
|
||||
msg_error "$(translate "Failed to configure Proxmox Backup Server repositories")"
|
||||
register_tool "proxmox_repos" false "$FUNC_VERSION"
|
||||
return 1
|
||||
fi
|
||||
register_tool "proxmox_repos" true "$FUNC_VERSION"
|
||||
msg_success "$(translate "Proxmox Backup Server APT repositories configured")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! ensure_repositories; then
|
||||
msg_error "$(translate "Failed to configure Proxmox repositories")"
|
||||
register_tool "proxmox_repos" false "$FUNC_VERSION"
|
||||
@@ -524,7 +558,7 @@ force_apt_ipv4() {
|
||||
# ==========================================================
|
||||
|
||||
apply_network_optimizations() {
|
||||
local FUNC_VERSION="1.1"
|
||||
local FUNC_VERSION="1.2"
|
||||
pmx_journal_context "apply_network_optimizations" "$FUNC_VERSION"
|
||||
# description: Tune TCP buffers, somaxconn, IPv4 hardening and disable rp_filter on fw bridges (PVE 9 compatible).
|
||||
msg_info "$(translate "Optimizing network settings...")"
|
||||
@@ -622,14 +656,16 @@ RemainAfterExit=yes
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
pmx_write_file /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules <<'EOF'
|
||||
pmx_remove_file /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
|
||||
|
||||
pmx_write_file /etc/udev/rules.d/99-zz-proxmenux-fwbr-tune.rules <<'EOF'
|
||||
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwbr*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
|
||||
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwln*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
|
||||
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwpr*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
|
||||
ACTION=="add", SUBSYSTEM=="net", KERNEL=="tap*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
|
||||
EOF
|
||||
chmod 0644 /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
|
||||
chown root:root /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
|
||||
chmod 0644 /etc/udev/rules.d/99-zz-proxmenux-fwbr-tune.rules
|
||||
chown root:root /etc/udev/rules.d/99-zz-proxmenux-fwbr-tune.rules
|
||||
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
udevadm control --reload-rules >/dev/null 2>&1 || true
|
||||
|
||||
@@ -53,7 +53,12 @@ initialize_cache
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/platform-functions.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/platform-functions.sh"
|
||||
pmx_require_pve || exit 1
|
||||
fi
|
||||
# On Proxmox Backup Server the menu lists only the options that apply to
|
||||
# it; repositories, upgrade and the subscription notice use its tooling.
|
||||
PMX_ON_PBS=false
|
||||
if declare -F pmx_is_pbs >/dev/null && pmx_is_pbs; then
|
||||
PMX_ON_PBS=true
|
||||
fi
|
||||
|
||||
# Load shared global functions
|
||||
@@ -122,6 +127,17 @@ setup_proxmox_repositories() {
|
||||
|
||||
msg_info2 "$(translate "Configuring Proxmox APT repositories...")"
|
||||
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
if ! ensure_pbs_repositories; then
|
||||
msg_error "$(translate "Failed to configure Proxmox Backup Server repositories")"
|
||||
register_tool "proxmox_repos" false "$FUNC_VERSION"
|
||||
return 1
|
||||
fi
|
||||
register_tool "proxmox_repos" true "$FUNC_VERSION"
|
||||
msg_success "$(translate "Proxmox Backup Server APT repositories configured")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! ensure_repositories; then
|
||||
msg_error "$(translate "Failed to configure Proxmox repositories")"
|
||||
register_tool "proxmox_repos" false "$FUNC_VERSION"
|
||||
@@ -248,6 +264,10 @@ EOF
|
||||
|
||||
|
||||
apt_upgrade() {
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
bash "$LOCAL_SCRIPTS/global/update-pbs.sh"
|
||||
return
|
||||
fi
|
||||
local pve_version
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
|
||||
@@ -2376,6 +2396,14 @@ EOF
|
||||
remove_subscription_banner() {
|
||||
local FUNC_VERSION="1.0"
|
||||
# description: Patch the Proxmox web UI to suppress the "no valid subscription" dialog (PVE 8 + 9 variants supported).
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
if ! bash "$LOCAL_SCRIPTS/global/remove-banner-pve-v3.sh"; then
|
||||
msg_error "$(translate "Subscription banner removal failed")"
|
||||
return 1
|
||||
fi
|
||||
register_tool "subscription_banner" true "$FUNC_VERSION"
|
||||
return 0
|
||||
fi
|
||||
local pve_version
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
|
||||
@@ -3143,8 +3171,11 @@ EOF
|
||||
}
|
||||
|
||||
|
||||
ensure_aliases
|
||||
msg_ok "$(translate "Aliases added to .bashrc")"
|
||||
# The aliases call Proxmox VE tools.
|
||||
if [[ "$PMX_ON_PBS" != true ]]; then
|
||||
ensure_aliases
|
||||
msg_ok "$(translate "Aliases added to .bashrc")"
|
||||
fi
|
||||
|
||||
msg_success "$(translate "Figurine installation and configuration completed successfully.")"
|
||||
register_tool "figurine" true "$FUNC_VERSION"
|
||||
@@ -3993,6 +4024,19 @@ main_menu() {
|
||||
"Optional|Install and configure Log2RAM|LOG2RAM"
|
||||
)
|
||||
|
||||
if [[ "$PMX_ON_PBS" == true ]]; then
|
||||
local -A pve_only=(
|
||||
[GUESTAGENT]=1 [VFIO_IOMMU]=1 [OPENVSWITCH]=1 [VZDUMP]=1 [PVEAM]=1
|
||||
[CEPH]=1 [ENABLE_HA]=1 [OVHRTM]=1 [PIGZ]=1
|
||||
)
|
||||
local pbs_options=() option_key
|
||||
for option in "${options[@]}"; do
|
||||
option_key="${option##*|}"
|
||||
[[ -n "${pve_only[$option_key]:-}" ]] || pbs_options+=("$option")
|
||||
done
|
||||
options=("${pbs_options[@]}")
|
||||
fi
|
||||
|
||||
IFS=$'\n' sorted_options=($(for option in "${options[@]}"; do
|
||||
IFS='|' read -r category description function_name <<< "$option"
|
||||
printf "%d|%s|%s|%s\n" "${category_order[$category]:-999}" "$category" "$description" "$function_name"
|
||||
|
||||
@@ -266,6 +266,12 @@ uninstall_subscription_banner() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Proxmox Backup Server ships the same widget toolkit without the PVE packages.
|
||||
local -a ui_packages=(pve-manager proxmox-widget-toolkit libjs-extjs libpve-http-server-perl)
|
||||
if [[ "$(dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null)" == "install ok installed" ]]; then
|
||||
ui_packages=(proxmox-widget-toolkit libjs-extjs)
|
||||
fi
|
||||
|
||||
if [[ "$restored" == false ]]; then
|
||||
msg_info "$(translate "Performing complete package reinstallation...")"
|
||||
|
||||
@@ -274,7 +280,7 @@ uninstall_subscription_banner() {
|
||||
|
||||
# Reinstall packages with force-confnew to restore original configs
|
||||
if apt-get --reinstall -o Dpkg::Options::="--force-confnew" install \
|
||||
pve-manager proxmox-widget-toolkit libjs-extjs libpve-http-server-perl -y >/dev/null 2>&1; then
|
||||
"${ui_packages[@]}" -y >/dev/null 2>&1; then
|
||||
msg_ok "$(translate "Reinstalled Proxmox packages successfully")"
|
||||
restored=true
|
||||
else
|
||||
@@ -286,7 +292,7 @@ uninstall_subscription_banner() {
|
||||
|
||||
# Second pass reinstallation to ensure everything is clean
|
||||
apt-get update >/dev/null 2>&1
|
||||
apt-get --reinstall install proxmox-widget-toolkit pve-manager libjs-extjs libpve-http-server-perl -y >/dev/null 2>&1 || true
|
||||
apt-get --reinstall install "${ui_packages[@]}" -y >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
msg_info "$(translate "Cleaning cached files...")"
|
||||
|
||||
Reference in New Issue
Block a user