--- /dev/null
+<tabset #tabsetChild
+ cdTableDetail
+ *ngIf="selection.hasSingleSelection">
+ <tab i18n-heading
+ heading="Details">
+ <cd-table-key-value [renderObjects]="true"
+ [data]="selection.first()"
+ [autoReload]="false">
+ </cd-table-key-value>
+ </tab>
+ <tab i18n-heading
+ *ngIf="permissions.grafana.read"
+ heading="Performance Details">
+ <cd-grafana [grafanaPath]="'ceph-pool-detail?var-pool_name='
+ + selection.first()['pool_name']"
+ uid="8ypfkWpik"
+ grafanaStyle="one">
+ </cd-grafana>
+ </tab>
+ <tab i18n-heading
+ *ngIf="selection.first()['tiers'].length > 0"
+ heading="Cache Tiers Details">
+ <cd-table [data]="cacheTiers"
+ [columns]="cacheTierColumns"
+ [autoSave]="false"
+ columnMode="flex">
+ </cd-table>
+ </tab>
+</tabset>
--- /dev/null
+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<PoolDetailsComponent>;
+
+ 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();
+ });
+ });
+});
--- /dev/null
+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<CdTableColumn> = [];
+ 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;
+ }
+ }
+ }
+}
[selection]="selection"
[tableActions]="tableActions">
</cd-table-actions>
- <tabset cdTableDetail *ngIf="selection.hasSingleSelection">
- <tab i18n-heading
- heading="Details">
- <cd-table-key-value [renderObjects]="true"
- [data]="getPoolDetails(selection.first())"
- [autoReload]="false">
- </cd-table-key-value>
- </tab>
- <tab i18n-heading
- *ngIf="permissions.grafana.read"
- heading="Performance Details">
- <cd-grafana [grafanaPath]="'ceph-pool-detail?var-pool_name='
- + selection.first()['pool_name']"
- uid="8ypfkWpik"
- grafanaStyle="one">
- </cd-grafana>
- </tab>
- </tabset>
+ <cd-pool-details cdTableDetail
+ [selection]="selection"
+ [permissions]="permissions"
+ [cacheTiers]="selectionCacheTiers">
+ </cd-pool-details>
</cd-table>
<ng-template #poolUsageTpl
import { SharedModule } from '../../../shared/shared.module';
import { PgCategoryService } from '../../shared/pg-category.service';
import { Pool } from '../pool';
+import { PoolDetailsComponent } from '../pool-details/pool-details.component';
import { PoolListComponent } from './pool-list.component';
describe('PoolListComponent', () => {
let fixture: ComponentFixture<PoolListComponent>;
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(),
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;
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();
});
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');
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([]);
+ });
+ });
});
permissions: Permissions;
tableActions: CdTableAction[];
viewCacheStatusList: any[];
+ selectionCacheTiers: any[] = [];
constructor(
private poolService: PoolService,
updateSelection(selection: CdTableSelection) {
this.selection = selection;
+ this.getSelectionTiers();
}
deletePoolModal() {
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));
+ }
}
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';
ServicesModule
],
exports: [PoolListComponent, PoolFormComponent],
- declarations: [PoolListComponent, PoolFormComponent, ErasureCodeProfileFormComponent],
+ declarations: [
+ PoolListComponent,
+ PoolFormComponent,
+ ErasureCodeProfileFormComponent,
+ PoolDetailsComponent
+ ],
entryComponents: [ErasureCodeProfileFormComponent]
})
export class PoolModule {}
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/pool/pool-list/pool-list.component.html</context>
- <context context-type="linenumber">48</context>
+ <context context-type="linenumber">35</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.html</context>
<context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
<context context-type="linenumber">438</context>
</context-group>
- </trans-unit><trans-unit id="4f8b2bb476981727ab34ed40fde1218361f92c45" datatype="html">
- <source>Details</source>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/pool-list/pool-list.component.html</context>
- <context context-type="linenumber">19</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/core/auth/role-details/role-details.component.html</context>
- <context context-type="linenumber">2</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html</context>
- <context context-type="linenumber">3</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html</context>
- <context context-type="linenumber">2</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
- <context context-type="linenumber">3</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/cluster/configuration/configuration-details/configuration-details.component.html</context>
- <context context-type="linenumber">3</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
- <context context-type="linenumber">8</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html</context>
- <context context-type="linenumber">3</context>
- </context-group>
- </trans-unit><trans-unit id="fbbaf5cb02ed419e79a27072478f716a4666a99d" datatype="html">
- <source>Performance Details</source>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/pool-list/pool-list.component.html</context>
- <context context-type="linenumber">27</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html</context>
- <context context-type="linenumber">16</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/cluster/osd/osd-details/osd-details.component.html</context>
- <context context-type="linenumber">46</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/cluster/hosts/host-details/host-details.component.html</context>
- <context context-type="linenumber">3</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html</context>
- <context context-type="linenumber">45</context>
- </context-group>
</trans-unit><trans-unit id="1d8a7c8aea58294a3c57c23af0468ddf0ba0c9c7" datatype="html">
<source>Pools List</source>
<context-group purpose="location">
<context context-type="sourcefile">app/core/navigation/identity/identity.component.html</context>
<context context-type="linenumber">25</context>
</context-group>
+ </trans-unit><trans-unit id="4f8b2bb476981727ab34ed40fde1218361f92c45" datatype="html">
+ <source>Details</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/core/auth/role-details/role-details.component.html</context>
+ <context context-type="linenumber">2</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html</context>
+ <context context-type="linenumber">3</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html</context>
+ <context context-type="linenumber">2</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
+ <context context-type="linenumber">3</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/configuration/configuration-details/configuration-details.component.html</context>
+ <context context-type="linenumber">3</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
+ <context context-type="linenumber">8</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/pool-details/pool-details.component.html</context>
+ <context context-type="linenumber">5</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html</context>
+ <context context-type="linenumber">3</context>
+ </context-group>
</trans-unit><trans-unit id="eeba399c4dae8d4890c27b7a2cd2dc28fcf8b5f9" datatype="html">
<source>Performance Counters</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html</context>
<context context-type="linenumber">9</context>
</context-group>
+ </trans-unit><trans-unit id="fbbaf5cb02ed419e79a27072478f716a4666a99d" datatype="html">
+ <source>Performance Details</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html</context>
+ <context context-type="linenumber">16</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-details/osd-details.component.html</context>
+ <context context-type="linenumber">46</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/hosts/host-details/host-details.component.html</context>
+ <context context-type="linenumber">3</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/pool-details/pool-details.component.html</context>
+ <context context-type="linenumber">13</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html</context>
+ <context context-type="linenumber">45</context>
+ </context-group>
</trans-unit><trans-unit id="45cc8ca94b5a50842a9a8ef804a5ab089a38ae5c" datatype="html">
<source>ID</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/block/mirroring/pool-edit-peer-modal/pool-edit-peer-modal.component.html</context>
<context context-type="linenumber">97</context>
</context-group>
+ </trans-unit><trans-unit id="3938a411d76796f8ae73b72ea4c77661207453bd" datatype="html">
+ <source>Cache Tiers Details</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/pool-details/pool-details.component.html</context>
+ <context context-type="linenumber">22</context>
+ </context-group>
</trans-unit><trans-unit id="0c1e17956453ad772dbe82d6946f62748c692f3e" datatype="html">
<source>Ranks</source>
<context-group purpose="location">
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
+ <trans-unit id="60fb10c145f9c7ede2b9ddf9b2b0b0f6191d0ebd" datatype="html">
+ <source>Cache Mode</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/pool/pool-details/pool-details.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="fa5bfad31d4139f6871443bb74324ffda5cad3e6" datatype="html">
+ <source>Min Evict Age</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/pool/pool-details/pool-details.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="fd0eb851666df45c34c76fabcda79b0d77bc910b" datatype="html">
+ <source>Min Flush Age</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/pool/pool-details/pool-details.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="cb54d2c420a534cee0c308ea5589f8c394ef8d67" datatype="html">
+ <source>Target Max Bytes</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/pool/pool-details/pool-details.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="07f4ebe6615dca466818ffdae2a69ee217d265fb" datatype="html">
+ <source>Target Max Objects</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/pool/pool-details/pool-details.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
<trans-unit id="eaa8f4df53cf7fa78d078e20a701d10f3dccd77d" datatype="html">
<source>No applications added</source>
<context-group purpose="location">