]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { Component, EventEmitter, OnInit } from '@angular/core'; |
2 | import { FormControl, ValidatorFn, Validators } from '@angular/forms'; | |
3 | import { ActivatedRoute, Router } from '@angular/router'; | |
4 | ||
5 | import { I18n } from '@ngx-translate/i18n-polyfill'; | |
6 | import * as _ from 'lodash'; | |
9f95a23c | 7 | |
1911f103 TL |
8 | import { forkJoin, Observable, ReplaySubject } from 'rxjs'; |
9 | import { first, switchMap } from 'rxjs/operators'; | |
11fdf7f2 TL |
10 | |
11 | import { PoolService } from '../../../shared/api/pool.service'; | |
12 | import { RbdService } from '../../../shared/api/rbd.service'; | |
13 | import { ActionLabelsI18n } from '../../../shared/constants/app.constants'; | |
9f95a23c | 14 | import { Icons } from '../../../shared/enum/icons.enum'; |
11fdf7f2 TL |
15 | import { CdFormGroup } from '../../../shared/forms/cd-form-group'; |
16 | import { | |
17 | RbdConfigurationEntry, | |
18 | RbdConfigurationSourceField | |
19 | } from '../../../shared/models/configuration'; | |
20 | import { FinishedTask } from '../../../shared/models/finished-task'; | |
9f95a23c | 21 | import { ImageSpec } from '../../../shared/models/image-spec'; |
11fdf7f2 TL |
22 | import { Permission } from '../../../shared/models/permissions'; |
23 | import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe'; | |
24 | import { AuthStorageService } from '../../../shared/services/auth-storage.service'; | |
25 | import { FormatterService } from '../../../shared/services/formatter.service'; | |
26 | import { TaskWrapperService } from '../../../shared/services/task-wrapper.service'; | |
9f95a23c | 27 | import { Pool } from '../../pool/pool'; |
494da23a | 28 | import { RbdImageFeature } from './rbd-feature.interface'; |
11fdf7f2 TL |
29 | import { RbdFormCloneRequestModel } from './rbd-form-clone-request.model'; |
30 | import { RbdFormCopyRequestModel } from './rbd-form-copy-request.model'; | |
31 | import { RbdFormCreateRequestModel } from './rbd-form-create-request.model'; | |
32 | import { RbdFormEditRequestModel } from './rbd-form-edit-request.model'; | |
33 | import { RbdFormMode } from './rbd-form-mode.enum'; | |
34 | import { RbdFormResponseModel } from './rbd-form-response.model'; | |
35 | ||
36 | @Component({ | |
37 | selector: 'cd-rbd-form', | |
38 | templateUrl: './rbd-form.component.html', | |
39 | styleUrls: ['./rbd-form.component.scss'] | |
40 | }) | |
41 | export class RbdFormComponent implements OnInit { | |
42 | poolPermission: Permission; | |
43 | rbdForm: CdFormGroup; | |
11fdf7f2 TL |
44 | getDirtyConfigurationValues: ( |
45 | includeLocalField?: boolean, | |
46 | localField?: RbdConfigurationSourceField | |
47 | ) => RbdConfigurationEntry[]; | |
48 | ||
9f95a23c TL |
49 | namespaces: Array<string> = []; |
50 | namespacesByPoolCache = {}; | |
51 | pools: Array<Pool> = null; | |
52 | allPools: Array<Pool> = null; | |
11fdf7f2 TL |
53 | dataPools: Array<string> = null; |
54 | allDataPools: Array<string> = null; | |
494da23a TL |
55 | features: { [key: string]: RbdImageFeature }; |
56 | featuresList: RbdImageFeature[] = []; | |
11fdf7f2 TL |
57 | initializeConfigData = new EventEmitter<{ |
58 | initialData: RbdConfigurationEntry[]; | |
59 | sourceType: RbdConfigurationSourceField; | |
60 | }>(); | |
61 | ||
62 | pool: string; | |
63 | ||
64 | advancedEnabled = false; | |
65 | ||
66 | public rbdFormMode = RbdFormMode; | |
67 | mode: RbdFormMode; | |
68 | ||
69 | response: RbdFormResponseModel; | |
70 | snapName: string; | |
71 | ||
72 | defaultObjectSize = '4 MiB'; | |
73 | ||
74 | objectSizes: Array<string> = [ | |
75 | '4 KiB', | |
76 | '8 KiB', | |
77 | '16 KiB', | |
78 | '32 KiB', | |
79 | '64 KiB', | |
80 | '128 KiB', | |
81 | '256 KiB', | |
82 | '512 KiB', | |
83 | '1 MiB', | |
84 | '2 MiB', | |
85 | '4 MiB', | |
86 | '8 MiB', | |
87 | '16 MiB', | |
88 | '32 MiB' | |
89 | ]; | |
90 | action: string; | |
91 | resource: string; | |
1911f103 | 92 | private rbdImage = new ReplaySubject(1); |
11fdf7f2 | 93 | |
9f95a23c TL |
94 | icons = Icons; |
95 | ||
11fdf7f2 TL |
96 | constructor( |
97 | private authStorageService: AuthStorageService, | |
98 | private route: ActivatedRoute, | |
11fdf7f2 TL |
99 | private poolService: PoolService, |
100 | private rbdService: RbdService, | |
101 | private formatter: FormatterService, | |
102 | private taskWrapper: TaskWrapperService, | |
103 | private dimlessBinaryPipe: DimlessBinaryPipe, | |
104 | private i18n: I18n, | |
494da23a TL |
105 | public actionLabels: ActionLabelsI18n, |
106 | public router: Router | |
11fdf7f2 TL |
107 | ) { |
108 | this.poolPermission = this.authStorageService.getPermissions().pool; | |
109 | this.resource = this.i18n('RBD'); | |
110 | this.features = { | |
111 | 'deep-flatten': { | |
112 | desc: this.i18n('Deep flatten'), | |
113 | requires: null, | |
114 | allowEnable: false, | |
115 | allowDisable: true | |
116 | }, | |
117 | layering: { | |
118 | desc: this.i18n('Layering'), | |
119 | requires: null, | |
120 | allowEnable: false, | |
121 | allowDisable: false | |
122 | }, | |
123 | 'exclusive-lock': { | |
124 | desc: this.i18n('Exclusive lock'), | |
125 | requires: null, | |
126 | allowEnable: true, | |
127 | allowDisable: true | |
128 | }, | |
129 | 'object-map': { | |
130 | desc: this.i18n('Object map (requires exclusive-lock)'), | |
131 | requires: 'exclusive-lock', | |
132 | allowEnable: true, | |
494da23a TL |
133 | allowDisable: true, |
134 | initDisabled: true | |
11fdf7f2 TL |
135 | }, |
136 | journaling: { | |
137 | desc: this.i18n('Journaling (requires exclusive-lock)'), | |
138 | requires: 'exclusive-lock', | |
139 | allowEnable: true, | |
494da23a TL |
140 | allowDisable: true, |
141 | initDisabled: true | |
11fdf7f2 TL |
142 | }, |
143 | 'fast-diff': { | |
494da23a | 144 | desc: this.i18n('Fast diff (interlocked with object-map)'), |
11fdf7f2 TL |
145 | requires: 'object-map', |
146 | allowEnable: true, | |
494da23a TL |
147 | allowDisable: true, |
148 | interlockedWith: 'object-map', | |
149 | initDisabled: true | |
11fdf7f2 TL |
150 | } |
151 | }; | |
494da23a | 152 | this.featuresList = this.objToArray(this.features); |
11fdf7f2 | 153 | this.createForm(); |
494da23a TL |
154 | } |
155 | ||
156 | objToArray(obj: { [key: string]: any }) { | |
157 | return _.map(obj, (o, key) => Object.assign(o, { key: key })); | |
11fdf7f2 TL |
158 | } |
159 | ||
160 | createForm() { | |
11fdf7f2 TL |
161 | this.rbdForm = new CdFormGroup( |
162 | { | |
163 | parent: new FormControl(''), | |
164 | name: new FormControl('', { | |
165 | validators: [Validators.required, Validators.pattern(/^[^@/]+?$/)] | |
166 | }), | |
167 | pool: new FormControl(null, { | |
168 | validators: [Validators.required] | |
169 | }), | |
9f95a23c | 170 | namespace: new FormControl(null), |
11fdf7f2 TL |
171 | useDataPool: new FormControl(false), |
172 | dataPool: new FormControl(null), | |
173 | size: new FormControl(null, { | |
174 | updateOn: 'blur' | |
175 | }), | |
176 | obj_size: new FormControl(this.defaultObjectSize), | |
494da23a | 177 | features: new CdFormGroup( |
9f95a23c | 178 | this.featuresList.reduce((acc: object, e) => { |
494da23a TL |
179 | acc[e.key] = new FormControl({ value: false, disabled: !!e.initDisabled }); |
180 | return acc; | |
181 | }, {}) | |
182 | ), | |
11fdf7f2 TL |
183 | stripingUnit: new FormControl(null), |
184 | stripingCount: new FormControl(null, { | |
185 | updateOn: 'blur' | |
186 | }) | |
187 | }, | |
188 | this.validateRbdForm(this.formatter) | |
189 | ); | |
190 | } | |
191 | ||
192 | disableForEdit() { | |
193 | this.rbdForm.get('parent').disable(); | |
194 | this.rbdForm.get('pool').disable(); | |
9f95a23c | 195 | this.rbdForm.get('namespace').disable(); |
11fdf7f2 TL |
196 | this.rbdForm.get('useDataPool').disable(); |
197 | this.rbdForm.get('dataPool').disable(); | |
198 | this.rbdForm.get('obj_size').disable(); | |
199 | this.rbdForm.get('stripingUnit').disable(); | |
200 | this.rbdForm.get('stripingCount').disable(); | |
201 | } | |
202 | ||
203 | disableForClone() { | |
204 | this.rbdForm.get('parent').disable(); | |
205 | this.rbdForm.get('size').disable(); | |
206 | } | |
207 | ||
208 | disableForCopy() { | |
209 | this.rbdForm.get('parent').disable(); | |
210 | this.rbdForm.get('size').disable(); | |
211 | } | |
212 | ||
213 | ngOnInit() { | |
214 | if (this.router.url.startsWith('/block/rbd/edit')) { | |
215 | this.mode = this.rbdFormMode.editing; | |
216 | this.action = this.actionLabels.EDIT; | |
217 | this.disableForEdit(); | |
218 | } else if (this.router.url.startsWith('/block/rbd/clone')) { | |
219 | this.mode = this.rbdFormMode.cloning; | |
220 | this.disableForClone(); | |
221 | this.action = this.actionLabels.CLONE; | |
222 | } else if (this.router.url.startsWith('/block/rbd/copy')) { | |
223 | this.mode = this.rbdFormMode.copying; | |
224 | this.action = this.actionLabels.COPY; | |
225 | this.disableForCopy(); | |
226 | } else { | |
227 | this.action = this.actionLabels.CREATE; | |
228 | } | |
9f95a23c TL |
229 | enum Promisse { |
230 | RbdServiceGet = 'rbdService.get', | |
231 | PoolServiceList = 'poolService.list' | |
232 | } | |
233 | const promisses = {}; | |
11fdf7f2 TL |
234 | if ( |
235 | this.mode === this.rbdFormMode.editing || | |
236 | this.mode === this.rbdFormMode.cloning || | |
237 | this.mode === this.rbdFormMode.copying | |
238 | ) { | |
9f95a23c TL |
239 | this.route.params.subscribe((params: { image_spec: string; snap: string }) => { |
240 | const imageSpec = ImageSpec.fromString(decodeURIComponent(params.image_spec)); | |
11fdf7f2 TL |
241 | if (params.snap) { |
242 | this.snapName = decodeURIComponent(params.snap); | |
243 | } | |
9f95a23c | 244 | promisses[Promisse.RbdServiceGet] = this.rbdService.get(imageSpec); |
11fdf7f2 TL |
245 | }); |
246 | } else { | |
494da23a | 247 | // New image |
11fdf7f2 TL |
248 | this.rbdService.defaultFeatures().subscribe((defaultFeatures: Array<string>) => { |
249 | this.setFeatures(defaultFeatures); | |
250 | }); | |
251 | } | |
252 | if (this.mode !== this.rbdFormMode.editing && this.poolPermission.read) { | |
9f95a23c TL |
253 | promisses[Promisse.PoolServiceList] = this.poolService.list([ |
254 | 'pool_name', | |
255 | 'type', | |
256 | 'flags_names', | |
257 | 'application_metadata' | |
258 | ]); | |
259 | } | |
260 | ||
261 | forkJoin(promisses).subscribe((data: object) => { | |
262 | // poolService.list | |
263 | if (data[Promisse.PoolServiceList]) { | |
264 | const pools: Pool[] = []; | |
265 | const dataPools = []; | |
266 | for (const pool of data[Promisse.PoolServiceList]) { | |
267 | if (this.rbdService.isRBDPool(pool)) { | |
268 | if (pool.type === 'replicated') { | |
269 | pools.push(pool); | |
270 | dataPools.push(pool); | |
271 | } else if ( | |
272 | pool.type === 'erasure' && | |
273 | pool.flags_names.indexOf('ec_overwrites') !== -1 | |
274 | ) { | |
275 | dataPools.push(pool); | |
11fdf7f2 TL |
276 | } |
277 | } | |
9f95a23c TL |
278 | } |
279 | this.pools = pools; | |
280 | this.allPools = pools; | |
281 | this.dataPools = dataPools; | |
282 | this.allDataPools = dataPools; | |
283 | if (this.pools.length === 1) { | |
284 | const poolName = this.pools[0].pool_name; | |
285 | this.rbdForm.get('pool').setValue(poolName); | |
286 | this.onPoolChange(poolName); | |
287 | } | |
288 | } | |
289 | ||
290 | // rbdService.get | |
291 | if (data[Promisse.RbdServiceGet]) { | |
292 | const resp: RbdFormResponseModel = data[Promisse.RbdServiceGet]; | |
293 | this.setResponse(resp, this.snapName); | |
294 | this.rbdImage.next(resp); | |
295 | } | |
296 | }); | |
297 | ||
494da23a TL |
298 | _.each(this.features, (feature) => { |
299 | this.rbdForm | |
300 | .get('features') | |
301 | .get(feature.key) | |
302 | .valueChanges.subscribe((value) => this.featureFormUpdate(feature.key, value)); | |
11fdf7f2 TL |
303 | }); |
304 | } | |
305 | ||
9f95a23c TL |
306 | onPoolChange(selectedPoolName: string) { |
307 | const newDataPools = this.allDataPools | |
308 | ? this.allDataPools.filter((dataPool: any) => { | |
309 | return dataPool.pool_name !== selectedPoolName; | |
310 | }) | |
311 | : []; | |
11fdf7f2 TL |
312 | if (this.rbdForm.getValue('dataPool') === selectedPoolName) { |
313 | this.rbdForm.get('dataPool').setValue(null); | |
314 | } | |
315 | this.dataPools = newDataPools; | |
9f95a23c TL |
316 | this.namespaces = null; |
317 | if (selectedPoolName in this.namespacesByPoolCache) { | |
318 | this.namespaces = this.namespacesByPoolCache[selectedPoolName]; | |
319 | } else { | |
320 | this.rbdService.listNamespaces(selectedPoolName).subscribe((namespaces: any[]) => { | |
321 | namespaces = namespaces.map((namespace) => namespace.namespace); | |
322 | this.namespacesByPoolCache[selectedPoolName] = namespaces; | |
323 | this.namespaces = namespaces; | |
324 | }); | |
325 | } | |
326 | this.rbdForm.get('namespace').setValue(null); | |
11fdf7f2 TL |
327 | } |
328 | ||
329 | onUseDataPoolChange() { | |
330 | if (!this.rbdForm.getValue('useDataPool')) { | |
331 | this.rbdForm.get('dataPool').setValue(null); | |
332 | this.onDataPoolChange(null); | |
333 | } | |
334 | } | |
335 | ||
9f95a23c TL |
336 | onDataPoolChange(selectedDataPoolName: string) { |
337 | const newPools = this.allPools.filter((pool: Pool) => { | |
11fdf7f2 TL |
338 | return pool.pool_name !== selectedDataPoolName; |
339 | }); | |
340 | if (this.rbdForm.getValue('pool') === selectedDataPoolName) { | |
341 | this.rbdForm.get('pool').setValue(null); | |
342 | } | |
343 | this.pools = newPools; | |
344 | } | |
345 | ||
346 | validateRbdForm(formatter: FormatterService): ValidatorFn { | |
347 | return (formGroup: CdFormGroup) => { | |
348 | // Data Pool | |
349 | const useDataPoolControl = formGroup.get('useDataPool'); | |
350 | const dataPoolControl = formGroup.get('dataPool'); | |
351 | let dataPoolControlErrors = null; | |
352 | if (useDataPoolControl.value && dataPoolControl.value == null) { | |
353 | dataPoolControlErrors = { required: true }; | |
354 | } | |
355 | dataPoolControl.setErrors(dataPoolControlErrors); | |
356 | // Size | |
357 | const sizeControl = formGroup.get('size'); | |
358 | const objectSizeControl = formGroup.get('obj_size'); | |
359 | const objectSizeInBytes = formatter.toBytes( | |
360 | objectSizeControl.value != null ? objectSizeControl.value : this.defaultObjectSize | |
361 | ); | |
362 | const stripingCountControl = formGroup.get('stripingCount'); | |
363 | const stripingCount = stripingCountControl.value != null ? stripingCountControl.value : 1; | |
364 | let sizeControlErrors = null; | |
365 | if (sizeControl.value === null) { | |
366 | sizeControlErrors = { required: true }; | |
367 | } else { | |
368 | const sizeInBytes = formatter.toBytes(sizeControl.value); | |
369 | if (stripingCount * objectSizeInBytes > sizeInBytes) { | |
370 | sizeControlErrors = { invalidSizeObject: true }; | |
371 | } | |
372 | } | |
373 | sizeControl.setErrors(sizeControlErrors); | |
374 | // Striping Unit | |
375 | const stripingUnitControl = formGroup.get('stripingUnit'); | |
376 | let stripingUnitControlErrors = null; | |
377 | if (stripingUnitControl.value === null && stripingCountControl.value !== null) { | |
378 | stripingUnitControlErrors = { required: true }; | |
379 | } else if (stripingUnitControl.value !== null) { | |
380 | const stripingUnitInBytes = formatter.toBytes(stripingUnitControl.value); | |
381 | if (stripingUnitInBytes > objectSizeInBytes) { | |
382 | stripingUnitControlErrors = { invalidStripingUnit: true }; | |
383 | } | |
384 | } | |
385 | stripingUnitControl.setErrors(stripingUnitControlErrors); | |
386 | // Striping Count | |
387 | let stripingCountControlErrors = null; | |
388 | if (stripingCountControl.value === null && stripingUnitControl.value !== null) { | |
389 | stripingCountControlErrors = { required: true }; | |
390 | } else if (stripingCount < 1) { | |
391 | stripingCountControlErrors = { min: true }; | |
392 | } | |
393 | stripingCountControl.setErrors(stripingCountControlErrors); | |
394 | return null; | |
395 | }; | |
396 | } | |
397 | ||
494da23a TL |
398 | protected getDependendChildFeatures(featureKey: string) { |
399 | return _.filter(this.features, (f) => f.requires === featureKey) || []; | |
400 | } | |
401 | ||
9f95a23c | 402 | deepBoxCheck(key: string, checked: boolean) { |
494da23a TL |
403 | const childFeatures = this.getDependendChildFeatures(key); |
404 | childFeatures.forEach((feature) => { | |
405 | const featureControl = this.rbdForm.get(feature.key); | |
406 | if (checked) { | |
407 | featureControl.enable({ emitEvent: false }); | |
408 | } else { | |
409 | featureControl.disable({ emitEvent: false }); | |
410 | featureControl.setValue(false, { emitEvent: false }); | |
411 | this.deepBoxCheck(feature.key, checked); | |
11fdf7f2 | 412 | } |
494da23a TL |
413 | |
414 | const featureFormGroup = this.rbdForm.get('features'); | |
415 | if (this.mode === this.rbdFormMode.editing && featureFormGroup.get(feature.key).enabled) { | |
416 | if (this.response.features_name.indexOf(feature.key) !== -1 && !feature.allowDisable) { | |
417 | featureFormGroup.get(feature.key).disable(); | |
418 | } else if ( | |
419 | this.response.features_name.indexOf(feature.key) === -1 && | |
420 | !feature.allowEnable | |
421 | ) { | |
422 | featureFormGroup.get(feature.key).disable(); | |
11fdf7f2 TL |
423 | } |
424 | } | |
425 | }); | |
426 | } | |
427 | ||
9f95a23c | 428 | interlockCheck(key: string, checked: boolean) { |
494da23a TL |
429 | // Adds a compatibility layer for Ceph cluster where the feature interlock of features hasn't |
430 | // been implemented yet. It disables the feature interlock for images which only have one of | |
431 | // both interlocked features (at the time of this writing: object-map and fast-diff) enabled. | |
432 | const feature = this.featuresList.find((f) => f.key === key); | |
433 | if (this.response) { | |
434 | // Ignore `create` page | |
435 | const hasInterlockedFeature = feature.interlockedWith != null; | |
436 | const dependentInterlockedFeature = this.featuresList.find( | |
437 | (f) => f.interlockedWith === feature.key | |
438 | ); | |
439 | const isOriginFeatureEnabled = !!this.response.features_name.find((e) => e === feature.key); // in this case: fast-diff | |
440 | if (hasInterlockedFeature) { | |
441 | const isLinkedEnabled = !!this.response.features_name.find( | |
442 | (e) => e === feature.interlockedWith | |
443 | ); // depends: object-map | |
444 | if (isOriginFeatureEnabled !== isLinkedEnabled) { | |
445 | return; // Ignore incompatible setting because it's from a previous cluster version | |
446 | } | |
447 | } else if (dependentInterlockedFeature) { | |
448 | const isOtherInterlockedFeatureEnabled = !!this.response.features_name.find( | |
449 | (e) => e === dependentInterlockedFeature.key | |
450 | ); | |
451 | if (isOtherInterlockedFeatureEnabled !== isOriginFeatureEnabled) { | |
452 | return; // Ignore incompatible setting because it's from a previous cluster version | |
453 | } | |
454 | } | |
455 | } | |
456 | ||
457 | if (checked) { | |
458 | _.filter(this.features, (f) => f.interlockedWith === key).forEach((f) => | |
459 | this.rbdForm.get(f.key).setValue(true, { emitEvent: false }) | |
460 | ); | |
461 | } else { | |
462 | if (feature.interlockedWith) { | |
463 | // Don't skip emitting the event here, as it prevents `fast-diff` from | |
464 | // becoming disabled when manually unchecked. This is because it | |
465 | // triggers an update on `object-map` and if `object-map` doesn't emit, | |
466 | // `fast-diff` will not be automatically disabled. | |
e306af50 | 467 | this.rbdForm.get('features').get(feature.interlockedWith).setValue(false); |
494da23a TL |
468 | } |
469 | } | |
470 | } | |
471 | ||
9f95a23c | 472 | featureFormUpdate(key: string, checked: boolean) { |
11fdf7f2 TL |
473 | if (checked) { |
474 | const required = this.features[key].requires; | |
475 | if (required && !this.rbdForm.getValue(required)) { | |
494da23a | 476 | this.rbdForm.get(`features.${key}`).setValue(false); |
11fdf7f2 TL |
477 | return; |
478 | } | |
479 | } | |
480 | this.deepBoxCheck(key, checked); | |
494da23a | 481 | this.interlockCheck(key, checked); |
11fdf7f2 TL |
482 | } |
483 | ||
484 | setFeatures(features: Array<string>) { | |
485 | const featuresControl = this.rbdForm.get('features'); | |
486 | _.forIn(this.features, (feature) => { | |
487 | if (features.indexOf(feature.key) !== -1) { | |
488 | featuresControl.get(feature.key).setValue(true); | |
489 | } | |
494da23a | 490 | this.featureFormUpdate(feature.key, featuresControl.get(feature.key).value); |
11fdf7f2 TL |
491 | }); |
492 | } | |
493 | ||
494 | setResponse(response: RbdFormResponseModel, snapName: string) { | |
495 | this.response = response; | |
9f95a23c TL |
496 | const imageSpec = new ImageSpec( |
497 | response.pool_name, | |
498 | response.namespace, | |
499 | response.name | |
500 | ).toString(); | |
11fdf7f2 | 501 | if (this.mode === this.rbdFormMode.cloning) { |
9f95a23c | 502 | this.rbdForm.get('parent').setValue(`${imageSpec}@${snapName}`); |
11fdf7f2 TL |
503 | } else if (this.mode === this.rbdFormMode.copying) { |
504 | if (snapName) { | |
9f95a23c | 505 | this.rbdForm.get('parent').setValue(`${imageSpec}@${snapName}`); |
11fdf7f2 | 506 | } else { |
9f95a23c | 507 | this.rbdForm.get('parent').setValue(`${imageSpec}`); |
11fdf7f2 TL |
508 | } |
509 | } else if (response.parent) { | |
510 | const parent = response.parent; | |
511 | this.rbdForm | |
512 | .get('parent') | |
513 | .setValue(`${parent.pool_name}/${parent.image_name}@${parent.snap_name}`); | |
514 | } | |
515 | if (this.mode === this.rbdFormMode.editing) { | |
516 | this.rbdForm.get('name').setValue(response.name); | |
517 | } | |
518 | this.rbdForm.get('pool').setValue(response.pool_name); | |
9f95a23c TL |
519 | this.onPoolChange(response.pool_name); |
520 | this.rbdForm.get('namespace').setValue(response.namespace); | |
11fdf7f2 TL |
521 | if (response.data_pool) { |
522 | this.rbdForm.get('useDataPool').setValue(true); | |
523 | this.rbdForm.get('dataPool').setValue(response.data_pool); | |
524 | } | |
525 | this.rbdForm.get('size').setValue(this.dimlessBinaryPipe.transform(response.size)); | |
526 | this.rbdForm.get('obj_size').setValue(this.dimlessBinaryPipe.transform(response.obj_size)); | |
527 | this.setFeatures(response.features_name); | |
528 | this.rbdForm | |
529 | .get('stripingUnit') | |
530 | .setValue(this.dimlessBinaryPipe.transform(response.stripe_unit)); | |
531 | this.rbdForm.get('stripingCount').setValue(response.stripe_count); | |
532 | ||
533 | /* Configuration */ | |
534 | this.initializeConfigData.emit({ | |
535 | initialData: this.response.configuration, | |
536 | sourceType: RbdConfigurationSourceField.image | |
537 | }); | |
538 | } | |
539 | ||
540 | createRequest() { | |
541 | const request = new RbdFormCreateRequestModel(); | |
542 | request.pool_name = this.rbdForm.getValue('pool'); | |
9f95a23c | 543 | request.namespace = this.rbdForm.getValue('namespace'); |
11fdf7f2 TL |
544 | request.name = this.rbdForm.getValue('name'); |
545 | request.size = this.formatter.toBytes(this.rbdForm.getValue('size')); | |
546 | request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size')); | |
547 | _.forIn(this.features, (feature) => { | |
548 | if (this.rbdForm.getValue(feature.key)) { | |
549 | request.features.push(feature.key); | |
550 | } | |
551 | }); | |
552 | ||
553 | /* Striping */ | |
554 | request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit')); | |
555 | request.stripe_count = this.rbdForm.getValue('stripingCount'); | |
556 | request.data_pool = this.rbdForm.getValue('dataPool'); | |
557 | ||
558 | /* Configuration */ | |
559 | request.configuration = this.getDirtyConfigurationValues(); | |
560 | ||
561 | return request; | |
562 | } | |
563 | ||
564 | createAction(): Observable<any> { | |
565 | const request = this.createRequest(); | |
566 | return this.taskWrapper.wrapTaskAroundCall({ | |
567 | task: new FinishedTask('rbd/create', { | |
568 | pool_name: request.pool_name, | |
9f95a23c | 569 | namespace: request.namespace, |
11fdf7f2 TL |
570 | image_name: request.name |
571 | }), | |
572 | call: this.rbdService.create(request) | |
573 | }); | |
574 | } | |
575 | ||
576 | editRequest() { | |
577 | const request = new RbdFormEditRequestModel(); | |
578 | request.name = this.rbdForm.getValue('name'); | |
579 | request.size = this.formatter.toBytes(this.rbdForm.getValue('size')); | |
580 | _.forIn(this.features, (feature) => { | |
581 | if (this.rbdForm.getValue(feature.key)) { | |
582 | request.features.push(feature.key); | |
583 | } | |
584 | }); | |
585 | ||
586 | request.configuration = this.getDirtyConfigurationValues(); | |
587 | ||
588 | return request; | |
589 | } | |
590 | ||
591 | cloneRequest(): RbdFormCloneRequestModel { | |
592 | const request = new RbdFormCloneRequestModel(); | |
593 | request.child_pool_name = this.rbdForm.getValue('pool'); | |
9f95a23c | 594 | request.child_namespace = this.rbdForm.getValue('namespace'); |
11fdf7f2 TL |
595 | request.child_image_name = this.rbdForm.getValue('name'); |
596 | request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size')); | |
597 | _.forIn(this.features, (feature) => { | |
598 | if (this.rbdForm.getValue(feature.key)) { | |
599 | request.features.push(feature.key); | |
600 | } | |
601 | }); | |
602 | ||
603 | /* Striping */ | |
604 | request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit')); | |
605 | request.stripe_count = this.rbdForm.getValue('stripingCount'); | |
606 | request.data_pool = this.rbdForm.getValue('dataPool'); | |
607 | ||
608 | /* Configuration */ | |
609 | request.configuration = this.getDirtyConfigurationValues( | |
610 | true, | |
611 | RbdConfigurationSourceField.image | |
612 | ); | |
613 | ||
614 | return request; | |
615 | } | |
616 | ||
617 | editAction(): Observable<any> { | |
9f95a23c TL |
618 | const imageSpec = new ImageSpec( |
619 | this.response.pool_name, | |
620 | this.response.namespace, | |
621 | this.response.name | |
622 | ); | |
11fdf7f2 TL |
623 | return this.taskWrapper.wrapTaskAroundCall({ |
624 | task: new FinishedTask('rbd/edit', { | |
9f95a23c | 625 | image_spec: imageSpec.toString() |
11fdf7f2 | 626 | }), |
9f95a23c | 627 | call: this.rbdService.update(imageSpec, this.editRequest()) |
11fdf7f2 TL |
628 | }); |
629 | } | |
630 | ||
631 | cloneAction(): Observable<any> { | |
632 | const request = this.cloneRequest(); | |
9f95a23c TL |
633 | const imageSpec = new ImageSpec( |
634 | this.response.pool_name, | |
635 | this.response.namespace, | |
636 | this.response.name | |
637 | ); | |
11fdf7f2 TL |
638 | return this.taskWrapper.wrapTaskAroundCall({ |
639 | task: new FinishedTask('rbd/clone', { | |
9f95a23c | 640 | parent_image_spec: imageSpec.toString(), |
11fdf7f2 TL |
641 | parent_snap_name: this.snapName, |
642 | child_pool_name: request.child_pool_name, | |
9f95a23c | 643 | child_namespace: request.child_namespace, |
11fdf7f2 TL |
644 | child_image_name: request.child_image_name |
645 | }), | |
9f95a23c | 646 | call: this.rbdService.cloneSnapshot(imageSpec, this.snapName, request) |
11fdf7f2 TL |
647 | }); |
648 | } | |
649 | ||
650 | copyRequest(): RbdFormCopyRequestModel { | |
651 | const request = new RbdFormCopyRequestModel(); | |
652 | if (this.snapName) { | |
653 | request.snapshot_name = this.snapName; | |
654 | } | |
655 | request.dest_pool_name = this.rbdForm.getValue('pool'); | |
9f95a23c | 656 | request.dest_namespace = this.rbdForm.getValue('namespace'); |
11fdf7f2 TL |
657 | request.dest_image_name = this.rbdForm.getValue('name'); |
658 | request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size')); | |
659 | _.forIn(this.features, (feature) => { | |
660 | if (this.rbdForm.getValue(feature.key)) { | |
661 | request.features.push(feature.key); | |
662 | } | |
663 | }); | |
664 | ||
665 | /* Striping */ | |
666 | request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit')); | |
667 | request.stripe_count = this.rbdForm.getValue('stripingCount'); | |
668 | request.data_pool = this.rbdForm.getValue('dataPool'); | |
669 | ||
670 | /* Configuration */ | |
671 | request.configuration = this.getDirtyConfigurationValues( | |
672 | true, | |
673 | RbdConfigurationSourceField.image | |
674 | ); | |
675 | ||
676 | return request; | |
677 | } | |
678 | ||
679 | copyAction(): Observable<any> { | |
680 | const request = this.copyRequest(); | |
9f95a23c TL |
681 | const imageSpec = new ImageSpec( |
682 | this.response.pool_name, | |
683 | this.response.namespace, | |
684 | this.response.name | |
685 | ); | |
11fdf7f2 TL |
686 | return this.taskWrapper.wrapTaskAroundCall({ |
687 | task: new FinishedTask('rbd/copy', { | |
9f95a23c | 688 | src_image_spec: imageSpec.toString(), |
11fdf7f2 | 689 | dest_pool_name: request.dest_pool_name, |
9f95a23c | 690 | dest_namespace: request.dest_namespace, |
11fdf7f2 TL |
691 | dest_image_name: request.dest_image_name |
692 | }), | |
9f95a23c | 693 | call: this.rbdService.copy(imageSpec, request) |
11fdf7f2 TL |
694 | }); |
695 | } | |
696 | ||
697 | submit() { | |
eafe8130 TL |
698 | if (!this.mode) { |
699 | this.rbdImage.next('create'); | |
11fdf7f2 | 700 | } |
eafe8130 TL |
701 | this.rbdImage |
702 | .pipe( | |
1911f103 | 703 | first(), |
eafe8130 TL |
704 | switchMap(() => { |
705 | if (this.mode === this.rbdFormMode.editing) { | |
706 | return this.editAction(); | |
707 | } else if (this.mode === this.rbdFormMode.cloning) { | |
708 | return this.cloneAction(); | |
709 | } else if (this.mode === this.rbdFormMode.copying) { | |
710 | return this.copyAction(); | |
711 | } else { | |
712 | return this.createAction(); | |
713 | } | |
714 | }) | |
715 | ) | |
716 | .subscribe( | |
717 | () => {}, | |
718 | () => this.rbdForm.setErrors({ cdSubmitButton: true }), | |
719 | () => this.router.navigate(['/block/rbd']) | |
720 | ); | |
11fdf7f2 TL |
721 | } |
722 | } |