fix: resolve PBS alert toggle and offline alert issues (addresses #426)

- Fixed PBS alert toggle not responding in thresholds settings
- PBS servers now use connectivity toggle like nodes instead of disabled toggle
- Added support for disableConnectivity flag on PBS instances in backend
- Fixed PBS ID format mismatch between frontend and backend
- PBS offline alerts now properly respect the disableConnectivity setting
- Prevents spam alerts by checking disableConnectivity flag for PBS offline alerts
This commit is contained in:
Pulse Monitor
2025-09-07 07:13:56 +00:00
parent 5886f9731e
commit 749a52dd5a
4 changed files with 51 additions and 30 deletions
@@ -250,16 +250,17 @@ export function ResourceTable(props: ResourceTableProps) {
{resource.disabled ? 'Disabled' : 'Enabled'}
</button>
</Show>
<Show when={resource.type === 'pbs' && props.onToggleDisabled}>
<Show when={resource.type === 'pbs' && props.onToggleNodeConnectivity}>
<button type="button"
onClick={() => props.onToggleDisabled?.(resource.id)}
onClick={() => props.onToggleNodeConnectivity?.(resource.id)}
class={`px-2 py-0.5 text-xs font-medium rounded transition-colors ${
resource.disabled
resource.disableConnectivity
? 'bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300 hover:bg-red-200 dark:hover:bg-red-800/50'
: 'bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300 hover:bg-green-200 dark:hover:bg-green-800/50'
}`}
title="Toggle connectivity alerts for this PBS server"
>
{resource.disabled ? 'Disabled' : 'Enabled'}
{resource.disableConnectivity ? 'No Offline' : 'Alert Offline'}
</button>
</Show>
</td>
@@ -471,16 +472,17 @@ export function ResourceTable(props: ResourceTableProps) {
{resource.disabled ? 'Disabled' : 'Enabled'}
</button>
</Show>
<Show when={resource.type === 'pbs' && props.onToggleDisabled}>
<Show when={resource.type === 'pbs' && props.onToggleNodeConnectivity}>
<button type="button"
onClick={() => props.onToggleDisabled?.(resource.id)}
onClick={() => props.onToggleNodeConnectivity?.(resource.id)}
class={`px-2 py-0.5 text-xs font-medium rounded transition-colors ${
resource.disabled
resource.disableConnectivity
? 'bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300 hover:bg-red-200 dark:hover:bg-red-800/50'
: 'bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300 hover:bg-green-200 dark:hover:bg-green-800/50'
}`}
title="Toggle connectivity alerts for this PBS server"
>
{resource.disabled ? 'Disabled' : 'Enabled'}
{resource.disableConnectivity ? 'No Offline' : 'Alert Offline'}
</button>
</Show>
</td>
@@ -238,7 +238,8 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
const pbsInstances = props.pbsInstances || [];
const pbsServers = pbsInstances.filter((pbs) => (pbs.cpu || 0) > 0 || (pbs.memory?.usage || 0) > 0).map((pbs) => {
const pbsId = `pbs-${pbs.id}`;
// PBS IDs already have "pbs-" prefix from backend, don't double it
const pbsId = pbs.id;
const override = overridesMap.get(pbsId);
// Check if any threshold values actually differ from defaults
@@ -264,6 +265,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
uptime: pbs.uptime,
hasOverride: hasCustomThresholds || false,
disabled: false,
disableConnectivity: override?.disableConnectivity || false,
thresholds: override?.thresholds || {},
defaults: {
cpu: props.nodeDefaults.cpu,
@@ -502,15 +504,18 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
};
const toggleNodeConnectivity = (nodeId: string, forceState?: boolean) => {
const node = nodesWithOverrides().find(r => r.id === nodeId);
if (!node || node.type !== 'node') return;
const toggleNodeConnectivity = (resourceId: string, forceState?: boolean) => {
// Find the resource - could be a node or PBS server
const nodes = nodesWithOverrides();
const pbsServers = pbsServersWithOverrides();
const resource = [...nodes, ...pbsServers].find(r => r.id === resourceId);
if (!resource || (resource.type !== 'node' && resource.type !== 'pbs')) return;
// Get existing override if it exists
const existingOverride = props.overrides().find(o => o.id === nodeId);
const existingOverride = props.overrides().find(o => o.id === resourceId);
// Determine the current state - use the node's computed state, not just the override
const currentDisableConnectivity = node.disableConnectivity;
// Determine the current state - use the resource's computed state, not just the override
const currentDisableConnectivity = resource.disableConnectivity;
const newDisableConnectivity = forceState !== undefined ? forceState : !currentDisableConnectivity;
// Clean the thresholds to exclude any unwanted fields
@@ -521,25 +526,25 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
// If enabling connectivity alerts (disableConnectivity = false) and no custom thresholds exist, remove the override entirely
if (!newDisableConnectivity && Object.keys(cleanThresholds).length === 0) {
// Remove the override completely
props.setOverrides(props.overrides().filter(o => o.id !== nodeId));
props.setOverrides(props.overrides().filter(o => o.id !== resourceId));
// Remove from raw config
const newRawConfig = { ...props.rawOverridesConfig() };
delete newRawConfig[nodeId];
delete newRawConfig[resourceId];
props.setRawOverridesConfig(newRawConfig);
} else {
// Update or create the override
const override: Override = {
id: nodeId,
name: node.name,
type: node.type,
resourceType: node.resourceType,
id: resourceId,
name: resource.name,
type: resource.type as 'node' | 'guest' | 'storage',
resourceType: resource.resourceType,
disableConnectivity: newDisableConnectivity,
thresholds: cleanThresholds
};
// Update overrides list
const existingIndex = props.overrides().findIndex(o => o.id === nodeId);
const existingIndex = props.overrides().findIndex(o => o.id === resourceId);
if (existingIndex >= 0) {
const newOverrides = [...props.overrides()];
newOverrides[existingIndex] = override;
@@ -566,7 +571,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
hysteresisThresholds.disableConnectivity = true;
}
newRawConfig[nodeId] = hysteresisThresholds;
newRawConfig[resourceId] = hysteresisThresholds;
props.setRawOverridesConfig(newRawConfig);
}
@@ -915,7 +920,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
onSaveEdit={saveEdit}
onCancelEdit={cancelEdit}
onRemoveOverride={removeOverride}
onToggleDisabled={toggleDisabled}
onToggleNodeConnectivity={toggleNodeConnectivity}
editingId={editingId}
editingThresholds={editingThresholds}
setEditingThresholds={setEditingThresholds}
+4 -4
View File
@@ -1408,14 +1408,14 @@ func (m *Manager) checkPBSOffline(pbs models.PBSInstance) {
m.mu.Lock()
defer m.mu.Unlock()
// Check if PBS offline alerts are disabled
if override, exists := m.config.Overrides[pbs.ID]; exists && override.Disabled {
// PBS alerts are disabled, clear any existing alert and return
// Check if PBS offline alerts are disabled via disableConnectivity flag
if override, exists := m.config.Overrides[pbs.ID]; exists && (override.Disabled || override.DisableConnectivity) {
// PBS connectivity alerts are disabled, clear any existing alert and return
if _, alertExists := m.activeAlerts[alertID]; alertExists {
delete(m.activeAlerts, alertID)
log.Debug().
Str("pbs", pbs.Name).
Msg("PBS offline alert cleared (alerts disabled)")
Msg("PBS offline alert cleared (connectivity alerts disabled)")
}
return
}
+16 -2
View File
@@ -40,8 +40,11 @@ pkill -f vite 2>/dev/null
pkill -f "npm run dev" 2>/dev/null
pkill -f "npm exec" 2>/dev/null
# Kill Pulse binary (exact match only)
# Kill Pulse binary - first try gracefully, then force
pkill -x "pulse" 2>/dev/null
sleep 1
# Force kill if still running
pkill -9 -x "pulse" 2>/dev/null
# Force-kill ANYTHING on our ports
kill_port 7655
@@ -158,11 +161,22 @@ EOF
cleanup() {
echo ""
echo "Stopping services..."
kill $BACKEND_PID 2>/dev/null
# Try graceful shutdown first
if [ -n "$BACKEND_PID" ] && kill -0 $BACKEND_PID 2>/dev/null; then
kill $BACKEND_PID 2>/dev/null
sleep 1
# Force kill if still running
if kill -0 $BACKEND_PID 2>/dev/null; then
echo "Backend not responding to SIGTERM, force killing..."
kill -9 $BACKEND_PID 2>/dev/null
fi
fi
rm -f vite.config.dev.ts
# Clean up any leftover Vite processes
pkill -f vite 2>/dev/null
pkill -f "npm run dev" 2>/dev/null
# Final cleanup of any stuck pulse processes
pkill -9 -x "pulse" 2>/dev/null
echo "Hot-dev stopped. To restart normal service, run: sudo systemctl start pulse-backend"
exit
}