mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-29 10:06:41 +00:00
fix: Health Monitor storage and disk notices, SELinux-safe host backup, Frigate detector keys
This commit is contained in:
@@ -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$/)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user