]>
Commit | Line | Data |
---|---|---|
a4b75251 | 1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; |
20effc67 | 2 | import { FormControl } from '@angular/forms'; |
9f95a23c TL |
3 | import { Router } from '@angular/router'; |
4 | ||
f67539c2 TL |
5 | import _ from 'lodash'; |
6 | ||
7 | import { InventoryDevice } from '~/app/ceph/cluster/inventory/inventory-devices/inventory-device.model'; | |
8 | import { HostService } from '~/app/shared/api/host.service'; | |
9 | import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; | |
2a845540 | 10 | import { OsdService } from '~/app/shared/api/osd.service'; |
f67539c2 | 11 | import { FormButtonPanelComponent } from '~/app/shared/components/form-button-panel/form-button-panel.component'; |
2a845540 | 12 | import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants'; |
f67539c2 TL |
13 | import { Icons } from '~/app/shared/enum/icons.enum'; |
14 | import { CdForm } from '~/app/shared/forms/cd-form'; | |
15 | import { CdFormGroup } from '~/app/shared/forms/cd-form-group'; | |
16 | import { CdTableColumn } from '~/app/shared/models/cd-table-column'; | |
2a845540 TL |
17 | import { FinishedTask } from '~/app/shared/models/finished-task'; |
18 | import { | |
19 | DeploymentOptions, | |
20 | OsdDeploymentOptions | |
21 | } from '~/app/shared/models/osd-deployment-options'; | |
f67539c2 TL |
22 | import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; |
23 | import { ModalService } from '~/app/shared/services/modal.service'; | |
2a845540 | 24 | import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; |
9f95a23c TL |
25 | import { OsdCreationPreviewModalComponent } from '../osd-creation-preview-modal/osd-creation-preview-modal.component'; |
26 | import { DevicesSelectionChangeEvent } from '../osd-devices-selection-groups/devices-selection-change-event.interface'; | |
27 | import { DevicesSelectionClearEvent } from '../osd-devices-selection-groups/devices-selection-clear-event.interface'; | |
28 | import { OsdDevicesSelectionGroupsComponent } from '../osd-devices-selection-groups/osd-devices-selection-groups.component'; | |
29 | import { DriveGroup } from './drive-group.model'; | |
30 | import { OsdFeature } from './osd-feature.interface'; | |
31 | ||
32 | @Component({ | |
33 | selector: 'cd-osd-form', | |
34 | templateUrl: './osd-form.component.html', | |
35 | styleUrls: ['./osd-form.component.scss'] | |
36 | }) | |
f67539c2 TL |
37 | export class OsdFormComponent extends CdForm implements OnInit { |
38 | @ViewChild('dataDeviceSelectionGroups') | |
9f95a23c TL |
39 | dataDeviceSelectionGroups: OsdDevicesSelectionGroupsComponent; |
40 | ||
f67539c2 | 41 | @ViewChild('walDeviceSelectionGroups') |
9f95a23c TL |
42 | walDeviceSelectionGroups: OsdDevicesSelectionGroupsComponent; |
43 | ||
f67539c2 | 44 | @ViewChild('dbDeviceSelectionGroups') |
9f95a23c TL |
45 | dbDeviceSelectionGroups: OsdDevicesSelectionGroupsComponent; |
46 | ||
f67539c2 TL |
47 | @ViewChild('previewButtonPanel') |
48 | previewButtonPanel: FormButtonPanelComponent; | |
9f95a23c | 49 | |
a4b75251 TL |
50 | @Input() |
51 | hideTitle = false; | |
52 | ||
53 | @Input() | |
54 | hideSubmitBtn = false; | |
55 | ||
56 | @Output() emitDriveGroup: EventEmitter<DriveGroup> = new EventEmitter(); | |
57 | ||
2a845540 TL |
58 | @Output() emitDeploymentOption: EventEmitter<object> = new EventEmitter(); |
59 | ||
60 | @Output() emitMode: EventEmitter<boolean> = new EventEmitter(); | |
61 | ||
9f95a23c TL |
62 | icons = Icons; |
63 | ||
64 | form: CdFormGroup; | |
65 | columns: Array<CdTableColumn> = []; | |
66 | ||
9f95a23c TL |
67 | allDevices: InventoryDevice[] = []; |
68 | ||
69 | availDevices: InventoryDevice[] = []; | |
70 | dataDeviceFilters: any[] = []; | |
71 | dbDeviceFilters: any[] = []; | |
72 | walDeviceFilters: any[] = []; | |
73 | hostname = ''; | |
74 | driveGroup = new DriveGroup(); | |
75 | ||
76 | action: string; | |
77 | resource: string; | |
78 | ||
79 | features: { [key: string]: OsdFeature }; | |
80 | featureList: OsdFeature[] = []; | |
81 | ||
f67539c2 | 82 | hasOrchestrator = true; |
9f95a23c | 83 | |
2a845540 TL |
84 | simpleDeployment = true; |
85 | ||
86 | deploymentOptions: DeploymentOptions; | |
87 | optionNames = Object.values(OsdDeploymentOptions); | |
88 | ||
9f95a23c TL |
89 | constructor( |
90 | public actionLabels: ActionLabelsI18n, | |
91 | private authStorageService: AuthStorageService, | |
9f95a23c | 92 | private orchService: OrchestratorService, |
f67539c2 | 93 | private hostService: HostService, |
9f95a23c | 94 | private router: Router, |
a4b75251 | 95 | private modalService: ModalService, |
2a845540 TL |
96 | private osdService: OsdService, |
97 | private taskWrapper: TaskWrapperService | |
9f95a23c | 98 | ) { |
f67539c2 TL |
99 | super(); |
100 | this.resource = $localize`OSDs`; | |
9f95a23c TL |
101 | this.action = this.actionLabels.CREATE; |
102 | this.features = { | |
103 | encrypted: { | |
104 | key: 'encrypted', | |
f67539c2 | 105 | desc: $localize`Encryption` |
9f95a23c TL |
106 | } |
107 | }; | |
108 | this.featureList = _.map(this.features, (o, key) => Object.assign(o, { key: key })); | |
109 | this.createForm(); | |
110 | } | |
111 | ||
112 | ngOnInit() { | |
113 | this.orchService.status().subscribe((status) => { | |
114 | this.hasOrchestrator = status.available; | |
f67539c2 | 115 | if (status.available) { |
9f95a23c | 116 | this.getDataDevices(); |
f67539c2 TL |
117 | } else { |
118 | this.loadingNone(); | |
9f95a23c TL |
119 | } |
120 | }); | |
121 | ||
2a845540 TL |
122 | this.osdService.getDeploymentOptions().subscribe((options) => { |
123 | this.deploymentOptions = options; | |
124 | this.form.get('deploymentOption').setValue(this.deploymentOptions?.recommended_option); | |
125 | ||
126 | if (this.deploymentOptions?.recommended_option) { | |
127 | this.enableFeatures(); | |
128 | } | |
129 | }); | |
9f95a23c TL |
130 | this.form.get('walSlots').valueChanges.subscribe((value) => this.setSlots('wal', value)); |
131 | this.form.get('dbSlots').valueChanges.subscribe((value) => this.setSlots('db', value)); | |
132 | _.each(this.features, (feature) => { | |
133 | this.form | |
134 | .get('features') | |
135 | .get(feature.key) | |
136 | .valueChanges.subscribe((value) => this.featureFormUpdate(feature.key, value)); | |
137 | }); | |
138 | } | |
139 | ||
140 | createForm() { | |
141 | this.form = new CdFormGroup({ | |
20effc67 TL |
142 | walSlots: new FormControl(0), |
143 | dbSlots: new FormControl(0), | |
9f95a23c TL |
144 | features: new CdFormGroup( |
145 | this.featureList.reduce((acc: object, e) => { | |
146 | // disable initially because no data devices are selected | |
147 | acc[e.key] = new FormControl({ value: false, disabled: true }); | |
148 | return acc; | |
149 | }, {}) | |
2a845540 TL |
150 | ), |
151 | deploymentOption: new FormControl(0) | |
9f95a23c TL |
152 | }); |
153 | } | |
154 | ||
155 | getDataDevices() { | |
f67539c2 | 156 | this.hostService.inventoryDeviceList().subscribe( |
9f95a23c TL |
157 | (devices: InventoryDevice[]) => { |
158 | this.allDevices = _.filter(devices, 'available'); | |
159 | this.availDevices = [...this.allDevices]; | |
f67539c2 | 160 | this.loadingReady(); |
9f95a23c TL |
161 | }, |
162 | () => { | |
163 | this.allDevices = []; | |
164 | this.availDevices = []; | |
f67539c2 | 165 | this.loadingError(); |
9f95a23c TL |
166 | } |
167 | ); | |
168 | } | |
169 | ||
170 | setSlots(type: string, slots: number) { | |
171 | if (typeof slots !== 'number') { | |
172 | return; | |
173 | } | |
174 | if (slots >= 0) { | |
175 | this.driveGroup.setSlots(type, slots); | |
176 | } | |
177 | } | |
178 | ||
179 | featureFormUpdate(key: string, checked: boolean) { | |
180 | this.driveGroup.setFeature(key, checked); | |
181 | } | |
182 | ||
183 | enableFeatures() { | |
184 | this.featureList.forEach((feature) => { | |
185 | this.form.get(feature.key).enable({ emitEvent: false }); | |
186 | }); | |
187 | } | |
188 | ||
189 | disableFeatures() { | |
190 | this.featureList.forEach((feature) => { | |
191 | const control = this.form.get(feature.key); | |
192 | control.disable({ emitEvent: false }); | |
193 | control.setValue(false, { emitEvent: false }); | |
194 | }); | |
195 | } | |
196 | ||
197 | onDevicesSelected(event: DevicesSelectionChangeEvent) { | |
198 | this.availDevices = event.dataOut; | |
199 | ||
200 | if (event.type === 'data') { | |
201 | // If user selects data devices for a single host, make only remaining devices on | |
202 | // that host as available. | |
203 | const hostnameFilter = _.find(event.filters, { prop: 'hostname' }); | |
204 | if (hostnameFilter) { | |
205 | this.hostname = hostnameFilter.value.raw; | |
206 | this.availDevices = event.dataOut.filter((device: InventoryDevice) => { | |
207 | return device.hostname === this.hostname; | |
208 | }); | |
209 | this.driveGroup.setHostPattern(this.hostname); | |
210 | } else { | |
211 | this.driveGroup.setHostPattern('*'); | |
212 | } | |
213 | this.enableFeatures(); | |
214 | } | |
215 | this.driveGroup.setDeviceSelection(event.type, event.filters); | |
a4b75251 TL |
216 | |
217 | this.emitDriveGroup.emit(this.driveGroup); | |
9f95a23c TL |
218 | } |
219 | ||
220 | onDevicesCleared(event: DevicesSelectionClearEvent) { | |
221 | if (event.type === 'data') { | |
39ae355f | 222 | this.hostname = ''; |
9f95a23c TL |
223 | this.availDevices = [...this.allDevices]; |
224 | this.walDeviceSelectionGroups.devices = []; | |
225 | this.dbDeviceSelectionGroups.devices = []; | |
226 | this.disableFeatures(); | |
227 | this.driveGroup.reset(); | |
228 | this.form.get('walSlots').setValue(0, { emitEvent: false }); | |
229 | this.form.get('dbSlots').setValue(0, { emitEvent: false }); | |
230 | } else { | |
231 | this.availDevices = [...this.availDevices, ...event.clearedDevices]; | |
232 | this.driveGroup.clearDeviceSelection(event.type); | |
233 | const slotControlName = `${event.type}Slots`; | |
234 | this.form.get(slotControlName).setValue(0, { emitEvent: false }); | |
235 | } | |
236 | } | |
237 | ||
2a845540 TL |
238 | emitDeploymentSelection() { |
239 | const option = this.form.get('deploymentOption').value; | |
240 | const encrypted = this.form.get('encrypted').value; | |
241 | this.emitDeploymentOption.emit({ option: option, encrypted: encrypted }); | |
242 | } | |
243 | ||
244 | emitDeploymentMode() { | |
245 | this.simpleDeployment = !this.simpleDeployment; | |
246 | if (!this.simpleDeployment && this.dataDeviceSelectionGroups.devices.length === 0) { | |
247 | this.disableFeatures(); | |
248 | } else { | |
249 | this.enableFeatures(); | |
250 | } | |
251 | this.emitMode.emit(this.simpleDeployment); | |
252 | } | |
253 | ||
9f95a23c | 254 | submit() { |
2a845540 TL |
255 | if (this.simpleDeployment) { |
256 | const option = this.form.get('deploymentOption').value; | |
257 | const encrypted = this.form.get('encrypted').value; | |
258 | const deploymentSpec = { option: option, encrypted: encrypted }; | |
259 | const title = this.deploymentOptions.options[deploymentSpec.option].title; | |
260 | const trackingId = `${title} deployment`; | |
261 | this.taskWrapper | |
262 | .wrapTaskAroundCall({ | |
263 | task: new FinishedTask('osd/' + URLVerbs.CREATE, { | |
264 | tracking_id: trackingId | |
265 | }), | |
266 | call: this.osdService.create([deploymentSpec], trackingId, 'predefined') | |
267 | }) | |
268 | .subscribe({ | |
269 | complete: () => { | |
270 | this.router.navigate(['/osd']); | |
271 | } | |
272 | }); | |
273 | } else { | |
274 | // use user name and timestamp for drive group name | |
275 | const user = this.authStorageService.getUsername(); | |
276 | this.driveGroup.setName(`dashboard-${user}-${_.now()}`); | |
277 | const modalRef = this.modalService.show(OsdCreationPreviewModalComponent, { | |
278 | driveGroups: [this.driveGroup.spec] | |
279 | }); | |
280 | modalRef.componentInstance.submitAction.subscribe(() => { | |
281 | this.router.navigate(['/osd']); | |
282 | }); | |
283 | this.previewButtonPanel.submitButton.loading = false; | |
284 | } | |
9f95a23c TL |
285 | } |
286 | } |