Skip to content

Commit

Permalink
mgr/dashboard: Add info to Pools table
Browse files Browse the repository at this point in the history
- Column 'Placement Groups' renamed to 'Pg Status':
It shows PG states.
- Created 'CephSharedModule' for shared services within ceph module.
- Created PgCategoryService & PgCategory model (logic encapsulation).
- Color consistency: PG chart (landing page) and cell text color are similar.

Fixes: https://tracker.ceph.com/issues/36740

Signed-off-by: Alfonso Martínez <almartin@redhat.com>
  • Loading branch information
alfonsomthd committed Dec 17, 2018
1 parent f289140 commit 3fcdbdd
Show file tree
Hide file tree
Showing 18 changed files with 439 additions and 185 deletions.
2 changes: 1 addition & 1 deletion src/pybind/mgr/dashboard/controllers/pool.py
Expand Up @@ -54,7 +54,7 @@ def _pool_list(self, attrs=None, stats=False):

return [self._serialize_pool(pool, attrs) for pool in pools]

def list(self, attrs=None, stats=False):
def list(self, attrs=None, stats=True):
return self._pool_list(attrs, stats)

def _get(self, pool_name, attrs=None, stats=False):
Expand Down
Expand Up @@ -7,6 +7,7 @@ import { PopoverModule } from 'ngx-bootstrap/popover';
import { TabsModule } from 'ngx-bootstrap/tabs';

import { SharedModule } from '../../shared/shared.module';
import { CephSharedModule } from '../shared/ceph-shared.module';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HealthPieComponent } from './health-pie/health-pie.component';
import { HealthComponent } from './health/health.component';
Expand All @@ -17,11 +18,10 @@ import { MdsSummaryPipe } from './mds-summary.pipe';
import { MgrSummaryPipe } from './mgr-summary.pipe';
import { MonSummaryPipe } from './mon-summary.pipe';
import { OsdSummaryPipe } from './osd-summary.pipe';
import { PgStatusStylePipe } from './pg-status-style.pipe';
import { PgStatusPipe } from './pg-status.pipe';

@NgModule({
imports: [
CephSharedModule,
CommonModule,
TabsModule.forRoot(),
SharedModule,
Expand All @@ -37,9 +37,7 @@ import { PgStatusPipe } from './pg-status.pipe';
OsdSummaryPipe,
LogColorPipe,
MgrSummaryPipe,
PgStatusPipe,
MdsSummaryPipe,
PgStatusStylePipe,
HealthPieComponent,
InfoCardComponent,
InfoGroupComponent
Expand Down
Expand Up @@ -12,12 +12,13 @@ import { HealthService } from '../../../shared/api/health.service';
import { Permissions } from '../../../shared/models/permissions';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
import { SharedModule } from '../../../shared/shared.module';
import { PgCategoryService } from '../../shared/pg-category.service';
import { HealthPieColor } from '../health-pie/health-pie-color.enum';
import { HealthPieComponent } from '../health-pie/health-pie.component';
import { MdsSummaryPipe } from '../mds-summary.pipe';
import { MgrSummaryPipe } from '../mgr-summary.pipe';
import { MonSummaryPipe } from '../mon-summary.pipe';
import { OsdSummaryPipe } from '../osd-summary.pipe';
import { PgStatusStylePipe } from '../pg-status-style.pipe';
import { PgStatusPipe } from '../pg-status.pipe';
import { HealthComponent } from './health.component';

describe('HealthComponent', () => {
Expand Down Expand Up @@ -49,29 +50,32 @@ describe('HealthComponent', () => {
imports: [SharedModule, HttpClientTestingModule, PopoverModule.forRoot()],
declarations: [
HealthComponent,
HealthPieComponent,
MonSummaryPipe,
OsdSummaryPipe,
MdsSummaryPipe,
MgrSummaryPipe,
PgStatusStylePipe,
PgStatusPipe
MgrSummaryPipe
],
schemas: [NO_ERRORS_SCHEMA],
providers: [i18nProviders, { provide: AuthStorageService, useValue: fakeAuthStorageService }]
providers: [
i18nProviders,
{ provide: AuthStorageService, useValue: fakeAuthStorageService },
PgCategoryService
]
});

beforeEach(() => {
fixture = TestBed.createComponent(HealthComponent);
component = fixture.componentInstance;
getHealthSpy = spyOn(TestBed.get(HealthService), 'getMinimalHealth');
getHealthSpy.and.returnValue(of(healthPayload));
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should render all info groups and all info cards', () => {
getHealthSpy.and.returnValue(of(healthPayload));
fixture.detectChanges();

const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
Expand Down Expand Up @@ -148,7 +152,6 @@ describe('HealthComponent', () => {
});

it('should render "Cluster Status" card text that is not clickable', () => {
getHealthSpy.and.returnValue(of(healthPayload));
fixture.detectChanges();

const clusterStatusCard = fixture.debugElement.query(
Expand Down Expand Up @@ -177,4 +180,70 @@ describe('HealthComponent', () => {
const clickableContent = clusterStatusCard.query(By.css('.info-card-content-clickable'));
expect(clickableContent.nativeElement.textContent).toEqual(` ${payload.health.status} `);
});

it('event binding "prepareReadWriteRatio" is called', () => {
const prepareReadWriteRatio = spyOn(component, 'prepareReadWriteRatio');

const payload = _.cloneDeep(healthPayload);
payload.client_perf['read_op_per_sec'] = 1;
payload.client_perf['write_op_per_sec'] = 1;
getHealthSpy.and.returnValue(of(payload));
fixture.detectChanges();

expect(prepareReadWriteRatio).toHaveBeenCalled();
});

it('event binding "prepareRawUsage" is called', () => {
const prepareRawUsage = spyOn(component, 'prepareRawUsage');

fixture.detectChanges();

expect(prepareRawUsage).toHaveBeenCalled();
});

it('event binding "preparePgStatus" is called', () => {
const preparePgStatus = spyOn(component, 'preparePgStatus');

fixture.detectChanges();

expect(preparePgStatus).toHaveBeenCalled();
});

describe('preparePgStatus', () => {
const expectedChart = (data: number[]) => ({
colors: [
{
backgroundColor: [
HealthPieColor.SHADE_GREEN_CYAN,
HealthPieColor.MEDIUM_DARK_SHADE_CYAN_BLUE,
HealthPieColor.LIGHT_SHADE_BROWN,
HealthPieColor.MEDIUM_LIGHT_SHADE_PINK_RED
]
}
],
labels: ['Clean', 'Working', 'Warning', 'Unknown'],
dataset: [{ data: data }]
});

it('gets no data', () => {
const chart = { dataset: [{}] };
component.preparePgStatus(chart, { pg_info: {} });
expect(chart).toEqual(expectedChart([undefined, undefined, undefined, undefined]));
});

it('gets data from all categories', () => {
const chart = { dataset: [{}] };
component.preparePgStatus(chart, {
pg_info: {
statuses: {
'clean+active+scrubbing+nonMappedState': 4,
'clean+active+scrubbing': 2,
'clean+active': 1,
'clean+active+scrubbing+down': 3
}
}
});
expect(chart).toEqual(expectedChart([1, 2, 3, 4]));
});
});
});
Expand Up @@ -6,6 +6,8 @@ import * as _ from 'lodash';
import { HealthService } from '../../../shared/api/health.service';
import { Permissions } from '../../../shared/models/permissions';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
import { PgCategoryService } from '../../shared/pg-category.service';
import { HealthPieColor } from '../health-pie/health-pie-color.enum';

@Component({
selector: 'cd-health',
Expand All @@ -20,7 +22,8 @@ export class HealthComponent implements OnInit, OnDestroy {
constructor(
private healthService: HealthService,
private i18n: I18n,
private authStorageService: AuthStorageService
private authStorageService: AuthStorageService,
private pgCategoryService: PgCategoryService
) {
this.permissions = this.authStorageService.getPermissions();
}
Expand Down Expand Up @@ -76,65 +79,35 @@ export class HealthComponent implements OnInit, OnDestroy {
}

preparePgStatus(chart, data) {
const pgCategoryClean = this.i18n('Clean');
const pgCategoryCleanStates = ['active', 'clean'];
const pgCategoryWarning = this.i18n('Warning');
const pgCategoryWarningStates = [
'backfill_toofull',
'backfill_unfound',
'down',
'incomplete',
'inconsistent',
'recovery_toofull',
'recovery_unfound',
'remapped',
'snaptrim_error',
'stale',
'undersized'
const categoryPgAmount = {};
chart.labels = [
this.i18n('Clean'),
this.i18n('Working'),
this.i18n('Warning'),
this.i18n('Unknown')
];
const pgCategoryUnknown = this.i18n('Unknown');
const pgCategoryWorking = this.i18n('Working');
const pgCategoryWorkingStates = [
'activating',
'backfill_wait',
'backfilling',
'creating',
'deep',
'degraded',
'forced_backfill',
'forced_recovery',
'peering',
'peered',
'recovering',
'recovery_wait',
'repair',
'scrubbing',
'snaptrim',
'snaptrim_wait'
chart.colors = [
{
backgroundColor: [
HealthPieColor.SHADE_GREEN_CYAN,
HealthPieColor.MEDIUM_DARK_SHADE_CYAN_BLUE,
HealthPieColor.LIGHT_SHADE_BROWN,
HealthPieColor.MEDIUM_LIGHT_SHADE_PINK_RED
]
}
];
let totalPgClean = 0;
let totalPgWarning = 0;
let totalPgUnknown = 0;
let totalPgWorking = 0;

_.forEach(data.pg_info.statuses, (pgAmount, pgStatesText) => {
const pgStates = pgStatesText.split('+');
const isWarning = _.intersection(pgCategoryWarningStates, pgStates).length > 0;
const pgWorkingStates = _.intersection(pgCategoryWorkingStates, pgStates);
const pgCleanStates = _.intersection(pgCategoryCleanStates, pgStates);

if (isWarning) {
totalPgWarning += pgAmount;
} else if (pgStates.length > pgCleanStates.length + pgWorkingStates.length) {
totalPgUnknown += pgAmount;
} else if (pgWorkingStates.length > 0) {
totalPgWorking = pgAmount;
} else {
totalPgClean += pgAmount;
const categoryType = this.pgCategoryService.getTypeByStates(pgStatesText);

if (_.isUndefined(categoryPgAmount[categoryType])) {
categoryPgAmount[categoryType] = 0;
}
categoryPgAmount[categoryType] += pgAmount;
});

chart.labels = [pgCategoryWarning, pgCategoryClean, pgCategoryUnknown, pgCategoryWorking];
chart.dataset[0].data = [totalPgWarning, totalPgClean, totalPgUnknown, totalPgWorking];
chart.dataset[0].data = this.pgCategoryService
.getAllTypes()
.map((categoryType) => categoryPgAmount[categoryType]);
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit 3fcdbdd

Please sign in to comment.