]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { Component, OnInit } from '@angular/core'; |
2 | import { FormArray, FormControl, 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'; | |
7 | import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; | |
8 | import { forkJoin } from 'rxjs'; | |
9 | ||
10 | import { IscsiService } from '../../../shared/api/iscsi.service'; | |
11 | import { RbdService } from '../../../shared/api/rbd.service'; | |
12 | import { SelectMessages } from '../../../shared/components/select/select-messages.model'; | |
13 | import { SelectOption } from '../../../shared/components/select/select-option.model'; | |
14 | import { ActionLabelsI18n } from '../../../shared/constants/app.constants'; | |
15 | import { CdFormGroup } from '../../../shared/forms/cd-form-group'; | |
16 | import { CdValidators } from '../../../shared/forms/cd-validators'; | |
17 | import { FinishedTask } from '../../../shared/models/finished-task'; | |
18 | import { TaskWrapperService } from '../../../shared/services/task-wrapper.service'; | |
19 | import { IscsiTargetImageSettingsModalComponent } from '../iscsi-target-image-settings-modal/iscsi-target-image-settings-modal.component'; | |
20 | import { IscsiTargetIqnSettingsModalComponent } from '../iscsi-target-iqn-settings-modal/iscsi-target-iqn-settings-modal.component'; | |
21 | ||
22 | @Component({ | |
23 | selector: 'cd-iscsi-target-form', | |
24 | templateUrl: './iscsi-target-form.component.html', | |
25 | styleUrls: ['./iscsi-target-form.component.scss'] | |
26 | }) | |
27 | export class IscsiTargetFormComponent implements OnInit { | |
28 | targetForm: CdFormGroup; | |
29 | modalRef: BsModalRef; | |
30 | minimum_gateways = 1; | |
31 | target_default_controls: any; | |
32 | disk_default_controls: any; | |
33 | backstores: string[]; | |
34 | default_backstore: string; | |
35 | supported_rbd_features: any; | |
36 | required_rbd_features: any; | |
37 | ||
38 | isEdit = false; | |
39 | target_iqn: string; | |
40 | ||
41 | imagesAll: any[]; | |
42 | imagesSelections: SelectOption[]; | |
43 | portalsSelections: SelectOption[] = []; | |
44 | ||
45 | imagesInitiatorSelections: SelectOption[][] = []; | |
46 | groupDiskSelections: SelectOption[][] = []; | |
47 | groupMembersSelections: SelectOption[][] = []; | |
48 | ||
49 | imagesSettings: any = {}; | |
50 | messages = { | |
51 | portals: new SelectMessages( | |
52 | { noOptions: this.i18n('There are no portals available.') }, | |
53 | this.i18n | |
54 | ), | |
55 | images: new SelectMessages( | |
56 | { noOptions: this.i18n('There are no images available.') }, | |
57 | this.i18n | |
58 | ), | |
59 | initiatorImage: new SelectMessages( | |
60 | { | |
61 | noOptions: this.i18n( | |
62 | 'There are no images available. Please make sure you add an image to the target.' | |
63 | ) | |
64 | }, | |
65 | this.i18n | |
66 | ), | |
67 | groupInitiator: new SelectMessages( | |
68 | { | |
69 | noOptions: this.i18n( | |
70 | 'There are no initiators available. Please make sure you add an initiator to the target.' | |
71 | ) | |
72 | }, | |
73 | this.i18n | |
74 | ) | |
75 | }; | |
76 | ||
77 | IQN_REGEX = /^iqn\.(19|20)\d\d-(0[1-9]|1[0-2])\.\D{2,3}(\.[A-Za-z0-9-]+)+(:[A-Za-z0-9-\.]+)*$/; | |
78 | USER_REGEX = /[\w\.:@_-]{8,64}/; | |
79 | PASSWORD_REGEX = /[\w@\-_\/]{12,16}/; | |
80 | action: string; | |
81 | resource: string; | |
82 | ||
83 | constructor( | |
84 | private iscsiService: IscsiService, | |
85 | private modalService: BsModalService, | |
86 | private rbdService: RbdService, | |
87 | private router: Router, | |
88 | private route: ActivatedRoute, | |
89 | private i18n: I18n, | |
90 | private taskWrapper: TaskWrapperService, | |
91 | public actionLabels: ActionLabelsI18n | |
92 | ) { | |
93 | this.resource = this.i18n('target'); | |
94 | } | |
95 | ||
96 | ngOnInit() { | |
97 | const promises: any[] = [ | |
98 | this.iscsiService.listTargets(), | |
99 | this.rbdService.list(), | |
100 | this.iscsiService.portals(), | |
101 | this.iscsiService.settings() | |
102 | ]; | |
103 | ||
104 | if (this.router.url.startsWith('/block/iscsi/targets/edit')) { | |
105 | this.isEdit = true; | |
106 | this.route.params.subscribe((params: { target_iqn: string }) => { | |
107 | this.target_iqn = decodeURIComponent(params.target_iqn); | |
108 | promises.push(this.iscsiService.getTarget(this.target_iqn)); | |
109 | }); | |
110 | } | |
111 | this.action = this.isEdit ? this.actionLabels.EDIT : this.actionLabels.CREATE; | |
112 | ||
113 | forkJoin(promises).subscribe((data: any[]) => { | |
114 | // iscsiService.listTargets | |
115 | const usedImages = _(data[0]) | |
116 | .filter((target) => target.target_iqn !== this.target_iqn) | |
117 | .flatMap((target) => target.disks) | |
118 | .map((image) => `${image.pool}/${image.image}`) | |
119 | .value(); | |
120 | ||
121 | // iscsiService.settings() | |
122 | this.minimum_gateways = data[3].config.minimum_gateways; | |
123 | this.target_default_controls = data[3].target_default_controls; | |
124 | this.disk_default_controls = data[3].disk_default_controls; | |
125 | this.backstores = data[3].backstores; | |
126 | this.default_backstore = data[3].default_backstore; | |
127 | this.supported_rbd_features = data[3].supported_rbd_features; | |
128 | this.required_rbd_features = data[3].required_rbd_features; | |
129 | ||
130 | // rbdService.list() | |
131 | this.imagesAll = _(data[1]) | |
132 | .flatMap((pool) => pool.value) | |
133 | .filter((image) => { | |
134 | const imageId = `${image.pool_name}/${image.name}`; | |
135 | if (usedImages.indexOf(imageId) !== -1) { | |
136 | return false; | |
137 | } | |
138 | const validBackstores = this.getValidBackstores(image); | |
139 | if (validBackstores.length === 0) { | |
140 | return false; | |
141 | } | |
142 | return true; | |
143 | }) | |
144 | .value(); | |
145 | ||
146 | this.imagesSelections = this.imagesAll.map( | |
147 | (image) => new SelectOption(false, `${image.pool_name}/${image.name}`, '') | |
148 | ); | |
149 | ||
150 | // iscsiService.portals() | |
151 | const portals: SelectOption[] = []; | |
152 | data[2].forEach((portal) => { | |
153 | portal.ip_addresses.forEach((ip) => { | |
154 | portals.push(new SelectOption(false, portal.name + ':' + ip, '')); | |
155 | }); | |
156 | }); | |
157 | this.portalsSelections = [...portals]; | |
158 | ||
159 | this.createForm(); | |
160 | ||
161 | // iscsiService.getTarget() | |
162 | if (data[4]) { | |
163 | this.resolveModel(data[4]); | |
164 | } | |
165 | }); | |
166 | } | |
167 | ||
168 | createForm() { | |
169 | this.targetForm = new CdFormGroup({ | |
170 | target_iqn: new FormControl('iqn.2001-07.com.ceph:' + Date.now(), { | |
171 | validators: [Validators.required, Validators.pattern(this.IQN_REGEX)] | |
172 | }), | |
173 | target_controls: new FormControl({}), | |
174 | portals: new FormControl([], { | |
175 | validators: [ | |
176 | CdValidators.custom('minGateways', (value) => { | |
177 | const gateways = _.uniq(value.map((elem) => elem.split(':')[0])); | |
178 | return gateways.length < Math.max(1, this.minimum_gateways); | |
179 | }) | |
180 | ] | |
181 | }), | |
182 | disks: new FormControl([]), | |
183 | initiators: new FormArray([]), | |
184 | groups: new FormArray([]), | |
185 | acl_enabled: new FormControl(false) | |
186 | }); | |
187 | } | |
188 | ||
189 | resolveModel(res) { | |
190 | this.targetForm.patchValue({ | |
191 | target_iqn: res.target_iqn, | |
192 | target_controls: res.target_controls, | |
193 | acl_enabled: res.acl_enabled | |
194 | }); | |
195 | ||
196 | const portals = []; | |
197 | _.forEach(res.portals, (portal) => { | |
198 | const id = `${portal.host}:${portal.ip}`; | |
199 | portals.push(id); | |
200 | }); | |
201 | this.targetForm.patchValue({ | |
202 | portals: portals | |
203 | }); | |
204 | ||
205 | const disks = []; | |
206 | _.forEach(res.disks, (disk) => { | |
207 | const id = `${disk.pool}/${disk.image}`; | |
208 | disks.push(id); | |
209 | this.imagesSettings[id] = { | |
210 | backstore: disk.backstore | |
211 | }; | |
212 | this.imagesSettings[id][disk.backstore] = disk.controls; | |
213 | ||
214 | this.onImageSelection({ option: { name: id, selected: true } }); | |
215 | }); | |
216 | this.targetForm.patchValue({ | |
217 | disks: disks | |
218 | }); | |
219 | ||
220 | _.forEach(res.clients, (client) => { | |
221 | const initiator = this.addInitiator(); | |
222 | client.luns = _.map(client.luns, (lun) => `${lun.pool}/${lun.image}`); | |
223 | initiator.patchValue(client); | |
224 | // updatedInitiatorSelector() | |
225 | }); | |
226 | ||
227 | _.forEach(res.groups, (group) => { | |
228 | const fg = this.addGroup(); | |
229 | console.log(group); | |
230 | group.disks = _.map(group.disks, (disk) => `${disk.pool}/${disk.image}`); | |
231 | fg.patchValue(group); | |
232 | _.forEach(group.members, (member) => { | |
233 | this.onGroupMemberSelection({ option: new SelectOption(true, member, '') }); | |
234 | }); | |
235 | }); | |
236 | } | |
237 | ||
238 | hasAdvancedSettings(settings: any) { | |
239 | return Object.values(settings).length > 0; | |
240 | } | |
241 | ||
242 | // Portals | |
243 | get portals() { | |
244 | return this.targetForm.get('portals') as FormControl; | |
245 | } | |
246 | ||
247 | onPortalSelection() { | |
248 | this.portals.setValue(this.portals.value); | |
249 | } | |
250 | ||
251 | removePortal(index: number, portal: string) { | |
252 | this.portalsSelections.forEach((value) => { | |
253 | if (value.name === portal) { | |
254 | value.selected = false; | |
255 | } | |
256 | }); | |
257 | ||
258 | this.portals.value.splice(index, 1); | |
259 | this.portals.setValue(this.portals.value); | |
260 | return false; | |
261 | } | |
262 | ||
263 | // Images | |
264 | get disks() { | |
265 | return this.targetForm.get('disks') as FormControl; | |
266 | } | |
267 | ||
268 | removeImage(index: number, image: string) { | |
269 | this.imagesSelections.forEach((value) => { | |
270 | if (value.name === image) { | |
271 | value.selected = false; | |
272 | } | |
273 | }); | |
274 | this.disks.value.splice(index, 1); | |
275 | this.removeImageRefs(image); | |
276 | return false; | |
277 | } | |
278 | ||
279 | removeImageRefs(name) { | |
280 | this.initiators.controls.forEach((element) => { | |
281 | const newImages = element.value.luns.filter((item) => item !== name); | |
282 | element.get('luns').setValue(newImages); | |
283 | }); | |
284 | ||
285 | this.groups.controls.forEach((element) => { | |
286 | const newDisks = element.value.disks.filter((item) => item !== name); | |
287 | element.get('disks').setValue(newDisks); | |
288 | }); | |
289 | ||
290 | _.forEach(this.imagesInitiatorSelections, (selections, i) => { | |
291 | this.imagesInitiatorSelections[i] = selections.filter((item: any) => item.name !== name); | |
292 | }); | |
293 | _.forEach(this.groupDiskSelections, (selections, i) => { | |
294 | this.groupDiskSelections[i] = selections.filter((item: any) => item.name !== name); | |
295 | }); | |
296 | } | |
297 | ||
298 | getDefaultBackstore(imageId) { | |
299 | let result = this.default_backstore; | |
300 | const image = this.getImageById(imageId); | |
301 | if (!this.validFeatures(image, this.default_backstore)) { | |
302 | this.backstores.forEach((backstore) => { | |
303 | if (backstore !== this.default_backstore) { | |
304 | if (this.validFeatures(image, backstore)) { | |
305 | result = backstore; | |
306 | } | |
307 | } | |
308 | }); | |
309 | } | |
310 | return result; | |
311 | } | |
312 | ||
313 | onImageSelection($event) { | |
314 | const option = $event.option; | |
315 | ||
316 | if (option.selected) { | |
317 | if (!this.imagesSettings[option.name]) { | |
318 | const defaultBackstore = this.getDefaultBackstore(option.name); | |
319 | this.imagesSettings[option.name] = { | |
320 | backstore: defaultBackstore | |
321 | }; | |
322 | this.imagesSettings[option.name][defaultBackstore] = {}; | |
323 | } | |
324 | ||
325 | _.forEach(this.imagesInitiatorSelections, (selections, i) => { | |
326 | selections.push(new SelectOption(false, option.name, '')); | |
327 | this.imagesInitiatorSelections[i] = [...selections]; | |
328 | }); | |
329 | ||
330 | _.forEach(this.groupDiskSelections, (selections, i) => { | |
331 | selections.push(new SelectOption(false, option.name, '')); | |
332 | this.groupDiskSelections[i] = [...selections]; | |
333 | }); | |
334 | } else { | |
335 | this.removeImageRefs(option.name); | |
336 | } | |
337 | } | |
338 | ||
339 | // Initiators | |
340 | get initiators() { | |
341 | return this.targetForm.get('initiators') as FormArray; | |
342 | } | |
343 | ||
344 | addInitiator() { | |
345 | const fg = new CdFormGroup({ | |
346 | client_iqn: new FormControl('', { | |
347 | validators: [ | |
348 | Validators.required, | |
349 | CdValidators.custom('notUnique', (client_iqn) => { | |
350 | const flattened = this.initiators.controls.reduce(function(accumulator, currentValue) { | |
351 | return accumulator.concat(currentValue.value.client_iqn); | |
352 | }, []); | |
353 | ||
354 | return flattened.indexOf(client_iqn) !== flattened.lastIndexOf(client_iqn); | |
355 | }), | |
356 | Validators.pattern(this.IQN_REGEX) | |
357 | ] | |
358 | }), | |
359 | auth: new CdFormGroup({ | |
360 | user: new FormControl(''), | |
361 | password: new FormControl(''), | |
362 | mutual_user: new FormControl(''), | |
363 | mutual_password: new FormControl('') | |
364 | }), | |
365 | luns: new FormControl([]), | |
366 | cdIsInGroup: new FormControl(false) | |
367 | }); | |
368 | ||
369 | CdValidators.validateIf( | |
370 | fg.get('user'), | |
371 | () => fg.getValue('password') || fg.getValue('mutual_user') || fg.getValue('mutual_password'), | |
372 | [Validators.required], | |
373 | [Validators.pattern(this.USER_REGEX)], | |
374 | [fg.get('password'), fg.get('mutual_user'), fg.get('mutual_password')] | |
375 | ); | |
376 | ||
377 | CdValidators.validateIf( | |
378 | fg.get('password'), | |
379 | () => fg.getValue('user') || fg.getValue('mutual_user') || fg.getValue('mutual_password'), | |
380 | [Validators.required], | |
381 | [Validators.pattern(this.PASSWORD_REGEX)], | |
382 | [fg.get('user'), fg.get('mutual_user'), fg.get('mutual_password')] | |
383 | ); | |
384 | ||
385 | CdValidators.validateIf( | |
386 | fg.get('mutual_user'), | |
387 | () => fg.getValue('mutual_password'), | |
388 | [Validators.required], | |
389 | [Validators.pattern(this.USER_REGEX)], | |
390 | [fg.get('user'), fg.get('password'), fg.get('mutual_password')] | |
391 | ); | |
392 | ||
393 | CdValidators.validateIf( | |
394 | fg.get('mutual_password'), | |
395 | () => fg.getValue('mutual_user'), | |
396 | [Validators.required], | |
397 | [Validators.pattern(this.PASSWORD_REGEX)], | |
398 | [fg.get('user'), fg.get('password'), fg.get('mutual_user')] | |
399 | ); | |
400 | ||
401 | this.initiators.push(fg); | |
402 | ||
403 | _.forEach(this.groupMembersSelections, (selections, i) => { | |
404 | selections.push(new SelectOption(false, '', '')); | |
405 | this.groupMembersSelections[i] = [...selections]; | |
406 | }); | |
407 | ||
408 | const disks = _.map( | |
409 | this.targetForm.getValue('disks'), | |
410 | (disk) => new SelectOption(false, disk, '') | |
411 | ); | |
412 | this.imagesInitiatorSelections.push(disks); | |
413 | ||
414 | return fg; | |
415 | } | |
416 | ||
417 | removeInitiator(index) { | |
418 | const removed = this.initiators.value[index]; | |
419 | ||
420 | this.initiators.removeAt(index); | |
421 | ||
422 | _.forEach(this.groupMembersSelections, (selections, i) => { | |
423 | selections.splice(index, 1); | |
424 | this.groupMembersSelections[i] = [...selections]; | |
425 | }); | |
426 | ||
427 | this.groups.controls.forEach((element) => { | |
428 | const newMembers = element.value.members.filter((item) => item !== removed.client_iqn); | |
429 | element.get('members').setValue(newMembers); | |
430 | }); | |
431 | ||
432 | this.imagesInitiatorSelections.splice(index, 1); | |
433 | } | |
434 | ||
435 | updatedInitiatorSelector() { | |
436 | // Validate all client_iqn | |
437 | this.initiators.controls.forEach((control) => { | |
438 | control.get('client_iqn').updateValueAndValidity({ emitEvent: false }); | |
439 | }); | |
440 | ||
441 | // Update Group Initiator Selector | |
442 | _.forEach(this.groupMembersSelections, (group, group_index) => { | |
443 | _.forEach(group, (elem, index) => { | |
444 | const oldName = elem.name; | |
445 | elem.name = this.initiators.controls[index].value.client_iqn; | |
446 | ||
447 | this.groups.controls.forEach((element) => { | |
448 | const members = element.value.members; | |
449 | const i = members.indexOf(oldName); | |
450 | ||
451 | if (i !== -1) { | |
452 | members[i] = elem.name; | |
453 | } | |
454 | element.get('members').setValue(members); | |
455 | }); | |
456 | }); | |
457 | this.groupMembersSelections[group_index] = [...this.groupMembersSelections[group_index]]; | |
458 | }); | |
459 | } | |
460 | ||
461 | removeInitiatorImage(initiator: any, lun_index: number, initiator_index: string, image: string) { | |
462 | const luns = initiator.getValue('luns'); | |
463 | luns.splice(lun_index, 1); | |
464 | initiator.patchValue({ luns: luns }); | |
465 | ||
466 | this.imagesInitiatorSelections[initiator_index].forEach((value) => { | |
467 | if (value.name === image) { | |
468 | value.selected = false; | |
469 | } | |
470 | }); | |
471 | ||
472 | return false; | |
473 | } | |
474 | ||
475 | // Groups | |
476 | get groups() { | |
477 | return this.targetForm.get('groups') as FormArray; | |
478 | } | |
479 | ||
480 | addGroup() { | |
481 | const fg = new CdFormGroup({ | |
482 | group_id: new FormControl('', { validators: [Validators.required] }), | |
483 | members: new FormControl([]), | |
484 | disks: new FormControl([]) | |
485 | }); | |
486 | ||
487 | this.groups.push(fg); | |
488 | ||
489 | const disks = _.map( | |
490 | this.targetForm.getValue('disks'), | |
491 | (disk) => new SelectOption(false, disk, '') | |
492 | ); | |
493 | this.groupDiskSelections.push(disks); | |
494 | ||
495 | const initiators = _.map( | |
496 | this.initiators.value, | |
497 | (initiator) => new SelectOption(false, initiator.client_iqn, '') | |
498 | ); | |
499 | this.groupMembersSelections.push(initiators); | |
500 | ||
501 | return fg; | |
502 | } | |
503 | ||
504 | removeGroup(index) { | |
505 | this.groups.removeAt(index); | |
506 | this.groupDiskSelections.splice(index, 1); | |
507 | } | |
508 | ||
509 | onGroupMemberSelection($event) { | |
510 | const option = $event.option; | |
511 | ||
512 | this.initiators.controls.forEach((element) => { | |
513 | if (element.value.client_iqn === option.name) { | |
514 | element.patchValue({ luns: [] }); | |
515 | element.get('cdIsInGroup').setValue(option.selected); | |
516 | } | |
517 | }); | |
518 | } | |
519 | ||
520 | removeGroupInitiator(group, member_index, group_index) { | |
521 | const name = group.getValue('members')[member_index]; | |
522 | group.getValue('members').splice(member_index, 1); | |
523 | ||
524 | this.groupMembersSelections[group_index].forEach((value) => { | |
525 | if (value.name === name) { | |
526 | value.selected = false; | |
527 | } | |
528 | }); | |
529 | this.groupMembersSelections[group_index] = [...this.groupMembersSelections[group_index]]; | |
530 | ||
531 | this.onGroupMemberSelection({ option: new SelectOption(false, name, '') }); | |
532 | } | |
533 | ||
534 | removeGroupDisk(group, disk_index, group_index) { | |
535 | const name = group.getValue('disks')[disk_index]; | |
536 | group.getValue('disks').splice(disk_index, 1); | |
537 | ||
538 | this.groupDiskSelections[group_index].forEach((value) => { | |
539 | if (value.name === name) { | |
540 | value.selected = false; | |
541 | } | |
542 | }); | |
543 | this.groupDiskSelections[group_index] = [...this.groupDiskSelections[group_index]]; | |
544 | } | |
545 | ||
546 | submit() { | |
547 | const formValue = _.cloneDeep(this.targetForm.value); | |
548 | ||
549 | const request = { | |
550 | target_iqn: this.targetForm.getValue('target_iqn'), | |
551 | target_controls: this.targetForm.getValue('target_controls'), | |
552 | acl_enabled: this.targetForm.getValue('acl_enabled'), | |
553 | portals: [], | |
554 | disks: [], | |
555 | clients: [], | |
556 | groups: [] | |
557 | }; | |
558 | ||
559 | // Disks | |
560 | formValue.disks.forEach((disk) => { | |
561 | const imageSplit = disk.split('/'); | |
562 | const backstore = this.imagesSettings[disk].backstore; | |
563 | request.disks.push({ | |
564 | pool: imageSplit[0], | |
565 | image: imageSplit[1], | |
566 | backstore: backstore, | |
567 | controls: this.imagesSettings[disk][backstore] | |
568 | }); | |
569 | }); | |
570 | ||
571 | // Portals | |
572 | formValue.portals.forEach((portal) => { | |
573 | const portalSplit = portal.split(':'); | |
574 | request.portals.push({ | |
575 | host: portalSplit[0], | |
576 | ip: portalSplit[1] | |
577 | }); | |
578 | }); | |
579 | ||
580 | // Clients | |
581 | if (request.acl_enabled) { | |
582 | formValue.initiators.forEach((initiator) => { | |
583 | if (!initiator.auth.user) { | |
584 | initiator.auth.user = ''; | |
585 | } | |
586 | if (!initiator.auth.password) { | |
587 | initiator.auth.password = ''; | |
588 | } | |
589 | if (!initiator.auth.mutual_user) { | |
590 | initiator.auth.mutual_user = ''; | |
591 | } | |
592 | if (!initiator.auth.mutual_password) { | |
593 | initiator.auth.mutual_password = ''; | |
594 | } | |
595 | delete initiator.cdIsInGroup; | |
596 | ||
597 | const newLuns = []; | |
598 | initiator.luns.forEach((lun) => { | |
599 | const imageSplit = lun.split('/'); | |
600 | newLuns.push({ | |
601 | pool: imageSplit[0], | |
602 | image: imageSplit[1] | |
603 | }); | |
604 | }); | |
605 | ||
606 | initiator.luns = newLuns; | |
607 | }); | |
608 | request.clients = formValue.initiators; | |
609 | } | |
610 | ||
611 | // Groups | |
612 | if (request.acl_enabled) { | |
613 | formValue.groups.forEach((group) => { | |
614 | const newDisks = []; | |
615 | group.disks.forEach((disk) => { | |
616 | const imageSplit = disk.split('/'); | |
617 | newDisks.push({ | |
618 | pool: imageSplit[0], | |
619 | image: imageSplit[1] | |
620 | }); | |
621 | }); | |
622 | ||
623 | group.disks = newDisks; | |
624 | }); | |
625 | request.groups = formValue.groups; | |
626 | } | |
627 | ||
628 | let wrapTask; | |
629 | if (this.isEdit) { | |
630 | request['new_target_iqn'] = request.target_iqn; | |
631 | request.target_iqn = this.target_iqn; | |
632 | wrapTask = this.taskWrapper.wrapTaskAroundCall({ | |
633 | task: new FinishedTask('iscsi/target/edit', { | |
634 | target_iqn: request.target_iqn | |
635 | }), | |
636 | call: this.iscsiService.updateTarget(this.target_iqn, request) | |
637 | }); | |
638 | } else { | |
639 | wrapTask = this.taskWrapper.wrapTaskAroundCall({ | |
640 | task: new FinishedTask('iscsi/target/create', { | |
641 | target_iqn: request.target_iqn | |
642 | }), | |
643 | call: this.iscsiService.createTarget(request) | |
644 | }); | |
645 | } | |
646 | ||
647 | wrapTask.subscribe( | |
648 | undefined, | |
649 | () => { | |
650 | this.targetForm.setErrors({ cdSubmitButton: true }); | |
651 | }, | |
652 | () => this.router.navigate(['/block/iscsi/targets']) | |
653 | ); | |
654 | } | |
655 | ||
656 | targetSettingsModal() { | |
657 | const initialState = { | |
658 | target_controls: this.targetForm.get('target_controls'), | |
659 | target_default_controls: this.target_default_controls | |
660 | }; | |
661 | ||
662 | this.modalRef = this.modalService.show(IscsiTargetIqnSettingsModalComponent, { initialState }); | |
663 | } | |
664 | ||
665 | imageSettingsModal(image) { | |
666 | const initialState = { | |
667 | imagesSettings: this.imagesSettings, | |
668 | image: image, | |
669 | disk_default_controls: this.disk_default_controls, | |
670 | backstores: this.getValidBackstores(this.getImageById(image)) | |
671 | }; | |
672 | ||
673 | this.modalRef = this.modalService.show(IscsiTargetImageSettingsModalComponent, { | |
674 | initialState | |
675 | }); | |
676 | } | |
677 | ||
678 | validFeatures(image, backstore) { | |
679 | const imageFeatures = image.features; | |
680 | const requiredFeatures = this.required_rbd_features[backstore]; | |
681 | const supportedFeatures = this.supported_rbd_features[backstore]; | |
682 | // tslint:disable-next-line:no-bitwise | |
683 | const validRequiredFeatures = (imageFeatures & requiredFeatures) === requiredFeatures; | |
684 | // tslint:disable-next-line:no-bitwise | |
685 | const validSupportedFeatures = (imageFeatures & supportedFeatures) === imageFeatures; | |
686 | return validRequiredFeatures && validSupportedFeatures; | |
687 | } | |
688 | ||
689 | getImageById(imageId) { | |
690 | return this.imagesAll.find((image) => imageId === `${image.pool_name}/${image.name}`); | |
691 | } | |
692 | ||
693 | getValidBackstores(image) { | |
694 | return this.backstores.filter((backstore) => this.validFeatures(image, backstore)); | |
695 | } | |
696 | } |