]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard : Hide suppressed alert on landing page
authorAbhishek Desai <abhishek.desai1@ibm.com>
Tue, 9 Sep 2025 18:53:05 +0000 (00:23 +0530)
committerAbhishek Desai <abhishek.desai1@ibm.com>
Wed, 24 Sep 2025 12:32:02 +0000 (18:02 +0530)
fixes : https://tracker.ceph.com/issues/72944

Signed-off-by: Abhishek Desai <abhishek.desai1@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/models/prometheus-alerts.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.ts

index b3ba03caed84014ec8f915e3d40b340c7b6b37da..b20efd6509a5c6735c5f6ef459f971e08da5fd1f 100644 (file)
@@ -11,6 +11,7 @@
   <cd-table
     [data]="prometheusAlertService.alerts"
     [columns]="columns"
+    [extraFilterableColumns]="filters"
     identifier="fingerprint"
     [forceIdentifier]="true"
     [customCss]="customCss"
index cc12b29bfcab41261f3a9acde21dd12f708240c6..6aa32132c238d267eabb53a020fcf2102771bfcd 100644 (file)
@@ -8,6 +8,7 @@ import { CdTableAction } from '~/app/shared/models/cd-table-action';
 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
 import { Permission } from '~/app/shared/models/permissions';
+import { AlertState } from '~/app/shared/models/prometheus-alerts';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
 import { URLBuilderService } from '~/app/shared/services/url-builder.service';
@@ -31,6 +32,21 @@ export class ActiveAlertListComponent extends PrometheusListHelper implements On
   icons = Icons;
   expandedInnerRow: any;
 
+  filters: CdTableColumn[] = [
+    {
+      name: $localize`State`,
+      prop: 'status.state',
+      filterOptions: [$localize`All`, $localize`Active`, $localize`Suppressed`],
+      filterInitValue: $localize`Active`,
+      filterPredicate: (row, value) => {
+        if (value === 'Active') return row.status?.state === AlertState.ACTIVE;
+        else if (value === 'Suppressed') return row.status?.state === AlertState.SUPPRESSED;
+        if (value === 'All') return true;
+        return false;
+      }
+    }
+  ];
+
   constructor(
     // NotificationsComponent will refresh all alerts every 5s (No need to do it here as well)
     private authStorageService: AuthStorageService,
index 1613c9650b877deef7488ca1c50703ae86a40520..acff1a473d28ac114895a3e099b3fafdad46829a 100644 (file)
@@ -102,7 +102,7 @@ describe('SilenceFormComponent', () => {
 
     prometheus = new PrometheusHelper();
     prometheusService = TestBed.inject(PrometheusService);
-    spyOn(prometheusService, 'getGroupedAlerts').and.callFake(() => {
+    spyOn(prometheusService, 'getAlerts').and.callFake(() => {
       const name = _.split(router.url, '/').pop();
       return of([prometheus.createAlert(name)]);
     });
@@ -285,7 +285,7 @@ describe('SilenceFormComponent', () => {
       params = { id: 'alert0' };
       expectMode('alertAdd', false, false, 'Create');
       expect(prometheusService.getSilences).not.toHaveBeenCalled();
-      expect(prometheusService.getGroupedAlerts).toHaveBeenCalled();
+      expect(prometheusService.getAlerts).toHaveBeenCalled();
       expect(component.matchers).toEqual([createMatcher('alertname', 'alert0', false)]);
       expect(component.matcherMatch).toEqual({
         cssClass: 'has-success',
index a9002040f07db70ff87886a5be2fac9e8687de79..958039a31dc716fd7d48fdbff9100b7becd49fb7 100644 (file)
@@ -19,7 +19,7 @@ import {
   AlertmanagerSilenceMatcherMatch
 } from '~/app/shared/models/alertmanager-silence';
 import { Permission } from '~/app/shared/models/permissions';
-import { GroupAlertmanagerAlert, PrometheusRule } from '~/app/shared/models/prometheus-alerts';
+import { AlertmanagerAlert, PrometheusRule } from '~/app/shared/models/prometheus-alerts';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { ModalService } from '~/app/shared/services/modal.service';
 import { NotificationService } from '~/app/shared/services/notification.service';
@@ -225,7 +225,7 @@ export class SilenceFormComponent {
           }
         });
       } else {
-        this.prometheusService.getGroupedAlerts().subscribe((alerts) => {
+        this.prometheusService.getAlerts().subscribe((alerts) => {
           const alert = _.find(alerts, ['fingerprint', params.id]);
           if (!_.isUndefined(alert)) {
             this.fillFormByAlert(alert);
@@ -258,7 +258,7 @@ export class SilenceFormComponent {
     this.form.updateValueAndValidity();
   }
 
-  private fillFormByAlert(alert: GroupAlertmanagerAlert) {
+  private fillFormByAlert(alert: AlertmanagerAlert) {
     const labels = alert.labels;
     this.setMatcher({
       name: 'alertname',
index f3e583db274f6a89584267a146c48581cb4afceb..b2b3d10f1603bb517afcb1b884217c3d6cdc7171 100644 (file)
   <ng-container *ngFor="let alert of prometheusAlertService.alerts; let i = index;  trackBy: trackByFn">
     <div [ngClass]="['border-'+alertClass[alert.labels.severity]]"
          *ngIf="alert.status.state === 'active' &&
-         alert.labels.severity === alertType ||
-         !alertType">
+         (alert.labels.severity === alertType ||
+         !alertType)">
       <div class="card tc_alerts border-0 pt-3">
         <div class="row no-gutters ps-2">
           <div class="col-sm-1 text-center">
                        [title]="alert.startsAt | cdDate"
                        i18n>Active since: {{ alert.startsAt  | relativeDate }}</small>
                 <small class="alert_count"
+                       *ngIf="alert.alert_count > 1"
                        [title]="alert.alert_count"
                        i18n>Total occurrences: {{ alert.alert_count }}</small>
               </p>
index a7b24235aa788f9f36a29edcc6ce44571ead8814..a27f4c741781a481c80dd1c95c7ec74da91d7d1f 100644 (file)
@@ -1,3 +1,5 @@
+type AlertState = 'active' | 'suppressed' | 'resolved' | 'unprocessed';
+
 export class PrometheusAlertLabels {
   alertname: string;
   instance: string;
@@ -48,7 +50,7 @@ export class PrometheusRule {
 
 export class AlertmanagerAlert extends CommonAlertmanagerAlert {
   status: {
-    state: 'unprocessed' | 'active' | 'suppressed';
+    state: AlertState;
     silencedBy: null | string[];
     inhibitedBy: null | string[];
   };
@@ -84,10 +86,17 @@ export class AlertmanagerNotification {
 }
 
 export class PrometheusCustomAlert {
-  status: 'resolved' | 'unprocessed' | 'active' | 'suppressed';
+  status: AlertState;
   name: string;
   url: string;
   description: string;
   fingerprint?: string | boolean;
   severity?: string;
 }
+
+export const AlertState = {
+  ACTIVE: 'active' as AlertState,
+  SUPPRESSED: 'suppressed' as AlertState,
+  RESOLVED: 'resolved' as AlertState,
+  UNPROCCESSED: 'unprocessed' as AlertState
+};
index cba2aeedeb39c39f2e32177c49a0dbe2167ea4ed..048ecc572e91d1714a2967e2109071c4cbca1411 100644 (file)
@@ -7,7 +7,8 @@ import {
   AlertmanagerAlert,
   PrometheusCustomAlert,
   PrometheusRule,
-  GroupAlertmanagerAlert
+  GroupAlertmanagerAlert,
+  AlertState
 } from '../models/prometheus-alerts';
 import { PrometheusAlertFormatter } from './prometheus-alert-formatter';
 import { BehaviorSubject } from 'rxjs';
@@ -64,10 +65,15 @@ export class PrometheusAlertService {
 
   private handleAlerts(alertGroups: GroupAlertmanagerAlert[]) {
     const alerts: AlertmanagerAlert[] = alertGroups
-      .map((g) => {
-        if (!g.alerts.length) return null;
-        if (g.alerts.length === 1) return { ...g.alerts[0], alert_count: 1 };
-        return { ...g.alerts[0], alert_count: g.alerts.length, subalerts: g.alerts };
+      .map((group) => {
+        if (!group.alerts.length) return null;
+        if (group.alerts.length === 1) return { ...group.alerts[0], alert_count: 1 };
+        const hasActive = group.alerts.some(
+          (alert: AlertmanagerAlert) => alert.status.state === AlertState.ACTIVE
+        );
+        const parent = { ...group.alerts[0] };
+        if (hasActive) parent.status.state = AlertState.ACTIVE;
+        return { ...parent, alert_count: group.alerts.length, subalerts: group.alerts };
       })
       .filter(Boolean) as AlertmanagerAlert[];
 
@@ -78,19 +84,23 @@ export class PrometheusAlertService {
     }
     this.activeAlerts = _.reduce<AlertmanagerAlert, number>(
       alerts,
-      (result, alert) => (alert.status.state === 'active' ? ++result : result),
+      (result, alert) => (alert.status.state === AlertState.ACTIVE ? ++result : result),
       0
     );
     this.activeCriticalAlerts = _.reduce<AlertmanagerAlert, number>(
       alerts,
       (result, alert) =>
-        alert.status.state === 'active' && alert.labels.severity === 'critical' ? ++result : result,
+        alert.status.state === AlertState.ACTIVE && alert.labels.severity === 'critical'
+          ? ++result
+          : result,
       0
     );
     this.activeWarningAlerts = _.reduce<AlertmanagerAlert, number>(
       alerts,
       (result, alert) =>
-        alert.status.state === 'active' && alert.labels.severity === 'warning' ? ++result : result,
+        alert.status.state === AlertState.ACTIVE && alert.labels.severity === 'warning'
+          ? ++result
+          : result,
       0
     );
     this.alerts = alerts
@@ -105,7 +115,7 @@ export class PrometheusAlertService {
       this.alertFormatter.convertToCustomAlerts(oldAlerts)
     );
     const suppressedFiltered = _.filter(changedAlerts, (alert) => {
-      return alert.status !== 'suppressed';
+      return alert.status !== AlertState.SUPPRESSED;
     });
     const notifications = suppressedFiltered.map((alert) =>
       this.alertFormatter.convertAlertToNotification(alert)
@@ -121,7 +131,7 @@ export class PrometheusAlertService {
   private getVanishedAlerts(alerts: PrometheusCustomAlert[], oldAlerts: PrometheusCustomAlert[]) {
     return _.differenceWith(oldAlerts, alerts, (a, b) => a.fingerprint === b.fingerprint).map(
       (alert) => {
-        alert.status = 'resolved';
+        alert.status = AlertState.RESOLVED;
         return alert;
       }
     );