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';
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$;
35 backgroundColor: ['--color-cyan', '--color-purple']
40 rawCapacityChartConfig = {
43 backgroundColor: ['--color-blue', '--color-gray']
48 pgStatusChartConfig = {
55 private healthService: HealthService,
57 private authStorageService: AuthStorageService,
58 private pgCategoryService: PgCategoryService,
59 private featureToggles: FeatureTogglesService,
60 private refreshIntervalService: RefreshIntervalService,
61 private dimlessBinary: DimlessBinaryPipe,
62 private dimless: DimlessPipe
64 this.permissions = this.authStorageService.getPermissions();
65 this.enabledFeature$ = this.featureToggles.get();
70 this.interval = this.refreshIntervalService.intervalData$.subscribe(() => {
76 this.interval.unsubscribe();
80 this.healthService.getMinimalHealth().subscribe((data: any) => {
81 this.healthData = data;
85 prepareReadWriteRatio(chart: Record<string, any>) {
86 const ratioLabels = [];
90 this.healthData.client_perf.write_op_per_sec + this.healthData.client_perf.read_op_per_sec;
93 `${this.i18n(`Reads`)}: ${this.dimless.transform(
94 this.healthData.client_perf.read_op_per_sec
95 )} ${this.i18n(`/s`)}`
97 ratioData.push(this.calcPercentage(this.healthData.client_perf.read_op_per_sec, total));
99 `${this.i18n(`Writes`)}: ${this.dimless.transform(
100 this.healthData.client_perf.write_op_per_sec
101 )} ${this.i18n(`/s`)}`
103 ratioData.push(this.calcPercentage(this.healthData.client_perf.write_op_per_sec, total));
105 chart.labels = ratioLabels;
106 chart.dataset[0].data = ratioData;
107 chart.dataset[0].label = `${this.dimless.transform(total)}\n${this.i18n(`IOPS`)}`;
110 prepareClientThroughput(chart: Record<string, any>) {
111 const ratioLabels = [];
112 const ratioData = [];
115 this.healthData.client_perf.read_bytes_sec + this.healthData.client_perf.write_bytes_sec;
118 `${this.i18n(`Reads`)}: ${this.dimlessBinary.transform(
119 this.healthData.client_perf.read_bytes_sec
120 )}${this.i18n(`/s`)}`
122 ratioData.push(this.calcPercentage(this.healthData.client_perf.read_bytes_sec, total));
124 `${this.i18n(`Writes`)}: ${this.dimlessBinary.transform(
125 this.healthData.client_perf.write_bytes_sec
126 )}${this.i18n(`/s`)}`
128 ratioData.push(this.calcPercentage(this.healthData.client_perf.write_bytes_sec, total));
130 chart.labels = ratioLabels;
131 chart.dataset[0].data = ratioData;
132 chart.dataset[0].label = `${this.dimlessBinary.transform(total).replace(' ', '\n')}${this.i18n(
137 prepareRawUsage(chart: Record<string, any>, data: Record<string, any>) {
138 const percentAvailable = this.calcPercentage(
139 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes,
140 data.df.stats.total_bytes
142 const percentUsed = this.calcPercentage(
143 data.df.stats.total_used_raw_bytes,
144 data.df.stats.total_bytes
147 chart.dataset[0].data = [percentUsed, percentAvailable];
150 `${this.i18n(`Used`)}: ${this.dimlessBinary.transform(data.df.stats.total_used_raw_bytes)}`,
151 `${this.i18n(`Avail.`)}: ${this.dimlessBinary.transform(
152 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes
156 chart.dataset[0].label = `${percentUsed}%\nof ${this.dimlessBinary.transform(
157 data.df.stats.total_bytes
161 preparePgStatus(chart: Record<string, any>, data: Record<string, any>) {
162 const categoryPgAmount: Record<string, number> = {};
165 _.forEach(data.pg_info.statuses, (pgAmount, pgStatesText) => {
166 const categoryType = this.pgCategoryService.getTypeByStates(pgStatesText);
168 if (_.isUndefined(categoryPgAmount[categoryType])) {
169 categoryPgAmount[categoryType] = 0;
171 categoryPgAmount[categoryType] += pgAmount;
172 totalPgs += pgAmount;
175 for (const categoryType of this.pgCategoryService.getAllTypes()) {
176 if (_.isUndefined(categoryPgAmount[categoryType])) {
177 categoryPgAmount[categoryType] = 0;
181 chart.dataset[0].data = this.pgCategoryService
183 .map((categoryType) => this.calcPercentage(categoryPgAmount[categoryType], totalPgs));
186 `${this.i18n(`Clean`)}: ${this.dimless.transform(categoryPgAmount['clean'])}`,
187 `${this.i18n(`Working`)}: ${this.dimless.transform(categoryPgAmount['working'])}`,
188 `${this.i18n(`Warning`)}: ${this.dimless.transform(categoryPgAmount['warning'])}`,
189 `${this.i18n(`Unknown`)}: ${this.dimless.transform(categoryPgAmount['unknown'])}`
192 chart.dataset[0].label = `${totalPgs}\n${this.i18n(`PGs`)}`;
195 prepareObjects(chart: Record<string, any>, data: Record<string, any>) {
196 const objectCopies = data.pg_info.object_stats.num_object_copies;
199 data.pg_info.object_stats.num_objects_misplaced -
200 data.pg_info.object_stats.num_objects_degraded -
201 data.pg_info.object_stats.num_objects_unfound;
202 const healthyPercentage = this.calcPercentage(healthy, objectCopies);
203 const misplacedPercentage = this.calcPercentage(
204 data.pg_info.object_stats.num_objects_misplaced,
207 const degradedPercentage = this.calcPercentage(
208 data.pg_info.object_stats.num_objects_degraded,
211 const unfoundPercentage = this.calcPercentage(
212 data.pg_info.object_stats.num_objects_unfound,
217 `${this.i18n(`Healthy`)}: ${healthyPercentage}%`,
218 `${this.i18n(`Misplaced`)}: ${misplacedPercentage}%`,
219 `${this.i18n(`Degraded`)}: ${degradedPercentage}%`,
220 `${this.i18n(`Unfound`)}: ${unfoundPercentage}%`
223 chart.dataset[0].data = [
230 chart.dataset[0].label = `${this.dimless.transform(
231 data.pg_info.object_stats.num_objects
232 )}\n${this.i18n(`objects`)}`;
235 isClientReadWriteChartShowable() {
236 const readOps = this.healthData.client_perf.read_op_per_sec || 0;
237 const writeOps = this.healthData.client_perf.write_op_per_sec || 0;
239 return readOps + writeOps > 0;
242 private calcPercentage(dividend: number, divisor: number) {
243 if (!_.isNumber(dividend) || !_.isNumber(divisor) || divisor === 0) {
247 return Math.round((dividend / divisor) * 100);