diff --git a/.last-branch b/.last-branch index 261aab8..7b2d1b6 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260209-05-responsive-navbar-fix +v20260209-06-synology-firmware-update-parser diff --git a/containers/backupchecks/src/backend/app/parsers/registry.py b/containers/backupchecks/src/backend/app/parsers/registry.py index b2fc100..1ea80b4 100644 --- a/containers/backupchecks/src/backend/app/parsers/registry.py +++ b/containers/backupchecks/src/backend/app/parsers/registry.py @@ -50,13 +50,13 @@ PARSER_DEFINITIONS = [ }, "description": "Parses NTFS Auditing file audit report mails (attachment-based HTML reports).", "example": { - "subject": "Bouter btr-dc001.bouter.nl file audits → 6 ↑ 12", - "from_address": "auditing@bouter.nl", + "subject": "SERVER-HOSTNAME file audits → 6 ↑ 12", + "from_address": "auditing@example.local", "body_snippet": "(empty body, HTML report in attachment)", "parsed_result": { "backup_software": "NTFS Auditing", "backup_type": "Audit", - "job_name": "btr-dc001.bouter.nl file audits", + "job_name": "SERVER-HOSTNAME file audits", "objects": [], }, }, @@ -73,16 +73,42 @@ PARSER_DEFINITIONS = [ }, "description": "Parses QNAP Notification Center firmware update notifications (informational; excluded from reporting and missing logic).", "example": { - "subject": "[Info][Firmware Update] Notification from your device: BETSIES-NAS01", + "subject": "[Info][Firmware Update] Notification from your device: NAS-HOSTNAME", "from_address": "notifications@customer.tld", - "body_snippet": "NAS Name: BETSIES-NAS01\n...\nMessage: ...", + "body_snippet": "NAS Name: NAS-HOSTNAME\n...\nMessage: ...", "parsed_result": { "backup_software": "QNAP", "backup_type": "Firmware Update", "job_name": "Firmware Update", "overall_status": "Warning", "objects": [ - {"name": "BETSIES-NAS01", "status": "Warning", "error_message": None} + {"name": "NAS-HOSTNAME", "status": "Warning", "error_message": None} + ], + }, + }, + }, + { + "name": "synology_dsm_update", + "backup_software": "Synology", + "backup_types": ["Updates"], + "order": 236, + "enabled": True, + "match": { + "subject_contains_any": ["DSM-update", "DSM update"], + "body_contains_any": ["automatische DSM-update", "automatic DSM update", "Automatic update of DSM"], + }, + "description": "Parses Synology DSM automatic update cancelled notifications (informational; excluded from reporting and missing logic).", + "example": { + "subject": "Synology NAS-HOSTNAME - Automatische DSM-update op NAS-HOSTNAME is geannuleerd door het systeem", + "from_address": "backup@example.local", + "body_snippet": "Het systeem heeft de automatische DSM-update op NAS-HOSTNAME geannuleerd...", + "parsed_result": { + "backup_software": "Synology", + "backup_type": "Updates", + "job_name": "Synology Automatic Update", + "overall_status": "Warning", + "objects": [ + {"name": "NAS-HOSTNAME", "status": "Warning"} ], }, }, @@ -383,16 +409,16 @@ PARSER_DEFINITIONS = [ }, "description": "Parses NAKIVO Backup & Replication reports for VMware backup jobs.", "example": { - "subject": '"exchange01.kuiperbv.nl" job: Successful', + "subject": '"VM-HOSTNAME" job: Successful', "from_address": "NAKIVO Backup & Replication ", "body_snippet": "Job Run Report... Backup job for VMware ... Successful", "parsed_result": { "backup_software": "NAKIVO", "backup_type": "Backup job for VMware", - "job_name": "exchange01.kuiperbv.nl", + "job_name": "VM-HOSTNAME", "objects": [ { - "name": "exchange01.kuiperbv.nl", + "name": "VM-HOSTNAME", "status": "Success", "error_message": "", } diff --git a/containers/backupchecks/src/backend/app/parsers/synology.py b/containers/backupchecks/src/backend/app/parsers/synology.py index 8968e26..3164633 100644 --- a/containers/backupchecks/src/backend/app/parsers/synology.py +++ b/containers/backupchecks/src/backend/app/parsers/synology.py @@ -18,10 +18,23 @@ DSM_UPDATE_CANCELLED_PATTERNS = [ "Automatische update van DSM is geannuleerd", "Automatic DSM update was cancelled", "Automatic update of DSM was cancelled", + "Automatische DSM-update", + "DSM-update op", + "Packages on", + "out-of-date", + "Package Center", + "new DSM update", + "Auto Update has detected", + "new version of DSM", + "Update & Restore", + "belangrijke DSM-update", + "kritieke oplossingen", + "wordt automatisch geïnstalleerd", + "is beschikbaar op", ] _DSM_UPDATE_CANCELLED_HOST_RE = re.compile( - r"\b(?:geannuleerd\s+op|cancelled\s+on)\s+(?P[A-Za-z0-9._-]+)\b", + r"\b(?:geannuleerd\s+op|cancelled\s+on|DSM-update\s+op|DSM\s+update\s+on|Packages\s+on|running\s+on|detected\s+on)\s+(?P[A-Za-z0-9._-]+)\b", re.I, ) @@ -176,12 +189,14 @@ _ABB_SUBJECT_RE = re.compile(r"\bactive\s+backup\s+for\s+business\b", re.I) # Examples (NL): # "De back-uptaak vSphere-Task-1 op KANTOOR-NEW is voltooid." # "Virtuele machine back-uptaak vSphere-Task-1 op KANTOOR-NEW is gedeeltelijk voltooid." +# "back-uptaak vSphere-Task-1 op KANTOOR-NEW is genegeerd" # Examples (EN): # "The backup task vSphere-Task-1 on KANTOOR-NEW has completed." # "Virtual machine backup task vSphere-Task-1 on KANTOOR-NEW partially completed." +# "backup task vSphere-Task-1 on KANTOOR-NEW was skipped" _ABB_COMPLETED_RE = re.compile( - r"\b(?:virtuele\s+machine\s+)?(?:de\s+)?back-?up\s*taak\s+(?P.+?)\s+op\s+(?P[A-Za-z0-9._-]+)\s+is\s+(?Pvoltooid|gedeeltelijk\s+voltooid)\b" - r"|\b(?:virtual\s+machine\s+)?(?:the\s+)?back-?up\s+task\s+(?P.+?)\s+on\s+(?P[A-Za-z0-9._-]+)\s+(?:is\s+)?(?Pcompleted|finished|has\s+completed|partially\s+completed)\b", + r"\b(?:virtuele\s+machine\s+)?(?:de\s+)?back-?up\s*(?:taak|job)\s+(?:van\s+deze\s+taak\s+)?(?P.+?)\s+op\s+(?P[A-Za-z0-9._-]+)\s+is\s+(?Pvoltooid|gedeeltelijk\s+voltooid|genegeerd)\b" + r"|\b(?:virtual\s+machine\s+)?(?:the\s+)?back-?up\s+(?:task|job)\s+(?P.+?)\s+on\s+(?P[A-Za-z0-9._-]+)\s+(?:is\s+|was\s+)?(?Pcompleted|finished|has\s+completed|partially\s+completed|skipped|ignored)\b", re.I, ) @@ -233,6 +248,11 @@ def _parse_active_backup_for_business(subject: str, text: str) -> Tuple[bool, Di overall_status = "Warning" overall_message = "Partially completed" + # "genegeerd" / "skipped" / "ignored" should be treated as Warning + if "genegeerd" in status_raw or "skipped" in status_raw or "ignored" in status_raw: + overall_status = "Warning" + overall_message = "Skipped" + # Explicit failure wording overrides everything if _ABB_FAILED_RE.search(haystack): overall_status = "Error" diff --git a/docs/changelog-claude.md b/docs/changelog-claude.md index 2bc0cab..f5a116d 100644 --- a/docs/changelog-claude.md +++ b/docs/changelog-claude.md @@ -8,9 +8,13 @@ This file documents all changes made to this project via Claude Code. - Added "Cleanup orphaned jobs" maintenance option in Settings → Maintenance to delete jobs without valid customer links and their associated emails/runs permanently from database (useful when customers are removed) - Added "Preview orphaned jobs" button to show detailed list of jobs to be deleted with run/email counts before confirming deletion (verification step for safety) - Added "Generate test emails" feature in Settings → Maintenance with three separate buttons to create fixed test email sets (success/warning/error) in inbox for testing parsers and maintenance operations (each set contains exactly 3 Veeam Backup Job emails with the same job name "Test-Backup-Job" and different dates/objects/statuses for reproducible testing and proper status flow testing) +- Added parser registry entry for Synology DSM automatic update cancelled notifications (backup software: Synology, backup type: Updates, informational only, no schedule learning) +- Extended Synology DSM update parser with additional detection patterns ("Automatische DSM-update", "DSM-update op", "Packages on", "out-of-date", "Package Center", "new DSM update", "Auto Update has detected", "Update & Restore", "belangrijke DSM-update", "kritieke oplossingen", "wordt automatisch geïnstalleerd", "is beschikbaar op") and hostname extraction regex to recognize DSM update cancelled, out-of-date packages, new update available, and automatic installation announcements under same Updates job type while maintaining backward compatibility with existing patterns +- Extended Synology Active Backup for Business parser to recognize skipped/ignored backup tasks ("genegeerd", "skipped", "ignored") as Warning status when backup was skipped due to previous backup still running ### Changed - Removed customer name from Autotask ticket title to keep titles concise (format changed from "[Backupchecks] Customer - Job Name - Status" to "[Backupchecks] Job Name - Status") +- Replaced real customer names in parser registry examples with generic placeholders (NTFS Auditing, QNAP Firmware Update, NAKIVO) to prevent customer information in codebase ### Fixed - Fixed Autotask ticket description being set to NULL when resolving tickets via `update_ticket_resolution_safe` by adding "description" to the optional_fields list, ensuring the original description is preserved during PUT operations