From: Afreen Misbah Date: Tue, 21 Jul 2026 19:05:58 +0000 (+0530) Subject: mgr/dashboard: Fix spacings in donut chart in resiliency card X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=609d54083381025bb9c00ef4482df89f9fe5d0ba;p=ceph.git mgr/dashboard: Fix spacings in donut chart in resiliency card Fixes https://tracker.ceph.com/issues/78507 Signed-off-by: Afreen Misbah --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts index 2930710b55bb..c367ad9ba6a6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts @@ -5,6 +5,7 @@ import { PrometheusService } from '~/app/shared/api/prometheus.service'; import { CellTemplate } from '~/app/shared/enum/cell-template.enum'; import { Icons } from '~/app/shared/enum/icons.enum'; import { PrometheusListHelper } from '~/app/shared/helpers/prometheus-list-helper'; +import { TableComponent } from '~/app/shared/datatable/table/table.component'; 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'; @@ -32,6 +33,7 @@ const SeverityMap = { export class ActiveAlertListComponent extends PrometheusListHelper implements OnInit { @ViewChild('externalLinkTpl', { static: true }) externalLinkTpl: TemplateRef; + @ViewChild(TableComponent) table: TableComponent; columns: CdTableColumn[]; innerColumns: CdTableColumn[]; tableActions: CdTableAction[]; @@ -162,6 +164,14 @@ export class ActiveAlertListComponent extends PrometheusListHelper implements On this.route.queryParams.subscribe((params) => { const severity = params['severity']; this.filters[1].filterInitValue = SeverityMap[severity]; + if (params['search']) { + setTimeout(() => { + if (this.table) { + this.table.search = params['search']; + this.table.updateFilter(); + } + }); + } }); } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.html index 80be6845654b..c641fbd244fa 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.html @@ -369,6 +369,7 @@ size="sm" class="overview-health-card-resiliency-btn" [routerLink]="['/monitoring/active-alerts']" + [queryParams]="{ search: 'CephPG' }" > View alerts ({{ pgAlertCount }}) @@ -396,7 +397,7 @@

Data resiliency reflects data availability and replication (% of placement groups that are active and clean). @@ -418,10 +419,7 @@ let isLast = $last ) { @if (item.count) { -

+

{{ item?.state }}: {{ item.count }} % { { provide: MgrModuleService, useValue: mockMgrModuleService }, { provide: HardwareService, useValue: mockHardwareService }, { provide: HealthService, useValue: mockHealthService }, + { provide: PrometheusAlertService, useValue: { totalAlerts$: of(0), alerts: [] } }, provideRouter([]) ] }).compileComponents(); @@ -250,6 +252,7 @@ describe('OverviewHealthCardComponent (all healthy)', () => { }, { provide: HardwareService, useValue: { getSummary: jest.fn(() => of(healthyMock)) } }, { provide: HealthService, useValue: { getTelemetryStatus: jest.fn(() => of(false)) } }, + { provide: PrometheusAlertService, useValue: { totalAlerts$: of(0), alerts: [] } }, provideRouter([]) ] }).compileComponents(); @@ -326,6 +329,7 @@ describe('OverviewHealthCardComponent (warn only)', () => { }, { provide: HardwareService, useValue: { getSummary: jest.fn(() => of(warnMock)) } }, { provide: HealthService, useValue: { getTelemetryStatus: jest.fn(() => of(false)) } }, + { provide: PrometheusAlertService, useValue: { totalAlerts$: of(0), alerts: [] } }, provideRouter([]) ] }).compileComponents(); @@ -387,6 +391,7 @@ describe('OverviewHealthCardComponent (hw disabled)', () => { }, { provide: HardwareService, useValue: { getSummary: jest.fn(() => of(null)) } }, { provide: HealthService, useValue: { getTelemetryStatus: jest.fn(() => of(false)) } }, + { provide: PrometheusAlertService, useValue: { totalAlerts$: of(0), alerts: [] } }, provideRouter([]) ] }).compileComponents(); @@ -441,6 +446,7 @@ describe('OverviewHealthCardComponent (no permissions)', () => { { provide: MgrModuleService, useValue: { getConfig: jest.fn(() => of({})) } }, { provide: HardwareService, useValue: { getSummary: jest.fn(() => of(null)) } }, { provide: HealthService, useValue: { getTelemetryStatus: jest.fn(() => of(false)) } }, + { provide: PrometheusAlertService, useValue: { totalAlerts$: of(0), alerts: [] } }, provideRouter([]) ] }).compileComponents(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.ts index 1eac035f9b18..602a3ebf3101 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.ts @@ -32,6 +32,7 @@ import { HardwareCardVM, buildHardwareCardVM } from '~/app/shared/models/overview'; +import { AlertState } from '~/app/shared/models/prometheus-alerts'; import { HardwareService } from '~/app/shared/api/hardware.service'; import { HealthService } from '~/app/shared/api/health.service'; import { MgrModuleService } from '~/app/shared/api/mgr-module.service'; @@ -40,6 +41,8 @@ import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { GaugeChartComponent } from '@carbon/charts-angular'; import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service'; +const PG_ALERT_PREFIX = 'CephPG'; + type OverviewHealthData = { summary: Summary; upgrade: UpgradeInfoInterface | null; @@ -148,7 +151,16 @@ export class OverviewHealthCardComponent { shareReplay({ bufferSize: 1, refCount: true }) ); - readonly pgAlertCount$ = this.prometheusAlertService.pgAlerts$.pipe(startWith(0)); + readonly pgAlertCount$ = this.prometheusAlertService.totalAlerts$.pipe( + map( + () => + this.prometheusAlertService.alerts.filter( + (a) => + a.status.state === AlertState.ACTIVE && a.labels.alertname?.startsWith(PG_ALERT_PREFIX) + ).length + ), + startWith(0) + ); readonly telemetryEnabled$: Observable = this.healthService.getTelemetryStatus().pipe( map((enabled: any) => !!enabled), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/overview.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/overview.spec.ts deleted file mode 100644 index 7fbb8d609043..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/overview.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { buildHealthCardVM } from './overview'; -import { HealthSnapshotMap } from './health.interface'; - -function makeSnapshot(checks: Record = {}): HealthSnapshotMap { - return { - fsid: 'test-fsid', - health: { status: 'HEALTH_OK', checks, mutes: [] }, - monmap: { num_mons: 3 }, - osdmap: { in: 3, up: 3, num_osds: 3 }, - pgmap: { - pgs_by_state: [{ state_name: 'active+clean', count: 100 }], - num_pools: 1, - bytes_used: 0, - bytes_total: 1000, - num_pgs: 100, - write_bytes_sec: 0, - read_bytes_sec: 0, - recovering_bytes_per_sec: 0 - }, - mgrmap: { num_active: 1, num_standbys: 1 }, - fsmap: { num_active: 0, num_standbys: 0 }, - num_rgw_gateways: 0, - num_iscsi_gateways: { up: 0, down: 0 }, - num_hosts: 3 - } as any; -} - -describe('buildHealthCardVM', () => { - it('should not include pgAlertCount in the VM', () => { - const vm = buildHealthCardVM(makeSnapshot()); - expect('pgAlertCount' in vm).toBe(false); - }); - - it('should build resiliencyHealth from health checks', () => { - const vm = buildHealthCardVM( - makeSnapshot({ - PG_DEGRADED: { - severity: 'HEALTH_WARN', - summary: { message: 'degraded', count: 5 }, - muted: false - } - }) - ); - expect(vm.resiliencyHealth.severity).toBe('warn'); - }); - - it('should report ok resiliency when no PG checks', () => { - const vm = buildHealthCardVM(makeSnapshot()); - expect(vm.resiliencyHealth.severity).toBe('ok'); - }); -}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.ts index bfed174febe3..4273f8b4f95f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.ts @@ -34,9 +34,6 @@ export class PrometheusAlertService { private warningSubject = new BehaviorSubject(0); readonly warningAlerts$ = this.warningSubject.asObservable(); - private pgAlertsSubject = new BehaviorSubject(0); - readonly pgAlerts$ = this.pgAlertsSubject.asObservable(); - constructor( private alertFormatter: PrometheusAlertFormatter, private prometheusService: PrometheusService @@ -120,16 +117,9 @@ export class PrometheusAlertService { 0 ); - const activePgAlerts = alerts.filter( - (alert) => - alert.status.state === AlertState.ACTIVE && - alert.labels.alertname?.startsWith('CephPG') - ).length; - this.totalSubject.next(this.activeAlerts); this.criticalSubject.next(this.activeCriticalAlerts); this.warningSubject.next(this.activeWarningAlerts); - this.pgAlertsSubject.next(activePgAlerts); this.alerts = alerts .reverse()