From 297c026c959dc4ee73e503ad7c324fdc7eb16cc9 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Mon, 28 Sep 2026 18:18:09 +0200 Subject: [PATCH] fix: Health Monitor storage and disk notices, SELinux-safe host backup, Frigate detector keys --- AppImage/components/health-status-modal.tsx | 2 + AppImage/messages/de/common.json | 1 + AppImage/messages/en/common.json | 1 + AppImage/messages/es/common.json | 5 +- AppImage/messages/fr/common.json | 1 + AppImage/messages/it/common.json | 1 + AppImage/messages/pt/common.json | 1 + AppImage/messages/sk/common.json | 1 + AppImage/messages/sv/common.json | 1 + AppImage/scripts/health_monitor.py | 18 ++++ AppImage/scripts/notification_events.py | 22 +++++ .../scripts/tests/test_benign_disk_notices.py | 94 +++++++++++++++++++ lang/de.json | 4 +- lang/es.json | 8 +- lang/fr.json | 4 +- lang/it.json | 4 +- lang/pt.json | 4 +- lang/sk.json | 4 +- lang/sv.json | 4 +- .../backup_restore/apply_pending_restore.sh | 6 +- scripts/backup_restore/backup_host.sh | 6 +- .../backup_restore/lib_host_backup_common.sh | 4 +- 22 files changed, 171 insertions(+), 25 deletions(-) create mode 100644 AppImage/scripts/tests/test_benign_disk_notices.py diff --git a/AppImage/components/health-status-modal.tsx b/AppImage/components/health-status-modal.tsx index cad28bb2..09a835ee 100644 --- a/AppImage/components/health-status-modal.tsx +++ b/AppImage/components/health-status-modal.tsx @@ -347,6 +347,8 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu if (match) return t("healthStatus.details.startupStorageUnavailable", { storage: match[1] }) match = value.match(/^\[Startup\] Storage '(.+)' has status: (.+)\. \(checking\.\.\.\)$/) if (match) return t("healthStatus.details.startupStorageStatus", { storage: match[1], status: match[2] }) + match = value.match(/^([\d.]+)% used$/) + if (match) return t("healthStatus.details.storageUsage", { percent: match[1] }) match = value.match(/^(.+) storage available$/) if (match) return t("healthStatus.details.storageAvailable", { type: match[1] }) match = value.match(/^(.+) mount reachable$/) diff --git a/AppImage/messages/de/common.json b/AppImage/messages/de/common.json index 85327d41..dd85a055 100644 --- a/AppImage/messages/de/common.json +++ b/AppImage/messages/de/common.json @@ -4699,6 +4699,7 @@ "pendingPackages": "{count} Pakete stehen aus", "updatedDaysAgo": "Zuletzt aktualisiert vor {count} Tagen", "storageAvailable": "{type} Speicher ist verfügbar", + "storageUsage": "{percent} % belegt", "mountReachable": "{type} Mount ist erreichbar", "rootfsUsed": "Rootfs-Nutzung: {percent} % ({size})", "runningCtsSafe": "{count} laufende CTs haben eine sichere Rootfs-Nutzung", diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json index 95a17cde..4e31d98b 100644 --- a/AppImage/messages/en/common.json +++ b/AppImage/messages/en/common.json @@ -4703,6 +4703,7 @@ "startupStorageUnavailable": "Checking Proxmox storage after startup: {storage} is not ready yet", "startupStorageStatus": "Checking Proxmox storage after startup: {storage} (current status: {status})", "storageAvailable": "{type} storage is available", + "storageUsage": "{percent}% used", "mountReachable": "{type} mount is reachable", "rootfsUsed": "rootfs usage: {percent}% ({size})", "runningCtsSafe": "{count} running CTs have safe rootfs usage", diff --git a/AppImage/messages/es/common.json b/AppImage/messages/es/common.json index 77327c43..ba4e759c 100644 --- a/AppImage/messages/es/common.json +++ b/AppImage/messages/es/common.json @@ -4698,8 +4698,9 @@ "uptimeDays": "Uptime: {count} días", "pendingPackages": "{count} paquetes pendientes", "updatedDaysAgo": "Última actualización hace {count} días", - "storageAvailable": "{type} almacenamiento está disponible", - "mountReachable": "Se puede acceder al soporte {type}", + "storageAvailable": "Almacenamiento {type} disponible", + "storageUsage": "{percent} % en uso", + "mountReachable": "Montaje {type} accesible", "rootfsUsed": "Uso de rootfs: {percent}% ({size})", "runningCtsSafe": "{count} los CT en ejecución tienen un uso seguro de rootfs", "pveStorageSafe": "{count} Los objetivos de almacenamiento en bloque PVE tienen un uso seguro", diff --git a/AppImage/messages/fr/common.json b/AppImage/messages/fr/common.json index 1331ee9c..646f0c81 100644 --- a/AppImage/messages/fr/common.json +++ b/AppImage/messages/fr/common.json @@ -4699,6 +4699,7 @@ "pendingPackages": "{count} colis en attente", "updatedDaysAgo": "Dernière mise à jour il y a {count} jours", "storageAvailable": "Le stockage {type} est disponible", + "storageUsage": "{percent} % utilisé", "mountReachable": "La monture {type} est accessible", "rootfsUsed": "Utilisation de rootfs : {percent}% ({size})", "runningCtsSafe": "{count} les CT en cours d'exécution ont une utilisation sécurisée de rootfs", diff --git a/AppImage/messages/it/common.json b/AppImage/messages/it/common.json index 7013a031..92fdb4f7 100644 --- a/AppImage/messages/it/common.json +++ b/AppImage/messages/it/common.json @@ -4699,6 +4699,7 @@ "pendingPackages": "{count} pacchetti in sospeso", "updatedDaysAgo": "Ultimo aggiornamento {count} giorni fa", "storageAvailable": "{type} spazio di archiviazione disponibile", + "storageUsage": "{percent}% utilizzato", "mountReachable": "La montatura {type} è raggiungibile", "rootfsUsed": "Utilizzo di rootfs: {percent}% ({size})", "runningCtsSafe": "I {count} CT in esecuzione hanno un utilizzo sicuro di rootfs", diff --git a/AppImage/messages/pt/common.json b/AppImage/messages/pt/common.json index 32c837bb..e3d7e0da 100644 --- a/AppImage/messages/pt/common.json +++ b/AppImage/messages/pt/common.json @@ -4699,6 +4699,7 @@ "pendingPackages": "{count} pacotes pendentes", "updatedDaysAgo": "Última atualização há {count} dias", "storageAvailable": "O armazenamento {type} está disponível", + "storageUsage": "{percent}% em uso", "mountReachable": "A montagem {type} está acessível", "rootfsUsed": "Uso de rootfs: {percent}% ({size})", "runningCtsSafe": "{count} CTs em execução têm uso seguro de rootfs", diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index 5ccfba52..daf44fe4 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -4703,6 +4703,7 @@ "startupStorageUnavailable": "Po štarte sa ešte overuje úložisko {storage}; zatiaľ nie je pripravené", "startupStorageStatus": "Po štarte sa ešte overuje úložisko {storage} (aktuálny stav: {status})", "storageAvailable": "Úložisko {type} je dostupné", + "storageUsage": "{percent} % využité", "mountReachable": "Pripojenie {type} je dostupné", "rootfsUsed": "Využitie rootfs: {percent} % ({size})", "runningCtsSafe": "Bežiace CT s bezpečným využitím rootfs: {count}", diff --git a/AppImage/messages/sv/common.json b/AppImage/messages/sv/common.json index 4fa1f9e1..f4a4fe27 100644 --- a/AppImage/messages/sv/common.json +++ b/AppImage/messages/sv/common.json @@ -4699,6 +4699,7 @@ "pendingPackages": "{count} paket väntar", "updatedDaysAgo": "Senast uppdaterad för {count} dagar sedan", "storageAvailable": "{type} lagringsutrymme är tillgängligt", + "storageUsage": "{percent} % använt", "mountReachable": "{type}-monteringen kan nåbar", "rootfsUsed": "rootfs-användning: {percent}% ({size})", "runningCtsSafe": "{count} som kör CT:er har säker rootfs-användning", diff --git a/AppImage/scripts/health_monitor.py b/AppImage/scripts/health_monitor.py index de1e85ee..4e2431db 100644 --- a/AppImage/scripts/health_monitor.py +++ b/AppImage/scripts/health_monitor.py @@ -199,6 +199,16 @@ def _is_disk_usb(disk_name: str) -> bool: """True when sysfs places the disk behind a USB bus.""" return resolver_is_usb_disk(disk_name) + +# smartd lines about its own configuration, not about a disk's health. +_SMARTD_CONFIGURATION_NOTICES = ( + 'ignoring -n directive', + 'not found in smartd database', + 'is in standby mode', + 'is in sleep mode', +) + + class HealthMonitor: """ Monitors system health across multiple components with minimal impact. @@ -5496,6 +5506,12 @@ class HealthMonitor: if not line.strip(): continue line_lower = line.lower() + # smartd reports its own configuration at the same priority as + # a failing disk: a USB bridge without ATA CHECK POWER STATUS + # makes it ignore -n, a disk it does not know is monitored with + # defaults. Those lines say nothing about the disk's health. + if 'smartd' in line_lower and any(text in line_lower for text in _SMARTD_CONFIGURATION_NOTICES): + continue # Extract disk name -- multiple patterns for different log formats: # /dev/sdh, /dev/nvme0n1 @@ -6609,6 +6625,7 @@ class HealthMonitor: name = st.get('name', 'unknown') label = f'{name} ({stype})' entry = { + 'detail': f'{pct:.1f}% used', 'usage_percent': round(pct, 1), 'storage_name': name, 'storage_type': stype, @@ -6732,6 +6749,7 @@ class HealthMonitor: except ValueError: continue entry = { + 'detail': f'{pct}% used', 'usage_percent': pct, 'pool_name': name, } diff --git a/AppImage/scripts/notification_events.py b/AppImage/scripts/notification_events.py index 6434a3d5..2ff24767 100644 --- a/AppImage/scripts/notification_events.py +++ b/AppImage/scripts/notification_events.py @@ -265,6 +265,22 @@ def capture_journal_context(keywords: list, lines: int = 30, return "" +# Errors fstrim reports for a filesystem whose device cannot discard. +_FSTRIM_UNSUPPORTED = ('remote i/o error', 'operation not supported', 'inappropriate ioctl for device') + + +def _fstrim_failed_only_unsupported() -> bool: + """True when every filesystem the last fstrim run failed on cannot discard.""" + try: + output = subprocess.run( + ['journalctl', '-u', 'fstrim.service', '--since', '-15min', '-o', 'cat', '--no-pager'], + capture_output=True, text=True, timeout=5).stdout + except (OSError, subprocess.SubprocessError): + return False + failures = [line.lower() for line in output.splitlines() if 'fitrim ioctl failed' in line.lower()] + return bool(failures) and all(any(text in line for text in _FSTRIM_UNSUPPORTED) for line in failures) + + # ─── smartd observation helper (shared by JournalWatcher & ProxmoxHookWatcher) ── # # Both watchers receive smartd messages — JournalWatcher via local journal, @@ -1316,6 +1332,12 @@ class JournalWatcher: for noise in _NOISE_PATTERNS: if re.search(noise, msg) or re.search(noise, unit): return + + # fstrim exits non-zero when a mounted disk does not support discard, + # as a USB bridge or a remote share does, every time the timer runs. + # That is what the disk can do, not a failure to act on. + if ('fstrim.service' in msg or unit == 'fstrim.service') and _fstrim_failed_only_unsupported(): + return service_patterns = [ r'Failed to start (.+)', diff --git a/AppImage/scripts/tests/test_benign_disk_notices.py b/AppImage/scripts/tests/test_benign_disk_notices.py new file mode 100644 index 00000000..e04afabc --- /dev/null +++ b/AppImage/scripts/tests/test_benign_disk_notices.py @@ -0,0 +1,94 @@ +import sys +from pathlib import Path +import subprocess +import unittest +from unittest.mock import MagicMock, patch + +SCRIPTS = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(SCRIPTS)) +import health_monitor +import notification_events + +NO_POWER_STATUS = ('Sep 27 09:08:50 proxmox smartd[893]: Device: /dev/sdc [SAT], ' + 'no ATA CHECK POWER STATUS support, ignoring -n Directive') +PENDING = ('Sep 27 09:08:50 proxmox smartd[893]: Device: /dev/sdc [SAT], ' + '8 Currently unreadable (pending) sectors') + + +class SmartdConfigurationNotices(unittest.TestCase): + def issues(self, *lines): + monitor = health_monitor.HealthMonitor.__new__(health_monitor.HealthMonitor) + monitor._get_journalctl_1hour_warnings = lambda: '\n'.join(lines) + monitor._get_disk_identity = lambda disk: None + with patch.object(health_monitor.os.path, 'exists', return_value=True): + return monitor._check_disk_health_from_events() + + def test_configuration_notice_is_not_a_disk_problem(self): + self.assertEqual(self.issues(NO_POWER_STATUS), {}) + + def test_a_real_warning_on_the_same_disk_is_still_reported(self): + issues = self.issues(NO_POWER_STATUS, PENDING) + self.assertEqual(list(issues), ['/dev/sdc']) + self.assertIn('pending', issues['/dev/sdc']['reason']) + self.assertNotIn('CHECK POWER STATUS', issues['/dev/sdc']['reason']) + + +class StorageUsageDetail(unittest.TestCase): + def test_a_full_pbs_storage_reads_its_usage(self): + # The storage row merges this entry over the availability one, so it + # must carry its own text or it reads "pbs storage available". + monitor = health_monitor.HealthMonitor.__new__(health_monitor.HealthMonitor) + monitor._read_capacity_thresholds = lambda section: (85, 95) + status = {'available': [{'name': 'PBS-Cloud', 'type': 'pbs', 'total': 1000, 'used': 924}]} + module = MagicMock() + module.proxmox_storage_monitor.get_storage_status.return_value = status + persistence = health_monitor.health_persistence + with patch.dict(sys.modules, {'proxmox_storage_monitor': module}), \ + patch.object(health_monitor, 'PROXMOX_STORAGE_AVAILABLE', True), \ + patch.object(persistence, 'is_error_acknowledged', return_value=False), \ + patch.object(persistence, 'record_error'), \ + patch.object(persistence, 'get_active_errors', return_value=[]): + result = monitor._check_pve_storage_capacity() + entry = result['checks']['PBS-Cloud (pbs)'] + self.assertEqual((entry['status'], entry['detail']), ('WARNING', '92.4% used')) + + +class FstrimUnsupportedDevices(unittest.TestCase): + def run_with(self, journal): + result = MagicMock(stdout=journal) + with patch.object(notification_events.subprocess, 'run', return_value=result): + return notification_events._fstrim_failed_only_unsupported() + + def test_devices_that_cannot_discard(self): + self.assertTrue(self.run_with( + 'fstrim: /mnt/nas1_con_backup: FITRIM ioctl failed: Remote I/O error\n' + 'fstrim: /mnt/usb2: FITRIM ioctl failed: Operation not supported\n' + '/: 20 GiB (21474836480 bytes) trimmed on /dev/mapper/pve-root\n')) + + def test_a_real_failure_is_still_reported(self): + self.assertFalse(self.run_with( + 'fstrim: /mnt/nas1_con_backup: FITRIM ioctl failed: Remote I/O error\n' + 'fstrim: /mnt/data: FITRIM ioctl failed: Input/output error\n')) + + def test_failure_without_fitrim_lines_or_journal_is_reported(self): + self.assertFalse(self.run_with('fstrim: cannot open /etc/fstab\n')) + with patch.object(notification_events.subprocess, 'run', side_effect=subprocess.TimeoutExpired('journalctl', 5)): + self.assertFalse(notification_events._fstrim_failed_only_unsupported()) + + def test_the_service_failure_is_not_emitted(self): + watcher = notification_events.JournalWatcher.__new__(notification_events.JournalWatcher) + watcher._emit = MagicMock() + with patch.object(notification_events, 'is_apt_active_on_host', return_value=False), \ + patch.object(notification_events, '_fstrim_failed_only_unsupported', return_value=True): + watcher._check_service_failure('fstrim.service: Main process exited, code=exited, status=32/n/a', + 'init.scope') + watcher._emit.assert_not_called() + with patch.object(notification_events, 'is_apt_active_on_host', return_value=False), \ + patch.object(notification_events, '_fstrim_failed_only_unsupported', return_value=False): + watcher._check_service_failure('fstrim.service: Main process exited, code=exited, status=32/n/a', + 'init.scope') + self.assertEqual(watcher._emit.call_args.args[0], 'service_fail') + + +if __name__ == '__main__': + unittest.main() diff --git a/lang/de.json b/lang/de.json index 21a77599..7dd7e47c 100644 --- a/lang/de.json +++ b/lang/de.json @@ -2347,8 +2347,8 @@ "French": "Französisch", "Freshrss is a free, self-hostable aggregator for rss feeds.": "Freshrss ist ein kostenloser, selbstgehosteter Aggregator für rss-Feeds.", "Frigate WebUI": "Frigate WebUI", - "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate verwendet Coral, sobald /config/config.yaml einen Detektor mit Typ: Edgetpu und Gerät: PCI definiert.", - "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate verwendet die Intel-NPU, sobald /config/config.yaml einen Detektor mit Typ: openvino und Gerät: NPU definiert.", + "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate verwendet Coral, sobald /config/config.yaml einen Detektor mit type: edgetpu und device: pci definiert.", + "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate verwendet die Intel-NPU, sobald /config/config.yaml einen Detektor mit type: openvino und device: NPU definiert.", "Full SMART Report": "Vollständiger SMART-Bericht", "Full SMART info and attributes": "Vollständige SMART-Informationen und -Attribute", "Full format — new GPT partition + filesystem": "Vollformat – neue GPT-Partition + Dateisystem", diff --git a/lang/es.json b/lang/es.json index ac9fc561..ebfa8303 100644 --- a/lang/es.json +++ b/lang/es.json @@ -2347,8 +2347,8 @@ "French": "Francés", "Freshrss is a free, self-hostable aggregator for rss feeds.": "Freshrss es un agregador libre y auto-hostable para las fuentes de rss.", "Frigate WebUI": "Frigate WebUI", - "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate usa Coral una vez que /config/config.yaml define un detector con tipo: edgetpu y dispositivo: pci.", - "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate usa la NPU Intel una vez que /config/config.yaml define un detector con tipo: openvino y dispositivo: NPU.", + "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate usa el Coral cuando /config/config.yaml define un detector con type: edgetpu y device: pci.", + "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate usa la NPU de Intel cuando /config/config.yaml define un detector con type: openvino y device: NPU.", "Full SMART Report": "Informe SMART completo", "Full SMART info and attributes": "Información y atributos SMART completos", "Full format — new GPT partition + filesystem": "Formato completo: nueva partición GPT + sistema de archivos", @@ -3947,7 +3947,7 @@ "No custom key (rely on default SSH config)": "Sin clave personalizada (confía en la configuración SSH predeterminada)", "No custom logos were found in /usr/local/share/fastfetch/logos/.\n\nPlease add a logo and try again.": "No se encontraron logotipos personalizados en /usr/local/share/fastfetch/logos/.\n\nAgregue un logotipo e inténtelo nuevamente.", "No desktop UI backup found, will reinstall packages": "No se encontró ninguna copia de seguridad de la interfaz de usuario del escritorio; se reinstalarán los paquetes", - "No detector device (CPU)": "Sin dispositivo detector (CPU)", + "No detector device (CPU)": "Sin dispositivo de detección (CPU)", "No directories found in": "No se encontraron directorios en", "No disk images found in /var/lib/vz/images/. Please add an image first.": "No se encontraron imágenes de disco en /var/lib/vz/images/. Primero agregue una imagen.", "No disk shares were selected from the guest listing.": "No se seleccionaron recursos compartidos de disco de la lista de invitados.", @@ -4159,7 +4159,7 @@ "One or more NVIDIA GPUs are currently configured for VM passthrough (vfio-pci):": "Actualmente, una o más GPU NVIDIA están configuradas para el paso a través de VM (vfio-pci):", "Online retro games emulator": "emulador de juegos retro en línea", "Only Intel/AMD DRM, Coral and USB nodes can be adopted automatically": "Solo se pueden incorporar automáticamente dispositivos DRM Intel/AMD, Coral y USB", - "Only Intel/AMD DRM, Coral, NPU and USB nodes can be adopted automatically": "Sólo los nodos Intel/AMD DRM, Coral, NPU y USB se pueden adoptar automáticamente", + "Only Intel/AMD DRM, Coral, NPU and USB nodes can be adopted automatically": "Solo los nodos DRM de Intel/AMD, Coral, NPU y USB se pueden adoptar automáticamente", "Only a Docker Swarm uses these settings, so they are not applied:": "Solo un Docker Swarm utiliza estos ajustes, por lo que no se aplican:", "Only an installed standalone OCI instance can adopt external changes": "Solo una instancia OCI independiente e instalada puede incorporar cambios externos", "Only backed-up Proxmox volumes can be adopted": "Solo se pueden incorporar volúmenes de Proxmox incluidos en las copias de seguridad", diff --git a/lang/fr.json b/lang/fr.json index ed2e8dfa..d7d07088 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -2347,8 +2347,8 @@ "French": "Français", "Freshrss is a free, self-hostable aggregator for rss feeds.": "Freshrss est un agrégateur libre et autonome pour les flux rss.", "Frigate WebUI": "Frigate WebUI", - "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate utilise le Coral une fois que /config/config.yaml définit un détecteur avec le type : edgetpu et le périphérique : pci.", - "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate utilise le NPU Intel une fois que /config/config.yaml définit un détecteur avec le type : openvino et le périphérique : NPU.", + "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate utilise le Coral une fois que /config/config.yaml définit un détecteur avec type: edgetpu et device: pci.", + "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate utilise le NPU Intel une fois que /config/config.yaml définit un détecteur avec type: openvino et device: NPU.", "Full SMART Report": "Rapport SMART complet", "Full SMART info and attributes": "Informations et attributs SMART complets", "Full format — new GPT partition + filesystem": "Format complet – nouvelle partition GPT + système de fichiers", diff --git a/lang/it.json b/lang/it.json index 3902c933..b8f5a6c4 100644 --- a/lang/it.json +++ b/lang/it.json @@ -2347,8 +2347,8 @@ "French": "francese", "Freshrss is a free, self-hostable aggregator for rss feeds.": "Freshrss è un aggregatore auto-hostable gratuito per feed rss.", "Frigate WebUI": "Frigate WebUI", - "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate utilizza Coral una volta che /config/config.yaml definisce un rilevatore con tipo: edgetpu e dispositivo: pci.", - "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate utilizza la NPU Intel una volta che /config/config.yaml definisce un rilevatore con tipo: openvino e dispositivo: NPU.", + "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate utilizza Coral una volta che /config/config.yaml definisce un rilevatore con type: edgetpu e device: pci.", + "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate utilizza la NPU Intel una volta che /config/config.yaml definisce un rilevatore con type: openvino e device: NPU.", "Full SMART Report": "Rapporto SMART completo", "Full SMART info and attributes": "Informazioni e attributi SMART completi", "Full format — new GPT partition + filesystem": "Formato completo: nuova partizione GPT + filesystem", diff --git a/lang/pt.json b/lang/pt.json index 77c1f3d2..9f73b77c 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -2347,8 +2347,8 @@ "French": "Francês", "Freshrss is a free, self-hostable aggregator for rss feeds.": "Freshrss é um agregador livre e auto-hostável para feeds rss.", "Frigate WebUI": "Frigate WebUI", - "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate usa o Coral uma vez que /config/config.yaml define um detector com tipo: edgetpu e dispositivo: pci.", - "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate usa o NPU Intel uma vez que /config/config.yaml define um detector com tipo: openvino e dispositivo: NPU.", + "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate usa o Coral uma vez que /config/config.yaml define um detector com type: edgetpu e device: pci.", + "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate usa o NPU Intel uma vez que /config/config.yaml define um detector com type: openvino e device: NPU.", "Full SMART Report": "Relatório SMART completo", "Full SMART info and attributes": "Informações e atributos SMART completos", "Full format — new GPT partition + filesystem": "Formato completo – nova partição GPT + sistema de arquivos", diff --git a/lang/sk.json b/lang/sk.json index 6f87c868..e6a3aeaf 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -2347,8 +2347,8 @@ "French": "Francúzština", "Freshrss is a free, self-hostable aggregator for rss feeds.": "Freshrss je voľný, self-hostinný aggregátor pre RSS kanály.", "Frigate WebUI": "Frigate WebUI", - "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate používa Coral raz /config/config.yaml definuje detektor s typom: edgetpu a zariadením: pci.", - "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate používa Intel NPU raz /config/config.yaml definuje detektor s typom: openvino a zariadením: NPU.", + "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate používa Coral, keď /config/config.yaml definuje detektor s type: edgetpu a device: pci.", + "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate používa Intel NPU, keď /config/config.yaml definuje detektor s type: openvino a device: NPU.", "Full SMART Report": "Úplná SMART správa", "Full SMART info and attributes": "Úplné SMART informácie a atribúty", "Full format — new GPT partition + filesystem": "Úplné formátovanie - nový GPT oddiel + súborový systém", diff --git a/lang/sv.json b/lang/sv.json index 1054fc36..aef17b98 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -2347,8 +2347,8 @@ "French": "franska", "Freshrss is a free, self-hostable aggregator for rss feeds.": "Freshrss är en fri, självvärd aggregator för rss feeds.", "Frigate WebUI": "Frigate WebUI", - "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate använder Coral när /config/config.yaml definierar en detektor med typ: edgetpu och enhet: pci.", - "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate använder Intel NPU en gång /config/config.yaml definierar en detektor med typ: openvino och enhet: NPU.", + "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate använder Coral när /config/config.yaml definierar en detektor med type: edgetpu och device: pci.", + "Frigate uses the Intel NPU once /config/config.yaml defines a detector with type: openvino and device: NPU.": "Frigate använder Intel NPU när /config/config.yaml definierar en detektor med type: openvino och device: NPU.", "Full SMART Report": "Fullständig SMART-rapport", "Full SMART info and attributes": "Fullständig SMART-information och attribut", "Full format — new GPT partition + filesystem": "Fullformat — ny GPT-partition + filsystem", diff --git a/scripts/backup_restore/apply_pending_restore.sh b/scripts/backup_restore/apply_pending_restore.sh index 2dbc0e88..add0bf92 100755 --- a/scripts/backup_restore/apply_pending_restore.sh +++ b/scripts/backup_restore/apply_pending_restore.sh @@ -177,7 +177,7 @@ while IFS= read -r rel; do if (( ${#state_excludes[@]} )); then # backup_root lives inside /var/lib/proxmenux/pre-restore. mkdir -p "$backup_root/$rel" >/dev/null 2>&1 || true - rsync -aAXH "${state_excludes[@]}" "$dst/" "$backup_root/$rel/" >/dev/null 2>&1 || true + rsync -aAXH --filter='-x security.selinux' "${state_excludes[@]}" "$dst/" "$backup_root/$rel/" >/dev/null 2>&1 || true else cp -a "$dst" "$backup_root/$rel" >/dev/null 2>&1 || true fi @@ -190,7 +190,7 @@ while IFS= read -r rel; do # copy of the excluded file isn't removed by rsync after being # skipped from the source side. if [[ ${#RSYNC_EXCLUDES[@]} -gt 0 ]]; then - if rsync -aAXH "${RSYNC_EXCLUDES[@]}" "${state_excludes[@]}" "$src/" "$dst/" >/dev/null 2>&1; then + if rsync -aAXH --filter='-x security.selinux' "${RSYNC_EXCLUDES[@]}" "${state_excludes[@]}" "$src/" "$dst/" >/dev/null 2>&1; then ((applied++)) [[ "$rel" == "var/lib/proxmenux/backup-jobs" || "$rel" == "var/lib/proxmenux/backup-jobs/"* ]] && jobs_restored=1 (( state_jobs )) && jobs_restored=1 @@ -198,7 +198,7 @@ while IFS= read -r rel; do ((failed++)) fi else - if rsync -aAXH --delete "${state_excludes[@]}" "$src/" "$dst/" >/dev/null 2>&1; then + if rsync -aAXH --filter='-x security.selinux' --delete "${state_excludes[@]}" "$src/" "$dst/" >/dev/null 2>&1; then ((applied++)) [[ "$rel" == "var/lib/proxmenux/backup-jobs" || "$rel" == "var/lib/proxmenux/backup-jobs/"* ]] && jobs_restored=1 (( state_jobs )) && jobs_restored=1 diff --git a/scripts/backup_restore/backup_host.sh b/scripts/backup_restore/backup_host.sh index 6794dc98..b9b4148f 100755 --- a/scripts/backup_restore/backup_host.sh +++ b/scripts/backup_restore/backup_host.sh @@ -1763,7 +1763,7 @@ _rs_apply() { if (( ${#state_excludes[@]} )); then # backup_root lives inside /var/lib/proxmenux/pre-restore. mkdir -p "$backup_root/$rel" - rsync -aAXH "${state_excludes[@]}" "$dst/" "$backup_root/$rel/" 2>/dev/null || true + rsync -aAXH --filter='-x security.selinux' "${state_excludes[@]}" "$dst/" "$backup_root/$rel/" 2>/dev/null || true else cp -a "$dst" "$backup_root/$rel" 2>/dev/null || true fi @@ -1797,7 +1797,7 @@ _rs_apply() { ) fi rsync_extra+=("${state_excludes[@]}") - if rsync -aAXH --delete "${rsync_extra[@]}" "$src/" "$dst/" 2>/dev/null; then + if rsync -aAXH --filter='-x security.selinux' --delete "${rsync_extra[@]}" "$src/" "$dst/" 2>/dev/null; then ((applied++)) [[ "$rel" == "var/lib/proxmenux/backup-jobs" || "$rel" == "var/lib/proxmenux/backup-jobs/"* ]] && jobs_restored=1 [[ "$rel" == "var/lib/proxmenux" && -d "$src/backup-jobs" ]] && jobs_restored=1 @@ -2337,7 +2337,7 @@ _rs_prepare_pending_restore() { mkdir -p "$(dirname "$dst")" || exit 1 if [[ -d "$src" ]]; then mkdir -p "$dst" || exit 1 - if ! rsync -aAXH --delete "$src/" "$dst/" 2>/dev/null; then + if ! rsync -aAXH --filter='-x security.selinux' --delete "$src/" "$dst/" 2>/dev/null; then msg_error "$(translate "Could not stage pending restore path:") $rel" exit 1 fi diff --git a/scripts/backup_restore/lib_host_backup_common.sh b/scripts/backup_restore/lib_host_backup_common.sh index a1573e33..80b8f7f3 100755 --- a/scripts/backup_restore/lib_host_backup_common.sh +++ b/scripts/backup_restore/lib_host_backup_common.sh @@ -680,8 +680,10 @@ hb_prepare_staging() { target="$staging_root/rootfs/$rel" if [[ -d "$p" ]]; then mkdir -p "$target" + # The SELinux label belongs to the host that wrote the file: pmxcfs + # has none, and rsync cannot remove the one /tmp gives the copy. local -a rsync_opts=( - -aAXH --numeric-ids + -aAXH --numeric-ids --filter='-x security.selinux' ) # /var/lib/pve-cluster: skip the raw config.db and its WAL/SHM