From: Kiefer Chang Date: Thu, 17 Sep 2020 12:58:47 +0000 (+0800) Subject: mgr/dashboard: display devices' health information within a tabset X-Git-Tag: v17.0.0~1026^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=ba3350c7e8c755d5c84c1a027a3a173191cb898d;p=ceph.git mgr/dashboard: display devices' health information within a tabset Wrap all devices' health information within a tabset instead of displaying them from top to bottom. Add more guard in the HTML template to prevent referencing undefined variables. Fixes: https://tracker.ceph.com/issues/47494 Fixes: https://tracker.ceph.com/issues/43177 Signed-off-by: Kiefer Chang --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html index 2179c049d3496..78e0c15785037 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html @@ -8,75 +8,91 @@ dashboard. - No SMART data available. - + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts index 7570528aba625..6ee1af02e45f2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts @@ -79,6 +79,34 @@ describe('OsdSmartListComponent', () => { component.ngOnChanges(changes); }; + /** + * Verify an alert panel and its attributes. + * + * @param selector The CSS selector for the alert panel. + * @param panelTitle The title should be displayed. + * @param panelType Alert level of panel. Can be in `warning` or `info`. + * @param panelSize Pass `slim` for slim alert panel. + */ + const verifyAlertPanel = ( + selector: string, + panelTitle: string, + panelType: 'warning' | 'info', + panelSize?: 'slim' + ) => { + const alertPanel = fixture.debugElement.query(By.css(selector)); + expect(component.incompatible).toBe(false); + expect(component.loading).toBe(false); + + expect(alertPanel.attributes.type).toBe(panelType); + if (panelSize === 'slim') { + expect(alertPanel.attributes.title).toBe(panelTitle); + expect(alertPanel.attributes.size).toBe(panelSize); + } else { + const panelText = alertPanel.query(By.css('.alert-panel-text')); + expect(panelText.nativeElement.textContent).toBe(panelTitle); + } + }; + configureTestBed({ declarations: [SmartListComponent], imports: [BrowserAnimationsModule, SharedModule, HttpClientTestingModule, NgbNavModule] @@ -143,22 +171,61 @@ describe('OsdSmartListComponent', () => { it('should display info panel for passed self test', () => { initializeComponentWithData('hdd_v1'); fixture.detectChanges(); - const alertPanel = fixture.debugElement.query(By.css('cd-alert-panel#alert-self-test-passed')); - expect(component.incompatible).toBe(false); - expect(component.loading).toBe(false); - expect(alertPanel.attributes.size).toBe('slim'); - expect(alertPanel.attributes.title).toBe('SMART overall-health self-assessment test result'); - expect(alertPanel.attributes.type).toBe('info'); + verifyAlertPanel( + 'cd-alert-panel#alert-self-test-passed', + 'SMART overall-health self-assessment test result', + 'info', + 'slim' + ); }); it('should display warning panel for failed self test', () => { initializeComponentWithData('hdd_v1', { 'smart_status.passed': false }); fixture.detectChanges(); - const alertPanel = fixture.debugElement.query(By.css('cd-alert-panel#alert-self-test-failed')); - expect(component.incompatible).toBe(false); - expect(component.loading).toBe(false); - expect(alertPanel.attributes.size).toBe('slim'); - expect(alertPanel.attributes.title).toBe('SMART overall-health self-assessment test result'); - expect(alertPanel.attributes.type).toBe('warning'); + verifyAlertPanel( + 'cd-alert-panel#alert-self-test-failed', + 'SMART overall-health self-assessment test result', + 'warning', + 'slim' + ); + }); + + it('should display warning panel for unknown self test', () => { + initializeComponentWithData('hdd_v1', { smart_status: undefined }); + fixture.detectChanges(); + verifyAlertPanel( + 'cd-alert-panel#alert-self-test-unknown', + 'SMART overall-health self-assessment test result', + 'warning', + 'slim' + ); + }); + + it('should display info panel for empty device info', () => { + initializeComponentWithData('hdd_v1'); + const deviceId: string = _.keys(component.data)[0]; + component.data[deviceId]['info'] = {}; + fixture.detectChanges(); + component.nav.select(1); + fixture.detectChanges(); + verifyAlertPanel( + 'cd-alert-panel#alert-device-info-unavailable', + 'No device information available for this device.', + 'info' + ); + }); + + it('should display info panel for empty SMART data', () => { + initializeComponentWithData('hdd_v1'); + const deviceId: string = _.keys(component.data)[0]; + component.data[deviceId]['smart'] = {}; + fixture.detectChanges(); + component.nav.select(2); + fixture.detectChanges(); + verifyAlertPanel( + 'cd-alert-panel#alert-device-smart-data-unavailable', + 'No SMART data available for this device.', + 'info' + ); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.ts index ee9c729813e4c..e9cab7e9c4d69 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.ts @@ -1,5 +1,6 @@ -import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core'; +import { NgbNav } from '@ng-bootstrap/ng-bootstrap'; import _ from 'lodash'; import { HostService } from '../../../shared/api/host.service'; @@ -19,6 +20,9 @@ import { styleUrls: ['./smart-list.component.scss'] }) export class SmartListComponent implements OnInit, OnChanges { + @ViewChild('innerNav') + nav: NgbNav; + @Input() osdId: number = null; @Input() @@ -141,6 +145,10 @@ smartmontools is required to successfully retrieve data.`; } } + isEmpty(value: any) { + return _.isEmpty(value); + } + ngOnInit() { this.smartDataColumns = [ { prop: 'id', name: $localize`ID` },