From de9da756e9b60d68c24e085b8a0bc6a389a6fe69 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Wed, 7 Jan 2026 19:27:13 -0500 Subject: [PATCH] Fix duplicate ticket creation for evolving SMART errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: When SMART errors evolved on the same drive, new tickets were created instead of updating the existing ticket. This happened because the hash was based on specific error values (e.g., "Reallocated_Sector_Ct: 8") instead of just the issue category. Root Cause: - Old hash included specific SMART attribute names and values - When errors changed (8 → 16 reallocated sectors, or new errors appeared), the hash changed, allowing duplicate tickets - Only matched "Warning" attributes, missing "Critical" and "Error X occurred" - Only matched /dev/sd[a-z], missing NVMe devices Solution: - Hash now based on: hostname + device + issue_category (e.g., "smart") - Does NOT include specific error values or attribute names - Supports both /dev/sdX and /dev/nvmeXnY devices - Detects issue categories: smart, storage, memory, cpu, network Result: ✅ Same drive, errors evolve → Same hash → Updates existing ticket ✅ Different device → Different hash → New ticket ✅ Drive replaced → Different device → New ticket ✅ NVMe devices now supported Example: Before: - "Warning Reallocated: 8" → hash abc123 - "Warning Reallocated: 16" → hash xyz789 (NEW TICKET - bad!) After: - "Warning Reallocated: 8" → hash abc123 - "Warning Reallocated: 16" → hash abc123 (SAME TICKET - good!) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- create_ticket_api.php | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/create_ticket_api.php b/create_ticket_api.php index 2e7bc87..c8c009b 100644 --- a/create_ticket_api.php +++ b/create_ticket_api.php @@ -84,22 +84,32 @@ $data = json_decode($rawInput, true); // Generate hash from stable components function generateTicketHash($data) { - // Extract device name if present (matches /dev/sdX pattern) - preg_match('/\/dev\/sd[a-z]/', $data['title'], $deviceMatches); + // Extract device name if present (matches /dev/sdX, /dev/nvmeXnY patterns) + preg_match('/\/dev\/(sd[a-z]|nvme\d+n\d+)/', $data['title'], $deviceMatches); $isDriveTicket = !empty($deviceMatches); - + // Extract hostname from title [hostname][tags]... preg_match('/\[([\w\d-]+)\]/', $data['title'], $hostMatches); $hostname = $hostMatches[1] ?? ''; - // Extract SMART attribute types without their values - preg_match_all('/Warning ([^:]+)/', $data['title'], $smartMatches); - $smartAttributes = $smartMatches[1] ?? []; + // Detect issue category (not specific attribute values) + $issueCategory = ''; + if (stripos($data['title'], 'SMART issues') !== false) { + $issueCategory = 'smart'; + } elseif (stripos($data['title'], 'LXC') !== false || stripos($data['title'], 'storage usage') !== false) { + $issueCategory = 'storage'; + } elseif (stripos($data['title'], 'memory') !== false) { + $issueCategory = 'memory'; + } elseif (stripos($data['title'], 'cpu') !== false) { + $issueCategory = 'cpu'; + } elseif (stripos($data['title'], 'network') !== false) { + $issueCategory = 'network'; + } // Build stable components with only static data $stableComponents = [ 'hostname' => $hostname, - 'smart_attributes' => $smartAttributes, + 'issue_category' => $issueCategory, // Generic category, not specific errors 'environment_tags' => array_filter( explode('][', $data['title']), fn($tag) => in_array($tag, ['production', 'development', 'staging', 'single-node']) @@ -112,9 +122,8 @@ function generateTicketHash($data) { } // Sort arrays for consistent hashing - sort($stableComponents['smart_attributes']); sort($stableComponents['environment_tags']); - + return hash('sha256', json_encode($stableComponents, JSON_UNESCAPED_SLASHES)); }