From: guodan1 Date: Wed, 12 Dec 2018 11:30:34 +0000 (+0800) Subject: mgr/dashboard: Add pool cache tiering details tab X-Git-Tag: v14.1.0~354^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=9e913bfd59c3a08c9e06bd8535bb7b92593ba549;p=ceph.git mgr/dashboard: Add pool cache tiering details tab Fixes:http://tracker.ceph.com/issues/25158 Signed-off-by: familyuu --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.html new file mode 100644 index 0000000000000..8ed4ebf472b50 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.html @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.scss new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.spec.ts new file mode 100644 index 0000000000000..dc939073def06 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.spec.ts @@ -0,0 +1,83 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TabsetComponent, TabsModule } from 'ngx-bootstrap/tabs'; + +import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper'; +import { AppModule } from '../../../app.module'; +import { CdTableSelection } from '../../../shared/models/cd-table-selection'; +import { Permissions } from '../../../shared/models/permissions'; +import { PoolDetailsComponent } from './pool-details.component'; + +describe('PoolDetailsComponent', () => { + let poolDetailsComponent: PoolDetailsComponent; + let fixture: ComponentFixture; + + configureTestBed({ + imports: [TabsModule.forRoot(), AppModule], + decalarations: [PoolDetailsComponent], + providers: [i18nProviders] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PoolDetailsComponent); + poolDetailsComponent = fixture.componentInstance; + poolDetailsComponent.selection = new CdTableSelection(); + poolDetailsComponent.permissions = new Permissions({ + grafana: ['read'] + }); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(poolDetailsComponent).toBeTruthy(); + }); + + describe('Pool details tabset', () => { + beforeEach(() => { + poolDetailsComponent.selection.selected = [ + { + tiers: [0], + pool: 0 + } + ]; + poolDetailsComponent.selection.update(); + }); + + it('should recognize a tabset child', () => { + fixture.detectChanges(); + const tabsetChild: TabsetComponent = poolDetailsComponent.tabsetChild; + expect(tabsetChild).toBeDefined(); + }); + + it('should show "Cache Tiers Details" tab if selected pool has "tiers"', () => { + fixture.detectChanges(); + const tabs = poolDetailsComponent.tabsetChild.tabs; + expect(tabs.length).toBe(3); + expect(tabs[2].heading).toBe('Cache Tiers Details'); + expect(tabs[0].active).toBeTruthy(); + }); + + it('should not show "Cache Tiers Details" tab if selected pool has no "tiers"', () => { + poolDetailsComponent.selection.selected = [ + { + tiers: [] + } + ]; + poolDetailsComponent.selection.update(); + fixture.detectChanges(); + const tabs = poolDetailsComponent.tabsetChild.tabs; + expect(tabs.length).toEqual(2); + expect(tabs[0].active).toBeTruthy(); + }); + + it('current active status of tabs should not change when selection is same with previour selection', () => { + fixture.detectChanges(); + const tabs = poolDetailsComponent.tabsetChild.tabs; + expect(tabs[0].active).toBeTruthy(); + + tabs[1].active = true; + fixture.detectChanges(); + expect(tabs[1].active).toBeTruthy(); + }); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.ts new file mode 100644 index 0000000000000..083745bc5eb12 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.ts @@ -0,0 +1,73 @@ +import { Component, Input, OnChanges, ViewChild } from '@angular/core'; + +import { I18n } from '@ngx-translate/i18n-polyfill'; +import { TabsetComponent } from 'ngx-bootstrap/tabs'; + +import { CdTableColumn } from '../../../shared/models/cd-table-column'; +import { CdTableSelection } from '../../../shared/models/cd-table-selection'; +import { Permissions } from '../../../shared/models/permissions'; + +@Component({ + selector: 'cd-pool-details', + templateUrl: './pool-details.component.html', + styleUrls: ['./pool-details.component.scss'] +}) +export class PoolDetailsComponent implements OnChanges { + cacheTierColumns: Array = []; + prevSelectionPool: Number = -1; + + @Input() + selection: CdTableSelection; + @Input() + permissions: Permissions; + @Input() + cacheTiers: any[]; + @ViewChild(TabsetComponent) + tabsetChild: TabsetComponent; + + constructor(private i18n: I18n) { + this.cacheTierColumns = [ + { + prop: 'pool_name', + name: this.i18n('Name'), + flexGrow: 3 + }, + { + prop: 'cache_mode', + name: this.i18n('Cache Mode'), + flexGrow: 2 + }, + { + prop: 'cache_min_evict_age', + name: this.i18n('Min Evict Age'), + flexGrow: 2 + }, + { + prop: 'cache_min_flush_age', + name: this.i18n('Min Flush Age'), + flexGrow: 2 + }, + { + prop: 'target_max_bytes', + name: this.i18n('Target Max Bytes'), + flexGrow: 2 + }, + { + prop: 'target_max_objects', + name: this.i18n('Target Max Objects'), + flexGrow: 2 + } + ]; + } + + ngOnChanges() { + if (this.tabsetChild) { + if (this.prevSelectionPool !== this.selection.first().pool) { + this.tabsetChild.tabs[0].active = true; + this.prevSelectionPool = this.selection.first().pool; + } else { + return; + } + } + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.html index 66d00f4631c95..124a670d2353c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.html @@ -14,24 +14,11 @@ [selection]="selection" [tableActions]="tableActions"> - - - - - - - - - - + + { @@ -23,8 +24,22 @@ describe('PoolListComponent', () => { let fixture: ComponentFixture; let poolService: PoolService; + const addPool = (pools, name, id) => { + const pool = new Pool(name); + pool.pool = id; + pool.pg_num = 256; + pools.push(pool); + }; + + const setUpPools = (pools) => { + addPool(pools, 'a', 0); + addPool(pools, 'b', 1); + addPool(pools, 'c', 2); + component.pools = pools; + }; + configureTestBed({ - declarations: [PoolListComponent], + declarations: [PoolListComponent, PoolDetailsComponent], imports: [ SharedModule, ToastModule.forRoot(), @@ -97,12 +112,6 @@ describe('PoolListComponent', () => { let pools: Pool[]; let summaryService: SummaryService; - const addPool = (name) => { - const pool = new Pool(name); - pool.pg_num = 256; - pools.push(pool); - }; - const addTask = (name: string, pool: string) => { const task = new ExecutingTask(); task.name = name; @@ -114,10 +123,7 @@ describe('PoolListComponent', () => { summaryService = TestBed.get(SummaryService); summaryService['summaryDataSource'].next({ executing_tasks: [], finished_tasks: [] }); pools = []; - addPool('a'); - addPool('b'); - addPool('c'); - component.pools = pools; + setUpPools(pools); spyOn(poolService, 'getList').and.callFake(() => of(pools)); fixture.detectChanges(); }); @@ -154,7 +160,7 @@ describe('PoolListComponent', () => { expect(component.pools[2].cdExecuting).toBe('Deleting'); }); - it('gets all pools with multiple executing tasks (not only pool tasks', () => { + it('gets all pools with multiple executing tasks (not only pool tasks)', () => { addTask('rbd/create', 'a'); addTask('rbd/edit', 'a'); addTask('pool/delete', 'a'); @@ -267,4 +273,51 @@ describe('PoolListComponent', () => { expect(component.getPoolDetails(pool)).toEqual(expected); }); }); + + describe('getSelectionTiers', () => { + let pools: Pool[]; + const setSelectionTiers = (tiers: number[]) => { + component.selection.selected = [ + { + tiers + } + ]; + component.selection.update(); + component.getSelectionTiers(); + }; + + beforeEach(() => { + pools = []; + setUpPools(pools); + }); + + it('should select multiple existing cache tiers', () => { + setSelectionTiers([0, 1, 2]); + expect(component.selectionCacheTiers).toEqual(pools); + }); + + it('should select correct existing cache tier', () => { + setSelectionTiers([0]); + expect(component.selectionCacheTiers).toEqual([{ pg_num: 256, pool: 0, pool_name: 'a' }]); + }); + + it('should not select cache tier if id is invalid', () => { + setSelectionTiers([-1]); + expect(component.selectionCacheTiers).toEqual([]); + }); + + it('should not select cache tier if empty', () => { + setSelectionTiers([]); + expect(component.selectionCacheTiers).toEqual([]); + }); + + it('should be able to selected one pool with multiple tiers, than with a single tier, than with no tiers', () => { + setSelectionTiers([0, 1, 2]); + expect(component.selectionCacheTiers).toEqual(pools); + setSelectionTiers([0]); + expect(component.selectionCacheTiers).toEqual([{ pg_num: 256, pool: 0, pool_name: 'a' }]); + setSelectionTiers([]); + expect(component.selectionCacheTiers).toEqual([]); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts index 46a921b05483f..a5545572e73dd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts @@ -42,6 +42,7 @@ export class PoolListComponent implements OnInit { permissions: Permissions; tableActions: CdTableAction[]; viewCacheStatusList: any[]; + selectionCacheTiers: any[] = []; constructor( private poolService: PoolService, @@ -169,6 +170,7 @@ export class PoolListComponent implements OnInit { updateSelection(selection: CdTableSelection) { this.selection = selection; + this.getSelectionTiers(); } deletePoolModal() { @@ -225,4 +227,9 @@ export class PoolListComponent implements OnInit { getPoolDetails(pool: object) { return _.omit(pool, ['cdExecuting', 'cdIsBinary']); } + + getSelectionTiers() { + const cacheTierIds = this.selection.hasSingleSelection ? this.selection.first()['tiers'] : []; + this.selectionCacheTiers = this.pools.filter((pool) => cacheTierIds.includes(pool.pool)); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts index 46c4fd073ff43..1a789675b720c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts @@ -11,6 +11,7 @@ import { ServicesModule } from '../../shared/services/services.module'; import { SharedModule } from '../../shared/shared.module'; import { CephSharedModule } from '../shared/ceph-shared.module'; import { ErasureCodeProfileFormComponent } from './erasure-code-profile-form/erasure-code-profile-form.component'; +import { PoolDetailsComponent } from './pool-details/pool-details.component'; import { PoolFormComponent } from './pool-form/pool-form.component'; import { PoolListComponent } from './pool-list/pool-list.component'; @@ -27,7 +28,12 @@ import { PoolListComponent } from './pool-list/pool-list.component'; ServicesModule ], exports: [PoolListComponent, PoolFormComponent], - declarations: [PoolListComponent, PoolFormComponent, ErasureCodeProfileFormComponent], + declarations: [ + PoolListComponent, + PoolFormComponent, + ErasureCodeProfileFormComponent, + PoolDetailsComponent + ], entryComponents: [ErasureCodeProfileFormComponent] }) export class PoolModule {} diff --git a/src/pybind/mgr/dashboard/frontend/src/locale/messages.xlf b/src/pybind/mgr/dashboard/frontend/src/locale/messages.xlf index 9774dc4c8e58f..cd579583d4ba1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/locale/messages.xlf +++ b/src/pybind/mgr/dashboard/frontend/src/locale/messages.xlf @@ -661,7 +661,7 @@ app/ceph/pool/pool-list/pool-list.component.html - 48 + 35 app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.html @@ -1524,62 +1524,6 @@ app/ceph/pool/pool-form/pool-form.component.html 438 - - Details - - app/ceph/pool/pool-list/pool-list.component.html - 19 - - - app/core/auth/role-details/role-details.component.html - 2 - - - app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html - 3 - - - app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html - 2 - - - app/ceph/rgw/rgw-user-details/rgw-user-details.component.html - 3 - - - app/ceph/cluster/configuration/configuration-details/configuration-details.component.html - 3 - - - app/ceph/block/rbd-details/rbd-details.component.html - 8 - - - app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html - 3 - - - Performance Details - - app/ceph/pool/pool-list/pool-list.component.html - 27 - - - app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html - 16 - - - app/ceph/cluster/osd/osd-details/osd-details.component.html - 46 - - - app/ceph/cluster/hosts/host-details/host-details.component.html - 3 - - - app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html - 45 - Pools List @@ -2367,12 +2311,68 @@ app/core/navigation/identity/identity.component.html 25 + + Details + + app/core/auth/role-details/role-details.component.html + 2 + + + app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html + 3 + + + app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html + 2 + + + app/ceph/rgw/rgw-user-details/rgw-user-details.component.html + 3 + + + app/ceph/cluster/configuration/configuration-details/configuration-details.component.html + 3 + + + app/ceph/block/rbd-details/rbd-details.component.html + 8 + + + app/ceph/pool/pool-details/pool-details.component.html + 5 + + + app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html + 3 + Performance Counters app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html 9 + + Performance Details + + app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html + 16 + + + app/ceph/cluster/osd/osd-details/osd-details.component.html + 46 + + + app/ceph/cluster/hosts/host-details/host-details.component.html + 3 + + + app/ceph/pool/pool-details/pool-details.component.html + 13 + + + app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html + 45 + ID @@ -3102,6 +3102,12 @@ app/ceph/block/mirroring/pool-edit-peer-modal/pool-edit-peer-modal.component.html 97 + + Cache Tiers Details + + app/ceph/pool/pool-details/pool-details.component.html + 22 + Ranks @@ -4296,6 +4302,41 @@ 1 + + Cache Mode + + src/app/ceph/pool/pool-details/pool-details.component.ts + 1 + + + + Min Evict Age + + src/app/ceph/pool/pool-details/pool-details.component.ts + 1 + + + + Min Flush Age + + src/app/ceph/pool/pool-details/pool-details.component.ts + 1 + + + + Target Max Bytes + + src/app/ceph/pool/pool-details/pool-details.component.ts + 1 + + + + Target Max Objects + + src/app/ceph/pool/pool-details/pool-details.component.ts + 1 + + No applications added