mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 10:22:08 +00:00
Merge develop and preserve Slovak Monitor and CLI messages
This commit is contained in:
@@ -312,7 +312,7 @@ EOFAPT
|
||||
|
||||
# Verify APT hook syntax
|
||||
apt-config dump >/dev/null 2>&1 || {
|
||||
msg_warn "APT hook syntax issue, removing..."
|
||||
msg_warn "$(translate "APT hook syntax issue, removing...")"
|
||||
rm -f "$APT_HOOK"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ repository API responses: they can contain subscription keys or credentials.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import string
|
||||
import sys
|
||||
|
||||
ENDPOINT = '/nodes/localhost/apt/repositories'
|
||||
@@ -26,6 +28,41 @@ PRODUCTS = {
|
||||
'test': {'pbs-no-subscription', 'pbs-test', 'pbstest'},
|
||||
'ui': 'Administration > Repositories', 'ceph': False},
|
||||
}
|
||||
BASE = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def N_(text):
|
||||
"""Mark a CLI message without changing the policy's English exception."""
|
||||
return text
|
||||
|
||||
|
||||
class PolicyError(ValueError):
|
||||
def __init__(self, template, **params):
|
||||
self.template = template
|
||||
self.params = params
|
||||
super().__init__(template.format(**params))
|
||||
|
||||
|
||||
def cli_error(error, product):
|
||||
"""Localize only PBS CLI diagnostics; policy callers retain English errors."""
|
||||
if product != 'pbs' or not isinstance(error, PolicyError):
|
||||
return str(error)
|
||||
try:
|
||||
language = json.loads((BASE / 'config.json').read_text()).get('language', 'en')
|
||||
if language not in ('sk', 'de', 'es', 'fr', 'it', 'pt', 'sv'):
|
||||
return str(error)
|
||||
catalog = json.loads((BASE / 'lang' / f'{language}.json').read_text())
|
||||
message = catalog.get(error.template)
|
||||
if isinstance(message, str) and message.strip():
|
||||
fields = lambda text: {name for _, name, _, _ in string.Formatter().parse(text) if name is not None}
|
||||
if fields(message) != fields(error.template) or any(
|
||||
spec or conversion for _, _, spec, conversion in string.Formatter().parse(message)
|
||||
):
|
||||
return str(error)
|
||||
return message.format(**error.params)
|
||||
except (OSError, ValueError, KeyError, IndexError, TypeError, AttributeError):
|
||||
pass
|
||||
return str(error)
|
||||
|
||||
|
||||
def run(args):
|
||||
@@ -43,7 +80,7 @@ def subscription_status(product='pve'):
|
||||
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.')
|
||||
raise PolicyError(N_('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.
|
||||
@@ -110,12 +147,12 @@ def repository_state(suite, product='pve'):
|
||||
entries.append((file['path'], index, row))
|
||||
return data, entries
|
||||
except (KeyError, TypeError, ValueError, IndexError) as exc:
|
||||
raise ValueError(f"Proxmox APT repository inventory is invalid or reports parse errors. Fix sources in {p['ui']}; no repository changed.") from exc
|
||||
raise PolicyError(N_('Proxmox APT repository inventory is invalid or reports parse errors. Fix sources in {ui}; no repository changed.'), ui=p['ui']) from exc
|
||||
|
||||
|
||||
def evaluate(suite, apply=False, product='pve'):
|
||||
if suite not in ('bookworm', 'trixie'):
|
||||
raise ValueError('Unsupported Proxmox suite; no repository changed.')
|
||||
raise PolicyError(N_('Unsupported Proxmox suite; no repository changed.'))
|
||||
p = PRODUCTS[product]
|
||||
data, entries = repository_state(suite, product)
|
||||
pve = []
|
||||
@@ -139,8 +176,8 @@ def evaluate(suite, apply=False, product='pve'):
|
||||
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 {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.")
|
||||
raise PolicyError(N_('This host runs {name} on {suite}, but these {name} repositories are for another suite: {details}. Disable or remove them in {ui} and run the update again; no repository changed.'),
|
||||
name=p['name'], suite=suite, details='; '.join(wrong_pve), ui=p['ui'])
|
||||
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.")
|
||||
@@ -152,20 +189,20 @@ def evaluate(suite, apply=False, product='pve'):
|
||||
status = subscription_status(product) # fail closed before any write
|
||||
if status == 'active':
|
||||
if not pve:
|
||||
raise ValueError(f"Host has an active subscription but no active {p['short']} repository. Configure its Enterprise source in {p['ui']}; no repository changed.")
|
||||
raise PolicyError(N_('Host has an active subscription but no active {short} repository. Configure its Enterprise source in {ui}; no repository changed.'), short=p['short'], ui=p['ui'])
|
||||
return 'preserve'
|
||||
if not debian:
|
||||
raise ValueError('No active Debian base repository for this suite; configure it in the Proxmox repository UI before continuing.')
|
||||
raise PolicyError(N_('No active Debian base repository for this suite; configure it in the Proxmox repository UI before continuing.'))
|
||||
# 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 p['enterprise'] in item[2]['Components']] + ceph
|
||||
for _, _, row in disable:
|
||||
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.')
|
||||
raise PolicyError(N_('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']
|
||||
if len(handles) != 1:
|
||||
raise ValueError('Proxmox no-subscription standard repository handle unavailable; no repository changed.')
|
||||
raise PolicyError(N_('Proxmox no-subscription standard repository handle unavailable; no repository changed.'))
|
||||
if not apply:
|
||||
return 'offer'
|
||||
# Adopt a refreshed digest only after checking the complete expected
|
||||
@@ -192,11 +229,12 @@ def evaluate(suite, apply=False, product='pve'):
|
||||
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(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
|
||||
raise PolicyError(N_('Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.'), ui=p['ui']) from exc
|
||||
return 'changed'
|
||||
|
||||
|
||||
def main():
|
||||
product = 'pve'
|
||||
try:
|
||||
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):
|
||||
@@ -204,7 +242,7 @@ def main():
|
||||
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)
|
||||
print(cli_error(exc, product), file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ update_pbs() {
|
||||
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")"
|
||||
msg_error "$(translate "Insufficient disk space. Available:") ${available_space}MB"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -149,7 +149,7 @@ update_pbs() {
|
||||
rm -f "$screen_capture"
|
||||
|
||||
if [ $upgrade_exit_code -ne 0 ]; then
|
||||
msg_error "$(translate "System upgrade failed. Check log: $log_file")"
|
||||
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
|
||||
|
||||
@@ -63,13 +63,13 @@ confirm_automated_script() {
|
||||
|
||||
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"
|
||||
script_info+="• $(translate "Upgrade the system, offering the") \Z4$(translate "no-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 "Optionally remove") \Z4$(translate "subscription banner")\Z0 $(translate "from Proxmox web interface (you will be asked)")\n"
|
||||
script_info+="• $(translate "Optimize") \Z4$(translate "memory")\Z0, \Z4$(translate "kernel")\Z0, $(translate "and") \Z4$(translate "network")\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"
|
||||
script_info+="• $(translate "Enable") \Z4ZFS autotrim\Z0 $(translate "on SSD/NVMe pools that support discard")\n"
|
||||
script_info+="• $(translate "Improve log rotation and limit log size to save space and extend disk life")\n"
|
||||
@@ -289,10 +289,10 @@ post_install_label() {
|
||||
echo "Proxmox VE Microcode"
|
||||
;;
|
||||
"Proxmox Backup Server Post Install")
|
||||
echo "Proxmox Backup Server Post Install"
|
||||
translate "Proxmox Backup Server Post Install"
|
||||
;;
|
||||
"Proxmox Backup Server Microcode")
|
||||
echo "Proxmox Backup Server Microcode"
|
||||
translate "Proxmox Backup Server Microcode"
|
||||
;;
|
||||
"Community Scripts")
|
||||
translate "Community Scripts"
|
||||
|
||||
@@ -182,9 +182,9 @@ remove_subscription_banner() {
|
||||
# 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" \
|
||||
if ! whiptail --title "$(translate "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."
|
||||
msg_warn "$(translate "Banner removal cancelled by user.")"
|
||||
return 1
|
||||
fi
|
||||
if ! bash "$LOCAL_SCRIPTS/global/remove-banner-pve-v3.sh"; then
|
||||
|
||||
@@ -2574,7 +2574,7 @@ configure_zram_swap() {
|
||||
fi
|
||||
version="$(dpkg-query -W -f='${Version}' systemd-zram-generator 2>/dev/null)"
|
||||
if [[ -z "$version" ]]; then
|
||||
failure="Cannot determine installed systemd-zram-generator version"
|
||||
failure="$(translate "Cannot determine installed systemd-zram-generator version")"
|
||||
else
|
||||
if dpkg --compare-versions "$version" ge '1.2.0'; then
|
||||
supports_params=true
|
||||
@@ -2588,7 +2588,7 @@ configure_zram_swap() {
|
||||
msg_success "$(translate "ZRAM profile is already active")"
|
||||
return 0
|
||||
fi
|
||||
failure="Existing ZRAM profile is active but does not match its configuration"
|
||||
failure="$(translate "Existing ZRAM profile is active but does not match its configuration")"
|
||||
fi
|
||||
if [[ -z "$failure" && "$previous_active" == true ]]; then
|
||||
if ! swapoff /dev/zram0; then
|
||||
@@ -2597,31 +2597,31 @@ configure_zram_swap() {
|
||||
fi
|
||||
state_mutated=true
|
||||
if ! systemctl stop dev-zram0.swap >/dev/null 2>&1; then
|
||||
failure="Could not stop existing dev-zram0.swap"
|
||||
failure="$(translate "Could not stop existing dev-zram0.swap")"
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$failure" ]]; then
|
||||
state_mutated=true
|
||||
if ! install -d -m 755 "$(dirname "$config")"; then
|
||||
failure="Could not create ZRAM generator directory"
|
||||
failure="$(translate "Could not create ZRAM generator directory")"
|
||||
elif ! printf '%s\n' "$desired" | pmx_write_file "$config"; then
|
||||
failure="Could not write ZRAM generator configuration"
|
||||
failure="$(translate "Could not write ZRAM generator configuration")"
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$failure" ]]; then
|
||||
old_umask="$(umask)"
|
||||
umask 077
|
||||
if ! printf 'installed_by_proxmenux=%s\n' "$package_owned" | pmx_write_file "$marker"; then
|
||||
failure="Could not write ZRAM package ownership state"
|
||||
failure="$(translate "Could not write ZRAM package ownership state")"
|
||||
fi
|
||||
umask "$old_umask"
|
||||
[[ -f "$marker" ]] && chmod 600 "$marker"
|
||||
fi
|
||||
if [[ -z "$failure" ]]; then
|
||||
if ! systemctl daemon-reload || ! systemctl start dev-zram0.swap; then
|
||||
failure="Could not activate dev-zram0.swap"
|
||||
failure="$(translate "Could not activate dev-zram0.swap")"
|
||||
elif ! zram_profile_verify_active "$algorithm"; then
|
||||
failure="Active ZRAM algorithm, capacity or swap sources did not match the profile"
|
||||
failure="$(translate "Active ZRAM algorithm, capacity or swap sources did not match the profile")"
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$failure" ]]; then
|
||||
|
||||
@@ -1356,7 +1356,7 @@ show_uninstall_menu() {
|
||||
system_limits) desc="System Limits Increase";;
|
||||
entropy) desc="Entropy Generation (haveged)";;
|
||||
memory_settings) desc="Memory Settings Optimization";;
|
||||
zram_swap) desc="ZRAM swap profile";;
|
||||
zram_swap) desc="$(translate "ZRAM swap profile")";;
|
||||
kernel_panic) desc="Kernel Panic Configuration";;
|
||||
apt_ipv4) desc="APT IPv4 Force";;
|
||||
kexec) desc="kexec for quick reboots";;
|
||||
|
||||
Reference in New Issue
Block a user