fix: Health Monitor storage and disk notices, SELinux-safe host backup, Frigate detector keys

This commit is contained in:
MacRimi
2026-09-28 18:18:09 +02:00
parent 33245a423a
commit 297c026c95
22 changed files with 171 additions and 25 deletions
@@ -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$/)
+1
View File
@@ -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",
+1
View File
@@ -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",
+3 -2
View File
@@ -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",
+1
View File
@@ -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",
+1
View File
@@ -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",
+1
View File
@@ -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",
+1
View File
@@ -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}",
+1
View File
@@ -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",
+18
View File
@@ -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,
}
+22
View File
@@ -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 (.+)',
@@ -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()
+2 -2
View File
@@ -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",
+4 -4
View File
@@ -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",
+2 -2
View File
@@ -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",
+2 -2
View File
@@ -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",
+2 -2
View File
@@ -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",
+2 -2
View File
@@ -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",
+2 -2
View File
@@ -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",
@@ -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
+3 -3
View File
@@ -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
@@ -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