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';
7 import { HealthService } from '../../../shared/api/health.service';
8 import { Icons } from '../../../shared/enum/icons.enum';
9 import { Permissions } from '../../../shared/models/permissions';
10 import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
11 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
12 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
16 } from '../../../shared/services/feature-toggles.service';
17 import { RefreshIntervalService } from '../../../shared/services/refresh-interval.service';
18 import { PgCategoryService } from '../../shared/pg-category.service';
19 import { HealthPieColor } from '../health-pie/health-pie-color.enum';
22 selector: 'cd-health',
23 templateUrl: './health.component.html',
24 styleUrls: ['./health.component.scss']
26 export class HealthComponent implements OnInit, OnDestroy {
28 interval = new Subscription();
29 permissions: Permissions;
30 enabledFeature$: FeatureTogglesMap$;
33 rawCapacityChartConfig = {
35 title: { display: true, position: 'bottom' }
38 objectsChartConfig = {
40 title: { display: true, position: 'bottom' }
45 HealthPieColor.DEFAULT_GREEN,
46 HealthPieColor.DEFAULT_MAGENTA,
47 HealthPieColor.DEFAULT_ORANGE,
48 HealthPieColor.DEFAULT_RED
53 pgStatusChartConfig = {
57 HealthPieColor.DEFAULT_GREEN,
58 HealthPieColor.DEFAULT_BLUE,
59 HealthPieColor.DEFAULT_ORANGE,
60 HealthPieColor.DEFAULT_RED
67 private healthService: HealthService,
69 private authStorageService: AuthStorageService,
70 private pgCategoryService: PgCategoryService,
71 private featureToggles: FeatureTogglesService,
72 private refreshIntervalService: RefreshIntervalService,
73 private dimlessBinary: DimlessBinaryPipe,
74 private dimless: DimlessPipe
76 this.permissions = this.authStorageService.getPermissions();
77 this.enabledFeature$ = this.featureToggles.get();
82 this.interval = this.refreshIntervalService.intervalData$.subscribe(() => {
88 this.interval.unsubscribe();
92 this.healthService.getMinimalHealth().subscribe((data: any) => {
93 this.healthData = data;
97 prepareReadWriteRatio(chart: Record<string, any>) {
98 const ratioLabels = [];
102 this.healthData.client_perf.write_op_per_sec + this.healthData.client_perf.read_op_per_sec;
105 `${this.i18n('Writes')} (${this.calcPercentage(
106 this.healthData.client_perf.write_op_per_sec,
110 ratioData.push(this.healthData.client_perf.write_op_per_sec);
112 `${this.i18n('Reads')} (${this.calcPercentage(
113 this.healthData.client_perf.read_op_per_sec,
117 ratioData.push(this.healthData.client_perf.read_op_per_sec);
119 chart.dataset[0].data = ratioData;
120 chart.labels = ratioLabels;
123 prepareRawUsage(chart: Record<string, any>, data: Record<string, any>) {
124 const percentAvailable = this.calcPercentage(
125 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes,
126 data.df.stats.total_bytes
128 const percentUsed = this.calcPercentage(
129 data.df.stats.total_used_raw_bytes,
130 data.df.stats.total_bytes
133 chart.dataset[0].data = [data.df.stats.total_used_raw_bytes, data.df.stats.total_avail_bytes];
136 `${this.dimlessBinary.transform(data.df.stats.total_used_raw_bytes)} ${this.i18n(
138 )} (${percentUsed}%)`,
139 `${this.dimlessBinary.transform(
140 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes
141 )} ${this.i18n('Avail.')} (${percentAvailable}%)`
144 chart.options.title.text = `${this.dimlessBinary.transform(
145 data.df.stats.total_bytes
146 )} ${this.i18n('total')}`;
149 preparePgStatus(chart: Record<string, any>, data: Record<string, any>) {
150 const categoryPgAmount: Record<string, number> = {};
153 _.forEach(data.pg_info.statuses, (pgAmount, pgStatesText) => {
154 const categoryType = this.pgCategoryService.getTypeByStates(pgStatesText);
156 if (_.isUndefined(categoryPgAmount[categoryType])) {
157 categoryPgAmount[categoryType] = 0;
159 categoryPgAmount[categoryType] += pgAmount;
160 totalPgs += pgAmount;
163 chart.dataset[0].data = this.pgCategoryService
165 .map((categoryType) => categoryPgAmount[categoryType]);
168 `${this.i18n('Clean')} (${this.calcPercentage(categoryPgAmount['clean'], totalPgs)}%)`,
169 `${this.i18n('Working')} (${this.calcPercentage(categoryPgAmount['working'], totalPgs)}%)`,
170 `${this.i18n('Warning')} (${this.calcPercentage(categoryPgAmount['warning'], totalPgs)}%)`,
171 `${this.i18n('Unknown')} (${this.calcPercentage(categoryPgAmount['unknown'], totalPgs)}%)`
175 prepareObjects(chart: Record<string, any>, data: Record<string, any>) {
176 const totalReplicas = data.pg_info.object_stats.num_object_copies;
179 data.pg_info.object_stats.num_objects_misplaced -
180 data.pg_info.object_stats.num_objects_degraded -
181 data.pg_info.object_stats.num_objects_unfound;
184 `${this.i18n('Healthy')} (${this.calcPercentage(healthy, totalReplicas)}%)`,
185 `${this.i18n('Misplaced')} (${this.calcPercentage(
186 data.pg_info.object_stats.num_objects_misplaced,
189 `${this.i18n('Degraded')} (${this.calcPercentage(
190 data.pg_info.object_stats.num_objects_degraded,
193 `${this.i18n('Unfound')} (${this.calcPercentage(
194 data.pg_info.object_stats.num_objects_unfound,
199 chart.dataset[0].data = [
201 data.pg_info.object_stats.num_objects_misplaced,
202 data.pg_info.object_stats.num_objects_degraded,
203 data.pg_info.object_stats.num_objects_unfound
206 chart.options.title.text = `${this.dimless.transform(
207 data.pg_info.object_stats.num_objects
208 )} ${this.i18n('total')} (${this.dimless.transform(totalReplicas)} ${this.i18n('replicas')})`;
210 chart.options.maintainAspectRatio = window.innerWidth >= 375;
213 isClientReadWriteChartShowable() {
214 const readOps = this.healthData.client_perf.read_op_per_sec || 0;
215 const writeOps = this.healthData.client_perf.write_op_per_sec || 0;
217 return readOps + writeOps > 0;
220 private calcPercentage(dividend: number, divisor: number) {
221 if (!_.isNumber(dividend) || !_.isNumber(divisor) || divisor === 0) {
225 return Math.round((dividend / divisor) * 100);