From f0d7d4f8f7c951b761d5c27853e5e4c4bd2902a4 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Wed, 7 Oct 2026 07:35:10 +0200 Subject: [PATCH] fix: more messages say only what is certain - Host restore notification: reports its tasks and no longer calls the node fully ready. - Watchdog notification: covers an application that did not start as well as one that stopped again. - OCI: Modify summary, Immich recognition preview, private network kept by another guest, empty USB list. - gzip diagnostic no longer says that nothing changed. Source questions from the Italian review by @f3rs3n. --- .../tests/test_notification_backup_split.py | 16 ++++++++++----- .../test_notification_outcome_wording.py | 8 ++++---- .../scripts/tests/test_notification_pve92.py | 2 +- .../tests/test_outcome_promises_wording.py | 20 +++++++++++++++++++ AppImage/messages/de/common.json | 10 +++++----- AppImage/messages/en/common.json | 4 ++-- AppImage/messages/es/common.json | 10 +++++----- AppImage/messages/fr/common.json | 10 +++++----- AppImage/messages/it/common.json | 10 +++++----- AppImage/messages/pt/common.json | 10 +++++----- AppImage/messages/sk/common.json | 10 +++++----- AppImage/messages/sv/common.json | 10 +++++----- AppImage/scripts/notification_templates.py | 13 ++++++------ lang/es.json | 12 +++++------ oci/remote/oci_remove.py | 2 +- oci/remote/oci_watchdog.py | 2 +- oci/src/proxmenux_oci/extra_devices.py | 2 +- oci/src/proxmenux_oci/management.py | 2 +- oci/src/proxmenux_oci/stack_recreation.py | 11 ++++++---- oci/tests/test_stack_recreation.py | 2 +- .../post_install/customizable_post_install.sh | 8 ++++---- scripts/post_install/uninstall-tools.sh | 4 ++-- .../en/docs/oci-manager/lifecycle.json | 2 +- .../es/docs/oci-manager/lifecycle.json | 2 +- 24 files changed, 105 insertions(+), 77 deletions(-) diff --git a/.github/scripts/tests/test_notification_backup_split.py b/.github/scripts/tests/test_notification_backup_split.py index d63b4e6d..4f191087 100644 --- a/.github/scripts/tests/test_notification_backup_split.py +++ b/.github/scripts/tests/test_notification_backup_split.py @@ -76,22 +76,28 @@ class BackupSplitTests(unittest.TestCase): self.assertEqual(result['title'], 'alias: Backup outcome unconfirmed') self.assertEqual(result['body'], 'The backup outcome is not confirmed.') - def test_restore_keeps_original_ready_line_and_success_icon_with_warnings(self): + def test_restore_reports_its_tasks_without_calling_the_node_ready_and_keeps_the_success_icon(self): + import json + from pathlib import Path from notification_final_fixture import restore_event - ready = {'en': 'The node is now fully ready to use.', 'de': 'Der Knoten ist nun vollständig einsatzbereit.', 'es': 'El nodo está listo para usarse.', 'fr': 'Le nœud est maintenant entièrement prêt à être utilisé.', 'it': "Il nodo è ora completamente pronto per l'uso.", 'pt': 'O nó agora está totalmente pronto para uso.', 'sk': 'Uzol je teraz úplne pripravený na použitie.', 'sv': 'Noden är nu helt redo att användas.'} + messages = Path(__file__).resolve().parents[3] / 'AppImage/messages' + first = {language: json.loads((messages / language / 'common.json').read_text()) + ['runtime']['notifications']['templates']['system_restore_completed']['body'].split('\n')[0] + for language in LANGUAGES} for warning in ('', 'missing module zfs'): event = restore_event(warning) for language in LANGUAGES: result = deliver(event['event_type'], event['data'], event['severity'], language) self.assertTrue(result['title'].startswith('✅ ')) - self.assertIn(ready[language], result['body']) - self.assertIn(ready[language], result['text']) + self.assertIn(first[language], result['body']) + self.assertIn(first[language], result['text']) + self.assertNotIn('fully ready', result['body']) self.assertIn('2m', result['text']) if warning: self.assertIn(warning, result['text']) quiet = deliver(event['event_type'], event['data'], event['severity'], language, quiet=True) self.assertIn('✅', quiet['body']) - self.assertIn(' ' + ready[language], quiet['body']) + self.assertIn(first[language], quiet['body']) self.assertIn('white-space:pre-wrap;', quiet['html']) def test_spanish_outcome_and_restore_titles_are_capitalized_and_failure_is_exact(self): diff --git a/.github/scripts/tests/test_notification_outcome_wording.py b/.github/scripts/tests/test_notification_outcome_wording.py index 7d5a35f7..b8aa78d3 100644 --- a/.github/scripts/tests/test_notification_outcome_wording.py +++ b/.github/scripts/tests/test_notification_outcome_wording.py @@ -15,7 +15,7 @@ from notification_fixture import templates as actual_templates ROOT = Path(__file__).resolve().parents[3] SCRIPTS = ROOT / 'AppImage/scripts' CATALOG = ROOT / 'AppImage/messages/en/common.json' -EXPECTED = {'system_restore_completed': {'body': 'Post-restore tasks completed in background.\n\nGuests applied: {guests}\nBind-mount stubs: {stubs}\nStale node dirs removed: {stale_nodes}\nComponents reinstalled: {components}\nDuration: {duration}\n{warnings_block}\nThe node is now fully ready to use.'}} +EXPECTED = {'system_restore_completed': {'body': 'Post-restore tasks completed in background.\n\nGuest configurations copied: {guests}\nBind-mount stub directories created: {stubs}\nStale node dirs removed: {stale_nodes}\nComponents reinstalled: {components}\nDuration: {duration}\n{warnings_block}'}} def extract(path, name, owner=None, namespace=None): @@ -72,7 +72,7 @@ class OutcomeWording(unittest.TestCase): result = module.render_template('system_restore_completed', { 'hostname':'node-a','guests':3,'stubs':0,'stale_nodes':0, 'components':1,'duration':'2m','warnings_block':''}, 'es') - self.assertIn('Guests aplicados: 3', result['body']) + self.assertIn('Configuración de guests copiada: 3', result['body']) self.assertNotIn('invitados', result['body'].lower()) def test_settings_labels_stay_at_upstream_values_in_all_locales(self): @@ -345,7 +345,7 @@ class OutcomeWording(unittest.TestCase): restore = module.render_template('system_restore_completed', {'hostname':'node', 'guests':4, 'stubs':1,'stale_nodes':2,'components':1,'duration':'2m','warnings_block':'Missing module'},lang) self.assertIn('Missing module',restore['body']) - if lang == 'en': self.assertIn('fully ready',restore['body'].lower()) + if lang == 'en': self.assertNotIn('fully ready',restore['body'].lower()) @@ -366,7 +366,7 @@ class OutcomeWording(unittest.TestCase): self.assertEqual(event['severity'], 'WARNING' if warnings else 'INFO') result = self.render(event['event_type'], event['data'], 'en') self.assertIn('Post-restore tasks completed', result['body']) - self.assertIn('fully ready', result['body']) + self.assertNotIn('fully ready', result['body']) if warnings: self.assertIn('missing modules', result['body']) diff --git a/.github/scripts/tests/test_notification_pve92.py b/.github/scripts/tests/test_notification_pve92.py index d0f781a4..8d88b642 100644 --- a/.github/scripts/tests/test_notification_pve92.py +++ b/.github/scripts/tests/test_notification_pve92.py @@ -107,7 +107,7 @@ class PVE92Tests(unittest.TestCase): with self.subTest(event=event, field=field): value = slovak['templates'][event][field] result = templates.render_template(event, data, 'sk') - self.assertEqual(result[field], value.format(**data)) + self.assertEqual(result[field].strip(), value.format(**data).strip()) self.assertNotIn(value, (SCRIPTS / 'notification_templates.py').read_text()) # Independently updated and absent leaves use the usual provider. import copy diff --git a/.github/scripts/tests/test_outcome_promises_wording.py b/.github/scripts/tests/test_outcome_promises_wording.py index 9d292788..2b793640 100644 --- a/.github/scripts/tests/test_outcome_promises_wording.py +++ b/.github/scripts/tests/test_outcome_promises_wording.py @@ -49,3 +49,23 @@ class OutcomePromises(TestCase): for key in ('deleteAttached', 'storageAttached'): self.assertEqual(texts[key].count('{ids}'), 1, f'{locale} {key}') self.assertEqual(texts['disableBody'].count('. '), 1, f'{locale} disableBody') + + def test_the_host_restore_notice_reports_tasks_and_does_not_call_the_node_ready(self): + for locale in LOCALES: + body = json.loads((ROOT / f'AppImage/messages/{locale}/common.json').read_text())['runtime']['notifications']['templates']['system_restore_completed']['body'] + self.assertEqual(body.split('\n')[-1], '{warnings_block}', locale) + self.assertEqual(body.count('\n'), 7, locale) + + def test_the_watchdog_failure_covers_an_application_that_did_not_start(self): + english = json.loads((ROOT / 'AppImage/messages/en/common.json').read_text())['runtime']['notifications']['templates']['oci_watchdog_failed'] + self.assertNotIn('keeps stopping', english['title'] + english['body'] + english['label']) + self.assertIn('did not start', english['body']) + + def test_other_wording_that_promised_too_much(self): + texts = oci_menu_texts() + self.assertIn('Private network kept, because other guests still use it:', texts) + recreation = (ROOT / 'oci/src/proxmenux_oci/stack_recreation.py').read_text() + self.assertNotIn('if anything fails', recreation) + self.assertIn('a stopped one is left stopped', recreation) + for script in ('scripts/post_install/customizable_post_install.sh', 'scripts/post_install/uninstall-tools.sh'): + self.assertNotIn('leaving it unchanged', (ROOT / script).read_text(), script) diff --git a/AppImage/messages/de/common.json b/AppImage/messages/de/common.json index 5a6ad367..fbe07890 100644 --- a/AppImage/messages/de/common.json +++ b/AppImage/messages/de/common.json @@ -2011,7 +2011,7 @@ "oci_modify_completed": "OCI-Anwendung geändert", "oci_modify_failed": "Änderung der OCI-Anwendung fehlgeschlagen", "oci_watchdog_restarted": "OCI-Anwendung vom Watchdog neu gestartet", - "oci_watchdog_failed": "OCI-Anwendung stoppt immer wieder", + "oci_watchdog_failed": "OCI-Anwendung vom Watchdog gestoppt belassen", "app_update_available": "App-Update verfügbar", "lxc_update_applied": "LXC Update angewendet", "docker_stack_update_available": "Docker Updates verfügbar" @@ -6451,9 +6451,9 @@ "label": "OCI-Anwendung vom Watchdog neu gestartet" }, "oci_watchdog_failed": { - "title": "{hostname}: {app_name} stoppt immer wieder", - "body": "{app_name} stoppt immer wieder von selbst und wird vom Watchdog nicht mehr neu gestartet.\nContainer: {containers}\nStarten Sie die Anwendung von Hand, sobald die Ursache behoben ist; der Watchdog überwacht sie dann wieder.", - "label": "OCI-Anwendung stoppt immer wieder" + "title": "{hostname}: {app_name} konnte nicht am Laufen gehalten werden", + "body": "Der Watchdog konnte {app_name} nicht am Laufen halten: Die Anwendung stoppte erneut oder startete nach mehreren Versuchen nicht und bleibt gestoppt.\nContainer: {containers}\nStarten Sie die Anwendung von Hand, sobald die Ursache behoben ist; der Watchdog überwacht sie dann wieder.", + "label": "OCI-Anwendung vom Watchdog gestoppt belassen" }, "app_update_available": { "title": "{hostname}: {app_name} Update verfügbar auf CT {vmid}", @@ -6722,7 +6722,7 @@ }, "system_restore_completed": { "title": "{hostname}: Host-Wiederherstellung abgeschlossen", - "body": "Aufgaben nach der Wiederherstellung im Hintergrund ausgeführt.\n\nGäste haben sich beworben: {guests}\nBind-Mount-Stubs: {stubs}\nVeraltete Knotenverzeichnisse entfernt: {stale_nodes}\nKomponenten neu installiert: {components}\nDauer: {duration}\n{warnings_block}\nDer Knoten ist nun vollständig einsatzbereit.", + "body": "Aufgaben nach der Wiederherstellung im Hintergrund ausgeführt.\n\nGastkonfigurationen kopiert: {guests}\nPlatzhalterverzeichnisse für Bind-Mounts erstellt: {stubs}\nVeraltete Knotenverzeichnisse entfernt: {stale_nodes}\nKomponenten neu installiert: {components}\nDauer: {duration}\n{warnings_block}", "label": "Host-Wiederherstellung abgeschlossen" }, "system_problem": { diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json index 3b0f30f3..f07a874a 100644 --- a/AppImage/messages/en/common.json +++ b/AppImage/messages/en/common.json @@ -1999,7 +1999,7 @@ "oci_modify_completed": "OCI application modified", "oci_modify_failed": "OCI application modification failed", "oci_watchdog_restarted": "OCI application restarted by the watchdog", - "oci_watchdog_failed": "OCI application keeps stopping", + "oci_watchdog_failed": "OCI application left stopped by the watchdog", "app_update_available": "App update available", "ai_model_migrated": "AI model updated automatically", "proxmenux_update": "ProxMenux update available", @@ -6376,5 +6376,5 @@ "cancel": "Cancel" } }, - "runtime": {"notifications":{"templates":{"state_change":{"title":"{hostname}: {category} changed to {current}{entity_suffix}","body":"{category} status changed from {previous} to {current}.\n{reason}","label":"Health state changed"},"new_error":{"title":"{hostname}: New {severity} - {category}{entity_suffix}","body":"{reason}","label":"New health issue"},"error_resolved":{"title":"{hostname}: Resolved - {category}{entity_suffix}","body":"The {category} issue has been resolved.\n{reason}\n🚦 Previous severity: {original_severity}\n⏱️ Duration: {duration}","label":"Recovery notification"},"error_escalated":{"title":"{hostname}: Escalated to {severity} - {category}{entity_suffix}","body":"{reason}","label":"Health issue escalated"},"health_degraded":{"title":"{title_or_default}","body":"{reason}","label":"Health check degraded"},"lxc_updates_available":{"title":"{hostname}: {count} LXC(s) with package updates available","body":"📊 {count} LXC(s) with pending package updates (📦 {total_packages} total, 🔒 {security_count} security):\n\n{ct_list}","label":"LXC updates available (experimental)"},"lxc_update_applied":{"title":"{hostname}: LXC {ct_name} ({vmid}) update {result}","body":"{details}","label":"LXC update applied"},"oci_update_completed":{"title":"{hostname}: {app_name} updated","body":"{app_name} was updated to its new image and its data was kept.\nContainers: {containers}","label":"OCI application updated"},"oci_update_failed":{"title":"{hostname}: {app_name} update did not complete","body":"The update of {app_name} did not complete.\nReason: {reason}\nContainers: {containers}\nOpen Manage installed OCI applications to check its state.","label":"OCI application update failed"},"oci_recreate_completed":{"title":"{hostname}: {app_name} recreated","body":"{app_name} was recreated with its saved configuration and its data was kept.\nContainers: {containers}","label":"OCI application recreated"},"oci_recreate_failed":{"title":"{hostname}: {app_name} recreation did not complete","body":"The recreation of {app_name} did not complete.\nReason: {reason}\nContainers: {containers}\nOpen Manage installed OCI applications to check its state.","label":"OCI application recreation failed"},"oci_modify_completed":{"title":"{hostname}: {app_name} modified","body":"{app_name} was modified with its new options and its data was kept.\nContainers: {containers}","label":"OCI application modified"},"oci_modify_failed":{"title":"{hostname}: {app_name} modification did not complete","body":"The modification of {app_name} did not complete.\nReason: {reason}\nContainers: {containers}\nOpen Manage installed OCI applications to check its state.","label":"OCI application modification failed"},"oci_watchdog_restarted":{"title":"{hostname}: {app_name} restarted after it stopped","body":"{app_name} stopped on its own and the watchdog started it again.\nContainers: {containers}","label":"OCI application restarted by the watchdog"},"oci_watchdog_failed":{"title":"{hostname}: {app_name} keeps stopping","body":"{app_name} keeps stopping on its own and the watchdog no longer restarts it.\nContainers: {containers}\nStart it by hand once the cause is solved; the watchdog then watches it again.","label":"OCI application keeps stopping"},"app_update_available":{"title":"{hostname}: {app_name} update available on CT {vmid}","body":"{app_name} on CT {vmid} ({ct_name}) has a new version:\n {installed} → {latest}","label":"App update available"},"docker_stack_update_available":{"title":"{hostname}: Docker updates available on CT {vmid}","body":"Container {ct_name} (CT {vmid}) has {count} Docker update(s):\n{details}","label":"Docker updates available"},"vm_start":{"title":"{hostname}: VM {vmname} ({vmid}) started","body":"Virtual machine {vmname} (ID: {vmid}) is now running.","label":"VM started"},"vm_start_warning":{"title":"{hostname}: VM {vmname} ({vmid}) started with warnings","body":"Virtual machine {vmname} (ID: {vmid}) started successfully but has warnings.\nWarnings: {reason}","label":"VM started (warnings)"},"vm_stop":{"title":"{hostname}: VM {vmname} ({vmid}) stopped","body":"Virtual machine {vmname} (ID: {vmid}) has been stopped.","label":"VM stopped"},"vm_shutdown":{"title":"{hostname}: VM {vmname} ({vmid}) shut down","body":"Virtual machine {vmname} (ID: {vmid}) has been cleanly shut down.","label":"VM shutdown"},"vm_fail":{"title":"{hostname}: VM {vmname} ({vmid}) FAILED","body":"Virtual machine {vmname} (ID: {vmid}) has crashed or failed to start.\nReason: {reason}","label":"VM FAILED"},"vm_restart":{"title":"{hostname}: VM {vmname} ({vmid}) restarted","body":"Virtual machine {vmname} (ID: {vmid}) has been restarted.","label":"VM restarted"},"ct_start":{"title":"{hostname}: CT {vmname} ({vmid}) started","body":"Container {vmname} (ID: {vmid}) is now running.","label":"CT started"},"ct_start_warning":{"title":"{hostname}: CT {vmname} ({vmid}) started with warnings","body":"Container {vmname} (ID: {vmid}) started successfully but has warnings.\nWarnings: {reason}","label":"CT started (warnings)"},"ct_stop":{"title":"{hostname}: CT {vmname} ({vmid}) stopped","body":"Container {vmname} (ID: {vmid}) has been stopped.","label":"CT stopped"},"ct_shutdown":{"title":"{hostname}: CT {vmname} ({vmid}) shut down","body":"Container {vmname} (ID: {vmid}) has been cleanly shut down.","label":"CT shutdown"},"ct_restart":{"title":"{hostname}: CT {vmname} ({vmid}) restarted","body":"Container {vmname} (ID: {vmid}) has been restarted.","label":"CT restarted"},"ct_fail":{"title":"{hostname}: CT {vmname} ({vmid}) FAILED","body":"Container {vmname} (ID: {vmid}) has crashed or failed to start.\nReason: {reason}","label":"CT FAILED"},"migration_start":{"title":"{hostname}: Migration started — {vmname} ({vmid})","body":"Live migration of {vmname} (ID: {vmid}) to node {target_node} has started.","label":"Migration started"},"migration_complete":{"title":"{hostname}: Migration complete — {vmname} ({vmid})","body":"{vmname} (ID: {vmid}) successfully migrated to node {target_node}.","label":"Migration complete"},"migration_warning":{"title":"{hostname}: Migration complete with warnings — {vmname} ({vmid})","body":"{vmname} (ID: {vmid}) migrated to node {target_node} but encountered warnings.\nWarnings: {reason}","label":"Migration (warnings)"},"migration_fail":{"title":"{hostname}: Migration FAILED — {vmname} ({vmid})","body":"Migration of {vmname} (ID: {vmid}) to node {target_node} failed.\nReason: {reason}","label":"Migration FAILED"},"replication_fail":{"title":"{hostname}: Replication FAILED — {vmname} ({vmid})","body":"Replication of {vmname} (ID: {vmid}) failed.\nReason: {reason}","label":"Replication FAILED"},"replication_complete":{"title":"{hostname}: Replication complete — {vmname} ({vmid})","body":"Replication of {vmname} (ID: {vmid}) completed successfully.","label":"Replication complete"},"backup_start":{"title":"{hostname}: Backup started on {storage}","body":"Backup job started on storage {storage}.\n{reason}","label":"Backup started"},"backup_complete":{"title":"{hostname} → {storage}: Backup complete — {vmname} ({vmid})","body":"Backup of {vmname} (ID: {vmid}) completed successfully on {storage}.\nSize: {size}","label":"Backup complete"},"backup_warning":{"title":"{hostname} → {storage}: Backup complete with warnings — {vmname} ({vmid})","body":"Backup of {vmname} (ID: {vmid}) on {storage} completed but encountered warnings.\nWarnings: {reason}","label":"Backup (warnings)"},"backup_fail":{"title":"{hostname} → {storage}: Backup FAILED — {vmname} ({vmid})","body":"Backup of {vmname} (ID: {vmid}) failed on {storage}.\nReason: {reason}","label":"Backup FAILED"},"host_backup_start":{"title":"{hostname}: Host backup started → {backend_label}","body":"Job: {job_id}\nBackend: {backend_label}\nDestination: {destination}\nProfile: {profile_mode}","label":"Host backup started"},"host_backup_complete":{"title":"{hostname}: Host backup complete → {backend_label}","body":"Job: {job_id}\nBackend: {backend_label}\nDestination: {destination}\nData size: {data_size}\nArchive size: {archive_size}\nDuration: {duration}","label":"Host backup complete"},"host_backup_fail":{"title":"{hostname}: Host backup FAILED → {backend_label}","body":"Job: {job_id}\nBackend: {backend_label}\nDestination: {destination}\nDuration before failure: {duration}\nReason: {reason}\nLog: {log_file}","label":"Host backup FAILED"},"snapshot_complete":{"title":"{hostname}: Snapshot created — {vmname} ({vmid})","body":"Snapshot \"{snapshot_name}\" created for {vmname} (ID: {vmid}).","label":"Snapshot created"},"snapshot_fail":{"title":"{hostname}: Snapshot FAILED — {vmname} ({vmid})","body":"Snapshot creation for {vmname} (ID: {vmid}) failed.\nReason: {reason}","label":"Snapshot FAILED"},"cpu_high":{"title":"{hostname}: High CPU usage — {value}%","body":"CPU usage has reached {value}% on {cores} cores.\n{details}","label":"High CPU usage"},"ram_high":{"title":"{hostname}: High memory usage — {value}%","body":"Memory usage: {used} / {total} ({value}%).\n{details}","label":"High memory usage"},"temp_high":{"title":"{hostname}: High sensor temperature — {value}°C","body":"Sensor temperature has reached {value}°C (threshold: {threshold}°C).\n{details}","label":"High temperature"},"disk_space_low":{"title":"{hostname}: Low disk space on {mount}","body":"Filesystem {mount}: {used}% used ({available} available).\nFree up disk space to avoid service disruption.","label":"Low disk space"},"disk_io_error":{"title":"{hostname}: Disk failure detected — {device}","body":"I/O error or disk failure detected on device {device}.\n{reason}","label":"Disk failure / I/O error"},"storage_unavailable":{"title":"{hostname}: Storage unavailable — {storage_name}","body":"PVE storage \"{storage_name}\" (type: {storage_type}) is not accessible.\nReason: {reason}","label":"Storage unavailable"},"smart_test_complete":{"title":"{hostname}: SMART test completed — {device}","body":"SMART {test_type} test on /dev/{device} has completed.\nResult: {result}\nDuration: {duration}","label":"SMART test completed"},"smart_test_failed":{"title":"{hostname}: SMART test FAILED — {device}","body":"SMART {test_type} test on /dev/{device} has failed.\nResult: {result}\nReason: {reason}","label":"SMART test FAILED"},"gpu_mode_switch":{"title":"{hostname}: GPU mode changed to {new_mode}","body":"GPU passthrough mode has been switched.\nGPU: {gpu_name} ({gpu_pci})\nPrevious mode: {old_mode}\nNew mode: {new_mode}\n{details}","label":"GPU mode switched"},"gpu_passthrough_blocked":{"title":"{hostname}: {guest_type} {guest_id} blocked at startup","body":"PCIe passthrough guard prevented {guest_type} {guest_id} ({guest_name}) from starting.\nReason: {reason}\n{details}","label":"GPU passthrough blocked"},"pci_passthrough_conflict":{"title":"{hostname}: PCIe device conflict detected — {device_pci}","body":"A PCIe device is assigned to multiple guests.\nDevice: {device_pci}\nConflicting guests: {guest_list}\nAction required: Stop one of the guests or reassign the device.","label":"PCIe device conflict"},"load_high":{"title":"{hostname}: High system load — {value}","body":"System load average is {value} on {cores} cores.\n{details}","label":"High system load"},"network_down":{"title":"{hostname}: Network connectivity lost{entity_suffix}","body":"The node has lost network connectivity.\nReason: {reason}","label":"Network connectivity lost"},"network_latency":{"title":"{hostname}: High network latency — {value}ms","body":"Latency to gateway: {value}ms (threshold: {threshold}ms).\nThis may indicate network congestion or hardware issues.","label":"High network latency"},"auth_fail":{"title":"{hostname}: Authentication failure","body":"Failed login attempt detected.\nSource IP: {source_ip}\nUser: {username}\nService: {service}","label":"Authentication failure"},"ip_block":{"title":"{hostname}: IP blocked by Fail2Ban","body":"IP address {source_ip} has been banned.\nJail: {jail}\nFailed attempts: {failures}","label":"IP blocked by Fail2Ban"},"firewall_issue":{"title":"{hostname}: Firewall issue detected{entity_suffix}","body":"A firewall configuration issue has been detected.\nReason: {reason}","label":"Firewall issue detected"},"user_permission_change":{"title":"{hostname}: User permission changed","body":"User: {username}\nChange: {change_details}","label":"User permission changed"},"split_brain":{"title":"{hostname}: Cluster event reported","body":"A cluster event was reported:\n{reason}","label":"Cluster event"},"node_disconnect":{"title":"{hostname}: Node {node_name} disconnected","body":"Node {node_name} has disconnected from the cluster.","label":"Node disconnected"},"node_reconnect":{"title":"{hostname}: Node {node_name} reconnected","body":"Node {node_name} has rejoined the cluster successfully.","label":"Node reconnected"},"system_startup":{"title":"{hostname}: {reason}","body":"{summary}","label":"System startup report"},"system_shutdown":{"title":"{hostname}: System shutting down","body":"The node is shutting down.\n{reason}","label":"System shutting down"},"system_reboot":{"title":"{hostname}: System rebooting","body":"The node is rebooting.\n{reason}","label":"System rebooting"},"system_restore_completed":{"title":"{hostname}: Host restore finished","body":"Post-restore tasks completed in background.\n\nGuests applied: {guests}\nBind-mount stubs: {stubs}\nStale node dirs removed: {stale_nodes}\nComponents reinstalled: {components}\nDuration: {duration}\n{warnings_block}\nThe node is now fully ready to use.","label":"Host restore completed"},"system_problem":{"title":"{hostname}: System problem detected{entity_suffix}","body":"A system-level problem has been detected.\nReason: {reason}","label":"System problem detected"},"service_fail":{"title":"{hostname}: Service failed — {service_name}","body":"System service \"{service_name}\" has failed.\nReason: {reason}","label":"Service failed"},"oom_kill":{"title":"{hostname}: OOM Kill — {process}","body":"Process \"{process}\" was killed by the Out-of-Memory manager.\n{reason}","label":"Out of memory kill"},"service_fail_batch":{"title":"{hostname}: {service_count} services failed{entity_suffix}","body":"{reason}","label":"Service fail batch"},"cron_output":{"title":"{hostname}: {pve_title}","body":"{reason}","label":"Cron job output (per-cron stdout via mail)"},"system_mail":{"title":"{hostname}: {pve_title}","body":"{reason}","label":"Smartd / mail bounces (PVE system mail)"},"apt_listchanges":{"title":"{hostname}: {pve_title}","body":"Upstream package information forwarded by Proxmox VE through apt-listchanges. The following text comes from the package maintainer and is not a ProxMenux recommendation.\n\n{reason}","label":"apt-listchanges package notices"},"webhook_test":{"title":"{hostname}: Webhook test received","body":"PVE webhook connectivity test successful.\n{reason}","label":"Webhook test"},"update_available":{"title":"{hostname}: Updates available","body":"Total updates: {total_count}\nSecurity: {security_count}\nProxmox: {pve_count}\nKernel: {kernel_count}\nImportant packages:\n{important_list}","label":"Updates available (legacy)"},"unknown_persistent":{"title":"{hostname}: Check unavailable - {category}{entity_suffix}","body":"Health check for {category} has been unavailable for 3+ cycles.\n{reason}","label":"Check unavailable"},"health_persistent":{"title":"{hostname}: {count} active health issue(s){entity_suffix}","body":"The following health issues remain unresolved:\n{issue_list}\n\nThis digest is sent once every 24 hours while issues persist.","label":"Active health issues (daily)"},"health_issue_new":{"title":"{hostname}: New health issue — {category}{entity_suffix}","body":"New {severity} issue detected in: {category}\nDetails: {reason}","label":"New health issue"},"health_issue_resolved":{"title":"{hostname}: Resolved - {category}{entity_suffix}","body":"{category} issue has been resolved.\n{reason}\nDuration: {duration}","label":"Health issue resolved"},"update_summary":{"title":"{hostname}: Updates available","body":"Total updates: {total_count}\nSecurity updates: {security_count}\nProxmox-related updates: {pve_count}\nKernel updates: {kernel_count}\nImportant packages:\n{important_list}","label":"Host package updates"},"pve_update":{"title":"{hostname}: Proxmox VE {new_version} available","body":"A new Proxmox VE release is available.\nCurrent: {current_version}\nNew: {new_version}\n{details}","label":"Proxmox VE update available"},"update_complete":{"title":"{hostname}: System update completed","body":"System packages have been successfully updated.\n{details}","label":"Host update completed"},"ai_model_migrated":{"title":"{hostname}: AI model updated","body":"The AI model for notifications has been automatically updated.\nProvider: {provider}\nPrevious model: {old_model}\nNew model: {new_model}\n\n{message}","label":"AI model auto-updated"},"proxmenux_update":{"title":"{hostname}: ProxMenux {new_version} available","body":"A new version of ProxMenux is available.\nCurrent: {current_version}\nNew: {new_version}","label":"ProxMenux update available"},"mount_stale":{"title":"{hostname}: stale remote mount {mount_target}","body":"Remote mount {mount_target} ({fstype}) from {mount_source} is stale{lxc_scope}.\nStat timed out or returned an error: {error}\n\nApps writing to this path will silently land on the underlying filesystem and may fill the disk. Remount or fix connectivity ASAP.","label":"Remote mount stale"},"mount_readonly":{"title":"{hostname}: remote mount {mount_target} is read-only","body":"Remote mount {mount_target} ({fstype}) from {mount_source} is mounted read-only{lxc_scope}. Writes will fail. If this was unintentional, remount with rw.","label":"Remote mount read-only"},"lxc_disk_low":{"title":"{hostname}: CT {vmid} rootfs at {usage_percent}%","body":"CT {vmid} ({name}) rootfs is at {usage_percent}% ({disk_bytes_human} / {maxdisk_bytes_human}).\n\nA full LXC rootfs prevents the container from booting cleanly. Either expand the rootfs (pct resize {vmid} rootfs +1G) or free space inside the container.","label":"LXC rootfs near full"},"vm_disk_low":{"title":"{hostname}: VM {vmid} filesystems at {usage_percent}%","body":"VM {vmid} ({name}) guest filesystems are at {usage_percent}% ({disk_bytes_human} / {maxdisk_bytes_human}).\n\nReported by the QEMU guest agent. Includes every persistent filesystem the guest mounts on a block device — virtual disks and PCI-passthrough drives alike. Free up space inside the guest or expand the affected storage before writes start to fail.","label":"VM filesystems near full"},"lxc_mount_low":{"title":"{hostname}: CT {vmid} mount {mount} at {usage_percent}%","body":"Mount {mount} inside CT {vmid} ({name}) is at {usage_percent}% used.\nFilesystem type: {fstype}\n\nA full mount inside a container often blocks the application silently — writes either fail or, worse, land on the rootfs and trigger the rootfs alert next. Free up space on the mount or expand it.","label":"LXC mount near full"},"pve_storage_full":{"title":"{hostname}: PVE storage {storage_name} at {usage_percent}%","body":"Proxmox storage \"{storage_name}\" (type: {storage_type}) is at {usage_percent}% used.\n\nOnce full, no new VM/CT can be provisioned and existing guests may fail to write. Move/delete unused volumes or expand the underlying pool/LV/RBD image.","label":"PVE storage near full"},"zfs_pool_full":{"title":"{hostname}: ZFS pool {pool_name} at {usage_percent}%","body":"ZFS pool \"{pool_name}\" is at {usage_percent}% capacity.\n\nZFS performance and write reliability degrade sharply above ~80% capacity (CoW needs free space for new blocks). Free up snapshots, prune old datasets, or add more vdevs to the pool.","label":"ZFS pool near full"},"post_install_update":{"title":"{hostname}: {count} ProxMenux optimization update(s) available","body":"{count} ProxMenux optimization update(s) available on this host.\n\n🛠️ Available versions:\n{tool_list}\n\n💡 Apply from:\n • ProxMenux Monitor → Settings → ProxMenux Optimizations\n • Or open the ProxMenux menu → Settings post-install Proxmox → \"Apply available updates\"","label":"ProxMenux optimization updates available"},"secure_gateway_update_available":{"title":"{hostname}: {app_name} update available{update_title_suffix}","body":"{app_name} (managed by ProxMenux) has 📦 {package_count} package update(s) pending in its container.\n{version_line}\n\n💡 Open ProxMenux Monitor > Security > Secure Gateway and click \"Update\" to apply.\n\n🗂️ Packages:\n{package_list}","label":"Secure Gateway update available"},"nvidia_driver_update_available":{"title":"{hostname}: NVIDIA driver update available — v{latest_version}","body":"A newer maintenance release is available for the installed NVIDIA driver branch.\n🔹 Currently installed: v{current_version}\n🟢 Latest available: v{latest_version}\n\n{upgrade_reason}\n\n💡 To reinstall:\n • From the ProxMenux post-install menu: {menu_label}\n\nReinstalling rebuilds the DKMS module against the running kernel and requires a reboot to load the new driver.","label":"NVIDIA driver update available"},"coral_driver_update_available":{"title":"{hostname}: Coral TPU driver update available — {latest_version}","body":"A newer {variant_label} is available.\n🔹 Currently installed: {current_version}\n🟢 Latest available: {latest_version}\n\n{upgrade_reason}\n\n💡 To reinstall:\n • From the ProxMenux post-install menu: {menu_label}\n\n{reboot_note}","label":"Coral TPU driver update available"},"burst_auth_fail":{"title":"{hostname}: +{count} more auth failures in {window}","body":"+{count} additional authentication failures detected in {window} ({total_count} total).\nSources: {entity_list}","label":"Auth failures burst"},"burst_ip_block":{"title":"{hostname}: Fail2Ban banned +{count} more IPs in {window}","body":"+{count} additional IPs banned by Fail2Ban in {window} ({total_count} total).\nIPs: {entity_list}","label":"IP block burst"},"burst_disk_io":{"title":"{hostname}: +{count} more disk I/O errors on {entity_list}","body":"+{count} additional I/O errors detected in {window} ({total_count} total).\nDevices: {entity_list}","label":"Disk I/O burst"},"burst_cluster":{"title":"{hostname}: Cluster flapping detected (+{count} more changes)","body":"Cluster state changed +{count} more times in {window} ({total_count} total).\nNodes: {entity_list}","label":"Cluster flapping burst"},"burst_service_fail":{"title":"{hostname}: +{count} more services failed in {window}","body":"+{count} additional service failures detected in {window} ({total_count} total).\nThis typically indicates a node reboot or PVE service restart.\n\nAdditional failures:\n{details}","label":"Service fail burst"},"burst_system":{"title":"{hostname}: +{count} more system problems in {window}","body":"+{count} additional system problems detected in {window} ({total_count} total).\n\nAdditional issues:\n{details}","label":"System problems burst"},"burst_generic":{"title":"{hostname}: +{count} more {event_type} events in {window}","body":"+{count} additional events of type {event_type} in {window} ({total_count} total).\n\nAdditional events:\n{details}","label":"Generic burst"},"kernel_warning":{"title":"{hostname}: Kernel diagnostic event detected","body":"The kernel recorded a diagnostic event: {kernel_details}","label":"Kernel warnings and diagnostic traces"}},"lxcUpdate":{"status":{"success":"succeeded","failure":"failed","partial":"completed partially","deferred":"deferred","skipped":"skipped"},"source":{"scheduled":"Scheduled","manual":"Manual"},"sourceLabel":"Source","targets":"Targets","osPending":"OS packages pending: {before} → {after}","osUnverified":"OS packages: update command executed; result not verified","applications":"Applications: {items}","applicationsUnverified":"Applications: updater executed; no tracked version change was observed","dockerEngineChange":"Docker Engine: {before} → {after}","dockerEngineVerified":"Docker Engine: verified at {version}","dockerEngineUnverified":"Docker Engine: update command executed; version not verified","dockerImagesPending":"Docker images pending: {before} → {after}","dockerImagesChanged":"Docker images changed: {images}","deferredTargets":"Deferred targets: {targets}","reason":"Reason: {reason}","restartRequired":"Restart required: {value}","yes":"yes","no":"no","verificationPending":"Verification pending until the container is running","verificationWarning":"Verification warning: {error}","duration":"Duration: {duration}"},"fallback":{"unknownTitle":"{hostname}: {event_type}","healthCheckDegraded":"Health check degraded","none":"none","temperatureAlertTitle":"{hostname}: Sensor temperature alert","temperatureAlertBody":"Temperature alert recorded without a complete measurement payload.","recordedReason":"Recorded reason: {reason}","recordedDetails":"Recorded details: {details}"},"fields":{"vmid":"VM/CT","name":"Name","device":"Device","sourceIp":"Source IP","node":"Node","category":"Category","service":"Service","jail":"Jail","user":"User","count":"Count","window":"Window","affected":"Affected"},"vzdump":{"size":"Size: {value}","duration":"Duration: {value}","file":"File","backups":"{count} backups","failed":"{count} failed","total":"Total: {value}","time":"Time: {value}"},"startup":{"issuesTitle":"{hostname}: System startup - {count} issue(s) detected","completeTitle":"{hostname}: System startup completed","operational":"All systems operational.","vmCountOne":"{count} VM","vmCountMany":"{count} VMs","ctCountOne":"{count} CT","ctCountMany":"{count} CTs","started":"✅ {counts} started","vmFailed":"❌ VM failed: {name} - {reason}","ctFailed":"❌ CT failed: {name} - {reason}","unknownError":"unknown error","storageUnavailable":"⚠️ Storage: {count} unavailable ({names})","servicesFailed":"⚠️ Services: {count} failed ({names})","duration":"⏱️ Startup completed in {minutes} min"},"appUpdates":{"singleTitle":"{hostname}: {app_name} update available on CT {vmid}","singleBody":"{app_name} on CT {vmid} ({ct_name}) has a new version:\n {installed} → {latest}","emptyTitle":"{hostname}: Application updates available","emptyBody":"Application updates are available.","batchTitle":"{hostname}: {count} application updates available","batchLeadOneContainer":"{count} applications in {container_count} LXC container have a newer version:","batchLeadManyContainers":"{count} applications in {container_count} LXC containers have a newer version:","additional":"… {count} additional application(s)","app":"app","unknown":"unknown"},"healthDegraded":{"categories":{"cpu":"CPU usage and temperature","memory":"Memory and swap","storage":"Storage mounts and space","disks":"Disk I/O and errors","network":"Network interfaces","vms":"VMs and containers","services":"PVE services","logs":"System logs","updates":"System updates","security":"Security"},"severity":{"critical":"Critical","warning":"Warning"},"singleTitle":"{hostname}: {severity} health status – {category}","multipleTitle":"{hostname}: {count} health checks degraded","multipleLine":"• {severity}: {category}\n {reason}","reasons":{"cpuSustained":"CPU is above {threshold}% for {duration}s."}},"digest":{"title":"{hostname}: 24h summary ({timestamp})","readFailed":"digest read failed: {error}","empty":"No INFO events buffered for this digest window.","lead":"{count} INFO events grouped by category:\n","more":" • … and {count} more","footer":"(Critical/Warning events arrived at the time they happened, not in this digest.)","quietTitle":"{hostname}: {count} events buffered during Quiet Hours","groups":{"vm_ct":"VM a CT","backup":"Backup","resources":"Resources","storage":"Storage","network":"Network","security":"Security","cluster":"Cluster","services":"Services","health":"Health","updates":"Updates","hardware":"Hardware","system":"System","other":"Other"}},"test":{"title":"ProxMenux Test","welcome":"Welcome to ProxMenux Monitor!","verify":"This is a test message to verify your notification channel is working correctly.","configuration":"Channel configuration:","iconsEnabled":"Icons: enabled","iconsDisabled":"Icons: disabled","aiEnabled":"AI: enabled ({info})","aiDisabled":"AI: disabled","alerts":"You will receive alerts about system events, health status changes, and security incidents.","photoCaption":"You can use this image as the profile photo for your notification bot."},"channels":{"discord":{"category":"Category","host":"Host","severity":"Severity"},"email":{"report":"{group} Report","details":"Details","host":"Host","footer":"ProxMenux Notification Service","severity":{"critical":"Critical","warning":"Warning","info":"Information","ok":"Resolved","default":"Notice"},"groups":{"vm_ct":"Virtual Machine / Container","backup":"Backup & Snapshot","resources":"System Resources","storage":"Storage","network":"Network","security":"Security","cluster":"Cluster","services":"System Services","health":"Health Monitor","updates":"System Updates","system":"System","hardware":"Hardware","other":"System Notification"},"fields":{"vmCtId":"VM/CT ID","name":"Name","action":"Action","targetNode":"Target Node","reason":"Reason","storage":"Storage","status":"Status","size":"Size","duration":"Duration","snapshot":"Snapshot","metric":"Metric","currentValue":"Current Value","threshold":"Threshold","cpuCores":"CPU Cores","memory":"Memory","temperature":"Temperature","mountPoint":"Mount Point","usage":"Usage","available":"Available","device":"Device","severity":"Severity","storageName":"Storage Name","type":"Type","interface":"Interface","latency":"Latency","event":"Event","sourceIp":"Source IP","username":"Username","service":"Service","jail":"Jail","failures":"Failures","change":"Change","node":"Node","quorum":"Quorum","nodesAffected":"Nodes Affected","process":"Process","category":"Category","previousSeverity":"Previous Severity","activeIssues":"Active Issues","totalUpdates":"Total Updates","securityUpdates":"Security Updates","proxmoxUpdates":"Proxmox Updates","kernelUpdates":"Kernel Updates","importantPackages":"Important Packages","currentVersion":"Current Version","newVersion":"New Version"},"status":{"failed":"Failed","completed":"Completed","started":"Started","unconfirmed":"Unconfirmed","completed_with_warnings":"Completed with warnings"}}},"temperature":{"sampleSpan":"High samples span {duration}."},"backup":{"unconfirmedTitle":"{hostname}: Backup outcome unconfirmed","confirmedTitle":"{hostname}: Backup complete","confirmedBody":"Backup completed successfully.","errorTitle":"{hostname}: Backup error reported","errorBody":"The backup report contains an error.","unconfirmedBody":"The backup outcome is not confirmed.","warningTitle":"{hostname}: Backup completed with warnings","warningBody":"Backup completed with warnings.","diagnosticsOmitted":"Additional diagnostic lines or text omitted: {count}. Original report retained."}}} + "runtime": {"notifications":{"templates":{"state_change":{"title":"{hostname}: {category} changed to {current}{entity_suffix}","body":"{category} status changed from {previous} to {current}.\n{reason}","label":"Health state changed"},"new_error":{"title":"{hostname}: New {severity} - {category}{entity_suffix}","body":"{reason}","label":"New health issue"},"error_resolved":{"title":"{hostname}: Resolved - {category}{entity_suffix}","body":"The {category} issue has been resolved.\n{reason}\n🚦 Previous severity: {original_severity}\n⏱️ Duration: {duration}","label":"Recovery notification"},"error_escalated":{"title":"{hostname}: Escalated to {severity} - {category}{entity_suffix}","body":"{reason}","label":"Health issue escalated"},"health_degraded":{"title":"{title_or_default}","body":"{reason}","label":"Health check degraded"},"lxc_updates_available":{"title":"{hostname}: {count} LXC(s) with package updates available","body":"📊 {count} LXC(s) with pending package updates (📦 {total_packages} total, 🔒 {security_count} security):\n\n{ct_list}","label":"LXC updates available (experimental)"},"lxc_update_applied":{"title":"{hostname}: LXC {ct_name} ({vmid}) update {result}","body":"{details}","label":"LXC update applied"},"oci_update_completed":{"title":"{hostname}: {app_name} updated","body":"{app_name} was updated to its new image and its data was kept.\nContainers: {containers}","label":"OCI application updated"},"oci_update_failed":{"title":"{hostname}: {app_name} update did not complete","body":"The update of {app_name} did not complete.\nReason: {reason}\nContainers: {containers}\nOpen Manage installed OCI applications to check its state.","label":"OCI application update failed"},"oci_recreate_completed":{"title":"{hostname}: {app_name} recreated","body":"{app_name} was recreated with its saved configuration and its data was kept.\nContainers: {containers}","label":"OCI application recreated"},"oci_recreate_failed":{"title":"{hostname}: {app_name} recreation did not complete","body":"The recreation of {app_name} did not complete.\nReason: {reason}\nContainers: {containers}\nOpen Manage installed OCI applications to check its state.","label":"OCI application recreation failed"},"oci_modify_completed":{"title":"{hostname}: {app_name} modified","body":"{app_name} was modified with its new options and its data was kept.\nContainers: {containers}","label":"OCI application modified"},"oci_modify_failed":{"title":"{hostname}: {app_name} modification did not complete","body":"The modification of {app_name} did not complete.\nReason: {reason}\nContainers: {containers}\nOpen Manage installed OCI applications to check its state.","label":"OCI application modification failed"},"oci_watchdog_restarted":{"title":"{hostname}: {app_name} restarted after it stopped","body":"{app_name} stopped on its own and the watchdog started it again.\nContainers: {containers}","label":"OCI application restarted by the watchdog"},"oci_watchdog_failed":{"title":"{hostname}: {app_name} could not be kept running","body":"The watchdog could not keep {app_name} running: it stopped again or did not start after several attempts, and it is left stopped.\nContainers: {containers}\nStart it by hand once the cause is solved; the watchdog then watches it again.","label":"OCI application left stopped by the watchdog"},"app_update_available":{"title":"{hostname}: {app_name} update available on CT {vmid}","body":"{app_name} on CT {vmid} ({ct_name}) has a new version:\n {installed} → {latest}","label":"App update available"},"docker_stack_update_available":{"title":"{hostname}: Docker updates available on CT {vmid}","body":"Container {ct_name} (CT {vmid}) has {count} Docker update(s):\n{details}","label":"Docker updates available"},"vm_start":{"title":"{hostname}: VM {vmname} ({vmid}) started","body":"Virtual machine {vmname} (ID: {vmid}) is now running.","label":"VM started"},"vm_start_warning":{"title":"{hostname}: VM {vmname} ({vmid}) started with warnings","body":"Virtual machine {vmname} (ID: {vmid}) started successfully but has warnings.\nWarnings: {reason}","label":"VM started (warnings)"},"vm_stop":{"title":"{hostname}: VM {vmname} ({vmid}) stopped","body":"Virtual machine {vmname} (ID: {vmid}) has been stopped.","label":"VM stopped"},"vm_shutdown":{"title":"{hostname}: VM {vmname} ({vmid}) shut down","body":"Virtual machine {vmname} (ID: {vmid}) has been cleanly shut down.","label":"VM shutdown"},"vm_fail":{"title":"{hostname}: VM {vmname} ({vmid}) FAILED","body":"Virtual machine {vmname} (ID: {vmid}) has crashed or failed to start.\nReason: {reason}","label":"VM FAILED"},"vm_restart":{"title":"{hostname}: VM {vmname} ({vmid}) restarted","body":"Virtual machine {vmname} (ID: {vmid}) has been restarted.","label":"VM restarted"},"ct_start":{"title":"{hostname}: CT {vmname} ({vmid}) started","body":"Container {vmname} (ID: {vmid}) is now running.","label":"CT started"},"ct_start_warning":{"title":"{hostname}: CT {vmname} ({vmid}) started with warnings","body":"Container {vmname} (ID: {vmid}) started successfully but has warnings.\nWarnings: {reason}","label":"CT started (warnings)"},"ct_stop":{"title":"{hostname}: CT {vmname} ({vmid}) stopped","body":"Container {vmname} (ID: {vmid}) has been stopped.","label":"CT stopped"},"ct_shutdown":{"title":"{hostname}: CT {vmname} ({vmid}) shut down","body":"Container {vmname} (ID: {vmid}) has been cleanly shut down.","label":"CT shutdown"},"ct_restart":{"title":"{hostname}: CT {vmname} ({vmid}) restarted","body":"Container {vmname} (ID: {vmid}) has been restarted.","label":"CT restarted"},"ct_fail":{"title":"{hostname}: CT {vmname} ({vmid}) FAILED","body":"Container {vmname} (ID: {vmid}) has crashed or failed to start.\nReason: {reason}","label":"CT FAILED"},"migration_start":{"title":"{hostname}: Migration started — {vmname} ({vmid})","body":"Live migration of {vmname} (ID: {vmid}) to node {target_node} has started.","label":"Migration started"},"migration_complete":{"title":"{hostname}: Migration complete — {vmname} ({vmid})","body":"{vmname} (ID: {vmid}) successfully migrated to node {target_node}.","label":"Migration complete"},"migration_warning":{"title":"{hostname}: Migration complete with warnings — {vmname} ({vmid})","body":"{vmname} (ID: {vmid}) migrated to node {target_node} but encountered warnings.\nWarnings: {reason}","label":"Migration (warnings)"},"migration_fail":{"title":"{hostname}: Migration FAILED — {vmname} ({vmid})","body":"Migration of {vmname} (ID: {vmid}) to node {target_node} failed.\nReason: {reason}","label":"Migration FAILED"},"replication_fail":{"title":"{hostname}: Replication FAILED — {vmname} ({vmid})","body":"Replication of {vmname} (ID: {vmid}) failed.\nReason: {reason}","label":"Replication FAILED"},"replication_complete":{"title":"{hostname}: Replication complete — {vmname} ({vmid})","body":"Replication of {vmname} (ID: {vmid}) completed successfully.","label":"Replication complete"},"backup_start":{"title":"{hostname}: Backup started on {storage}","body":"Backup job started on storage {storage}.\n{reason}","label":"Backup started"},"backup_complete":{"title":"{hostname} → {storage}: Backup complete — {vmname} ({vmid})","body":"Backup of {vmname} (ID: {vmid}) completed successfully on {storage}.\nSize: {size}","label":"Backup complete"},"backup_warning":{"title":"{hostname} → {storage}: Backup complete with warnings — {vmname} ({vmid})","body":"Backup of {vmname} (ID: {vmid}) on {storage} completed but encountered warnings.\nWarnings: {reason}","label":"Backup (warnings)"},"backup_fail":{"title":"{hostname} → {storage}: Backup FAILED — {vmname} ({vmid})","body":"Backup of {vmname} (ID: {vmid}) failed on {storage}.\nReason: {reason}","label":"Backup FAILED"},"host_backup_start":{"title":"{hostname}: Host backup started → {backend_label}","body":"Job: {job_id}\nBackend: {backend_label}\nDestination: {destination}\nProfile: {profile_mode}","label":"Host backup started"},"host_backup_complete":{"title":"{hostname}: Host backup complete → {backend_label}","body":"Job: {job_id}\nBackend: {backend_label}\nDestination: {destination}\nData size: {data_size}\nArchive size: {archive_size}\nDuration: {duration}","label":"Host backup complete"},"host_backup_fail":{"title":"{hostname}: Host backup FAILED → {backend_label}","body":"Job: {job_id}\nBackend: {backend_label}\nDestination: {destination}\nDuration before failure: {duration}\nReason: {reason}\nLog: {log_file}","label":"Host backup FAILED"},"snapshot_complete":{"title":"{hostname}: Snapshot created — {vmname} ({vmid})","body":"Snapshot \"{snapshot_name}\" created for {vmname} (ID: {vmid}).","label":"Snapshot created"},"snapshot_fail":{"title":"{hostname}: Snapshot FAILED — {vmname} ({vmid})","body":"Snapshot creation for {vmname} (ID: {vmid}) failed.\nReason: {reason}","label":"Snapshot FAILED"},"cpu_high":{"title":"{hostname}: High CPU usage — {value}%","body":"CPU usage has reached {value}% on {cores} cores.\n{details}","label":"High CPU usage"},"ram_high":{"title":"{hostname}: High memory usage — {value}%","body":"Memory usage: {used} / {total} ({value}%).\n{details}","label":"High memory usage"},"temp_high":{"title":"{hostname}: High sensor temperature — {value}°C","body":"Sensor temperature has reached {value}°C (threshold: {threshold}°C).\n{details}","label":"High temperature"},"disk_space_low":{"title":"{hostname}: Low disk space on {mount}","body":"Filesystem {mount}: {used}% used ({available} available).\nFree up disk space to avoid service disruption.","label":"Low disk space"},"disk_io_error":{"title":"{hostname}: Disk failure detected — {device}","body":"I/O error or disk failure detected on device {device}.\n{reason}","label":"Disk failure / I/O error"},"storage_unavailable":{"title":"{hostname}: Storage unavailable — {storage_name}","body":"PVE storage \"{storage_name}\" (type: {storage_type}) is not accessible.\nReason: {reason}","label":"Storage unavailable"},"smart_test_complete":{"title":"{hostname}: SMART test completed — {device}","body":"SMART {test_type} test on /dev/{device} has completed.\nResult: {result}\nDuration: {duration}","label":"SMART test completed"},"smart_test_failed":{"title":"{hostname}: SMART test FAILED — {device}","body":"SMART {test_type} test on /dev/{device} has failed.\nResult: {result}\nReason: {reason}","label":"SMART test FAILED"},"gpu_mode_switch":{"title":"{hostname}: GPU mode changed to {new_mode}","body":"GPU passthrough mode has been switched.\nGPU: {gpu_name} ({gpu_pci})\nPrevious mode: {old_mode}\nNew mode: {new_mode}\n{details}","label":"GPU mode switched"},"gpu_passthrough_blocked":{"title":"{hostname}: {guest_type} {guest_id} blocked at startup","body":"PCIe passthrough guard prevented {guest_type} {guest_id} ({guest_name}) from starting.\nReason: {reason}\n{details}","label":"GPU passthrough blocked"},"pci_passthrough_conflict":{"title":"{hostname}: PCIe device conflict detected — {device_pci}","body":"A PCIe device is assigned to multiple guests.\nDevice: {device_pci}\nConflicting guests: {guest_list}\nAction required: Stop one of the guests or reassign the device.","label":"PCIe device conflict"},"load_high":{"title":"{hostname}: High system load — {value}","body":"System load average is {value} on {cores} cores.\n{details}","label":"High system load"},"network_down":{"title":"{hostname}: Network connectivity lost{entity_suffix}","body":"The node has lost network connectivity.\nReason: {reason}","label":"Network connectivity lost"},"network_latency":{"title":"{hostname}: High network latency — {value}ms","body":"Latency to gateway: {value}ms (threshold: {threshold}ms).\nThis may indicate network congestion or hardware issues.","label":"High network latency"},"auth_fail":{"title":"{hostname}: Authentication failure","body":"Failed login attempt detected.\nSource IP: {source_ip}\nUser: {username}\nService: {service}","label":"Authentication failure"},"ip_block":{"title":"{hostname}: IP blocked by Fail2Ban","body":"IP address {source_ip} has been banned.\nJail: {jail}\nFailed attempts: {failures}","label":"IP blocked by Fail2Ban"},"firewall_issue":{"title":"{hostname}: Firewall issue detected{entity_suffix}","body":"A firewall configuration issue has been detected.\nReason: {reason}","label":"Firewall issue detected"},"user_permission_change":{"title":"{hostname}: User permission changed","body":"User: {username}\nChange: {change_details}","label":"User permission changed"},"split_brain":{"title":"{hostname}: Cluster event reported","body":"A cluster event was reported:\n{reason}","label":"Cluster event"},"node_disconnect":{"title":"{hostname}: Node {node_name} disconnected","body":"Node {node_name} has disconnected from the cluster.","label":"Node disconnected"},"node_reconnect":{"title":"{hostname}: Node {node_name} reconnected","body":"Node {node_name} has rejoined the cluster successfully.","label":"Node reconnected"},"system_startup":{"title":"{hostname}: {reason}","body":"{summary}","label":"System startup report"},"system_shutdown":{"title":"{hostname}: System shutting down","body":"The node is shutting down.\n{reason}","label":"System shutting down"},"system_reboot":{"title":"{hostname}: System rebooting","body":"The node is rebooting.\n{reason}","label":"System rebooting"},"system_restore_completed":{"title":"{hostname}: Host restore finished","body":"Post-restore tasks completed in background.\n\nGuest configurations copied: {guests}\nBind-mount stub directories created: {stubs}\nStale node dirs removed: {stale_nodes}\nComponents reinstalled: {components}\nDuration: {duration}\n{warnings_block}","label":"Host restore completed"},"system_problem":{"title":"{hostname}: System problem detected{entity_suffix}","body":"A system-level problem has been detected.\nReason: {reason}","label":"System problem detected"},"service_fail":{"title":"{hostname}: Service failed — {service_name}","body":"System service \"{service_name}\" has failed.\nReason: {reason}","label":"Service failed"},"oom_kill":{"title":"{hostname}: OOM Kill — {process}","body":"Process \"{process}\" was killed by the Out-of-Memory manager.\n{reason}","label":"Out of memory kill"},"service_fail_batch":{"title":"{hostname}: {service_count} services failed{entity_suffix}","body":"{reason}","label":"Service fail batch"},"cron_output":{"title":"{hostname}: {pve_title}","body":"{reason}","label":"Cron job output (per-cron stdout via mail)"},"system_mail":{"title":"{hostname}: {pve_title}","body":"{reason}","label":"Smartd / mail bounces (PVE system mail)"},"apt_listchanges":{"title":"{hostname}: {pve_title}","body":"Upstream package information forwarded by Proxmox VE through apt-listchanges. The following text comes from the package maintainer and is not a ProxMenux recommendation.\n\n{reason}","label":"apt-listchanges package notices"},"webhook_test":{"title":"{hostname}: Webhook test received","body":"PVE webhook connectivity test successful.\n{reason}","label":"Webhook test"},"update_available":{"title":"{hostname}: Updates available","body":"Total updates: {total_count}\nSecurity: {security_count}\nProxmox: {pve_count}\nKernel: {kernel_count}\nImportant packages:\n{important_list}","label":"Updates available (legacy)"},"unknown_persistent":{"title":"{hostname}: Check unavailable - {category}{entity_suffix}","body":"Health check for {category} has been unavailable for 3+ cycles.\n{reason}","label":"Check unavailable"},"health_persistent":{"title":"{hostname}: {count} active health issue(s){entity_suffix}","body":"The following health issues remain unresolved:\n{issue_list}\n\nThis digest is sent once every 24 hours while issues persist.","label":"Active health issues (daily)"},"health_issue_new":{"title":"{hostname}: New health issue — {category}{entity_suffix}","body":"New {severity} issue detected in: {category}\nDetails: {reason}","label":"New health issue"},"health_issue_resolved":{"title":"{hostname}: Resolved - {category}{entity_suffix}","body":"{category} issue has been resolved.\n{reason}\nDuration: {duration}","label":"Health issue resolved"},"update_summary":{"title":"{hostname}: Updates available","body":"Total updates: {total_count}\nSecurity updates: {security_count}\nProxmox-related updates: {pve_count}\nKernel updates: {kernel_count}\nImportant packages:\n{important_list}","label":"Host package updates"},"pve_update":{"title":"{hostname}: Proxmox VE {new_version} available","body":"A new Proxmox VE release is available.\nCurrent: {current_version}\nNew: {new_version}\n{details}","label":"Proxmox VE update available"},"update_complete":{"title":"{hostname}: System update completed","body":"System packages have been successfully updated.\n{details}","label":"Host update completed"},"ai_model_migrated":{"title":"{hostname}: AI model updated","body":"The AI model for notifications has been automatically updated.\nProvider: {provider}\nPrevious model: {old_model}\nNew model: {new_model}\n\n{message}","label":"AI model auto-updated"},"proxmenux_update":{"title":"{hostname}: ProxMenux {new_version} available","body":"A new version of ProxMenux is available.\nCurrent: {current_version}\nNew: {new_version}","label":"ProxMenux update available"},"mount_stale":{"title":"{hostname}: stale remote mount {mount_target}","body":"Remote mount {mount_target} ({fstype}) from {mount_source} is stale{lxc_scope}.\nStat timed out or returned an error: {error}\n\nApps writing to this path will silently land on the underlying filesystem and may fill the disk. Remount or fix connectivity ASAP.","label":"Remote mount stale"},"mount_readonly":{"title":"{hostname}: remote mount {mount_target} is read-only","body":"Remote mount {mount_target} ({fstype}) from {mount_source} is mounted read-only{lxc_scope}. Writes will fail. If this was unintentional, remount with rw.","label":"Remote mount read-only"},"lxc_disk_low":{"title":"{hostname}: CT {vmid} rootfs at {usage_percent}%","body":"CT {vmid} ({name}) rootfs is at {usage_percent}% ({disk_bytes_human} / {maxdisk_bytes_human}).\n\nA full LXC rootfs prevents the container from booting cleanly. Either expand the rootfs (pct resize {vmid} rootfs +1G) or free space inside the container.","label":"LXC rootfs near full"},"vm_disk_low":{"title":"{hostname}: VM {vmid} filesystems at {usage_percent}%","body":"VM {vmid} ({name}) guest filesystems are at {usage_percent}% ({disk_bytes_human} / {maxdisk_bytes_human}).\n\nReported by the QEMU guest agent. Includes every persistent filesystem the guest mounts on a block device — virtual disks and PCI-passthrough drives alike. Free up space inside the guest or expand the affected storage before writes start to fail.","label":"VM filesystems near full"},"lxc_mount_low":{"title":"{hostname}: CT {vmid} mount {mount} at {usage_percent}%","body":"Mount {mount} inside CT {vmid} ({name}) is at {usage_percent}% used.\nFilesystem type: {fstype}\n\nA full mount inside a container often blocks the application silently — writes either fail or, worse, land on the rootfs and trigger the rootfs alert next. Free up space on the mount or expand it.","label":"LXC mount near full"},"pve_storage_full":{"title":"{hostname}: PVE storage {storage_name} at {usage_percent}%","body":"Proxmox storage \"{storage_name}\" (type: {storage_type}) is at {usage_percent}% used.\n\nOnce full, no new VM/CT can be provisioned and existing guests may fail to write. Move/delete unused volumes or expand the underlying pool/LV/RBD image.","label":"PVE storage near full"},"zfs_pool_full":{"title":"{hostname}: ZFS pool {pool_name} at {usage_percent}%","body":"ZFS pool \"{pool_name}\" is at {usage_percent}% capacity.\n\nZFS performance and write reliability degrade sharply above ~80% capacity (CoW needs free space for new blocks). Free up snapshots, prune old datasets, or add more vdevs to the pool.","label":"ZFS pool near full"},"post_install_update":{"title":"{hostname}: {count} ProxMenux optimization update(s) available","body":"{count} ProxMenux optimization update(s) available on this host.\n\n🛠️ Available versions:\n{tool_list}\n\n💡 Apply from:\n • ProxMenux Monitor → Settings → ProxMenux Optimizations\n • Or open the ProxMenux menu → Settings post-install Proxmox → \"Apply available updates\"","label":"ProxMenux optimization updates available"},"secure_gateway_update_available":{"title":"{hostname}: {app_name} update available{update_title_suffix}","body":"{app_name} (managed by ProxMenux) has 📦 {package_count} package update(s) pending in its container.\n{version_line}\n\n💡 Open ProxMenux Monitor > Security > Secure Gateway and click \"Update\" to apply.\n\n🗂️ Packages:\n{package_list}","label":"Secure Gateway update available"},"nvidia_driver_update_available":{"title":"{hostname}: NVIDIA driver update available — v{latest_version}","body":"A newer maintenance release is available for the installed NVIDIA driver branch.\n🔹 Currently installed: v{current_version}\n🟢 Latest available: v{latest_version}\n\n{upgrade_reason}\n\n💡 To reinstall:\n • From the ProxMenux post-install menu: {menu_label}\n\nReinstalling rebuilds the DKMS module against the running kernel and requires a reboot to load the new driver.","label":"NVIDIA driver update available"},"coral_driver_update_available":{"title":"{hostname}: Coral TPU driver update available — {latest_version}","body":"A newer {variant_label} is available.\n🔹 Currently installed: {current_version}\n🟢 Latest available: {latest_version}\n\n{upgrade_reason}\n\n💡 To reinstall:\n • From the ProxMenux post-install menu: {menu_label}\n\n{reboot_note}","label":"Coral TPU driver update available"},"burst_auth_fail":{"title":"{hostname}: +{count} more auth failures in {window}","body":"+{count} additional authentication failures detected in {window} ({total_count} total).\nSources: {entity_list}","label":"Auth failures burst"},"burst_ip_block":{"title":"{hostname}: Fail2Ban banned +{count} more IPs in {window}","body":"+{count} additional IPs banned by Fail2Ban in {window} ({total_count} total).\nIPs: {entity_list}","label":"IP block burst"},"burst_disk_io":{"title":"{hostname}: +{count} more disk I/O errors on {entity_list}","body":"+{count} additional I/O errors detected in {window} ({total_count} total).\nDevices: {entity_list}","label":"Disk I/O burst"},"burst_cluster":{"title":"{hostname}: Cluster flapping detected (+{count} more changes)","body":"Cluster state changed +{count} more times in {window} ({total_count} total).\nNodes: {entity_list}","label":"Cluster flapping burst"},"burst_service_fail":{"title":"{hostname}: +{count} more services failed in {window}","body":"+{count} additional service failures detected in {window} ({total_count} total).\nThis typically indicates a node reboot or PVE service restart.\n\nAdditional failures:\n{details}","label":"Service fail burst"},"burst_system":{"title":"{hostname}: +{count} more system problems in {window}","body":"+{count} additional system problems detected in {window} ({total_count} total).\n\nAdditional issues:\n{details}","label":"System problems burst"},"burst_generic":{"title":"{hostname}: +{count} more {event_type} events in {window}","body":"+{count} additional events of type {event_type} in {window} ({total_count} total).\n\nAdditional events:\n{details}","label":"Generic burst"},"kernel_warning":{"title":"{hostname}: Kernel diagnostic event detected","body":"The kernel recorded a diagnostic event: {kernel_details}","label":"Kernel warnings and diagnostic traces"}},"lxcUpdate":{"status":{"success":"succeeded","failure":"failed","partial":"completed partially","deferred":"deferred","skipped":"skipped"},"source":{"scheduled":"Scheduled","manual":"Manual"},"sourceLabel":"Source","targets":"Targets","osPending":"OS packages pending: {before} → {after}","osUnverified":"OS packages: update command executed; result not verified","applications":"Applications: {items}","applicationsUnverified":"Applications: updater executed; no tracked version change was observed","dockerEngineChange":"Docker Engine: {before} → {after}","dockerEngineVerified":"Docker Engine: verified at {version}","dockerEngineUnverified":"Docker Engine: update command executed; version not verified","dockerImagesPending":"Docker images pending: {before} → {after}","dockerImagesChanged":"Docker images changed: {images}","deferredTargets":"Deferred targets: {targets}","reason":"Reason: {reason}","restartRequired":"Restart required: {value}","yes":"yes","no":"no","verificationPending":"Verification pending until the container is running","verificationWarning":"Verification warning: {error}","duration":"Duration: {duration}"},"fallback":{"unknownTitle":"{hostname}: {event_type}","healthCheckDegraded":"Health check degraded","none":"none","temperatureAlertTitle":"{hostname}: Sensor temperature alert","temperatureAlertBody":"Temperature alert recorded without a complete measurement payload.","recordedReason":"Recorded reason: {reason}","recordedDetails":"Recorded details: {details}"},"fields":{"vmid":"VM/CT","name":"Name","device":"Device","sourceIp":"Source IP","node":"Node","category":"Category","service":"Service","jail":"Jail","user":"User","count":"Count","window":"Window","affected":"Affected"},"vzdump":{"size":"Size: {value}","duration":"Duration: {value}","file":"File","backups":"{count} backups","failed":"{count} failed","total":"Total: {value}","time":"Time: {value}"},"startup":{"issuesTitle":"{hostname}: System startup - {count} issue(s) detected","completeTitle":"{hostname}: System startup completed","operational":"All systems operational.","vmCountOne":"{count} VM","vmCountMany":"{count} VMs","ctCountOne":"{count} CT","ctCountMany":"{count} CTs","started":"✅ {counts} started","vmFailed":"❌ VM failed: {name} - {reason}","ctFailed":"❌ CT failed: {name} - {reason}","unknownError":"unknown error","storageUnavailable":"⚠️ Storage: {count} unavailable ({names})","servicesFailed":"⚠️ Services: {count} failed ({names})","duration":"⏱️ Startup completed in {minutes} min"},"appUpdates":{"singleTitle":"{hostname}: {app_name} update available on CT {vmid}","singleBody":"{app_name} on CT {vmid} ({ct_name}) has a new version:\n {installed} → {latest}","emptyTitle":"{hostname}: Application updates available","emptyBody":"Application updates are available.","batchTitle":"{hostname}: {count} application updates available","batchLeadOneContainer":"{count} applications in {container_count} LXC container have a newer version:","batchLeadManyContainers":"{count} applications in {container_count} LXC containers have a newer version:","additional":"… {count} additional application(s)","app":"app","unknown":"unknown"},"healthDegraded":{"categories":{"cpu":"CPU usage and temperature","memory":"Memory and swap","storage":"Storage mounts and space","disks":"Disk I/O and errors","network":"Network interfaces","vms":"VMs and containers","services":"PVE services","logs":"System logs","updates":"System updates","security":"Security"},"severity":{"critical":"Critical","warning":"Warning"},"singleTitle":"{hostname}: {severity} health status – {category}","multipleTitle":"{hostname}: {count} health checks degraded","multipleLine":"• {severity}: {category}\n {reason}","reasons":{"cpuSustained":"CPU is above {threshold}% for {duration}s."}},"digest":{"title":"{hostname}: 24h summary ({timestamp})","readFailed":"digest read failed: {error}","empty":"No INFO events buffered for this digest window.","lead":"{count} INFO events grouped by category:\n","more":" • … and {count} more","footer":"(Critical/Warning events arrived at the time they happened, not in this digest.)","quietTitle":"{hostname}: {count} events buffered during Quiet Hours","groups":{"vm_ct":"VM a CT","backup":"Backup","resources":"Resources","storage":"Storage","network":"Network","security":"Security","cluster":"Cluster","services":"Services","health":"Health","updates":"Updates","hardware":"Hardware","system":"System","other":"Other"}},"test":{"title":"ProxMenux Test","welcome":"Welcome to ProxMenux Monitor!","verify":"This is a test message to verify your notification channel is working correctly.","configuration":"Channel configuration:","iconsEnabled":"Icons: enabled","iconsDisabled":"Icons: disabled","aiEnabled":"AI: enabled ({info})","aiDisabled":"AI: disabled","alerts":"You will receive alerts about system events, health status changes, and security incidents.","photoCaption":"You can use this image as the profile photo for your notification bot."},"channels":{"discord":{"category":"Category","host":"Host","severity":"Severity"},"email":{"report":"{group} Report","details":"Details","host":"Host","footer":"ProxMenux Notification Service","severity":{"critical":"Critical","warning":"Warning","info":"Information","ok":"Resolved","default":"Notice"},"groups":{"vm_ct":"Virtual Machine / Container","backup":"Backup & Snapshot","resources":"System Resources","storage":"Storage","network":"Network","security":"Security","cluster":"Cluster","services":"System Services","health":"Health Monitor","updates":"System Updates","system":"System","hardware":"Hardware","other":"System Notification"},"fields":{"vmCtId":"VM/CT ID","name":"Name","action":"Action","targetNode":"Target Node","reason":"Reason","storage":"Storage","status":"Status","size":"Size","duration":"Duration","snapshot":"Snapshot","metric":"Metric","currentValue":"Current Value","threshold":"Threshold","cpuCores":"CPU Cores","memory":"Memory","temperature":"Temperature","mountPoint":"Mount Point","usage":"Usage","available":"Available","device":"Device","severity":"Severity","storageName":"Storage Name","type":"Type","interface":"Interface","latency":"Latency","event":"Event","sourceIp":"Source IP","username":"Username","service":"Service","jail":"Jail","failures":"Failures","change":"Change","node":"Node","quorum":"Quorum","nodesAffected":"Nodes Affected","process":"Process","category":"Category","previousSeverity":"Previous Severity","activeIssues":"Active Issues","totalUpdates":"Total Updates","securityUpdates":"Security Updates","proxmoxUpdates":"Proxmox Updates","kernelUpdates":"Kernel Updates","importantPackages":"Important Packages","currentVersion":"Current Version","newVersion":"New Version"},"status":{"failed":"Failed","completed":"Completed","started":"Started","unconfirmed":"Unconfirmed","completed_with_warnings":"Completed with warnings"}}},"temperature":{"sampleSpan":"High samples span {duration}."},"backup":{"unconfirmedTitle":"{hostname}: Backup outcome unconfirmed","confirmedTitle":"{hostname}: Backup complete","confirmedBody":"Backup completed successfully.","errorTitle":"{hostname}: Backup error reported","errorBody":"The backup report contains an error.","unconfirmedBody":"The backup outcome is not confirmed.","warningTitle":"{hostname}: Backup completed with warnings","warningBody":"Backup completed with warnings.","diagnosticsOmitted":"Additional diagnostic lines or text omitted: {count}. Original report retained."}}} } diff --git a/AppImage/messages/es/common.json b/AppImage/messages/es/common.json index e8b50751..d0e9d486 100644 --- a/AppImage/messages/es/common.json +++ b/AppImage/messages/es/common.json @@ -1998,7 +1998,7 @@ "oci_modify_completed": "Aplicación OCI modificada", "oci_modify_failed": "Modificación de aplicación OCI fallida", "oci_watchdog_restarted": "Aplicación OCI reiniciada por la vigilancia", - "oci_watchdog_failed": "Aplicación OCI que se detiene una y otra vez", + "oci_watchdog_failed": "Aplicación OCI que la vigilancia deja parada", "app_update_available": "Actualización de app disponible", "ai_model_migrated": "Modelo de IA actualizado automáticamente", "proxmenux_update": "Actualización de ProxMenux disponible", @@ -6451,9 +6451,9 @@ "label": "Aplicación OCI reiniciada por la vigilancia" }, "oci_watchdog_failed": { - "title": "{hostname}: {app_name} se detiene una y otra vez", - "body": "{app_name} sigue deteniéndose solo y la vigilancia ya no lo reinicia.\nContenedores: {containers}\nInícialo a mano cuando la causa esté resuelta; la vigilancia volverá a vigilarlo.", - "label": "Aplicación OCI que se detiene una y otra vez" + "title": "{hostname}: {app_name} no se ha podido mantener en marcha", + "body": "La vigilancia no ha podido mantener {app_name} en marcha: volvió a detenerse o no arrancó tras varios intentos, y se deja parado.\nContenedores: {containers}\nInícialo a mano cuando la causa esté resuelta; la vigilancia volverá a vigilarlo.", + "label": "Aplicación OCI que la vigilancia deja parada" }, "app_update_available": { "title": "{hostname}: actualización de {app_name} disponible en CT {vmid}", @@ -6722,7 +6722,7 @@ }, "system_restore_completed": { "title": "{hostname}: Restauración del host finalizada", - "body": "Tareas posteriores a la restauración completadas en segundo plano.\n\nGuests aplicados: {guests}\nStubs de bind mount: {stubs}\nDirectorios de nodos obsoletos eliminados: {stale_nodes}\nComponentes reinstalados: {components}\nDuración: {duration}\n{warnings_block}\nEl nodo está listo para usarse.", + "body": "Tareas posteriores a la restauración completadas en segundo plano.\n\nConfiguración de guests copiada: {guests}\nDirectorios vacíos creados para bind mounts: {stubs}\nDirectorios de nodos obsoletos eliminados: {stale_nodes}\nComponentes reinstalados: {components}\nDuración: {duration}\n{warnings_block}", "label": "Restauración del host completada" }, "system_problem": { diff --git a/AppImage/messages/fr/common.json b/AppImage/messages/fr/common.json index 34c3cb82..c8f3ae28 100644 --- a/AppImage/messages/fr/common.json +++ b/AppImage/messages/fr/common.json @@ -2011,7 +2011,7 @@ "oci_modify_completed": "Application OCI modifiée", "oci_modify_failed": "Échec de la modification de l'application OCI", "oci_watchdog_restarted": "Application OCI redémarrée par la surveillance", - "oci_watchdog_failed": "Application OCI qui s'arrête sans cesse", + "oci_watchdog_failed": "Application OCI laissée arrêtée par la surveillance", "app_update_available": "mise à jour de l'application disponible", "lxc_update_applied": "Mise à jour LXC appliquée", "docker_stack_update_available": "Docker mises à jour disponibles" @@ -6451,9 +6451,9 @@ "label": "Application OCI redémarrée par la surveillance" }, "oci_watchdog_failed": { - "title": "{hostname} : {app_name} s'arrête sans cesse", - "body": "{app_name} continue de s'arrêter tout seul et la surveillance ne le redémarre plus.\nConteneurs : {containers}\nDémarrez-le à la main une fois la cause résolue ; la surveillance le reprend alors.", - "label": "Application OCI qui s'arrête sans cesse" + "title": "{hostname} : {app_name} n'a pas pu être maintenu en marche", + "body": "La surveillance n'a pas pu maintenir {app_name} en marche : il s'est arrêté de nouveau ou n'a pas démarré après plusieurs tentatives, et il reste arrêté.\nConteneurs : {containers}\nDémarrez-le à la main une fois la cause résolue ; la surveillance le reprend alors.", + "label": "Application OCI laissée arrêtée par la surveillance" }, "app_update_available": { "title": "{hostname} : mise à jour {app_name} disponible sur CT {vmid}", @@ -6722,7 +6722,7 @@ }, "system_restore_completed": { "title": "{hostname} : restauration de l'hôte terminée", - "body": "Tâches post-restauration effectuées en arrière-plan.\n\nInvités postulés : {guests}\nTalons de montage liés : {stubs}\nRépertoires de nœuds obsolètes supprimés : {stale_nodes}\nComposants réinstallés : {components}\nDurée : {duration}\n{warnings_block}\nLe nœud est maintenant entièrement prêt à être utilisé.", + "body": "Tâches post-restauration effectuées en arrière-plan.\n\nConfigurations de VM et CT copiées : {guests}\nRépertoires vides créés pour les bind mounts : {stubs}\nRépertoires de nœuds obsolètes supprimés : {stale_nodes}\nComposants réinstallés : {components}\nDurée : {duration}\n{warnings_block}", "label": "Restauration de l'hôte terminée" }, "system_problem": { diff --git a/AppImage/messages/it/common.json b/AppImage/messages/it/common.json index ea8eb818..d3bee74d 100644 --- a/AppImage/messages/it/common.json +++ b/AppImage/messages/it/common.json @@ -2011,7 +2011,7 @@ "oci_modify_completed": "App OCI modificata", "oci_modify_failed": "Modifica app OCI non riuscita", "oci_watchdog_restarted": "App OCI riavviata dal watchdog", - "oci_watchdog_failed": "App OCI che continua ad arrestarsi", + "oci_watchdog_failed": "App OCI lasciata ferma dal watchdog", "app_update_available": "Aggiornamento app disponibile", "lxc_update_applied": "Aggiornamento LXC applicato", "docker_stack_update_available": "Aggiornamenti Docker disponibili" @@ -6451,9 +6451,9 @@ "label": "App OCI riavviata dal watchdog" }, "oci_watchdog_failed": { - "title": "{hostname}: {app_name} continua ad arrestarsi", - "body": "{app_name} continua ad arrestarsi da solo e il watchdog non lo riavvia più.\nContainer: {containers}\nAvvialo a mano una volta risolta la causa; il watchdog tornerà a sorvegliarlo.", - "label": "App OCI che continua ad arrestarsi" + "title": "{hostname}: non è stato possibile mantenere {app_name} in esecuzione", + "body": "Il watchdog non è riuscito a mantenere {app_name} in esecuzione: si è arrestato di nuovo o non si è avviato dopo diversi tentativi, e viene lasciato fermo.\nContainer: {containers}\nAvvialo a mano una volta risolta la causa; il watchdog tornerà a sorvegliarlo.", + "label": "App OCI lasciata ferma dal watchdog" }, "app_update_available": { "title": "{hostname}: aggiornamento {app_name} disponibile su CT {vmid}", @@ -6722,7 +6722,7 @@ }, "system_restore_completed": { "title": "{hostname}: ripristino host terminato", - "body": "Attività post-ripristino completate in background.\n\nGli ospiti hanno presentato domanda: {guests}\nStub con montaggio tramite collegamento: {stubs}\nDirectory dei nodi obsolete rimosse: {stale_nodes}\nComponenti reinstallati: {components}\nDurata: {duration}\n{warnings_block}\nIl nodo è ora completamente pronto per l'uso.", + "body": "Attività post-ripristino completate in background.\n\nConfigurazioni dei guest copiate: {guests}\nDirectory stub per i bind mount create: {stubs}\nDirectory dei nodi obsolete rimosse: {stale_nodes}\nComponenti reinstallati: {components}\nDurata: {duration}\n{warnings_block}", "label": "Ripristino host completato" }, "system_problem": { diff --git a/AppImage/messages/pt/common.json b/AppImage/messages/pt/common.json index df2e44ab..fbf795c0 100644 --- a/AppImage/messages/pt/common.json +++ b/AppImage/messages/pt/common.json @@ -2011,7 +2011,7 @@ "oci_modify_completed": "Aplicação OCI modificada", "oci_modify_failed": "Falha na modificação da aplicação OCI", "oci_watchdog_restarted": "Aplicação OCI reiniciada pela vigilância", - "oci_watchdog_failed": "Aplicação OCI que continua a parar", + "oci_watchdog_failed": "Aplicação OCI deixada parada pela vigilância", "app_update_available": "atualização de aplicativo disponível", "lxc_update_applied": "Atualização LXC aplicada", "docker_stack_update_available": "Docker atualizações disponíveis" @@ -6451,9 +6451,9 @@ "label": "Aplicação OCI reiniciada pela vigilância" }, "oci_watchdog_failed": { - "title": "{hostname}: {app_name} continua a parar", - "body": "{app_name} continua a parar sozinho e a vigilância já não o reinicia.\nContêineres: {containers}\nInicie-o à mão quando a causa estiver resolvida; a vigilância volta então a vigiá-lo.", - "label": "Aplicação OCI que continua a parar" + "title": "{hostname}: não foi possível manter {app_name} em execução", + "body": "A vigilância não conseguiu manter {app_name} em execução: voltou a parar ou não arrancou após várias tentativas, e fica parado.\nContêineres: {containers}\nInicie-o à mão quando a causa estiver resolvida; a vigilância volta então a vigiá-lo.", + "label": "Aplicação OCI deixada parada pela vigilância" }, "app_update_available": { "title": "{hostname}: atualização {app_name} disponível no CT {vmid}", @@ -6722,7 +6722,7 @@ }, "system_restore_completed": { "title": "{hostname}: restauração do host concluída", - "body": "Tarefas pós-restauração concluídas em segundo plano.\n\nConvidados inscritos: {guests}\nStubs de montagem de ligação: {stubs}\nDiretórios de nó obsoletos removidos: {stale_nodes}\nComponentes reinstalados: {components}\nDuração: {duration}\n{warnings_block}\nO nó agora está totalmente pronto para uso.", + "body": "Tarefas pós-restauração concluídas em segundo plano.\n\nConfigurações de VM e CT copiadas: {guests}\nDiretórios vazios criados para bind mounts: {stubs}\nDiretórios de nó obsoletos removidos: {stale_nodes}\nComponentes reinstalados: {components}\nDuração: {duration}\n{warnings_block}", "label": "Restauração do host concluída" }, "system_problem": { diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index aa35128c..9cd8b827 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -2012,7 +2012,7 @@ "oci_modify_completed": "OCI aplikácia upravená", "oci_modify_failed": "Úprava OCI aplikácie zlyhala", "oci_watchdog_restarted": "OCI aplikácia reštartovaná watchdogom", - "oci_watchdog_failed": "OCI aplikácia sa opakovane zastavuje", + "oci_watchdog_failed": "OCI aplikácia ponechaná zastavená watchdogom", "app_update_available": "K dispozícii je aktualizácia aplikácie" }, "ui": { @@ -6450,9 +6450,9 @@ "label": "OCI aplikácia reštartovaná watchdogom" }, "oci_watchdog_failed": { - "title": "{hostname}: {app_name} sa opakovane zastavuje", - "body": "Aplikácia {app_name} sa stále sama zastavuje a watchdog ju už nereštartuje.\nKontajnery: {containers}\nPo odstránení príčiny ju spustite ručne; watchdog ju potom bude znova sledovať.", - "label": "OCI aplikácia sa opakovane zastavuje" + "title": "{hostname}: {app_name} sa nepodarilo udržať v chode", + "body": "Watchdogu sa nepodarilo udržať aplikáciu {app_name} v chode: znova sa zastavila alebo sa po viacerých pokusoch nespustila a zostáva zastavená.\nKontajnery: {containers}\nPo odstránení príčiny ju spustite ručne; watchdog ju potom bude znova sledovať.", + "label": "OCI aplikácia ponechaná zastavená watchdogom" }, "app_update_available": { "title": "{hostname}: Pre {app_name} je na CT {vmid} dostupná aktualizácia", @@ -6721,7 +6721,7 @@ }, "system_restore_completed": { "title": "{hostname}: Obnova servera dokončená", - "body": "Úlohy po obnove boli dokončené na pozadí.\n\nPoužité VM a LXC: {guests}\nZástupné priečinky bind mountov: {stubs}\nOdstránené zastarané priečinky uzlov: {stale_nodes}\nPreinštalované súčasti: {components}\nTrvanie: {duration}\n{warnings_block}\nUzol je teraz úplne pripravený na použitie.", + "body": "Úlohy po obnove boli dokončené na pozadí.\n\nPoužité VM a LXC: {guests}\nZástupné priečinky bind mountov: {stubs}\nOdstránené zastarané priečinky uzlov: {stale_nodes}\nPreinštalované súčasti: {components}\nTrvanie: {duration}\n{warnings_block}", "label": "Obnova servera bola dokončená" }, "system_problem": { diff --git a/AppImage/messages/sv/common.json b/AppImage/messages/sv/common.json index c95baa74..998ac3c3 100644 --- a/AppImage/messages/sv/common.json +++ b/AppImage/messages/sv/common.json @@ -2011,7 +2011,7 @@ "oci_modify_completed": "OCI-applikation ändrad", "oci_modify_failed": "Ändring av OCI-applikation misslyckades", "oci_watchdog_restarted": "OCI-applikation omstartad av watchdog", - "oci_watchdog_failed": "OCI-applikation stannar om och om igen", + "oci_watchdog_failed": "OCI-applikation lämnad stoppad av watchdog", "app_update_available": "Appuppdatering tillgänglig", "lxc_update_applied": "LXC uppdatering tillämpad", "docker_stack_update_available": "Docker uppdateringar tillgängliga" @@ -6451,9 +6451,9 @@ "label": "OCI-applikation omstartad av watchdog" }, "oci_watchdog_failed": { - "title": "{hostname}: {app_name} stannar om och om igen", - "body": "{app_name} fortsätter att stanna av sig själv och watchdog startar inte längre om den.\nContainrar: {containers}\nStarta den manuellt när orsaken är löst; watchdog övervakar den sedan igen.", - "label": "OCI-applikation stannar om och om igen" + "title": "{hostname}: {app_name} kunde inte hållas igång", + "body": "Watchdog kunde inte hålla {app_name} igång: den stannade igen eller startade inte efter flera försök, och den lämnas stoppad.\nContainrar: {containers}\nStarta den manuellt när orsaken är löst; watchdog övervakar den sedan igen.", + "label": "OCI-applikation lämnad stoppad av watchdog" }, "app_update_available": { "title": "{hostname}: {app_name} uppdatering tillgänglig på CT {vmid}", @@ -6722,7 +6722,7 @@ }, "system_restore_completed": { "title": "{hostname}: Värdåterställning avslutad", - "body": "Uppgifter efter återställning slutförda i bakgrunden.\n\nGäster ansökte: {guests}\nBind-monterade stubbar: {stubs}\nInaktuella nodkataloger har tagits bort: {stale_nodes}\nKomponenter installerade om: {components}\nVaraktighet: {duration}\n{warnings_block}\nNoden är nu helt redo att användas.", + "body": "Uppgifter efter återställning slutförda i bakgrunden.\n\nGästkonfigurationer kopierade: {guests}\nPlatshållarkataloger för bind-monteringar skapade: {stubs}\nInaktuella nodkataloger har tagits bort: {stale_nodes}\nKomponenter installerade om: {components}\nVaraktighet: {duration}\n{warnings_block}", "label": "Värdåterställning slutförd" }, "system_problem": { diff --git a/AppImage/scripts/notification_templates.py b/AppImage/scripts/notification_templates.py index bf9073fc..b2751778 100644 --- a/AppImage/scripts/notification_templates.py +++ b/AppImage/scripts/notification_templates.py @@ -935,13 +935,13 @@ TEMPLATES = { 'default_enabled': True, }, 'oci_watchdog_failed': { - 'title': '{hostname}: {app_name} keeps stopping', + 'title': '{hostname}: {app_name} could not be kept running', 'body': ( - '{app_name} keeps stopping on its own and the watchdog no longer restarts it.\n' + 'The watchdog could not keep {app_name} running: it stopped again or did not start after several attempts, and it is left stopped.\n' 'Containers: {containers}\n' 'Start it by hand once the cause is solved; the watchdog then watches it again.' ), - 'label': 'OCI application keeps stopping', + 'label': 'OCI application left stopped by the watchdog', 'group': 'vm_ct', 'default_enabled': True, }, @@ -1378,13 +1378,12 @@ TEMPLATES = { 'title': '{hostname}: Host restore finished', 'body': ( 'Post-restore tasks completed in background.\n\n' - 'Guests applied: {guests}\n' - 'Bind-mount stubs: {stubs}\n' + 'Guest configurations copied: {guests}\n' + 'Bind-mount stub directories created: {stubs}\n' 'Stale node dirs removed: {stale_nodes}\n' 'Components reinstalled: {components}\n' 'Duration: {duration}\n' - '{warnings_block}\n' - 'The node is now fully ready to use.' + '{warnings_block}' ), 'label': 'Host restore completed', 'group': 'services', diff --git a/lang/es.json b/lang/es.json index cc1b4a18..37b1afff 100644 --- a/lang/es.json +++ b/lang/es.json @@ -101,6 +101,7 @@ "A relaxed AppArmor or seccomp profile is requested; this may be optional.": "Se solicita un perfil de AppArmor o seccomp relajado; puede ser opcional.", "A reproducible native startup is missing": "Falta una startup nativa reproducible", "A rootfs adaptation is stored in persistent storage": "Una adaptación de rootfs se almacena en almacenamiento persistente", + "A running container is restarted to apply the changes; a stopped one is left stopped. The data of the application, its database and the configuration of the other containers are not changed. A container volume removed here is deleted with the data in it.": "Un contenedor en marcha se reinicia para aplicar los cambios; uno parado se deja parado. Los datos de la aplicación, su base de datos y la configuración de los demás contenedores no cambian. Un volumen de contenedor eliminado aquí se borra con los datos que contiene.", "A scheduled run cannot answer:": "Una ejecución programada no puede responder:", "A self-hosted Bitwarden server": "Un servidor Bitwarden autoalojado", "A self-hosted, goal-free habit tracking tool.": "Una herramienta autoalojada de seguimiento de hábitos, sin objetivos.", @@ -4019,7 +4020,6 @@ "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 USB device was found on this host.": "No se ha encontrado ningún dispositivo USB en este host.", "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.", @@ -4164,6 +4164,7 @@ "No scheduled backup jobs found.": "No se encontraron trabajos de backup programados.", "No scripts found for:": "No se encontraron scripts para:", "No security relaxation is required for the reviewed profile.": "El perfil revisado no necesita ninguna relajación de seguridad.", + "No selectable USB device was found. Type the host node by hand.": "No se ha encontrado ningún dispositivo USB que se pueda elegir. Escribe a mano el nodo del host.", "No self-test history found for": "No se encontró ningún historial de autoevaluación para", "No self-test log available for": "No hay ningún log de autoprueba disponible para", "No server IP or hostname provided.": "No se proporcionó ninguna IP de servidor ni nombre de host.", @@ -4660,7 +4661,7 @@ "Privacy-first, self-hosted PDF toolkit": "Kit de herramientas PDF autoalojado que da prioridad a la privacidad", "Private installation record saved": "Registro de instalación privado guardado", "Private network assigned automatically:": "Red privada asignada automáticamente:", - "Private network kept, because other containers still use it:": "Red privada conservada, porque otros contenedores la siguen usando:", + "Private network kept, because other guests still use it:": "Red privada conservada, porque otras VM o CT la siguen usando:", "Private network of the application released:": "Red privada de la aplicación liberada:", "Private network of the application that is released:": "Red privada de la aplicación que se libera:", "Private network release attempted:": "Se ha intentado liberar la red privada:", @@ -6164,7 +6165,6 @@ "The container identity or configuration changed": "La identidad o configuración del contenedor cambió", "The container is currently stopped. Do you want to start it now to install the package?": "El contenedor se encuentra actualmente detenido. ¿Quieres iniciarlo ahora para instalar el paquete?", "The container is not modified because a host directory is not available:": "El contenedor no se modifica porque un directorio host no está disponible:", - "The container is restarted to apply the changes. Its own data, the database and the other containers of the application are not touched.": "El contenedor se reinicia para aplicar los cambios. Sus datos propios, la base de datos y los demás contenedores de la aplicación no se tocan.", "The container no longer exists:": "El contenedor ya no existe:", "The container of a member was replaced; the assembly is not resumed": "El contenedor de un miembro fue reemplazado; la asamblea no se reanuda", "The container runs on another node of the cluster; migrate it back to this node to remove it:": "El contenedor está en otro nodo del clúster; migra el contenedor de vuelta a este nodo para eliminarlo:", @@ -6304,7 +6304,7 @@ "The local envelope is dropped and future backups do not upload anything. Uploaded envelopes already on PBS stay intact and remain recoverable with their original passphrase.": "El sobre local se descarta y los backups futuros no suben nada. Los sobres ya subidos a PBS permanecen intactos y recuperables con su frase de contraseña original.", "The local storage does not accept snippets": "El almacenamiento local no admite snippets", "The long test runs directly on the disk hardware.": "La prueba larga se ejecuta directamente en el hardware del disco.", - "The machine learning container is rebuilt with the image of the new choice. Its model cache, the library and the database are kept. The whole application is stopped and updated, as in an update; if anything fails, the previous containers and the previous choice are restored.": "El contenedor Machine learning se reconstruye con la imagen de la nueva opción. Se conservan su caché de modelos, la biblioteca y la base de datos. Toda la aplicación se detiene y se actualiza, como en una actualización; si algo falla, se restauran los contenedores y la opción anteriores.", + "The machine learning container is rebuilt with the image of the new choice. Its model cache, the library and the database are kept. The whole application is stopped and updated, as in an update. If the operation fails after a container was changed, the previous containers and the previous choice are restored. An operation that stops halfway is recovered from the OCI management menu.": "El contenedor Machine learning se reconstruye con la imagen de la nueva opción. Se conservan su caché de modelos, la biblioteca y la base de datos. Toda la aplicación se detiene y se actualiza, como en una actualización. Si la operación falla después de modificar un contenedor, se restauran los contenedores y la opción anteriores. Una operación que se queda a medias se recupera desde el menú de gestión OCI.", "The main container of this application is not on this host. Restore it too.": "El contenedor principal de esta aplicación no está en este host. Restáuralo también.", "The main member must stop first and start last": "El miembro principal debe parar primero y comenzar el último", "The main member of the stack is missing": "Falta el miembro principal de la pila", @@ -6375,7 +6375,7 @@ "The private bridge does not have the expected address:": "El puente privado no tiene la dirección prevista:", "The private journal has an unsafe owner or permissions": "El diario privado tiene un propietario o permisos inseguros", "The private network allocator was not found": "El aleator de la red privada no fue encontrado", - "The private network is still used by another container and is kept:": "Otro contenedor sigue usando la red privada, así que se conserva:", + "The private network is still used by another guest and is kept:": "Otra VM u otro CT sigue usando la red privada, así que se conserva:", "The private network must be assigned automatically": "La red privada debe ser asignada automáticamente", "The private network of the application is already in use on this host:": "La red privada de la aplicación ya está en uso en este host:", "The privileged deployment does not include the required explicit consent": "El despliegue privilegiado no incluye el consentimiento explícito necesario", @@ -7482,7 +7482,7 @@ "go2rtc WebUI": "go2rtc WebUI", "google/gasket-driver cloned (fallback — will apply local patches).": "google/gasket-driver clonado (alternativa; se aplicarán parches locales).", "gpg not found; trying apt-key fallback": "gpg no encontrado; se prueba la alternativa apt-key", - "gzip could not be verified, leaving it unchanged": "No se ha podido verificar gzip; se deja sin cambios", + "gzip could not be verified; the change was not completed": "No se ha podido verificar gzip; el cambio no se ha completado", "gzip replaced with pigz wrapper successfully": "gzip reemplazado con el contenedor pigz correctamente", "has": "tiene", "has a different MAC than the backup — update any DHCP static reservation": "tiene una MAC diferente a la del backup: actualice cualquier reserva estática de DHCP", diff --git a/oci/remote/oci_remove.py b/oci/remote/oci_remove.py index c78dbb0e..2f76c838 100644 --- a/oci/remote/oci_remove.py +++ b/oci/remote/oci_remove.py @@ -338,7 +338,7 @@ def remove(root, vmid): msg_warn(f"{translate('Could not complete private network release:')} {bridge}") incomplete = True elif bridge: - msg_info(f"{translate('The private network is still used by another container and is kept:')} {bridge}") + msg_info(f"{translate('The private network is still used by another guest and is kept:')} {bridge}") if not remove_owned_host_firewall(primary): incomplete = True release_shared_host_files(hookscripts) diff --git a/oci/remote/oci_watchdog.py b/oci/remote/oci_watchdog.py index a896d833..cb14fdf9 100644 --- a/oci/remote/oci_watchdog.py +++ b/oci/remote/oci_watchdog.py @@ -199,7 +199,7 @@ def look(states, now): if started and notice_due(state, now): oci_operation_notice.notify('oci_watchdog_restarted', data) elif action == 'give-up': - print(f'CT {vmid} ({name}) keeps stopping; it is left stopped', flush=True) + print(f'CT {vmid} ({name}) could not be kept running; it is left stopped', flush=True) oci_operation_notice.notify('oci_watchdog_failed', data) diff --git a/oci/src/proxmenux_oci/extra_devices.py b/oci/src/proxmenux_oci/extra_devices.py index 8e3d4c75..e8689e7a 100644 --- a/oci/src/proxmenux_oci/extra_devices.py +++ b/oci/src/proxmenux_oci/extra_devices.py @@ -19,7 +19,7 @@ def choose_usb_device(ui, title, default=None, attached=()): for row in host.usb_devices() if row['path'] not in attached] manual_prompt = translate('Host USB node (e.g. /dev/ttyACM0 or /dev/bus/usb/003/004)') if not options: - ui.message(translate('No USB device was found on this host.')) + 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]) diff --git a/oci/src/proxmenux_oci/management.py b/oci/src/proxmenux_oci/management.py index f2b90e4d..fe2c94a4 100644 --- a/oci/src/proxmenux_oci/management.py +++ b/oci/src/proxmenux_oci/management.py @@ -472,7 +472,7 @@ def _removal_summary(project, vmid): if bridge and not oci_remove.bridge_in_use(bridge, set(members)): text += ['', f"{translate('Private network targeted for release if no other guest uses it:')} {bridge}"] elif bridge: - text += ['', f"{translate('Private network kept, because other containers still use it:')} {bridge}"] + text += ['', f"{translate('Private network kept, because other guests still use it:')} {bridge}"] if (primary.get('deployment') or {}).get('host_firewall'): text += ['', translate('A matching managed host firewall rule may also be removed.')] if kept: diff --git a/oci/src/proxmenux_oci/stack_recreation.py b/oci/src/proxmenux_oci/stack_recreation.py index 67d1299c..7eebc0a1 100644 --- a/oci/src/proxmenux_oci/stack_recreation.py +++ b/oci/src/proxmenux_oci/stack_recreation.py @@ -140,8 +140,10 @@ def summary(member, changes): lines += [f"- {path}" for path in changes['remove_mounts']] lines += [f"+ {device['host_path']}" for device in changes['add_devices']] lines += [f"- {path}" for path in changes['remove_devices']] - lines += ['', translate('The container is restarted to apply the changes. Its own data, the database ' - 'and the other containers of the application are not touched.')] + lines += ['', translate('A running container is restarted to apply the changes; a stopped one is left stopped. ' + 'The data of the application, its database and the configuration of the other ' + 'containers are not changed. A container volume removed here is deleted with the ' + 'data in it.')] return '\n'.join(lines) @@ -194,8 +196,9 @@ def change_recognition(project, ui, primary, member, run_lifecycle): text = (f"{translate('Recognition')}: {labels.get(current, current)} → {labels[selected]}\n\n" + translate('The machine learning container is rebuilt with the image of the new choice. Its model ' 'cache, the library and the database are kept. The whole application is stopped and ' - 'updated, as in an update; if anything fails, the previous containers and the previous ' - 'choice are restored.')) + 'updated, as in an update. If the operation fails after a container was changed, the ' + 'previous containers and the previous choice are restored. An operation that stops ' + 'halfway is recovered from the OCI management menu.')) if not ui.review(text, translate('Modify OCI stack'), question=translate('Change the recognition now?'), default=True): return False command = [sys.executable, str(project / 'remote/oci_immich_recognition.py'), str(primary['vmid']), diff --git a/oci/tests/test_stack_recreation.py b/oci/tests/test_stack_recreation.py index 2f5d5421..d00c0452 100644 --- a/oci/tests/test_stack_recreation.py +++ b/oci/tests/test_stack_recreation.py @@ -68,7 +68,7 @@ class StackRecreationTests(unittest.TestCase): {"host_path": "/dev/ttyACM0"}], "add_mounts": [ {"type": "host-bind", "container_path": "/scans", "source": "/mnt/scans"}]} text = stack_recreation.summary(MEMBER, changes) - for expected in ("CT 129", "+ /scans", "- /media-extra", "+ /dev/ttyACM0", "are not touched"): + for expected in ("CT 129", "+ /scans", "- /media-extra", "+ /dev/ttyACM0", "are not changed"): self.assertIn(expected, text) diff --git a/scripts/post_install/customizable_post_install.sh b/scripts/post_install/customizable_post_install.sh index 2d50b7d3..73671115 100644 --- a/scripts/post_install/customizable_post_install.sh +++ b/scripts/post_install/customizable_post_install.sh @@ -1707,12 +1707,12 @@ EOF src="$gz" gzip_is_binary "$src" || src=/bin/gzip.original if ! gzip_is_binary "$src"; then - ensure_gzip_binary "$gz" || { msg_error "$(translate "gzip could not be verified, leaving it unchanged")"; return 1; } + ensure_gzip_binary "$gz" || { msg_error "$(translate "gzip could not be verified; the change was not completed")"; return 1; } src="$gz" fi if ! pmx_write_file "$gz.distrib" < "$src" || ! chmod 755 "$gz.distrib" || ! gzip_is_binary "$gz.distrib"; then rm -f "$gz.distrib" - msg_error "$(translate "gzip could not be verified, leaving it unchanged")" + msg_error "$(translate "gzip could not be verified; the change was not completed")" return 1 fi pmx_remove_file /bin/gzip.original @@ -1729,10 +1729,10 @@ EOF # Never put the wrapper in place without a real gzip behind it. With the # diversion active, a reinstall writes straight into gzip.distrib. real=$(dpkg-divert --truename "$gz") - ensure_gzip_binary "$real" || { msg_error "$(translate "gzip could not be verified, leaving it unchanged")"; return 1; } + ensure_gzip_binary "$real" || { msg_error "$(translate "gzip could not be verified; the change was not completed")"; return 1; } if ! cmp -s "$gz" /bin/pigzwrapper && ! pmx_write_file "$gz" < /bin/pigzwrapper; then pmx_write_file "$gz" < "$real" - msg_error "$(translate "gzip could not be verified, leaving it unchanged")" + msg_error "$(translate "gzip could not be verified; the change was not completed")" return 1 fi msg_ok "$(translate "gzip replaced with pigz wrapper successfully")" diff --git a/scripts/post_install/uninstall-tools.sh b/scripts/post_install/uninstall-tools.sh index b8050e2f..10eeda09 100644 --- a/scripts/post_install/uninstall-tools.sh +++ b/scripts/post_install/uninstall-tools.sh @@ -1094,7 +1094,7 @@ uninstall_pigz() { # With the diversion still active, a reinstall writes into $real. if ! ensure_gzip_binary "$real" || ! pmx_write_file "$gz" < "$real"; then pmx_write_file "$gz" < /bin/pigzwrapper - msg_error "$(translate "gzip could not be verified, leaving it unchanged")" + msg_error "$(translate "gzip could not be verified; the change was not completed")" return 1 fi pmx_record_execution "Remove gzip diversion" "dpkg-divert --local --no-rename --remove $gz" @@ -1116,7 +1116,7 @@ uninstall_pigz() { rm -f "$real" done # pigz is still what gzip runs if anything above went wrong. - ensure_gzip_binary "$gz" || { msg_error "$(translate "gzip could not be verified, leaving it unchanged")"; return 1; } + ensure_gzip_binary "$gz" || { msg_error "$(translate "gzip could not be verified; the change was not completed")"; return 1; } pmx_remove_file /bin/pigzwrapper pmx_edit_file /etc/vzdump.conf 's/^pigz: 1/#pigz: 1/' 2>/dev/null || true pmx_record_execution "Purge pigz package" "apt-get purge -y pigz" diff --git a/web/messages/en/docs/oci-manager/lifecycle.json b/web/messages/en/docs/oci-manager/lifecycle.json index c328c0b9..8b9728e7 100644 --- a/web/messages/en/docs/oci-manager/lifecycle.json +++ b/web/messages/en/docs/oci-manager/lifecycle.json @@ -193,7 +193,7 @@ ], [ "It keeps stopping right after starting", - "An application that stops again within seconds of starting is tried five times, waiting 30 seconds, 1, 2 and 5 minutes between them. Then it is left stopped and a notification is sent" + "An application that stops again within seconds of starting, or that does not start, is tried five times, waiting 30 seconds, 1, 2 and 5 minutes between them. Then it is left stopped and a notification is sent" ] ] } diff --git a/web/messages/es/docs/oci-manager/lifecycle.json b/web/messages/es/docs/oci-manager/lifecycle.json index 57ddbf9e..3396ad48 100644 --- a/web/messages/es/docs/oci-manager/lifecycle.json +++ b/web/messages/es/docs/oci-manager/lifecycle.json @@ -193,7 +193,7 @@ ], [ "No deja de detenerse nada más arrancar", - "Una aplicación que vuelve a detenerse a los pocos segundos de arrancar se intenta cinco veces, con esperas de 30 segundos, 1, 2 y 5 minutos entre ellas. Después se deja parada y se envía una notificación" + "Una aplicación que vuelve a detenerse a los pocos segundos de arrancar, o que no arranca, se intenta cinco veces, con esperas de 30 segundos, 1, 2 y 5 minutos entre ellas. Después se deja parada y se envía una notificación" ] ] }