5 Commits

Author SHA1 Message Date
jared 6b2d8e4d03 Fix remaining spam issues and phpcs merge conflict marker
Lint / PHP (phpcs PSR-12) (push) Successful in 31s
Lint / JS (eslint) (push) Successful in 13s
Security / PHP Security (semgrep) (push) Failing after 1m41s
Lint / Deploy (push) Successful in 4s
Lint / Notify on failure (push) Has been skipped
Spam fixes:
- Add ZFS pool category to hash with subtypes (pool_state, pool_usage,
  pool_errors) so DEGRADED and usage-high on same pool get separate tickets
- Strip volatile percentages from LXC/ZFS usage titles ("usage high: 80.1%"
  → "usage high") and OSD counts from BlueStore slow-ops titles
  ("2 OSD(s) experiencing" → "OSD(s) experiencing") in hwmonDaemon.py

phpcs fix:
- Remove leftover merge conflict marker (<<<<<<< HEAD / >>>>>>>)
  in create_ticket_api.php which caused phpcs to fail on bitshift
  operator spacing

DB cleanup:
- Deleted 107 spam comments and 107 audit entries from tickets
  357934698 (ZFS pool), 673679581 (BlueStore), 925498317 (LXC storage)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:28:59 -04:00
jared 7fb60a365e Suppress title-only update comments to stop hourly comment spam
Lint / PHP (phpcs PSR-12) (push) Failing after 45s
Lint / JS (eslint) (push) Successful in 19s
Security / PHP Security (semgrep) (push) Failing after 1m37s
Lint / Deploy (push) Has been skipped
Lint / Notify on failure (push) Successful in 2s
Comments on worsening condition now only fire on priority escalation.
Title and description updates are silent — title changes (e.g. rising
Power_On_Hours counters) were generating a comment on every hourly run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:16:41 -04:00
jared fb3b607bd1 Resolve merge conflict in create_ticket_api.php OSD regex
Lint / PHP (phpcs PSR-12) (push) Failing after 33s
Lint / JS (eslint) (push) Successful in 15s
Security / PHP Security (semgrep) (push) Failing after 2m20s
Lint / Deploy (push) Has been skipped
Lint / Notify on failure (push) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:10:14 -04:00
jared dad7c24bff Fix hwmonDaemon hash collisions and automated comment formatting
- source_type (auto vs manual) added to dedup hash so automated
  tickets never collide with manually created ones
- OSD-specific subtype (osd_down_N) so each OSD gets its own ticket
- Description refreshed on every automated update (current sensor data)
- Comments on worsening condition only fire on meaningful changes
- ASCII art descriptions wrapped in fenced code blocks in comments
- Reopen comment also uses fenced code block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:09:12 -04:00
jared 9e9d8a33e3 Fix hwmonDaemon hash collisions and automated description rendering
Lint / PHP (phpcs PSR-12) (push) Successful in 40s
Lint / JS (eslint) (push) Successful in 13s
Security / PHP Security (semgrep) (push) Failing after 1m59s
Lint / Deploy (push) Successful in 5s
Lint / Notify on failure (push) Has been skipped
- Include source_type (auto vs manual) in dedup hash so automated
  tickets never collide with manually created ones. This was causing
  hwmonDaemon to hijack manual task tickets that shared the same
  cluster/category/environment tags.

- Include specific OSD ID in hash subtype (osd_down_N) so each OSD
  failure gets its own ticket instead of all colliding to osd_down.

- Wrap hwmonDaemon report descriptions in fenced code blocks in
  comments so ASCII art box-drawing renders correctly instead of
  collapsing into a paragraph blob.

- Refresh ticket description on every automated update so the ticket
  body shows current sensor data, not stale values from first report.

- Only post a worsening-condition comment when title or priority
  actually changed (not just a description refresh).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:06:13 -04:00
+47 -20
View File
@@ -129,6 +129,21 @@ function generateTicketHash($data)
if (stripos($title, 'SMART issues') !== false) { if (stripos($title, 'SMART issues') !== false) {
$issueCategory = 'smart'; $issueCategory = 'smart';
} elseif (stripos($title, 'ZFS pool') !== false) {
$issueCategory = 'zfs';
// Extract pool name so each pool gets its own ticket
if (preg_match("/ZFS pool '([^']+)'/i", $title, $poolMatch)) {
$poolName = strtolower(preg_replace('/[^a-z0-9_]/i', '_', $poolMatch[1]));
if (stripos($title, 'state:') !== false || preg_match('/DEGRADED|FAULTED|UNAVAIL|OFFLINE/i', $title)) {
$issueSubtype = 'pool_state_' . $poolName;
} elseif (stripos($title, 'usage') !== false) {
$issueSubtype = 'pool_usage_' . $poolName;
} elseif (stripos($title, 'errors') !== false) {
$issueSubtype = 'pool_errors_' . $poolName;
} else {
$issueSubtype = 'pool_' . $poolName;
}
}
} elseif (stripos($title, 'LXC') !== false || stripos($title, 'storage usage') !== false) { } elseif (stripos($title, 'LXC') !== false || stripos($title, 'storage usage') !== false) {
$issueCategory = 'storage'; $issueCategory = 'storage';
// Include the LXC container ID so each container gets its own ticket // Include the LXC container ID so each container gets its own ticket
@@ -158,15 +173,24 @@ function generateTicketHash($data)
$issueSubtype = 'clock_skew'; $issueSubtype = 'clock_skew';
} elseif (stripos($title, 'cluster usage') !== false) { } elseif (stripos($title, 'cluster usage') !== false) {
$issueSubtype = 'usage'; $issueSubtype = 'usage';
} elseif (stripos($title, 'OSD down') !== false) { } elseif (stripos($title, 'OSD down') !== false || preg_match('/osd\.\d+\s+is\s+DOWN/i', $title)) {
$issueSubtype = 'osd_down'; // Include the specific OSD ID so each individual OSD gets its own ticket
if (preg_match('/osd\.(\d+)/i', $title, $osdMatch)) {
$issueSubtype = 'osd_down_' . $osdMatch[1];
} else {
$issueSubtype = 'osd_down';
}
} elseif (stripos($title, 'HEALTH_ERR') !== false) { } elseif (stripos($title, 'HEALTH_ERR') !== false) {
$issueSubtype = 'health_err'; $issueSubtype = 'health_err';
} }
} }
// Include source type so automated tickets never collide with manual ones
$sourceType = stripos($title, '[auto]') !== false ? 'auto' : 'manual';
// Build stable components // Build stable components
$stableComponents = [ $stableComponents = [
'source_type' => $sourceType,
'issue_category' => $issueCategory, 'issue_category' => $issueCategory,
'issue_subtype' => $issueSubtype, 'issue_subtype' => $issueSubtype,
'environment_tags' => array_values(array_filter( 'environment_tags' => array_values(array_filter(
@@ -218,8 +242,8 @@ if ($existing) {
$newPriority = (int)$priority; $newPriority = (int)$priority;
if ($existingStatus !== 'Closed') { if ($existingStatus !== 'Closed') {
// Ticket is still active — update title and escalate priority if the new // Ticket is still active — update title, escalate priority, and refresh
// report is more severe (lower number = higher severity). // description with latest sensor data.
$changes = []; $changes = [];
$updateSql = "UPDATE tickets SET updated_at = NOW(), updated_by = ?"; $updateSql = "UPDATE tickets SET updated_at = NOW(), updated_by = ?";
$bindTypes = "i"; $bindTypes = "i";
@@ -239,6 +263,14 @@ if ($existing) {
$changes['priority'] = ['from' => $existingPriority, 'to' => $newPriority]; $changes['priority'] = ['from' => $existingPriority, 'to' => $newPriority];
} }
// Always refresh the description so the ticket body shows current sensor data
if (!empty($description)) {
$updateSql .= ", description = ?";
$bindTypes .= "s";
$bindVals[] = $description;
$changes['description_refreshed'] = true;
}
if (!empty($changes)) { if (!empty($changes)) {
$updateSql .= " WHERE ticket_id = ?"; $updateSql .= " WHERE ticket_id = ?";
$bindTypes .= "s"; $bindTypes .= "s";
@@ -249,25 +281,20 @@ if ($existing) {
$updStmt->execute(); $updStmt->execute();
$updStmt->close(); $updStmt->close();
// Comment summarising what changed // Only post a comment on priority escalation — title and description updates
$changeLines = []; // are silent (title changes like rising counters would spam a comment every run)
if (isset($changes['title'])) {
$changeLines[] = "- **Title updated** to reflect current issue";
}
if (isset($changes['priority'])) { if (isset($changes['priority'])) {
$changeLines[] = "- **Priority escalated** from P{$changes['priority']['from']} to P{$changes['priority']['to']}"; $commentText = "**hwmonDaemon escalated this ticket from P{$changes['priority']['from']} to P{$changes['priority']['to']}.**\n\n```\n" . $description . "\n```";
$commentStmt = $conn->prepare(
"INSERT INTO ticket_comments (ticket_id, user_id, user_name, comment_text, markdown_enabled) VALUES (?, ?, 'hwmonDaemon', ?, 1)"
);
$commentStmt->bind_param("sis", $existingId, $userId, $commentText);
$commentStmt->execute();
$commentStmt->close();
} }
$commentText = "**hwmonDaemon reported a worsened condition — ticket updated automatically.**\n\n" .
implode("\n", $changeLines) . "\n\nLatest report:\n\n" . $description;
$commentStmt = $conn->prepare(
"INSERT INTO ticket_comments (ticket_id, user_id, user_name, comment_text, markdown_enabled) VALUES (?, ?, 'hwmonDaemon', ?, 1)"
);
$commentStmt->bind_param("sis", $existingId, $userId, $commentText);
$commentStmt->execute();
$commentStmt->close();
$auditLog->log($userId, 'update', 'ticket', $existingId, array_merge( $auditLog->log($userId, 'update', 'ticket', $existingId, array_merge(
$changes, array_diff_key($changes, ['description_refreshed' => true]),
['reason' => 'auto-updated by hwmonDaemon (condition worsened)'] ['reason' => 'auto-updated by hwmonDaemon (condition worsened)']
)); ));
@@ -305,7 +332,7 @@ if ($existing) {
$reopenStmt->close(); $reopenStmt->close();
$commentText = "**Issue recurred — ticket reopened automatically.**\n\n" . $commentText = "**Issue recurred — ticket reopened automatically.**\n\n" .
"New report received from hwmonDaemon:\n\n" . $description; "New report received from hwmonDaemon:\n\n```\n" . $description . "\n```";
$commentStmt = $conn->prepare( $commentStmt = $conn->prepare(
"INSERT INTO ticket_comments (ticket_id, user_id, user_name, comment_text, markdown_enabled) VALUES (?, ?, 'hwmonDaemon', ?, 1)" "INSERT INTO ticket_comments (ticket_id, user_id, user_name, comment_text, markdown_enabled) VALUES (?, ?, 'hwmonDaemon', ?, 1)"
); );