From: Stephan Müller Date: Wed, 15 Aug 2018 10:02:48 +0000 (+0200) Subject: mgr/dashboard: Use task list service in pool list X-Git-Tag: v14.0.1~96^2~5 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=e5a04a3d486456b221d4d841e441b54467035c8e;p=ceph.git mgr/dashboard: Use task list service in pool list Now the pool list uses the task list service to monitor all tasks, in order to extend each pool item with corresponding tasks if any. If there is a running task for a pool you will see what action runs on it in the listing. Fixes: https://tracker.ceph.com/issues/36355 Signed-off-by: Stephan Müller --- 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 ea84ca0d036a..7b5ec49d5ebe 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 @@ -1,7 +1,11 @@ - + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts index 318aea0f99ee..8c3a56adba4e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts @@ -4,17 +4,22 @@ import { RouterTestingModule } from '@angular/router/testing'; import { ToastModule } from 'ng2-toastr'; import { BsModalService, TabsModule } from 'ngx-bootstrap'; +import { of } from 'rxjs'; import { configureTestBed } from '../../../../testing/unit-test-helper'; import { PoolService } from '../../../shared/api/pool.service'; import { DeletionModalComponent } from '../../../shared/components/deletion-modal/deletion-modal.component'; +import { ExecutingTask } from '../../../shared/models/executing-task'; +import { SummaryService } from '../../../shared/services/summary.service'; import { TaskWrapperService } from '../../../shared/services/task-wrapper.service'; import { SharedModule } from '../../../shared/shared.module'; +import { Pool } from '../pool'; import { PoolListComponent } from './pool-list.component'; describe('PoolListComponent', () => { let component: PoolListComponent; let fixture: ComponentFixture; + let poolService: PoolService; configureTestBed({ declarations: [PoolListComponent], @@ -30,8 +35,9 @@ describe('PoolListComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(PoolListComponent); component = fixture.componentInstance; - fixture.detectChanges(); component.permission.read = true; + poolService = TestBed.get(PoolService); + fixture.detectChanges(); }); it('should create', () => { @@ -39,7 +45,6 @@ describe('PoolListComponent', () => { }); describe('pool deletion', () => { - let poolService: PoolService; let taskWrapper: TaskWrapperService; const setSelectedPool = (poolName: string) => { @@ -74,7 +79,6 @@ describe('PoolListComponent', () => { content: Object.assign(new deletionClass(), config.initialState) }; }); - poolService = TestBed.get(PoolService); spyOn(poolService, 'delete').and.stub(); taskWrapper = TestBed.get(TaskWrapperService); spyOn(taskWrapper, 'wrapTaskAroundCall').and.callThrough(); @@ -85,4 +89,79 @@ describe('PoolListComponent', () => { testPoolDeletion('aDifferentPoolName'); }); }); + + describe('handling of executing tasks', () => { + 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; + task.metadata = { pool_name: pool }; + summaryService.addRunningTask(task); + }; + + beforeEach(() => { + summaryService = TestBed.get(SummaryService); + summaryService['summaryDataSource'].next({ executing_tasks: [], finished_tasks: [] }); + pools = []; + addPool('a'); + addPool('b'); + addPool('c'); + component.pools = pools; + spyOn(poolService, 'getList').and.callFake(() => of(pools)); + fixture.detectChanges(); + }); + + it('gets all pools without executing pools', () => { + expect(component.pools.length).toBe(3); + expect(component.pools.every((pool) => !pool.executingTasks)).toBeTruthy(); + }); + + it('gets a pool from a task during creation', () => { + addTask('pool/create', 'd'); + expect(component.pools.length).toBe(4); + expect(component.pools[3].cdExecuting).toBe('Creating'); + }); + + it('gets all pools with one executing pools', () => { + addTask('pool/create', 'a'); + expect(component.pools.length).toBe(3); + expect(component.pools[0].cdExecuting).toBe('Creating'); + expect(component.pools[1].cdExecuting).toBeFalsy(); + expect(component.pools[2].cdExecuting).toBeFalsy(); + }); + + it('gets all pools with multiple executing pools', () => { + addTask('pool/create', 'a'); + addTask('pool/edit', 'a'); + addTask('pool/delete', 'a'); + addTask('pool/edit', 'b'); + addTask('pool/delete', 'b'); + addTask('pool/delete', 'c'); + expect(component.pools.length).toBe(3); + expect(component.pools[0].cdExecuting).toBe('Creating, Updating, Deleting'); + expect(component.pools[1].cdExecuting).toBe('Updating, Deleting'); + expect(component.pools[2].cdExecuting).toBe('Deleting'); + }); + + it('gets all pools with multiple executing tasks (not only pool tasks', () => { + addTask('rbd/create', 'a'); + addTask('rbd/edit', 'a'); + addTask('pool/delete', 'a'); + addTask('pool/edit', 'b'); + addTask('rbd/delete', 'b'); + addTask('rbd/delete', 'c'); + expect(component.pools.length).toBe(3); + expect(component.pools[0].cdExecuting).toBe('Deleting'); + expect(component.pools[1].cdExecuting).toBe('Updating'); + expect(component.pools[2].cdExecuting).toBeFalsy(); + }); + }); }); 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 887b74dcf7e6..f6ddb2e7be96 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 @@ -1,26 +1,33 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { BsModalRef, BsModalService } from 'ngx-bootstrap'; import { PoolService } from '../../../shared/api/pool.service'; import { DeletionModalComponent } from '../../../shared/components/deletion-modal/deletion-modal.component'; +import { TableComponent } from '../../../shared/datatable/table/table.component'; +import { CellTemplate } from '../../../shared/enum/cell-template.enum'; +import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum'; import { CdTableAction } from '../../../shared/models/cd-table-action'; import { CdTableColumn } from '../../../shared/models/cd-table-column'; -import { CdTableFetchDataContext } from '../../../shared/models/cd-table-fetch-data-context'; import { CdTableSelection } from '../../../shared/models/cd-table-selection'; import { ExecutingTask } from '../../../shared/models/executing-task'; import { FinishedTask } from '../../../shared/models/finished-task'; import { Permission } from '../../../shared/models/permissions'; import { AuthStorageService } from '../../../shared/services/auth-storage.service'; +import { TaskListService } from '../../../shared/services/task-list.service'; import { TaskWrapperService } from '../../../shared/services/task-wrapper.service'; import { Pool } from '../pool'; @Component({ selector: 'cd-pool-list', templateUrl: './pool-list.component.html', + providers: [TaskListService], styleUrls: ['./pool-list.component.scss'] }) -export class PoolListComponent { +export class PoolListComponent implements OnInit { + @ViewChild(TableComponent) + table: TableComponent; + pools: Pool[] = []; columns: CdTableColumn[]; selection = new CdTableSelection(); @@ -28,11 +35,13 @@ export class PoolListComponent { executingTasks: ExecutingTask[] = []; permission: Permission; tableActions: CdTableAction[]; + viewCacheStatusList: any[]; constructor( private poolService: PoolService, private taskWrapper: TaskWrapperService, private authStorageService: AuthStorageService, + private taskListService: TaskListService, private modalService: BsModalService ) { this.permission = this.authStorageService.getPermissions().pool; @@ -55,7 +64,8 @@ export class PoolListComponent { { prop: 'pool_name', name: 'Name', - flexGrow: 3 + flexGrow: 3, + cellTransformation: CellTemplate.executing }, { prop: 'type', @@ -98,21 +108,29 @@ export class PoolListComponent { ]; } - updateSelection(selection: CdTableSelection) { - this.selection = selection; - } - - getPoolList(context: CdTableFetchDataContext) { - this.poolService.getList().subscribe( - (pools: Pool[]) => { - this.pools = pools; - }, + ngOnInit() { + this.taskListService.init( + () => this.poolService.getList(), + undefined, + (pools) => (this.pools = pools), () => { - context.error(); - } + this.table.reset(); // Disable loading indicator. + this.viewCacheStatusList = [{ status: ViewCacheStatus.ValueException }]; + }, + (task) => task.name.startsWith('pool/'), + (pool, task) => task.metadata['pool_name'] === pool.pool_name, + { default: (task: ExecutingTask) => new Pool(task.metadata['pool_name']) } ); } + updateSelection(selection: CdTableSelection) { + if (selection.hasSingleSelection && Object.keys(selection.first()).length === 3) { + selection.selected = []; + selection.update(); + } + this.selection = selection; + } + deletePoolModal() { const name = this.selection.first().pool_name; this.modalRef = this.modalService.show(DeletionModalComponent, { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.ts index 28a5bfd3a3d4..cccab137439a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.ts @@ -1,3 +1,5 @@ +import { ExecutingTask } from '../../shared/models/executing-task'; + export class Pool { cache_target_full_ratio_micro: number; fast_read: boolean; @@ -33,6 +35,8 @@ export class Pool { cache_target_dirty_ratio_micro: number; pool: number; removed_snaps: string; + cdExecuting?: string; + executingTasks?: ExecutingTask[]; crush_rule: string; tiers: any[]; hit_set_params: { @@ -56,4 +60,8 @@ export class Pool { last_change: string; min_write_recency_for_promote: number; read_tier: number; + + constructor(name) { + this.pool_name = name; + } }