1 import { Component, OnDestroy, OnInit } from '@angular/core';
3 import { I18n } from '@ngx-translate/i18n-polyfill';
4 import * as _ from 'lodash';
5 import { Subscription } from 'rxjs/Subscription';
7 import { HealthService } from '../../../shared/api/health.service';
8 import { Permissions } from '../../../shared/models/permissions';
9 import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
10 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
11 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
15 } from '../../../shared/services/feature-toggles.service';
16 import { RefreshIntervalService } from '../../../shared/services/refresh-interval.service';
17 import { PgCategoryService } from '../../shared/pg-category.service';
18 import { HealthPieColor } from '../health-pie/health-pie-color.enum';
21 selector: 'cd-health',
22 templateUrl: './health.component.html',
23 styleUrls: ['./health.component.scss']
25 export class HealthComponent implements OnInit, OnDestroy {
27 interval = new Subscription();
28 permissions: Permissions;
29 enabledFeature$: FeatureTogglesMap$;
31 rawCapacityChartConfig = {
33 title: { display: true, position: 'bottom' }
36 objectsChartConfig = {
38 title: { display: true, position: 'bottom' }
43 HealthPieColor.DEFAULT_GREEN,
44 HealthPieColor.DEFAULT_MAGENTA,
45 HealthPieColor.DEFAULT_ORANGE,
46 HealthPieColor.DEFAULT_RED
51 pgStatusChartConfig = {
55 HealthPieColor.DEFAULT_GREEN,
56 HealthPieColor.DEFAULT_BLUE,
57 HealthPieColor.DEFAULT_ORANGE,
58 HealthPieColor.DEFAULT_RED
65 private healthService: HealthService,
67 private authStorageService: AuthStorageService,
68 private pgCategoryService: PgCategoryService,
69 private featureToggles: FeatureTogglesService,
70 private refreshIntervalService: RefreshIntervalService,
71 private dimlessBinary: DimlessBinaryPipe,
72 private dimless: DimlessPipe
74 this.permissions = this.authStorageService.getPermissions();
75 this.enabledFeature$ = this.featureToggles.get();
80 this.interval = this.refreshIntervalService.intervalData$.subscribe(() => {
86 this.interval.unsubscribe();
90 this.healthService.getMinimalHealth().subscribe((data: any) => {
91 this.healthData = data;
95 prepareReadWriteRatio(chart) {
96 const ratioLabels = [];
100 this.healthData.client_perf.write_op_per_sec + this.healthData.client_perf.read_op_per_sec;
103 `${this.i18n('Writes')} (${this.calcPercentage(
104 this.healthData.client_perf.write_op_per_sec,
108 ratioData.push(this.healthData.client_perf.write_op_per_sec);
110 `${this.i18n('Reads')} (${this.calcPercentage(
111 this.healthData.client_perf.read_op_per_sec,
115 ratioData.push(this.healthData.client_perf.read_op_per_sec);
117 chart.dataset[0].data = ratioData;
118 chart.labels = ratioLabels;
121 prepareRawUsage(chart, data) {
122 const percentAvailable = this.calcPercentage(
123 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes,
124 data.df.stats.total_bytes
126 const percentUsed = this.calcPercentage(
127 data.df.stats.total_used_raw_bytes,
128 data.df.stats.total_bytes
131 chart.dataset[0].data = [data.df.stats.total_used_raw_bytes, data.df.stats.total_avail_bytes];
134 `${this.dimlessBinary.transform(data.df.stats.total_used_raw_bytes)} ${this.i18n(
136 )} (${percentUsed}%)`,
137 `${this.dimlessBinary.transform(
138 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes
139 )} ${this.i18n('Avail.')} (${percentAvailable}%)`
142 chart.options.title.text = `${this.dimlessBinary.transform(
143 data.df.stats.total_bytes
144 )} ${this.i18n('total')}`;
147 preparePgStatus(chart, data) {
148 const categoryPgAmount = {};
150 _.forEach(data.pg_info.statuses, (pgAmount, pgStatesText) => {
151 const categoryType = this.pgCategoryService.getTypeByStates(pgStatesText);
153 if (_.isUndefined(categoryPgAmount[categoryType])) {
154 categoryPgAmount[categoryType] = 0;
156 categoryPgAmount[categoryType] += pgAmount;
159 chart.dataset[0].data = this.pgCategoryService
161 .map((categoryType) => categoryPgAmount[categoryType]);
164 `${this.i18n('Clean')} (${this.calcPercentage(
165 categoryPgAmount['clean'],
166 data.pg_info.pgs_per_osd
168 `${this.i18n('Working')} (${this.calcPercentage(
169 categoryPgAmount['working'],
170 data.pg_info.pgs_per_osd
172 `${this.i18n('Warning')} (${this.calcPercentage(
173 categoryPgAmount['warning'],
174 data.pg_info.pgs_per_osd
176 `${this.i18n('Unknown')} (${this.calcPercentage(
177 categoryPgAmount['unknown'],
178 data.pg_info.pgs_per_osd
183 prepareObjects(chart, data) {
184 const totalReplicas = data.pg_info.object_stats.num_object_copies;
187 data.pg_info.object_stats.num_objects_misplaced -
188 data.pg_info.object_stats.num_objects_degraded -
189 data.pg_info.object_stats.num_objects_unfound;
192 `${this.i18n('Healthy')} (${this.calcPercentage(healthy, totalReplicas)}%)`,
193 `${this.i18n('Misplaced')} (${this.calcPercentage(
194 data.pg_info.object_stats.num_objects_misplaced,
197 `${this.i18n('Degraded')} (${this.calcPercentage(
198 data.pg_info.object_stats.num_objects_degraded,
201 `${this.i18n('Unfound')} (${this.calcPercentage(
202 data.pg_info.object_stats.num_objects_unfound,
207 chart.dataset[0].data = [
209 data.pg_info.object_stats.num_objects_misplaced,
210 data.pg_info.object_stats.num_objects_degraded,
211 data.pg_info.object_stats.num_objects_unfound
214 chart.options.title.text = `${this.dimless.transform(
215 data.pg_info.object_stats.num_objects
216 )} ${this.i18n('total')} (${this.dimless.transform(totalReplicas)} ${this.i18n('replicas')})`;
218 chart.options.maintainAspectRatio = window.innerWidth >= 375;
221 isClientReadWriteChartShowable() {
222 const readOps = this.healthData.client_perf.read_op_per_sec || 0;
223 const writeOps = this.healthData.client_perf.write_op_per_sec || 0;
225 return readOps + writeOps > 0;
228 private calcPercentage(dividend: number, divisor: number) {
229 if (!_.isNumber(dividend) || !_.isNumber(divisor) || divisor === 0) {
233 return Math.round((dividend / divisor) * 100);