]>
Commit | Line | Data |
---|---|---|
11fdf7f2 | 1 | import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; |
aee94f69 | 2 | import { UntypedFormControl } from '@angular/forms'; |
9f95a23c | 3 | import { Router } from '@angular/router'; |
11fdf7f2 | 4 | |
f67539c2 TL |
5 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; |
6 | import _ from 'lodash'; | |
9f95a23c | 7 | import { forkJoin as observableForkJoin, Observable } from 'rxjs'; |
20effc67 | 8 | import { take } from 'rxjs/operators'; |
11fdf7f2 | 9 | |
f67539c2 TL |
10 | import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; |
11 | import { OsdService } from '~/app/shared/api/osd.service'; | |
12 | import { ListWithDetails } from '~/app/shared/classes/list-with-details.class'; | |
13 | import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component'; | |
14 | import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; | |
15 | import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component'; | |
16 | import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants'; | |
17 | import { CellTemplate } from '~/app/shared/enum/cell-template.enum'; | |
18 | import { Icons } from '~/app/shared/enum/icons.enum'; | |
19 | import { NotificationType } from '~/app/shared/enum/notification-type.enum'; | |
20 | import { CdFormGroup } from '~/app/shared/forms/cd-form-group'; | |
21 | import { CdTableAction } from '~/app/shared/models/cd-table-action'; | |
22 | import { CdTableColumn } from '~/app/shared/models/cd-table-column'; | |
23 | import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; | |
24 | import { FinishedTask } from '~/app/shared/models/finished-task'; | |
25 | import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum'; | |
26 | import { OrchestratorStatus } from '~/app/shared/models/orchestrator.interface'; | |
20effc67 | 27 | import { OsdSettings } from '~/app/shared/models/osd-settings'; |
f67539c2 TL |
28 | import { Permissions } from '~/app/shared/models/permissions'; |
29 | import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe'; | |
30 | import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; | |
31 | import { ModalService } from '~/app/shared/services/modal.service'; | |
32 | import { NotificationService } from '~/app/shared/services/notification.service'; | |
33 | import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; | |
34 | import { URLBuilderService } from '~/app/shared/services/url-builder.service'; | |
adb31ebb | 35 | import { OsdFlagsIndivModalComponent } from '../osd-flags-indiv-modal/osd-flags-indiv-modal.component'; |
11fdf7f2 | 36 | import { OsdFlagsModalComponent } from '../osd-flags-modal/osd-flags-modal.component'; |
81eedcae | 37 | import { OsdPgScrubModalComponent } from '../osd-pg-scrub-modal/osd-pg-scrub-modal.component'; |
11fdf7f2 TL |
38 | import { OsdRecvSpeedModalComponent } from '../osd-recv-speed-modal/osd-recv-speed-modal.component'; |
39 | import { OsdReweightModalComponent } from '../osd-reweight-modal/osd-reweight-modal.component'; | |
40 | import { OsdScrubModalComponent } from '../osd-scrub-modal/osd-scrub-modal.component'; | |
41 | ||
9f95a23c TL |
42 | const 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 | 50 | export 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 | } |