From: Afreen Misbah Date: Fri, 27 Mar 2026 13:40:32 +0000 (+0530) Subject: mgr/dashbaord: Enable overview landing page X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=50cf5f2a5e0d6cfd578f4cae691f3149f2f866e3;p=ceph.git mgr/dashbaord: Enable overview landing page - removes feature toggle - removed unused dashboard component, dashboard v3 component, and helper pipes and components Fixes https://tracker.ceph.com/issues/75749 Signed-off-by: Afreen Misbah --- diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.po.ts index 70063ef8b168..75f4c405f7bc 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.po.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.po.ts @@ -1,7 +1,7 @@ import { PageHelper } from '../page-helper.po'; export class DashboardV3PageHelper extends PageHelper { - pages = { index: { url: '#/overview', id: 'cd-dashboard-v3' } }; + pages = { index: { url: '#/overview', id: 'cd-overview' } }; cardTitle(index: number) { return cy.get('.card-title').its(index).text(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts index 7f62f731c0d0..0e41636c19dd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts @@ -24,7 +24,6 @@ import { SilenceListComponent } from './ceph/cluster/prometheus/silence-list/sil import { ServiceFormComponent } from './ceph/cluster/services/service-form/service-form.component'; import { ServicesComponent } from './ceph/cluster/services/services.component'; import { TelemetryComponent } from './ceph/cluster/telemetry/telemetry.component'; -import { DashboardComponent } from './ceph/dashboard/dashboard/dashboard.component'; import { NfsFormComponent } from './ceph/nfs/nfs-form/nfs-form.component'; import { PerformanceCounterComponent } from './ceph/performance-counter/performance-counter/performance-counter.component'; import { LoginPasswordFormComponent } from './core/auth/login-password-form/login-password-form.component'; @@ -67,6 +66,7 @@ import { CephfsMirroringListComponent } from './ceph/cephfs/cephfs-mirroring-lis import { NotificationsPageComponent } from './core/navigation/notification-panel/notifications-page/notifications-page.component'; import { CephfsMirroringWizardComponent } from './ceph/cephfs/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component'; import { CephfsMirroringErrorComponent } from './ceph/cephfs/cephfs-mirroring-error/cephfs-mirroring-error.component'; +import { OverviewComponent } from './ceph/overview/overview.component'; @Injectable() export class PerformanceCounterBreadcrumbsResolver extends BreadcrumbsResolver { @@ -110,7 +110,7 @@ const routes: Routes = [ canActivate: [AuthGuardService, ChangePasswordGuardService], canActivateChild: [AuthGuardService, ChangePasswordGuardService], children: [ - { path: 'overview', component: DashboardComponent }, + { path: 'overview', component: OverviewComponent }, { path: 'error', component: ErrorComponent }, { path: 'cephfs/mirroring/error', diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/ceph.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/ceph.module.ts index 700211e68c45..93cdd60ab8e1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/ceph.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/ceph.module.ts @@ -4,7 +4,6 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { CephfsModule } from './cephfs/cephfs.module'; import { ClusterModule } from './cluster/cluster.module'; -import { DashboardModule } from './dashboard/dashboard.module'; import { NfsModule } from './nfs/nfs.module'; import { PerformanceCounterModule } from './performance-counter/performance-counter.module'; import { SmbModule } from './smb/smb.module'; @@ -14,7 +13,6 @@ import { TilesModule } from 'carbon-components-angular'; imports: [ CommonModule, ClusterModule, - DashboardModule, PerformanceCounterModule, CephfsModule, NfsModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.spec.ts index d3676c20c015..6511de1cd11d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.spec.ts @@ -9,7 +9,6 @@ import { BehaviorSubject, of } from 'rxjs'; import { InventoryDevice } from '~/app/ceph/cluster/inventory/inventory-devices/inventory-device.model'; import { InventoryDevicesComponent } from '~/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component'; -import { DashboardModule } from '~/app/ceph/dashboard/dashboard.module'; import { HostService } from '~/app/shared/api/host.service'; import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; import { CdFormGroup } from '~/app/shared/forms/cd-form-group'; @@ -143,8 +142,7 @@ describe('OsdFormComponent', () => { SharedModule, RouterTestingModule, ReactiveFormsModule, - ToastrModule.forRoot(), - DashboardModule + ToastrModule.forRoot() ], declarations: [OsdFormComponent, OsdDevicesSelectionGroupsComponent, InventoryDevicesComponent] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts index c9a658ac00c7..a5ecebe9b4f3 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts @@ -8,7 +8,6 @@ import { ToastrModule } from 'ngx-toastr'; import { CephModule } from '~/app/ceph/ceph.module'; import { ClusterModule } from '~/app/ceph/cluster/cluster.module'; -import { DashboardModule } from '~/app/ceph/dashboard/dashboard.module'; import { CoreModule } from '~/app/core/core.module'; import { TableActionsComponent } from '~/app/shared/datatable/table-actions/table-actions.component'; import { SharedModule } from '~/app/shared/shared.module'; @@ -30,7 +29,6 @@ describe('ActiveAlertListComponent', () => { ToastrModule.forRoot(), SharedModule, ClusterModule, - DashboardModule, CephModule, CoreModule ] diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts index 257215413b4f..a1b4a48fad3f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts @@ -12,8 +12,6 @@ import { CephSharedModule } from '../shared/ceph-shared.module'; import { DashboardAreaChartComponent } from './dashboard-area-chart/dashboard-area-chart.component'; import { DashboardPieComponent } from './dashboard-pie/dashboard-pie.component'; import { DashboardTimeSelectorComponent } from './dashboard-time-selector/dashboard-time-selector.component'; -import { DashboardV3Component } from './dashboard/dashboard-v3.component'; -import { PgSummaryPipe } from './pg-summary.pipe'; import { InlineLoadingModule, ToggletipModule, TagModule } from 'carbon-components-angular'; import { ProductiveCardComponent } from '~/app/shared/components/productive-card/productive-card.component'; @@ -35,18 +33,11 @@ import { ProductiveCardComponent } from '~/app/shared/components/productive-card ProductiveCardComponent ], declarations: [ - DashboardV3Component, DashboardPieComponent, - PgSummaryPipe, DashboardAreaChartComponent, DashboardTimeSelectorComponent ], - exports: [ - DashboardV3Component, - DashboardAreaChartComponent, - DashboardTimeSelectorComponent, - DashboardPieComponent - ], + exports: [DashboardAreaChartComponent, DashboardTimeSelectorComponent, DashboardPieComponent], providers: [provideCharts(withDefaultRegisterables())] }) export class DashboardV3Module {} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html deleted file mode 100644 index e7504450d9e9..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html +++ /dev/null @@ -1,322 +0,0 @@ -
-
- -
- -
-
Cluster ID
-
{{ detailsCardData.fsid }}
-
Orchestrator
-
{{ detailsCardData.orchestrator || 'Orchestrator is not available' }}
-
Ceph version
-
- {{ detailsCardData.cephVersion }} - -
-
Cluster API
-
- - {{ origin }}/api-docs - - -
- -
Telemetry Dashboard - - {{ telemetryEnabled ? 'Active' : 'Inactive' }} - -
-
- - {{ telemetryURL }} - - -
-
- - -
Managed By
-
- - {{ managedByConfig['MANAGED_BY_CLUSTERS'][0]['fsid'] }} - - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
- -
- - -
-
- - - Cluster - - -
-
- - -
-
-
-
-
- -
-
- - Hardware -
-
- -
-
-
- - - - - - -
- - -
- -
- - - - - - - - - - - - - - -
-
-
-
-
-
-
- - - -
-
-
-
- - - - -
-
-
-
{{ alert.labels.alertname }}
-

-

- Active since: {{ alert.startsAt | relativeDate }} - Total occurrences: {{ alert.alert_count }} -

-
-
-
-
-
-
-
-
- - - - diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.scss deleted file mode 100644 index 5fc3d4bb1175..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.scss +++ /dev/null @@ -1,93 +0,0 @@ -@use './src/styles/vendor/variables' as vv; -@use '@carbon/layout'; - -.details { - font-size: larger; - - dt { - margin-bottom: 0.3rem; - } - - dd { - margin-bottom: 0.8rem; - } -} - -.status { - .viewAlert { - position: absolute; - right: 2rem; - top: 2rem; - } -} - -.alerts { - ngx-simplebar { - height: 13.5rem; - overflow-x: hidden; - } - - .text-truncate { - -webkit-box-orient: vertical; /* stylelint-disable-line property-no-vendor-prefix */ - display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */ - -webkit-line-clamp: 2; - white-space: normal; - } - - .card-text .date { - display: inline-block; - min-width: layout.rem(220px); - } - - .card-text .alert_count { - display: inline-block; - } -} - -.info-card-popover-cluster-status { - max-height: 20vh; - max-width: 23vw; - - .popover-body { - font-size: 1rem; - max-height: 19vh; - max-width: 100%; - overflow: auto; - - li { - span { - font-size: 1.1em; - font-weight: bold; - } - - span.health-warn-description { - color: vv.$health-color-warning-800 !important; - } - } - } -} - -@media (max-width: vv.$screen-lg-max) { - .info-card-popover-cluster-status { - max-width: 31vw; - } -} - -@media (max-width: vv.$screen-md-max) { - .info-card-popover-cluster-status { - max-width: 46vw; - } -} - -@media (max-width: vv.$screen-sm-max) { - .info-card-popover-cluster-status { - max-width: 83vw; - } -} - -.details-card { - dl, - dd:last-of-type { - margin-bottom: 0; - } -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.spec.ts deleted file mode 100644 index 9a9ede1362ba..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.spec.ts +++ /dev/null @@ -1,378 +0,0 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { RouterTestingModule } from '@angular/router/testing'; - -import _ from 'lodash'; -import { ToastrModule } from 'ngx-toastr'; -import { BehaviorSubject, of } from 'rxjs'; - -import { HealthService } from '~/app/shared/api/health.service'; -import { PrometheusService } from '~/app/shared/api/prometheus.service'; -import { CssHelper } from '~/app/shared/classes/css-helper'; -import { AlertmanagerAlert } from '~/app/shared/models/prometheus-alerts'; -import { FeatureTogglesService } from '~/app/shared/services/feature-toggles.service'; -import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service'; -import { SummaryService } from '~/app/shared/services/summary.service'; -import { SharedModule } from '~/app/shared/shared.module'; -import { configureTestBed } from '~/testing/unit-test-helper'; -import { PgCategoryService } from '../../shared/pg-category.service'; -import { DashboardPieComponent } from '../dashboard-pie/dashboard-pie.component'; -import { PgSummaryPipe } from '../pg-summary.pipe'; -import { DashboardV3Component } from './dashboard-v3.component'; -import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; -import { AlertClass } from '~/app/shared/enum/health-icon.enum'; -import { HealthSnapshotMap } from '~/app/shared/models/health.interface'; -import { VERSION_PREFIX } from '~/app/shared/constants/app.constants'; - -export class SummaryServiceMock { - summaryDataSource = new BehaviorSubject({ - version: - `${VERSION_PREFIX} 17.0.0-12222-gcd0cd7cb ` + - '(b8193bb4cda16ccc5b028c3e1df62bc72350a15d) quincy (dev)' - }); - summaryData$ = this.summaryDataSource.asObservable(); - - subscribe(call: any) { - return this.summaryData$.subscribe(call); - } -} - -describe('Dashbord Component', () => { - let component: DashboardV3Component; - let fixture: ComponentFixture; - let orchestratorService: OrchestratorService; - let getHealthStatusSpy: jasmine.Spy; - let getAlertsSpy: jasmine.Spy; - let fakeFeatureTogglesService: jasmine.Spy; - - const healthStatusPayload: HealthSnapshotMap = { - fsid: '7d0cc9da-ca8d-4539-a953-ab062139c26a', - health: { - status: 'HEALTH_WARN', - checks: { - DASHBOARD_DEBUG: { - severity: 'HEALTH_WARN', - summary: { - message: 'Dashboard debug mode is enabled', - count: 0 - }, - muted: false - } - }, - mutes: [] - }, - monmap: { - num_mons: 3 - }, - osdmap: { - in: 3, - up: 3, - num_osds: 3 - }, - pgmap: { - pgs_by_state: [ - { - state_name: 'active+clean', - count: 497 - } - ], - num_pools: 14, - bytes_used: 3236978688, - bytes_total: 325343772672, - num_pgs: 497, - write_bytes_sec: 0, - read_bytes_sec: 0, - recovering_bytes_per_sec: 0 - }, - mgrmap: { - num_active: 1, - num_standbys: 0 - }, - fsmap: { - num_standbys: 2, - num_active: 1 - }, - num_rgw_gateways: 3, - num_iscsi_gateways: { - up: 0, - down: 0 - }, - num_hosts: 1 - }; - - const alertsPayload: AlertmanagerAlert[] = [ - { - labels: { - alertname: 'CephMgrPrometheusModuleInactive', - instance: 'ceph2:9283', - job: 'ceph', - severity: 'critical' - }, - annotations: { - description: 'The mgr/prometheus module at ceph2:9283 is unreachable.', - summary: 'The mgr/prometheus module is not available' - }, - startsAt: '2022-09-28T08:23:41.152Z', - endsAt: '2022-09-28T15:28:01.152Z', - generatorURL: 'http://prometheus:9090/testUrl', - status: { - state: 'active', - silencedBy: null, - inhibitedBy: null - }, - receivers: ['ceph2'], - fingerprint: 'fingerprint', - alert_count: 1 - }, - { - labels: { - alertname: 'CephOSDDownHigh', - instance: 'ceph:9283', - job: 'ceph', - severity: 'critical' - }, - annotations: { - description: '66.67% or 2 of 3 OSDs are down (>= 10%).', - summary: 'More than 10% of OSDs are down' - }, - startsAt: '2022-09-28T14:17:22.665Z', - endsAt: '2022-09-28T15:28:32.665Z', - generatorURL: 'http://prometheus:9090/testUrl', - status: { - state: 'active', - silencedBy: null, - inhibitedBy: null - }, - receivers: ['default'], - fingerprint: 'fingerprint', - alert_count: 1 - }, - { - labels: { - alertname: 'CephHealthWarning', - instance: 'ceph:9283', - job: 'ceph', - severity: 'warning' - }, - annotations: { - description: 'The cluster state has been HEALTH_WARN for more than 15 minutes.', - summary: 'Ceph is in the WARNING state' - }, - startsAt: '2022-09-28T08:41:38.454Z', - endsAt: '2022-09-28T15:28:38.454Z', - generatorURL: 'http://prometheus:9090/testUrl', - status: { - state: 'active', - silencedBy: null, - inhibitedBy: null - }, - receivers: ['ceph'], - fingerprint: 'fingerprint', - alert_count: 1 - } - ]; - - const orchName: any = 'Cephadm'; - - configureTestBed({ - imports: [RouterTestingModule, HttpClientTestingModule, ToastrModule.forRoot(), SharedModule], - declarations: [DashboardV3Component, DashboardPieComponent, PgSummaryPipe], - schemas: [NO_ERRORS_SCHEMA], - providers: [ - { provide: SummaryService, useClass: SummaryServiceMock }, - PrometheusAlertService, - CssHelper, - PgCategoryService - ] - }); - - beforeEach(() => { - fakeFeatureTogglesService = spyOn(TestBed.inject(FeatureTogglesService), 'get').and.returnValue( - of({ - rbd: true, - mirroring: true, - iscsi: true, - cephfs: true, - rgw: true - }) - ); - fixture = TestBed.createComponent(DashboardV3Component); - component = fixture.componentInstance; - orchestratorService = TestBed.inject(OrchestratorService); - getHealthStatusSpy = spyOn(TestBed.inject(HealthService), 'getHealthSnapshot'); - getHealthStatusSpy.and.returnValue(of(healthStatusPayload)); - getAlertsSpy = spyOn(TestBed.inject(PrometheusService), 'getAlerts'); - getAlertsSpy.and.returnValue(of(alertsPayload)); - component.prometheusAlertService.alerts = alertsPayload; - component.isAlertmanagerConfigured = true; - let prometheusAlertService = TestBed.inject(PrometheusAlertService); - spyOn(prometheusAlertService, 'getGroupedAlerts').and.callFake(() => of([])); - prometheusAlertService.activeCriticalAlerts = 2; - prometheusAlertService.activeWarningAlerts = 1; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should render all cards', () => { - fixture.detectChanges(); - const dashboardCards = fixture.debugElement.nativeElement.querySelectorAll('cd-card'); - expect(dashboardCards.length).toBe(4); - }); - - it('should get corresponding data into detailsCardData', () => { - spyOn(orchestratorService, 'getName').and.returnValue(of(orchName)); - component.ngOnInit(); - expect(component.detailsCardData.fsid).toBe(healthStatusPayload['fsid']); - expect(component.detailsCardData.orchestrator).toBe('Cephadm'); - expect(component.detailsCardData.cephVersion).toBe('17.0.0-12222-gcd0cd7cb quincy (dev)'); - }); - - it('should check if the respective icon is shown for each status', () => { - const payload = _.cloneDeep(healthStatusPayload); - - // HEALTH_WARN - payload.health['status'] = 'HEALTH_WARN'; - payload.health['checks'] = { - FAKE_CHECK: { - severity: 'HEALTH_WARN', - summary: { message: 'fake warning', count: 1 }, - muted: false - } - }; - - getHealthStatusSpy.and.returnValue(of(payload)); - fixture.detectChanges(); - const clusterStatusCard = fixture.debugElement.query(By.css('cd-card[cardTitle="Status"] i')); - expect(clusterStatusCard.nativeElement.title).toEqual(`${payload.health.status}`); - - // HEALTH_ERR - payload.health['status'] = 'HEALTH_ERR'; - payload.health['checks'] = { - FAKE_CHECK: { - severity: 'HEALTH_ERR', - summary: { message: 'fake error', count: 1 }, - muted: false - } - }; - - getHealthStatusSpy.and.returnValue(of(payload)); - fixture.detectChanges(); - expect(clusterStatusCard.nativeElement.title).toEqual(`${payload.health.status}`); - - // HEALTH_OK - payload.health['status'] = 'HEALTH_OK'; - payload.health['checks'] = {}; - - getHealthStatusSpy.and.returnValue(of(payload)); - fixture.detectChanges(); - expect(clusterStatusCard.nativeElement.title).toEqual(`${payload.health.status}`); - }); - - it('should show the actual alert count on each alerts pill', () => { - fixture.detectChanges(); - - const warningAlerts = fixture.debugElement.query(By.css('button[id=warningAlerts] span')); - - const dangerAlerts = fixture.debugElement.query(By.css('button[id=dangerAlerts] span')); - - expect(warningAlerts.nativeElement.textContent).toBe('1'); - expect(dangerAlerts.nativeElement.textContent).toBe('2'); - }); - - it('should show the critical alerts window and its content', () => { - const payload = _.cloneDeep(alertsPayload[0]); - component.toggleAlertsWindow(AlertClass[0]); - fixture.detectChanges(); - - const cardTitle = fixture.debugElement.query(By.css('.tc_alerts h6.card-title')); - - expect(cardTitle.nativeElement.textContent).toBe(payload.labels.alertname); - expect(component.alertType).not.toBe('warning'); - }); - - it('should show the warning alerts window and its content', () => { - const payload = _.cloneDeep(alertsPayload[2]); - component.toggleAlertsWindow(AlertClass.warning); - fixture.detectChanges(); - - const cardTitle = fixture.debugElement.query(By.css('.tc_alerts h6.card-title')); - - expect(cardTitle.nativeElement.textContent).toBe(payload.labels.alertname); - expect(component.alertType).not.toBe('critical'); - }); - - it('should only show the pills when the alerts are not empty', () => { - spyOn(TestBed.inject(PrometheusAlertService), 'alerts').and.returnValue(0); - fixture.detectChanges(); - - const warningAlerts = fixture.debugElement.query(By.css('button[id=warningAlerts]')); - - const dangerAlerts = fixture.debugElement.query(By.css('button[id=dangerAlerts]')); - - expect(warningAlerts).toBe(null); - expect(dangerAlerts).toBe(null); - }); - - it('should render "Status" card text that is not clickable', () => { - const payload = _.cloneDeep(healthStatusPayload); - payload.health['status'] = 'HEALTH_OK'; - payload.health['checks'] = null; - - getHealthStatusSpy.and.returnValue(of(payload)); - fixture.detectChanges(); - const clusterStatusCard = fixture.debugElement.query(By.css('cd-card[cardTitle="Status"]')); - const clickableContent = clusterStatusCard.query(By.css('.lead.text-primary')); - expect(clickableContent).toBeNull(); - }); - - it('should render "Status" card text that is clickable (popover)', () => { - const payload = _.cloneDeep(healthStatusPayload); - payload.health['status'] = 'HEALTH_WARN'; - payload.health['checks'] = { - FAKE_CHECK: { - severity: 'HEALTH_WARN', - summary: { message: 'fake warning', count: 1 }, - muted: false - } - }; - - getHealthStatusSpy.and.returnValue(of(payload)); - fixture.detectChanges(); - - const clusterStatusCard = fixture.debugElement.query(By.css('cd-card[cardTitle="Status"]')); - const clickableContent = clusterStatusCard.query(By.css('.lead.text-primary')); - expect(clickableContent).not.toBeNull(); - }); - - describe('features disabled', () => { - beforeEach(() => { - fakeFeatureTogglesService.and.returnValue( - of({ - rbd: false, - mirroring: false, - iscsi: false, - cephfs: false, - rgw: false - }) - ); - fixture = TestBed.createComponent(DashboardV3Component); - component = fixture.componentInstance; - }); - - it('should not render items related to disabled features', () => { - fixture.detectChanges(); - - const iscsiCard = fixture.debugElement.query(By.css('li[id=iscsi-item]')); - const rgwCard = fixture.debugElement.query(By.css('li[id=rgw-item]')); - const mds = fixture.debugElement.query(By.css('li[id=mds-item]')); - - expect(iscsiCard).toBeFalsy(); - expect(rgwCard).toBeFalsy(); - expect(mds).toBeFalsy(); - }); - }); -}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts deleted file mode 100644 index 2bf07cbc48d4..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts +++ /dev/null @@ -1,310 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; - -import _ from 'lodash'; -import { BehaviorSubject, EMPTY, Observable, Subject, Subscription, of } from 'rxjs'; -import { catchError, exhaustMap, switchMap, takeUntil } from 'rxjs/operators'; - -import { HealthService } from '~/app/shared/api/health.service'; -import { PrometheusService, PromqlGuageMetric } from '~/app/shared/api/prometheus.service'; -import { - CapacityCardQueries, - UtilizationCardQueries -} from '~/app/shared/enum/dashboard-promqls.enum'; -import { Icons } from '~/app/shared/enum/icons.enum'; -import { - CapacityCardDetails, - DashboardDetails, - InventoryCommonDetail, - InventoryDetails -} from '~/app/shared/models/cd-details'; -import { Permissions } from '~/app/shared/models/permissions'; -import { AlertmanagerAlert } from '~/app/shared/models/prometheus-alerts'; -import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; -import { - FeatureTogglesMap$, - FeatureTogglesService -} from '~/app/shared/services/feature-toggles.service'; -import { RefreshIntervalService } from '~/app/shared/services/refresh-interval.service'; -import { SummaryService } from '~/app/shared/services/summary.service'; -import { PrometheusListHelper } from '~/app/shared/helpers/prometheus-list-helper'; -import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service'; -import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; -import { MgrModuleService } from '~/app/shared/api/mgr-module.service'; -import { AlertClass } from '~/app/shared/enum/health-icon.enum'; -import { HardwareService } from '~/app/shared/api/hardware.service'; -import { SettingsService } from '~/app/shared/api/settings.service'; -import { - Health, - HealthSnapshotMap, - IscsiMap, - PgStateCount -} from '~/app/shared/models/health.interface'; -import { VERSION_PREFIX } from '~/app/shared/constants/app.constants'; - -@Component({ - selector: 'cd-dashboard-v3', - templateUrl: './dashboard-v3.component.html', - styleUrls: ['./dashboard-v3.component.scss'], - standalone: false -}) -export class DashboardV3Component extends PrometheusListHelper implements OnInit, OnDestroy { - telemetryURL = 'https://telemetry-public.ceph.com/'; - origin = window.location.origin; - icons = Icons; - - permissions: Permissions; - - hardwareSubject = new BehaviorSubject([]); - private subs = new Subscription(); - private destroy$ = new Subject(); - - enabledFeature$: FeatureTogglesMap$; - prometheusAlerts$: Observable; - isHardwareEnabled$: Observable; - hardwareSummary$: Observable; - managedByConfig$: Observable; - - color: string; - flexHeight = true; - simplebar = { - autoHide: true - }; - borderClass: string; - alertType: string; - alertClass = AlertClass; - - queriesResults: Record = { - USEDCAPACITY: [], - IPS: [], - OPS: [], - READLATENCY: [], - WRITELATENCY: [], - READCLIENTTHROUGHPUT: [], - WRITECLIENTTHROUGHPUT: [], - RECOVERYBYTES: [], - READIOPS: [], - WRITEIOPS: [] - }; - - telemetryEnabled: boolean; - detailsCardData: DashboardDetails = {}; - capacityCardData: CapacityCardDetails = { - osdNearfull: null, - osdFull: null - }; - healthCardData: Health; - hasHealthChecks: boolean; - hardwareHealth: any; - hardwareEnabled: boolean = false; - hasHardwareError: boolean = false; - totalCapacity: number = null; - usedCapacity: number = null; - hostsCount: number = null; - monCount: number = null; - poolCount: number = null; - rgwCount: number = null; - osdCount: { in: number; out: number; up: number; down: number } & InventoryCommonDetail = null; - pgStatus: { statuses: PgStateCount[] } & InventoryCommonDetail = null; - mgrStatus: InventoryDetails = null; - mdsStatus: InventoryDetails = null; - iscsiMap: IscsiMap = null; - - constructor( - private summaryService: SummaryService, - private orchestratorService: OrchestratorService, - private authStorageService: AuthStorageService, - private featureToggles: FeatureTogglesService, - private healthService: HealthService, - private settingsService: SettingsService, - public prometheusService: PrometheusService, - private mgrModuleService: MgrModuleService, - private refreshIntervalService: RefreshIntervalService, - public prometheusAlertService: PrometheusAlertService, - private hardwareService: HardwareService - ) { - super(prometheusService); - this.permissions = this.authStorageService.getPermissions(); - this.enabledFeature$ = this.featureToggles.get(); - } - - ngOnInit() { - super.ngOnInit(); - if (this.permissions.configOpt.read) { - this.isHardwareEnabled$ = this.getHardwareConfig(); - this.hardwareSummary$ = this.hardwareSubject.pipe( - switchMap(() => - this.hardwareService.getSummary().pipe( - switchMap((data: any) => { - this.hasHardwareError = data.host.flawed; - return of(data); - }) - ) - ) - ); - this.managedByConfig$ = this.settingsService.getValues('MANAGED_BY_CLUSTERS'); - } - - this.loadInventories(); - this.getPrometheusData(this.prometheusService.lastHourDateObject); - this.getDetailsCardData(); - this.getTelemetryReport(); - this.getCapacityCardData(); - this.prometheusAlertService.getGroupedAlerts(true); - } - - getTelemetryText(): string { - return this.telemetryEnabled - ? $localize`Cluster telemetry is active` - : $localize`Cluster telemetry is inactive. To Activate the Telemetry, \ - click settings icon on top navigation bar and select \ - Telemetry configration.`; - } - - ngOnDestroy() { - this.prometheusService.unsubscribe(); - this.subs?.unsubscribe(); - this.destroy$.next(); - this.destroy$.complete(); - } - - toggleAlertsWindow(type: AlertClass) { - this.alertType === type ? (this.alertType = null) : (this.alertType = type); - } - - getDetailsCardData() { - this.orchestratorService.getName().subscribe((data: string) => { - this.detailsCardData.orchestrator = data; - }); - this.subs.add( - this.summaryService.subscribe((summary) => { - const version = summary.version.replace(`${VERSION_PREFIX} `, '').split(' '); - this.detailsCardData.cephVersion = - version[0] + ' ' + version.slice(2, version.length).join(' '); - }) - ); - } - - public getPrometheusData(selectedTime: any) { - this.prometheusService - .getRangeQueriesData(selectedTime, UtilizationCardQueries, true) - .pipe(takeUntil(this.destroy$)) - .subscribe((results) => { - this.queriesResults = results; - }); - } - - getCapacityQueryValues(data: PromqlGuageMetric['result']) { - let osdFull = null; - let osdNearfull = null; - if (data?.[0]?.metric?.['__name__'] === CapacityCardQueries.OSD_FULL) { - osdFull = data[0]?.value?.[1]; - osdNearfull = data[1]?.value?.[1]; - } else { - osdFull = data?.[1]?.value?.[1]; - osdNearfull = data?.[0]?.value?.[1]; - } - return [osdFull, osdNearfull]; - } - - getCapacityCardData() { - const CAPACITY_QUERY = `{__name__=~"${CapacityCardQueries.OSD_FULL}|${CapacityCardQueries.OSD_NEARFULL}"}`; - this.prometheusService - .getGaugeQueryData(CAPACITY_QUERY) - .subscribe((data: PromqlGuageMetric) => { - const [osdFull, osdNearfull] = this.getCapacityQueryValues(data?.result); - this.capacityCardData.osdFull = this.prometheusService.formatGuageMetric(osdFull); - this.capacityCardData.osdNearfull = this.prometheusService.formatGuageMetric(osdNearfull); - }); - } - - private getTelemetryReport() { - this.healthService.getTelemetryStatus().subscribe((enabled: boolean) => { - this.telemetryEnabled = enabled; - }); - } - - trackByFn(index: any) { - return index; - } - - getHardwareConfig(): Observable { - return this.mgrModuleService.getConfig('cephadm').pipe( - switchMap((resp: any) => { - this.hardwareEnabled = resp?.hw_monitoring; - return of(resp?.hw_monitoring); - }) - ); - } - - refreshIntervalObs(fn: Function) { - return this.refreshIntervalService.intervalData$.pipe( - exhaustMap(() => fn().pipe(catchError(() => EMPTY))), - takeUntil(this.destroy$) - ); - } - - private safeSum(a: number, b: number): number | null { - return a != null && b != null ? a + b : null; - } - - private safeDifference(a: number, b: number): number | null { - return a != null && b != null ? a - b : null; - } - - loadInventories() { - this.refreshIntervalObs(() => this.healthService.getHealthSnapshot()).subscribe({ - next: (data: HealthSnapshotMap) => { - this.detailsCardData.fsid = data?.fsid; - this.healthCardData = data?.health; - this.hasHealthChecks = !!Object.keys(this.healthCardData?.checks ?? {})?.length; - this.monCount = data?.monmap?.num_mons; - - const osdMap = data?.osdmap; - const osdIn = osdMap?.in; - const osdUp = osdMap?.up; - const osdTotal = osdMap?.num_osds; - - this.osdCount = { - in: osdIn, - up: osdUp, - total: osdTotal, - down: this.safeDifference(osdTotal, osdUp), - out: this.safeDifference(osdTotal, osdIn) - }; - - const pgmap = data?.pgmap; - this.poolCount = pgmap?.num_pools; - this.usedCapacity = pgmap?.bytes_used; - this.totalCapacity = pgmap?.bytes_total; - this.pgStatus = { - statuses: pgmap?.pgs_by_state, - total: pgmap?.num_pgs - }; - - const mgrmap = data?.mgrmap; - const mgrInfo = mgrmap?.num_standbys; - const mgrSuccess = mgrmap?.num_active; - - this.mgrStatus = { - info: mgrInfo, - success: mgrSuccess, - total: this.safeSum(mgrInfo, mgrSuccess) - }; - - const mdsInfo = data?.fsmap?.num_standbys; - const mdsSuccess = data?.fsmap?.num_active; - - this.mdsStatus = { - info: mdsInfo, - success: mdsSuccess, - total: this.safeSum(mdsInfo, mdsSuccess) - }; - - this.rgwCount = data?.num_rgw_gateways; - this.iscsiMap = data?.num_iscsi_gateways; - this.hostsCount = data?.num_hosts; - this.enabledFeature$ = this.featureToggles.get(); - } - }); - } -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/pg-summary.pipe.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/pg-summary.pipe.spec.ts deleted file mode 100644 index eb813a3fe53e..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/pg-summary.pipe.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { configureTestBed } from '~/testing/unit-test-helper'; -import { PgCategoryService } from '../shared/pg-category.service'; -import { PgSummaryPipe } from './pg-summary.pipe'; - -describe('PgSummaryPipe', () => { - let pipe: PgSummaryPipe; - - configureTestBed({ - providers: [PgSummaryPipe, PgCategoryService] - }); - - beforeEach(() => { - pipe = TestBed.inject(PgSummaryPipe); - }); - - it('create an instance', () => { - expect(pipe).toBeTruthy(); - }); - - it('tranforms value', () => { - const value = { - statuses: [ - { - state_name: 'active+clean', - count: 497 - } - ], - total: 497 - }; - expect(pipe.transform(value)).toEqual({ - categoryPgAmount: { - clean: 497 - }, - total: 497 - }); - }); -}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/pg-summary.pipe.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/pg-summary.pipe.ts deleted file mode 100644 index dc3f32934db9..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/pg-summary.pipe.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import { PgCategoryService } from '~/app/ceph/shared/pg-category.service'; -import { PgStateCount } from '~/app/shared/models/health.interface'; - -@Pipe({ - name: 'pgSummary', - standalone: false -}) -export class PgSummaryPipe implements PipeTransform { - constructor(private pgCategoryService: PgCategoryService) {} - - transform(value: any): any { - if (!value) return null; - const categoryPgAmount: Record = {}; - value.statuses.forEach((status: PgStateCount) => { - const categoryType = this.pgCategoryService.getTypeByStates(status?.state_name); - if (!categoryPgAmount?.[categoryType]) { - categoryPgAmount[categoryType] = 0; - } - categoryPgAmount[categoryType] += status?.count; - }); - return { - categoryPgAmount, - total: value.total - }; - } -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts deleted file mode 100644 index 32588db56c12..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { RouterModule } from '@angular/router'; - -import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; -import { provideCharts, withDefaultRegisterables, BaseChartDirective } from 'ng2-charts'; - -import { SharedModule } from '~/app/shared/shared.module'; -import { DashboardV3Module } from '../dashboard-v3/dashboard-v3.module'; -import { CephSharedModule } from '../shared/ceph-shared.module'; -import { FeedbackComponent } from '../shared/feedback/feedback.component'; -import { DashboardComponent } from './dashboard/dashboard.component'; -import { HealthPieComponent } from './health-pie/health-pie.component'; -import { OverviewComponent } from '../overview/overview.component'; -import { - InputModule, - ModalModule, - SelectModule, - ThemeModule, - ToggletipModule -} from 'carbon-components-angular'; - -@NgModule({ - imports: [ - CephSharedModule, - CommonModule, - NgbNavModule, - SharedModule, - RouterModule, - FormsModule, - ReactiveFormsModule, - DashboardV3Module, - BaseChartDirective, - ToggletipModule, - ModalModule, - InputModule, - SelectModule, - OverviewComponent, - ThemeModule - ], - exports: [OverviewComponent], - declarations: [DashboardComponent, HealthPieComponent, FeedbackComponent], - providers: [provideCharts(withDefaultRegisterables())] -}) -export class DashboardModule {} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.html deleted file mode 100644 index 121c9781dd6d..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
-@if (enabledFeature$ | async; as features) { -@if (features.dashboard) { - - -} @else { - - - - - -} -} -
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.scss deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.spec.ts deleted file mode 100644 index 55b2152e1751..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; -import { FeatureTogglesService } from '~/app/shared/services/feature-toggles.service'; - -import { configureTestBed } from '~/testing/unit-test-helper'; -import { DashboardComponent } from './dashboard.component'; - -describe('DashboardComponent', () => { - let component: DashboardComponent; - let fixture: ComponentFixture; - - configureTestBed({ - imports: [NgbNavModule, HttpClientTestingModule], - declarations: [DashboardComponent], - providers: [FeatureTogglesService], - schemas: [NO_ERRORS_SCHEMA] - }); - - beforeEach(() => { - fixture = TestBed.createComponent(DashboardComponent); - component = fixture.componentInstance; - }); - - it('should create', () => { - fixture.detectChanges(); - expect(component).toBeTruthy(); - }); -}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.ts deleted file mode 100644 index b26b1452b09d..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, inject, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; -import { - FeatureTogglesMap, - FeatureTogglesService -} from '~/app/shared/services/feature-toggles.service'; - -@Component({ - selector: 'cd-dashboard', - templateUrl: './dashboard.component.html', - styleUrls: ['./dashboard.component.scss'], - standalone: false -}) -export class DashboardComponent implements OnInit { - enabledFeature$: Observable; - - private featureToggles = inject(FeatureTogglesService); - - ngOnInit() { - this.enabledFeature$ = this.featureToggles.get(); - } -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.html deleted file mode 100644 index a159dddc29e9..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
- - -
-
-
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.scss deleted file mode 100644 index 64e7a9822e22..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -@use './src/styles/chart-tooltip'; - -$canvas-width: 100%; -$canvas-height: 100%; - -.chart-container { - height: $canvas-height; - margin-left: auto; - margin-right: auto; - position: unset; - width: $canvas-width; -} - -.chart-canvas { - height: $canvas-height; - margin-left: auto; - margin-right: auto; - max-height: $canvas-height; - max-width: $canvas-width; - position: unset; - width: $canvas-width; -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.spec.ts deleted file mode 100644 index b87f4bfb5d66..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CssHelper } from '~/app/shared/classes/css-helper'; -import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe'; -import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe'; -import { FormatterService } from '~/app/shared/services/formatter.service'; -import { configureTestBed } from '~/testing/unit-test-helper'; -import { HealthPieComponent } from './health-pie.component'; - -describe('HealthPieComponent', () => { - let component: HealthPieComponent; - let fixture: ComponentFixture; - - configureTestBed({ - schemas: [NO_ERRORS_SCHEMA], - declarations: [HealthPieComponent], - providers: [DimlessBinaryPipe, DimlessPipe, FormatterService, CssHelper] - }); - - beforeEach(() => { - fixture = TestBed.createComponent(HealthPieComponent); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('Add slice border if there is more than one slice with numeric non zero value', () => { - component.chartConfig.dataset[0].data = [48, 0, 1, 0]; - component.ngOnChanges(); - - expect(component.chartConfig.dataset[0].borderWidth).toEqual(1); - }); - - it('Remove slice border if there is only one slice with numeric non zero value', () => { - component.chartConfig.dataset[0].data = [48, 0, undefined, 0]; - component.ngOnChanges(); - - expect(component.chartConfig.dataset[0].borderWidth).toEqual(0); - }); - - it('Remove slice border if there is no slice with numeric non zero value', () => { - component.chartConfig.dataset[0].data = [undefined, 0]; - component.ngOnChanges(); - - expect(component.chartConfig.dataset[0].borderWidth).toEqual(0); - }); - - it('should not hide any slice if there is no user click on legend item', () => { - const initialData = [8, 15]; - component.chartConfig.dataset[0].data = initialData; - component.ngOnChanges(); - - expect(component.chartConfig.dataset[0].data).toEqual(initialData); - }); - - describe('tooltip body', () => { - const tooltipBody = ['text: 10000']; - - it('should return amount converted to appropriate units', () => { - component.isBytesData = false; - expect(component['getChartTooltipBody'](tooltipBody)).toEqual('text: 10 k'); - - component.isBytesData = true; - expect(component['getChartTooltipBody'](tooltipBody)).toEqual('text: 9.8 KiB'); - }); - - it('should not return amount when showing label as tooltip', () => { - component.showLabelAsTooltip = true; - expect(component['getChartTooltipBody'](tooltipBody)).toEqual('text'); - }); - }); -}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.ts deleted file mode 100644 index 88c8f75c7bf2..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { - Component, - ElementRef, - EventEmitter, - Input, - OnChanges, - OnInit, - Output, - ViewChild -} from '@angular/core'; - -import * as Chart from 'chart.js'; -import _ from 'lodash'; - -import { CssHelper } from '~/app/shared/classes/css-helper'; -import { ChartTooltip } from '~/app/shared/models/chart-tooltip'; -import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe'; -import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe'; - -@Component({ - selector: 'cd-health-pie', - templateUrl: './health-pie.component.html', - styleUrls: ['./health-pie.component.scss'], - standalone: false -}) -export class HealthPieComponent implements OnChanges, OnInit { - @ViewChild('chartCanvas', { static: true }) - chartCanvasRef: ElementRef; - @ViewChild('chartTooltip', { static: true }) - chartTooltipRef: ElementRef; - - @Input() - data: any; - @Input() - config = {}; - @Input() - isBytesData = false; - @Input() - tooltipFn: any; - @Input() - showLabelAsTooltip = false; - @Output() - prepareFn = new EventEmitter(); - - chartConfig: any; - - public doughnutChartPlugins: any[] = [ - { - id: 'center_text', - beforeDraw(chart: any) { - const cssHelper = new CssHelper(); - const defaultFontFamily = 'Helvetica Neue, Helvetica, Arial, sans-serif'; - Chart.defaults.font.family = defaultFontFamily; - const ctx = chart.ctx; - if (!chart.options.plugins.center_text || !chart.data.datasets[0].label) { - return; - } - - ctx.save(); - const label = chart.data.datasets[0].label.split('\n'); - - const centerX = (chart.chartArea.left + chart.chartArea.right) / 2; - const centerY = (chart.chartArea.top + chart.chartArea.bottom) / 2; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - - ctx.font = `24px ${defaultFontFamily}`; - ctx.fillStyle = cssHelper.propertyValue('chart-color-center-text'); - ctx.fillText(label[0], centerX, centerY - 10); - - if (label.length > 1) { - ctx.font = `14px ${defaultFontFamily}`; - ctx.fillStyle = cssHelper.propertyValue('chart-color-center-text-description'); - ctx.fillText(label[1], centerX, centerY + 10); - } - ctx.restore(); - } - } - ]; - - constructor( - private dimlessBinary: DimlessBinaryPipe, - private dimless: DimlessPipe, - private cssHelper: CssHelper - ) { - this.chartConfig = { - chartType: 'doughnut', - dataset: [ - { - label: null, - borderWidth: 0, - backgroundColor: [ - this.cssHelper.propertyValue('chart-color-green'), - this.cssHelper.propertyValue('chart-color-yellow'), - this.cssHelper.propertyValue('chart-color-orange'), - this.cssHelper.propertyValue('chart-color-red'), - this.cssHelper.propertyValue('chart-color-blue') - ] - } - ], - options: { - cutout: '90%', - events: ['click', 'mouseout', 'touchstart'], - aspectRatio: 2, - plugins: { - center_text: true, - legend: { - display: true, - position: 'right', - labels: { - boxWidth: 10, - usePointStyle: false - } - }, - tooltips: { - enabled: true, - displayColors: false, - backgroundColor: this.cssHelper.propertyValue('chart-color-tooltip-background'), - cornerRadius: 0, - bodyFontSize: 14, - bodyFontStyle: '600', - position: 'nearest', - xPadding: 12, - yPadding: 12, - callbacks: { - label: (item: Record, data: Record) => { - let text = data.labels[item.index]; - if (!text.includes('%')) { - text = `${text} (${data.datasets[item.datasetIndex].data[item.index]}%)`; - } - return text; - } - } - }, - title: { - display: false - } - } - } - }; - } - - ngOnInit() { - const getStyleTop = (tooltip: any, positionY: number) => { - return positionY + tooltip.caretY - tooltip.height - 10 + 'px'; - }; - - const getStyleLeft = (tooltip: any, positionX: number) => { - return positionX + tooltip.caretX + 'px'; - }; - - const chartTooltip = new ChartTooltip( - this.chartCanvasRef, - this.chartTooltipRef, - getStyleLeft, - getStyleTop - ); - - chartTooltip.getBody = (body: any) => { - return this.getChartTooltipBody(body); - }; - - _.merge(this.chartConfig, this.config); - - this.prepareFn.emit([this.chartConfig, this.data]); - } - - ngOnChanges() { - this.prepareFn.emit([this.chartConfig, this.data]); - this.setChartSliceBorderWidth(); - } - - private getChartTooltipBody(body: string[]) { - const bodySplit = body[0].split(': '); - - if (this.showLabelAsTooltip) { - return bodySplit[0]; - } - - bodySplit[1] = this.isBytesData - ? this.dimlessBinary.transform(bodySplit[1]) - : this.dimless.transform(bodySplit[1]); - - return bodySplit.join(': '); - } - - private setChartSliceBorderWidth() { - let nonZeroValueSlices = 0; - _.forEach(this.chartConfig.dataset[0].data, function (slice) { - if (slice > 0) { - nonZeroValueSlices += 1; - } - }); - - this.chartConfig.dataset[0].borderWidth = nonZeroValueSlices > 1 ? 1 : 0; - } -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/overview.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/overview.component.html index 116b2bd2f8aa..796fbe7baf74 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/overview.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/overview.component.html @@ -1,3 +1,11 @@ + + + + + + + + @let storageCard = (storageCardVm$ | async); @let health = (healthCardVm$ | async);
{ let fixture: ComponentFixture; configureTestBed({ - imports: [DashboardModule, HttpClientTestingModule, SharedModule] + imports: [HttpClientTestingModule, SharedModule] }); beforeEach(() => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/feature-toggles.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/feature-toggles.service.ts index 039ca9dd183c..bb7f2a0d6145 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/feature-toggles.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/feature-toggles.service.ts @@ -12,7 +12,6 @@ export class FeatureTogglesMap { cephfs = true; rgw = true; nfs = true; - dashboard = true; } export type Features = keyof FeatureTogglesMap; export type FeatureTogglesMap$ = Observable; diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index d0c4476e3be6..f83366351ac9 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -7016,9 +7016,6 @@ paths: cephfs: description: '' type: boolean - dashboard: - description: '' - type: boolean iscsi: description: '' type: boolean @@ -7041,7 +7038,6 @@ paths: - cephfs - rgw - nfs - - dashboard type: object application/vnd.ceph.api.v1.0+json: schema: @@ -7049,9 +7045,6 @@ paths: cephfs: description: '' type: boolean - dashboard: - description: '' - type: boolean iscsi: description: '' type: boolean diff --git a/src/pybind/mgr/dashboard/plugins/feature_toggles.py b/src/pybind/mgr/dashboard/plugins/feature_toggles.py index 0ee72fa794fa..e4f0561c5012 100644 --- a/src/pybind/mgr/dashboard/plugins/feature_toggles.py +++ b/src/pybind/mgr/dashboard/plugins/feature_toggles.py @@ -27,7 +27,6 @@ class Features(Enum): CEPHFS = 'cephfs' RGW = 'rgw' NFS = 'nfs' - DASHBOARD = 'dashboard' # if we want to add any custom warning message when enabling a feature # we can add it here as key-value pair in warn_msg. @@ -152,7 +151,6 @@ class FeatureToggles(I.CanMgr, I.Setupable, I.HasOptions, "cephfs": (bool, ''), "rgw": (bool, ''), "nfs": (bool, ''), - "dashboard": (bool, '') } @APIRouter('/feature_toggles')