]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.ts
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / osd / osd-list / osd-list.component.ts
CommitLineData
11fdf7f2 1import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
aee94f69 2import { UntypedFormControl } from '@angular/forms';
9f95a23c 3import { Router } from '@angular/router';
11fdf7f2 4
f67539c2
TL
5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
6import _ from 'lodash';
9f95a23c 7import { forkJoin as observableForkJoin, Observable } from 'rxjs';
20effc67 8import { take } from 'rxjs/operators';
11fdf7f2 9
f67539c2
TL
10import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
11import { OsdService } from '~/app/shared/api/osd.service';
12import { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
13import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component';
14import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
15import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component';
16import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
17import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
18import { Icons } from '~/app/shared/enum/icons.enum';
19import { NotificationType } from '~/app/shared/enum/notification-type.enum';
20import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
21import { CdTableAction } from '~/app/shared/models/cd-table-action';
22import { CdTableColumn } from '~/app/shared/models/cd-table-column';
23import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
24import { FinishedTask } from '~/app/shared/models/finished-task';
25import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum';
26import { OrchestratorStatus } from '~/app/shared/models/orchestrator.interface';
20effc67 27import { OsdSettings } from '~/app/shared/models/osd-settings';
f67539c2
TL
28import { Permissions } from '~/app/shared/models/permissions';
29import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
30import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
31import { ModalService } from '~/app/shared/services/modal.service';
32import { NotificationService } from '~/app/shared/services/notification.service';
33import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
34import { URLBuilderService } from '~/app/shared/services/url-builder.service';
adb31ebb 35import { OsdFlagsIndivModalComponent } from '../osd-flags-indiv-modal/osd-flags-indiv-modal.component';
11fdf7f2 36import { OsdFlagsModalComponent } from '../osd-flags-modal/osd-flags-modal.component';
81eedcae 37import { OsdPgScrubModalComponent } from '../osd-pg-scrub-modal/osd-pg-scrub-modal.component';
11fdf7f2
TL
38import { OsdRecvSpeedModalComponent } from '../osd-recv-speed-modal/osd-recv-speed-modal.component';
39import { OsdReweightModalComponent } from '../osd-reweight-modal/osd-reweight-modal.component';
40import { OsdScrubModalComponent } from '../osd-scrub-modal/osd-scrub-modal.component';
41
9f95a23c
TL
42const BASE_URL = 'osd';
43
11fdf7f2
TL
44@Component({
45 selector: 'cd-osd-list',
46 templateUrl: './osd-list.component.html',
9f95a23c
TL
47 styleUrls: ['./osd-list.component.scss'],
48 providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
11fdf7f2 49})
e306af50 50export class OsdListComponent extends ListWithDetails implements OnInit {
9f95a23c 51 @ViewChild('osdUsageTpl', { static: true })
11fdf7f2 52 osdUsageTpl: TemplateRef<any>;
9f95a23c 53 @ViewChild('markOsdConfirmationTpl', { static: true })
11fdf7f2 54 markOsdConfirmationTpl: TemplateRef<any>;
9f95a23c 55 @ViewChild('criticalConfirmationTpl', { static: true })
11fdf7f2 56 criticalConfirmationTpl: TemplateRef<any>;
f67539c2 57 @ViewChild('reweightBodyTpl')
11fdf7f2 58 reweightBodyTpl: TemplateRef<any>;
f67539c2 59 @ViewChild('safeToDestroyBodyTpl')
11fdf7f2 60 safeToDestroyBodyTpl: TemplateRef<any>;
f67539c2 61 @ViewChild('deleteOsdExtraTpl')
f6b5b4d7 62 deleteOsdExtraTpl: TemplateRef<any>;
adb31ebb
TL
63 @ViewChild('flagsTpl', { static: true })
64 flagsTpl: TemplateRef<any>;
11fdf7f2
TL
65
66 permissions: Permissions;
67 tableActions: CdTableAction[];
f67539c2 68 bsModalRef: NgbModalRef;
11fdf7f2 69 columns: CdTableColumn[];
9f95a23c
TL
70 clusterWideActions: CdTableAction[];
71 icons = Icons;
20effc67 72 osdSettings = new OsdSettings();
11fdf7f2 73
11fdf7f2 74 selection = new CdTableSelection();
9f95a23c 75 osds: any[] = [];
adb31ebb
TL
76 disabledFlags: string[] = [
77 'sortbitwise',
78 'purged_snapdirs',
79 'recovery_deletes',
80 'pglog_hardlimit'
81 ];
82 indivFlagNames: string[] = ['noup', 'nodown', 'noin', 'noout'];
11fdf7f2 83
f67539c2
TL
84 orchStatus: OrchestratorStatus;
85 actionOrchFeatures = {
86 create: [OrchestratorFeature.OSD_CREATE],
87 delete: [OrchestratorFeature.OSD_DELETE]
88 };
89
9f95a23c
TL
90 protected static collectStates(osd: any) {
91 const states = [osd['in'] ? 'in' : 'out'];
92 if (osd['up']) {
93 states.push('up');
94 } else if (osd.state.includes('destroyed')) {
95 states.push('destroyed');
96 } else {
97 states.push('down');
98 }
99 return states;
11fdf7f2
TL
100 }
101
102 constructor(
103 private authStorageService: AuthStorageService,
104 private osdService: OsdService,
105 private dimlessBinaryPipe: DimlessBinaryPipe,
f67539c2 106 private modalService: ModalService,
9f95a23c
TL
107 private urlBuilder: URLBuilderService,
108 private router: Router,
9f95a23c
TL
109 private taskWrapper: TaskWrapperService,
110 public actionLabels: ActionLabelsI18n,
f67539c2
TL
111 public notificationService: NotificationService,
112 private orchService: OrchestratorService
11fdf7f2 113 ) {
e306af50 114 super();
11fdf7f2
TL
115 this.permissions = this.authStorageService.getPermissions();
116 this.tableActions = [
9f95a23c
TL
117 {
118 name: this.actionLabels.CREATE,
119 permission: 'create',
120 icon: Icons.add,
f67539c2
TL
121 click: () => this.router.navigate([this.urlBuilder.getCreate()]),
122 disable: (selection: CdTableSelection) => this.getDisable('create', selection),
9f95a23c
TL
123 canBePrimary: (selection: CdTableSelection) => !selection.hasSelection
124 },
125 {
126 name: this.actionLabels.EDIT,
127 permission: 'update',
128 icon: Icons.edit,
129 click: () => this.editAction()
130 },
adb31ebb
TL
131 {
132 name: this.actionLabels.FLAGS,
133 permission: 'update',
134 icon: Icons.flag,
135 click: () => this.configureFlagsIndivAction(),
136 disable: () => !this.hasOsdSelected
137 },
11fdf7f2 138 {
eafe8130 139 name: this.actionLabels.SCRUB,
11fdf7f2 140 permission: 'update',
9f95a23c 141 icon: Icons.analyse,
11fdf7f2 142 click: () => this.scrubAction(false),
9f95a23c
TL
143 disable: () => !this.hasOsdSelected,
144 canBePrimary: (selection: CdTableSelection) => selection.hasSelection
11fdf7f2
TL
145 },
146 {
eafe8130 147 name: this.actionLabels.DEEP_SCRUB,
11fdf7f2 148 permission: 'update',
9f95a23c 149 icon: Icons.deepCheck,
11fdf7f2
TL
150 click: () => this.scrubAction(true),
151 disable: () => !this.hasOsdSelected
152 },
153 {
eafe8130 154 name: this.actionLabels.REWEIGHT,
11fdf7f2
TL
155 permission: 'update',
156 click: () => this.reweight(),
9f95a23c
TL
157 disable: () => !this.hasOsdSelected || !this.selection.hasSingleSelection,
158 icon: Icons.reweight
11fdf7f2
TL
159 },
160 {
eafe8130 161 name: this.actionLabels.MARK_OUT,
11fdf7f2 162 permission: 'update',
f67539c2 163 click: () => this.showConfirmationModal($localize`out`, this.osdService.markOut),
11fdf7f2 164 disable: () => this.isNotSelectedOrInState('out'),
9f95a23c 165 icon: Icons.left
11fdf7f2
TL
166 },
167 {
eafe8130 168 name: this.actionLabels.MARK_IN,
11fdf7f2 169 permission: 'update',
f67539c2 170 click: () => this.showConfirmationModal($localize`in`, this.osdService.markIn),
11fdf7f2 171 disable: () => this.isNotSelectedOrInState('in'),
9f95a23c 172 icon: Icons.right
11fdf7f2
TL
173 },
174 {
eafe8130 175 name: this.actionLabels.MARK_DOWN,
11fdf7f2 176 permission: 'update',
f67539c2 177 click: () => this.showConfirmationModal($localize`down`, this.osdService.markDown),
11fdf7f2 178 disable: () => this.isNotSelectedOrInState('down'),
9f95a23c 179 icon: Icons.down
11fdf7f2
TL
180 },
181 {
eafe8130 182 name: this.actionLabels.MARK_LOST,
11fdf7f2
TL
183 permission: 'delete',
184 click: () =>
185 this.showCriticalConfirmationModal(
f67539c2
TL
186 $localize`Mark`,
187 $localize`OSD lost`,
188 $localize`marked lost`,
9f95a23c
TL
189 (ids: number[]) => {
190 return this.osdService.safeToDestroy(JSON.stringify(ids));
191 },
192 'is_safe_to_destroy',
11fdf7f2
TL
193 this.osdService.markLost
194 ),
195 disable: () => this.isNotSelectedOrInState('up'),
9f95a23c 196 icon: Icons.flatten
11fdf7f2
TL
197 },
198 {
eafe8130 199 name: this.actionLabels.PURGE,
11fdf7f2
TL
200 permission: 'delete',
201 click: () =>
202 this.showCriticalConfirmationModal(
f67539c2
TL
203 $localize`Purge`,
204 $localize`OSD`,
205 $localize`purged`,
9f95a23c
TL
206 (ids: number[]) => {
207 return this.osdService.safeToDestroy(JSON.stringify(ids));
208 },
209 'is_safe_to_destroy',
210 (id: number) => {
211 this.selection = new CdTableSelection();
212 return this.osdService.purge(id);
213 }
11fdf7f2
TL
214 ),
215 disable: () => this.isNotSelectedOrInState('up'),
9f95a23c 216 icon: Icons.erase
11fdf7f2
TL
217 },
218 {
eafe8130 219 name: this.actionLabels.DESTROY,
11fdf7f2
TL
220 permission: 'delete',
221 click: () =>
222 this.showCriticalConfirmationModal(
f67539c2
TL
223 $localize`destroy`,
224 $localize`OSD`,
225 $localize`destroyed`,
9f95a23c
TL
226 (ids: number[]) => {
227 return this.osdService.safeToDestroy(JSON.stringify(ids));
228 },
229 'is_safe_to_destroy',
230 (id: number) => {
231 this.selection = new CdTableSelection();
232 return this.osdService.destroy(id);
233 }
11fdf7f2
TL
234 ),
235 disable: () => this.isNotSelectedOrInState('up'),
9f95a23c
TL
236 icon: Icons.destroyCircle
237 },
238 {
239 name: this.actionLabels.DELETE,
240 permission: 'delete',
f6b5b4d7 241 click: () => this.delete(),
f67539c2 242 disable: (selection: CdTableSelection) => this.getDisable('delete', selection),
9f95a23c 243 icon: Icons.destroy
11fdf7f2
TL
244 }
245 ];
9f95a23c
TL
246 }
247
248 ngOnInit() {
249 this.clusterWideActions = [
81eedcae 250 {
f67539c2 251 name: $localize`Flags`,
9f95a23c 252 icon: Icons.flag,
81eedcae 253 click: () => this.configureFlagsAction(),
9f95a23c
TL
254 permission: 'read',
255 visible: () => this.permissions.osd.read
81eedcae
TL
256 },
257 {
f67539c2 258 name: $localize`Recovery Priority`,
9f95a23c 259 icon: Icons.deepCheck,
81eedcae 260 click: () => this.configureQosParamsAction(),
9f95a23c
TL
261 permission: 'read',
262 visible: () => this.permissions.configOpt.read
81eedcae
TL
263 },
264 {
f67539c2 265 name: $localize`PG scrub`,
9f95a23c 266 icon: Icons.analyse,
81eedcae 267 click: () => this.configurePgScrubAction(),
9f95a23c
TL
268 permission: 'read',
269 visible: () => this.permissions.configOpt.read
81eedcae
TL
270 }
271 ];
11fdf7f2 272 this.columns = [
f67539c2
TL
273 {
274 prop: 'id',
275 name: $localize`ID`,
276 flexGrow: 1,
277 cellTransformation: CellTemplate.executing,
278 customTemplateConfig: {
279 valueClass: 'bold'
280 }
281 },
282 { prop: 'host.name', name: $localize`Host` },
9f95a23c
TL
283 {
284 prop: 'collectedStates',
f67539c2 285 name: $localize`Status`,
9f95a23c
TL
286 flexGrow: 1,
287 cellTransformation: CellTemplate.badge,
288 customTemplateConfig: {
289 map: {
290 in: { class: 'badge-success' },
291 up: { class: 'badge-success' },
292 down: { class: 'badge-danger' },
293 out: { class: 'badge-danger' },
294 destroyed: { class: 'badge-danger' }
295 }
296 }
297 },
298 {
299 prop: 'tree.device_class',
f67539c2
TL
300 name: $localize`Device class`,
301 flexGrow: 1.2,
9f95a23c
TL
302 cellTransformation: CellTemplate.badge,
303 customTemplateConfig: {
304 map: {
305 hdd: { class: 'badge-hdd' },
306 ssd: { class: 'badge-ssd' }
307 }
308 }
309 },
310 {
311 prop: 'stats.numpg',
f67539c2 312 name: $localize`PGs`,
9f95a23c
TL
313 flexGrow: 1
314 },
315 {
316 prop: 'stats.stat_bytes',
f67539c2 317 name: $localize`Size`,
9f95a23c
TL
318 flexGrow: 1,
319 pipe: this.dimlessBinaryPipe
320 },
adb31ebb
TL
321 {
322 prop: 'state',
f67539c2 323 name: $localize`Flags`,
adb31ebb
TL
324 cellTemplate: this.flagsTpl
325 },
f67539c2 326 { prop: 'stats.usage', name: $localize`Usage`, cellTemplate: this.osdUsageTpl },
11fdf7f2
TL
327 {
328 prop: 'stats_history.out_bytes',
f67539c2 329 name: $localize`Read bytes`,
11fdf7f2
TL
330 cellTransformation: CellTemplate.sparkline
331 },
332 {
333 prop: 'stats_history.in_bytes',
f67539c2 334 name: $localize`Write bytes`,
11fdf7f2
TL
335 cellTransformation: CellTemplate.sparkline
336 },
337 {
338 prop: 'stats.op_r',
f67539c2 339 name: $localize`Read ops`,
11fdf7f2
TL
340 cellTransformation: CellTemplate.perSecond
341 },
342 {
343 prop: 'stats.op_w',
f67539c2 344 name: $localize`Write ops`,
11fdf7f2
TL
345 cellTransformation: CellTemplate.perSecond
346 }
347 ];
f67539c2
TL
348
349 this.orchService.status().subscribe((status: OrchestratorStatus) => (this.orchStatus = status));
20effc67
TL
350
351 this.osdService
352 .getOsdSettings()
353 .pipe(take(1))
354 .subscribe((data: any) => {
355 this.osdSettings = data;
356 });
f67539c2
TL
357 }
358
359 getDisable(action: 'create' | 'delete', selection: CdTableSelection): boolean | string {
360 if (action === 'delete') {
361 if (!selection.hasSelection) {
362 return true;
363 } else {
364 // Disable delete action if any selected OSDs are under deleting or unmanaged.
365 const deletingOSDs = _.some(this.getSelectedOsds(), (osd) => {
366 const status = _.get(osd, 'operational_status');
367 return status === 'deleting' || status === 'unmanaged';
368 });
369 if (deletingOSDs) {
370 return true;
371 }
372 }
373 }
374 return this.orchService.getTableActionDisableDesc(
375 this.orchStatus,
376 this.actionOrchFeatures[action]
377 );
9f95a23c 378 }
81eedcae 379
9f95a23c
TL
380 /**
381 * Only returns valid IDs, e.g. if an OSD is falsely still selected after being deleted, it won't
382 * get returned.
383 */
384 getSelectedOsdIds(): number[] {
385 const osdIds = this.osds.map((osd) => osd.id);
f67539c2
TL
386 return this.selection.selected
387 .map((row) => row.id)
388 .filter((id) => osdIds.includes(id))
389 .sort();
11fdf7f2
TL
390 }
391
9f95a23c
TL
392 getSelectedOsds(): any[] {
393 return this.osds.filter(
394 (osd) => !_.isUndefined(osd) && this.getSelectedOsdIds().includes(osd.id)
395 );
396 }
397
398 get hasOsdSelected(): boolean {
399 return this.getSelectedOsdIds().length > 0;
11fdf7f2
TL
400 }
401
402 updateSelection(selection: CdTableSelection) {
403 this.selection = selection;
404 }
405
406 /**
9f95a23c 407 * Returns true if no rows are selected or if *any* of the selected rows are in the given
11fdf7f2
TL
408 * state. Useful for deactivating the corresponding menu entry.
409 */
410 isNotSelectedOrInState(state: 'in' | 'up' | 'down' | 'out'): boolean {
9f95a23c
TL
411 const selectedOsds = this.getSelectedOsds();
412 if (selectedOsds.length === 0) {
11fdf7f2
TL
413 return true;
414 }
11fdf7f2
TL
415 switch (state) {
416 case 'in':
9f95a23c 417 return selectedOsds.some((osd) => osd.in === 1);
11fdf7f2 418 case 'out':
9f95a23c 419 return selectedOsds.some((osd) => osd.in !== 1);
11fdf7f2 420 case 'down':
9f95a23c 421 return selectedOsds.some((osd) => osd.up !== 1);
11fdf7f2 422 case 'up':
9f95a23c 423 return selectedOsds.some((osd) => osd.up === 1);
11fdf7f2
TL
424 }
425 }
426
427 getOsdList() {
adb31ebb
TL
428 const observables = [this.osdService.getList(), this.osdService.getFlags()];
429 observableForkJoin(observables).subscribe((resp: [any[], string[]]) => {
430 this.osds = resp[0].map((osd) => {
11fdf7f2 431 osd.collectedStates = OsdListComponent.collectStates(osd);
9f95a23c
TL
432 osd.stats_history.out_bytes = osd.stats_history.op_out_bytes.map((i: string) => i[1]);
433 osd.stats_history.in_bytes = osd.stats_history.op_in_bytes.map((i: string) => i[1]);
81eedcae 434 osd.stats.usage = osd.stats.stat_bytes_used / osd.stats.stat_bytes;
11fdf7f2 435 osd.cdIsBinary = true;
adb31ebb
TL
436 osd.cdIndivFlags = osd.state.filter((f: string) => this.indivFlagNames.includes(f));
437 osd.cdClusterFlags = resp[1].filter((f: string) => !this.disabledFlags.includes(f));
f67539c2
TL
438 const deploy_state = _.get(osd, 'operational_status', 'unmanaged');
439 if (deploy_state !== 'unmanaged' && deploy_state !== 'working') {
440 osd.cdExecuting = deploy_state;
441 }
11fdf7f2
TL
442 return osd;
443 });
444 });
445 }
446
9f95a23c
TL
447 editAction() {
448 const selectedOsd = _.filter(this.osds, ['id', this.selection.first().id]).pop();
449
450 this.modalService.show(FormModalComponent, {
f67539c2
TL
451 titleText: $localize`Edit OSD: ${selectedOsd.id}`,
452 fields: [
453 {
454 type: 'text',
455 name: 'deviceClass',
456 value: selectedOsd.tree.device_class,
457 label: $localize`Device class`,
458 required: true
9f95a23c 459 }
f67539c2
TL
460 ],
461 submitButtonText: $localize`Edit OSD`,
462 onSubmit: (values: any) => {
463 this.osdService.update(selectedOsd.id, values.deviceClass).subscribe(() => {
464 this.notificationService.show(
465 NotificationType.success,
466 $localize`Updated OSD '${selectedOsd.id}'`
467 );
468 this.getOsdList();
469 });
9f95a23c
TL
470 }
471 });
472 }
473
474 scrubAction(deep: boolean) {
11fdf7f2
TL
475 if (!this.hasOsdSelected) {
476 return;
477 }
478
479 const initialState = {
9f95a23c 480 selected: this.getSelectedOsdIds(),
11fdf7f2
TL
481 deep: deep
482 };
483
f67539c2 484 this.bsModalRef = this.modalService.show(OsdScrubModalComponent, initialState);
11fdf7f2
TL
485 }
486
81eedcae 487 configureFlagsAction() {
f67539c2 488 this.bsModalRef = this.modalService.show(OsdFlagsModalComponent);
11fdf7f2
TL
489 }
490
adb31ebb
TL
491 configureFlagsIndivAction() {
492 const initialState = {
493 selected: this.getSelectedOsds()
494 };
f67539c2 495 this.bsModalRef = this.modalService.show(OsdFlagsIndivModalComponent, initialState);
adb31ebb
TL
496 }
497
11fdf7f2 498 showConfirmationModal(markAction: string, onSubmit: (id: number) => Observable<any>) {
f67539c2 499 const osdIds = this.getSelectedOsdIds();
11fdf7f2 500 this.bsModalRef = this.modalService.show(ConfirmationModalComponent, {
f67539c2
TL
501 titleText: $localize`Mark OSD ${markAction}`,
502 buttonText: $localize`Mark ${markAction}`,
503 bodyTpl: this.markOsdConfirmationTpl,
504 bodyContext: {
505 markActionDescription: markAction,
506 osdIds
507 },
508 onSubmit: () => {
509 observableForkJoin(
510 this.getSelectedOsdIds().map((osd: any) => onSubmit.call(this.osdService, osd))
511 ).subscribe(() => this.bsModalRef.close());
11fdf7f2
TL
512 }
513 });
514 }
515
516 reweight() {
517 const selectedOsd = this.osds.filter((o) => o.id === this.selection.first().id).pop();
f67539c2
TL
518 this.bsModalRef = this.modalService.show(OsdReweightModalComponent, {
519 currentWeight: selectedOsd.weight,
520 osdId: selectedOsd.id
11fdf7f2
TL
521 });
522 }
523
f6b5b4d7
TL
524 delete() {
525 const deleteFormGroup = new CdFormGroup({
aee94f69 526 preserve: new UntypedFormControl(false)
f6b5b4d7
TL
527 });
528
f67539c2
TL
529 this.showCriticalConfirmationModal(
530 $localize`delete`,
531 $localize`OSD`,
532 $localize`deleted`,
533 (ids: number[]) => {
534 return this.osdService.safeToDelete(JSON.stringify(ids));
535 },
536 'is_safe_to_delete',
537 (id: number) => {
538 this.selection = new CdTableSelection();
539 return this.taskWrapper.wrapTaskAroundCall({
540 task: new FinishedTask('osd/' + URLVerbs.DELETE, {
541 svc_id: id
542 }),
543 call: this.osdService.delete(id, deleteFormGroup.value.preserve, true)
544 });
545 },
546 true,
547 deleteFormGroup,
548 this.deleteOsdExtraTpl
f6b5b4d7
TL
549 );
550 }
551
9f95a23c
TL
552 /**
553 * Perform check first and display a critical confirmation modal.
554 * @param {string} actionDescription name of the action.
555 * @param {string} itemDescription the item's name that the action operates on.
556 * @param {string} templateItemDescription the action name to be displayed in modal template.
557 * @param {Function} check the function is called to check if the action is safe.
558 * @param {string} checkKey the safe indicator's key in the check response.
559 * @param {Function} action the action function.
f6b5b4d7
TL
560 * @param {boolean} taskWrapped if true, hide confirmation modal after action
561 * @param {CdFormGroup} childFormGroup additional child form group to be passed to confirmation modal
562 * @param {TemplateRef<any>} childFormGroupTemplate template for additional child form group
9f95a23c 563 */
11fdf7f2
TL
564 showCriticalConfirmationModal(
565 actionDescription: string,
566 itemDescription: string,
567 templateItemDescription: string,
9f95a23c
TL
568 check: (ids: number[]) => Observable<any>,
569 checkKey: string,
570 action: (id: number | number[]) => Observable<any>,
f6b5b4d7
TL
571 taskWrapped: boolean = false,
572 childFormGroup?: CdFormGroup,
573 childFormGroupTemplate?: TemplateRef<any>
11fdf7f2 574 ): void {
9f95a23c 575 check(this.getSelectedOsdIds()).subscribe((result) => {
11fdf7f2 576 const modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
f67539c2
TL
577 actionDescription: actionDescription,
578 itemDescription: itemDescription,
579 bodyTemplate: this.criticalConfirmationTpl,
580 bodyContext: {
581 safeToPerform: result[checkKey],
582 message: result.message,
39ae355f
TL
583 active: result.active,
584 missingStats: result.missing_stats,
585 storedPgs: result.stored_pgs,
f67539c2
TL
586 actionDescription: templateItemDescription,
587 osdIds: this.getSelectedOsdIds()
588 },
589 childFormGroup: childFormGroup,
590 childFormGroupTemplate: childFormGroupTemplate,
591 submitAction: () => {
592 const observable = observableForkJoin(
593 this.getSelectedOsdIds().map((osd: any) => action.call(this.osdService, osd))
594 );
595 if (taskWrapped) {
596 observable.subscribe({
597 error: () => {
598 this.getOsdList();
599 modalRef.close();
600 },
601 complete: () => modalRef.close()
602 });
603 } else {
604 observable.subscribe(
605 () => {
606 this.getOsdList();
607 modalRef.close();
608 },
609 () => modalRef.close()
9f95a23c 610 );
11fdf7f2
TL
611 }
612 }
613 });
614 });
615 }
616
617 configureQosParamsAction() {
f67539c2 618 this.bsModalRef = this.modalService.show(OsdRecvSpeedModalComponent);
11fdf7f2 619 }
81eedcae
TL
620
621 configurePgScrubAction() {
f67539c2 622 this.bsModalRef = this.modalService.show(OsdPgScrubModalComponent, undefined, { size: 'lg' });
81eedcae 623 }
11fdf7f2 624}