]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { Component, EventEmitter, OnInit, Output } from '@angular/core'; |
2 | import { Validators } from '@angular/forms'; | |
3 | ||
4 | import { I18n } from '@ngx-translate/i18n-polyfill'; | |
5 | import { BsModalRef } from 'ngx-bootstrap/modal'; | |
6 | ||
7 | import { ErasureCodeProfileService } from '../../../shared/api/erasure-code-profile.service'; | |
8 | import { ActionLabelsI18n } from '../../../shared/constants/app.constants'; | |
9 | import { CdFormBuilder } from '../../../shared/forms/cd-form-builder'; | |
10 | import { CdFormGroup } from '../../../shared/forms/cd-form-group'; | |
11 | import { CdValidators } from '../../../shared/forms/cd-validators'; | |
12 | import { ErasureCodeProfile } from '../../../shared/models/erasure-code-profile'; | |
13 | import { FinishedTask } from '../../../shared/models/finished-task'; | |
14 | import { TaskWrapperService } from '../../../shared/services/task-wrapper.service'; | |
15 | ||
16 | @Component({ | |
17 | selector: 'cd-erasure-code-profile-form', | |
18 | templateUrl: './erasure-code-profile-form.component.html', | |
19 | styleUrls: ['./erasure-code-profile-form.component.scss'] | |
20 | }) | |
21 | export class ErasureCodeProfileFormComponent implements OnInit { | |
22 | @Output() | |
23 | submitAction = new EventEmitter(); | |
24 | ||
25 | form: CdFormGroup; | |
26 | failureDomains: string[]; | |
27 | plugins: string[]; | |
28 | names: string[]; | |
29 | techniques: string[]; | |
30 | requiredControls: string[] = []; | |
31 | devices: string[] = []; | |
32 | tooltips = this.ecpService.formTooltips; | |
33 | ||
34 | PLUGIN = { | |
35 | LRC: 'lrc', // Locally Repairable Erasure Code | |
36 | SHEC: 'shec', // Shingled Erasure Code | |
37 | JERASURE: 'jerasure', // default | |
38 | ISA: 'isa' // Intel Storage Acceleration | |
39 | }; | |
40 | plugin = this.PLUGIN.JERASURE; | |
41 | action: string; | |
42 | resource: string; | |
43 | ||
44 | constructor( | |
45 | private formBuilder: CdFormBuilder, | |
46 | public bsModalRef: BsModalRef, | |
47 | private taskWrapper: TaskWrapperService, | |
48 | private ecpService: ErasureCodeProfileService, | |
49 | private i18n: I18n, | |
50 | public actionLabels: ActionLabelsI18n | |
51 | ) { | |
52 | this.action = this.actionLabels.CREATE; | |
53 | this.resource = this.i18n('EC Profile'); | |
54 | this.createForm(); | |
55 | this.setJerasureDefaults(); | |
56 | } | |
57 | ||
58 | createForm() { | |
59 | this.form = this.formBuilder.group({ | |
60 | name: [ | |
61 | null, | |
62 | [ | |
63 | Validators.required, | |
64 | Validators.pattern('[A-Za-z0-9_-]+'), | |
65 | CdValidators.custom( | |
66 | 'uniqueName', | |
67 | (value) => this.names && this.names.indexOf(value) !== -1 | |
68 | ) | |
69 | ] | |
70 | ], | |
71 | plugin: [this.PLUGIN.JERASURE, [Validators.required]], | |
72 | k: [1], // Will be replaced by plugin defaults | |
73 | m: [1], // Will be replaced by plugin defaults | |
74 | crushFailureDomain: ['host'], | |
75 | crushRoot: ['default'], // default for all - is a list possible??? | |
76 | crushDeviceClass: [''], // set none to empty at submit - get list from configs? | |
77 | directory: [''], | |
78 | // Only for 'jerasure' and 'isa' use | |
79 | technique: ['reed_sol_van'], | |
80 | // Only for 'jerasure' use | |
81 | packetSize: [2048, [Validators.min(1)]], | |
82 | // Only for 'lrc' use | |
83 | l: [1, [Validators.required, Validators.min(1)]], | |
84 | crushLocality: [''], // set to none at the end (same list as for failure domains) | |
85 | // Only for 'shec' use | |
86 | c: [1, [Validators.required, Validators.min(1)]] | |
87 | }); | |
88 | this.form.get('plugin').valueChanges.subscribe((plugin) => this.onPluginChange(plugin)); | |
89 | } | |
90 | ||
91 | onPluginChange(plugin) { | |
92 | this.plugin = plugin; | |
93 | if (plugin === this.PLUGIN.JERASURE) { | |
94 | this.setJerasureDefaults(); | |
95 | } else if (plugin === this.PLUGIN.LRC) { | |
96 | this.setLrcDefaults(); | |
97 | } else if (plugin === this.PLUGIN.ISA) { | |
98 | this.setIsaDefaults(); | |
99 | } else if (plugin === this.PLUGIN.SHEC) { | |
100 | this.setShecDefaults(); | |
101 | } | |
102 | } | |
103 | ||
104 | private setNumberValidators(name: string, required: boolean) { | |
105 | const validators = [Validators.min(1)]; | |
106 | if (required) { | |
107 | validators.push(Validators.required); | |
108 | } | |
109 | this.form.get(name).setValidators(validators); | |
110 | } | |
111 | ||
112 | private setKMValidators(required: boolean) { | |
113 | ['k', 'm'].forEach((name) => this.setNumberValidators(name, required)); | |
114 | } | |
115 | ||
116 | private setJerasureDefaults() { | |
117 | this.requiredControls = ['k', 'm']; | |
118 | this.setDefaults({ | |
119 | k: 4, | |
120 | m: 2 | |
121 | }); | |
122 | this.setKMValidators(true); | |
123 | this.techniques = [ | |
124 | 'reed_sol_van', | |
125 | 'reed_sol_r6_op', | |
126 | 'cauchy_orig', | |
127 | 'cauchy_good', | |
128 | 'liberation', | |
129 | 'blaum_roth', | |
130 | 'liber8tion' | |
131 | ]; | |
132 | } | |
133 | ||
134 | private setLrcDefaults() { | |
135 | this.requiredControls = ['k', 'm', 'l']; | |
136 | this.setKMValidators(true); | |
137 | this.setNumberValidators('l', true); | |
138 | this.setDefaults({ | |
139 | k: 4, | |
140 | m: 2, | |
141 | l: 3 | |
142 | }); | |
143 | } | |
144 | ||
145 | private setIsaDefaults() { | |
146 | this.requiredControls = []; | |
147 | this.setKMValidators(false); | |
148 | this.setDefaults({ | |
149 | k: 7, | |
150 | m: 3 | |
151 | }); | |
152 | this.techniques = ['reed_sol_van', 'cauchy']; | |
153 | } | |
154 | ||
155 | private setShecDefaults() { | |
156 | this.requiredControls = []; | |
157 | this.setKMValidators(false); | |
158 | this.setDefaults({ | |
159 | k: 4, | |
160 | m: 3, | |
161 | c: 2 | |
162 | }); | |
163 | } | |
164 | ||
165 | private setDefaults(defaults: object) { | |
166 | Object.keys(defaults).forEach((controlName) => { | |
167 | if (this.form.get(controlName).pristine) { | |
168 | this.form.silentSet(controlName, defaults[controlName]); | |
169 | } | |
170 | }); | |
171 | } | |
172 | ||
173 | ngOnInit() { | |
174 | this.ecpService | |
175 | .getInfo() | |
176 | .subscribe( | |
177 | ({ | |
178 | failure_domains, | |
179 | plugins, | |
180 | names, | |
181 | directory, | |
182 | devices | |
183 | }: { | |
184 | failure_domains: string[]; | |
185 | plugins: string[]; | |
186 | names: string[]; | |
187 | directory: string; | |
188 | devices: string[]; | |
189 | }) => { | |
190 | this.failureDomains = failure_domains; | |
191 | this.plugins = plugins; | |
192 | this.names = names; | |
193 | this.devices = devices; | |
194 | this.form.silentSet('directory', directory); | |
195 | } | |
196 | ); | |
197 | } | |
198 | ||
199 | private createJson() { | |
200 | const pluginControls = { | |
201 | technique: [this.PLUGIN.ISA, this.PLUGIN.JERASURE], | |
202 | packetSize: [this.PLUGIN.JERASURE], | |
203 | l: [this.PLUGIN.LRC], | |
204 | crushLocality: [this.PLUGIN.LRC], | |
205 | c: [this.PLUGIN.SHEC] | |
206 | }; | |
207 | const ecp = new ErasureCodeProfile(); | |
208 | const plugin = this.form.getValue('plugin'); | |
209 | Object.keys(this.form.controls) | |
210 | .filter((name) => { | |
211 | const pluginControl = pluginControls[name]; | |
212 | const control = this.form.get(name); | |
213 | const usable = (pluginControl && pluginControl.includes(plugin)) || !pluginControl; | |
214 | return ( | |
215 | usable && | |
216 | (control.dirty || this.requiredControls.includes(name)) && | |
217 | this.form.getValue(name) | |
218 | ); | |
219 | }) | |
220 | .forEach((name) => { | |
221 | this.extendJson(name, ecp); | |
222 | }); | |
223 | return ecp; | |
224 | } | |
225 | ||
226 | private extendJson(name: string, ecp: ErasureCodeProfile) { | |
227 | const differentApiAttributes = { | |
228 | crushFailureDomain: 'crush-failure-domain', | |
229 | crushRoot: 'crush-root', | |
230 | crushDeviceClass: 'crush-device-class', | |
231 | packetSize: 'packetsize', | |
232 | crushLocality: 'crush-locality' | |
233 | }; | |
234 | ecp[differentApiAttributes[name] || name] = this.form.getValue(name); | |
235 | } | |
236 | ||
237 | onSubmit() { | |
238 | if (this.form.invalid) { | |
239 | this.form.setErrors({ cdSubmitButton: true }); | |
240 | return; | |
241 | } | |
242 | const profile = this.createJson(); | |
243 | this.taskWrapper | |
244 | .wrapTaskAroundCall({ | |
245 | task: new FinishedTask('ecp/create', { name: profile.name }), | |
246 | call: this.ecpService.create(profile) | |
247 | }) | |
248 | .subscribe( | |
249 | undefined, | |
250 | () => { | |
251 | this.form.setErrors({ cdSubmitButton: true }); | |
252 | }, | |
253 | () => { | |
254 | this.bsModalRef.hide(); | |
255 | this.submitAction.emit(profile); | |
256 | } | |
257 | ); | |
258 | } | |
259 | } |