Merge branch 'develop' into develop

This commit is contained in:
Detlef Schneider
2026-10-16 07:02:38 +02:00
committed by GitHub
51 changed files with 1854 additions and 225 deletions
@@ -212,6 +212,12 @@ class CommandDescriptionsTests(unittest.TestCase):
# New LXC observation labels intentionally ship only EN/SK.
# Model the bot's future output in temporary catalogs, just
# like the other pending keys above; never rewrite locales.
messages["scriptTerminal"].setdefault("endOfInput", english["scriptTerminal"]["endOfInput"])
for key in ("noShell", "enterFailed"):
messages["lxcTerminal"]["errors"].setdefault(key, english["lxcTerminal"]["errors"][key])
# The notification history panel texts await the bot as well.
for key, value in english["settings"]["notifications"]["history"].items():
messages["settings"]["notifications"].setdefault("history", {}).setdefault(key, value)
for key in ("checkNow", "checkReadOnlyHelp", "checkCooldown",
"checkStale", "checkStaleHelp", "checkBeforeUpdate",
"startBeforeCheck", "notCheckedYet",
@@ -0,0 +1,49 @@
"""The notification settings show the delivery history the Monitor records,
the daily digest runs included, and never offer to erase it: Audit & Report
reads the same history to tell whether each channel delivers."""
import json
from pathlib import Path
import re
import unittest
ROOT = Path(__file__).resolve().parents[3]
PAGE = ROOT / 'AppImage/components/notification-settings.tsx'
KEYS = ('title', 'description', 'empty', 'loadFailed', 'sent', 'notSent',
'severityInfo', 'severityWarning', 'severityCritical')
class NotificationHistoryPanelTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.page = PAGE.read_text(encoding='utf-8')
def test_the_panel_opens_and_lists_the_history(self):
self.assertIn('setShowHistory(open => !open)', self.page)
self.assertIn('history.map(entry =>', self.page)
self.assertIn('t("settings.notifications.history.title")', self.page)
def test_the_history_is_read_without_a_success_flag(self):
loader = re.search(r'const loadHistory = useCallback\(async \(\) => \{(.*?)\n \}, \[\]\)', self.page, re.S)
self.assertIsNotNone(loader)
self.assertIn('Array.isArray(data.history)', loader.group(1))
self.assertNotIn('data.success', loader.group(1))
def test_a_digest_run_and_a_failed_delivery_show_their_message(self):
self.assertIn('entry.event_type === "digest" ? entry.message', self.page)
self.assertIn('!sent ? entry.error_message', self.page)
def test_the_page_never_erases_the_history(self):
self.assertNotIn('handleClearHistory', self.page)
self.assertIsNone(re.search(r'notifications/history["\'`][^)]*method:\s*["\']DELETE', self.page))
def test_the_texts_exist_in_english_and_spanish(self):
for language in ('en', 'es'):
catalog = json.loads((ROOT / f'AppImage/messages/{language}/common.json').read_text(encoding='utf-8'))
history = catalog['settings']['notifications']['history']
for key in KEYS:
with self.subTest(language=language, key=key):
self.assertTrue(history.get(key))
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,46 @@
"""OCI manager Apps opens only on Proxmox VE 9.1 or later, the first release
that runs OCI images as LXC containers."""
from pathlib import Path
import re
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[3]
LAUNCHER = ROOT / 'scripts/oci/oci_manager_apps.sh'
class OciPveVersionGateTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
text = LAUNCHER.read_text(encoding='utf-8')
cls.function = re.search(r'^pve_supports_oci\(\) \{\n.*?^\}\n', text, re.M | re.S).group(0)
cls.text = text
def supported(self, version):
with tempfile.TemporaryDirectory() as folder:
fake = Path(folder, 'pveversion')
# Without Proxmox the command is missing: a stand-in that fails hides the host's own.
fake.write_text('#!/bin/sh\nexit 127\n' if version is None else
f'#!/bin/sh\necho "pve-manager/{version}/b9984c6d90a4bd80 (running kernel: 6.17)"\n')
fake.chmod(0o755)
result = subprocess.run(['bash', '-c', f'PATH={folder}:/usr/bin:/bin\n{self.function}pve_supports_oci'],
capture_output=True, text=True)
return result.returncode == 0
def test_versions_from_9_1_open_it(self):
for version in ('9.1.0', '9.1.4', '9.2.2', '10.0.1'):
with self.subTest(version=version):
self.assertTrue(self.supported(version))
def test_older_versions_and_hosts_without_proxmox_do_not(self):
for version in ('8.4.1', '9.0.11', None):
with self.subTest(version=version):
self.assertFalse(self.supported(version))
def test_it_is_checked_before_anything_is_installed(self):
self.assertLess(self.text.index('if ! pve_supports_oci; then'), self.text.index('ensure_oci_dependencies || exit 1'))
if __name__ == '__main__':
unittest.main()
@@ -7,7 +7,7 @@ from pathlib import Path
import subprocess
from types import SimpleNamespace
from unittest import TestCase
from unittest.mock import Mock
from unittest.mock import Mock, patch
ROOT = Path(__file__).resolve().parents[3]
MENU = ROOT / 'oci/src/proxmenux_oci/management.py'
@@ -48,6 +48,42 @@ class SelectionSetupWording(TestCase):
ui.message.assert_called_once_with(EMPTY, 'OCI management')
ui.choose.assert_not_called()
def _menu(self, inventories, selections):
rows = iter(inventories)
ui = SimpleNamespace(message=Mock(), choose=Mock(side_effect=selections))
refresh = Mock(return_value={})
manage = Mock(return_value=True)
fn = extracted('_interactive_management', os=SimpleNamespace(geteuid=lambda: 0),
shutil=SimpleNamespace(which=lambda _: '/fake/pct'), saved_inventory=lambda _: next(rows),
_clean_orphans=lambda project: None,
recover_automatically=lambda project: None, offer_recovery=lambda project, ui: None,
carry_records=refresh, manage_instance=manage, menu_size=lambda: 10,
STATUS_LABELS={}, translate=lambda s: s)
fn(Path('/fixture'), ui)
return ui, refresh, manage
def test_the_list_comes_back_after_an_operation_without_refreshing_every_copy(self):
row = {'vmid': 101, 'title': 'Jellyfin', 'hostname': 'jellyfin', 'status': 'installed', 'image': 'jellyfin/jellyfin'}
ui, refresh, manage = self._menu([[row], [row]], ['101', None])
self.assertEqual(ui.choose.call_count, 2)
manage.assert_called_once()
refresh.assert_called_once_with(Path('/fixture'), mount_stopped=False, shown=True)
def test_removing_the_last_application_returns_without_a_message(self):
row = {'vmid': 101, 'title': 'Jellyfin', 'hostname': 'jellyfin', 'status': 'installed', 'image': 'jellyfin/jellyfin'}
ui, _, manage = self._menu([[row], []], ['101'])
manage.assert_called_once()
ui.message.assert_not_called()
def test_the_watchdog_refreshes_the_copy_of_the_containers_it_changed(self):
refresh = Mock(return_value={})
watchdog = SimpleNamespace(set_watchdog=Mock(side_effect=[[101, 102]]))
fn = extracted('set_watchdog', sys=SimpleNamespace(path=[]), carry_records=refresh,
instances=SimpleNamespace(ROOT=Path('/fixture')))
with patch.dict('sys.modules', {'oci_watchdog': watchdog}):
self.assertTrue(fn(Path('/fixture'), [102], True))
refresh.assert_called_once_with(Path('/fixture'), mount_stopped=False, vmids=[101, 102])
def test_selector_filters_missing_guest_without_erasing_saved_contract(self):
class Entry:
name = '101'
@@ -0,0 +1,31 @@
"""Every web terminal of the Monitor can send Ctrl+D from its Ctrl menu, so
a pasted text can be finished from a phone or a tablet."""
import json
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[3]
TERMINALS = {
'terminal-panel': 'sendSequence',
'script-terminal-modal': 'sendCommand',
'lxc-terminal-modal': 'sendKey',
}
class TerminalCtrlKeysTests(unittest.TestCase):
def test_each_terminal_sends_ctrl_d(self):
for name, send in TERMINALS.items():
source = (ROOT / f'AppImage/components/{name}.tsx').read_text(encoding='utf-8')
with self.subTest(terminal=name):
self.assertIn(f'onSelect={{() => {send}("\\x04")}}', source)
self.assertIn('t("scriptTerminal.endOfInput")', source)
def test_the_label_exists_in_english_and_spanish(self):
for language in ('en', 'es'):
catalog = json.loads((ROOT / f'AppImage/messages/{language}/common.json').read_text(encoding='utf-8'))
with self.subTest(language=language):
self.assertTrue(catalog['scriptTerminal'].get('endOfInput'))
if __name__ == '__main__':
unittest.main()
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
90a46ab219d193ee635165ae1761f1c3806f0f0be2fc4434907ea6e8d2c86414 ProxMenux-1.2.6.2-beta.AppImage
31d7d4a566959150fbe4846cf7dd354f26df342a50f76ad2066ac6b6f9f0f5d6 ProxMenux-1.2.6.2-beta.AppImage
@@ -382,6 +382,19 @@ export function LxcTerminalModal({
return
}
// The host prompt came back: pct enter could not enter the
// container. Show why and close, so the session is never left
// on the host shell under the container's name.
const reason = afterPctEnter.split(/\r?\n/).slice(0, -1)
.map((line) => line.trim()).filter(Boolean).join("\r\n")
if (reason) term.writeln(`\x1b[31m${reason}\x1b[0m`)
term.writeln(`\r\n\x1b[33m${/Failed to exec/.test(reason)
? t("lxcTerminal.errors.noShell")
: t("lxcTerminal.errors.enterFailed")}\x1b[0m`)
isInsideLxcRef.current = true
ws.close()
return
}
}
} else {
@@ -688,6 +701,10 @@ export function LxcTerminalModal({
<span className="font-mono text-xs mr-2">Ctrl+C</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.cancelInterrupt")}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendKey("\x04")}>
<span className="font-mono text-xs mr-2">Ctrl+D</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.endOfInput")}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendKey("\x18")}>
<span className="font-mono text-xs mr-2">Ctrl+X</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.exitNano")}</span>
+79 -17
View File
@@ -18,7 +18,7 @@ import {
Trash2, ChevronRight, TestTube2, Mail, Webhook,
Copy, Server, Shield, ExternalLink, RefreshCw, Download, Upload,
Cloud, Brain, Globe, MessageSquareText, Sparkles, Pencil, Save, RotateCcw, Lightbulb,
Moon, Newspaper
Moon, Newspaper, History, ChevronDown
} from "lucide-react"
interface ChannelConfig {
@@ -109,7 +109,8 @@ interface HistoryEntry {
title: string
severity: string
sent_at: string
success: boolean
success: boolean | number
message?: string | null
error_message: string | null
}
@@ -331,7 +332,8 @@ export function NotificationSettings() {
const { resolvedTheme } = useTheme()
const [config, setConfig] = useState<NotificationConfig>(DEFAULT_CONFIG)
const [status, setStatus] = useState<ServiceStatus | null>(null)
const [history, setHistory] = useState<HistoryEntry[]>([])
const [history, setHistory] = useState<HistoryEntry[] | null>(null)
const [historyError, setHistoryError] = useState(false)
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [saved, setSaved] = useState(false)
@@ -452,12 +454,12 @@ export function NotificationSettings() {
const loadHistory = useCallback(async () => {
try {
const data = await fetchApi<{ success: boolean; history: HistoryEntry[]; total: number }>("/api/notifications/history?limit=20")
if (data.success) {
setHistory(data.history || [])
}
const data = await fetchApi<{ history?: HistoryEntry[]; error?: string }>("/api/notifications/history?limit=20")
if (data.error || !Array.isArray(data.history)) throw new Error("history unavailable")
setHistory(data.history)
setHistoryError(false)
} catch {
// Ignore
setHistoryError(true)
}
}, [])
@@ -1097,15 +1099,6 @@ export function NotificationSettings() {
}
}
const handleClearHistory = async () => {
try {
await fetchApi("/api/notifications/history", { method: "DELETE" })
setHistory([])
} catch {
// Ignore
}
}
// Maps each eye-button local key to the body shape the backend
// expects for /api/notifications/reveal-secret. Centralised here so
// the JSX call site stays a one-liner — `toggleSecret(key)`.
@@ -2761,6 +2754,75 @@ export function NotificationSettings() {
)}
</div>
{/* ── Notification history ── */}
<div className="space-y-3">
<button
type="button"
className="w-full flex items-start gap-3 py-1 text-left"
onClick={() => setShowHistory(open => !open)}
aria-expanded={showHistory}
>
<History className="h-5 w-5 text-blue-400 mt-0.5 shrink-0" />
<div className="flex-1 min-w-0">
<span className="text-sm font-medium">{t("settings.notifications.history.title")}</span>
<p className="text-xs sm:text-sm text-muted-foreground">{t("settings.notifications.history.description")}</p>
</div>
<ChevronDown className={`h-4 w-4 mt-1 shrink-0 text-muted-foreground transition-transform ${showHistory ? "rotate-180" : ""}`} />
</button>
{showHistory && (
historyError ? (
<p className="text-xs sm:text-sm text-red-400">{t("settings.notifications.history.loadFailed")}</p>
) : history === null ? (
<div className="flex justify-center py-3"><Loader2 className="h-4 w-4 animate-spin text-muted-foreground" /></div>
) : history.length === 0 ? (
<p className="text-xs sm:text-sm text-muted-foreground">{t("settings.notifications.history.empty")}</p>
) : (
<div className="space-y-1.5 max-h-80 overflow-y-auto pr-1">
{history.map(entry => {
const sent = Boolean(entry.success)
const severity = (entry.severity || "").toLowerCase()
const detail = !sent ? entry.error_message : entry.event_type === "digest" ? entry.message : null
return (
<div key={entry.id} className="flex items-start gap-2.5 p-2.5 rounded-md bg-muted/30 border border-border/50">
{sent ? (
<CheckCircle2 className="h-4 w-4 text-green-500 shrink-0 mt-0.5" aria-label={t("settings.notifications.history.sent")} />
) : (
<XCircle className="h-4 w-4 text-red-500 shrink-0 mt-0.5" aria-label={t("settings.notifications.history.notSent")} />
)}
<div className="flex-1 min-w-0">
<span className="text-xs sm:text-sm font-medium block truncate">{entry.title || entry.event_type}</span>
<span className="text-[11px] sm:text-xs text-muted-foreground">
{entry.channel ? entry.channel.charAt(0).toUpperCase() + entry.channel.slice(1) : ""} · {new Date(entry.sent_at).toLocaleString()}
</span>
{detail && (
<p className={`text-[11px] sm:text-xs mt-0.5 break-words ${sent ? "text-muted-foreground" : "text-red-400"}`}>{detail}</p>
)}
</div>
{(severity === "critical" || severity === "warning" || severity === "info") && (
<Badge
variant="outline"
className={`text-[10px] shrink-0 ${
severity === "critical"
? "border-red-500/30 text-red-400"
: severity === "warning"
? "border-amber-500/30 text-amber-400"
: "border-blue-500/30 text-blue-400"
}`}
>
{severity === "critical"
? t("settings.notifications.history.severityCritical")
: severity === "warning"
? t("settings.notifications.history.severityWarning")
: t("settings.notifications.history.severityInfo")}
</Badge>
)}
</div>
)
})}
</div>
)
)}
</div>
</>
)}
+1 -1
View File
@@ -307,7 +307,7 @@ const CURRENT_VERSION_FEATURES = [
{
icon: <Package className="h-5 w-5" />,
key: "releaseNotes.currentFeatures.ociManager",
text: "OCI manager Apps (beta) — a new entry in the ProxMenux menu installs applications from their official container images as native Proxmox LXC, with no Docker engine. The catalog carries over 360 applications.",
text: "OCI manager Apps (Technology Preview) — a new entry in the ProxMenux menu installs applications from their official container images as native Proxmox LXC, with no Docker engine. The catalog carries over 360 applications.",
},
{
icon: <ScrollText className="h-5 w-5" />,
@@ -918,6 +918,10 @@ const initMessage = {
<span className="font-mono text-xs mr-2">Ctrl+C</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.cancelInterrupt")}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendCommand("\x04")}>
<span className="font-mono text-xs mr-2">Ctrl+D</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.endOfInput")}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendCommand("\x18")}>
<span className="font-mono text-xs mr-2">Ctrl+X</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.exitNano")}</span>
+4
View File
@@ -1038,6 +1038,10 @@ const handleClose = () => {
<span className="font-mono text-xs mr-2">Ctrl+C</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.cancelInterrupt")}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendSequence("\x04")}>
<span className="font-mono text-xs mr-2">Ctrl+D</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.endOfInput")}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendSequence("\x18")}>
<span className="font-mono text-xs mr-2">Ctrl+X</span>
<span className="text-muted-foreground text-xs">{t("scriptTerminal.exitNano")}</span>
+40 -26
View File
@@ -90,8 +90,8 @@
"cpuPercent": "CPU %",
"loadAverage": "Ø Auslastung",
"loadAxis": "Laden",
"memory": "Speicher",
"memoryUsage": "Speichernutzung",
"memory": "Arbeitsspeicher",
"memoryUsage": "Arbeitsspeichernutzung",
"user": "Benutzer",
"system": "System",
"cores": "Kerne",
@@ -857,7 +857,7 @@
"user": "Benutzer",
"command": "Befehl",
"cpuPercent": "CPU %",
"memory": "Speicher",
"memory": "Arbeitsspeicher",
"memPercent": "Speicher %",
"noMatches": "Keine Prozesse passen zum Filter",
"captured": "Erfasst {time}",
@@ -892,7 +892,7 @@
"user": "Benutzer",
"group": "Gruppe",
"cpu": "CPU",
"memory": "Speicher",
"memory": "Arbeitsspeicher",
"residentRss": "Bewohner (RSS)",
"virtualSize": "Virtuelle Größe",
"swap": "Swap",
@@ -940,10 +940,10 @@
"empty": "Keine virtuellen Maschinen gefunden",
"uptime": "Betriebszeit: {uptime}",
"cpuUsage": "CPU-Auslastung",
"memory": "Speicher",
"disk": "Laufwerk",
"diskUsage": "Laufwerksnutzung",
"diskIo": "Laufwerks-E/A",
"memory": "Arbeitsspeicher",
"disk": "Scheibe",
"diskUsage": "Festplattennutzung",
"diskIo": "Festplatten-E/A",
"networkIo": "Netzwerk-E/A",
"sinceBoot": "seit dem Booten",
"loadingConfiguration": "Konfiguration wird geladen...",
@@ -1075,15 +1075,15 @@
"logs": "Logs"
},
"actions": {
"start": "Start",
"shutdown": "Runterfahren",
"start": "Starten",
"shutdown": "Herunterfahren",
"reboot": "Neustart",
"forceStop": "Stopp erzwingen",
"working": "Arbeiten...",
"startPending": "Started...",
"shutdownPending": "Herunterfahren...",
"rebootPending": "Neustart...",
"stopPending": "Stoppen..."
"working": "Wird ausgeführt...",
"startPending": "Wird gestartet...",
"shutdownPending": "Wird heruntergefahren...",
"rebootPending": "Wird neu gestartet...",
"stopPending": "Wird gestoppt..."
},
"confirm": {
"forceStopTitle": "Stopp erzwingen",
@@ -1493,9 +1493,9 @@
"saveFailed": "Die Sammelaktualisierungskonfiguration konnte nicht gespeichert werden.",
"deleteFailed": "Die Sammelaktualisierungskonfiguration konnte nicht entfernt werden.",
"planFailed": "Die Sammelaktualisierung konnte nicht vorbereitet werden. Prüfen Sie die nicht verfügbaren Methoden.",
"loadFailed": "Die Massenaktualisierungs-Konfiguration konnte nicht geladen werden.",
"osDockerScope": "Betriebssystem-Aktualisierungen enthalten auch Docker, wenn es als Systempaket installiert wurde.",
"unverifiedTargetsHelp": "Die Verfügbarkeit von Updates für einige ausgewählte Artikel ist nicht bestätigt. Beim Starten werden die ausgewählten Update-Methoden auch ohne bestätigte neue Version ausgeführt.",
"loadFailed": "Die Massenaktualisierungskonfiguration konnte nicht geladen werden.",
"osDockerScope": "Betriebssystem-Updates umfassen auch Docker, wenn es als Systempaket installiert ist.",
"unverifiedTargetsHelp": "Für einige ausgewählte Elemente ist nicht bestätigt, ob ein Update verfügbar ist. Beim Start werden die gewählten Update-Methoden auch ohne bestätigte neue Version ausgeführt.",
"dockerImageMethod": "Docker-Image"
},
"appEditor": {
@@ -2211,6 +2211,17 @@
"gettingStartedDescription": "Eine Beispielaufforderung steht bereit, um Ihnen den Einstieg zu erleichtern. Sie können es anpassen, exportieren, um es mit anderen zu teilen, oder eine Eingabeaufforderung aus der Community importieren.",
"loadExample": "Beispiel laden",
"startFromScratch": "Fangen Sie bei Null an"
},
"history": {
"title": "Benachrichtigungsverlauf",
"description": "Die letzten 20 gesendeten Benachrichtigungen, einschließlich jeder Ausführung des täglichen Digests, mit dem jeweiligen Ergebnis.",
"empty": "Es wurden noch keine Benachrichtigungen gesendet.",
"loadFailed": "Der Benachrichtigungsverlauf konnte nicht geladen werden.",
"sent": "Gesendet",
"notSent": "Nicht gesendet",
"severityInfo": "Info",
"severityWarning": "Warnung",
"severityCritical": "Kritisch"
}
},
"optimizations": {
@@ -3422,7 +3433,7 @@
"webhookHttps": "Proxmox-Benachrichtigungen erreichen den Monitor auch mit aktiviertem HTTPS — sie werden über eine nur lokale Adresse zugestellt und scheitern nicht mehr an einem Zertifikatsfehler.",
"persistentLogs": "Eine beendete Häufung von Log-Fehlern wird nicht mehr als dauerhaft gemeldet: Ein Muster muss 15 Minuten lang weiter auftreten, und seine Warnung schließt sich von selbst (gemeldet von @Joshua1264).",
"mountsLanAddress": "Mountpunkte auf LVM-thin und anderem Blockspeicher zeigen ihre Belegung, und Multi-Container-Anwendungen öffnen sich unter ihrer LAN-Adresse.",
"ociManager": "OCI manager Apps (Beta) – ein neuer Eintrag im ProxMenux-Menü installiert Anwendungen aus ihren offiziellen Container-Images als native Proxmox-LXC, ohne Docker-Engine. Der Katalog umfasst über 360 Anwendungen.",
"ociManager": "OCI manager Apps (Technology Preview) – ein neuer Eintrag im ProxMenux-Menü installiert Anwendungen aus ihren offiziellen Container-Images als native Proxmox-LXC, ohne Docker-Engine. Der Katalog umfasst über 360 Anwendungen.",
"ociInMonitor": "OCI-Anwendungen im Monitor – die App-Registerkarte erkennt sie, die Updates-Registerkarte aktualisiert und ändert sie wie das Menü, und eine Logs-Registerkarte verfolgt die Konsolenausgabe der Anwendung live.",
"ociAcceleration": "Hardwarebeschleunigung für OCI-Anwendungen – das Installationsprogramm bietet nur die Profile an, die der Host ausführen kann: NVIDIA-, Intel- und AMD-GPUs und für Frigate eine Intel-NPU oder eine Coral. Das NVIDIA Container Toolkit wird mit dem Treiber installiert.",
"ociRecovery": "Eine OCI-Anwendung reist mit ihrem Backup – auf diesem oder einem anderen Host wiederhergestellt, wird sie erneut registriert und verwaltet, und in einem Cluster startet sie auf dem Knoten, auf den sie verschoben wird.",
@@ -5004,7 +5015,9 @@
"lxcTerminal": {
"title": "Terminal: {name} (ID: {id})",
"errors": {
"invalidVmid": "Ungültige VMID – Ausführung von pct enter wird abgelehnt"
"invalidVmid": "Ungültige VMID – Ausführung von pct enter wird abgelehnt",
"noShell": "Das Image dieses Containers hat keine Shell und kann daher kein Terminal öffnen. Die Ausgabe steht auf der Registerkarte „Logs“.",
"enterFailed": "In den Container konnte nicht gewechselt werden."
},
"commands": {
"listFiles": "Listen Sie alle Dateien mit Details auf",
@@ -5237,7 +5250,8 @@
"yes": "Ja",
"yourInput": "Ihr Beitrag:",
"submit": "Einreichen",
"ok": "OK"
"ok": "OK",
"endOfInput": "Ende der Eingabe"
},
"apps": {
"searchPlaceholder": "Nach App or CT suchen…",
@@ -6135,7 +6149,7 @@
"board": "Board",
"processor": "Prozessor",
"topology": "Sockel × Kerne / Threads",
"memory": "Speicher",
"memory": "Arbeitsspeicher",
"cores": "Kerne",
"threads": "Threads",
"memoryModules": "Speichermodule",
@@ -7071,7 +7085,7 @@
"vm_ct": "VM a CT",
"backup": "Backup",
"resources": "Ressourcen",
"storage": "Lagerung",
"storage": "Speicher",
"network": "Netzwerk",
"security": "Sicherheit",
"cluster": "Cluster",
@@ -7120,7 +7134,7 @@
"vm_ct": "Virtuelle Maschine / Container",
"backup": "Backup & Snapshot",
"resources": "Systemressourcen",
"storage": "Lagerung",
"storage": "Speicher",
"network": "Netzwerk",
"security": "Sicherheit",
"cluster": "Cluster",
@@ -7137,7 +7151,7 @@
"action": "Aktion",
"targetNode": "Zielknoten",
"reason": "Grund",
"storage": "Lagerung",
"storage": "Speicher",
"status": "Status",
"size": "Größe",
"duration": "Dauer",
@@ -7146,7 +7160,7 @@
"currentValue": "Aktueller Wert",
"threshold": "Schwelle",
"cpuCores": "CPU-Kerne",
"memory": "Erinnerung",
"memory": "Arbeitsspeicher",
"temperature": "Temperatur",
"mountPoint": "Mount Point",
"usage": "Verwendung",
+16 -2
View File
@@ -2194,6 +2194,17 @@
"localPveApi": "The address cannot point to the local PVE API (localhost:8006/8007).",
"metadataIp": "The link-local metadata IP cannot be used as a Gotify endpoint."
},
"history": {
"title": "Notification history",
"description": "The last 20 notifications sent, including each run of the daily digest, with the result of each one.",
"empty": "No notifications have been sent yet.",
"loadFailed": "The notification history could not be loaded.",
"sent": "Sent",
"notSent": "Not sent",
"severityInfo": "Info",
"severityWarning": "Warning",
"severityCritical": "Critical"
},
"customPromptInfo": {
"title": "Custom prompt mode",
"description": "Create your own AI prompt for ProxMenux Monitor notifications",
@@ -3421,7 +3432,7 @@
"webhookHttps": "Proxmox notifications reach the Monitor with HTTPS enabled — they are delivered on a local-only address and no longer fail with a certificate error.",
"persistentLogs": "A burst of log errors that ended is no longer reported as persistent: a pattern has to keep appearing for 15 minutes, and its warning clears on its own (reported by @Joshua1264).",
"mountsLanAddress": "Mount points on LVM-thin and other block storage show their usage, and multi-container applications open at their LAN address.",
"ociManager": "OCI manager Apps (beta) — a new entry in the ProxMenux menu installs applications from their official container images as native Proxmox LXC, with no Docker engine. The catalog carries over 360 applications.",
"ociManager": "OCI manager Apps (Technology Preview) — a new entry in the ProxMenux menu installs applications from their official container images as native Proxmox LXC, with no Docker engine. The catalog carries over 360 applications.",
"ociInMonitor": "OCI applications in the Monitor — the App tab recognises them, the Updates tab updates and modifies them as the menu does, and a Logs tab follows the console output of the application live.",
"ociAcceleration": "Hardware acceleration for OCI applications — the installer offers only the profiles the host can run: NVIDIA, Intel and AMD GPUs and, for Frigate, an Intel NPU or a Coral. The NVIDIA Container Toolkit installs with the driver.",
"ociRecovery": "An OCI application travels with its backup — restored on this host or on another one, it is registered and managed again, and in a cluster it starts on whichever node it moves to.",
@@ -5003,7 +5014,9 @@
"lxcTerminal": {
"title": "Terminal: {name} (ID: {id})",
"errors": {
"invalidVmid": "Invalid VMID — refusing to execute pct enter"
"invalidVmid": "Invalid VMID — refusing to execute pct enter",
"noShell": "This container's image has no shell, so it cannot open a terminal. Its output is in the Logs tab.",
"enterFailed": "The container could not be entered."
},
"commands": {
"listFiles": "List all files with details",
@@ -5218,6 +5231,7 @@
"processing": "Processing...",
"controlSequences": "Control Sequences",
"cancelInterrupt": "Cancel/Interrupt",
"endOfInput": "End of input",
"exitNano": "Exit (nano)",
"searchHistory": "Search history",
"clipboard": "Clipboard",
+34 -20
View File
@@ -1080,8 +1080,8 @@
"reboot": "Reiniciar",
"forceStop": "Parar",
"working": "Procesando...",
"startPending": "Empezando...",
"shutdownPending": "Cerrando...",
"startPending": "Iniciando...",
"shutdownPending": "Apagando...",
"rebootPending": "Reiniciando...",
"stopPending": "Parando..."
},
@@ -1133,7 +1133,7 @@
"removeCustomCommandConfirm": "¿Eliminar el comando de actualización personalizado para \"{name}\"?",
"removeCustomCommandFailed": "No se pudo eliminar el comando de actualización personalizado: {message}",
"hideNoticeFailed": "No se pudo ocultar este aviso: {message}",
"controlNotConfirmed": "La operación no se ha confirmado completa.Verifique el estado del invitado antes de volver a intentarlo."
"controlNotConfirmed": "No se ha confirmado que la operación haya terminado. Compruebe el estado de la VM o del contenedor antes de volver a intentarlo."
},
"backupModal": {
"title": "Backup de {type} {vmid} ({name})",
@@ -1454,21 +1454,21 @@
"postApplyNothingPending": "Nada pendiente — todo actualizado.",
"postApplyPartial": "{pending} paquete(s) siguen pendientes tras la ejecución.",
"postApplyPartialSubline": "{applied} aplicados. Algunas actualizaciones no finalizaron — revisa la salida del terminal.",
"checkNow": "Comprobar ahora",
"checkNow": "Comprobar actualizaciones",
"checkReadOnlyHelp": "Lee el sistema operativo actual y los paquetes pendientes a partir de los índices existentes. No instala nada ni actualiza los índices de paquetes.",
"checkCooldown": "Ya hay una comprobación en curso o se ha solicitado hace poco. Inténtalo de nuevo en {seconds} segundos.",
"checkStale": "Comprobación caducada",
"checkStaleHelp": "Este resultado tiene más de 48 horas o no tiene una fecha de comprobación válida. Lanza una nueva comprobación antes de fiarte de él.",
"checkSystemPackages": "comprobar los paquetes del sistema",
"checkInProgress": "Verificación en progreso",
"refreshCheckHelp": "actualiza los índices de paquetes y verifica las actualizaciones pendientes mientras el contenedor se está ejecutando.No hay nada instalado.",
"notCheckedYet": "Las actualizaciones aún no se han verificado.",
"checkStillRunning": "La verificación aún se está ejecutando.Su resultado se guardará cuando finalice.",
"checkBeforeUpdate": "Check updates first",
"startBeforeCheck": "inicie el contenedor y luego busque actualizaciones.No tener insignia no significa que no haya actualizaciones.",
"runCustomCommand": "ejecutar comando personalizado",
"installedVersionUnverified": "la versión instalada no está verificada.No se pudo confirmar la disponibilidad de la actualización.",
"versionStatusUnknown": "La disponibilidad de la actualización no está verificada.La información de la versión no está disponible o está incompleta."
"checkSystemPackages": "Comprobar paquetes del sistema",
"checkInProgress": "Comprobación en curso",
"refreshCheckHelp": "Renueva los índices de paquetes y comprueba las actualizaciones pendientes mientras el contenedor está en marcha. No instala nada.",
"notCheckedYet": "Las actualizaciones aún no se han comprobado.",
"checkStillRunning": "La comprobación sigue en curso. Su resultado se guardará al terminar.",
"checkBeforeUpdate": "Comprobar actualizaciones primero",
"startBeforeCheck": "Inicie el contenedor y después compruebe las actualizaciones. Que no aparezca el indicador no significa que no haya actualizaciones.",
"runCustomCommand": "Ejecutar comando personalizado",
"installedVersionUnverified": "La versión instalada no está verificada. No se pudo confirmar si hay una actualización disponible.",
"versionStatusUnknown": "No se ha verificado si hay una actualización disponible. La información de versión falta o está incompleta."
},
"bulkUpdate": {
"title": "Actualización en bloque",
@@ -1477,7 +1477,7 @@
"notConfigured": "No configurada.",
"configure": "Configurar",
"edit": "Editar",
"apply": "Aplicar actualizaciones",
"apply": "Iniciar actualización masiva",
"osTarget": "SO",
"osRequired": "Obligatorio en una actualización en bloque.",
"includesDependencies": "Incluye también las dependencias declaradas: {names}",
@@ -1494,9 +1494,9 @@
"deleteFailed": "No se pudo eliminar la configuración de actualización en bloque.",
"planFailed": "No se pudo preparar la actualización en bloque. Edite la configuración y revise los métodos no disponibles.",
"loadFailed": "no se pudo cargar la configuración de actualización masiva.",
"osDockerScope": "las actualizaciones del sistema operativo también incluyen Docker cuando se instala como paquete del sistema.",
"unverifiedTargetsHelp": "la disponibilidad de actualizaciones para algunos elementos seleccionados no está verificada.Al iniciar se ejecutarán los métodos de actualización seleccionados incluso sin una nueva versión confirmada.",
"dockerImageMethod": "imagen Docker"
"osDockerScope": "Las actualizaciones del sistema operativo también incluyen Docker cuando está instalado como paquete del sistema.",
"unverifiedTargetsHelp": "No se ha verificado si hay actualización para algunos de los elementos seleccionados. Al iniciar se ejecutarán los métodos de actualización elegidos aunque no haya una versión nueva confirmada.",
"dockerImageMethod": "Imagen Docker"
},
"appEditor": {
"dockerDetectedWithWorkloads": "Docker detectado con {count} aplicaciones en contenedores",
@@ -2195,6 +2195,17 @@
"localPveApi": "La dirección no puede apuntar a la API PVE local (localhost:8006/8007).",
"metadataIp": "La IP de metadatos del enlace local no se puede utilizar como punto final de Gotify."
},
"history": {
"title": "Historial de notificaciones",
"description": "Las últimas 20 notificaciones enviadas, incluida cada ejecución del resumen diario, con el resultado de cada una.",
"empty": "Todavía no se ha enviado ninguna notificación.",
"loadFailed": "No se pudo cargar el historial de notificaciones.",
"sent": "Enviada",
"notSent": "No enviada",
"severityInfo": "Info",
"severityWarning": "Advertencia",
"severityCritical": "Crítico"
},
"customPromptInfo": {
"title": "Modo de aviso personalizado",
"description": "Cree su propio mensaje de IA para las notificaciones de ProxMenux Monitor",
@@ -3422,7 +3433,7 @@
"webhookHttps": "Las notificaciones de Proxmox llegan al Monitor con HTTPS activado — se entregan en una dirección solo local y ya no fallan por un error de certificado.",
"persistentLogs": "Una ráfaga de errores en los logs que ya terminó no se presenta como persistente: el patrón tiene que seguir apareciendo durante 15 minutos, y su aviso se cierra solo (reportado por @Joshua1264).",
"mountsLanAddress": "Los puntos de montaje en LVM-thin y otros almacenamientos de bloques muestran su uso, y las aplicaciones multicontenedor se abren en su dirección de la LAN.",
"ociManager": "OCI manager Apps (beta) — una nueva entrada del menú de ProxMenux instala aplicaciones desde sus imágenes de contenedor oficiales como LXC nativos de Proxmox, sin motor Docker. El catálogo reúne más de 360 aplicaciones.",
"ociManager": "OCI manager Apps (Technology Preview) — una nueva entrada del menú de ProxMenux instala aplicaciones desde sus imágenes de contenedor oficiales como LXC nativos de Proxmox, sin motor Docker. El catálogo reúne más de 360 aplicaciones.",
"ociInMonitor": "Aplicaciones OCI en el Monitor — la pestaña App las reconoce, la pestaña Actualizaciones las actualiza y modifica igual que el menú, y una pestaña Logs sigue en directo la salida de consola de la aplicación.",
"ociAcceleration": "Aceleración por hardware para aplicaciones OCI — el instalador ofrece solo los perfiles que el host puede ejecutar: GPU NVIDIA, Intel y AMD y, para Frigate, una NPU de Intel o una Coral. NVIDIA Container Toolkit se instala con el driver.",
"ociRecovery": "Una aplicación OCI viaja con su backup — restaurada en este host o en otro, vuelve a quedar registrada y gestionada, y en un clúster arranca en el nodo al que se mueva.",
@@ -5004,7 +5015,9 @@
"lxcTerminal": {
"title": "Terminal: {name} (ID: {id})",
"errors": {
"invalidVmid": "VMID no válido: se niega a ejecutar pct enter"
"invalidVmid": "VMID no válido: se niega a ejecutar pct enter",
"noShell": "La imagen de este contenedor no incluye shell, así que no puede abrir una terminal. Su salida está en la pestaña Logs.",
"enterFailed": "No se pudo entrar en el contenedor."
},
"commands": {
"listFiles": "Listar todos los archivos con detalles",
@@ -5219,6 +5232,7 @@
"processing": "Tratamiento...",
"controlSequences": "Secuencias de control",
"cancelInterrupt": "Cancelar/Interrumpir",
"endOfInput": "Fin de la entrada",
"exitNano": "Salir (nano)",
"searchHistory": "Historial de búsqueda",
"clipboard": "Portapapeles",
+34 -20
View File
@@ -1075,15 +1075,15 @@
"logs": "Logs"
},
"actions": {
"start": "Commencer",
"shutdown": "Fermer",
"start": "Démarrer",
"shutdown": "Éteindre",
"reboot": "Redémarrer",
"forceStop": "Forcer l'arrêt",
"working": "Fonctionnement...",
"startPending": "Démarrer...",
"shutdownPending": "Arrêter...",
"working": "En cours...",
"startPending": "Démarrage...",
"shutdownPending": "Extinction...",
"rebootPending": "Redémarrage...",
"stopPending": "Arrêter..."
"stopPending": "Arrêt..."
},
"confirm": {
"forceStopTitle": "Forcer l'arrêt",
@@ -1133,7 +1133,7 @@
"removeCustomCommandConfirm": "Supprimer la commande de mise à jour personnalisée pour \"{name}\" ?",
"removeCustomCommandFailed": "Impossible de supprimer la commande de mise à jour personnalisée : {message}",
"hideNoticeFailed": "Impossible de masquer cet avis : {message}",
"controlNotConfirmed": "L'opération n'a pas été confirmée comme étant terminée.Vérifiez le statut de l'invité avant de réessayer."
"controlNotConfirmed": "La fin de l'opération n'a pas été confirmée. Vérifiez l'état de l'invité avant de réessayer."
},
"backupModal": {
"title": "Sauvegarde {type} {vmid} ({name})",
@@ -1459,16 +1459,16 @@
"checkCooldown": "Une vérification est déjà en cours ou a été demandée récemment. Réessayez dans {seconds} secondes.",
"checkStale": "Vérification obsolète",
"checkStaleHelp": "Ce résultat date de plus de 48 heures ou n’a pas de date de vérification valide. Lancez une nouvelle vérification avant de vous y fier.",
"checkSystemPackages": "Vérifier les packages système",
"checkSystemPackages": "Vérifier les paquets système",
"checkInProgress": "Vérification en cours",
"refreshCheckHelp": "actualise les index des packages et vérifie les mises à jour en attente pendant l'exécution du conteneur.Rien n'est installé.",
"refreshCheckHelp": "Actualise les index de paquets et vérifie les mises à jour en attente pendant que le conteneur est en cours d'exécution. Rien n'est installé.",
"notCheckedYet": "Les mises à jour n'ont pas encore été vérifiées.",
"checkStillRunning": "Le contrôle est toujours en cours.Son résultat sera enregistré une fois terminé.",
"checkBeforeUpdate": "Vérifiez d'abord les mises à jour",
"startBeforeCheck": "démarrez le conteneur, puis recherchez les mises à jour.L'absence de badge ne signifie pas qu'il n'y a pas de mises à jour.",
"checkStillRunning": "La vérification est toujours en cours. Son résultat sera enregistré à la fin.",
"checkBeforeUpdate": "Vérifier d'abord les mises à jour",
"startBeforeCheck": "Démarrez le conteneur, puis vérifiez les mises à jour. L'absence de badge ne signifie pas qu'il n'y a pas de mises à jour.",
"runCustomCommand": "Exécuter une commande personnalisée",
"installedVersionUnverified": "la version installée n’est pas vérifiée.La disponibilité de la mise à jour n'a pas pu être confirmée.",
"versionStatusUnknown": "la disponibilité de la mise à jour n'est pas vérifiée.Les informations sur la version sont indisponibles ou incomplètes."
"installedVersionUnverified": "La version installée n'est pas vérifiée. La disponibilité d'une mise à jour n'a pas pu être confirmée.",
"versionStatusUnknown": "La disponibilité d'une mise à jour n'est pas vérifiée. Les informations de version sont indisponibles ou incomplètes."
},
"bulkUpdate": {
"title": "Mise à jour groupée",
@@ -1494,9 +1494,9 @@
"deleteFailed": "Impossible de supprimer la configuration de mise à jour groupée.",
"planFailed": "Impossible de préparer la mise à jour groupée. Vérifiez les méthodes indisponibles.",
"loadFailed": "Impossible de charger la configuration de la mise à jour groupée.",
"osDockerScope": "Les mises à jour du système d'exploitation incluent également Docker lorsqu'il est installé en tant que package système.",
"unverifiedTargetsHelp": "la disponibilité des mises à jour pour certains éléments sélectionnés n'est pas vérifiée.Le démarrage exécutera les méthodes de mise à jour sélectionnées même sans nouvelle version confirmée.",
"dockerImageMethod": "image Docker"
"osDockerScope": "Les mises à jour du système incluent aussi Docker lorsqu'il est installé comme paquet système.",
"unverifiedTargetsHelp": "La disponibilité des mises à jour n'est pas vérifiée pour certains éléments sélectionnés. Le lancement exécutera les méthodes de mise à jour choisies même sans nouvelle version confirmée.",
"dockerImageMethod": "Image Docker"
},
"appEditor": {
"methodOciImage": "Image OCI (installée par ProxMenux)",
@@ -2211,6 +2211,17 @@
"gettingStartedDescription": "Un exemple d’invite est prêt à vous aider à démarrer. Vous pouvez l'ajuster, l'exporter pour le partager avec d'autres ou importer une invite de la communauté.",
"loadExample": "Charger un exemple",
"startFromScratch": "Repartir de zéro"
},
"history": {
"title": "Historique des notifications",
"description": "Les 20 dernières notifications envoyées, y compris chaque exécution du résumé quotidien, avec le résultat de chacune.",
"empty": "Aucune notification n'a encore été envoyée.",
"loadFailed": "L'historique des notifications n'a pas pu être chargé.",
"sent": "Envoyé",
"notSent": "Non envoyé",
"severityInfo": "Info",
"severityWarning": "Avertissement",
"severityCritical": "Critique"
}
},
"optimizations": {
@@ -3422,7 +3433,7 @@
"webhookHttps": "Les notifications de Proxmox atteignent le Monitor avec HTTPS activé — elles sont livrées sur une adresse uniquement locale et n'échouent plus sur une erreur de certificat.",
"persistentLogs": "Une rafale d'erreurs de logs terminée n'est plus signalée comme persistante : un motif doit continuer d'apparaître pendant 15 minutes, et son avertissement se ferme de lui-même (signalé par @Joshua1264).",
"mountsLanAddress": "Les points de montage sur LVM-thin et autres stockages en mode bloc affichent leur utilisation, et les applications multi-conteneurs s'ouvrent sur leur adresse LAN.",
"ociManager": "OCI manager Apps (bêta) — une nouvelle entrée du menu ProxMenux installe les applications à partir de leurs images de conteneur officielles en tant que LXC Proxmox natifs, sans moteur Docker. Le catalogue réunit plus de 360 applications.",
"ociManager": "OCI manager Apps (Technology Preview) — une nouvelle entrée du menu ProxMenux installe les applications à partir de leurs images de conteneur officielles en tant que LXC Proxmox natifs, sans moteur Docker. Le catalogue réunit plus de 360 applications.",
"ociInMonitor": "Applications OCI dans le Monitor — l'onglet App les reconnaît, l'onglet Mises à jour les met à jour et les modifie comme le fait le menu, et un onglet Logs suit en direct la sortie console de l'application.",
"ociAcceleration": "Accélération matérielle pour les applications OCI — l'installateur ne propose que les profils que l'hôte peut exécuter : GPU NVIDIA, Intel et AMD et, pour Frigate, un NPU Intel ou une Coral. Le NVIDIA Container Toolkit s'installe avec le pilote.",
"ociRecovery": "Une application OCI voyage avec sa sauvegarde — restaurée sur cet hôte ou sur un autre, elle est de nouveau enregistrée et gérée, et dans un cluster elle démarre sur le nœud vers lequel elle est déplacée.",
@@ -5004,7 +5015,9 @@
"lxcTerminal": {
"title": "Terminal : {name} (ID : {id})",
"errors": {
"invalidVmid": "VMID invalide - refus d'exécuter PCT Enter"
"invalidVmid": "VMID invalide - refus d'exécuter PCT Enter",
"noShell": "L'image de ce conteneur n'a pas de shell, elle ne peut donc pas ouvrir de terminal. Sa sortie se trouve dans l'onglet Logs.",
"enterFailed": "Impossible d'entrer dans le conteneur."
},
"commands": {
"listFiles": "Lister tous les fichiers avec des détails",
@@ -5237,7 +5250,8 @@
"yes": "Oui",
"yourInput": "Votre contribution :",
"submit": "Soumettre",
"ok": "OK"
"ok": "OK",
"endOfInput": "Fin de la saisie"
},
"apps": {
"searchPlaceholder": "Search app or CT…",
+33 -19
View File
@@ -1080,10 +1080,10 @@
"reboot": "Riavvia",
"forceStop": "Forza arresto",
"working": "Operazione in corso...",
"startPending": "A partire...",
"startPending": "Avvio...",
"shutdownPending": "Spegnimento...",
"rebootPending": "Riavvio...",
"stopPending": "Fermarsi..."
"stopPending": "Arresto..."
},
"confirm": {
"forceStopTitle": "Forza arresto",
@@ -1133,7 +1133,7 @@
"removeCustomCommandConfirm": "Rimuovere il comando di aggiornamento personalizzato per \"{name}\"?",
"removeCustomCommandFailed": "Impossibile rimuovere il comando di aggiornamento personalizzato: {message}",
"hideNoticeFailed": "Impossibile nascondere questo avviso: {message}",
"controlNotConfirmed": "L'operazione non è stata confermata completata.Controlla lo stato dell'ospite prima di riprovare."
"controlNotConfirmed": "Il completamento dell'operazione non è stato confermato. Controlla lo stato del guest prima di riprovare."
},
"backupModal": {
"title": "Backup {type} {vmid} ({name})",
@@ -1459,16 +1459,16 @@
"checkCooldown": "Un controllo è già in corso o è stato richiesto di recente. Riprovare tra {seconds} secondi.",
"checkStale": "Controllo obsoleto",
"checkStaleHelp": "Questo risultato ha più di 48 ore o non ha una data di controllo valida. Eseguire un nuovo controllo prima di farvi affidamento.",
"checkSystemPackages": "controlla i pacchetti di sistema",
"checkInProgress": "controllo in corso",
"refreshCheckHelp": "aggiorna gli indici dei pacchetti e controlla gli aggiornamenti in sospeso mentre il contenitore è in esecuzione.Non è installato nulla.",
"notCheckedYet": "gli aggiornamenti non sono stati ancora controllati.",
"checkStillRunning": "Il controllo è ancora in corso.Il risultato verrà salvato al termine.",
"checkBeforeUpdate": "controlla prima gli aggiornamenti",
"startBeforeCheck": "avvia il contenitore, quindi controlla gli aggiornamenti.Nessun badge non significa che non ci sono aggiornamenti.",
"runCustomCommand": "esegui il comando personalizzato",
"installedVersionUnverified": "la versione installata non è verificata.Non è stato possibile confermare la disponibilità dell'aggiornamento.",
"versionStatusUnknown": "la disponibilità dell'aggiornamento non è verificata.Le informazioni sulla versione non sono disponibili o sono incomplete."
"checkSystemPackages": "Controlla pacchetti di sistema",
"checkInProgress": "Controllo in corso",
"refreshCheckHelp": "Aggiorna gli indici dei pacchetti e controlla gli aggiornamenti in sospeso mentre il container è in esecuzione. Non viene installato nulla.",
"notCheckedYet": "Gli aggiornamenti non sono ancora stati controllati.",
"checkStillRunning": "Il controllo è ancora in corso. Il risultato verrà salvato al termine.",
"checkBeforeUpdate": "Controlla prima gli aggiornamenti",
"startBeforeCheck": "Avvia il container, poi controlla gli aggiornamenti. L'assenza del badge non significa che non ci siano aggiornamenti.",
"runCustomCommand": "Esegui comando personalizzato",
"installedVersionUnverified": "La versione installata non è verificata. Non è stato possibile confermare la disponibilità di un aggiornamento.",
"versionStatusUnknown": "La disponibilità di un aggiornamento non è verificata. Le informazioni sulla versione non sono disponibili o sono incomplete."
},
"bulkUpdate": {
"title": "Aggiornamento in blocco",
@@ -1494,9 +1494,9 @@
"deleteFailed": "Impossibile rimuovere la configurazione di aggiornamento in blocco.",
"planFailed": "Impossibile preparare l’aggiornamento in blocco. Modifica la configurazione e controlla i metodi non disponibili.",
"loadFailed": "Impossibile caricare la configurazione dell’aggiornamento in blocco.",
"osDockerScope": "gli aggiornamenti del sistema operativo includono anche Docker quando è installato come pacchetto di sistema.",
"unverifiedTargetsHelp": "la disponibilità dell'aggiornamento per alcuni articoli selezionati non è verificata.L'avvio eseguirà i metodi di aggiornamento selezionati anche senza una nuova versione confermata.",
"dockerImageMethod": "immagine Docker"
"osDockerScope": "Gli aggiornamenti del sistema operativo includono anche Docker quando è installato come pacchetto di sistema.",
"unverifiedTargetsHelp": "La disponibilità di aggiornamenti per alcuni elementi selezionati non è verificata. L'avvio eseguirà i metodi di aggiornamento scelti anche senza una nuova versione confermata.",
"dockerImageMethod": "Immagine Docker"
},
"appEditor": {
"methodOciImage": "Immagine OCI (installata da ProxMenux)",
@@ -2211,6 +2211,17 @@
"gettingStartedDescription": "Un prompt di esempio è pronto per aiutarti a iniziare. Puoi modificarlo, esportarlo per condividerlo con altri o importare un prompt dalla community.",
"loadExample": "Carica esempio",
"startFromScratch": "Inizia da zero"
},
"history": {
"title": "Cronologia delle notifiche",
"description": "Le ultime 20 notifiche inviate, inclusa ogni esecuzione del digest giornaliero, con il risultato di ciascuna.",
"empty": "Nessuna notifica è stata ancora inviata.",
"loadFailed": "Non è stato possibile caricare la cronologia delle notifiche.",
"sent": "Inviata",
"notSent": "Non inviata",
"severityInfo": "Info",
"severityWarning": "Avviso",
"severityCritical": "Critico"
}
},
"optimizations": {
@@ -3422,7 +3433,7 @@
"webhookHttps": "Le notifiche Proxmox raggiungono il Monitor con HTTPS attivo: consegnate su un indirizzo solo locale, non falliscono più per errori di certificato.",
"persistentLogs": "Una raffica di errori di log conclusa non è più segnalata come persistente: un pattern deve continuare a comparire per 15 minuti e l’avviso si chiude da solo (segnalato da @Joshua1264).",
"mountsLanAddress": "I mount su LVM-thin e altri storage a blocchi mostrano l’utilizzo; le app multi-container si aprono al proprio indirizzo LAN.",
"ociManager": "OCI manager Apps (beta): una nuova voce nel menu di ProxMenux installa le applicazioni dalle loro immagini container ufficiali come LXC nativi di Proxmox, senza motore Docker. Il catalogo contiene oltre 360 applicazioni.",
"ociManager": "OCI manager Apps (Technology Preview): una nuova voce nel menu di ProxMenux installa le applicazioni dalle loro immagini container ufficiali come LXC nativi di Proxmox, senza motore Docker. Il catalogo contiene oltre 360 applicazioni.",
"ociInMonitor": "Applicazioni OCI nel Monitor: la scheda App le riconosce, la scheda Aggiornamenti le aggiorna e le modifica come fa il menu, e una scheda Log segue in tempo reale l'output della console dell'applicazione.",
"ociAcceleration": "Accelerazione hardware per le applicazioni OCI: l'installer offre solo i profili che l'host può eseguire: GPU NVIDIA, Intel e AMD e, per Frigate, una NPU Intel o una Coral. NVIDIA Container Toolkit viene installato con il driver.",
"ociRecovery": "Un'applicazione OCI viaggia con il suo backup: ripristinata su questo host o su un altro, viene di nuovo registrata e gestita, e in un cluster si avvia sul nodo su cui viene spostata.",
@@ -5004,7 +5015,9 @@
"lxcTerminal": {
"title": "Terminale: {name} (ID: {id})",
"errors": {
"invalidVmid": "VMID non valido: esecuzione di pct enter rifiutata"
"invalidVmid": "VMID non valido: esecuzione di pct enter rifiutata",
"noShell": "L'immagine di questo container non ha una shell, quindi non può aprire un terminale. Il suo output si trova nella scheda Log.",
"enterFailed": "Impossibile entrare nel container."
},
"commands": {
"listFiles": "Elenca tutti i file con dettagli",
@@ -5237,7 +5250,8 @@
"yes": "Sì",
"yourInput": "Input:",
"submit": "Invia",
"ok": "OK"
"ok": "OK",
"endOfInput": "Fine dell'input"
},
"apps": {
"searchPlaceholder": "Cerca app o CT…",
+33 -19
View File
@@ -1075,12 +1075,12 @@
"logs": "Logs"
},
"actions": {
"start": "Começar",
"start": "Iniciar",
"shutdown": "Desligar",
"reboot": "Reinício",
"reboot": "Reiniciar",
"forceStop": "Forçar parada",
"working": "Trabalhando...",
"startPending": "Começando...",
"working": "Processando...",
"startPending": "Iniciando...",
"shutdownPending": "Desligando...",
"rebootPending": "Reiniciando...",
"stopPending": "Parando..."
@@ -1133,7 +1133,7 @@
"removeCustomCommandConfirm": "Remover o comando de atualização personalizado para \"{name}\"?",
"removeCustomCommandFailed": "não foi possível remover o comando de atualização personalizado: {message}",
"hideNoticeFailed": "Não foi possível ocultar este aviso: {message}",
"controlNotConfirmed": "A operação não foi confirmada como concluída.Verifique o status do convidado antes de tentar novamente."
"controlNotConfirmed": "A conclusão da operação não foi confirmada. Verifique o estado do convidado antes de tentar novamente."
},
"backupModal": {
"title": "Backup {type} {vmid} ({name})",
@@ -1459,16 +1459,16 @@
"checkCooldown": "Já há uma verificação em execução ou pedida recentemente. Tente novamente dentro de {seconds} segundos.",
"checkStale": "Verificação desatualizada",
"checkStaleHelp": "Este resultado tem mais de 48 horas ou não tem uma data de verificação válida. Execute uma nova verificação antes de confiar nele.",
"checkSystemPackages": "Verifique os pacotes do sistema",
"checkInProgress": "verificação em andamento",
"refreshCheckHelp": "atualiza índices de pacotes e verifica atualizações pendentes enquanto o contêiner está em execução.Nada está instalado.",
"checkSystemPackages": "Verificar pacotes do sistema",
"checkInProgress": "Verificação em andamento",
"refreshCheckHelp": "Atualiza os índices de pacotes e verifica as atualizações pendentes enquanto o contêiner está em execução. Nada é instalado.",
"notCheckedYet": "As atualizações ainda não foram verificadas.",
"checkStillRunning": "A verificação ainda está em execução.Seu resultado será salvo quando terminar.",
"checkBeforeUpdate": "verifique as atualizações primeiro",
"startBeforeCheck": "inicie o contêiner e verifique se há atualizações.Nenhum selo não significa que não haja atualizações.",
"checkStillRunning": "A verificação ainda está em execução. O resultado será salvo quando terminar.",
"checkBeforeUpdate": "Verificar atualizações primeiro",
"startBeforeCheck": "Inicie o contêiner e depois verifique as atualizações. A ausência do selo não significa que não haja atualizações.",
"runCustomCommand": "Executar comando personalizado",
"installedVersionUnverified": "a versão instalada não foi verificada.A disponibilidade da atualização não pôde ser confirmada.",
"versionStatusUnknown": "a disponibilidade da atualização não foi verificada.As informações da versão estão indisponíveis ou incompletas."
"installedVersionUnverified": "A versão instalada não foi verificada. Não foi possível confirmar se há uma atualização disponível.",
"versionStatusUnknown": "A disponibilidade de atualização não foi verificada. As informações de versão estão indisponíveis ou incompletas."
},
"bulkUpdate": {
"title": "Atualização em bloco",
@@ -1494,9 +1494,9 @@
"deleteFailed": "Não foi possível remover a configuração de atualização em bloco.",
"planFailed": "Não foi possível preparar a atualização em bloco. Reveja os métodos indisponíveis.",
"loadFailed": "não foi possível carregar a configuração de atualização em massa.",
"osDockerScope": "as atualizações do sistema operacional também incluem Docker quando instalado como um pacote de sistema.",
"unverifiedTargetsHelp": "a disponibilidade de atualização para alguns itens selecionados não foi verificada.A inicialização executará os métodos de atualização selecionados mesmo sem uma nova versão confirmada.",
"dockerImageMethod": "imagem Docker"
"osDockerScope": "As atualizações do sistema operacional também incluem o Docker quando ele está instalado como pacote do sistema.",
"unverifiedTargetsHelp": "A disponibilidade de atualização de alguns itens selecionados não foi verificada. Ao iniciar, os métodos de atualização escolhidos serão executados mesmo sem uma nova versão confirmada.",
"dockerImageMethod": "Imagem Docker"
},
"appEditor": {
"methodOciImage": "Imagem OCI (instalada pelo ProxMenux)",
@@ -2211,6 +2211,17 @@
"gettingStartedDescription": "Um exemplo de prompt está pronto para ajudá-lo a começar. Você pode ajustá-lo, exportá-lo para compartilhar com outras pessoas ou importar um prompt da comunidade.",
"loadExample": "Exemplo de carregamento",
"startFromScratch": "Comece do zero"
},
"history": {
"title": "Histórico de notificações",
"description": "As últimas 20 notificações enviadas, incluindo cada execução do resumo diário, com o resultado de cada uma.",
"empty": "Nenhuma notificação foi enviada ainda.",
"loadFailed": "O histórico de notificações não pôde ser carregado.",
"sent": "Enviado",
"notSent": "Não enviado",
"severityInfo": "Info",
"severityWarning": "Aviso",
"severityCritical": "Crítico"
}
},
"optimizations": {
@@ -3422,7 +3433,7 @@
"webhookHttps": "As notificações do Proxmox chegam ao Monitor com HTTPS ativo — são entregues num endereço apenas local e já não falham por um erro de certificado.",
"persistentLogs": "Uma rajada de erros nos logs que terminou já não é apresentada como persistente: um padrão tem de continuar a aparecer durante 15 minutos, e o seu aviso fecha-se sozinho (reportado por @Joshua1264).",
"mountsLanAddress": "Os pontos de montagem em LVM-thin e outros armazenamentos em bloco mostram a sua utilização, e as aplicações com vários contentores abrem no seu endereço LAN.",
"ociManager": "OCI manager Apps (beta) — uma nova entrada no menu do ProxMenux instala aplicações a partir das suas imagens de contentor oficiais como LXC nativos do Proxmox, sem motor Docker. O catálogo reúne mais de 360 aplicações.",
"ociManager": "OCI manager Apps (Technology Preview) — uma nova entrada no menu do ProxMenux instala aplicações a partir das suas imagens de contentor oficiais como LXC nativos do Proxmox, sem motor Docker. O catálogo reúne mais de 360 aplicações.",
"ociInMonitor": "Aplicações OCI no Monitor — o separador App reconhece-as, o separador Atualizações atualiza-as e modifica-as como o menu, e um separador Logs segue em direto a saída da consola da aplicação.",
"ociAcceleration": "Aceleração por hardware para aplicações OCI — o instalador oferece apenas os perfis que o host pode executar: GPU NVIDIA, Intel e AMD e, para o Frigate, uma NPU Intel ou uma Coral. O NVIDIA Container Toolkit é instalado com o driver.",
"ociRecovery": "Uma aplicação OCI viaja com o seu backup — restaurada neste host ou noutro, volta a ficar registada e gerida, e num cluster arranca no nó para onde for movida.",
@@ -5004,7 +5015,9 @@
"lxcTerminal": {
"title": "Terminal: {name} (ID: {id})",
"errors": {
"invalidVmid": "VMID inválido – recusando-se a executar pct enter"
"invalidVmid": "VMID inválido – recusando-se a executar pct enter",
"noShell": "A imagem deste contêiner não possui shell, portanto não pode abrir um terminal. A sua saída está na aba Logs.",
"enterFailed": "Não foi possível entrar no contêiner."
},
"commands": {
"listFiles": "Liste todos os arquivos com detalhes",
@@ -5237,7 +5250,8 @@
"yes": "Sim",
"yourInput": "Sua entrada:",
"submit": "Enviar",
"ok": "OK"
"ok": "OK",
"endOfInput": "Fim da entrada"
},
"apps": {
"searchPlaceholder": "Search app or CT…",
+17 -3
View File
@@ -2210,6 +2210,17 @@
"gettingStartedDescription": "Na začiatok máte pripravený vzorový prompt. Môžete ho upraviť, exportovať a zdieľať alebo importovať prompt od komunity.",
"loadExample": "Načítať príklad",
"startFromScratch": "Začať od začiatku"
},
"history": {
"title": "História upozornení",
"description": "Posledných 20 odoslaných upozornení vrátane každého spustenia denného súhrnu s výsledkom každého z nich.",
"empty": "Zatiaľ neboli odoslané žiadne upozornenia.",
"loadFailed": "Históriu upozornení sa nepodarilo načítať.",
"sent": "Odoslané",
"notSent": "Neodoslané",
"severityInfo": "Info",
"severityWarning": "Upozornenie",
"severityCritical": "Kritické"
}
},
"optimizations": {
@@ -3421,7 +3432,7 @@
"webhookHttps": "Upozornenia Proxmoxu sa dostanú do Monitora aj so zapnutým HTTPS — doručujú sa na výhradne lokálnu adresu a už nezlyhávajú na chybe certifikátu.",
"persistentLogs": "Skončená vlna chýb v logoch sa už nehlási ako trvalá: vzor sa musí opakovať 15 minút a jeho upozornenie sa zatvorí samo (nahlásil @Joshua1264).",
"mountsLanAddress": "Body pripojenia na LVM-thin a inom blokovom úložisku zobrazujú svoje využitie a aplikácie s viacerými kontajnermi sa otvárajú na svojej LAN adrese.",
"ociManager": "OCI manager Apps (beta) — nová položka v ponuke ProxMenux inštaluje aplikácie z ich oficiálnych kontajnerových image ako natívne Proxmox LXC, bez enginu Docker. Katalóg obsahuje viac ako 360 aplikácií.",
"ociManager": "OCI manager Apps (Technology Preview) — nová položka v ponuke ProxMenux inštaluje aplikácie z ich oficiálnych kontajnerových image ako natívne Proxmox LXC, bez enginu Docker. Katalóg obsahuje viac ako 360 aplikácií.",
"ociInMonitor": "OCI aplikácie v Monitore — karta Aplikácie ich rozpozná, karta Aktualizácie ich aktualizuje a upravuje rovnako ako ponuka a karta Logy naživo sleduje výstup konzoly aplikácie.",
"ociAcceleration": "Hardvérová akcelerácia pre OCI aplikácie — inštalátor ponúka iba profily, ktoré hostiteľ dokáže spustiť: GPU NVIDIA, Intel a AMD a pre Frigate Intel NPU alebo Coral. NVIDIA Container Toolkit sa inštaluje spolu s ovládačom.",
"ociRecovery": "OCI aplikácia cestuje so svojou zálohou — po obnovení na tomto alebo inom hostiteľovi je znova zaregistrovaná a spravovaná a v klastri sa spustí na uzle, na ktorý sa presunie.",
@@ -5003,7 +5014,9 @@
"lxcTerminal": {
"title": "Terminál: {name} (ID: {id})",
"errors": {
"invalidVmid": "Neplatné VMID — pct enter sa nespustí"
"invalidVmid": "Neplatné VMID — pct enter sa nespustí",
"noShell": "Image tohto kontajnera nemá shell, takže nemôže otvoriť terminál. Jeho výstup je na karte Logy.",
"enterFailed": "Do kontajnera sa nepodarilo vstúpiť."
},
"commands": {
"listFiles": "Zobraziť podrobnosti o súboroch",
@@ -5236,7 +5249,8 @@
"yes": "Áno",
"yourInput": "Tvoj vstup:",
"submit": "Odoslať",
"ok": "OK"
"ok": "OK",
"endOfInput": "Koniec zadávania"
},
"apps": {
"searchPlaceholder": "Hľadať aplikáciu, VM alebo LXC…",
+32 -18
View File
@@ -1075,12 +1075,12 @@
"logs": "Loggar"
},
"actions": {
"start": "Start",
"shutdown": "Stängning",
"start": "Starta",
"shutdown": "Stäng av",
"reboot": "Starta om",
"forceStop": "Tvinga stopp",
"working": "Arbetssätt...",
"startPending": "Börjar...",
"working": "Arbetar...",
"startPending": "Startar...",
"shutdownPending": "Stänger av...",
"rebootPending": "Startar om...",
"stopPending": "Stoppar..."
@@ -1133,7 +1133,7 @@
"removeCustomCommandConfirm": "Ta bort det anpassade uppdateringskommandot för \"{name}\"?",
"removeCustomCommandFailed": "Kunde inte ta bort det anpassade uppdateringskommandot: {message}",
"hideNoticeFailed": "Kunde inte dölja detta meddelande: {message}",
"controlNotConfirmed": "Operationen har inte bekräftats slutförd.Kontrollera gäststatusen innan du försöker igen."
"controlNotConfirmed": "Det har inte bekräftats att åtgärden är slutförd. Kontrollera gästens status innan du försöker igen."
},
"backupModal": {
"title": "Säkerhetskopiera {type} {vmid} ({name})",
@@ -1460,15 +1460,15 @@
"checkStale": "Föråldrad kontroll",
"checkStaleHelp": "Detta resultat är äldre än 48 timmar eller saknar ett giltigt kontrolldatum. Kör en ny kontroll innan du litar på det.",
"checkSystemPackages": "Kontrollera systempaket",
"checkInProgress": "Incheckning pågår",
"refreshCheckHelp": "Uppdaterar paketindex och kontrollerar väntande uppdateringar medan behållaren körs.Inget är installerat.",
"checkInProgress": "Kontroll pågår",
"refreshCheckHelp": "Uppdaterar paketindex och kontrollerar väntande uppdateringar medan behållaren körs. Inget installeras.",
"notCheckedYet": "Uppdateringar har inte kontrollerats ännu.",
"checkStillRunning": "Kontrollen körs fortfarande.Resultatet kommer att sparas när det är klart.",
"checkBeforeUpdate": "Kontrollera uppdateringarna först",
"startBeforeCheck": "Starta behållaren och leta sedan efter uppdateringar.Inget märke betyder inte att det inte finns några uppdateringar.",
"checkStillRunning": "Kontrollen pågår fortfarande. Resultatet sparas när den är klar.",
"checkBeforeUpdate": "Kontrollera uppdateringar först",
"startBeforeCheck": "Starta behållaren och kontrollera sedan uppdateringar. Att ett märke saknas betyder inte att det inte finns uppdateringar.",
"runCustomCommand": "Kör anpassat kommando",
"installedVersionUnverified": "Den installerade versionen är overifierad.Uppdateringstillgänglighet kunde inte bekräftas.",
"versionStatusUnknown": "Uppdateringstillgängligheten är overifierad.Versionsinformation är inte tillgänglig eller ofullständig."
"installedVersionUnverified": "Den installerade versionen är inte verifierad. Det gick inte att bekräfta om en uppdatering finns.",
"versionStatusUnknown": "Det är inte verifierat om en uppdatering finns. Versionsinformationen saknas eller är ofullständig."
},
"bulkUpdate": {
"title": "Gruppuppdatering",
@@ -1494,9 +1494,9 @@
"deleteFailed": "Det gick inte att ta bort gruppuppdateringskonfigurationen.",
"planFailed": "Det gick inte att förbereda gruppuppdateringen. Kontrollera otillgängliga metoder.",
"loadFailed": "Det gick inte att läsa in massuppdateringskonfigurationen.",
"osDockerScope": "OS-uppdateringar inkluderar även Docker när det är installerat som ett systempaket.",
"unverifiedTargetsHelp": "Uppdateringstillgängligheten för vissa valda objekt är overifierad.Om du startar kommer de valda uppdateringsmetoderna att köras även utan en bekräftad ny version.",
"dockerImageMethod": "Docker bild"
"osDockerScope": "OS-uppdateringar omfattar även Docker när det är installerat som systempaket.",
"unverifiedTargetsHelp": "För vissa valda objekt är det inte verifierat om en uppdatering finns. Vid start körs de valda uppdateringsmetoderna även utan en bekräftad ny version.",
"dockerImageMethod": "Docker-avbildning"
},
"appEditor": {
"methodOciImage": "OCI-avbild (installerad av ProxMenux)",
@@ -2211,6 +2211,17 @@
"gettingStartedDescription": "Ett exempel är redo för att hjälpa dig att komma igång. Du kan justera den, exportera den för att dela med andra eller importera en uppmaning från communityn.",
"loadExample": "Ladda exempel",
"startFromScratch": "Börja från början"
},
"history": {
"title": "Aviseringshistorik",
"description": "De senaste 20 aviseringarna som skickats, inklusive varje körning av det dagliga sammandraget, med resultatet av var och en.",
"empty": "Inga meddelanden har skickats än.",
"loadFailed": "Aviseringshistoriken kunde inte laddas.",
"sent": "Skickat",
"notSent": "Ej skickat",
"severityInfo": "Info",
"severityWarning": "Varning",
"severityCritical": "Kritisk"
}
},
"optimizations": {
@@ -3422,7 +3433,7 @@
"webhookHttps": "Proxmox-aviseringar når Monitor med HTTPS aktiverat — de levereras till en adress som bara är lokal och misslyckas inte längre på grund av ett certifikatfel.",
"persistentLogs": "En avslutad skur av loggfel rapporteras inte längre som bestående: ett mönster måste fortsätta att dyka upp i 15 minuter, och dess varning stängs av sig själv (rapporterat av @Joshua1264).",
"mountsLanAddress": "Monteringspunkter på LVM-thin och annan blocklagring visar sin användning, och program med flera containrar öppnas på sin LAN-adress.",
"ociManager": "OCI manager Apps (beta) — en ny post i ProxMenux-menyn installerar applikationer från deras officiella containeravbilder som inbyggda Proxmox LXC, utan Docker-motor. Katalogen innehåller över 360 applikationer.",
"ociManager": "OCI manager Apps (Technology Preview) — en ny post i ProxMenux-menyn installerar applikationer från deras officiella containeravbilder som inbyggda Proxmox LXC, utan Docker-motor. Katalogen innehåller över 360 applikationer.",
"ociInMonitor": "OCI-applikationer i Monitor — fliken App känner igen dem, fliken Uppdateringar uppdaterar och ändrar dem som menyn gör, och fliken Logs följer applikationens konsolutdata live.",
"ociAcceleration": "Hårdvaruacceleration för OCI-applikationer — installationsprogrammet erbjuder bara de profiler som värden kan köra: NVIDIA-, Intel- och AMD-GPU:er och, för Frigate, en Intel NPU eller en Coral. NVIDIA Container Toolkit installeras med drivrutinen.",
"ociRecovery": "En OCI-applikation reser med sin säkerhetskopia — återställd på den här värden eller på en annan registreras och hanteras den igen, och i ett kluster startar den på den nod den flyttas till.",
@@ -5004,7 +5015,9 @@
"lxcTerminal": {
"title": "Terminal: {name} (ID: {id})",
"errors": {
"invalidVmid": "Ogiltig VMID — vägrar att köra pct enter"
"invalidVmid": "Ogiltig VMID — vägrar att köra pct enter",
"noShell": "Den här behållarens avbildning har inget skal, så den kan inte öppna en terminal. Dess utdata finns på fliken Loggar.",
"enterFailed": "Det gick inte att gå in i behållaren."
},
"commands": {
"listFiles": "Lista alla filer med detaljer",
@@ -5237,7 +5250,8 @@
"yes": "Ja",
"yourInput": "Din inmatning:",
"submit": "Skicka",
"ok": "OK"
"ok": "OK",
"endOfInput": "Slut på inmatning"
},
"apps": {
"searchPlaceholder": "Search app or CT…",
+9 -3
View File
@@ -3620,7 +3620,7 @@
"Memory optimizations removed": "Speicheroptimierungen entfernt",
"Memory restored.": "Speicher wiederhergestellt.",
"Memory settings optimized successfully": "Speichereinstellungen erfolgreich optimiert",
"Memory:": "Erinnerung:",
"Memory:": "Arbeitsspeicher:",
"Memos is a lightweight, self-hosted memo hub. Open Source and Free forever.": "Memos ist ein leichter, selbst gehosteter Memo-Hub. Open Source und Free für immer.",
"Messaging & Notifications": "Nachrichten und Benachrichtigungen",
"Messaging & Queues": "Messaging & Queues",
@@ -3817,6 +3817,7 @@
"NICs:": "Netzwerkkarten:",
"NOT FOUND": "NICHT GEFUNDEN",
"NOTE: The host directory and its contents will remain unchanged.": "HINWEIS: Das Hostverzeichnis und sein Inhalt bleiben unverändert.",
"NUT server user for clients such as PeaNUT": "NUT server-Benutzer für Clients wie PeaNUT",
"NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.": "NVENC-Patch erkannt – Liste eingegrenzt auf Versionen, die von keylase/nvidia-patch unterstützt werden.",
"NVIDIA (CUDA)": "NVIDIEN (CUDA)",
"NVIDIA (CUDA; official GPU image)": "NVIDIA (CUDA; offizielles GPU-Bild)",
@@ -3932,6 +3933,7 @@
"Network Restart Failed": "Netzwerkneustart fehlgeschlagen",
"Network Restarted": "Netzwerk neu gestartet",
"Network Tools": "Netzwerk-Tools",
"Network UPS Tools server for a UPS connected to the host by USB.": "Netzwerk-UPS-Tools-Server für eine USV, die über USB mit dem Host verbunden ist.",
"Network access:": "Netzwerkzugriff:",
"Network bridge": "Netzbrücke",
"Network configuration backed up": "Netzwerkkonfiguration gesichert",
@@ -4070,6 +4072,7 @@
"No Shares Found": "Keine Aktien gefunden",
"No Shares to Test": "Keine Freigaben zum Testen",
"No Storage Found": "Kein Speicher gefunden",
"No UPS was found on a USB port of this host. Connect it and start the installation again.": "An einem USB-Port dieses Hosts wurde keine USV gefunden. Schließen Sie sie an und starten Sie die Installation erneut.",
"No USB device was found on this host.": "Auf diesem Host wurde kein USB-Gerät gefunden.",
"No USB drives detected. Enter the mountpoint path manually:": "Keine USB-Laufwerke erkannt. Geben Sie den Mountpoint-Pfad manuell ein:",
"No USB drives mounted by ProxMenux yet. Mount one first to use it as a target.": "Noch keine USB-Laufwerke von ProxMenux gemountet. Montieren Sie zuerst eines, um es als Ziel zu verwenden.",
@@ -4314,8 +4317,8 @@
"OCI containers require Proxmox VE 9.1 or later.": "OCI-Container erfordern Proxmox VE 9.1 oder höher.",
"OCI management": "OCI-Verwaltung",
"OCI management could not be completed. Check the backend status; no additional cleanup has been authorized.": "Das OCI-Management konnte nicht abgeschlossen werden. Überprüfen Sie den Backend-Status; es wurde keine zusätzliche Bereinigung autorisiert.",
"OCI manager Apps (beta)": "OCI manager Apps (beta)",
"OCI manager Apps is a beta: if something does not work as expected, please report it on GitHub with the application name.": "OCI Manager Apps ist eine Beta: Wenn etwas nicht wie erwartet funktioniert, melden Sie es bitte auf GitHub mit dem Anwendungsnamen.",
"OCI manager Apps (Technology Preview)": "OCI manager Apps (Technology Preview)",
"OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps ist eine Technology Preview: Wenn etwas nicht wie erwartet funktioniert, melden Sie es bitte auf GitHub unter Angabe des Anwendungsnamens.",
"OCI metadata integrity mismatch": "OCI Unstimmigkeiten bei Metadaten",
"OCI metadata too large": "OCI-Metadaten zu groß",
"OCI stack management": "OCI-Stackmanagement",
@@ -4510,6 +4513,7 @@
"Pass /dev/video11 to the LXC": "Pass /dev/video11 zum LXC",
"Pass /dev/video12 to the LXC": "Pass /dev/video12 zum LXC",
"Pass a host device to the LXC": "Übergeben Sie ein Host-Gerät an den LXC",
"Pass the UPS to the LXC": "Übergeben Sie die USV an LXC",
"Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "Durch die Übergabe von NVIDIA wird keine Beschleunigung innerhalb der Anwendung aktiviert. Der Host benötigt NVIDIA Container Toolkit und ein kompatibles Image. Weitermachen?",
"Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Passphrase, die zum Entsperren der importierten Schlüsseldatei verwendet wird (leer lassen, wenn die Schlüsseldatei unverschlüsselt ist / kdf=none):",
"Passphrases do not match.": "Passphrasen stimmen nicht überein.",
@@ -4535,6 +4539,7 @@
"Password of the LibreDB Studio administrator": "Passwort des LibreDB Studio-Administrators",
"Password of the MariaDB user": "Passwort des MariaDB-Benutzers",
"Password of the MineOS web interface user": "Kennwort des MineOS Web Interface User",
"Password of the NUT server user": "Passwort des NUT server-Benutzers",
"Password of the NetBox admin account": "Passwort des NetBox Admin Accounts",
"Password of the OpenList admin user": "Passwort des OpenList-Admin-Benutzers",
"Password of the PhotoPrism admin user (at least 8 characters)": "Passwort des PhotoPrism-Admin-Benutzers (mindestens 8 Zeichen)",
@@ -6860,6 +6865,7 @@
"USB Accelerators:": "USB-Beschleuniger:",
"USB bus directory": "USB-Busverzeichnis",
"USB bus numbers can change after reconnecting or rebooting.": "USB-Busnummern können sich nach erneutem Anschließen oder Neustart ändern.",
"USB device of the UPS": "USB-Gerät der USV",
"USB device to attach": "USB-Gerät zum Anschließen",
"USB disk target": "USB-Festplattenziel",
"USB drives mounted now:": "Jetzt gemountete USB-Laufwerke:",
+8 -2
View File
@@ -3813,6 +3813,7 @@
"NICs:": "NIC:",
"NOT FOUND": "NO ENCONTRADO",
"NOTE: The host directory and its contents will remain unchanged.": "NOTA: El directorio del host y su contenido permanecerán sin cambios.",
"NUT server user for clients such as PeaNUT": "usuario NUT server para clientes como PeaNUT",
"NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.": "Parche NVENC detectado: lista reducida a versiones compatibles con keylase/nvidia-patch.",
"NVIDIA (CUDA)": "NVIDIA (CUDA)",
"NVIDIA (CUDA; official GPU image)": "NVIDIA (CUDA; imagen oficial para GPU)",
@@ -3928,6 +3929,7 @@
"Network Restart Failed": "Error al reiniciar la red",
"Network Restarted": "Red reiniciada",
"Network Tools": "Herramientas de red",
"Network UPS Tools server for a UPS connected to the host by USB.": "Servidor de red UPS Tools para un UPS conectado al host mediante USB.",
"Network access:": "Acceso a la red:",
"Network bridge": "Puente de red",
"Network configuration backed up": "Backup de la configuración de red creado",
@@ -4066,6 +4068,7 @@
"No Shares Found": "No se encontraron recursos compartidos",
"No Shares to Test": "Sin recursos compartidos que probar",
"No Storage Found": "No se encontró almacenamiento",
"No UPS was found on a USB port of this host. Connect it and start the installation again.": "No se encontró ningún SAI en los puertos USB de este host. Conéctelo y vuelva a iniciar la instalación.",
"No USB drives detected. Enter the mountpoint path manually:": "No se detectaron unidades USB.Ingrese la ruta del punto de montaje manualmente:",
"No USB drives mounted by ProxMenux yet. Mount one first to use it as a target.": "Aún no hay unidades USB montadas por ProxMenux. Monte una primero para usarla como destino.",
"No UUP folder found.": "No se encontró ninguna carpeta UUP.",
@@ -4306,8 +4309,8 @@
"OCI containers require Proxmox VE 9.1 or later.": "Los contenedores OCI requieren Proxmox VE 9.1 o posterior.",
"OCI management": "Gestión OCI",
"OCI management could not be completed. Check the backend status; no additional cleanup has been authorized.": "La gestión OCI no pudo completarse. Comprueba el estado del backend; no se ha autorizado ninguna limpieza adicional.",
"OCI manager Apps (beta)": "OCI manager Apps (beta)",
"OCI manager Apps is a beta: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps es una beta: si algo no funciona como esperabas, repórtalo en GitHub con el nombre de la aplicación.",
"OCI manager Apps (Technology Preview)": "OCI manager Apps (Technology Preview)",
"OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps es una Technology Preview: si algo no funciona como esperabas, repórtalo en GitHub con el nombre de la aplicación.",
"OCI metadata integrity mismatch": "Desigualdad de la integridad de los metadatos de la OCI",
"OCI metadata too large": "OCI metadatos demasiado grandes",
"OCI stack management": "Gestión de pilas OCI",
@@ -4502,6 +4505,7 @@
"Pass /dev/video11 to the LXC": "Pasar /dev/video11 al LXC",
"Pass /dev/video12 to the LXC": "Pasar /dev/video12 al LXC",
"Pass a host device to the LXC": "Pasar un dispositivo del host al LXC",
"Pass the UPS to the LXC": "Pase el UPS al LXC",
"Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "Pasar NVIDIA no habilita la aceleración dentro de la aplicación. El host necesita NVIDIA Container Toolkit y una imagen compatible.¿Continuar?",
"Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Frase de contraseña utilizada para desbloquear el archivo de claves importado (déjelo en blanco si el archivo de claves no está cifrado/kdf=none):",
"Passphrases do not match.": "Las frases de contraseña no coinciden.",
@@ -4527,6 +4531,7 @@
"Password of the LibreDB Studio administrator": "Contraseña del administrador de LibreDB Studio",
"Password of the MariaDB user": "Contraseña del usuario de MariaDB",
"Password of the MineOS web interface user": "Contraseña del usuario de la interfaz web de MineOS",
"Password of the NUT server user": "Contraseña del usuario NUT server",
"Password of the NetBox admin account": "Contraseña de la cuenta de administrador de NetBox",
"Password of the OpenList admin user": "Contraseña del usuario administrador de OpenList",
"Password of the PhotoPrism admin user (at least 8 characters)": "Contraseña del usuario administrador de PhotoPrism (al menos 8 caracteres)",
@@ -6835,6 +6840,7 @@
"USB Accelerators:": "Aceleradores USB:",
"USB bus directory": "Directorio del bus USB",
"USB bus numbers can change after reconnecting or rebooting.": "Los números de bus USB pueden cambiar al volver a conectar el dispositivo o al reiniciar.",
"USB device of the UPS": "dispositivo USB del UPS",
"USB device to attach": "Dispositivo USB que añadir",
"USB disk target": "Destino de disco USB",
"USB drives mounted now:": "Unidades USB montadas ahora:",
+8 -2
View File
@@ -3817,6 +3817,7 @@
"NICs:": "Cartes réseau :",
"NOT FOUND": "NON TROUVÉ",
"NOTE: The host directory and its contents will remain unchanged.": "REMARQUE : Le répertoire hôte et son contenu resteront inchangés.",
"NUT server user for clients such as PeaNUT": "utilisateur NUT server pour des clients tels que PeaNUT",
"NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.": "Correctif NVENC détecté — liste restreinte aux versions prises en charge par keylase/nvidia-patch.",
"NVIDIA (CUDA)": "NVIDIA (CUDA)",
"NVIDIA (CUDA; official GPU image)": "NVIDIA (CUDA; image officielle du GPU)",
@@ -3932,6 +3933,7 @@
"Network Restart Failed": "Échec du redémarrage du réseau",
"Network Restarted": "Réseau redémarré",
"Network Tools": "Outils réseau",
"Network UPS Tools server for a UPS connected to the host by USB.": "Serveur Network UPS Tools pour un UPS connecté à l'hôte par USB.",
"Network access:": "Accès au réseau :",
"Network bridge": "Pont réseau",
"Network configuration backed up": "Configuration réseau sauvegardée",
@@ -4070,6 +4072,7 @@
"No Shares Found": "Aucun partage trouvé",
"No Shares to Test": "aucun partage à tester",
"No Storage Found": "Aucun stockage trouvé",
"No UPS was found on a USB port of this host. Connect it and start the installation again.": "Aucun onduleur n'a été trouvé sur un port USB de cet hôte. Branchez-le et relancez l'installation.",
"No USB device was found on this host.": "Aucun périphérique USB n'a été trouvé sur cet hôte.",
"No USB drives detected. Enter the mountpoint path manually:": "Aucune clé USB détectée. Saisissez manuellement le chemin du point de montage :",
"No USB drives mounted by ProxMenux yet. Mount one first to use it as a target.": "Aucune clé USB montée par ProxMenux pour le moment. Montez-en un d’abord pour l’utiliser comme cible.",
@@ -4314,8 +4317,8 @@
"OCI containers require Proxmox VE 9.1 or later.": "Les conteneurs OCI nécessitent Proxmox VE 9.1 ou version ultérieure.",
"OCI management": "Gestion OCI",
"OCI management could not be completed. Check the backend status; no additional cleanup has been authorized.": "La gestion de OCI n'a pas pu être terminée. Vérifiez l'état du moteur; aucun nettoyage supplémentaire n'a été autorisé.",
"OCI manager Apps (beta)": "OCI manager Apps (beta)",
"OCI manager Apps is a beta: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps est une bêta : si quelque chose ne fonctionne pas comme prévu, veuillez le signaler sur GitHub avec le nom de l'application.",
"OCI manager Apps (Technology Preview)": "OCI manager Apps (Technology Preview)",
"OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps est une Technology Preview : si quelque chose ne fonctionne pas comme prévu, veuillez le signaler sur GitHub avec le nom de l'application.",
"OCI metadata integrity mismatch": "Inadéquation de l'intégrité des métadonnées OCI",
"OCI metadata too large": "Métadonnées OCI trop grandes",
"OCI stack management": "Gestion de la pile OCI",
@@ -4510,6 +4513,7 @@
"Pass /dev/video11 to the LXC": "Passez /dev/video11 au LXC",
"Pass /dev/video12 to the LXC": "Passer /dev/video12 au LXC",
"Pass a host device to the LXC": "Passer un appareil hôte au LXC",
"Pass the UPS to the LXC": "Passer l'UPS au LXC",
"Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "Passer NVIDIA n’active pas l’accélération dans l’application. L'hôte a besoin du NVIDIA Container Toolkit et d'une image compatible. Continuer?",
"Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Phrase secrète utilisée pour déverrouiller le fichier de clés importé (laisser vide si le fichier de clés n'est pas chiffré / kdf=aucun) :",
"Passphrases do not match.": "Les phrases secrètes ne correspondent pas.",
@@ -4535,6 +4539,7 @@
"Password of the LibreDB Studio administrator": "Mot de passe de l'administrateur LibreDB Studio",
"Password of the MariaDB user": "Mot de passe de l'utilisateur MariaDB",
"Password of the MineOS web interface user": "Mot de passe de l'utilisateur de l'interface web MineOS",
"Password of the NUT server user": "Mot de passe de l'utilisateur NUT server",
"Password of the NetBox admin account": "Mot de passe du compte admin NetBox",
"Password of the OpenList admin user": "Mot de passe de l'utilisateur administrateur OpenList",
"Password of the PhotoPrism admin user (at least 8 characters)": "Mot de passe de l'utilisateur administrateur PhotoPrism (au moins 8 caractères)",
@@ -6859,6 +6864,7 @@
"USB Accelerators:": "Accélérateurs USB :",
"USB bus directory": "Répertoire de bus USB",
"USB bus numbers can change after reconnecting or rebooting.": "Les numéros de bus USB peuvent changer après la reconnexion ou le redémarrage.",
"USB device of the UPS": "Périphérique USB de l'onduleur",
"USB device to attach": "Périphérique USB à connecter",
"USB disk target": "cible du disque USB",
"USB drives mounted now:": "Clés USB montées maintenant :",
+8 -2
View File
@@ -3817,6 +3817,7 @@
"NICs:": "NIC:",
"NOT FOUND": "NON TROVATO",
"NOTE: The host directory and its contents will remain unchanged.": "NOTA: la directory host e il suo contenuto rimarranno invariati.",
"NUT server user for clients such as PeaNUT": "utente NUT server per client come PeaNUT",
"NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.": "Rilevata patch NVENC: elenco ristretto alle versioni supportate da keylase/nvidia-patch.",
"NVIDIA (CUDA)": "NVIDIA (CUDA)",
"NVIDIA (CUDA; official GPU image)": "NVIDIA (CUDA; immagine GPU ufficiale)",
@@ -3932,6 +3933,7 @@
"Network Restart Failed": "Riavvio rete non riuscito",
"Network Restarted": "Rete riavviata",
"Network Tools": "Strumenti rete",
"Network UPS Tools server for a UPS connected to the host by USB.": "Server Network UPS Tools per un UPS collegato all'host tramite USB.",
"Network access:": "Accesso rete:",
"Network bridge": "Bridge di rete",
"Network configuration backed up": "Backup della configurazione di rete creato",
@@ -4070,6 +4072,7 @@
"No Shares Found": "Nessuna condivisione trovata",
"No Shares to Test": "Nessuna condivisione da testare",
"No Storage Found": "Nessuno storage trovato",
"No UPS was found on a USB port of this host. Connect it and start the installation again.": "Nessun UPS trovato su una porta USB di questo host. Collegatelo e riavviate l'installazione.",
"No USB device was found on this host.": "nessun dispositivo USB trovato su questo host.",
"No USB drives detected. Enter the mountpoint path manually:": "Nessuna unità USB rilevata. Inserisci manualmente il percorso del punto di montaggio:",
"No USB drives mounted by ProxMenux yet. Mount one first to use it as a target.": "Ancora nessuna unità USB montata da ProxMenux. Montane una prima per usarla come destinazione.",
@@ -4314,8 +4317,8 @@
"OCI containers require Proxmox VE 9.1 or later.": "I container OCI richiedono Proxmox VE 9.1 o versione successiva.",
"OCI management": "Gestione OCI",
"OCI management could not be completed. Check the backend status; no additional cleanup has been authorized.": "Impossibile completare la gestione OCI. Controlla lo stato del backend; non è stata autorizzata alcuna ulteriore pulizia.",
"OCI manager Apps (beta)": "OCI manager Apps (beta)",
"OCI manager Apps is a beta: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps è in versione beta: se qualcosa non funziona come previsto, segnalarlo su GitHub indicando il nome dell'applicazione.",
"OCI manager Apps (Technology Preview)": "OCI manager Apps (Technology Preview)",
"OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps è una Technology Preview: se qualcosa non funziona come previsto, segnalalo su GitHub con il nome dell'applicazione.",
"OCI metadata integrity mismatch": "Verifica integrità metadati OCI non superata",
"OCI metadata too large": "Metadati OCI troppo grandi",
"OCI stack management": "Gestione stack OCI",
@@ -4510,6 +4513,7 @@
"Pass /dev/video11 to the LXC": "Passa /dev/video11 al container LXC",
"Pass /dev/video12 to the LXC": "Collega /dev/video12 al container LXC",
"Pass a host device to the LXC": "Collega un dispositivo dell’host al container LXC",
"Pass the UPS to the LXC": "passa l'UPS al LXC",
"Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "il passaggio di NVIDIA non abilita l'accelerazione all'interno dell'applicazione. L'host necessita di NVIDIA Container Toolkit e di un'immagine compatibile. Continuare?",
"Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Passphrase per sbloccare il file di chiavi importato (lascia vuoto se non è crittografato / kdf=none):",
"Passphrases do not match.": "Le passphrase non corrispondono.",
@@ -4535,6 +4539,7 @@
"Password of the LibreDB Studio administrator": "Password amministratore LibreDB Studio",
"Password of the MariaDB user": "Password dell'utente MariaDB",
"Password of the MineOS web interface user": "Password utente interfaccia web MineOS",
"Password of the NUT server user": "Password dell'utente NUT server",
"Password of the NetBox admin account": "Password account amministratore NetBox",
"Password of the OpenList admin user": "Password amministratore OpenList",
"Password of the PhotoPrism admin user (at least 8 characters)": "Password amministratore PhotoPrism (almeno 8 caratteri)",
@@ -6860,6 +6865,7 @@
"USB Accelerators:": "Acceleratori USB:",
"USB bus directory": "Directory bus USB",
"USB bus numbers can change after reconnecting or rebooting.": "I numeri del bus USB possono cambiare dopo una riconnessione o un riavvio.",
"USB device of the UPS": "Dispositivo USB dell'UPS",
"USB device to attach": "Dispositivo USB da collegare",
"USB disk target": "Destinazione disco USB",
"USB drives mounted now:": "Unità USB montate ora:",
+8 -2
View File
@@ -3817,6 +3817,7 @@
"NICs:": "Placas de rede:",
"NOT FOUND": "NÃO ENCONTRADO",
"NOTE: The host directory and its contents will remain unchanged.": "NOTA: O diretório host e seu conteúdo permanecerão inalterados.",
"NUT server user for clients such as PeaNUT": "usuário NUT server para clientes como PeaNUT",
"NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.": "Patch NVENC detectado — lista reduzida às versões suportadas por keylase/nvidia-patch.",
"NVIDIA (CUDA)": "NVIDIA (CUDA)",
"NVIDIA (CUDA; official GPU image)": "NVIDIA (CUDA; imagem oficial da GPU)",
@@ -3932,6 +3933,7 @@
"Network Restart Failed": "Falha ao reiniciar a rede",
"Network Restarted": "Rede reiniciada",
"Network Tools": "Ferramentas de rede",
"Network UPS Tools server for a UPS connected to the host by USB.": "Servidor Network UPS Tools para um UPS conectado ao host por USB.",
"Network access:": "Acesso à rede:",
"Network bridge": "Ponte de rede",
"Network configuration backed up": "Configuração de rede com backup",
@@ -4070,6 +4072,7 @@
"No Shares Found": "Nenhum compartilhamento encontrado",
"No Shares to Test": "Sem compartilhamentos para testar",
"No Storage Found": "Nenhum armazenamento encontrado",
"No UPS was found on a USB port of this host. Connect it and start the installation again.": "Nenhum UPS foi encontrado em uma porta USB deste host. Conecte-o e reinicie a instalação.",
"No USB device was found on this host.": "Nenhum dispositivo USB foi encontrado neste host.",
"No USB drives detected. Enter the mountpoint path manually:": "Nenhuma unidade USB detectada. Insira o caminho do ponto de montagem manualmente:",
"No USB drives mounted by ProxMenux yet. Mount one first to use it as a target.": "Ainda não há unidades USB montadas pelo ProxMenux. Monte um primeiro para usá-lo como alvo.",
@@ -4314,8 +4317,8 @@
"OCI containers require Proxmox VE 9.1 or later.": "Os contêineres OCI requerem Proxmox VE 9.1 ou posterior.",
"OCI management": "Gestão de OCI",
"OCI management could not be completed. Check the backend status; no additional cleanup has been authorized.": "A gestão do OCI não pôde ser concluída. Verifique o estado da infra- estrutura; nenhuma limpeza adicional foi autorizada.",
"OCI manager Apps (beta)": "OCI manager Apps (beta)",
"OCI manager Apps is a beta: if something does not work as expected, please report it on GitHub with the application name.": "Gerenciador de OCI Apps é um beta: se algo não funcionar como esperado, por favor, relate no GitHub com o nome da aplicação.",
"OCI manager Apps (Technology Preview)": "OCI manager Apps (Technology Preview)",
"OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps é uma Technology Preview: se algo não funcionar conforme o esperado, informe no GitHub com o nome do aplicativo.",
"OCI metadata integrity mismatch": "Incompatibilidade da integridade dos metadados OCI",
"OCI metadata too large": "Metadados OCI demasiado grandes",
"OCI stack management": "Gestão de pilhas OCI",
@@ -4510,6 +4513,7 @@
"Pass /dev/video11 to the LXC": "Passe /dev/video11 para o LXC",
"Pass /dev/video12 to the LXC": "Passe /dev/video12 para o LXC",
"Pass a host device to the LXC": "Passe um dispositivo da máquina para o LXC",
"Pass the UPS to the LXC": "Passe o UPS para o LXC",
"Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "Passar NVIDIA não habilita a aceleração dentro do aplicativo. O host precisa do NVIDIA Container Toolkit e de uma imagem compatível. Continuar?",
"Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Senha usada para desbloquear o arquivo-chave importado (deixe em branco se o arquivo-chave não estiver criptografado / kdf=none):",
"Passphrases do not match.": "As senhas não correspondem.",
@@ -4535,6 +4539,7 @@
"Password of the LibreDB Studio administrator": "Senha do administrador do LibreDB Studio",
"Password of the MariaDB user": "Senha do usuário MariaDB",
"Password of the MineOS web interface user": "Senha do utilizador da interface Web MineOS",
"Password of the NUT server user": "Senha do usuário NUT server",
"Password of the NetBox admin account": "Senha da conta de administração NetBox",
"Password of the OpenList admin user": "Senha do usuário administrador do OpenList",
"Password of the PhotoPrism admin user (at least 8 characters)": "Senha do usuário administrador do PhotoPrism (pelo menos 8 caracteres)",
@@ -6860,6 +6865,7 @@
"USB Accelerators:": "Aceleradores USB:",
"USB bus directory": "Directório do barramento USB",
"USB bus numbers can change after reconnecting or rebooting.": "Os números do barramento USB podem mudar após reconectar ou reiniciar.",
"USB device of the UPS": "Dispositivo USB do UPS",
"USB device to attach": "dispositivo USB para conectar",
"USB disk target": "destino de disco USB",
"USB drives mounted now:": "unidades USB montadas agora:",
+8
View File
@@ -3817,6 +3817,7 @@
"NICs:": "Sieťové karty:",
"NOT FOUND": "NENÁJDENÉ",
"NOTE: The host directory and its contents will remain unchanged.": "POZNÁMKA: priečinok na hostovi ani jeho obsah sa nezmenia.",
"NUT server user for clients such as PeaNUT": "Používateľ NUT server pre klientov ako PeaNUT",
"NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.": "Zistil sa NVENC patch - zoznam je zúžený na verzie podporované cez keylase/nvidia-patch.",
"NVIDIA (CUDA)": "NVIDIA (CUDA)",
"NVIDIA (CUDA; official GPU image)": "NVIDIA (CUDA; oficiálny obraz GPU)",
@@ -3932,6 +3933,7 @@
"Network Restart Failed": "Reštart siete zlyhal",
"Network Restarted": "Sieť bola reštartovaná",
"Network Tools": "Sieťové nástroje",
"Network UPS Tools server for a UPS connected to the host by USB.": "Sieťový server UPS Tools pre UPS pripojený k hostiteľovi cez USB.",
"Network access:": "Sieťový prístup:",
"Network bridge": "Sieťový most",
"Network configuration backed up": "Nastavenie siete bolo zálohované",
@@ -4070,6 +4072,7 @@
"No Shares Found": "Nenašli sa žiadne zdieľania",
"No Shares to Test": "Žiadne zdieľania na testovanie",
"No Storage Found": "Nenašlo sa žiadne úložisko",
"No UPS was found on a USB port of this host. Connect it and start the installation again.": "Na USB porte tohto hostiteľa sa nenašiel žiadny UPS. Pripojte ho a spustite inštaláciu znova.",
"No USB device was found on this host.": "Na tomto hostiteľovi sa nenašlo žiadne zariadenie USB.",
"No USB drives detected. Enter the mountpoint path manually:": "Nenašli sa žiadne USB disky. Zadajte cestu mount pointu ručne:",
"No USB drives mounted by ProxMenux yet. Mount one first to use it as a target.": "ProxMenux zatiaľ nepripojil žiadny USB disk. Najprv jeden pripojte, aby sa dal použiť ako cieľ.",
@@ -4314,7 +4317,9 @@
"OCI containers require Proxmox VE 9.1 or later.": "OCI kontajnery vyžadujú Proxmox VE 9.1 alebo novší.",
"OCI management": "Správa OCI aplikácií",
"OCI management could not be completed. Check the backend status; no additional cleanup has been authorized.": "Správu OCI aplikácií nebolo možné dokončiť. Skontrolujte stav backendu; žiadne ďalšie vyčistenie nebolo povolené.",
"OCI manager Apps (Technology Preview)": "Správca OCI aplikácií (Technology Preview)",
"OCI manager Apps (beta)": "Správca OCI aplikácií (beta)",
"OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps je Technology Preview: ak niečo nefunguje podľa očakávania, nahláste to na GitHub s názvom aplikácie.",
"OCI manager Apps is a beta: if something does not work as expected, please report it on GitHub with the application name.": "Správca OCI aplikácií je beta: ak niečo nefunguje podľa očakávania, nahláste to na GitHub spolu s názvom aplikácie.",
"OCI metadata integrity mismatch": "Nepomer integrity metaúdajov OCI",
"OCI metadata too large": "Metaúdaje OCI príliš veľké",
@@ -4510,6 +4515,7 @@
"Pass /dev/video11 to the LXC": "Prešiel /dev/video11 na LXC",
"Pass /dev/video12 to the LXC": "Prechod /dev/video12 na LXC",
"Pass a host device to the LXC": "Posuňte hostiteľské zariadenie na LXC",
"Pass the UPS to the LXC": "Pošlite UPS do LXC",
"Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "Prejdenie NVIDIA neumožní zrýchlenie v aplikácii. Hostiteľ potrebuje NVIDIA Container Toolkit a kompatibilný obrázok. Pokračovať?",
"Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Fráza na odomknutie importovaného keyfile (nechajte prázdne, ak keyfile nie je šifrovaný / kdf=none):",
"Passphrases do not match.": "Frázy sa nezhodujú.",
@@ -4535,6 +4541,7 @@
"Password of the LibreDB Studio administrator": "Heslo správcu LibreDB Studio",
"Password of the MariaDB user": "Heslo používateľa MariaDB",
"Password of the MineOS web interface user": "Heslo používateľa webového rozhrania MineOS",
"Password of the NUT server user": "Heslo používateľa NUT server",
"Password of the NetBox admin account": "Heslo admin účtu NetBox",
"Password of the OpenList admin user": "Heslo užívateľa OpenList admin",
"Password of the PhotoPrism admin user (at least 8 characters)": "Heslo PhotoPrism admin užívateľa (aspoň 8 znakov)",
@@ -6859,6 +6866,7 @@
"USB Accelerators:": "USB akcelerátory:",
"USB bus directory": "Adresár USB zbernice",
"USB bus numbers can change after reconnecting or rebooting.": "Čísla zbernice USB sa môžu zmeniť po opätovnom pripojení alebo reštarte.",
"USB device of the UPS": "USB zariadenie UPS",
"USB device to attach": "Zariadenie USB na pripojenie",
"USB disk target": "Cieľ na USB disku",
"USB drives mounted now:": "Aktuálne pripojené USB disky:",
+8 -2
View File
@@ -3817,6 +3817,7 @@
"NICs:": "NIC:er:",
"NOT FOUND": "HITTADE INTE",
"NOTE: The host directory and its contents will remain unchanged.": "OBS: Värdkatalogen och dess innehåll förblir oförändrade.",
"NUT server user for clients such as PeaNUT": "NUT server användare för klienter som PeaNUT",
"NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.": "NVENC-patch upptäckt — listan minskad till versioner som stöds av keylase/nvidia-patch.",
"NVIDIA (CUDA)": "NVIDIA (CUDA)",
"NVIDIA (CUDA; official GPU image)": "NVIDIA (CUDA, officiell GPU-bild)",
@@ -3932,6 +3933,7 @@
"Network Restart Failed": "Omstart av nätverket misslyckades",
"Network Restarted": "Nätverket har startat om",
"Network Tools": "Nätverksverktyg",
"Network UPS Tools server for a UPS connected to the host by USB.": "Network UPS Tools-server för en UPS ansluten till värden via USB.",
"Network access:": "Nätverksåtkomst:",
"Network bridge": "Nätverksbro",
"Network configuration backed up": "Nätverkskonfiguration säkerhetskopierad",
@@ -4070,6 +4072,7 @@
"No Shares Found": "Inga aktier hittades",
"No Shares to Test": "Inga aktier att testa",
"No Storage Found": "Ingen lagring hittades",
"No UPS was found on a USB port of this host. Connect it and start the installation again.": "Ingen UPS hittades på en USB-port på denna värd. Anslut den och starta installationen igen.",
"No USB device was found on this host.": "Ingen USB-enhet hittades på denna värd.",
"No USB drives detected. Enter the mountpoint path manually:": "Inga USB-enheter upptäcktes. Ange monteringspunktens sökväg manuellt:",
"No USB drives mounted by ProxMenux yet. Mount one first to use it as a target.": "Inga USB-enheter monterade av ProxMenux ännu. Montera en först för att använda den som mål.",
@@ -4314,8 +4317,8 @@
"OCI containers require Proxmox VE 9.1 or later.": "OCI-behållare kräver Proxmox VE 9.1 eller senare.",
"OCI management": "OCI management",
"OCI management could not be completed. Check the backend status; no additional cleanup has been authorized.": "OCI-hanteringen kunde inte slutföras. Kontrollera backend status; ingen ytterligare rengöring har godkänts.",
"OCI manager Apps (beta)": "OCI manager Apps (beta)",
"OCI manager Apps is a beta: if something does not work as expected, please report it on GitHub with the application name.": "OCI Manager Apps är en beta: om något inte fungerar som förväntat, vänligen rapportera det på GitHub med applikationsnamnet.",
"OCI manager Apps (Technology Preview)": "OCI manager Apps (Technology Preview)",
"OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps är en Technology Preview: om något inte fungerar som förväntat, rapportera det på GitHub med applikationsnamnet.",
"OCI metadata integrity mismatch": "OCI metadata integritet mismatch",
"OCI metadata too large": "OCI metadata för stor",
"OCI stack management": "OCI stackhantering",
@@ -4510,6 +4513,7 @@
"Pass /dev/video11 to the LXC": "Pass /dev/video11 till LXC",
"Pass /dev/video12 to the LXC": "Pass /dev/video12 till LXC",
"Pass a host device to the LXC": "Passera en värdenhet till LXC",
"Pass the UPS to the LXC": "Skicka UPS:en till LXC",
"Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "Att passera NVIDIA möjliggör inte acceleration inuti applikationen. Värden behöver NVIDIA Container Toolkit och en kompatibel bild. Fortsätta?",
"Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Lösenfras som används för att låsa upp den importerade nyckelfilen (lämna tomt om nyckelfilen är okrypterad / kdf=ingen):",
"Passphrases do not match.": "Lösenfraser matchar inte.",
@@ -4535,6 +4539,7 @@
"Password of the LibreDB Studio administrator": "Lösenord för LibreDB Studio-administratören",
"Password of the MariaDB user": "Lösenordet för MariaDB-användaren",
"Password of the MineOS web interface user": "Lösenord för MineOS webbgränssnitt användare",
"Password of the NUT server user": "Lösenordet för NUT server-användaren",
"Password of the NetBox admin account": "Lösenord för NetBox admin konto",
"Password of the OpenList admin user": "Lösenord för OpenList admin användaren",
"Password of the PhotoPrism admin user (at least 8 characters)": "Lösenord för PhotoPrism-administratören (minst 8 tecken)",
@@ -6860,6 +6865,7 @@
"USB Accelerators:": "USB-acceleratorer:",
"USB bus directory": "USB buss katalog",
"USB bus numbers can change after reconnecting or rebooting.": "USB-bussnummer kan ändras efter återanslutning eller omstart.",
"USB device of the UPS": "UPS:ens USB-enhet",
"USB device to attach": "USB-enhet att ansluta",
"USB disk target": "USB-diskmål",
"USB drives mounted now:": "USB-enheter monterade nu:",
+1 -1
View File
@@ -2,7 +2,7 @@
<!-- Generated from oci/catalog/verification.json by .github/scripts/oci_validation.py. Do not edit by hand. -->
43 of 334 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 2 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
43 of 335 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 2 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
Each test describes the scenario that was run and names the image it ran on. It does not cover every possible configuration of the application, and a newer image has not been tested until someone reports it.
+469
View File
@@ -0,0 +1,469 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-nut-server",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "NUT Server"
},
"tagline": {
"en_US": "Network UPS Tools server for a UPS connected to the host by USB."
},
"description": {
"en_US": "Network UPS Tools server (upsd) with the driver of a UPS connected to a USB port of the host. It reads the battery, load and power state of the UPS and serves them on port 3493 to NUT clients: PeaNUT, the Proxmox host and any other machine that has to shut down when the power fails.\n\n**Before installing:**\n- Connect the UPS to a USB port of the Proxmox host. The installation passes it to the container.\n- Clients connect with the user upsmon and the password shown at the end of the installation.\n- The Proxmox host is not shut down by this server on its own: it needs its own NUT client (upsmon) pointing to it.\n\n**Learn More:**\n- [Network UPS Tools](https://networkupstools.org/)\n- [Image documentation](https://github.com/instantlinux/docker-tools/tree/main/images/nut-upsd)"
},
"category": "monitoring",
"category_label": "Monitoring & Analytics",
"author": "instantlinux",
"developer": "Network UPS Tools",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/network-ups-tools.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": null,
"path": "/"
},
"website": "https://networkupstools.org/",
"documentation": "https://github.com/instantlinux/docker-tools/tree/main/images/nut-upsd",
"repository": "https://github.com/instantlinux/docker-tools",
"tips": [],
"mini_changelog": [],
"display_version": null,
"updated_at": null
},
"source": {
"provider": "instantlinux",
"repository": "https://github.com/instantlinux/docker-tools",
"revision": "3d3e17dc5dafb3f544ce0bdaa5ea4cf3814d5b1cb5b6323af2c40d1b224c5fd9",
"image_repository_url": "https://hub.docker.com/r/instantlinux/nut-upsd",
"readme_pushed_at": "",
"compose_sha256": "3d3e17dc5dafb3f544ce0bdaa5ea4cf3814d5b1cb5b6323af2c40d1b224c5fd9",
"generated_at": "2026-10-15T17:50:24+00:00"
},
"container_contract": {
"service_name": "nut-upsd",
"container_name": "nut-upsd",
"image": {
"reference": "instantlinux/nut-upsd:latest",
"registry": "docker.io",
"repository": "instantlinux/nut-upsd",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [
{
"name": "NAME",
"example": "ups",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "DESCRIPTION",
"example": "UPS",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "DRIVER",
"example": "usbhid-ups",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "API_USER",
"example": "upsmon",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "API_PASSWORD",
"example": "",
"required": true,
"sensitive": true,
"source": "docker-compose",
"prompt": "Password of the NUT server user"
}
],
"volumes": [],
"ports": [
{
"container_port": 3493,
"published_example": 3493,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "services:\n nut-upsd:\n image: instantlinux/nut-upsd:latest\n container_name: nut-upsd\n restart: unless-stopped\n environment:\n NAME: ups\n DESCRIPTION: UPS\n DRIVER: usbhid-ups\n API_USER: upsmon\n API_PASSWORD: ${GENERATED_API_PASSWORD}\n ports:\n - 3493:3493\n devices:\n - /dev/bus/usb/001/001:/dev/bus/usb/001/001\n"
},
"compose_stack": {
"project_name": "nut-upsd",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "nut-upsd",
"service_count": 1,
"services": [
{
"name": "nut-upsd",
"image": "instantlinux/nut-upsd:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"image": "instantlinux/nut-upsd:latest",
"container_name": "nut-upsd",
"restart": "unless-stopped",
"environment": {
"NAME": "ups",
"DESCRIPTION": "UPS",
"DRIVER": "usbhid-ups",
"API_USER": "upsmon",
"API_PASSWORD": "${GENERATED_API_PASSWORD}"
},
"ports": [
"3493:3493"
],
"devices": [
"/dev/bus/usb/001/001:/dev/bus/usb/001/001"
]
}
}
],
"top_level": {},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": false,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"nut-upsd"
],
"stop_order": [
"nut-upsd"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": [
{
"id": "api-password",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "nut-upsd",
"environment_variable": "API_PASSWORD"
}
]
}
]
}
},
"first_run": {
"endpoints": [],
"credentials": [
{
"label": "NUT server user for clients such as PeaNUT",
"type": "configured-or-installer-generated",
"username": "upsmon",
"password": null,
"password_environment": "API_PASSWORD",
"change_required": false,
"source": "image-documentation",
"retrieval": null
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 1,
"memory_mb": 256,
"swap_mb": 256,
"rootfs_size_gb": 4,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": true,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"installer_profile": {
"device_requests": [
{
"id": "ups-usb",
"kind": "character-device",
"purpose": "ups",
"enable_prompt": "Pass the UPS to the LXC",
"enabled_default": true,
"required_for_runtime": true,
"required_by_compose": true,
"path_prompt": "USB device of the UPS",
"host_path_default": "/dev/bus/usb/001/001",
"container_path_strategy": "same-as-host",
"mode": "0666",
"deny_write": false,
"gid_strategy": "host-device-gid",
"source_mapping": "/dev/bus/usb/001/001:/dev/bus/usb/001/001"
}
],
"generated_sensitive_environment": {
"API_PASSWORD": {
"strategy": "token-hex",
"bytes": 12,
"prompt": true
}
}
},
"adaptations": [
{
"id": "imported-compose-source",
"upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
"native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
"reason": "Catalog import must not imply runtime compatibility.",
"behavioral_impact": "No automatic installation before review.",
"validation": "pending-per-application"
},
{
"id": "rolling-latest-image",
"upstream_behavior": "A discovered Compose may pin a release tag or digest.",
"native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
"reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
"behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
"validation": "pending-per-application"
},
{
"id": "dedicated-lxc-network",
"upstream_behavior": "Docker publishes selected container ports on the Docker host.",
"native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
"reason": "A native LXC has its own address and does not require Docker port NAT.",
"behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
"validation": "pending-per-application"
},
{
"id": "compose-shm-size",
"upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
"native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
"reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
"behavioral_impact": "None expected.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-command",
"upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
"native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
"reason": "Proxmox stores the effective OCI process as one entrypoint string.",
"behavioral_impact": "None expected.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-privileged-mode",
"upstream_behavior": "Compose selects whether the container runs in privileged mode.",
"native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
"reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
"behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
"validation": "native-equivalent"
},
{
"id": "compose-process-runtime",
"upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
"native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
"reason": "The OCI process must start with the same identity, command and working directory without Docker.",
"behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-healthcheck",
"upstream_behavior": "Docker periodically executes the declared container healthcheck.",
"native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
"reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
"behavioral_impact": "The check runs during installation rather than continuously after installation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-cpu-priority",
"upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
"native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
"reason": "Both settings express relative CPU priority on different scales.",
"behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-resource-limits",
"upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
"native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
"reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
"behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-network-identity",
"upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
"native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
"reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
"behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-network-mode",
"upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
"native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
"reason": "The LXC is the application host and already has its own address and port namespace.",
"behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-capabilities-and-sysctls",
"upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
"native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
"reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
"behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
"validation": "not-requested-by-compose"
},
{
"id": "docker-engine-metadata",
"upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
"native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
"reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
"behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-device-passthrough",
"upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
"native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
"reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
"behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
"validation": "pending-per-application"
},
{
"id": "compose-host-ipc",
"upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
"native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
"reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
"behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
"validation": "not-requested-by-compose"
}
]
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
},
"validation": {
"schema": "passed-at-generation",
"clean_install": "pending",
"service_health": "pending",
"restart_persistence": "pending",
"backup_restore": "pending",
"update_preserves_data": "pending"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false
}
}
+469
View File
@@ -0,0 +1,469 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-nut-server",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "NUT Server"
},
"tagline": {
"en_US": "Network UPS Tools server for a UPS connected to the host by USB."
},
"description": {
"en_US": "Network UPS Tools server (upsd) with the driver of a UPS connected to a USB port of the host. It reads the battery, load and power state of the UPS and serves them on port 3493 to NUT clients: PeaNUT, the Proxmox host and any other machine that has to shut down when the power fails.\n\n**Before installing:**\n- Connect the UPS to a USB port of the Proxmox host. The installation passes it to the container.\n- Clients connect with the user upsmon and the password shown at the end of the installation.\n- The Proxmox host is not shut down by this server on its own: it needs its own NUT client (upsmon) pointing to it.\n\n**Learn More:**\n- [Network UPS Tools](https://networkupstools.org/)\n- [Image documentation](https://github.com/instantlinux/docker-tools/tree/main/images/nut-upsd)"
},
"category": "monitoring",
"category_label": "Monitoring & Analytics",
"author": "instantlinux",
"developer": "Network UPS Tools",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/network-ups-tools.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": null,
"path": "/"
},
"website": "https://networkupstools.org/",
"documentation": "https://github.com/instantlinux/docker-tools/tree/main/images/nut-upsd",
"repository": "https://github.com/instantlinux/docker-tools",
"tips": [],
"mini_changelog": [],
"display_version": null,
"updated_at": null
},
"source": {
"provider": "instantlinux",
"repository": "https://github.com/instantlinux/docker-tools",
"revision": "3d3e17dc5dafb3f544ce0bdaa5ea4cf3814d5b1cb5b6323af2c40d1b224c5fd9",
"image_repository_url": "https://hub.docker.com/r/instantlinux/nut-upsd",
"readme_pushed_at": "",
"compose_sha256": "3d3e17dc5dafb3f544ce0bdaa5ea4cf3814d5b1cb5b6323af2c40d1b224c5fd9",
"generated_at": "2026-10-15T17:50:24+00:00"
},
"container_contract": {
"service_name": "nut-upsd",
"container_name": "nut-upsd",
"image": {
"reference": "instantlinux/nut-upsd:latest",
"registry": "docker.io",
"repository": "instantlinux/nut-upsd",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [
{
"name": "NAME",
"example": "ups",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "DESCRIPTION",
"example": "UPS",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "DRIVER",
"example": "usbhid-ups",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "API_USER",
"example": "upsmon",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "API_PASSWORD",
"example": "",
"required": true,
"sensitive": true,
"source": "docker-compose",
"prompt": "Password of the NUT server user"
}
],
"volumes": [],
"ports": [
{
"container_port": 3493,
"published_example": 3493,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "services:\n nut-upsd:\n image: instantlinux/nut-upsd:latest\n container_name: nut-upsd\n restart: unless-stopped\n environment:\n NAME: ups\n DESCRIPTION: UPS\n DRIVER: usbhid-ups\n API_USER: upsmon\n API_PASSWORD: ${GENERATED_API_PASSWORD}\n ports:\n - 3493:3493\n devices:\n - /dev/bus/usb/001/001:/dev/bus/usb/001/001\n"
},
"compose_stack": {
"project_name": "nut-upsd",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "nut-upsd",
"service_count": 1,
"services": [
{
"name": "nut-upsd",
"image": "instantlinux/nut-upsd:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"image": "instantlinux/nut-upsd:latest",
"container_name": "nut-upsd",
"restart": "unless-stopped",
"environment": {
"NAME": "ups",
"DESCRIPTION": "UPS",
"DRIVER": "usbhid-ups",
"API_USER": "upsmon",
"API_PASSWORD": "${GENERATED_API_PASSWORD}"
},
"ports": [
"3493:3493"
],
"devices": [
"/dev/bus/usb/001/001:/dev/bus/usb/001/001"
]
}
}
],
"top_level": {},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": false,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"nut-upsd"
],
"stop_order": [
"nut-upsd"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": [
{
"id": "api-password",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "nut-upsd",
"environment_variable": "API_PASSWORD"
}
]
}
]
}
},
"first_run": {
"endpoints": [],
"credentials": [
{
"label": "NUT server user for clients such as PeaNUT",
"type": "configured-or-installer-generated",
"username": "upsmon",
"password": null,
"password_environment": "API_PASSWORD",
"change_required": false,
"source": "image-documentation",
"retrieval": null
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 1,
"memory_mb": 256,
"swap_mb": 256,
"rootfs_size_gb": 4,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": true,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"installer_profile": {
"device_requests": [
{
"id": "ups-usb",
"kind": "character-device",
"purpose": "ups",
"enable_prompt": "Pass the UPS to the LXC",
"enabled_default": true,
"required_for_runtime": true,
"required_by_compose": true,
"path_prompt": "USB device of the UPS",
"host_path_default": "/dev/bus/usb/001/001",
"container_path_strategy": "same-as-host",
"mode": "0666",
"deny_write": false,
"gid_strategy": "host-device-gid",
"source_mapping": "/dev/bus/usb/001/001:/dev/bus/usb/001/001"
}
],
"generated_sensitive_environment": {
"API_PASSWORD": {
"strategy": "token-hex",
"bytes": 12,
"prompt": true
}
}
},
"adaptations": [
{
"id": "imported-compose-source",
"upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
"native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
"reason": "Catalog import must not imply runtime compatibility.",
"behavioral_impact": "No automatic installation before review.",
"validation": "pending-per-application"
},
{
"id": "rolling-latest-image",
"upstream_behavior": "A discovered Compose may pin a release tag or digest.",
"native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
"reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
"behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
"validation": "pending-per-application"
},
{
"id": "dedicated-lxc-network",
"upstream_behavior": "Docker publishes selected container ports on the Docker host.",
"native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
"reason": "A native LXC has its own address and does not require Docker port NAT.",
"behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
"validation": "pending-per-application"
},
{
"id": "compose-shm-size",
"upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
"native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
"reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
"behavioral_impact": "None expected.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-command",
"upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
"native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
"reason": "Proxmox stores the effective OCI process as one entrypoint string.",
"behavioral_impact": "None expected.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-privileged-mode",
"upstream_behavior": "Compose selects whether the container runs in privileged mode.",
"native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
"reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
"behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
"validation": "native-equivalent"
},
{
"id": "compose-process-runtime",
"upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
"native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
"reason": "The OCI process must start with the same identity, command and working directory without Docker.",
"behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-healthcheck",
"upstream_behavior": "Docker periodically executes the declared container healthcheck.",
"native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
"reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
"behavioral_impact": "The check runs during installation rather than continuously after installation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-cpu-priority",
"upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
"native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
"reason": "Both settings express relative CPU priority on different scales.",
"behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-resource-limits",
"upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
"native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
"reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
"behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-network-identity",
"upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
"native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
"reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
"behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-network-mode",
"upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
"native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
"reason": "The LXC is the application host and already has its own address and port namespace.",
"behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-capabilities-and-sysctls",
"upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
"native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
"reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
"behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
"validation": "not-requested-by-compose"
},
{
"id": "docker-engine-metadata",
"upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
"native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
"reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
"behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-device-passthrough",
"upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
"native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
"reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
"behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
"validation": "pending-per-application"
},
{
"id": "compose-host-ipc",
"upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
"native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
"reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
"behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
"validation": "not-requested-by-compose"
}
]
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
},
"validation": {
"schema": "passed-at-generation",
"clean_install": "pending",
"service_health": "pending",
"restart_persistence": "pending",
"backup_restore": "pending",
"update_preserves_data": "pending"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false
}
}
+2 -1
View File
@@ -12,6 +12,7 @@
"makemkv",
"mkvtoolnix",
"nextcloud-stack",
"nut-server",
"open-webui",
"open-webui-cuda",
"open-webui-ollama",
@@ -22,7 +23,7 @@
"stremio",
"tandoor"
],
"generated_count": 21,
"generated_count": 22,
"failed": [],
"failed_count": 0
}
+32
View File
@@ -7154,6 +7154,38 @@
"hidden": false,
"template_id": "image-node-red"
},
{
"id": "nut-server",
"provider": "instantlinux",
"template_family": "curated-profile",
"title": "NUT Server",
"repository": "https://github.com/instantlinux/docker-tools",
"description": "Network UPS Tools server for a UPS connected to the host by USB.",
"website": "https://networkupstools.org/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/network-ups-tools.webp",
"architectures": [
"amd64",
"arm64"
],
"updated_at": null,
"main_image": "instantlinux/nut-upsd:latest",
"category": "monitoring",
"category_label": "Monitoring & Analytics",
"replaces_discovered_ids": [],
"curated_path": "catalog/curated/nut-server.json",
"template": "apps/nut-server.json",
"template_status": "generated-unvalidated",
"automatic_install_candidate": true,
"untranslated_blockers": [],
"requires_privileged_lxc": false,
"optional_privileged_lxc": false,
"requires_host_pid_namespace": false,
"requires_relaxed_confinement": false,
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"hidden": false,
"template_id": "image-nut-server"
},
{
"id": "nzbfast",
"source_app_id": "nzbfast",
+6 -6
View File
@@ -29,7 +29,7 @@ from .installer import (
run_remote_install,
run_remote_rclone_mount,
)
from .ui import APP_TITLE, DETAIL_WINDOW_WIDTH, UserCancelled, interactive_ui, menu_size
from .ui import APP_TITLE, DETAIL_WINDOW_WIDTH, ReadableTags, UserCancelled, interactive_ui, menu_size
PROJECT_ROOT = Path(__file__).resolve().parents[2]
@@ -173,7 +173,7 @@ def _app_detail_text(catalog: Catalog, item: dict[str, Any], template: dict[str,
lines.append(f"{label + ':':<17} {value}")
row(translate("Source"), publisher(item))
# Only a real test is shown; OCI manager Apps is labelled beta as a whole.
# Only a real test is shown; OCI manager Apps is a Technology Preview as a whole.
if is_tested(item):
row(translate("Status"), translate("Verified"))
row(translate("Architectures"), _display_architectures(ui))
@@ -389,7 +389,7 @@ def _print_installation_summary(result: dict[str, Any], images_removed: str | No
if result.get("log"):
console.msg_note(f"{translate('Installation log:')} {result['log']}")
print()
console.msg_note(translate("OCI manager Apps is a beta: if something does not work as expected, "
console.msg_note(translate("OCI manager Apps is a Technology Preview: if something does not work as expected, "
"please report it on GitHub with the application name."))
@@ -565,8 +565,8 @@ def interactive(catalog: Catalog) -> int:
for number, key in category_by_index.items()]
selection = "search"
while True:
selection = ui.choose(translate("Select a category or search for applications:"), options, selection,
size=MENU_SIZE)
selection = ui.choose(translate("Select a category or search for applications:"), ReadableTags(options),
selection, size=MENU_SIZE)
if selection is None:
return 0
try:
@@ -612,7 +612,7 @@ def _template_summary_text(template: dict[str, Any]) -> str:
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="oci_manager_apps.sh",
description="ProxMenux OCI manager Apps (beta)")
description="ProxMenux OCI manager Apps (Technology Preview)")
subparsers = parser.add_subparsers(dest="command")
subparsers.add_parser("sync", help="Refresh the index from the image sources")
list_parser = subparsers.add_parser("list", help="List applications")
+2 -2
View File
@@ -5,7 +5,7 @@ from pathlib import Path
from . import host
from .i18n import translate
from .ui import UserCancelled
from .ui import ReadableTags, UserCancelled
GPU_NODE = re.compile(r'/dev/dri/(?:renderD|card)[0-9]+|/dev/kfd')
@@ -22,7 +22,7 @@ def choose_usb_device(ui, title, default=None, attached=()):
ui.message(translate('No selectable USB device was found. Type the host node by hand.'))
return ui.ask(manual_prompt, default or '/dev/ttyACM0')
options.append(('manual', translate('Another node, typed by hand')))
selected = ui.choose(title, options, default if default in dict(options) else options[0][0])
selected = ui.choose(title, ReadableTags(options), default if default in dict(options) else options[0][0])
if selected is None:
raise UserCancelled(translate('Device configuration cancelled'))
return ui.ask(manual_prompt, default or '/dev/ttyACM0') if selected == 'manual' else selected
+19 -4
View File
@@ -18,7 +18,7 @@ from typing import Any
from . import host
from . import network as access
from .i18n import source_text, translate
from .ui import DialogUI, TerminalUI, UserCancelled
from .ui import DialogUI, ReadableTags, TerminalUI, UserCancelled
from .custom_mounts import ask_custom_mounts
from .extra_devices import device_permissions
@@ -82,6 +82,19 @@ def _image_documentation(template: dict[str, Any]) -> str | None:
if str(ui.get(key) or "").startswith(("http://", "https://"))), None)
def ups_device_request(item: dict[str, Any], advanced: bool) -> dict[str, Any]:
"""The UPS proposed is the one the host sees on USB: its bus node changes
with the port and the order of connection. Without one, only the advanced
installation goes on, to type the node by hand."""
found = [row["path"] for row in host.usb_devices() if row["kind"] == "UPS"]
if found:
return {**item, "host_path_default": found[0]}
if not advanced:
raise UserCancelled(translate("No UPS was found on a USB port of this host. "
"Connect it and start the installation again."))
return item
def external_services_notice(template: dict[str, Any]) -> str | None:
"""What an application needs already running on the network, as a database
of its own. Said before its installation asks how to reach it, so it can be
@@ -147,7 +160,7 @@ def ask_storage(ui, text: str, content: str, default: str, mode: str = ADVANCED_
options = [(row["storage"], f"{row.get('type', '')} {host.gib(row.get('avail'))} GB {translate('free')}")
for row in rows]
names = [name for name, _ in options]
selected = ui.choose(text, options, default if default in names else names[0])
selected = ui.choose(text, ReadableTags(options), default if default in names else names[0])
if selected is None:
raise UserCancelled(text)
return selected
@@ -289,7 +302,7 @@ def ask_bridge(ui, text: str, default: str, mode: str = ADVANCED_MODE) -> str:
return ui.ask(text, default)
options = [(row["iface"], row.get("cidr") or (row.get("comments") or "").strip() or "-") for row in rows]
names = [name for name, _ in options]
selected = ui.choose(text, options, default if default in names else names[0])
selected = ui.choose(text, ReadableTags(options), default if default in names else names[0])
if selected is None:
raise UserCancelled(text)
return selected
@@ -1974,9 +1987,11 @@ def configure_acceleration(installer_profile, environment, unprivileged, ui, mod
if vendor_id in item.get("drm_vendor_ids", []) for node in host.gpus()[vendor]]
if nodes and item["host_path_default"] not in nodes:
item = {**item, "host_path_default": nodes[0]}
if item.get("purpose") == "ups":
item = ups_device_request(item, advanced)
if not advanced:
host_path = item["host_path_default"]
elif item.get("purpose") in ("serial", "user-selected-device"):
elif item.get("purpose") in ("serial", "user-selected-device", "ups"):
from .extra_devices import choose_usb_device
host_path = choose_usb_device(ui, translate(item["path_prompt"]), item["host_path_default"],
{device.get("host_path") for device in devices})
+29 -21
View File
@@ -254,27 +254,33 @@ def _interactive_management(project, ui):
recover_automatically(project)
offer_recovery(project, ui)
carry_records(project, mount_stopped=False, shown=True)
rows = saved_inventory(project)
if not rows:
ui.message(translate('No registered OCI containers are available for selection on this host.'), translate('OCI management'))
return
# Same layout as the catalog lists; only an unusual state is shown.
tag_width = max(len(str(r['vmid'])) for r in rows)
header = f" {'CT':<{tag_width + 2}}{translate('Application')[:32]:<32} {translate('Image')}"
options = []
for r in rows:
line = f"{(r['title'] or r['hostname'])[:32]:<32} "
if r['status'] == 'installed':
line += r['image'][:30]
else:
line += f"\\Z1{translate(STATUS_LABELS.get(r['status'], r['status']))}\\Zn"
options.append((str(r['vmid']), f"{line:<72}"))
selection = ui.choose(header, options, size=menu_size(), colors=True,
title=translate('Manage installed OCI applications'))
if selection is None:
return
row = next(r for r in rows if str(r['vmid']) == selection)
manage_instance(project, ui, row)
# After each operation the list is shown again. Every operation leaves
# the copies of the records up to date, so they are not all refreshed again.
first = True
while True:
rows = saved_inventory(project)
if not rows:
if first:
ui.message(translate('No registered OCI containers are available for selection on this host.'), translate('OCI management'))
return
first = False
# Same layout as the catalog lists; only an unusual state is shown.
tag_width = max(len(str(r['vmid'])) for r in rows)
header = f" {'CT':<{tag_width + 2}}{translate('Application')[:32]:<32} {translate('Image')}"
options = []
for r in rows:
line = f"{(r['title'] or r['hostname'])[:32]:<32} "
if r['status'] == 'installed':
line += r['image'][:30]
else:
line += f"\\Z1{translate(STATUS_LABELS.get(r['status'], r['status']))}\\Zn"
options.append((str(r['vmid']), f"{line:<72}"))
selection = ui.choose(header, options, size=menu_size(), colors=True,
title=translate('Manage installed OCI applications'))
if selection is None:
return
row = next(r for r in rows if str(r['vmid']) == selection)
manage_instance(project, ui, row)
def set_watchdog(project, vmids, enabled):
@@ -291,6 +297,8 @@ def set_watchdog(project, vmids, enabled):
done.update(oci_watchdog.set_watchdog(instances.ROOT, vmid, enabled))
except (OSError, ValueError, KeyError):
return False
# The copy a restore on another host reads carries the watchdog as well.
carry_records(project, mount_stopped=False, vmids=sorted(done))
return True
+23 -3
View File
@@ -107,7 +107,7 @@ class BacktrackUI:
raise UserCancelled(translate('Wizard cancelled'))
APP_TITLE = translate("OCI manager Apps (beta)")
APP_TITLE = translate("OCI manager Apps (Technology Preview)")
@dataclass
@@ -190,6 +190,11 @@ class TerminalUI:
# Width of the windows of the installer: questions, choices and notices. The
# final summary and the page of an application, which show tables, are wider.
MENU_WIDTH = 72
class ReadableTags(list):
"""Menu options whose tags the user reads, as a storage, a bridge or a
device node: they are shown as they are, not numbered."""
SUMMARY_WIDTH = 78
DETAIL_WINDOW_WIDTH = 90
@@ -279,6 +284,19 @@ class DialogUI:
def choose(self, text: str, options: list[tuple[str, str]], default: str | None = None,
title: str | None = None, show_tags: bool = True,
size: tuple[int, int, int] | None = None, colors: bool = False) -> str | None:
# A word is an internal value: the menu numbers the options, as the
# rest of ProxMenux does, and returns the value of the chosen one.
values: dict[str, str] = {}
if not isinstance(options, ReadableTags) and any(tag and not tag.isdigit() for tag, _ in options):
numbered = []
for tag, label in options:
if tag:
values[str(len(values) + 1)] = tag
if tag == default:
default = str(len(values))
tag = str(len(values))
numbered.append((tag, label))
options = numbered
if size:
widget = ["--colors"] if colors else []
widget += ["--default-item", default] if default else []
@@ -286,7 +304,8 @@ class DialogUI:
for tag, label in options:
widget += [tag, label]
result = self._run(widget, title)
return result.stderr.strip() if result.returncode == 0 else None
selected = result.stderr.strip() if result.returncode == 0 else None
return values.get(selected, selected) if values and selected else selected
terminal = shutil.get_terminal_size((100, 30))
tag_width = max((len(tag) for tag, _ in options), default=0) + 2 if show_tags else 0
width = min(max(MENU_WIDTH, max((len(label) for _, label in options), default=0) + tag_width + 14,
@@ -302,7 +321,8 @@ class DialogUI:
for tag, label in options:
widget += [tag, label]
result = self._run(widget, title)
return result.stderr.strip() if result.returncode == 0 else None
selected = result.stderr.strip() if result.returncode == 0 else None
return values.get(selected, selected) if values and selected else selected
def detail_menu(self, text: str, options: list[tuple[str, str]], default: str | None = None,
title: str | None = None) -> str | None:
+60
View File
@@ -0,0 +1,60 @@
"""A menu shows numbers, not internal values: the code still receives the
value of the option chosen. A tag the user reads, such as a storage, stays."""
from pathlib import Path
import sys
from types import SimpleNamespace
import unittest
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from proxmenux_oci.ui import DialogUI, ReadableTags
SOURCES = [("paste", "Paste its Compose file in the terminal"),
("file", "Read its Compose file from a file of this host"),
("oci-compose-paste", "Paste an oci-compose in the terminal")]
class DialogMenuTagsTests(unittest.TestCase):
def run_menu(self, options, answer, **kwargs):
seen = {}
def run(widget, title):
seen["widget"] = widget
return SimpleNamespace(returncode=0, stderr=answer)
ui = DialogUI()
with patch.object(ui, "_run", side_effect=run):
selected = ui.choose("How is the image described?", options, **kwargs)
menu = seen["widget"][seen["widget"].index("--menu") + 5:]
return selected, menu[0::2], seen["widget"]
def test_word_tags_are_shown_as_numbers_and_the_value_comes_back(self):
selected, tags, _ = self.run_menu(SOURCES, "3", default="file", size=(20, 80, 10))
self.assertEqual(tags, ["1", "2", "3"])
self.assertEqual(selected, "oci-compose-paste")
def test_the_default_follows_its_number(self):
_, _, widget = self.run_menu(SOURCES, "2", default="file")
self.assertEqual(widget[widget.index("--default-item") + 1], "2")
def test_a_tag_the_user_reads_is_kept(self):
storages = [("local-lvm", "lvmthin 669 GB free"), ("local2-lvm", "lvmthin 269 GB free")]
selected, tags, _ = self.run_menu(ReadableTags(storages), "local2-lvm")
self.assertEqual((tags, selected), (["local-lvm", "local2-lvm"], "local2-lvm"))
def test_number_tags_and_separators_are_left_as_they_are(self):
options = [("101", "Jellyfin"), ("", ""), ("205", "Immich")]
selected, tags, _ = self.run_menu(options, "205")
self.assertEqual((tags, selected), (["101", "", "205"], "205"))
def test_cancelling_returns_nothing(self):
ui = DialogUI()
with patch.object(ui, "_run", return_value=SimpleNamespace(returncode=1, stderr="")):
self.assertIsNone(ui.choose("How is the image described?", SOURCES))
if __name__ == "__main__":
unittest.main()
+32
View File
@@ -90,5 +90,37 @@ class UsbDevicesTests(unittest.TestCase):
[("/dev/ttyACM0", "/dev/ttyACM0")])
class UpsDeviceRequestTests(unittest.TestCase):
"""The UPS a recipe asks for is the one the host sees on USB."""
REQUEST = {"id": "ups-usb", "purpose": "ups", "host_path_default": "/dev/bus/usb/001/001"}
def test_the_host_ups_is_proposed(self):
from proxmenux_oci.installer import ups_device_request
rows = [{"path": "/dev/ttyACM0", "name": "ConBee II", "kind": "Serial"},
{"path": "/dev/bus/usb/003/002", "name": "Ellipse ECO", "kind": "UPS"}]
with patch.object(host, "usb_devices", return_value=rows):
self.assertEqual(ups_device_request(self.REQUEST, False)["host_path_default"], "/dev/bus/usb/003/002")
def test_without_a_ups_the_installation_stops(self):
from proxmenux_oci.installer import ups_device_request
from proxmenux_oci.ui import UserCancelled
with patch.object(host, "usb_devices", return_value=[]):
with self.assertRaises(UserCancelled):
ups_device_request(self.REQUEST, False)
# The advanced installation lets the node be typed by hand.
self.assertEqual(ups_device_request(self.REQUEST, True), self.REQUEST)
def test_the_nut_server_recipe_asks_for_the_ups(self):
import json
recipe = json.loads((ROOT / "catalog/curated/nut-server.json").read_text(encoding="utf-8"))
self.assertEqual(recipe, json.loads((ROOT / "catalog/apps/nut-server.json").read_text(encoding="utf-8")))
request, = recipe["proxmox"]["installer_profile"]["device_requests"]
self.assertEqual((request["purpose"], request["mode"], request["required_for_runtime"]), ("ups", "0666", True))
self.assertIn("API_PASSWORD", recipe["proxmox"]["installer_profile"]["generated_sensitive_environment"])
self.assertIsNone(recipe["catalog_ui"]["launch"]["port"])
self.assertTrue(recipe["proxmox"]["defaults"]["onboot"])
if __name__ == "__main__":
unittest.main()
+30 -14
View File
@@ -456,6 +456,34 @@ PROXMENUX_NVIDIA_SERVICE_LEGACY_STATE="${PROXMENUX_NVIDIA_SERVICE_LEGACY_STATE:-
# remove them when migrating a host that ran the older init-top hook.
PROXMENUX_VFIO_BIND_LEGACY_HOOK="${PROXMENUX_VFIO_BIND_LEGACY_HOOK:-${PROXMENUX_ETC_ROOT}/initramfs-tools/scripts/init-top/proxmenux-vfio-bind}"
# The udev rule the state file asks for.
_proxmenux_vfio_bind_render_udev_rule() {
{
echo "# ProxMenux: per-BDF VFIO driver override"
echo "# Auto-generated from $PROXMENUX_VFIO_BIND_STATE"
echo "# DO NOT EDIT MANUALLY — regenerated by add_gpu_vm.sh / switch_gpu_mode*.sh"
while IFS= read -r bdf; do
[[ -z "$bdf" ]] && continue
[[ "$bdf" == \#* ]] && continue
# KERNEL match expects the "0000:XX:YY.Z" form
local full="$bdf"
[[ "$full" != 0000:* ]] && full="0000:${full}"
echo "SUBSYSTEM==\"pci\", KERNEL==\"${full}\", ATTR{driver_override}=\"vfio-pci\""
done < "$PROXMENUX_VFIO_BIND_STATE"
}
}
# True when the udev rule on disk is the one the state file asks for, so a
# rule removed or edited by hand is written again on the next run.
_proxmenux_vfio_bind_udev_rule_current() {
if [[ ! -s "$PROXMENUX_VFIO_BIND_STATE" ]]; then
[[ ! -e "$PROXMENUX_VFIO_BIND_UDEV_RULE" ]]
return
fi
[[ -f "$PROXMENUX_VFIO_BIND_UDEV_RULE" ]] \
&& cmp -s <(_proxmenux_vfio_bind_render_udev_rule) "$PROXMENUX_VFIO_BIND_UDEV_RULE"
}
_proxmenux_vfio_bind_write_udev_rule() {
# Always nuke the obsolete init-top hook from earlier attempts (if it
# still exists) so a stale copy in initramfs can't run alongside the
@@ -470,19 +498,7 @@ _proxmenux_vfio_bind_write_udev_rule() {
fi
mkdir -p "$(dirname "$PROXMENUX_VFIO_BIND_UDEV_RULE")"
{
echo "# ProxMenux: per-BDF VFIO driver override"
echo "# Auto-generated from $PROXMENUX_VFIO_BIND_STATE"
echo "# DO NOT EDIT MANUALLY — regenerated by add_gpu_vm.sh / switch_gpu_mode*.sh"
while IFS= read -r bdf; do
[[ -z "$bdf" ]] && continue
[[ "$bdf" == \#* ]] && continue
# KERNEL match expects the "0000:XX:YY.Z" form
local full="$bdf"
[[ "$full" != 0000:* ]] && full="0000:${full}"
echo "SUBSYSTEM==\"pci\", KERNEL==\"${full}\", ATTR{driver_override}=\"vfio-pci\""
done < "$PROXMENUX_VFIO_BIND_STATE"
} > "$PROXMENUX_VFIO_BIND_UDEV_RULE"
_proxmenux_vfio_bind_render_udev_rule > "$PROXMENUX_VFIO_BIND_UDEV_RULE"
udevadm control --reload-rules >/dev/null 2>&1 || true
}
@@ -549,7 +565,7 @@ _proxmenux_vfio_bind_add_bdfs() {
changed=true
fi
done
if $changed; then
if $changed || ! _proxmenux_vfio_bind_udev_rule_current; then
_proxmenux_vfio_bind_write_udev_rule
_proxmenux_nvidia_vfio_policy_sync || true
_proxmenux_mark_host_config_changed
+11 -2
View File
@@ -96,20 +96,29 @@ def evaluate(suite, apply=False):
pve = []
ceph = []
debian = False
wrong_pve = []
wrong_ceph = []
for path, index, row in entries:
if not row['Enabled'] or 'deb' not in row['Types']:
continue
components = set(row['Components'])
if components & PVE_CHANNELS:
if suite not in row['Suites']:
raise ValueError('PVE repository suite does not match the installed version; no repository changed.')
wrong_pve.append(f"{' '.join(sorted(components & PVE_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:
debian = True
if 'enterprise' in components and any('/ceph-' in uri for uri in row['URIs']):
if suite not in row['Suites']:
raise ValueError('Enterprise Ceph repository suite does not match this PVE version; correct it in the Proxmox repository UI before switching.')
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.")
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):
return 'preserve'
+5
View File
@@ -339,6 +339,11 @@ evaluate_host_reboot_requirement() {
needs_change=true
fi
done
# The state alone is not enough: its udev rule may have been removed.
if declare -F _proxmenux_vfio_bind_udev_rule_current >/dev/null 2>&1 \
&& ! _proxmenux_vfio_bind_udev_rule_current; then
needs_change=true
fi
_file_has_exact_line "softdep nvidia pre: vfio-pci" "$vfio_conf" || needs_change=true
_file_has_exact_line "softdep nvidia_drm pre: vfio-pci" "$vfio_conf" || needs_change=true
_file_has_exact_line "softdep nvidia_modeset pre: vfio-pci" "$vfio_conf" || needs_change=true
@@ -67,6 +67,28 @@ grep -q 'ids=8086:7ad0' "$PROXMENUX_VFIO_CONF"
! grep -qi '10de:228b' "$PROXMENUX_VFIO_CONF"
_proxmenux_all_nvidia_in_vfio
[[ -f "$PROXMENUX_NVIDIA_VFIO_BLACKLIST" ]]
_proxmenux_vfio_bind_udev_rule_current
# A rule removed by hand while the state still lists the GPU is written again
# when the GPU is added once more, and the host is marked for a reboot.
rm -f "$PROXMENUX_VFIO_BIND_UDEV_RULE"
! _proxmenux_vfio_bind_udev_rule_current
HOST_CONFIG_CHANGED=false
_proxmenux_vfio_bind_add_bdfs 0000:01:00.0 0000:01:00.1
grep -qF 'KERNEL=="0000:01:00.0"' "$PROXMENUX_VFIO_BIND_UDEV_RULE"
grep -qF 'KERNEL=="0000:02:00.1"' "$PROXMENUX_VFIO_BIND_UDEV_RULE"
_proxmenux_vfio_bind_udev_rule_current
[[ "$HOST_CONFIG_CHANGED" == true ]]
# A rule that already matches the state is left alone.
HOST_CONFIG_CHANGED=false
_proxmenux_vfio_bind_add_bdfs 0000:01:00.0
[[ "$HOST_CONFIG_CHANGED" == false ]]
# A rule edited by hand is replaced by the one the state asks for.
echo '# edited' >> "$PROXMENUX_VFIO_BIND_UDEV_RULE"
_proxmenux_vfio_bind_add_bdfs 0000:01:00.0
! grep -qF '# edited' "$PROXMENUX_VFIO_BIND_UDEV_RULE"
# Returning only one GPU to the host must keep the other exact BDF in VFIO,
# remove the global NVIDIA blacklist, retain NVIDIA softdeps, and restore any
+1 -1
View File
@@ -73,7 +73,7 @@ show_menu() {
4 "$(translate "Disk Manager")" \
5 "$(translate "Storage & Share Manager")" \
6 "$(translate "Proxmox VE Helper Scripts")" \
o "$(translate "OCI manager Apps (beta)")" \
o "$(translate "OCI manager Apps (Technology Preview)")" \
7 "$(translate "Network Management")" \
8 "$(translate "Security")" \
9 "$(translate "Utilities and Tools")" \
+2 -2
View File
@@ -36,8 +36,8 @@
"keywords": ""
},
{
"title": "OCI manager Apps (beta)",
"menu": "OCI manager Apps (beta)",
"title": "OCI manager Apps (Technology Preview)",
"menu": "OCI manager Apps (Technology Preview)",
"script": "oci/oci_manager_apps.sh",
"keywords": "docker container image contenedor imagen apps"
},
+27 -2
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# ==========================================================
# ProxMenux - OCI manager Apps (beta)
# ProxMenux - OCI manager Apps (Technology Preview)
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
@@ -37,7 +37,7 @@ ensure_oci_dependencies() {
[[ ${#missing[@]} -eq 0 ]] && return 0
show_proxmenux_logo
msg_title "$(translate "OCI manager Apps (beta)")"
msg_title "$(translate "OCI manager Apps (Technology Preview)")"
msg_info "$(translate "Installing required packages...")"
apt-get update >/dev/null 2>&1
local status
@@ -56,6 +56,31 @@ ensure_oci_dependencies() {
stop_spinner
}
# Proxmox VE runs OCI images as LXC containers from 9.1 on.
pve_supports_oci() {
local version major minor
version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
major=${version%%.*}
minor=${version#*.}
[[ $major =~ ^[0-9]+$ && $minor =~ ^[0-9]+$ ]] || return 1
(( major > 9 || (major == 9 && minor >= 1) ))
}
if ! pve_supports_oci; then
show_proxmenux_logo
msg_title "$(translate "OCI manager Apps (Technology Preview)")"
msg_error "$(translate "OCI containers require Proxmox VE 9.1 or later.")"
PVE_VERSION=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9.]+' | head -1)
[[ -n $PVE_VERSION ]] && msg_info2 "$(translate "Current version:") $PVE_VERSION"
if [[ -t 0 ]]; then
echo
msg_success "$(translate "Press Enter to return to the menu...")"
read -r
[[ $# -eq 0 ]] && exec bash "$LOCAL_SCRIPTS/menus/main_menu.sh"
fi
exit 1
fi
if [[ ! -f "$OCI_ENGINE_DIR/src/proxmenux_oci/__main__.py" ]]; then
msg_error "$(translate "The OCI engine is not installed. Update ProxMenux and try again.")"
exit 1
+19
View File
@@ -468,6 +468,25 @@ class RepositoryPolicyTest(unittest.TestCase):
self.run_policy(apply=True)
self.assertFalse(any(c[1] in ('create', 'set') for c in self.calls))
def test_wrong_suite_names_each_stale_source_and_where_to_fix_it(self):
self.repositories = fixture(
('/etc/apt/sources.list.d/pve.list', [repo('pve-no-subscription')]),
('/etc/apt/sources.list.d/pve-install-repo.list',
[repo('pve-no-subscription', uri='https://user:secret@mirror.example/debian/pve', suite='bookworm')]),
('/etc/apt/sources.list.d/debian.sources', [repo('main', uri='https://deb.example/debian')]),
)
with self.assertRaises(ValueError) as error:
self.run_policy(apply=True)
message = str(error.exception)
self.assertIn('/etc/apt/sources.list.d/pve-install-repo.list', message)
self.assertIn('pve-no-subscription', message)
self.assertIn('suite bookworm', message)
self.assertIn('runs Proxmox VE on trixie', message)
self.assertIn('Node > Updates > Repositories', message)
self.assertNotIn('/etc/apt/sources.list.d/pve.list', message)
self.assertNotIn('secret', message)
self.assertFalse(any(c[1] in ('create', 'set') for c in self.calls))
def test_ceph_enterprise_mirror_is_disabled_without_choosing_ceph_channel(self):
self.repositories['files'].append({'path': '/etc/apt/sources.list.d/storage.list',
'repositories': [repo('enterprise', uri='https://mirror.example/ceph-squid')]})
@@ -441,7 +441,7 @@
"history": {
"heading": "History",
"body1": "Every dispatch <em>attempt</em> the dispatcher actually performs is recorded in the <code>notification_history</code> SQLite table. Each row stores the timestamp (<code>sent_at</code>), channel, event type, severity, title, rendered message body, a <code>success</code> flag and — when the send failed — the error returned by the provider in <code>error_message</code>. Burst-aggregated events appear as a single row with the <code>burst_*</code> event type. Events suppressed by the cooldown stage are not logged: they never become a dispatch attempt.",
"body2": "The History tab inside Settings → Notifications shows the last 20 entries and has a single <em>Clear</em> button that wipes the table.",
"body2": "Settings → Notifications has a <em>Notification history</em> section, closed by default, with the last 20 entries: the result of each send, its channel and date, the error when it failed and, for each daily digest run, whether it had something to send. The page does not offer to erase it: Audit & Report reads the same history to check the delivery of each channel.",
"body3": "The same data is exposed at <code>GET /api/notifications/history</code> with optional <code>limit</code>, <code>offset</code>, <code>severity</code> and <code>channel</code> query parameters, and can be cleared with <code>DELETE /api/notifications/history</code>."
},
"api": {
+10 -2
View File
@@ -33,7 +33,7 @@
{
"id": "where",
"title": "Where it is",
"intro": "OCI manager Apps opens from option <strong>OCI manager Apps (beta)</strong> of the ProxMenux main menu, on the Proxmox node and as root. Its first screen offers:",
"intro": "OCI manager Apps opens from option <strong>OCI manager Apps (Technology Preview)</strong> of the ProxMenux main menu, on the Proxmox node and as root. Its first screen offers:",
"blocks": [
{
"list": {
@@ -54,13 +54,21 @@
}
},
{
"p": "Selecting an application shows its description, the image it runs and two installation modes: <strong>Install with default settings</strong>, which asks almost nothing, and <strong>Install with advanced settings</strong>, which offers the real storage, bridge and resource selectors of the node."
"p": "Selecting an application shows its description, the image it runs and two installation modes: <strong>Install with default settings</strong>, which asks only what every installation decides (storage, where the data goes, address and start), and <strong>Install with advanced settings</strong>, which opens every decision in order: VMID and resources, the storage of each path, the bridge and the devices."
},
{
"p": "An application that has been tested for real shows <strong>Verified</strong> in that view and is marked with ✓ in the lists, whether it was tested in the ProxMenux lab or by the community."
}
]
},
{
"id": "beta",
"title": "Why it is a Technology Preview",
"blocks": [
{ "p": "OCI manager Apps carries the <strong>Technology Preview</strong> label that Proxmox VE gives to application containers created from OCI images, introduced in Proxmox VE 9.1. It refers to what OCI manager Apps runs on, not to the ProxMenux engine: the behaviour of those containers can still change between Proxmox VE releases. The label stays until Proxmox VE marks that support as stable." },
{ "p": "The engine of OCI manager Apps (installation, update, modification, recreation, backups, restore, migration and watchdog) is tested in the ProxMenux lab. Each application is tested on its own: the ones tested for real show <strong>Verified</strong> and carry a ✓ in the lists." }
]
},
{
"id": "translation",
"title": "What Docker expresses and what OCI manager Apps turns it into",
@@ -441,7 +441,7 @@
"history": {
"heading": "Historial",
"body1": "Cada <em>intento</em> de despacho que el dispatcher realmente ejecuta se registra en la tabla SQLite <code>notification_history</code>. Cada fila guarda el timestamp (<code>sent_at</code>), canal, tipo de evento, severidad, título, cuerpo de mensaje renderizado, un flag <code>success</code> y — cuando el envío falló — el error devuelto por el proveedor en <code>error_message</code>. Los eventos agregados en ráfaga aparecen como una única fila con el tipo de evento <code>burst_*</code>. Los eventos suprimidos por la etapa de cooldown no se loguean: nunca se convierten en un intento de despacho.",
"body2": "La pestaña History dentro de Settings → Notifications muestra las últimas 20 entradas y tiene un único botón <em>Clear</em> que borra la tabla.",
"body2": "Settings → Notifications tiene una sección <em>Historial de notificaciones</em>, cerrada por defecto, con las últimas 20 entradas: el resultado de cada envío, su canal y su fecha, el error cuando falló y, en cada ejecución del resumen diario, si tenía algo que enviar. La página no ofrece borrarlo: Audit & Report lee ese mismo historial para comprobar la entrega de cada canal.",
"body3": "Los mismos datos se exponen en <code>GET /api/notifications/history</code> con parámetros opcionales <code>limit</code>, <code>offset</code>, <code>severity</code> y <code>channel</code>, y se pueden borrar con <code>DELETE /api/notifications/history</code>."
},
"api": {
+10 -2
View File
@@ -33,7 +33,7 @@
{
"id": "where",
"title": "Dónde se encuentra",
"intro": "OCI manager Apps se abre desde la opción <strong>OCI manager Apps (beta)</strong> del menú principal de ProxMenux, en el nodo Proxmox y como root. Su primera pantalla ofrece:",
"intro": "OCI manager Apps se abre desde la opción <strong>OCI manager Apps (Technology Preview)</strong> del menú principal de ProxMenux, en el nodo Proxmox y como root. Su primera pantalla ofrece:",
"blocks": [
{
"list": {
@@ -54,13 +54,21 @@
}
},
{
"p": "Al seleccionar una aplicación se muestra su descripción, la imagen que ejecuta y dos modos de instalación: <strong>Instalar con la configuración predeterminada</strong>, que apenas hace preguntas, e <strong>Instalar con la configuración avanzada</strong>, que ofrece los selectores reales de almacenamiento, bridge y recursos del nodo."
"p": "Al seleccionar una aplicación se muestra su descripción, la imagen que ejecuta y dos modos de instalación: <strong>Instalar con la configuración predeterminada</strong>, que pregunta solo lo que decide toda instalación (almacenamiento, dónde van los datos, dirección e inicio), e <strong>Instalar con la configuración avanzada</strong>, que abre cada decisión en orden: VMID y recursos, el almacenamiento de cada ruta, el bridge y los dispositivos."
},
{
"p": "Una aplicación probada en real muestra <strong>Verificado</strong> en esa vista y aparece marcada con ✓ en las listas, tanto si se probó en el laboratorio de ProxMenux como si la probó la comunidad."
}
]
},
{
"id": "beta",
"title": "Por qué es una Technology Preview",
"blocks": [
{ "p": "OCI manager Apps lleva la etiqueta <strong>Technology Preview</strong> que Proxmox VE da a los contenedores de aplicación creados a partir de imágenes OCI, introducidos en Proxmox VE 9.1. Se refiere a aquello sobre lo que funciona OCI manager Apps, no al motor de ProxMenux: el comportamiento de esos contenedores todavía puede cambiar entre versiones de Proxmox VE. La etiqueta se mantiene hasta que Proxmox VE dé ese soporte por estable." },
{ "p": "El motor de OCI manager Apps (instalación, actualización, modificación, recreación, backups, restauración, migración y vigilancia) está probado en el laboratorio de ProxMenux. Cada aplicación se prueba por separado: las que se han probado de verdad muestran <strong>Verified</strong> y llevan una ✓ en las listas." }
]
},
{
"id": "translation",
"title": "Lo que Docker expresa y en qué lo convierte OCI manager Apps",