]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health/health.component.ts
import ceph pacific 16.2.5
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / dashboard / health / health.component.ts
1 import { Component, OnDestroy, OnInit } from '@angular/core';
2
3 import _ from 'lodash';
4 import { Subscription } from 'rxjs';
5
6 import { PgCategoryService } from '~/app/ceph/shared/pg-category.service';
7 import { HealthService } from '~/app/shared/api/health.service';
8 import { CssHelper } from '~/app/shared/classes/css-helper';
9 import { Icons } from '~/app/shared/enum/icons.enum';
10 import { Permissions } from '~/app/shared/models/permissions';
11 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
12 import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe';
13 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
14 import {
15 FeatureTogglesMap$,
16 FeatureTogglesService
17 } from '~/app/shared/services/feature-toggles.service';
18 import { RefreshIntervalService } from '~/app/shared/services/refresh-interval.service';
19
20 @Component({
21 selector: 'cd-health',
22 templateUrl: './health.component.html',
23 styleUrls: ['./health.component.scss']
24 })
25 export class HealthComponent implements OnInit, OnDestroy {
26 healthData: any;
27 interval = new Subscription();
28 permissions: Permissions;
29 enabledFeature$: FeatureTogglesMap$;
30 icons = Icons;
31
32 clientStatsConfig = {
33 colors: [
34 {
35 backgroundColor: [
36 this.cssHelper.propertyValue('chart-color-cyan'),
37 this.cssHelper.propertyValue('chart-color-purple')
38 ]
39 }
40 ]
41 };
42
43 rawCapacityChartConfig = {
44 colors: [
45 {
46 backgroundColor: [
47 this.cssHelper.propertyValue('chart-color-blue'),
48 this.cssHelper.propertyValue('chart-color-gray')
49 ]
50 }
51 ]
52 };
53
54 pgStatusChartConfig = {
55 options: {
56 events: ['']
57 }
58 };
59
60 constructor(
61 private healthService: HealthService,
62 private authStorageService: AuthStorageService,
63 private pgCategoryService: PgCategoryService,
64 private featureToggles: FeatureTogglesService,
65 private refreshIntervalService: RefreshIntervalService,
66 private dimlessBinary: DimlessBinaryPipe,
67 private dimless: DimlessPipe,
68 private cssHelper: CssHelper
69 ) {
70 this.permissions = this.authStorageService.getPermissions();
71 this.enabledFeature$ = this.featureToggles.get();
72 }
73
74 ngOnInit() {
75 this.interval = this.refreshIntervalService.intervalData$.subscribe(() => {
76 this.getHealth();
77 });
78 }
79
80 ngOnDestroy() {
81 this.interval.unsubscribe();
82 }
83
84 getHealth() {
85 this.healthService.getMinimalHealth().subscribe((data: any) => {
86 this.healthData = data;
87 });
88 }
89
90 prepareReadWriteRatio(chart: Record<string, any>) {
91 const ratioLabels = [];
92 const ratioData = [];
93
94 const total =
95 this.healthData.client_perf.write_op_per_sec + this.healthData.client_perf.read_op_per_sec;
96
97 ratioLabels.push(
98 `${$localize`Reads`}: ${this.dimless.transform(
99 this.healthData.client_perf.read_op_per_sec
100 )} ${$localize`/s`}`
101 );
102 ratioData.push(this.calcPercentage(this.healthData.client_perf.read_op_per_sec, total));
103 ratioLabels.push(
104 `${$localize`Writes`}: ${this.dimless.transform(
105 this.healthData.client_perf.write_op_per_sec
106 )} ${$localize`/s`}`
107 );
108 ratioData.push(this.calcPercentage(this.healthData.client_perf.write_op_per_sec, total));
109
110 chart.labels = ratioLabels;
111 chart.dataset[0].data = ratioData;
112 chart.dataset[0].label = `${this.dimless.transform(total)}\n${$localize`IOPS`}`;
113 }
114
115 prepareClientThroughput(chart: Record<string, any>) {
116 const ratioLabels = [];
117 const ratioData = [];
118
119 const total =
120 this.healthData.client_perf.read_bytes_sec + this.healthData.client_perf.write_bytes_sec;
121
122 ratioLabels.push(
123 `${$localize`Reads`}: ${this.dimlessBinary.transform(
124 this.healthData.client_perf.read_bytes_sec
125 )}${$localize`/s`}`
126 );
127 ratioData.push(this.calcPercentage(this.healthData.client_perf.read_bytes_sec, total));
128 ratioLabels.push(
129 `${$localize`Writes`}: ${this.dimlessBinary.transform(
130 this.healthData.client_perf.write_bytes_sec
131 )}${$localize`/s`}`
132 );
133 ratioData.push(this.calcPercentage(this.healthData.client_perf.write_bytes_sec, total));
134
135 chart.labels = ratioLabels;
136 chart.dataset[0].data = ratioData;
137 chart.dataset[0].label = `${this.dimlessBinary
138 .transform(total)
139 .replace(' ', '\n')}${$localize`/s`}`;
140 }
141
142 prepareRawUsage(chart: Record<string, any>, data: Record<string, any>) {
143 const percentAvailable = this.calcPercentage(
144 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes,
145 data.df.stats.total_bytes
146 );
147 const percentUsed = this.calcPercentage(
148 data.df.stats.total_used_raw_bytes,
149 data.df.stats.total_bytes
150 );
151
152 chart.dataset[0].data = [percentUsed, percentAvailable];
153
154 chart.labels = [
155 `${$localize`Used`}: ${this.dimlessBinary.transform(data.df.stats.total_used_raw_bytes)}`,
156 `${$localize`Avail.`}: ${this.dimlessBinary.transform(
157 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes
158 )}`
159 ];
160
161 chart.dataset[0].label = `${percentUsed}%\nof ${this.dimlessBinary.transform(
162 data.df.stats.total_bytes
163 )}`;
164 }
165
166 preparePgStatus(chart: Record<string, any>, data: Record<string, any>) {
167 const categoryPgAmount: Record<string, number> = {};
168 let totalPgs = 0;
169
170 _.forEach(data.pg_info.statuses, (pgAmount, pgStatesText) => {
171 const categoryType = this.pgCategoryService.getTypeByStates(pgStatesText);
172
173 if (_.isUndefined(categoryPgAmount[categoryType])) {
174 categoryPgAmount[categoryType] = 0;
175 }
176 categoryPgAmount[categoryType] += pgAmount;
177 totalPgs += pgAmount;
178 });
179
180 for (const categoryType of this.pgCategoryService.getAllTypes()) {
181 if (_.isUndefined(categoryPgAmount[categoryType])) {
182 categoryPgAmount[categoryType] = 0;
183 }
184 }
185
186 chart.dataset[0].data = this.pgCategoryService
187 .getAllTypes()
188 .map((categoryType) => this.calcPercentage(categoryPgAmount[categoryType], totalPgs));
189
190 chart.labels = [
191 `${$localize`Clean`}: ${this.dimless.transform(categoryPgAmount['clean'])}`,
192 `${$localize`Working`}: ${this.dimless.transform(categoryPgAmount['working'])}`,
193 `${$localize`Warning`}: ${this.dimless.transform(categoryPgAmount['warning'])}`,
194 `${$localize`Unknown`}: ${this.dimless.transform(categoryPgAmount['unknown'])}`
195 ];
196
197 chart.dataset[0].label = `${totalPgs}\n${$localize`PGs`}`;
198 }
199
200 prepareObjects(chart: Record<string, any>, data: Record<string, any>) {
201 const objectCopies = data.pg_info.object_stats.num_object_copies;
202 const healthy =
203 objectCopies -
204 data.pg_info.object_stats.num_objects_misplaced -
205 data.pg_info.object_stats.num_objects_degraded -
206 data.pg_info.object_stats.num_objects_unfound;
207 const healthyPercentage = this.calcPercentage(healthy, objectCopies);
208 const misplacedPercentage = this.calcPercentage(
209 data.pg_info.object_stats.num_objects_misplaced,
210 objectCopies
211 );
212 const degradedPercentage = this.calcPercentage(
213 data.pg_info.object_stats.num_objects_degraded,
214 objectCopies
215 );
216 const unfoundPercentage = this.calcPercentage(
217 data.pg_info.object_stats.num_objects_unfound,
218 objectCopies
219 );
220
221 chart.labels = [
222 `${$localize`Healthy`}: ${healthyPercentage}%`,
223 `${$localize`Misplaced`}: ${misplacedPercentage}%`,
224 `${$localize`Degraded`}: ${degradedPercentage}%`,
225 `${$localize`Unfound`}: ${unfoundPercentage}%`
226 ];
227
228 chart.dataset[0].data = [
229 healthyPercentage,
230 misplacedPercentage,
231 degradedPercentage,
232 unfoundPercentage
233 ];
234
235 chart.dataset[0].label = `${this.dimless.transform(
236 data.pg_info.object_stats.num_objects
237 )}\n${$localize`objects`}`;
238 }
239
240 isClientReadWriteChartShowable() {
241 const readOps = this.healthData.client_perf.read_op_per_sec || 0;
242 const writeOps = this.healthData.client_perf.write_op_per_sec || 0;
243
244 return readOps + writeOps > 0;
245 }
246
247 private calcPercentage(dividend: number, divisor: number) {
248 if (!_.isNumber(dividend) || !_.isNumber(divisor) || divisor === 0) {
249 return 0;
250 }
251
252 return Math.round((dividend / divisor) * 100);
253 }
254 }