]> git.proxmox.com Git - ceph.git/blame - 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
CommitLineData
11fdf7f2
TL
1import { Component, OnDestroy, OnInit } from '@angular/core';
2
f67539c2 3import _ from 'lodash';
9f95a23c 4import { Subscription } from 'rxjs';
11fdf7f2 5
f67539c2
TL
6import { PgCategoryService } from '~/app/ceph/shared/pg-category.service';
7import { HealthService } from '~/app/shared/api/health.service';
b3b6e05e 8import { CssHelper } from '~/app/shared/classes/css-helper';
f67539c2
TL
9import { Icons } from '~/app/shared/enum/icons.enum';
10import { Permissions } from '~/app/shared/models/permissions';
11import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
12import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe';
13import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
11fdf7f2
TL
14import {
15 FeatureTogglesMap$,
16 FeatureTogglesService
f67539c2
TL
17} from '~/app/shared/services/feature-toggles.service';
18import { RefreshIntervalService } from '~/app/shared/services/refresh-interval.service';
11fdf7f2
TL
19
20@Component({
21 selector: 'cd-health',
22 templateUrl: './health.component.html',
23 styleUrls: ['./health.component.scss']
24})
25export class HealthComponent implements OnInit, OnDestroy {
26 healthData: any;
27 interval = new Subscription();
28 permissions: Permissions;
29 enabledFeature$: FeatureTogglesMap$;
9f95a23c 30 icons = Icons;
11fdf7f2 31
f91f0fd5 32 clientStatsConfig = {
81eedcae
TL
33 colors: [
34 {
b3b6e05e
TL
35 backgroundColor: [
36 this.cssHelper.propertyValue('chart-color-cyan'),
37 this.cssHelper.propertyValue('chart-color-purple')
38 ]
81eedcae
TL
39 }
40 ]
41 };
f91f0fd5
TL
42
43 rawCapacityChartConfig = {
81eedcae
TL
44 colors: [
45 {
b3b6e05e
TL
46 backgroundColor: [
47 this.cssHelper.propertyValue('chart-color-blue'),
48 this.cssHelper.propertyValue('chart-color-gray')
49 ]
81eedcae
TL
50 }
51 ]
52 };
53
f91f0fd5
TL
54 pgStatusChartConfig = {
55 options: {
56 events: ['']
57 }
58 };
59
11fdf7f2
TL
60 constructor(
61 private healthService: HealthService,
11fdf7f2
TL
62 private authStorageService: AuthStorageService,
63 private pgCategoryService: PgCategoryService,
64 private featureToggles: FeatureTogglesService,
81eedcae
TL
65 private refreshIntervalService: RefreshIntervalService,
66 private dimlessBinary: DimlessBinaryPipe,
b3b6e05e
TL
67 private dimless: DimlessPipe,
68 private cssHelper: CssHelper
11fdf7f2
TL
69 ) {
70 this.permissions = this.authStorageService.getPermissions();
71 this.enabledFeature$ = this.featureToggles.get();
72 }
73
74 ngOnInit() {
11fdf7f2
TL
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
9f95a23c 90 prepareReadWriteRatio(chart: Record<string, any>) {
11fdf7f2
TL
91 const ratioLabels = [];
92 const ratioData = [];
93
81eedcae
TL
94 const total =
95 this.healthData.client_perf.write_op_per_sec + this.healthData.client_perf.read_op_per_sec;
96
97 ratioLabels.push(
f67539c2 98 `${$localize`Reads`}: ${this.dimless.transform(
f91f0fd5 99 this.healthData.client_perf.read_op_per_sec
f67539c2 100 )} ${$localize`/s`}`
81eedcae 101 );
adb31ebb 102 ratioData.push(this.calcPercentage(this.healthData.client_perf.read_op_per_sec, total));
81eedcae 103 ratioLabels.push(
f67539c2 104 `${$localize`Writes`}: ${this.dimless.transform(
f91f0fd5 105 this.healthData.client_perf.write_op_per_sec
f67539c2 106 )} ${$localize`/s`}`
81eedcae 107 );
f91f0fd5 108 ratioData.push(this.calcPercentage(this.healthData.client_perf.write_op_per_sec, total));
11fdf7f2 109
f91f0fd5 110 chart.labels = ratioLabels;
11fdf7f2 111 chart.dataset[0].data = ratioData;
f67539c2 112 chart.dataset[0].label = `${this.dimless.transform(total)}\n${$localize`IOPS`}`;
f91f0fd5
TL
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(
f67539c2 123 `${$localize`Reads`}: ${this.dimlessBinary.transform(
f91f0fd5 124 this.healthData.client_perf.read_bytes_sec
f67539c2 125 )}${$localize`/s`}`
f91f0fd5
TL
126 );
127 ratioData.push(this.calcPercentage(this.healthData.client_perf.read_bytes_sec, total));
128 ratioLabels.push(
f67539c2 129 `${$localize`Writes`}: ${this.dimlessBinary.transform(
f91f0fd5 130 this.healthData.client_perf.write_bytes_sec
f67539c2 131 )}${$localize`/s`}`
f91f0fd5
TL
132 );
133 ratioData.push(this.calcPercentage(this.healthData.client_perf.write_bytes_sec, total));
134
11fdf7f2 135 chart.labels = ratioLabels;
f91f0fd5 136 chart.dataset[0].data = ratioData;
f67539c2
TL
137 chart.dataset[0].label = `${this.dimlessBinary
138 .transform(total)
139 .replace(' ', '\n')}${$localize`/s`}`;
11fdf7f2
TL
140 }
141
9f95a23c 142 prepareRawUsage(chart: Record<string, any>, data: Record<string, any>) {
81eedcae
TL
143 const percentAvailable = this.calcPercentage(
144 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes,
145 data.df.stats.total_bytes
11fdf7f2 146 );
81eedcae
TL
147 const percentUsed = this.calcPercentage(
148 data.df.stats.total_used_raw_bytes,
149 data.df.stats.total_bytes
11fdf7f2
TL
150 );
151
f91f0fd5 152 chart.dataset[0].data = [percentUsed, percentAvailable];
81eedcae 153
11fdf7f2 154 chart.labels = [
f67539c2
TL
155 `${$localize`Used`}: ${this.dimlessBinary.transform(data.df.stats.total_used_raw_bytes)}`,
156 `${$localize`Avail.`}: ${this.dimlessBinary.transform(
81eedcae 157 data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes
f91f0fd5 158 )}`
11fdf7f2 159 ];
81eedcae 160
f91f0fd5 161 chart.dataset[0].label = `${percentUsed}%\nof ${this.dimlessBinary.transform(
81eedcae 162 data.df.stats.total_bytes
f91f0fd5 163 )}`;
11fdf7f2
TL
164 }
165
9f95a23c
TL
166 preparePgStatus(chart: Record<string, any>, data: Record<string, any>) {
167 const categoryPgAmount: Record<string, number> = {};
eafe8130 168 let totalPgs = 0;
11fdf7f2
TL
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;
eafe8130 177 totalPgs += pgAmount;
11fdf7f2
TL
178 });
179
f91f0fd5
TL
180 for (const categoryType of this.pgCategoryService.getAllTypes()) {
181 if (_.isUndefined(categoryPgAmount[categoryType])) {
182 categoryPgAmount[categoryType] = 0;
183 }
184 }
185
11fdf7f2
TL
186 chart.dataset[0].data = this.pgCategoryService
187 .getAllTypes()
f91f0fd5 188 .map((categoryType) => this.calcPercentage(categoryPgAmount[categoryType], totalPgs));
81eedcae
TL
189
190 chart.labels = [
f67539c2
TL
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'])}`
81eedcae 195 ];
f91f0fd5 196
f67539c2 197 chart.dataset[0].label = `${totalPgs}\n${$localize`PGs`}`;
81eedcae
TL
198 }
199
9f95a23c 200 prepareObjects(chart: Record<string, any>, data: Record<string, any>) {
f91f0fd5 201 const objectCopies = data.pg_info.object_stats.num_object_copies;
81eedcae 202 const healthy =
f91f0fd5 203 objectCopies -
81eedcae
TL
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;
f91f0fd5
TL
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 );
81eedcae
TL
220
221 chart.labels = [
f67539c2
TL
222 `${$localize`Healthy`}: ${healthyPercentage}%`,
223 `${$localize`Misplaced`}: ${misplacedPercentage}%`,
224 `${$localize`Degraded`}: ${degradedPercentage}%`,
225 `${$localize`Unfound`}: ${unfoundPercentage}%`
81eedcae
TL
226 ];
227
228 chart.dataset[0].data = [
f91f0fd5
TL
229 healthyPercentage,
230 misplacedPercentage,
231 degradedPercentage,
232 unfoundPercentage
81eedcae
TL
233 ];
234
f91f0fd5 235 chart.dataset[0].label = `${this.dimless.transform(
81eedcae 236 data.pg_info.object_stats.num_objects
f67539c2 237 )}\n${$localize`objects`}`;
11fdf7f2
TL
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 }
81eedcae
TL
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 }
11fdf7f2 254}